billion-context-omp 0.1.9 → 0.2.1
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 +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/index.js +407 -120
- package/dist/index.js.map +1 -1
- package/dist/user-config.d.ts +1 -0
- package/dist/wire-transform.d.ts +53 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -145,6 +145,7 @@ Create `~/.omp/acp-omp.json` (global) and/or `<project>/.omp/acp-omp.json` (proj
|
|
|
145
145
|
| Key | Default | Description |
|
|
146
146
|
|-----|---------|-------------|
|
|
147
147
|
| `debug` | `false` | Enable verbose **debug-level** events in the log. The always-on log (lifecycle events, errors, warnings) is written regardless; `debug` only adds extra diagnostics. Also enabled by env `ACP_DEBUG=1`. |
|
|
148
|
+
| `transformMode` | `"context"` (default) or `"provider"` — where the compression surgery intercepts. `provider` transforms the provider wire payload (no feedback re-entry; experimental). |
|
|
148
149
|
| `autoUpdate` | `true` | On session start (throttled to one check per 3 minutes), check npm for a newer version and auto-install it. Disable to avoid all startup network calls. |
|
|
149
150
|
| `modelContextLimit` | *(auto)* | Override the context limit (in tokens). Defaults to the model's `contextWindow`. |
|
|
150
151
|
| `compressModel` | *(session model)* | `provider:modelId` used for `/compact` model-summarized compaction (e.g. `"zhipuai:glm-5.2"`). Defaults to the current session model when omitted. |
|
package/dist/config.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export interface CompressConfig {
|
|
|
27
27
|
* (live model context window, protected tools, state persistence).
|
|
28
28
|
*/
|
|
29
29
|
export interface AdapterConfig {
|
|
30
|
+
/** Where the compression surgery intercepts (issue #52). "context" (default)
|
|
31
|
+
* rewrites the context event — battle-tested, but omp's recap/subagent
|
|
32
|
+
* pipelines can re-feed our output as input (feedback-view loops).
|
|
33
|
+
* "provider" leaves the agent array untouched and transforms the WIRE
|
|
34
|
+
* payload at before_provider_request — request-local, no re-entry; unknown
|
|
35
|
+
* provider formats pass through untransformed (fail-open). */
|
|
36
|
+
transformMode?: "context" | "provider";
|
|
30
37
|
/** When omitted, the adapter reads `ctx.model.contextWindow` live each turn.
|
|
31
38
|
* Set explicitly for tests/headless runs. */
|
|
32
39
|
modelContextLimit?: number;
|
package/dist/index.js
CHANGED
|
@@ -3166,6 +3166,15 @@ function freshSlot(preserveFrom) {
|
|
|
3166
3166
|
}
|
|
3167
3167
|
return slot;
|
|
3168
3168
|
}
|
|
3169
|
+
function isViewFlip(foldedLen, lcp) {
|
|
3170
|
+
return foldedLen > 0 && lcp >= Math.floor(foldedLen / 2);
|
|
3171
|
+
}
|
|
3172
|
+
function preserveCompressedSlot(prev) {
|
|
3173
|
+
const slot = freshSlot(prev);
|
|
3174
|
+
slot.state = { ...slot.state, blocks: prev.state.blocks, messageRefs: prev.state.messageRefs, stats: prev.state.stats };
|
|
3175
|
+
slot.appliedCallIds = new Set(prev.appliedCallIds);
|
|
3176
|
+
return slot;
|
|
3177
|
+
}
|
|
3169
3178
|
function stateHasCompressCall(state, callId) {
|
|
3170
3179
|
return state.blocks.some((b) => b.compressCallId === callId);
|
|
3171
3180
|
}
|
|
@@ -3217,8 +3226,9 @@ function createRuntime(adapter) {
|
|
|
3217
3226
|
let lcp = 0;
|
|
3218
3227
|
while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
|
|
3219
3228
|
if (lcp < slot.foldedLen) {
|
|
3220
|
-
|
|
3221
|
-
slot
|
|
3229
|
+
const flip = isViewFlip(slot.foldedLen, lcp);
|
|
3230
|
+
debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp, streamLen: ids.length, flip });
|
|
3231
|
+
slot = flip ? preserveCompressedSlot(slot) : freshSlot(slot);
|
|
3222
3232
|
slots.set(sid, slot);
|
|
3223
3233
|
lcp = 0;
|
|
3224
3234
|
}
|
|
@@ -3244,6 +3254,7 @@ function createRuntime(adapter) {
|
|
|
3244
3254
|
debug.event("fold-replay-skipped", { sid, callId: call.id });
|
|
3245
3255
|
continue;
|
|
3246
3256
|
}
|
|
3257
|
+
if (slot.appliedCallIds.has(call.id) || stateHasCompressCall(slot.state, call.id)) continue;
|
|
3247
3258
|
const stale = call.ranges.map((r, ri) => staleRange(r, ri, resultText, coreMessages, i, slot.state.messageRefs.byRef, slot.state.blocks)).find((s) => s !== false);
|
|
3248
3259
|
if (stale) {
|
|
3249
3260
|
debug.event("fold-replay-stale", { sid, callId: call.id, reason: stale });
|
|
@@ -3425,6 +3436,11 @@ function makeCompressTool(runtime) {
|
|
|
3425
3436
|
async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
3426
3437
|
const ranges = args.content ?? [];
|
|
3427
3438
|
if (ranges.length === 0) return "No ranges provided.";
|
|
3439
|
+
const invalid = ranges.filter((r) => !r || typeof r.summary !== "string" || !r.summary.trim() || !r.startId || !r.endId);
|
|
3440
|
+
if (invalid.length > 0) {
|
|
3441
|
+
logWarn("compress", { sid: ctx.sessionManager.getSessionId(), event: "invalid-ranges", count: invalid.length });
|
|
3442
|
+
return `Every range needs startId, endId and a summary (min 50 chars) \u2014 got ${invalid.length} range(s) missing fields. Re-send the FULL ranges array with summaries included.`;
|
|
3443
|
+
}
|
|
3428
3444
|
const releaseLock = await runtime.acquireLock(ctx.sessionManager.getSessionId());
|
|
3429
3445
|
try {
|
|
3430
3446
|
const { state: initialState, coreMessages } = await runtime.stateFor(ctx);
|
|
@@ -4492,7 +4508,7 @@ async function statusReport(runtime, ctx) {
|
|
|
4492
4508
|
const coveredIds = collectCoveredMessageIds(state);
|
|
4493
4509
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
4494
4510
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
4495
|
-
const versionStr = "0.1
|
|
4511
|
+
const versionStr = "0.2.1" ? `billion-context-omp@${"0.2.1"}` : void 0;
|
|
4496
4512
|
return buildStatusPanel({
|
|
4497
4513
|
version: versionStr,
|
|
4498
4514
|
tokenCount: sessionTokens,
|
|
@@ -4504,6 +4520,237 @@ async function statusReport(runtime, ctx) {
|
|
|
4504
4520
|
});
|
|
4505
4521
|
}
|
|
4506
4522
|
|
|
4523
|
+
// src/wire-transform.ts
|
|
4524
|
+
var AI = /* @__PURE__ */ Symbol("acp.streamIndex");
|
|
4525
|
+
function detectWireFormat(payload) {
|
|
4526
|
+
if (payload === null || typeof payload !== "object") return "unknown";
|
|
4527
|
+
const p = payload;
|
|
4528
|
+
const messages = p.messages;
|
|
4529
|
+
if (!Array.isArray(messages)) return "unknown";
|
|
4530
|
+
if ("system" in p || "anthropic_version" in p) return "anthropic";
|
|
4531
|
+
for (const m of messages) {
|
|
4532
|
+
if (m === null || typeof m !== "object") continue;
|
|
4533
|
+
const c = m.content;
|
|
4534
|
+
if (Array.isArray(c)) {
|
|
4535
|
+
for (const b of c) {
|
|
4536
|
+
if (b && typeof b === "object" && typeof b.type === "string") {
|
|
4537
|
+
if (b.type === "tool_use" || b.type === "tool_result" || b.type === "thinking") return "anthropic";
|
|
4538
|
+
if (b.type === "text" && "cache_control" in b) return "anthropic";
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
if (Array.isArray(m.tool_calls)) return "openai";
|
|
4543
|
+
if (m.role === "tool" && typeof m.tool_call_id === "string") return "openai";
|
|
4544
|
+
if (m.role === "system" || m.role === "developer") return "openai";
|
|
4545
|
+
}
|
|
4546
|
+
return "openai";
|
|
4547
|
+
}
|
|
4548
|
+
function anthropicBlocks(m) {
|
|
4549
|
+
return typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content ?? [];
|
|
4550
|
+
}
|
|
4551
|
+
var assistantBase = () => ({
|
|
4552
|
+
api: "anthropic",
|
|
4553
|
+
provider: "anthropic",
|
|
4554
|
+
model: "wire",
|
|
4555
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
|
|
4556
|
+
stopReason: "stop",
|
|
4557
|
+
timestamp: Date.now()
|
|
4558
|
+
});
|
|
4559
|
+
function synthesizeStream(payload, format) {
|
|
4560
|
+
const messages = payload.messages ?? [];
|
|
4561
|
+
const stream = [];
|
|
4562
|
+
const back = [];
|
|
4563
|
+
const push = (msg, wi, kind) => {
|
|
4564
|
+
msg[AI] = stream.length;
|
|
4565
|
+
stream.push(msg);
|
|
4566
|
+
back.push({ wi, kind });
|
|
4567
|
+
};
|
|
4568
|
+
if (format === "anthropic") {
|
|
4569
|
+
const toolNames = /* @__PURE__ */ new Map();
|
|
4570
|
+
for (const raw of messages) {
|
|
4571
|
+
if (raw === null || typeof raw !== "object") continue;
|
|
4572
|
+
const m = raw;
|
|
4573
|
+
if (m.role !== "assistant") continue;
|
|
4574
|
+
for (const b of anthropicBlocks(m)) if (b.type === "tool_use" && b.id) toolNames.set(b.id, b.name ?? "");
|
|
4575
|
+
}
|
|
4576
|
+
messages.forEach((raw, wi) => {
|
|
4577
|
+
if (raw === null || typeof raw !== "object") return;
|
|
4578
|
+
const m = raw;
|
|
4579
|
+
const blocks = anthropicBlocks(m);
|
|
4580
|
+
if (m.role === "user") {
|
|
4581
|
+
let texts = [];
|
|
4582
|
+
for (const b of blocks) {
|
|
4583
|
+
if (b.type === "text" && typeof b.text === "string") texts.push(b.text);
|
|
4584
|
+
else if (b.type === "tool_result") {
|
|
4585
|
+
if (texts.length > 0) {
|
|
4586
|
+
push({ role: "user", content: texts.map((t2) => ({ type: "text", text: t2 })), timestamp: Date.now() }, wi, "text");
|
|
4587
|
+
texts = [];
|
|
4588
|
+
}
|
|
4589
|
+
const trText = typeof b.content === "string" ? b.content : Array.isArray(b.content) ? b.content.map((c) => c.text ?? "").join("\n") : "";
|
|
4590
|
+
push({
|
|
4591
|
+
role: "toolResult",
|
|
4592
|
+
content: [{ type: "text", text: trText }],
|
|
4593
|
+
toolName: toolNames.get(b.tool_use_id ?? "") ?? "",
|
|
4594
|
+
toolCallId: b.tool_use_id ?? "",
|
|
4595
|
+
isError: b.is_error === true,
|
|
4596
|
+
timestamp: Date.now()
|
|
4597
|
+
}, wi, "toolResult");
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
if (texts.length > 0) push({ role: "user", content: texts.map((t2) => ({ type: "text", text: t2 })), timestamp: Date.now() }, wi, "text");
|
|
4601
|
+
return;
|
|
4602
|
+
}
|
|
4603
|
+
if (m.role === "assistant") {
|
|
4604
|
+
const content = [];
|
|
4605
|
+
for (const b of blocks) {
|
|
4606
|
+
if (b.type === "text" && typeof b.text === "string" && b.text.length > 0) content.push({ type: "text", text: b.text });
|
|
4607
|
+
else if (b.type === "tool_use") content.push({ type: "toolCall", id: b.id, name: b.name, arguments: b.input ?? {} });
|
|
4608
|
+
}
|
|
4609
|
+
if (content.length > 0) push({ role: "assistant", ...assistantBase(), content }, wi, "toolCall");
|
|
4610
|
+
return;
|
|
4611
|
+
}
|
|
4612
|
+
const t = blocks.map((b) => typeof b.text === "string" ? b.text : "").join("\n");
|
|
4613
|
+
if (t) push({ role: "user", content: [{ type: "text", text: t }], timestamp: Date.now() }, wi, "text");
|
|
4614
|
+
});
|
|
4615
|
+
return { stream, back, format };
|
|
4616
|
+
}
|
|
4617
|
+
messages.forEach((raw, wi) => {
|
|
4618
|
+
if (raw === null || typeof raw !== "object") return;
|
|
4619
|
+
const m = raw;
|
|
4620
|
+
const textOf = () => {
|
|
4621
|
+
const c = m.content;
|
|
4622
|
+
if (typeof c === "string") return c;
|
|
4623
|
+
if (Array.isArray(c)) return c.map((p) => p.type === "text" ? p.text ?? "" : "").join("\n");
|
|
4624
|
+
return "";
|
|
4625
|
+
};
|
|
4626
|
+
if (m.role === "system" || m.role === "developer") {
|
|
4627
|
+
const t2 = textOf();
|
|
4628
|
+
if (t2) push({ role: "user", content: [{ type: "text", text: t2 }], timestamp: Date.now() }, wi, "text");
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
if (m.role === "tool") {
|
|
4632
|
+
push({
|
|
4633
|
+
role: "toolResult",
|
|
4634
|
+
content: [{ type: "text", text: textOf() }],
|
|
4635
|
+
toolName: "",
|
|
4636
|
+
toolCallId: m.tool_call_id ?? "",
|
|
4637
|
+
isError: false,
|
|
4638
|
+
timestamp: Date.now()
|
|
4639
|
+
}, wi, "toolResult");
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
if (m.role === "assistant") {
|
|
4643
|
+
const calls = m.tool_calls ?? [];
|
|
4644
|
+
if (calls.length > 0) {
|
|
4645
|
+
const content = [];
|
|
4646
|
+
const t3 = textOf();
|
|
4647
|
+
if (t3) content.push({ type: "text", text: t3 });
|
|
4648
|
+
for (const c of calls) {
|
|
4649
|
+
let args = {};
|
|
4650
|
+
try {
|
|
4651
|
+
args = c.function?.arguments ? JSON.parse(c.function.arguments) : {};
|
|
4652
|
+
} catch {
|
|
4653
|
+
args = { raw: c.function?.arguments ?? "" };
|
|
4654
|
+
}
|
|
4655
|
+
content.push({ type: "toolCall", id: c.id, name: c.function?.name ?? "", arguments: args });
|
|
4656
|
+
}
|
|
4657
|
+
push({ role: "assistant", ...assistantBase(), content }, wi, "toolCall");
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
const t2 = textOf();
|
|
4661
|
+
if (t2) push({ role: "assistant", ...assistantBase(), content: [{ type: "text", text: t2 }] }, wi, "text");
|
|
4662
|
+
return;
|
|
4663
|
+
}
|
|
4664
|
+
const t = textOf();
|
|
4665
|
+
if (t) push({ role: "user", content: [{ type: "text", text: t }], timestamp: Date.now() }, wi, "text");
|
|
4666
|
+
});
|
|
4667
|
+
return { stream, back, format };
|
|
4668
|
+
}
|
|
4669
|
+
function rebuildWirePayload(rebuilt, payload, synth) {
|
|
4670
|
+
const messages = (payload.messages ?? []).slice();
|
|
4671
|
+
const out = [];
|
|
4672
|
+
const agentIndexOf = (m) => m[AI];
|
|
4673
|
+
for (const agent of rebuilt) {
|
|
4674
|
+
const ai = agentIndexOf(agent);
|
|
4675
|
+
if (ai === void 0 || ai >= synth.back.length) {
|
|
4676
|
+
const text2 = extractText(agent.content);
|
|
4677
|
+
const role = agent.role === "assistant" ? "assistant" : "user";
|
|
4678
|
+
if (synth.format === "anthropic") {
|
|
4679
|
+
out.push({ role, content: [{ type: "text", text: text2 }] });
|
|
4680
|
+
} else {
|
|
4681
|
+
out.push({ role, content: text2 });
|
|
4682
|
+
}
|
|
4683
|
+
continue;
|
|
4684
|
+
}
|
|
4685
|
+
const { wi, kind } = synth.back[ai];
|
|
4686
|
+
const src = messages[wi];
|
|
4687
|
+
if (!src) {
|
|
4688
|
+
out.push(agent);
|
|
4689
|
+
continue;
|
|
4690
|
+
}
|
|
4691
|
+
if (synth.format === "anthropic") {
|
|
4692
|
+
const srcMsg2 = src;
|
|
4693
|
+
const blocks = anthropicBlocks(srcMsg2);
|
|
4694
|
+
if (kind === "toolResult") {
|
|
4695
|
+
const block2 = blocks.find((b) => b.type === "tool_result" && b.tool_use_id === agent.toolCallId);
|
|
4696
|
+
const text3 = extractText(agent.content);
|
|
4697
|
+
if (block2) {
|
|
4698
|
+
out.push({ role: "user", content: [{ ...block2, content: [{ type: "text", text: text3 }] }] });
|
|
4699
|
+
continue;
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
if (kind === "toolCall") {
|
|
4703
|
+
const callIds = new Set(
|
|
4704
|
+
(agent.content ?? []).filter((b) => b.type === "toolCall" && b.id).map((b) => b.id)
|
|
4705
|
+
);
|
|
4706
|
+
const agentText = extractText(agent.content);
|
|
4707
|
+
const outBlocks = [];
|
|
4708
|
+
let textSeen = false;
|
|
4709
|
+
for (const b of blocks) {
|
|
4710
|
+
if (b.type === "tool_use") {
|
|
4711
|
+
if (callIds.has(b.id ?? "")) outBlocks.push(b);
|
|
4712
|
+
continue;
|
|
4713
|
+
}
|
|
4714
|
+
if (b.type === "text") {
|
|
4715
|
+
if (!textSeen && typeof b.text === "string") {
|
|
4716
|
+
outBlocks.push(b);
|
|
4717
|
+
textSeen = true;
|
|
4718
|
+
}
|
|
4719
|
+
continue;
|
|
4720
|
+
}
|
|
4721
|
+
outBlocks.push(b);
|
|
4722
|
+
}
|
|
4723
|
+
if (agentText && !textSeen) outBlocks.unshift({ type: "text", text: agentText });
|
|
4724
|
+
out.push({ role: "assistant", content: outBlocks });
|
|
4725
|
+
continue;
|
|
4726
|
+
}
|
|
4727
|
+
const text2 = extractText(agent.content);
|
|
4728
|
+
const block = blocks.find((b) => b.type === "text");
|
|
4729
|
+
if (block) out.push({ role: srcMsg2.role, content: [{ ...block, text: text2 }] });
|
|
4730
|
+
else out.push({ role: srcMsg2.role, content: [{ type: "text", text: text2 }] });
|
|
4731
|
+
continue;
|
|
4732
|
+
}
|
|
4733
|
+
const srcMsg = src;
|
|
4734
|
+
const text = extractText(agent.content);
|
|
4735
|
+
if (kind === "toolResult") {
|
|
4736
|
+
out.push({ role: "tool", tool_call_id: agent.toolCallId ?? srcMsg.tool_call_id, content: text });
|
|
4737
|
+
continue;
|
|
4738
|
+
}
|
|
4739
|
+
if (kind === "toolCall") {
|
|
4740
|
+
const callIds = new Set(
|
|
4741
|
+
(agent.content ?? []).filter((b) => b.type === "toolCall" && b.id).map((b) => b.id)
|
|
4742
|
+
);
|
|
4743
|
+
const surviving = (srcMsg.tool_calls ?? []).filter((c) => callIds.has(c.id));
|
|
4744
|
+
const entry = { role: "assistant", content: text };
|
|
4745
|
+
if (surviving.length > 0) entry.tool_calls = surviving;
|
|
4746
|
+
out.push(entry);
|
|
4747
|
+
continue;
|
|
4748
|
+
}
|
|
4749
|
+
out.push({ role: srcMsg.role === "assistant" ? "assistant" : srcMsg.role, content: text });
|
|
4750
|
+
}
|
|
4751
|
+
return { ...payload, messages: out };
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4507
4754
|
// src/auto-compress.ts
|
|
4508
4755
|
import { readFileSync } from "fs";
|
|
4509
4756
|
import { join as join3 } from "path";
|
|
@@ -4934,7 +5181,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
4934
5181
|
const data = await res.json();
|
|
4935
5182
|
const latest = data.version;
|
|
4936
5183
|
if (!latest) return;
|
|
4937
|
-
const current = runtimeVersion ?? "0.1
|
|
5184
|
+
const current = runtimeVersion ?? "0.2.1";
|
|
4938
5185
|
const hasUpdate = isNewer(latest, current);
|
|
4939
5186
|
debug.event("update-check", {
|
|
4940
5187
|
current,
|
|
@@ -5149,6 +5396,7 @@ var KNOWN = /* @__PURE__ */ new Set([
|
|
|
5149
5396
|
"debug",
|
|
5150
5397
|
"autoUpdate",
|
|
5151
5398
|
"modelContextLimit",
|
|
5399
|
+
"transformMode",
|
|
5152
5400
|
"toolBashDefaultTimeout",
|
|
5153
5401
|
"toolOutputMaxBytes",
|
|
5154
5402
|
"delegate",
|
|
@@ -5192,6 +5440,7 @@ function createAcpExtension(adapter = {}) {
|
|
|
5192
5440
|
wireSessionLifecycle(pi, runtime);
|
|
5193
5441
|
wireContextTransform(pi, runtime);
|
|
5194
5442
|
wireSystemPrompt(pi, runtime);
|
|
5443
|
+
wireProviderTransform(pi, runtime);
|
|
5195
5444
|
wireProviderDebug(pi);
|
|
5196
5445
|
wireToolGuardrails(pi, runtime);
|
|
5197
5446
|
pi.registerTool(makeCompressTool(runtime));
|
|
@@ -5248,7 +5497,7 @@ function wireCompactionDisable(pi, runtime) {
|
|
|
5248
5497
|
function wireSessionLifecycle(pi, runtime) {
|
|
5249
5498
|
pi.on("session_start", async (_event, ctx) => {
|
|
5250
5499
|
const sid = ctx.sessionManager.getSessionId();
|
|
5251
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1
|
|
5500
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.1" : null });
|
|
5252
5501
|
try {
|
|
5253
5502
|
const user = await loadUserConfig(ctx.cwd);
|
|
5254
5503
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
@@ -5275,130 +5524,168 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
5275
5524
|
closeLogStream();
|
|
5276
5525
|
});
|
|
5277
5526
|
}
|
|
5278
|
-
function
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5527
|
+
async function transformStream(ctx, runtime, input, mode) {
|
|
5528
|
+
const sid = ctx.sessionManager.getSessionId();
|
|
5529
|
+
const release = await runtime.acquireLock(sid);
|
|
5530
|
+
try {
|
|
5531
|
+
if (input.length === 0) {
|
|
5532
|
+
debug.event("empty-stream-bypass", { sid });
|
|
5533
|
+
return void 0;
|
|
5534
|
+
}
|
|
5535
|
+
debug.event("context-in-raw", { sid, msgs: input.length, mode });
|
|
5536
|
+
const { state, coreMessages, originalById, streamLen } = runtime.foldStream(ctx, input);
|
|
5537
|
+
const preTurnNudgeBaseline = state.nudge.lastPerMessageNudgeTokens;
|
|
5538
|
+
const preTurnNudgeShownTokens = state.nudge.lastNudgeShownTokens;
|
|
5539
|
+
const preTurnNudgeShownByTier = state.nudge.lastShownByTier;
|
|
5540
|
+
const config = runtime.configFor(ctx);
|
|
5541
|
+
const coveredIds = collectCoveredMessageIds(state);
|
|
5542
|
+
const systemPromptTokens = estimateTextTokens2(getSystemPromptText(ctx) ?? "");
|
|
5543
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
5544
|
+
const sessionTokens = ctx.getContextUsage?.()?.tokens ?? null;
|
|
5545
|
+
const tokenCount = sentTokens;
|
|
5546
|
+
debug.event("context-in", {
|
|
5547
|
+
sid,
|
|
5548
|
+
mode,
|
|
5549
|
+
streamLen,
|
|
5550
|
+
coreMsgs: coreMessages.length,
|
|
5551
|
+
tokenCount,
|
|
5552
|
+
sessionTokens,
|
|
5553
|
+
limit: config.modelContextLimit,
|
|
5554
|
+
blocksBefore: state.blocks.length,
|
|
5555
|
+
activeBefore: state.blocks.filter((b) => b.active).length
|
|
5556
|
+
});
|
|
5557
|
+
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount });
|
|
5558
|
+
runtime.commitFoldState(ctx, turn.state);
|
|
5559
|
+
logInfo("turn", {
|
|
5560
|
+
sid,
|
|
5561
|
+
inMsgs: coreMessages.length,
|
|
5562
|
+
outMsgs: turn.messages.length,
|
|
5563
|
+
tokens: tokenCount,
|
|
5564
|
+
sessionTokens,
|
|
5565
|
+
pct: config.modelContextLimit > 0 ? Math.round(tokenCount / config.modelContextLimit * 100) : null,
|
|
5566
|
+
limit: config.modelContextLimit,
|
|
5567
|
+
nudge: turn.nudge?.shouldInject ? turn.nudge.breakdown?.emergencyOverride === 1 ? "emergency" : "active" : "idle",
|
|
5568
|
+
nudgeReason: turn.nudge?.reason ?? null,
|
|
5569
|
+
blocks: turn.state.blocks.length,
|
|
5570
|
+
activeBlocks: turn.state.blocks.filter((b) => b.active).length
|
|
5571
|
+
});
|
|
5572
|
+
debug.event("processTurn", {
|
|
5573
|
+
outMsgs: turn.messages.length,
|
|
5574
|
+
summaryMsgs: turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
5575
|
+
prunedMsgs: coreMessages.length - turn.messages.length + turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
5576
|
+
nudgeShouldInject: turn.nudge?.shouldInject ?? false,
|
|
5577
|
+
nudgeReason: turn.nudge?.reason ?? null,
|
|
5578
|
+
nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge, runtime.prompts).voice : null,
|
|
5579
|
+
nudgePct: turn.nudge ? Math.round(turn.nudge.contextUsage * 100) : null,
|
|
5580
|
+
nudgeTier: turn.nudge?.tier ?? null,
|
|
5581
|
+
nudgeCompressibleCount: turn.nudge?.compressibleRanges.length ?? 0,
|
|
5582
|
+
nudgeProtectedCount: turn.nudge?.protectedRanges?.length ?? 0,
|
|
5583
|
+
nothingToCompress: turn.nudge?.reason?.includes("nothing to compress") ?? false,
|
|
5584
|
+
blocksAfter: turn.state.blocks.length,
|
|
5585
|
+
activeAfter: turn.state.blocks.filter((b) => b.active).length
|
|
5586
|
+
});
|
|
5587
|
+
const rebuilt = coreOutToAgentMessages(turn.messages, originalById);
|
|
5588
|
+
debug.event("core-out", {
|
|
5589
|
+
sid,
|
|
5590
|
+
coreOutMsgs: turn.messages.length,
|
|
5591
|
+
originalByIdSize: originalById.size,
|
|
5592
|
+
rebuiltMsgs: rebuilt.length
|
|
5593
|
+
});
|
|
5594
|
+
const debugOn2 = debug.enabled;
|
|
5595
|
+
let nudgeInjected = false;
|
|
5596
|
+
if (turn.nudge?.shouldInject) {
|
|
5597
|
+
const lastUser = [...input].reverse().find((m) => m.role === "user");
|
|
5598
|
+
const tailText = lastUser ? JSON.stringify(lastUser.content ?? "") : "";
|
|
5599
|
+
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
|
|
5600
|
+
if (isFeedbackView) {
|
|
5601
|
+
debug.event("nudge-feedback-skip", { sid: ctx.sessionManager.getSessionId(), msgs: input.length });
|
|
5602
|
+
} else {
|
|
5603
|
+
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
5604
|
+
const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
|
|
5605
|
+
const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
5606
|
+
const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
|
|
5607
|
+
const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
|
|
5608
|
+
if (suppressed) {
|
|
5609
|
+
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
5610
|
+
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
5611
|
+
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
5612
|
+
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
5355
5613
|
} else {
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
5363
|
-
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
5364
|
-
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
5365
|
-
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
5366
|
-
} else {
|
|
5367
|
-
nudgeInjected = true;
|
|
5368
|
-
{
|
|
5369
|
-
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
5370
|
-
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
5371
|
-
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
5372
|
-
const example = top ? `
|
|
5614
|
+
nudgeInjected = true;
|
|
5615
|
+
{
|
|
5616
|
+
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
5617
|
+
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
5618
|
+
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
5619
|
+
const example = top ? `
|
|
5373
5620
|
|
|
5374
5621
|
Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5622
|
+
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example));
|
|
5623
|
+
if (emergency) {
|
|
5624
|
+
logWarn("nudge", { sid: ctx.sessionManager.getSessionId(), event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
|
|
5625
|
+
}
|
|
5626
|
+
if (debugOn2 && ctx.hasUI) {
|
|
5627
|
+
ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
|
|
5381
5628
|
${rendered.text}${example}`);
|
|
5382
|
-
}
|
|
5383
|
-
debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
5384
5629
|
}
|
|
5630
|
+
debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
5385
5631
|
}
|
|
5386
5632
|
}
|
|
5387
5633
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5634
|
+
}
|
|
5635
|
+
dumpContextMessages(rebuilt, {
|
|
5636
|
+
sid,
|
|
5637
|
+
injected: nudgeInjected,
|
|
5638
|
+
emergency: turn.nudge?.breakdown?.emergencyOverride === 1
|
|
5639
|
+
});
|
|
5640
|
+
await checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
5641
|
+
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5642
|
+
});
|
|
5643
|
+
return { rebuilt, nudgeInjected };
|
|
5644
|
+
} catch (e) {
|
|
5645
|
+
logThrow("context", e, { sid, phase: "transform", mode });
|
|
5646
|
+
throw e;
|
|
5647
|
+
} finally {
|
|
5648
|
+
release();
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
function wireContextTransform(pi, runtime) {
|
|
5652
|
+
pi.on("context", async (event, ctx) => {
|
|
5653
|
+
if ((runtime.adapter.transformMode ?? "context") === "provider") {
|
|
5654
|
+
debug.event("context-observer-skip", { sid: ctx.sessionManager.getSessionId(), msgs: event.messages?.length ?? 0 });
|
|
5655
|
+
return void 0;
|
|
5656
|
+
}
|
|
5657
|
+
const result = await transformStream(ctx, runtime, event.messages ?? [], "context");
|
|
5658
|
+
if (!result) return void 0;
|
|
5659
|
+
return { messages: result.rebuilt };
|
|
5660
|
+
});
|
|
5661
|
+
}
|
|
5662
|
+
function wireProviderTransform(pi, runtime) {
|
|
5663
|
+
pi.on("before_provider_request", async (event, ctx) => {
|
|
5664
|
+
if ((runtime.adapter.transformMode ?? "context") !== "provider") return void 0;
|
|
5665
|
+
const payload = event.payload;
|
|
5666
|
+
if (payload === null || typeof payload !== "object" || !Array.isArray(payload.messages)) return void 0;
|
|
5667
|
+
const sid = ctx.sessionManager?.getSessionId?.() ?? "";
|
|
5668
|
+
const fmt2 = detectWireFormat(payload);
|
|
5669
|
+
if (fmt2 === "unknown") {
|
|
5670
|
+
debug.event("provider-transform-unknown-format", { sid });
|
|
5671
|
+
return void 0;
|
|
5672
|
+
}
|
|
5673
|
+
try {
|
|
5674
|
+
const synth = synthesizeStream(payload, fmt2);
|
|
5675
|
+
if (synth.stream.length === 0) return void 0;
|
|
5676
|
+
const result = await transformStream(ctx, runtime, synth.stream, "provider");
|
|
5677
|
+
if (!result) return void 0;
|
|
5678
|
+
const wireOut = rebuildWirePayload(result.rebuilt, payload, synth);
|
|
5679
|
+
const outMsgs = wireOut.messages?.length ?? 0;
|
|
5680
|
+
const inMsgs = payload.messages?.length ?? 0;
|
|
5681
|
+
if (outMsgs !== inMsgs || wireOut !== payload) {
|
|
5682
|
+
logInfo("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudge: result.nudgeInjected ? "injected" : "idle" });
|
|
5683
|
+
}
|
|
5684
|
+
debug.event("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudgeInjected: result.nudgeInjected });
|
|
5685
|
+
return wireOut === payload ? void 0 : wireOut;
|
|
5397
5686
|
} catch (e) {
|
|
5398
|
-
logThrow("
|
|
5399
|
-
|
|
5400
|
-
} finally {
|
|
5401
|
-
release();
|
|
5687
|
+
logThrow("provider-transform", e, { sid, fmt: fmt2 });
|
|
5688
|
+
return void 0;
|
|
5402
5689
|
}
|
|
5403
5690
|
});
|
|
5404
5691
|
}
|