Merge remote-tracking branch 'origin/main' into brennanb2025/ua-b-finish

This commit is contained in:
Brennan Benson
2026-09-14 23:30:55 -07:00
7 changed files with 149 additions and 24 deletions
@@ -25,7 +25,14 @@ describe('Electron runtime package contract', () => {
}
it('keeps root postinstall as the single Electron binary install owner', () => {
expect(packageJson.scripts.postinstall).toBe('node config/scripts/rebuild-native-deps.mjs')
// Why not an exact match: the invariant is that the root postinstall owns the Electron
// binary install, not that nothing else may run after it. Pinning the whole string made
// any unrelated chained step (a lint-plugin sync, say) a CI failure for every open PR.
const postinstall = packageJson.scripts.postinstall
const steps = postinstall.split('&&').map((step) => step.trim())
expect(steps[0]).toBe('node config/scripts/rebuild-native-deps.mjs')
// No later step may take over the Electron install the first step owns.
expect(steps.slice(1).join(' ')).not.toMatch(/electron/i)
expect(pnpmWorkspace.allowBuilds).not.toHaveProperty('electron')
})
+4 -4
View File
@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="106" height="20" role="img" aria-label="downloads: 56m">
<title>downloads: 56m</title>
<svg xmlns="http://www.w3.org/2000/svg" width="106" height="20" role="img" aria-label="downloads: 58m">
<title>downloads: 58m</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
@@ -15,7 +15,7 @@
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
<text x="37" y="15" fill="#010101" fill-opacity=".3">downloads</text>
<text x="37" y="14">downloads</text>
<text x="90" y="15" fill="#010101" fill-opacity=".3">56m</text>
<text x="90" y="14">56m</text>
<text x="90" y="15" fill="#010101" fill-opacity=".3">58m</text>
<text x="90" y="14">58m</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 935 B

After

Width:  |  Height:  |  Size: 935 B

+1 -1
View File
@@ -97,7 +97,7 @@
"build": "pnpm run build:desktop && pnpm run build:native",
"build:release": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer",
"build:release:parallel": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite:parallel && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer",
"postinstall": "node config/scripts/rebuild-native-deps.mjs && node config/scripts/sync-anti-slop-plugin.mjs",
"postinstall": "node config/scripts/rebuild-native-deps.mjs",
"rebuild:electron": "node config/scripts/rebuild-native-deps.mjs",
"reclaim:electron-dists": "node config/scripts/reclaim-electron-dists.mjs",
"reclaim:dev-bundles": "node config/scripts/reclaim-dev-electron-bundles.mjs",
@@ -54,15 +54,17 @@ export class OrcaRuntimeWithBindPtyIncarnationHandle extends OrcaRuntimeWithBuil
for (const [ptyId, retained] of this.handleByPtyIncarnation) {
const pty = this.ptysById.get(ptyId)
const leaves = this.getLeavesForPty(ptyId)
if (
!pty ||
pty.incarnationId !== retained.incarnationId ||
leaves.length !== 1 ||
this.handleByPtyId.has(ptyId)
) {
// Why: a handle issued before the host reported the incarnation is un-fenced, so
// learning it is not a replacement; only a known-to-different incarnation is.
const incarnationReplaced =
retained.incarnationId !== null &&
pty !== undefined &&
pty.incarnationId !== retained.incarnationId
if (!pty || incarnationReplaced || leaves.length !== 1 || this.handleByPtyId.has(ptyId)) {
this.invalidatePtyIncarnationHandle(ptyId)
continue
}
retained.incarnationId = pty.incarnationId
this.bindPtyIncarnationHandle(retained, leaves[0])
}
}
@@ -107,6 +107,40 @@ describe('runtime terminal handle incarnation fencing', () => {
await expect(runtime.readTerminal(handle)).resolves.toMatchObject({ handle, status: 'running' })
})
it('keeps a listed handle when graph sync learns the incarnation after issue', async () => {
// Daemon-hosted PTYs are recorded from first output before the spawn commit reports an
// incarnation, so the handle is issued un-fenced and must survive learning it.
const { runtime } = makeRuntime()
runtime.registerPty(PTY_ID, WORKTREE_ID, 'target', { tabId: TAB_ID, leafId: LEAF_ID })
syncGraph(runtime)
const [listed] = (await runtime.listTerminals()).terminals
register(runtime, 'incarnation-learned')
syncGraph(runtime)
await expect(runtime.readTerminal(listed.handle)).resolves.toMatchObject({
handle: listed.handle,
status: 'running'
})
})
it('stales a listed handle when graph sync sees a replaced incarnation', async () => {
const { runtime } = makeRuntime()
register(runtime, 'incarnation-old')
syncGraph(runtime)
const [listed] = (await runtime.listTerminals()).terminals
// Rotate the record directly so reconcile is the only fence exercised.
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: test reaches the runtime's protected pty record map to bypass the registerPty fence.
const internals = runtime as unknown as {
ptysById: Map<string, { incarnationId: string | null }>
}
internals.ptysById.get(PTY_ID)!.incarnationId = 'incarnation-new'
syncGraph(runtime)
await expect(runtime.readTerminal(listed.handle)).rejects.toThrow('terminal_handle_stale')
})
it('invalidates a direct handle when a reused PTY id gets a new incarnation', async () => {
const { runtime, writes } = makeRuntime()
const staleHandle = runtime.preAllocateHandleForPty(PTY_ID)
@@ -1,6 +1,9 @@
import { describe, expect, it, beforeEach } from 'vitest'
import { isTerminalLeafId, parsePaneKey } from '../../shared/stable-pane-id'
import { structuredAgentSessionPaneKey } from '../../shared/structured-agent-session-projection'
import {
structuredAgentSessionPaneKey,
structuredAgentSessionTabId
} from '../../shared/structured-agent-session-projection'
import { selectExactWorkerProviderSession } from './orchestration/worker-provider-session'
import { structuredWorkerChildIdentityEnv } from './structured-worker-child-identity-env'
import {
@@ -85,12 +88,57 @@ describe('structured worker identity', () => {
)
})
it("accepts a persisted pane key for its own session and rejects another session's", () => {
it('accepts only the registered pane key for its session', () => {
const handle = mintStructuredWorkerHandle()
const paneKey = mintStructuredWorkerPaneKey(SESSION_ID)
expect(structuredWorkerPaneKeyBelongsToSession(paneKey, SESSION_ID)).toBe(true)
expect(structuredWorkerPaneKeyBelongsToSession(paneKey, 'another-session-id')).toBe(false)
expect(structuredWorkerPaneKeyBelongsToSession('not-a-pane-key', SESSION_ID)).toBe(false)
expect(structuredWorkerPaneKeyBelongsToSession(null, SESSION_ID)).toBe(false)
structuredWorkerIdentities.register({
handle,
sessionId: SESSION_ID,
agent: 'claude',
paneKey,
processIncarnation: structuredWorkerProcessIncarnation(SESSION_ID),
worktreeId: 'wt_1',
hostScope: { kind: 'local', hostId: 'local' }
})
try {
expect(structuredWorkerPaneKeyBelongsToSession(paneKey, SESSION_ID)).toBe(true)
expect(
structuredWorkerPaneKeyBelongsToSession(mintStructuredWorkerPaneKey(SESSION_ID), SESSION_ID)
).toBe(false)
expect(structuredWorkerPaneKeyBelongsToSession(paneKey, 'another-session-id')).toBe(false)
expect(structuredWorkerPaneKeyBelongsToSession('not-a-pane-key', SESSION_ID)).toBe(false)
expect(structuredWorkerPaneKeyBelongsToSession(null, SESSION_ID)).toBe(false)
} finally {
structuredWorkerIdentities.forget(handle)
}
})
it('rejects the deterministic public status key even for a registered worker', () => {
const handle = mintStructuredWorkerHandle()
const paneKey = mintStructuredWorkerPaneKey(SESSION_ID)
structuredWorkerIdentities.register({
handle,
sessionId: SESSION_ID,
agent: 'claude',
paneKey,
processIncarnation: structuredWorkerProcessIncarnation(SESSION_ID),
worktreeId: 'wt_1',
hostScope: { kind: 'local', hostId: 'local' }
})
try {
const statusPaneKey = structuredAgentSessionPaneKey(
structuredAgentSessionTabId(SESSION_ID),
SESSION_ID
)
expect(structuredWorkerPaneKeyBelongsToSession(statusPaneKey, SESSION_ID)).toBe(false)
} finally {
structuredWorkerIdentities.forget(handle)
}
})
it('fails closed when the session has no registry record', () => {
const paneKey = mintStructuredWorkerPaneKey(SESSION_ID)
expect(structuredWorkerPaneKeyBelongsToSession(paneKey, SESSION_ID)).toBe(false)
})
it('derives a pane key whose leaf passes the terminal leaf check', () => {
@@ -169,6 +217,21 @@ describe('structured worker identity registry', () => {
).toBeNull()
})
it('refuses to rehydrate the deterministic public status key as a worker credential', () => {
expect(
registry.rehydrate({
terminal_handle: mintStructuredWorkerHandle(),
pane_key: structuredAgentSessionPaneKey(
structuredAgentSessionTabId(SESSION_ID),
SESSION_ID
),
process_incarnation: structuredWorkerProcessIncarnation(SESSION_ID),
worktree_id: 'wt_1',
host_scope: JSON.stringify({ kind: 'local', hostId: 'local' })
})
).toBeNull()
})
it('forgets both indexes', () => {
const handle = mintStructuredWorkerHandle()
registry.register({
+25 -6
View File
@@ -20,7 +20,10 @@ import type {
AgentSessionRecord
} from '../../shared/agent-session-record'
import { LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host'
import { structuredAgentSessionTabId } from '../../shared/structured-agent-session-projection'
import {
structuredAgentSessionPaneKey,
structuredAgentSessionTabId
} from '../../shared/structured-agent-session-projection'
import { isTerminalLeafId, makePaneKey, parsePaneKey } from '../../shared/stable-pane-id'
import {
parseWorkerTerminalHostScope,
@@ -67,13 +70,30 @@ export function mintStructuredWorkerPaneKey(sessionId: string): string {
return makePaneKey(structuredAgentSessionTabId(sessionId), randomUUID())
}
/** Integrity check for a persisted pane key: same session's tab, and a real terminal leaf. */
/** Credential check: only the pane key registered for this session can prove its identity. */
export function structuredWorkerPaneKeyBelongsToSession(
paneKey: string | null | undefined,
sessionId: string
): boolean {
const registered = structuredWorkerIdentities.getBySessionId(sessionId)
const parsed = paneKey ? parsePaneKey(paneKey) : null
return Boolean(
registered &&
registered.paneKey === paneKey &&
parsed &&
parsed.tabId === structuredAgentSessionTabId(sessionId)
)
}
/** Bootstrap validation for a durable row before its key can enter the registry. */
function persistedStructuredWorkerPaneKeyIsValid(
paneKey: string | null | undefined,
sessionId: string
): paneKey is string {
const parsed = paneKey ? parsePaneKey(paneKey) : null
return Boolean(
paneKey &&
paneKey !== structuredAgentSessionPaneKey(structuredAgentSessionTabId(sessionId), sessionId) &&
parsed &&
parsed.tabId === structuredAgentSessionTabId(sessionId) &&
isTerminalLeafId(parsed.leafId)
@@ -176,9 +196,8 @@ export class StructuredWorkerIdentityRegistry {
!hostScope ||
!row.worktree_id ||
!isStructuredWorkerHandle(row.terminal_handle) ||
// The leaf is random, so the row IS the only source for it; verify only that it is a real
// leaf under this session's tab rather than trying to re-derive it.
!structuredWorkerPaneKeyBelongsToSession(row.pane_key, sessionId)
// The durable row bootstraps the registry after restart, so validate it before registration.
!persistedStructuredWorkerPaneKeyIsValid(row.pane_key, sessionId)
) {
return null
}
@@ -187,7 +206,7 @@ export class StructuredWorkerIdentityRegistry {
sessionId,
// The row does not carry the provider; callers that need it read the durable record.
agent: null,
paneKey: row.pane_key as string,
paneKey: row.pane_key,
processIncarnation: structuredWorkerProcessIncarnation(sessionId),
worktreeId: row.worktree_id,
hostScope