Skip to content

fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

Description

@liugddx

What happened

A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

callKind: history_compact
status: failed
failOpenReason: provider_error
usageBasis: missing
input/output: 0/0

The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

The observed request was large and tool-heavy:

  • 10 turns / 154 RuntimeEvents selected for folding
  • ~158,572 estimated source tokens (~172,448 before the compaction projection)
  • 677,991-byte provider request capture
  • 110 messages
  • 66 tool calls and 66 matching tool results
  • provider attempt failed after 2,866 ms before reporting any usage

The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

Why the cause is lost

packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

newHistoryCompactSummarizerError('provider_error',{cause: error})

packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

Expected behavior

History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

Suggested fields, where available:

interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

Proposed direction

  1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
  2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
  3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
  4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
  5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

Acceptance criteria

  • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
  • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
  • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
  • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
  • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
  • Tests cover persistence, reopen, redaction, and truncation.

Related: #3030, #3038, #3029, #1625

简体中文对照

发生了什么

一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

callKind: history_compact
status: failed
failOpenReason: provider_error
usageBasis: missing
input/output: 0/0

会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

本次请求规模较大且包含很多工具历史:

  • 选择折叠 10 个 Turn、154 个 RuntimeEvent
  • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
  • Provider 请求 Capture 为 677,991 字节
  • 110 条消息
  • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
  • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

为什么原因丢失

packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

newHistoryCompactSummarizerError('provider_error',{cause: error})

随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

期望行为

历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

建议方向

  1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
  2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
  3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
  4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
  5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

验收标准

  • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
  • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
  • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
  • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
  • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
  • 测试覆盖持久化、重启读取、脱敏和截断。

相关:#3030#3038#3029#1625

Generated-by: Maka

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    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;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    fix(runtime): preserve redacted provider failure details for history compaction fail-open · Issue #3098 · apache/maka · GitHub
    Skip to content

    fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

    Description

    @liugddx

    What happened

    A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

    callKind: history_compact
    status: failed
    failOpenReason: provider_error
    usageBasis: missing
    input/output: 0/0
    

    The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

    The observed request was large and tool-heavy:

    • 10 turns / 154 RuntimeEvents selected for folding
    • ~158,572 estimated source tokens (~172,448 before the compaction projection)
    • 677,991-byte provider request capture
    • 110 messages
    • 66 tool calls and 66 matching tool results
    • provider attempt failed after 2,866 ms before reporting any usage

    The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

    Why the cause is lost

    packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

    newHistoryCompactSummarizerError('provider_error',{cause: error})

    packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

    This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

    Expected behavior

    History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

    Suggested fields, where available:

    interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

    The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

    Proposed direction

    1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
    2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
    3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
    4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
    5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

    Acceptance criteria

    • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
    • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
    • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
    • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
    • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
    • Tests cover persistence, reopen, redaction, and truncation.

    Related: #3030, #3038, #3029, #1625

    简体中文对照

    发生了什么

    一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

    callKind: history_compact
    status: failed
    failOpenReason: provider_error
    usageBasis: missing
    input/output: 0/0
    

    会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

    本次请求规模较大且包含很多工具历史:

    • 选择折叠 10 个 Turn、154 个 RuntimeEvent
    • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
    • Provider 请求 Capture 为 677,991 字节
    • 110 条消息
    • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
    • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

    Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

    为什么原因丢失

    packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

    newHistoryCompactSummarizerError('provider_error',{cause: error})

    随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

    这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

    期望行为

    历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

    interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

    UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

    建议方向

    1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
    2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
    3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
    4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
    5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

    验收标准

    • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
    • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
    • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
    • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
    • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
    • 测试覆盖持久化、重启读取、脱敏和截断。

    相关:#3030#3038#3029#1625

    Generated-by: Maka

    Metadata

    Metadata

    Assignees

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): preserve redacted provider failure details for history compaction fail-open · Issue #3098 · apache/maka · GitHub
      Skip to content

      fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

      Description

      @liugddx

      What happened

      A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

      callKind: history_compact
      status: failed
      failOpenReason: provider_error
      usageBasis: missing
      input/output: 0/0
      

      The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

      The observed request was large and tool-heavy:

      • 10 turns / 154 RuntimeEvents selected for folding
      • ~158,572 estimated source tokens (~172,448 before the compaction projection)
      • 677,991-byte provider request capture
      • 110 messages
      • 66 tool calls and 66 matching tool results
      • provider attempt failed after 2,866 ms before reporting any usage

      The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

      Why the cause is lost

      packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

      newHistoryCompactSummarizerError('provider_error',{cause: error})

      packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

      This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

      Expected behavior

      History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

      Suggested fields, where available:

      interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

      The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

      Proposed direction

      1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
      2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
      3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
      4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
      5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

      Acceptance criteria

      • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
      • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
      • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
      • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
      • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
      • Tests cover persistence, reopen, redaction, and truncation.

      Related: #3030, #3038, #3029, #1625

      简体中文对照

      发生了什么

      一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

      callKind: history_compact
      status: failed
      failOpenReason: provider_error
      usageBasis: missing
      input/output: 0/0
      

      会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

      本次请求规模较大且包含很多工具历史:

      • 选择折叠 10 个 Turn、154 个 RuntimeEvent
      • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
      • Provider 请求 Capture 为 677,991 字节
      • 110 条消息
      • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
      • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

      Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

      为什么原因丢失

      packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

      newHistoryCompactSummarizerError('provider_error',{cause: error})

      随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

      这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

      期望行为

      历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

      interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

      UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

      建议方向

      1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
      2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
      3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
      4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
      5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

      验收标准

      • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
      • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
      • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
      • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
      • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
      • 测试覆盖持久化、重启读取、脱敏和截断。

      相关:#3030#3038#3029#1625

      Generated-by: Maka

      Metadata

      Metadata

      Assignees

      Labels

      bugSomething isn't working

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

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

        fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

        Description

        @liugddx

        What happened

        A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

        callKind: history_compact
        status: failed
        failOpenReason: provider_error
        usageBasis: missing
        input/output: 0/0
        

        The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

        The observed request was large and tool-heavy:

        • 10 turns / 154 RuntimeEvents selected for folding
        • ~158,572 estimated source tokens (~172,448 before the compaction projection)
        • 677,991-byte provider request capture
        • 110 messages
        • 66 tool calls and 66 matching tool results
        • provider attempt failed after 2,866 ms before reporting any usage

        The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

        Why the cause is lost

        packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

        newHistoryCompactSummarizerError('provider_error',{cause: error})

        packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

        This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

        Expected behavior

        History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

        Suggested fields, where available:

        interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

        The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

        Proposed direction

        1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
        2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
        3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
        4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
        5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

        Acceptance criteria

        • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
        • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
        • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
        • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
        • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
        • Tests cover persistence, reopen, redaction, and truncation.

        Related: #3030, #3038, #3029, #1625

        简体中文对照

        发生了什么

        一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

        callKind: history_compact
        status: failed
        failOpenReason: provider_error
        usageBasis: missing
        input/output: 0/0
        

        会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

        本次请求规模较大且包含很多工具历史:

        • 选择折叠 10 个 Turn、154 个 RuntimeEvent
        • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
        • Provider 请求 Capture 为 677,991 字节
        • 110 条消息
        • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
        • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

        Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

        为什么原因丢失

        packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

        newHistoryCompactSummarizerError('provider_error',{cause: error})

        随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

        这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

        期望行为

        历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

        interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

        UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

        建议方向

        1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
        2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
        3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
        4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
        5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

        验收标准

        • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
        • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
        • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
        • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
        • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
        • 测试覆盖持久化、重启读取、脱敏和截断。

        相关:#3030#3038#3029#1625

        Generated-by: Maka

        Metadata

        Metadata

        Assignees

        Labels

        bugSomething isn't working

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

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

          fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

          Description

          @liugddx

          What happened

          A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

          callKind: history_compact
          status: failed
          failOpenReason: provider_error
          usageBasis: missing
          input/output: 0/0
          

          The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

          The observed request was large and tool-heavy:

          • 10 turns / 154 RuntimeEvents selected for folding
          • ~158,572 estimated source tokens (~172,448 before the compaction projection)
          • 677,991-byte provider request capture
          • 110 messages
          • 66 tool calls and 66 matching tool results
          • provider attempt failed after 2,866 ms before reporting any usage

          The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

          Why the cause is lost

          packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

          newHistoryCompactSummarizerError('provider_error',{cause: error})

          packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

          This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

          Expected behavior

          History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

          Suggested fields, where available:

          interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

          The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

          Proposed direction

          1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
          2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
          3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
          4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
          5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

          Acceptance criteria

          • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
          • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
          • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
          • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
          • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
          • Tests cover persistence, reopen, redaction, and truncation.

          Related: #3030, #3038, #3029, #1625

          简体中文对照

          发生了什么

          一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

          callKind: history_compact
          status: failed
          failOpenReason: provider_error
          usageBasis: missing
          input/output: 0/0
          

          会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

          本次请求规模较大且包含很多工具历史:

          • 选择折叠 10 个 Turn、154 个 RuntimeEvent
          • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
          • Provider 请求 Capture 为 677,991 字节
          • 110 条消息
          • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
          • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

          Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

          为什么原因丢失

          packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

          newHistoryCompactSummarizerError('provider_error',{cause: error})

          随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

          这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

          期望行为

          历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

          interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

          UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

          建议方向

          1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
          2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
          3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
          4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
          5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

          验收标准

          • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
          • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
          • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
          • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
          • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
          • 测试覆盖持久化、重启读取、脱敏和截断。

          相关:#3030#3038#3029#1625

          Generated-by: Maka

          Metadata

          Metadata

          Assignees

          Labels

          bugSomething isn't working

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): preserve redacted provider failure details for history compaction fail-open · Issue #3098 · apache/maka · GitHub
            Skip to content

            fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

            Description

            @liugddx

            What happened

            A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

            callKind: history_compact
            status: failed
            failOpenReason: provider_error
            usageBasis: missing
            input/output: 0/0
            

            The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

            The observed request was large and tool-heavy:

            • 10 turns / 154 RuntimeEvents selected for folding
            • ~158,572 estimated source tokens (~172,448 before the compaction projection)
            • 677,991-byte provider request capture
            • 110 messages
            • 66 tool calls and 66 matching tool results
            • provider attempt failed after 2,866 ms before reporting any usage

            The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

            Why the cause is lost

            packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

            newHistoryCompactSummarizerError('provider_error',{cause: error})

            packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

            This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

            Expected behavior

            History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

            Suggested fields, where available:

            interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

            The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

            Proposed direction

            1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
            2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
            3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
            4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
            5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

            Acceptance criteria

            • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
            • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
            • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
            • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
            • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
            • Tests cover persistence, reopen, redaction, and truncation.

            Related: #3030, #3038, #3029, #1625

            简体中文对照

            发生了什么

            一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

            callKind: history_compact
            status: failed
            failOpenReason: provider_error
            usageBasis: missing
            input/output: 0/0
            

            会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

            本次请求规模较大且包含很多工具历史:

            • 选择折叠 10 个 Turn、154 个 RuntimeEvent
            • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
            • Provider 请求 Capture 为 677,991 字节
            • 110 条消息
            • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
            • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

            Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

            为什么原因丢失

            packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

            newHistoryCompactSummarizerError('provider_error',{cause: error})

            随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

            这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

            期望行为

            历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

            interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

            UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

            建议方向

            1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
            2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
            3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
            4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
            5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

            验收标准

            • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
            • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
            • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
            • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
            • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
            • 测试覆盖持久化、重启读取、脱敏和截断。

            相关:#3030#3038#3029#1625

            Generated-by: Maka

            Metadata

            Metadata

            Assignees

            Labels

            bugSomething isn't working

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): preserve redacted provider failure details for history compaction fail-open · Issue #3098 · apache/maka · GitHub
              Skip to content

              fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

              Description

              @liugddx

              What happened

              A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

              callKind: history_compact
              status: failed
              failOpenReason: provider_error
              usageBasis: missing
              input/output: 0/0
              

              The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

              The observed request was large and tool-heavy:

              • 10 turns / 154 RuntimeEvents selected for folding
              • ~158,572 estimated source tokens (~172,448 before the compaction projection)
              • 677,991-byte provider request capture
              • 110 messages
              • 66 tool calls and 66 matching tool results
              • provider attempt failed after 2,866 ms before reporting any usage

              The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

              Why the cause is lost

              packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

              newHistoryCompactSummarizerError('provider_error',{cause: error})

              packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

              This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

              Expected behavior

              History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

              Suggested fields, where available:

              interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

              The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

              Proposed direction

              1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
              2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
              3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
              4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
              5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

              Acceptance criteria

              • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
              • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
              • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
              • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
              • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
              • Tests cover persistence, reopen, redaction, and truncation.

              Related: #3030, #3038, #3029, #1625

              简体中文对照

              发生了什么

              一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

              callKind: history_compact
              status: failed
              failOpenReason: provider_error
              usageBasis: missing
              input/output: 0/0
              

              会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

              本次请求规模较大且包含很多工具历史:

              • 选择折叠 10 个 Turn、154 个 RuntimeEvent
              • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
              • Provider 请求 Capture 为 677,991 字节
              • 110 条消息
              • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
              • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

              Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

              为什么原因丢失

              packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

              newHistoryCompactSummarizerError('provider_error',{cause: error})

              随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

              这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

              期望行为

              历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

              interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

              UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

              建议方向

              1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
              2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
              3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
              4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
              5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

              验收标准

              • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
              • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
              • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
              • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
              • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
              • 测试覆盖持久化、重启读取、脱敏和截断。

              相关:#3030#3038#3029#1625

              Generated-by: Maka

              Metadata

              Metadata

              Assignees

              Labels

              bugSomething isn't working

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

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

                fix(runtime): preserve redacted provider failure details for history compaction fail-open #3098

                Description

                @liugddx

                What happened

                A manual history compaction failed open on an openai-codex / Codex Subscription session. The durable records retained only:

                callKind: history_compact
                status: failed
                failOpenReason: provider_error
                usageBasis: missing
                input/output: 0/0
                

                The conversation received context_compaction_failed_open, but after the in-memory Runtime Host logs were no longer available there was no durable way to tell whether the provider returned a 400, 413, 429, 5xx, timeout, or transport error.

                The observed request was large and tool-heavy:

                • 10 turns / 154 RuntimeEvents selected for folding
                • ~158,572 estimated source tokens (~172,448 before the compaction projection)
                • 677,991-byte provider request capture
                • 110 messages
                • 66 tool calls and 66 matching tool results
                • provider attempt failed after 2,866 ms before reporting any usage

                The captured generic summarizer request also emitted parallel tool calls as separate assistant messages, so this incident likely matches #3030 and should be fixed at the request-shape level by #3038. This issue is deliberately narrower and remains useful after #3038: a provider failure must retain enough redacted causal evidence to diagnose the next distinct failure without relying on an ephemeral log buffer.

                Why the cause is lost

                packages/runtime/src/history-compact-summarizer.ts wraps every unrecognized provider exception as:

                newHistoryCompactSummarizerError('provider_error',{cause: error})

                packages/runtime/src/ai-sdk-compaction.ts then persists only the coarse provider_error fail-open reason. The corresponding ModelCallAttempt records status: failed, but no redacted error class, HTTP status, provider code/message, or provider request id. The provider request capture contains only the prepared request, not the failure response.

                This makes the safe fail-open behavior operationally opaque: history is preserved correctly, but the incident cannot be distinguished from authentication, rate limiting, payload rejection, malformed provider history, or an outage after restart.

                Expected behavior

                History compaction should continue to fail open, but its canonical attempt/diagnostic records should retain a bounded, redacted failure fingerprint sufficient for diagnosis.

                Suggested fields, where available:

                interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

                The UI can remain concise; raw or expanded diagnostic information may stay behind the existing diagnostics surface.

                Proposed direction

                1. Normalize the original provider exception before HistoryCompactSummarizerError collapses it to a compaction policy reason.
                2. Persist bounded/redacted failure details on the canonical ModelCallAttempt (or an equally authoritative linked diagnostic record).
                3. Keep failOpenReason: provider_error for policy compatibility, but add a stable diagnostic reference or failure fingerprint.
                4. Include the failure in Runtime Host execution diagnostics while preserving secret redaction and byte caps.
                5. Add regression coverage for representative 400/413/429/5xx/timeout errors and verify that diagnostics survive store reopen.

                Acceptance criteria

                • A failed history_compact provider attempt remains diagnosable after Runtime Host/Desktop restart.
                • Diagnostics distinguish at least request rejection, rate limit, server error, timeout/transport error, and context-length error when the provider exposes that information.
                • context_compaction_failed_open remains a concise user-facing note and compaction still preserves the prior durable checkpoint/history.
                • API keys, authorization headers, subscription tokens, request bodies, and unbounded provider payloads are never persisted as error details.
                • Stored messages and Runtime Events retain only bounded/redacted fields or a reference to a protected diagnostic artifact.
                • Tests cover persistence, reopen, redaction, and truncation.

                Related: #3030, #3038, #3029, #1625

                简体中文对照

                发生了什么

                一次使用 openai-codex / Codex Subscription 的手动历史压缩发生 fail-open。持久化记录只保留了:

                callKind: history_compact
                status: failed
                failOpenReason: provider_error
                usageBasis: missing
                input/output: 0/0
                

                会话中写入了 context_compaction_failed_open,但 Runtime Host 的内存日志不可用后,无法从持久化数据判断 Provider 返回的是 400、413、429、5xx、超时还是网络传输错误。

                本次请求规模较大且包含很多工具历史:

                • 选择折叠 10 个 Turn、154 个 RuntimeEvent
                • 待压缩历史约 158,572 个估算 Token,压缩投影前约 172,448
                • Provider 请求 Capture 为 677,991 字节
                • 110 条消息
                • 66 个 Tool Call 与 66 个完整匹配的 Tool Result
                • Provider Attempt 在 2,866 ms 后失败,且没有返回任何 Usage

                Capture 中的通用摘要请求还把并行 Tool Call 拆成了多条 Assistant Message,因此本次直接失败原因很可能与 #3030 相同,并由 #3038 修复请求形态。但本 Issue 范围不同:即使 #3038 合入,后续其他 Provider 错误也应留下足够的脱敏因果证据,而不能依赖易失的内存日志。

                为什么原因丢失

                packages/runtime/src/history-compact-summarizer.ts 会把所有未识别的 Provider 异常包装为:

                newHistoryCompactSummarizerError('provider_error',{cause: error})

                随后 packages/runtime/src/ai-sdk-compaction.ts 只持久化粗粒度的 provider_error。对应的 ModelCallAttempt 虽然记录 status: failed,却没有脱敏后的错误分类、HTTP 状态、Provider 错误码/消息或请求 ID。Provider Request Capture 也只包含准备好的请求,不包含失败响应。

                这使得 fail-open 在安全上正确、在运维上却不可诊断:重启后无法区分认证失败、限流、Payload 被拒绝、Provider 历史格式非法或服务故障。

                期望行为

                历史压缩仍应 fail-open,但 canonical attempt/diagnostic 应持久化一份有界、脱敏的失败指纹,例如:

                interfaceProviderFailureDiagnostic{errorClass: string;httpStatus?: number;providerCode?: string;safeMessage?: string;providerRequestId?: string;retryable?: boolean;}

                UI 可以继续保持简洁,详细信息放在现有 Diagnostics 界面中即可。

                建议方向

                1. 在异常被折叠为 HistoryCompactSummarizerError('provider_error') 前规范化原始 Provider 错误。
                2. 在 canonical ModelCallAttempt 或同等权威的关联记录中保存有界、脱敏的失败详情。
                3. 保留 failOpenReason: provider_error 兼容策略判断,同时增加稳定的诊断引用或失败指纹。
                4. 将失败信息纳入 Runtime Host execution diagnostics,并继续执行 Secret Redaction 和长度限制。
                5. 增加 400、413、429、5xx、超时等测试,并验证重新打开 Store 后仍可诊断。

                验收标准

                • history_compact 失败后,即使 Runtime Host/Desktop 重启仍能诊断原因。
                • Provider 提供信息时,可以区分请求拒绝、限流、服务端错误、超时/传输错误和 Context Length 错误。
                • context_compaction_failed_open 仍保持简洁,且压缩失败继续保留旧 Checkpoint 和完整历史。
                • 不持久化 API Key、Authorization Header、订阅 Token、请求正文或无界 Provider Payload。
                • StoredMessage 和 RuntimeEvent 只保存有界脱敏字段,或保存指向受保护诊断 Artifact 的引用。
                • 测试覆盖持久化、重启读取、脱敏和截断。

                相关:#3030#3038#3029#1625

                Generated-by: Maka

                Metadata

                Metadata

                Assignees

                Labels

                bugSomething isn't working

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions