') + ')', '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('^' + ".*" + ', '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" + ', '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('^' + ".*" + ', '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); } })(); })(); fix(runtime): branch copied tool authority facts by liugddx · Pull Request #2061 · apache/maka · GitHub
Skip to content

fix(runtime): branch copied tool authority facts - #2061

Merged
likun666661 merged 4 commits into
apache:mainfrom
liugddx:fix/context-branch-authority-copy
Aug 4, 2026
Merged

fix(runtime): branch copied tool authority facts#2061
likun666661 merged 4 commits into
apache:mainfrom
liugddx:fix/context-branch-authority-copy

Conversation

@liugddx

Copy link
Copy Markdown
Member

Summary

  • allow branch, branch-before, and revision copies containing tool dispatch/recovery facts to reach the existing typed Runtime ledger copy path
  • keep V1/V2 continuation source/start authority fail-closed because claim and boundary evidence are outside the copied Run/Event ledger
  • stop classifying authority as authentication failure by matching explicit authentication terms
  • align the runtime-resume architecture note with the capabilities already shipped in conversation ledger copy

Root cause

The conversation copy implementation already creates new Run, invocation, RuntimeEvent, provider trace, and tool operation identities, then rewrites dispatch/recovery and operation references before importing the target ledger. The later continuation preflight rejected those supported tool facts together with unsupported continuation claims, so the typed copy path was unreachable for ordinary tool-bearing conversations.

The Desktop error was also misleading: the shared classifier used includes('auth'), so the word authority became “Authentication failed”.

Safety boundary

This does not copy or bypass continuation authority. A retained Run with continuationSource, or a retained RuntimeEvent with continuationStart, is still rejected before the target Session is created. Tool operation facts are only admitted through the existing typed rewrite and atomic RuntimeEvent import path.

Verification

  • npm --workspace @maka/core test — 771 passed
  • TMPDIR=/private/tmp npm --workspace @maka/runtime run test:dist — 3,080 passed, 9 skipped, 0 failed
  • focused SessionManager tests — branch, branch-before, revision rewrite pass; legacy continuation rejection pass
  • node --test packages/runtime/dist/__tests__/conversation-copy.test.js — 10 passed
  • npm run test:scripts — 86 passed
  • npm run typecheck — all workspaces clean
  • npm run lint
  • npm run format:check
  • git diff --check

Real Desktop acceptance

Using the current macOS Desktop build and the exact persisted conversation/turn that reproduced the failure:

  • invoked window.maka.sessions.branchFromTurn() through the real preload/IPC boundary
  • child Session was created with the expected parentSessionId and branchOfTurnId
  • all 3 retained turns and 44 messages were readable from the child
  • no Runtime/IPC error was logged
  • removed the acceptance child Session afterward

Fixes#2060

Astro-Han

This comment was marked as outdated.

Astro-Han

This comment was marked as outdated.

@Astro-Han
Astro-Han dismissed stale reviews from themself August 4, 2026 06:31

Superseded by a plain comment; no formal rating intended.

@Astro-Han

Astro-Han commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review notes (no rating on the PR itself; severity labels per finding): the direction is right (admitting tool dispatch/recovery facts with a typed identity rewrite, classifying authority as non-auth), but the fail-closed gate does not hold as written.

P1 — The gate inspects a different set than the copy.
The preflight checks sourceView.runs, but RuntimeReadModel.getSessionView filters to inline runs (runtime-read-model.ts:90runs.filter(isSessionInlineRun)), so non-inline child-agent runs are structurally invisible to it. The copy set is the closure in conversationCopyTurnClosure (conversation-copy.ts:1089), which pulls in non-inline child runs whose parent is retained, including the { parentRunId, agentId, continuationSource } shape that is a real production form for provider-retry continuation targets. So a session containing such a run can pass the preflight and still copy the facts this PR is meant to reject. The second copy caller, session-revision-coordinator.ts, has no gate at all.
Suggested change: run the gate at the end of prepareConversationRuntimeLedgerCopy, over the plan it returns (the same object the clone consumes), rejecting any run with continuationSource or any event flagged by a shared isContinuationStartRuntimeEvent predicate covering both the V2 actions.continuationStart and the V1 stateDelta.continuationStart === true form the read model already recognizes (runtime-event-read-model.ts:303). Then delete the SessionManager preflight. prepare is a pure read and both callers can invoke it before creating the target session, so fail-before-create is preserved.

P2 — Keep the old auth-classification recall while excluding authority.
The new term list misses real provider errors the old includes('auth') caught: AuthenticationError (OpenAI SDK), OAuth2 token expired, User is not authorized, Please authenticate, authToken. A one-line veto keeps both: message.replace(/\bauthorit\w*/g, '').includes('auth'). The sibling classifier at provider-error-classification.ts:312 still uses includes('auth'); sharing the helper would keep the two consistent.

P2 — Add two tests.
A child-retry shape ({ parentRunId, agentId, continuationSource }) selected through the closure must be rejected for branch, branch-before, and revision; and the same rejection on the runtime-host SessionRevisionCoordinator path, which currently has zero coverage. These are the acceptance tests for the P1 fix.

@jackwener

Copy link
Copy Markdown
Member

Review by maka-审美专家 — request changes(修复方向正确:工具授权走 typed rewrite 路径的论证成立——toolOperationIdMap 由新 invocation id 派生、requiredMappedId 缺映射即 loud fail、SQLite 侧 rebuildToolProjections 重投影,这套链路我们核过是对的。但门控收窄后暴露出两个真缺口):

1. 阻塞:hosted 复制路径完全没有门控。 runtime-host/src/server/session-revision-coordinator.ts:397 直接调 cloneConversationRuntimeLedger,全文件无 assertConversationRuntimeLedgerCloneSupported 等价物(已核实)。PR 声明的不变量「continuation authority 在有 typed copy 协议前保持 fail-closed」只在 legacy SessionManager 路径成立;host coordinator 会把 actions.continuationStart 原样拷贝——claimId / boundaryDigest / immediateSource.* 全部指回源会话,而 recovery 是按 claimId 查事件的。请给该路径加同一门控(或 rewrite/reject)。

2. 应修:门控的 turn 集合窄于复制的 turn 集合。 session-manager.ts:5462 用复制消息推导 copiedTurnIds,但实际计划走 conversationCopyTurnClosure(conversation-copy.ts:1269)会经 parentRunId 拉入 child-run turns——closure 加入的 turn 里若有 continuationStart / run.continuationSource 会绕过门控被原样复制。门控改为对 plan.copyTurnIds 求值,并加一个「closure 子 run turn 里埋 continuationStart」的测试把两处一起钉住。

3. 说明或收敛:cloneRunHeader(conversation-copy.ts:867)对 continuationSource 只重写 sourceInvocationId/sourceRunId,V2 的 claimId/boundaryDigest/…以及 rootExecution/automationId 等原样存活——当前只经路径 1 可达,修掉 1 后请在注释里写明这是被门控挡住的死角还是待办。

4. 顺带盯一下:redaction.ts 把 includes('auth') 换成词边界正则修掉了「authority」误报是对的,但也放走了 auth_token / AuthError / authenticate / OAuth2 这类真认证错误(降级为通用文案,无泄漏风险)。建议用 authenticat|authoriz|oauth 词干或对 authority 负向前瞻收紧。5. rollback 现在只删 session 元数据,门控收窄后 mid-copy 失败会孤儿化 runs/events/tool_operations 行——至少在 PR 里声明这个已知洞。

1+2 修完(含新测试)我复审合入。

@liugddx

Copy link
Copy Markdown
MemberAuthor

Addressed the review feedback in e1d976c.

  1. Moved the continuation-authority gate into prepareConversationRuntimeLedgerCopy, where it validates the actual copy plan/child-run closure. The shared continuation-start predicate covers both V2 actions.continuationStart and V1 stateDelta.continuationStart === true. The legacy SessionManager now prepares the plan before creating the target Session, so fail-before-create holds there as well as in the runtime-host path.
  2. Shared the auth text classifier between redaction and provider error classification. It preserves the previous broad auth recall while removing authorit* terms first, so provider spellings such as AuthenticationError, OAuth2, authorized, authenticate, and authToken still classify correctly without treating authority as auth.
  3. Added regression coverage for child retry continuation authority pulled in through the closure, V1 continuation-start events, branch/branch-before/revision fail-before-create behavior, and the runtime-host copy entry point.

Validation completed:

  • typecheck: @maka/core, @maka/runtime, @maka/runtime-host
  • Biome format/lint and git diff --check
  • focused core/runtime regression tests
  • runtime-host POSIX UDS test compiles locally and will execute on Linux CI (skipped on Windows by its existing guard)

@liugddx
liugddxforce-pushed the fix/context-branch-authority-copy branch from e1d976c to c6b194cCompareAugust 4, 2026 09:18
@liugddx

Copy link
Copy Markdown
MemberAuthor

Rebased this PR onto current upstream/main (c50e1823) and resolved the runtime-host test conflict by retaining both the new upstream graph-child coverage and this PR's continuation-source coverage. New head: c6b194c8. The affected packages rebuild/typecheck successfully and the focused regressions pass. GitHub now reports the PR as mergeable; remaining unstable state is check-related, not a merge conflict.

@liugddx
liugddxforce-pushed the fix/context-branch-authority-copy branch from 2afbe67 to c211d03CompareAugust 4, 2026 09:29
@liugddx

Copy link
Copy Markdown
MemberAuthor

CI follow-up: fixed the runtime-host Linux fixture in c211d034 by seeding the canonical parent RuntimeEvent boundary and a complete persisted child provider-retry identity. The PR-specific UDS regression now passes. A subsequent unrelated host-kernel timing failure was retried via ee392cf0; the full workflow is now green (test_runtime_host, workspace tests, headless tests, typecheck, both e2e shards, and aggregate test all succeeded).

@likun666661likun666661 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

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.

Branching a conversation with tool authority facts is rejected as an auth failure

4 participants

@liugddx@Astro-Han@jackwener@likun666661