diff --git a/config/scripts/run-linux-packaged-node-pty-floor-smoke.test.mjs b/config/scripts/run-linux-packaged-node-pty-floor-smoke.test.mjs index c04aced995f..3f59c6600fc 100644 --- a/config/scripts/run-linux-packaged-node-pty-floor-smoke.test.mjs +++ b/config/scripts/run-linux-packaged-node-pty-floor-smoke.test.mjs @@ -1,4 +1,8 @@ -import { readFileSync } from 'node:fs' +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { runProcessSync } from '@orca/process-host' import { describe, expect, it } from 'vitest' import { packagedNodePtyFloorDockerArgs } from './run-linux-packaged-node-pty-floor-smoke.mjs' @@ -24,13 +28,37 @@ describe('packaged node-pty Linux floor smoke', () => { ).toThrow('linux-packaged-node-pty-floor-app-directory-invalid') }) - it('loads node-pty from packaged runtime resources', () => { - const childSource = readFileSync( - new URL('./linux-packaged-node-pty-floor-child.cjs', import.meta.url), - 'utf8' + it.each([ + ['valid output', 0, 'orca-node-pty-floor-ok', 0], + ['nonzero exit', 3, 'orca-node-pty-floor-ok', 1], + ['wrong output', 0, 'unexpected', 1] + ])('validates packaged node-pty %s', (_name, exitCode, output, expectedCode) => { + const resources = mkdtempSync(join(tmpdir(), 'packaged-node-pty-floor-')) + const moduleDirectory = join(resources, 'node_modules', 'node-pty') + mkdirSync(moduleDirectory, { recursive: true }) + writeFileSync( + join(moduleDirectory, 'index.js'), + `module.exports = { spawn() { return { + onData(callback) { callback(${JSON.stringify(output)}) }, + onExit(callback) { queueMicrotask(() => callback({ exitCode: ${exitCode} })) }, + kill() {} + } } }` ) - - expect(childSource).toContain("join(resourcesDirectory, 'node_modules', 'node-pty')") - expect(childSource).not.toContain("join(resourcesDirectory, 'app.asar'") + try { + const result = runProcessSync({ + program: process.execPath, + args: [ + fileURLToPath(new URL('./linux-packaged-node-pty-floor-child.cjs', import.meta.url)), + resources + ] + }) + expect(result.code, result.stderr).toBe(expectedCode) + expect(result.stdout).toBe(expectedCode === 0 ? 'orca-node-pty-floor-ok\n' : '') + if (expectedCode !== 0) { + expect(result.stderr).toContain('packaged node-pty floor smoke failed:') + } + } finally { + rmSync(resources, { recursive: true, force: true }) + } }) }) diff --git a/mobile/src/session/active-session-tab.test.ts b/mobile/src/session/active-session-tab.test.ts index 390e947358e..475ee654341 100644 --- a/mobile/src/session/active-session-tab.test.ts +++ b/mobile/src/session/active-session-tab.test.ts @@ -26,18 +26,6 @@ describe('resolveActiveSessionTab', () => { expect(result.retainSelectedSessionTabId).toBe(false) }) - it('keeps the device on a terminal tab when an agent turn flags another terminal active', () => { - const result = resolveActiveSessionTab( - [terminalTab('agent', true), terminalTab('shell', false)], - { - pendingActiveSessionTabId: null, - selectedSessionTabId: 'shell' - } - ) - - expect(result.activeTab?.id).toBe('shell') - }) - it('follows the host when it explicitly navigates this device', () => { const result = resolveActiveSessionTab( [terminalTab('agent', true), browserTab('browser', false)], diff --git a/mobile/src/session/initial-session-terminal.test.ts b/mobile/src/session/initial-session-terminal.test.ts index f8f569cbb48..cb30c8ff75c 100644 --- a/mobile/src/session/initial-session-terminal.test.ts +++ b/mobile/src/session/initial-session-terminal.test.ts @@ -20,81 +20,13 @@ function baseInput( } } -/** - * Replays the route state the session screen actually holds - * (`app/h/[hostId]/session/[worktreeId].tsx`): `applySessionTabs` publishes the - * tab list and flips `terminalsLoaded`, `handleCloseSessionTab` prunes the - * closed tab and nulls the active handle. - */ -class SessionRouteState { - newlyCreatedWorkspace = true - tabsLoaded = false - tabIds: string[] = [] - activeHandle: string | null = null - sawSessionTabs = false - autoCreatedForWorktree = false - - applySessionTabs(tabIds: string[], activeHandle: string | null = tabIds[0] ? 'pty-1' : null) { - this.tabIds = tabIds - this.activeHandle = activeHandle - if (tabIds.length > 0) { - this.sawSessionTabs = true - } - this.tabsLoaded = true - } - - closeSessionTab(tabId: string) { - this.tabIds = this.tabIds.filter((candidate) => candidate !== tabId) - this.activeHandle = null - } - - gate(): boolean { - return shouldAutoCreateInitialSessionTerminal({ - newlyCreatedWorkspace: this.newlyCreatedWorkspace, - connected: true, - tabsLoaded: this.tabsLoaded, - visibleTabCount: this.tabIds.length, - hasActiveTerminalHandle: this.activeHandle !== null, - createInFlight: false, - sawSessionTabs: this.sawSessionTabs, - autoCreatedForWorktree: this.autoCreatedForWorktree - }) - } -} - describe('shouldAutoCreateInitialSessionTerminal', () => { it('creates the first terminal for a workspace that hydrates with nothing', () => { - const route = new SessionRouteState() - route.applySessionTabs([]) - expect(route.gate()).toBe(true) + expect(shouldAutoCreateInitialSessionTerminal(baseInput())).toBe(true) }) - it('does not re-create after the user closes the last tab (#9717, #7345)', () => { - const route = new SessionRouteState() - route.applySessionTabs(['tab-1']) - expect(route.gate()).toBe(false) - - route.closeSessionTab('tab-1') - expect(route.tabIds).toHaveLength(0) - expect(route.activeHandle).toBeNull() - // Emptiness that follows a populated list is a close, not a cold hydrate. - expect(route.gate()).toBe(false) - }) - - it('does not re-create after the consumed creation route remounts empty', () => { - const creationRoute = new SessionRouteState() - creationRoute.applySessionTabs([]) - expect(creationRoute.gate()).toBe(true) - creationRoute.newlyCreatedWorkspace = false - creationRoute.autoCreatedForWorktree = true - creationRoute.applySessionTabs(['tab-1']) - creationRoute.closeSessionTab('tab-1') - expect(creationRoute.gate()).toBe(false) - - const reopenedRoute = new SessionRouteState() - reopenedRoute.newlyCreatedWorkspace = creationRoute.newlyCreatedWorkspace - reopenedRoute.applySessionTabs([]) - expect(reopenedRoute.gate()).toBe(false) + it('does not re-create once this route has shown session tabs', () => { + expect(shouldAutoCreateInitialSessionTerminal(baseInput({ sawSessionTabs: true }))).toBe(false) }) it('does not create from stale empty state while switching ordinary workspace routes', () => { @@ -109,13 +41,6 @@ describe('shouldAutoCreateInitialSessionTerminal', () => { ).toBe(false) }) - it('does not re-create when the host drops every tab mid-session', () => { - const route = new SessionRouteState() - route.applySessionTabs(['tab-1', 'tab-2']) - route.applySessionTabs([], null) - expect(route.gate()).toBe(false) - }) - it('stays armed while an empty workspace is still loading', () => { expect(shouldAutoCreateInitialSessionTerminal(baseInput({ tabsLoaded: false }))).toBe(false) }) diff --git a/mobile/src/session/mobile-markdown-document-lifetime.test.ts b/mobile/src/session/mobile-markdown-document-lifetime.test.ts index de813c3489c..7a6788cc1cf 100644 --- a/mobile/src/session/mobile-markdown-document-lifetime.test.ts +++ b/mobile/src/session/mobile-markdown-document-lifetime.test.ts @@ -1,4 +1,3 @@ -import { randomBytes } from 'node:crypto' import { describe, expect, it, vi } from 'vitest' import { hookMount, performHookAction } from '../test-support/rpc-recording/hook-mount' import { mountFixture } from '../test-support/rpc-recording/recorder-fixture-shape' @@ -82,7 +81,7 @@ function mountDocuments(readReply: () => Promise) { unmount: hook.unmount, read(id: string) { const target = tab(id) - tabs.current = [target] + tabs.current = [...tabs.current.filter((current) => current.id !== id), target] return performHookAction(() => readers?.readMarkdownTab(target)) }, close(id: string) { @@ -107,15 +106,30 @@ function delayedReply() { } describe('mobile Markdown document lifetime', () => { - it('releases 40 MiB of clean content after twenty reads and successful closes', async () => { - const h = mountDocuments(async () => success(randomBytes(1536 * 1024).toString('base64'))) + it('releases only the closed clean document and preserves other cached documents', async () => { + const readReply = vi + .fn() + .mockResolvedValueOnce(success('# a')) + .mockResolvedValueOnce(success('# b')) + .mockResolvedValueOnce(success('# c')) + const h = mountDocuments(readReply) try { - for (let index = 0; index < 20; index += 1) { - await h.read('tab-' + index) - const doc = h.docs.get('tab-' + index) - expect(doc?.status === 'ready' && Buffer.byteLength(doc.content)).toBe(2 * 1024 * 1024) - await h.close('tab-' + index) - } + await h.read('a') + await h.read('b') + await h.read('c') + expect(h.docs.get('a')).toMatchObject({ status: 'ready', content: '# a' }) + expect(h.docs.get('b')).toMatchObject({ status: 'ready', content: '# b' }) + expect(h.docs.get('c')).toMatchObject({ status: 'ready', content: '# c' }) + const a = h.docs.get('a') + const c = h.docs.get('c') + await h.close('b') + expect([...h.docs.keys()]).toEqual(['a', 'c']) + expect(h.docs.get('a')).toBe(a) + expect(h.docs.get('c')).toBe(c) + await h.close('a') + expect([...h.docs.keys()]).toEqual(['c']) + expect(h.docs.get('c')).toBe(c) + await h.close('c') expect(h.docs.size).toBe(0) } finally { h.unmount() diff --git a/mobile/src/session/mobile-markdown-reader-status.test.ts b/mobile/src/session/mobile-markdown-reader-status.test.ts index 8bee01b44d6..323e1284eb2 100644 --- a/mobile/src/session/mobile-markdown-reader-status.test.ts +++ b/mobile/src/session/mobile-markdown-reader-status.test.ts @@ -12,19 +12,28 @@ const readOnlyDoc = { } describe('markdownReaderStatusText', () => { - it('names a truncated preview and the full file size the way file tabs do', () => { + it('names a truncated preview with or without the full file size', () => { expect( markdownReaderStatusText({ ...readOnlyDoc, truncated: true, byteLength: 3_000_000 }) ).toBe('Preview truncated. File size: 2.9 MB.') + expect(markdownReaderStatusText({ ...readOnlyDoc, truncated: true })).toBe('Preview truncated.') }) - it('keeps read-only, stale, and save-error states as before', () => { + it('shows read-only, stale and save-error states, and no status for a clean editable document', () => { + expect( + markdownReaderStatusText({ ...readOnlyDoc, editable: true, readOnlyReason: undefined }) + ).toBeNull() expect(markdownReaderStatusText(readOnlyDoc)).toBe('Read only') expect( markdownReaderStatusText({ ...readOnlyDoc, readOnlyReason: undefined, stale: true }) ).toBe('Changed on desktop') - expect(markdownReaderStatusText({ ...readOnlyDoc, saveError: 'Save failed' })).toBe( - 'Save failed' - ) + expect( + markdownReaderStatusText({ + ...readOnlyDoc, + editable: true, + readOnlyReason: undefined, + saveError: 'Save failed' + }) + ).toBe('Save failed') }) }) diff --git a/mobile/src/session/mobile-session-tab-activation.test.ts b/mobile/src/session/mobile-session-tab-activation.test.ts index 8c490dffca5..a84004649e8 100644 --- a/mobile/src/session/mobile-session-tab-activation.test.ts +++ b/mobile/src/session/mobile-session-tab-activation.test.ts @@ -13,7 +13,7 @@ function clientWith(sendRequest: RpcClient['sendRequest']): Pick { - it('retries terminal focus once on the authenticated replacement after cutover', async () => { + it('retries terminal focus once after cutover', async () => { const sendRequest = vi .fn() .mockRejectedValueOnce(new LogicalClientCutoverError()) diff --git a/mobile/src/session/mobile-session-tabs-accepted-effects.test.ts b/mobile/src/session/mobile-session-tabs-accepted-effects.test.ts index 54a66e3a084..f95bf3d0546 100644 --- a/mobile/src/session/mobile-session-tabs-accepted-effects.test.ts +++ b/mobile/src/session/mobile-session-tabs-accepted-effects.test.ts @@ -17,6 +17,12 @@ describe('runAcceptedMobileSessionTabsEffects', () => { const activateBrowserTab = vi.fn() const options = { effectiveTabs: [ + { + id: 'browser-other', + type: 'browser' as const, + isActive: false, + browserPageId: 'page-other' + }, { id: 'browser-1', type: 'browser' as const, @@ -39,7 +45,12 @@ describe('runAcceptedMobileSessionTabsEffects', () => { runAcceptedMobileSessionTabsEffects(options) expect(pendingPageId).toBeNull() - expect(activateBrowserTab).toHaveBeenCalledTimes(1) + expect(activateBrowserTab).toHaveBeenCalledExactlyOnceWith({ + id: 'browser-1', + type: 'browser', + isActive: true, + browserPageId: 'page-1' + }) } ) @@ -86,7 +97,12 @@ describe('runAcceptedMobileSessionTabsEffects', () => { runAcceptedMobileSessionTabsEffects({ ...base, - effectiveTabs: [markdown], + effectiveTabs: [ + { ...markdown, id: 'browser-dirty', type: 'browser' }, + { ...markdown, id: 'markdown-inactive', isActive: false }, + { ...markdown, id: 'markdown-clean', isDirty: false }, + markdown + ], source: 'stream' }) expect(markActiveMarkdownStale).toHaveBeenCalledExactlyOnceWith('markdown-1') diff --git a/mobile/src/session/opened-mobile-session-tab.test.ts b/mobile/src/session/opened-mobile-session-tab.test.ts index 4df5bb245f3..c848d0a6d3a 100644 --- a/mobile/src/session/opened-mobile-session-tab.test.ts +++ b/mobile/src/session/opened-mobile-session-tab.test.ts @@ -240,6 +240,13 @@ describe('shouldActivateOpenedMobileSessionTab', () => { activeTerminalHandle: null }) ).toBe(false) + expect( + shouldActivateOpenedMobileSessionTab({ + ...currentTerminalState, + activated: false, + activeTerminalHandle: 'pty-2' + }) + ).toBe(false) }) it('allows a structured agent-session tab to anchor chat file activation', () => { @@ -255,5 +262,16 @@ describe('shouldActivateOpenedMobileSessionTab', () => { activeTabType: 'agent-session' }) ).toBe(true) + expect( + shouldActivateOpenedMobileSessionTab({ + ...currentTerminalState, + activated: false, + sourceTerminalHandle: null, + activeTerminalHandle: null, + sourceSessionTabId: 'agent-tab-1', + activeSessionTabId: 'agent-tab-2', + activeTabType: 'agent-session' + }) + ).toBe(false) }) }) diff --git a/mobile/src/session/pending-session-selection.test.ts b/mobile/src/session/pending-session-selection.test.ts index 9a69bf53e62..0f6d7c4f46a 100644 --- a/mobile/src/session/pending-session-selection.test.ts +++ b/mobile/src/session/pending-session-selection.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest' import type { MobileSessionTab } from './mobile-session-route-types' import { - LAUNCHED_SELECTION_SNAPSHOT_BUDGET, isLaunchedSurfaceListed, launchedSelection, pendingSelectionHandle, @@ -101,13 +100,13 @@ describe('resolveLaunchedSelection', () => { it('waits without a countdown until the reply, then gives up after the budget', () => { let selection: PendingSessionSelection | null = launchedSelection('l1', { pane: PANE }, null) - for (let snapshot = 0; snapshot < LAUNCHED_SELECTION_SNAPSHOT_BUDGET * 2; snapshot += 1) { + for (let snapshot = 0; snapshot < 10; snapshot += 1) { selection = resolveLaunchedSelection(selection, []).selection } expect(selection).toEqual(launchedSelection('l1', { pane: PANE }, null)) selection = withLaunchReply(selection, 'l1', { handle: 'term_9' }) - for (let snapshot = 1; snapshot < LAUNCHED_SELECTION_SNAPSHOT_BUDGET; snapshot += 1) { + for (let snapshot = 1; snapshot < 5; snapshot += 1) { const next = resolveLaunchedSelection(selection, [chatTab('other', 'claude_other')]) expect(next.landedTabId).toBeNull() expect(next.selection?.kind).toBe('launched') diff --git a/mobile/src/session/session-markdown-hardware-back.test.ts b/mobile/src/session/session-markdown-hardware-back.test.ts index 72ee554bb67..aa9599c3705 100644 --- a/mobile/src/session/session-markdown-hardware-back.test.ts +++ b/mobile/src/session/session-markdown-hardware-back.test.ts @@ -10,6 +10,7 @@ const native = vi.hoisted(() => { platform, remove, dismiss: vi.fn(), + setLeaveDrafts: vi.fn(), addEventListener: vi.fn((_event: string, _handler: () => boolean) => ({ remove })) } }) @@ -67,7 +68,7 @@ function scopeWith(markdownDocs: Map): MobileSessionMa setMarkdownDocs: () => {}, discardMarkdownTarget: null, setDiscardMarkdownTarget: () => {}, - setLeaveDrafts: () => {}, + setLeaveDrafts: native.setLeaveDrafts, markdownSaveSeqRef: { current: new Map() }, markdownSaveInFlightRef: { current: new Set() }, showToast: () => {}, @@ -107,6 +108,7 @@ beforeEach(() => { native.addEventListener.mockClear() native.remove.mockClear() native.dismiss.mockClear() + native.setLeaveDrafts.mockClear() leaves.back = null leaves.replaced = [] }) @@ -166,6 +168,9 @@ describe("the session's hardware back gate", () => { }) expect(left).not.toHaveBeenCalled() expect(native.dismiss).toHaveBeenCalledTimes(1) + expect(native.setLeaveDrafts).toHaveBeenCalledExactlyOnceWith([ + { tabId: 'tab-1', title: 'Markdown', content: 'edited' } + ]) }) it('registers once as the drafts change, and the newest drafts are what a press reads', () => { diff --git a/mobile/src/session/session-tab-snapshot-gate.test.ts b/mobile/src/session/session-tab-snapshot-gate.test.ts index 9cb742629ec..45da4f5ac84 100644 --- a/mobile/src/session/session-tab-snapshot-gate.test.ts +++ b/mobile/src/session/session-tab-snapshot-gate.test.ts @@ -88,6 +88,10 @@ describe('applyClosedTabTombstones', () => { it('stops suppressing and clears an expired tombstone even if still present', () => { const tombstones = new Map([['a', 5000]]) + expect( + applyClosedTabTombstones([tab('a'), tab('b')], tombstones, 4999).map((t) => t.id) + ).toEqual(['b']) + expect(tombstones.has('a')).toBe(true) const result = applyClosedTabTombstones([tab('a'), tab('b')], tombstones, 5000) expect(result.map((t) => t.id)).toEqual(['a', 'b']) expect(tombstones.has('a')).toBe(false) diff --git a/src/renderer/src/lib/agent-launch-pane-verdict-application.test.ts b/src/renderer/src/lib/agent-launch-pane-verdict-application.test.ts index 57267c70c8b..0fff7f97a1f 100644 --- a/src/renderer/src/lib/agent-launch-pane-verdict-application.test.ts +++ b/src/renderer/src/lib/agent-launch-pane-verdict-application.test.ts @@ -126,6 +126,9 @@ describe("a launch pane's verdict in the window", () => { it('never touches a pane no launch laid out', () => { apply({ kind: 'withdrawn' }, '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d') expect(launchTab()).toBeDefined() + expect(launchTab()?.agentLaunchPane).toEqual({ leafId: LEAF, operationId: 'op-1' }) + expect(agentLaunchPanePrompt(tabId)).toBe('fix the build') + expect(applyClosedTerminalLeafNotice).not.toHaveBeenCalled() }) }) diff --git a/src/renderer/src/lib/launch-agent-in-new-tab-host-agents.test.ts b/src/renderer/src/lib/launch-agent-in-new-tab-host-agents.test.ts index b93e8780a63..72863ceb8e9 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab-host-agents.test.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab-host-agents.test.ts @@ -227,12 +227,13 @@ describe("a new tab during startup, before this computer's runtime answered", () it.each(['claude', 'opencode'] as const)( 'opens the %s chat when the capabilities arrive within the wait, after a failed probe', async (agent) => { + vi.useFakeTimers() const route = awaitingRoute(agent) const relaunch = fakeRelaunch() const result = launchOnceHostAnswered(route, { ...args, agent }, startupPlan, relaunch) // The failed probe has answered; the launch is still waiting for one that lands. - await new Promise((resolve) => setTimeout(resolve, 20)) + await vi.advanceTimersByTimeAsync(20) expect(relaunch).not.toHaveBeenCalled() await capabilitiesLand() @@ -240,6 +241,7 @@ describe("a new tab during startup, before this computer's runtime answered", () kind: 'structured', sessionId: 'session-1' }) + expect(relaunch).toHaveBeenCalledOnce() expect(relaunch.mock.calls[0]?.[0]).toMatchObject({ agentSessionLaunchPlan: { route: 'structured-native-chat' } }) diff --git a/src/shared/agent-session-mutation-envelope.test.ts b/src/shared/agent-session-mutation-envelope.test.ts index 028de74935a..bdc1805afc5 100644 --- a/src/shared/agent-session-mutation-envelope.test.ts +++ b/src/shared/agent-session-mutation-envelope.test.ts @@ -94,19 +94,37 @@ describe('agentSessionFingerprintConflict', () => { describe('admitAgentSessionMutation', () => { const base = { envelope: envelope(), hostFingerprint: 'f'.repeat(64), lease: LEASE } - it('admits a first-time operation under a live lease at the expected fence', () => { + it('admits a matching first-time operation and refuses a mismatched payload', () => { expect(admitAgentSessionMutation({ ...base, ledger: ADMIT('f'.repeat(64)) }).decision).toBe( 'admit' ) + expect( + admitAgentSessionMutation({ + ...base, + hostFingerprint: 'a'.repeat(64), + ledger: ADMIT('a'.repeat(64)) + }) + ).toMatchObject({ + decision: 'refused', + refusal: { + code: 'agent_session_operation_conflict', + details: { reason: 'fingerprintMismatch' } + } + }) }) - it('replays a recorded operation without re-checking the fence', () => { + it('replays a recorded outcome even while the lease is unreconciled', () => { + const recorded = { + ...row('f'.repeat(64)), + outcome: { status: 'succeeded' as const, sessionId: 'created-session' } + } const admission = admitAgentSessionMutation({ ...base, envelope: envelope({ expectedRuntimeFence: 1 }), - ledger: { decision: 'replay', row: row('f'.repeat(64)) } + lease: { ...LEASE, unreconciled: true }, + ledger: { decision: 'replay', row: recorded } }) - expect(admission.decision).toBe('replay') + expect(admission).toEqual({ decision: 'replay', row: recorded }) }) it('admits a writer whatever fence the client last saw', () => { diff --git a/src/shared/agent-session-operation-ledger.test.ts b/src/shared/agent-session-operation-ledger.test.ts index ceeb8386efe..78d6eddfd38 100644 --- a/src/shared/agent-session-operation-ledger.test.ts +++ b/src/shared/agent-session-operation-ledger.test.ts @@ -156,6 +156,7 @@ describe('retention', () => { const late = agentSessionOperationExpiry(NOW + 10_000, NOW) expect(late).toBe(agentSessionOperationExpiry(NOW + 10_000, NOW + 10_000)) expect(late).toBeGreaterThan(agentSessionOperationExpiry(NOW, NOW)) + expect(agentSessionOperationExpiry(NOW, NOW + 10_000)).toBe(late) }) it('drops only rows past their own expiry', () => { diff --git a/src/shared/agent-session-thread-goal.test.ts b/src/shared/agent-session-thread-goal.test.ts index c86c4ecdee0..81b49d8088d 100644 --- a/src/shared/agent-session-thread-goal.test.ts +++ b/src/shared/agent-session-thread-goal.test.ts @@ -71,11 +71,15 @@ describe('current thread goal', () => { it('takes the latest transition by sequence for items held unordered', () => { const paused = goal({ status: 'paused' }) + const latest = goalRow(5, 'set', paused) const unordered = new Map([ - ['b', goalRow(5, 'set', paused)], + ['b', latest], ['a', goalRow(2, 'set')] ]) - expect(currentAgentSessionThreadGoalSourceBySequence(unordered.values())?.goal).toEqual(paused) + expect(currentAgentSessionThreadGoalSourceBySequence(unordered.values())).toEqual({ + item: latest, + goal: paused + }) expect(currentAgentSessionThreadGoalSourceBySequence([])?.goal).toBeUndefined() }) diff --git a/src/shared/agent-session-write-failure.test.ts b/src/shared/agent-session-write-failure.test.ts index 7fd8b0be3f3..d9094dab0aa 100644 --- a/src/shared/agent-session-write-failure.test.ts +++ b/src/shared/agent-session-write-failure.test.ts @@ -165,20 +165,6 @@ describe('a refusal a failed request carried in its error', () => { }) }) - it("keeps a journal refusal's reason", () => { - const corrupt = { - code: 'agent_session_journal_unreadable', - details: { reason: 'journalCorrupt' } - } - // Not "Orca couldn't confirm what happened": the host refused it before running it. - expect( - agentSessionThrownFailure(saved({ ...payload, data: { refusal: corrupt } }), 'runtime_error') - ).toEqual({ - kind: 'refused', - ...corrupt - }) - }) - it("degrades a reason another build added to the code's own words", () => { const newer = { code: 'agent_session_journal_unreadable',