billion-context-omp 0.2.9 → 0.3.0

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/index.js CHANGED
@@ -3005,6 +3005,27 @@ function resolveTransformMode(adapter, model, hostVersion = VERSION) {
3005
3005
  if (api === "openai-completions" && hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) return "provider";
3006
3006
  return "context";
3007
3007
  }
3008
+ function providerDeliveryWarning(adapter, model, hostVersion = VERSION) {
3009
+ if (adapter.transformMode !== "provider") return void 0;
3010
+ const api = model?.api;
3011
+ const dropWarning = (target) => ({
3012
+ key: `drop:${target}`,
3013
+ reason: `host < 17.3.8 drops the before_provider_request replacement on ${target} (fixed upstream pi-ai 17.3.8, can1357/oh-my-pi#8717)`,
3014
+ message: `\u26A0 billion-context-omp: transformMode "provider" is set, but this host (pi-ai < 17.3.8) discards the rewritten payload on ${target} \u2014 compression is NOT applied. Upgrade the host (omp update) or remove the transformMode override.`
3015
+ });
3016
+ if (api === "openai-completions" && !hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) {
3017
+ return dropWarning(api);
3018
+ }
3019
+ if (api === "amazon-bedrock" || api === "cursor") {
3020
+ if (!hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) return dropWarning(api);
3021
+ return {
3022
+ key: `nocodec:${api}`,
3023
+ reason: `${api} honors the replacement from 17.3.8 but its wire body has no codec path yet (issue #83)`,
3024
+ message: `\u26A0 billion-context-omp: transformMode "provider" is set, but the ${api} wire body has no codec path yet (#83) \u2014 compression is NOT applied. Remove the transformMode override to use context mode.`
3025
+ };
3026
+ }
3027
+ return void 0;
3028
+ }
3008
3029
 
3009
3030
  // node_modules/acp-kernel/dist/wire/index.js
3010
3031
  import { createHash } from "crypto";
@@ -3399,6 +3420,22 @@ function toolResultTexts(stream) {
3399
3420
  }
3400
3421
  return results;
3401
3422
  }
3423
+ function lastRejectedCompressPair(stream) {
3424
+ for (let i = stream.length - 1; i >= 0; i--) {
3425
+ const m = stream[i];
3426
+ if (m.role !== "toolResult" || m.toolName !== "compress" || !m.toolCallId) continue;
3427
+ if (!extractText(m.content).includes("No changes applied")) continue;
3428
+ for (let j = stream.length - 1; j >= 0; j--) {
3429
+ if (j === i) continue;
3430
+ const c = stream[j];
3431
+ if (c.role !== "assistant") continue;
3432
+ if (!allToolCalls(c.content).some((call) => call.id === m.toolCallId && compressToolArgs(call))) continue;
3433
+ return [stream[j], stream[i]];
3434
+ }
3435
+ return null;
3436
+ }
3437
+ return null;
3438
+ }
3402
3439
  function findCompressCalls(message) {
3403
3440
  const out = [];
3404
3441
  for (const call of allToolCalls(message.content)) {
@@ -3817,6 +3854,13 @@ function unrepresentableOpenaiMessage(message) {
3817
3854
  if (typeof role !== "string" || !OPENAI_CODEC_ROLES.has(role)) {
3818
3855
  return `openai role ${JSON.stringify(role) ?? "missing"}`;
3819
3856
  }
3857
+ for (const field of ["function_call", "audio", "annotations"]) {
3858
+ if (message[field] !== void 0) {
3859
+ return `openai ${field} field is dropped by the rebuild`;
3860
+ }
3861
+ }
3862
+ const refusal = message.refusal;
3863
+ if (refusal !== null && refusal !== void 0) return "openai refusal content is dropped by the rebuild";
3820
3864
  const content = message.content;
3821
3865
  if (content == null || typeof content === "string") return null;
3822
3866
  if (!Array.isArray(content)) return "content neither string nor part array";
@@ -3833,6 +3877,39 @@ function unrepresentableOpenaiMessage(message) {
3833
3877
  }
3834
3878
  return null;
3835
3879
  }
3880
+ function restoreOpenaiWireFidelity(originalMessages, rebuilt) {
3881
+ const detailsByCall = /* @__PURE__ */ new Map();
3882
+ for (const message of originalMessages) {
3883
+ if (message === null || typeof message !== "object") continue;
3884
+ const calls = message.tool_calls;
3885
+ const details = message.reasoning_details;
3886
+ if (!Array.isArray(calls) || !Array.isArray(details) || details.length === 0) continue;
3887
+ for (const call of calls) {
3888
+ const id = call?.id;
3889
+ if (typeof id === "string" && !detailsByCall.has(id)) detailsByCall.set(id, details);
3890
+ }
3891
+ }
3892
+ return rebuilt.map((message) => {
3893
+ if (message === null || typeof message !== "object") return message;
3894
+ const m = message;
3895
+ if (m.role !== "assistant") return message;
3896
+ const calls = Array.isArray(m.tool_calls) ? m.tool_calls : [];
3897
+ const attached = [];
3898
+ for (const call of calls) {
3899
+ const id = call?.id;
3900
+ if (typeof id !== "string") continue;
3901
+ const d = detailsByCall.get(id);
3902
+ if (d) attached.push(...d);
3903
+ }
3904
+ const hasReasoningField = m.reasoning_content !== void 0 || m.reasoning !== void 0 || m.reasoning_text !== void 0;
3905
+ const emptyContent = m.content === null && (calls.length > 0 || hasReasoningField);
3906
+ if (attached.length === 0 && !emptyContent) return message;
3907
+ const out = { ...m };
3908
+ if (emptyContent) out.content = "";
3909
+ if (attached.length > 0) out.reasoning_details = attached;
3910
+ return out;
3911
+ });
3912
+ }
3836
3913
  var renderRefsAll = createRenderRefsNode("all");
3837
3914
  function applyWireTagContract(msgs, state, scope) {
3838
3915
  const stripAssistantTags = (m) => m.contentType === "text" && m.role === "assistant" ? { ...m, text: stripRefTag(m.text ?? "") } : m;
@@ -3873,6 +3950,18 @@ function toolResultTextsCore(msgs) {
3873
3950
  }
3874
3951
  return results;
3875
3952
  }
3953
+ function lastRejectedPairCore(msgs) {
3954
+ const names = toolCallNames(msgs);
3955
+ for (let i = msgs.length - 1; i >= 0; i--) {
3956
+ const m = msgs[i];
3957
+ if (m.contentType !== "tool-result" || !m.toolCallId) continue;
3958
+ if (!(m.text ?? "").includes("No changes applied")) continue;
3959
+ if (names.get(m.toolCallId) !== "compress") continue;
3960
+ const call = msgs.find((c) => c.contentType === "tool-call" && c.toolCallId === m.toolCallId);
3961
+ return call ? [call, m] : null;
3962
+ }
3963
+ return null;
3964
+ }
3876
3965
  function findCompressCallsCore(msg) {
3877
3966
  if (msg.contentType !== "tool-call" || !msg.toolName) return [];
3878
3967
  const args = compressToolArgs({ name: msg.toolName, arguments: msg.text });
@@ -3991,22 +4080,23 @@ function viewToCoreStream(view, systemText) {
3991
4080
  if (text) messages.push({ role: "user", content: text });
3992
4081
  } else if (m.role === "assistant") {
3993
4082
  const blocks = Array.isArray(m.content) ? m.content : [];
3994
- const calls = blocks.filter(
3995
- (b) => b !== null && typeof b === "object" && b.type === "toolCall"
3996
- );
4083
+ const typed = blocks;
4084
+ const calls = typed.filter((b) => b !== null && typeof b === "object" && b.type === "toolCall");
4085
+ const reasoning = typed.filter((b) => b !== null && typeof b === "object" && b.type === "thinking" && typeof b.thinking === "string" && b.thinking.trim().length > 0).map((b) => b.thinking).join("\n");
3997
4086
  const text = extractViewText(m.content);
3998
4087
  if (calls.length > 0) {
3999
4088
  messages.push({
4000
4089
  role: "assistant",
4001
4090
  content: text,
4091
+ ...reasoning ? { reasoning_content: reasoning } : {},
4002
4092
  tool_calls: calls.map((c) => ({
4003
4093
  id: c.id,
4004
4094
  type: "function",
4005
4095
  function: { name: c.name ?? "", arguments: JSON.stringify(c.arguments ?? {}) }
4006
4096
  }))
4007
4097
  });
4008
- } else if (text) {
4009
- messages.push({ role: "assistant", content: text });
4098
+ } else if (text || reasoning) {
4099
+ messages.push({ role: "assistant", content: text, ...reasoning ? { reasoning_content: reasoning } : {} });
4010
4100
  }
4011
4101
  } else if (m.role === "toolResult") {
4012
4102
  messages.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: extractViewText(m.content) });
@@ -4027,20 +4117,27 @@ function viewToAnthropicCore(view) {
4027
4117
  if (text) messages.push({ role: "user", content: [{ type: "text", text }] });
4028
4118
  } else if (m.role === "assistant") {
4029
4119
  const blocks = Array.isArray(m.content) ? m.content : [];
4030
- const calls = blocks.filter(
4031
- (b) => b !== null && typeof b === "object" && b.type === "toolCall"
4032
- );
4033
- const text = extractViewText(m.content);
4120
+ const typed = blocks;
4034
4121
  const content = [];
4035
- if (text) content.push({ type: "text", text });
4036
- for (const c of calls) {
4037
- let input = {};
4038
- try {
4039
- input = c.arguments && typeof c.arguments === "object" ? c.arguments : JSON.parse(JSON.stringify(c.arguments ?? {}));
4040
- } catch {
4041
- input = {};
4122
+ for (const b of typed) {
4123
+ if (b === null || typeof b !== "object") continue;
4124
+ if (b.type === "thinking" && typeof b.thinking === "string" && b.thinking.trim().length > 0) {
4125
+ content.push({
4126
+ type: "thinking",
4127
+ thinking: b.thinking,
4128
+ ...typeof b.thinkingSignature === "string" && b.thinkingSignature ? { signature: b.thinkingSignature } : {}
4129
+ });
4130
+ } else if (b.type === "text" && typeof b.text === "string" && stripRefTag(b.text).trim().length > 0) {
4131
+ content.push({ type: "text", text: stripRefTag(b.text) });
4132
+ } else if (b.type === "toolCall") {
4133
+ let input = {};
4134
+ try {
4135
+ input = b.arguments && typeof b.arguments === "object" ? b.arguments : JSON.parse(JSON.stringify(b.arguments ?? {}));
4136
+ } catch {
4137
+ input = {};
4138
+ }
4139
+ content.push({ type: "tool_use", id: b.id, name: b.name ?? "", input });
4042
4140
  }
4043
- content.push({ type: "tool_use", id: c.id, name: c.name ?? "", input });
4044
4141
  }
4045
4142
  if (content.length > 0) messages.push({ role: "assistant", content });
4046
4143
  } else if (m.role === "toolResult") {
@@ -4353,6 +4450,9 @@ ${acp}`);
4353
4450
  slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
4354
4451
  return slot.rejectStreak;
4355
4452
  }
4453
+ function rejectStreakFor(ctx) {
4454
+ return slotForMode(ctx, sidOf(ctx)).rejectStreak;
4455
+ }
4356
4456
  return {
4357
4457
  core,
4358
4458
  get adapter() {
@@ -4375,6 +4475,7 @@ ${acp}`);
4375
4475
  commitFoldState,
4376
4476
  recordRebuiltOutput,
4377
4477
  noteCompressOutcome,
4478
+ rejectStreakFor,
4378
4479
  forgetSession,
4379
4480
  primeFold,
4380
4481
  acquireLock
@@ -5562,7 +5663,7 @@ async function statusReport(runtime, ctx) {
5562
5663
  const coveredIds = collectCoveredMessageIds(state);
5563
5664
  const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
5564
5665
  const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
5565
- const versionStr = "0.2.9" ? `billion-context-omp@${"0.2.9"}` : void 0;
5666
+ const versionStr = "0.3.0" ? `billion-context-omp@${"0.3.0"}` : void 0;
5566
5667
  return buildStatusPanel({
5567
5668
  version: versionStr,
5568
5669
  tokenCount: sessionTokens,
@@ -5727,7 +5828,7 @@ function stampAndDetect(selfPath, version, now = Date.now()) {
5727
5828
  }
5728
5829
 
5729
5830
  // src/update.ts
5730
- import { readFile, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
5831
+ import { readFile, writeFile as writeFile2, mkdir as mkdir2, access } from "fs/promises";
5731
5832
  import { join as join4, dirname as dirname3 } from "path";
5732
5833
  import { fileURLToPath } from "url";
5733
5834
  import { execFile } from "child_process";
@@ -5793,43 +5894,112 @@ async function findExtensionDir() {
5793
5894
  dir = parent;
5794
5895
  }
5795
5896
  }
5796
- async function autoInstallLatest(latest) {
5897
+ var runNpmImpl = (args, cwd) => new Promise((resolve2) => {
5898
+ execFile(
5899
+ "npm",
5900
+ args,
5901
+ { cwd, timeout: 6e4, shell: process.platform === "win32", maxBuffer: 4 * 1024 * 1024 },
5902
+ (err, stdout, stderr) => {
5903
+ if (err) logWarn("update", { event: "install-exec-failed", error: err.message, stderr: String(stderr).slice(0, 300) });
5904
+ resolve2({ code: err ? 1 : 0, stdout: String(stdout), stderr: String(stderr) });
5905
+ }
5906
+ );
5907
+ });
5908
+ var runNpm = runNpmImpl;
5909
+ var runNodeImpl = (args) => new Promise((resolve2) => {
5910
+ execFile(
5911
+ process.execPath,
5912
+ args,
5913
+ { timeout: 15e3, maxBuffer: 4 * 1024 * 1024, shell: false },
5914
+ (err, stdout, stderr) => {
5915
+ resolve2({ code: err ? 1 : 0, stdout: String(stdout), stderr: String(stderr) });
5916
+ }
5917
+ );
5918
+ });
5919
+ var runNode = runNodeImpl;
5920
+ function declaredEntries(pkg) {
5921
+ const entries = /* @__PURE__ */ new Set();
5922
+ for (const ext of pkg.omp?.extensions ?? []) {
5923
+ if (typeof ext === "string") entries.add(ext);
5924
+ }
5925
+ const dot = pkg.exports?.["."];
5926
+ if (typeof dot === "string") entries.add(dot);
5927
+ else if (dot && typeof dot.import === "string") entries.add(dot.import);
5928
+ if (typeof pkg.main === "string") entries.add(pkg.main);
5929
+ return [...entries];
5930
+ }
5931
+ async function verifyInstall(npmDir, latest) {
5932
+ const extDir = join4(npmDir, "node_modules", PACKAGE_NAME);
5933
+ const pkg = await readPackageJson(join4(extDir, "package.json"));
5934
+ if (!pkg) return { ok: false, reason: "package-json-missing" };
5935
+ if (pkg.version !== latest) return { ok: false, reason: `version-mismatch:${pkg.version ?? "none"}` };
5936
+ const entries = declaredEntries(pkg);
5937
+ if (entries.length === 0) return { ok: false, reason: "no-entry-declared" };
5938
+ for (const rel of entries) {
5939
+ try {
5940
+ await access(join4(extDir, rel));
5941
+ } catch {
5942
+ return { ok: false, reason: `entry-missing:${rel}` };
5943
+ }
5944
+ }
5945
+ const smokeEntry = pkg.omp?.extensions?.[0] ?? entries[0];
5946
+ if (!smokeEntry) return { ok: false, reason: "no-entry-declared" };
5947
+ const smoke = `const{pathToFileURL}=require("node:url");import(pathToFileURL(process.argv[1]).href).then(()=>{},(e)=>{console.error(e&&e.stack||e);process.exit(1)});`;
5948
+ const r = await runNode(["-e", smoke, join4(extDir, smokeEntry)]);
5949
+ if (r.code !== 0) return { ok: false, reason: `entry-import-failed:${r.stderr.slice(0, 500)}` };
5950
+ return { ok: true };
5951
+ }
5952
+ var installArgs = (version) => [
5953
+ "install",
5954
+ `${PACKAGE_NAME}@${version}`,
5955
+ // --no-save: the auto-updater must never mutate the host's package.json
5956
+ // or lockfile.
5957
+ "--no-save",
5958
+ "--silent",
5959
+ "--no-audit",
5960
+ "--no-fund"
5961
+ ];
5962
+ async function autoInstallLatest(latest, npmDirOverride) {
5797
5963
  if (!SEMVER_RE.test(latest)) {
5798
5964
  logWarn("update", { event: "install-abort", reason: "semver", latest });
5799
- return false;
5965
+ return "failed";
5800
5966
  }
5801
5967
  const extDir = await findExtensionDir();
5802
5968
  if (!extDir) {
5803
5969
  logWarn("update", { event: "install-abort", reason: "extdir-not-found", moduleUrl: import.meta.url });
5804
- return false;
5970
+ return "failed";
5805
5971
  }
5806
- const npmDir = findNpmRoot(extDir);
5972
+ const npmDir = npmDirOverride ?? findNpmRoot(extDir);
5807
5973
  if (!npmDir) {
5808
5974
  logWarn("update", { event: "install-abort", reason: "npmroot-not-found", extDir });
5809
- return false;
5975
+ return "failed";
5810
5976
  }
5977
+ const prevVersion = (await readPackageJson(join4(npmDir, "node_modules", PACKAGE_NAME, "package.json")))?.version ?? "0.3.0";
5811
5978
  try {
5812
5979
  const keepAlive = setInterval(() => {
5813
5980
  }, 500);
5814
5981
  try {
5815
- const code = await new Promise((resolve2) => {
5816
- execFile(
5817
- "npm",
5818
- ["install", `${PACKAGE_NAME}@${latest}`, "--silent", "--no-audit", "--no-fund"],
5819
- { cwd: npmDir, timeout: 6e4, shell: process.platform === "win32" },
5820
- (err, _stdout, stderr) => {
5821
- if (err) logWarn("update", { event: "install-exec-failed", error: err.message, stderr: String(stderr).slice(0, 300) });
5822
- resolve2(err ? 1 : 0);
5823
- }
5824
- );
5825
- });
5826
- return code === 0;
5982
+ const res = await runNpm(installArgs(latest), npmDir);
5983
+ if (res.code !== 0) {
5984
+ logWarn("update", { event: "auto-install-failed", latest, stderr: res.stderr.slice(0, 2e3) });
5985
+ return "failed";
5986
+ }
5987
+ const verify = await verifyInstall(npmDir, latest);
5988
+ if (!verify.ok) {
5989
+ const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.3.0";
5990
+ logWarn("update", { event: "auto-install-verify-failed", latest, reason: verify.reason, rollbackTo });
5991
+ const rb = await runNpm(installArgs(rollbackTo), npmDir);
5992
+ logInfo("update", { event: "rollback", from: latest, to: rollbackTo, ok: rb.code === 0 });
5993
+ return "rolled-back";
5994
+ }
5995
+ logInfo("update", { event: "auto-installed", from: prevVersion, to: latest });
5996
+ return "ok";
5827
5997
  } finally {
5828
5998
  clearInterval(keepAlive);
5829
5999
  }
5830
6000
  } catch (e) {
5831
6001
  logWarn("update", { event: "install-throw", error: e instanceof Error ? e.message : String(e) });
5832
- return false;
6002
+ return "failed";
5833
6003
  }
5834
6004
  }
5835
6005
  async function checkForUpdate(autoUpdate, notify) {
@@ -5856,7 +6026,7 @@ async function checkForUpdate(autoUpdate, notify) {
5856
6026
  const data = await res.json();
5857
6027
  const latest = data.version;
5858
6028
  if (!latest) return;
5859
- const current = runtimeVersion ?? "0.2.9";
6029
+ const current = runtimeVersion ?? "0.3.0";
5860
6030
  const hasUpdate = isNewer(latest, current);
5861
6031
  debug.event("update-check", {
5862
6032
  current,
@@ -5865,16 +6035,16 @@ async function checkForUpdate(autoUpdate, notify) {
5865
6035
  });
5866
6036
  logInfo("update", { event: "check", current, latest, hasUpdate });
5867
6037
  if (hasUpdate) {
5868
- const installed = await autoInstallLatest(latest);
5869
- if (installed && notify) {
6038
+ const outcome = await autoInstallLatest(latest);
6039
+ if (!notify) return;
6040
+ if (outcome === "ok") {
6041
+ notify(`\x1B[32m\u2714 ACP auto-updated ${current} \u2192 ${latest}. Restart omp to finish.\x1B[0m`);
6042
+ } else if (outcome === "rolled-back") {
5870
6043
  notify(
5871
- `\x1B[32m\u2714 ACP auto-updated ${current} \u2192 ${latest}. Restart omp to finish.\x1B[0m`
5872
- );
5873
- logInfo("update", { event: "auto-installed", from: current, to: latest });
5874
- } else if (!installed && notify) {
5875
- notify(
5876
- `${PACKAGE_NAME} ${latest} available (you have ${current}). Run: omp install ${PACKAGE_NAME}@latest`
6044
+ `\x1B[33m\u26A0 ${PACKAGE_NAME} ${latest} failed verification and was rolled back. Keeping ${current}. A later release will auto-update.\x1B[0m`
5877
6045
  );
6046
+ } else {
6047
+ notify(`${PACKAGE_NAME} ${latest} available (you have ${current}). Run: omp install ${PACKAGE_NAME}@latest`);
5878
6048
  }
5879
6049
  }
5880
6050
  } catch (e) {
@@ -6112,10 +6282,11 @@ function applyUserConfig(adapter, user) {
6112
6282
  function createAcpExtension(adapter = {}) {
6113
6283
  return (pi) => {
6114
6284
  const runtime = createRuntime(adapter);
6285
+ const warnDelivery = makeDeliveryWarner();
6115
6286
  wireSessionLifecycle(pi, runtime);
6116
- wireContextTransform(pi, runtime);
6287
+ wireContextTransform(pi, runtime, warnDelivery);
6117
6288
  wireSystemPrompt(pi, runtime);
6118
- wireProviderTransform(pi, runtime);
6289
+ wireProviderTransform(pi, runtime, warnDelivery);
6119
6290
  wireProviderDebug(pi);
6120
6291
  wireToolGuardrails(pi, runtime);
6121
6292
  pi.registerTool(makeCompressTool(runtime));
@@ -6129,26 +6300,14 @@ function createAcpExtension(adapter = {}) {
6129
6300
  }
6130
6301
  var index_default = createAcpExtension();
6131
6302
  function wireSessionLifecycle(pi, runtime) {
6132
- pi.on("session_start", async (_event, ctx) => {
6303
+ const prepareAndPrime = async (ctx, phase) => {
6133
6304
  const sid = ctx.sessionManager.getSessionId();
6134
- logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.9" : null });
6135
- const selfPath = import.meta.url;
6136
- const conflict = stampAndDetect(selfPath, true ? "0.2.9" : null);
6137
- if (conflict) {
6138
- logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
6139
- try {
6140
- if (ctx.hasUI) {
6141
- ctx.ui.notify(`\u26A0 billion-context-omp loaded TWICE (also from ${conflict.path}). Two instances corrupt compression state \u2014 remove one (check 'omp plugin list' vs config.yml extensions).`);
6142
- }
6143
- } catch {
6144
- }
6145
- }
6146
6305
  try {
6147
6306
  const user = await loadUserConfig(ctx.cwd);
6148
6307
  runtime.setAdapter(applyUserConfig(runtime.adapter, user));
6149
6308
  if (runtime.adapter.debug !== void 0) setDebugEnabled(runtime.adapter.debug);
6150
6309
  } catch (e) {
6151
- logThrow("config", e, { sid, phase: "session_start" });
6310
+ logThrow("config", e, { sid, phase });
6152
6311
  }
6153
6312
  try {
6154
6313
  runtime.setPrompts(resolvePrompts(runtime.adapter.prompts, { acknowledgeRisk: runtime.adapter.acknowledgePromptsRisk === true }));
@@ -6157,10 +6316,35 @@ function wireSessionLifecycle(pi, runtime) {
6157
6316
  runtime.setPrompts(defaultPrompts);
6158
6317
  }
6159
6318
  runtime.primeFold(ctx);
6319
+ };
6320
+ pi.on("session_start", async (_event, ctx) => {
6321
+ const sid = ctx.sessionManager.getSessionId();
6322
+ const modelInfo = ctx.model;
6323
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.3.0" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
6324
+ const selfPath = import.meta.url;
6325
+ const conflict = stampAndDetect(selfPath, true ? "0.3.0" : null);
6326
+ if (conflict) {
6327
+ logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
6328
+ try {
6329
+ if (ctx.hasUI) {
6330
+ ctx.ui.notify(`\u26A0 billion-context-omp loaded TWICE (also from ${conflict.path}). Two instances corrupt compression state \u2014 remove one (check 'omp plugin list' vs config.yml extensions).`);
6331
+ }
6332
+ } catch {
6333
+ }
6334
+ }
6335
+ await prepareAndPrime(ctx, "session_start");
6160
6336
  void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
6161
6337
  if (ctx.hasUI) ctx.ui.notify(msg);
6162
6338
  }).catch((e) => logThrow("update", e, { sid, phase: "session_start" }));
6163
6339
  });
6340
+ pi.on("session_switch", async (event, ctx) => {
6341
+ logInfo("session", { event: "switch", sid: ctx.sessionManager.getSessionId(), reason: event.reason, previous: event.previousSessionFile ?? null });
6342
+ await prepareAndPrime(ctx, "session_switch");
6343
+ });
6344
+ pi.on("session_branch", async (_event, ctx) => {
6345
+ logInfo("session", { event: "branch", sid: ctx.sessionManager.getSessionId() });
6346
+ await prepareAndPrime(ctx, "session_branch");
6347
+ });
6164
6348
  pi.on("session_shutdown", (_event, ctx) => {
6165
6349
  try {
6166
6350
  runtime.forgetSession(ctx.sessionManager.getSessionId());
@@ -6204,6 +6388,7 @@ async function transformStream(ctx, runtime, input, mode) {
6204
6388
  runtime.commitFoldState(ctx, turn.state);
6205
6389
  logInfo("turn", {
6206
6390
  sid,
6391
+ model: ctx.model?.id ?? null,
6207
6392
  inMsgs: coreMessages.length,
6208
6393
  outMsgs: turn.messages.length,
6209
6394
  tokens: tokenCount,
@@ -6231,6 +6416,11 @@ async function transformStream(ctx, runtime, input, mode) {
6231
6416
  activeAfter: turn.state.blocks.filter((b) => b.active).length
6232
6417
  });
6233
6418
  const rebuilt = coreOutToAgentMessages(turn.messages, originalById);
6419
+ const rejectedPair = lastRejectedCompressPair(input);
6420
+ if (rejectedPair) {
6421
+ rebuilt.push(...rejectedPair);
6422
+ debug.event("rejected-pair-visible", { sid, callId: rejectedPair[1].toolCallId ?? null });
6423
+ }
6234
6424
  debug.event("core-out", {
6235
6425
  sid,
6236
6426
  coreOutMsgs: turn.messages.length,
@@ -6242,38 +6432,48 @@ async function transformStream(ctx, runtime, input, mode) {
6242
6432
  if (turn.nudge?.shouldInject) {
6243
6433
  const lastUser = [...input].reverse().find((m) => m.role === "user");
6244
6434
  const tailText = lastUser ? JSON.stringify(lastUser.content ?? "") : "";
6245
- const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
6435
+ const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached") || tailText.includes("compress calls were rejected in a row");
6246
6436
  if (isFeedbackView) {
6247
6437
  debug.event("nudge-feedback-skip", { sid: ctx.sessionManager.getSessionId(), msgs: input.length });
6248
6438
  } else {
6249
6439
  const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
6440
+ const rejectStreak = runtime.rejectStreakFor(ctx);
6250
6441
  const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
6251
- const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
6252
- const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
6253
- const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
6254
- if (suppressed) {
6255
- turn.state.nudge.lastNudgeShownTokens = prevShown;
6442
+ if (rejectStreak >= LOOP_GUARD_STOP) {
6443
+ turn.state.nudge.lastNudgeShownTokens = epochReset ? 0 : preTurnNudgeShownTokens;
6256
6444
  turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
6257
- logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6258
- debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6259
- } else {
6260
6445
  nudgeInjected = true;
6261
- {
6262
- turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
6263
- const rendered = renderNudgeText(turn.nudge, runtime.prompts);
6264
- const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
6265
- const example = top ? `
6446
+ rebuilt.push(holdMessage(rejectStreak));
6447
+ logInfo("nudge", { sid, event: "hold-injected", streak: rejectStreak, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6448
+ debug.event("nudge-hold", { sid, streak: rejectStreak });
6449
+ } else {
6450
+ const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
6451
+ const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
6452
+ const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
6453
+ if (suppressed) {
6454
+ turn.state.nudge.lastNudgeShownTokens = prevShown;
6455
+ turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
6456
+ logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6457
+ debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6458
+ } else {
6459
+ nudgeInjected = true;
6460
+ {
6461
+ turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
6462
+ const rendered = renderNudgeText(turn.nudge, runtime.prompts);
6463
+ const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
6464
+ const example = top ? `
6266
6465
 
6267
6466
  Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
6268
- rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example));
6269
- if (emergency) {
6270
- logWarn("nudge", { sid: ctx.sessionManager.getSessionId(), event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
6271
- }
6272
- if (debugOn2 && ctx.hasUI) {
6273
- ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
6467
+ rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example));
6468
+ if (emergency) {
6469
+ logWarn("nudge", { sid: ctx.sessionManager.getSessionId(), event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
6470
+ }
6471
+ if (debugOn2 && ctx.hasUI) {
6472
+ ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
6274
6473
  ${rendered.text}${example}`);
6474
+ }
6475
+ debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
6275
6476
  }
6276
- debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
6277
6477
  }
6278
6478
  }
6279
6479
  }
@@ -6298,10 +6498,13 @@ ${rendered.text}${example}`);
6298
6498
  }).catch((e) => logThrow("update", e, { sid, phase: "context" }));
6299
6499
  return result;
6300
6500
  }
6301
- function wireContextTransform(pi, runtime) {
6501
+ function wireContextTransform(pi, runtime, warnDelivery) {
6302
6502
  pi.on("context", async (event, ctx) => {
6303
6503
  if (resolveTransformMode(runtime.adapter, ctx.model) === "provider") {
6304
- debug.event("context-observer-skip", { sid: ctx.sessionManager.getSessionId(), msgs: event.messages?.length ?? 0 });
6504
+ const sid = ctx.sessionManager.getSessionId();
6505
+ debug.event("context-observer-skip", { sid, msgs: event.messages?.length ?? 0 });
6506
+ const warning = providerDeliveryWarning(runtime.adapter, ctx.model);
6507
+ if (warning) warnDelivery(ctx, sid, warning);
6305
6508
  return void 0;
6306
6509
  }
6307
6510
  const result = await transformStream(ctx, runtime, event.messages ?? [], "context");
@@ -6309,7 +6512,20 @@ function wireContextTransform(pi, runtime) {
6309
6512
  return { messages: result.rebuilt };
6310
6513
  });
6311
6514
  }
6312
- function wireProviderTransform(pi, runtime) {
6515
+ function makeDeliveryWarner() {
6516
+ const warned = /* @__PURE__ */ new Set();
6517
+ return (ctx, sid, warning) => {
6518
+ const dedup = `${sid}:${warning.key}`;
6519
+ if (warned.has(dedup)) return;
6520
+ warned.add(dedup);
6521
+ logWarn("provider-transform", { sid, event: "undelivered", reason: warning.reason });
6522
+ try {
6523
+ if (ctx.hasUI) ctx.ui.notify(warning.message);
6524
+ } catch {
6525
+ }
6526
+ };
6527
+ }
6528
+ function wireProviderTransform(pi, runtime, warnDelivery) {
6313
6529
  pi.on("before_provider_request", async (event, ctx) => {
6314
6530
  if (resolveTransformMode(runtime.adapter, ctx.model) !== "provider") return void 0;
6315
6531
  const payload = event.payload;
@@ -6318,6 +6534,13 @@ function wireProviderTransform(pi, runtime) {
6318
6534
  const fmt2 = detectProviderWireFormat(payload);
6319
6535
  if (fmt2 === null) {
6320
6536
  debug.event("provider-transform-unknown-format", { sid });
6537
+ if (runtime.adapter.transformMode === "provider") {
6538
+ warnDelivery(ctx, sid, {
6539
+ key: "unknown-wire-format",
6540
+ reason: "explicit provider mode but the wire body has no codec path (unknown format) \u2014 payload passes through",
6541
+ message: '\u26A0 billion-context-omp: transformMode "provider" is set, but this wire body has no codec path \u2014 compression is NOT applied here. Remove the override to use context mode.'
6542
+ });
6543
+ }
6321
6544
  return void 0;
6322
6545
  }
6323
6546
  const representable = payloadRepresentable(payload, fmt2);
@@ -6331,13 +6554,15 @@ function wireProviderTransform(pi, runtime) {
6331
6554
  if (msgs.length === 0) return void 0;
6332
6555
  const result = await transformStreamCore(ctx, runtime, msgs, fmt2);
6333
6556
  if (!result) return void 0;
6334
- const outMsgs = coreToPayloadMessages(result.coreOut, fmt2, cacheControls).length;
6335
- const inMsgs = payload.messages?.length ?? 0;
6557
+ const inMessages = payload.messages ?? [];
6558
+ const rebuilt = fmt2 === "openai" ? restoreOpenaiWireFidelity(inMessages, coreToPayloadMessages(result.coreOut, fmt2, cacheControls)) : coreToPayloadMessages(result.coreOut, fmt2, cacheControls);
6559
+ const outMsgs = rebuilt.length;
6560
+ const inMsgs = inMessages.length;
6336
6561
  if (outMsgs !== inMsgs) {
6337
6562
  logInfo("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudge: result.nudgeInjected ? "injected" : "idle" });
6338
6563
  }
6339
6564
  debug.event("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudgeInjected: result.nudgeInjected });
6340
- return { ...payload, messages: coreToPayloadMessages(result.coreOut, fmt2, cacheControls) };
6565
+ return { ...payload, messages: rebuilt };
6341
6566
  } catch (e) {
6342
6567
  logThrow("provider-transform", e, { sid, fmt: fmt2 });
6343
6568
  return void 0;
@@ -6411,42 +6636,57 @@ async function transformStreamCore(ctx, runtime, wireMsgs, fmt2) {
6411
6636
  turn.state,
6412
6637
  { config, tokenCount }
6413
6638
  );
6639
+ const rejectedPair = lastRejectedPairCore(wireMsgs);
6640
+ if (rejectedPair) {
6641
+ coreOut.push(...rejectedPair);
6642
+ debug.event("rejected-pair-visible", { sid, space: "core", callId: rejectedPair[1]?.toolCallId ?? null });
6643
+ }
6414
6644
  let nudgeInjected = false;
6415
6645
  if (turn.nudge?.shouldInject) {
6416
6646
  const lastUser = [...wireMsgs].reverse().find((m) => m.role === "user");
6417
6647
  const tailText = lastUser ? lastUser.text ?? "" : "";
6418
- const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
6648
+ const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached") || tailText.includes("compress calls were rejected in a row");
6419
6649
  if (isFeedbackView) {
6420
6650
  debug.event("nudge-feedback-skip", { sid, msgs: wireMsgs.length });
6421
6651
  } else {
6422
6652
  const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
6423
6653
  const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
6424
- const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
6425
- const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
6426
- const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
6427
- if (suppressed) {
6428
- turn.state.nudge.lastNudgeShownTokens = prevShown;
6654
+ const rejectStreak = runtime.rejectStreakFor(ctx);
6655
+ if (rejectStreak >= LOOP_GUARD_STOP) {
6656
+ turn.state.nudge.lastNudgeShownTokens = epochReset ? 0 : preTurnNudgeShownTokens;
6429
6657
  turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
6430
- logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6431
- debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6432
- } else {
6433
6658
  nudgeInjected = true;
6434
- turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
6435
- const rendered = renderNudgeText(turn.nudge, runtime.prompts);
6436
- const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
6437
- const example = top ? `
6659
+ coreOut.push({ id: `acp_hold_${Date.now()}`, role: "user", contentType: "text", text: holdText(rejectStreak) });
6660
+ logInfo("nudge", { sid, event: "hold-injected", streak: rejectStreak, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6661
+ debug.event("nudge-hold", { sid, streak: rejectStreak });
6662
+ } else {
6663
+ const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
6664
+ const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
6665
+ const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
6666
+ if (suppressed) {
6667
+ turn.state.nudge.lastNudgeShownTokens = prevShown;
6668
+ turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
6669
+ logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6670
+ debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6671
+ } else {
6672
+ nudgeInjected = true;
6673
+ turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
6674
+ const rendered = renderNudgeText(turn.nudge, runtime.prompts);
6675
+ const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
6676
+ const example = top ? `
6438
6677
 
6439
6678
  Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
6440
- if (emergency) {
6441
- logWarn("nudge", { sid, event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
6442
- }
6443
- const debugOn2 = debug.enabled;
6444
- if (debugOn2 && ctx.hasUI) {
6445
- ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
6679
+ if (emergency) {
6680
+ logWarn("nudge", { sid, event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
6681
+ }
6682
+ const debugOn2 = debug.enabled;
6683
+ if (debugOn2 && ctx.hasUI) {
6684
+ ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
6446
6685
  ${rendered.text}${example}`);
6686
+ }
6687
+ debug.event("nudge-injected", { sid, voice: rendered.voice, channels: ["wire", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
6688
+ coreOut.push({ id: `acp_nudge_${Date.now()}`, role: "user", contentType: "text", text: nudgeText(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example) });
6447
6689
  }
6448
- debug.event("nudge-injected", { sid, voice: rendered.voice, channels: ["wire", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
6449
- coreOut.push({ id: `acp_nudge_${Date.now()}`, role: "user", contentType: "text", text: nudgeText(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example) });
6450
6690
  }
6451
6691
  }
6452
6692
  }
@@ -6522,6 +6762,16 @@ function nudgeMessage(nudge, blocks, prompts, example) {
6522
6762
  timestamp: Date.now()
6523
6763
  };
6524
6764
  }
6765
+ function holdText(streak) {
6766
+ return `[ACP hold] Your last ${streak} compress calls were rejected in a row. Do NOT call compress again now and do NOT retry the same range \u2014 the compress reminder is suspended until context actually changes. Continue the actual task. If context still needs relief, run acp_status first and target ONLY a range that meets the minimum size.`;
6767
+ }
6768
+ function holdMessage(streak) {
6769
+ return {
6770
+ role: "user",
6771
+ content: [{ type: "text", text: holdText(streak) }],
6772
+ timestamp: Date.now()
6773
+ };
6774
+ }
6525
6775
  export {
6526
6776
  createAcpExtension,
6527
6777
  index_default as default