fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(context-pivot): preflight native compaction history - #320

Closed
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight
Closed

fix(context-pivot): preflight native compaction history#320
testikun wants to merge 2 commits into
openpi-dev:mainfrom
testikun:codex/issue-24-compaction-preflight

Conversation

@testikun

Copy link
Copy Markdown
Contributor

Problem

Issue #24 describes a false-positive context-pivot path: OpenPI's 30k context-usage gate can pass when the session has no discardable conversation history, after which native Pi compaction fails with Nothing to compact (session too small).

Value

The pivot is now fail-closed before starting asynchronous compaction when Pi's public compaction cut-point logic finds no history to summarize. Users get the existing actionable /sessions / new Session guidance immediately, instead of a misleading “started” state or an unavailable follow-up tool request.

Approach

  • Add a small preflight built from Pi's public findCutPoint, sessionEntryToContextMessages, and DEFAULT_COMPACTION_SETTINGS exports.
  • Check the current branch before enabling or executing context_pivot; malformed branches and missing kept-entry IDs fail closed.
  • Apply the same check to the /context-pivot command path.
  • Keep ctx.compact() and its native error callback unchanged as the race-safe final guard; no second compressor, persistence format, or Session lifecycle is introduced.
  • Add regression coverage for metadata-only context, valid history, existing compaction boundaries, malformed entries, command behavior, and no-start execution.

The installed Pi 0.84.1 package does not actually export prepareCompaction at runtime, despite the internal type/source references, so this PR deliberately avoids a private deep import. The preflight mirrors the public cut-point boundary and documents that the asynchronous native call remains authoritative if the branch changes between checks.

Validation

  • npx --yes bun@1.3.14 run check — passed
  • npx --yes node@24 --test --experimental-strip-types tests/extensions/context-pivot/index.test.ts — 9/9 passed
  • npx --yes node@24 scripts/run-tests.mjs — 1079 passed, 0 failed, 1 skipped; Vitest 30/30 passed
  • git diff --check — passed

Impact

  • User-visible behavior: no-history pivots are rejected with actionable guidance before starting.
  • Model-visible context/tools: context_pivot is not exposed when the current branch has no discardable history.
  • Runtime/lifecycle: native Pi compaction remains the owner; preflight is best-effort and the existing error callback handles races.
  • Persisted config/data: none.
  • Compatibility/risk: low; custom non-default Pi compaction settings are not exposed through the current ExtensionContext, so the check uses Pi's documented defaults and fails closed on malformed input.

@tt-a1itt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请求修改,审查提交:6b14f2b0dbeec940cc5673c70305371184f8a005

这个 PR 想解决 #24 的真实体验问题:总上下文达到 30k,并不意味着存在可丢弃的历史,提前给出解释有价值。不过,当前预检查与 Pi 的实际资格判断不等价:既会误拒绝可压缩的会话,也会在特定旧/自定义摘要边界下误放行。两处复现见行内评论。

处理方向请保持简单:现阶段建议撤回这层基于默认配置的硬性预检查,保留已有的 ctx.compact() 和原生失败的友好提示。不要为此新增 OpenPI 配置读取器、缓存、私有 API 适配或另一套压缩资格算法。拿不到准确的原生资格查询时,让 Pi 做最终判断即可;将来 Pi 提供基于生效配置的公开查询接口,再直接接入。

撤回该层检查会保留“先尝试、再收到无历史提示”的现有限制,也可能使本 PR 不再需要生产代码改动;不必为了保留 PR 而增加替代机制。这不是要求在本 PR 中解决完整上游 eligibility API。

验证:该提交上 bun run check 通过;专项 9/9;完整 Node 测试 1080 通过、1 跳过,Vitest 30/30。额外使用 Node 24 / Pi 0.84.1 源码和内存 Session 对照复现两项问题,未发起真实模型调用。这些是源码/测试证据,不是真实 TUI 验收。

Comment threadextensions/context-pivot/index.ts Outdated
branch,
boundaryStart,
branch.length,
DEFAULT_COMPACTION_SETTINGS.keepRecentTokens,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认保留量不能作为实际压缩资格的硬性否决。

这里固定使用 20k,但 Pi 的 native preparation 使用 SettingsManager 中生效的 keepRecentTokens。已复现:两条 user 消息分别为 12,000 / 20,000 字符,reported context=35,000,实际 keepRecentTokens=4,000;原生 prepareCompaction 有效并会总结第一条消息,本函数却返回 false。agent_start 隐藏 context_pivot,execute 提前拒绝,compactCalls=0。因此原生 onError 无法兜住此假阴性,原本可工作的 pivot 被禁用。

建议收回这个近似结果对工具可见性和执行的硬性拦截,继续交给原生 compact 判断;不需要为此再实现一套配置读取机制。

Comment threadextensions/context-pivot/index.ts Outdated
: cutPoint.firstKeptEntryIndex;
const hasHistory = branch
.slice(boundaryStart, historyEnd)
.some((entry) => sessionEntryToContextMessages(entry).length > 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 已有 compaction 摘要不等于可再次丢弃的普通历史。

sessionEntryToContextMessages(compaction) 会产生上下文消息,但原生 getMessageFromEntryForCompaction 明确排除 compaction 条目;此处及下方 turn-prefix 判断没有排除。已用公开 SessionManager.inMemory() 复现:appendCustomEntry(metadata) → appendCompaction(summary, metadataId, 30000) → appendMessage(user, 100000 字符)。默认配置下本函数返回 true,而 native prepareCompaction 返回 undefined,仍会走到 Nothing to compact。

这是旧/自定义摘要保留边界的情况,不是普通原生摘要主路径。它说明复制这段判定还需要额外维护语义一致性;结合本次简化方向,建议直接撤回近似预检查,而非继续扩展适配层。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Review 结论

请求修改,基于 6b14f2b,2 项已复现问题:完整审查和行内说明

这次建议做减法:撤回默认配置驱动的硬性预检查,保留 Pi 原生 ctx.compact() 和现有友好报错。不要求新增配置读取器、缓存或压缩适配框架。无法准确提前判断时,接受原生调用返回“没有可压缩历史”的限制即可。

@tt-a1i

Copy link
Copy Markdown
Collaborator

Thanks for simplifying this after review. The current head fully reverts the proposed preflight, so the PR now has an empty diff and there is nothing useful to merge. Closing this PR keeps Issue #24 open for a future Pi-native solution if Pi exposes an authoritative compaction-eligibility API.

@tt-a1itt-a1i closed this Sep 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@testikun@tt-a1i