mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 08:03:14 +00:00
fix(native-chat): preserve initial chat mode on paired-host launches
This commit is contained in:
@@ -17074,7 +17074,9 @@ describe('OrcaRuntimeService', () => {
|
||||
})
|
||||
runtime.syncWindowGraph(0, { tabs: [], leaves: [] })
|
||||
|
||||
const result = await runtime.createMobileSessionTerminal(`id:${TEST_WORKTREE_ID}`)
|
||||
const result = await runtime.createMobileSessionTerminal(`id:${TEST_WORKTREE_ID}`, {
|
||||
viewMode: 'chat'
|
||||
})
|
||||
|
||||
expect(spawn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -17090,6 +17092,7 @@ describe('OrcaRuntimeService', () => {
|
||||
type: 'terminal',
|
||||
status: 'ready',
|
||||
terminal: expect.stringMatching(/^term_/),
|
||||
viewMode: 'chat',
|
||||
isActive: true
|
||||
})
|
||||
|
||||
@@ -20138,7 +20141,8 @@ describe('OrcaRuntimeService', () => {
|
||||
})
|
||||
|
||||
const result = await runtime.createMobileSessionTerminal(`id:${TEST_WORKTREE_ID}`, {
|
||||
activate: false
|
||||
activate: false,
|
||||
viewMode: 'chat'
|
||||
})
|
||||
|
||||
expect(send).toHaveBeenCalledWith(
|
||||
@@ -20146,7 +20150,8 @@ describe('OrcaRuntimeService', () => {
|
||||
expect.objectContaining({
|
||||
worktreeId: TEST_WORKTREE_ID,
|
||||
activate: false,
|
||||
source: 'runtime-session'
|
||||
source: 'runtime-session',
|
||||
viewMode: 'chat'
|
||||
})
|
||||
)
|
||||
expect(focusTerminal).not.toHaveBeenCalled()
|
||||
|
||||
@@ -17869,6 +17869,7 @@ export class OrcaRuntimeService {
|
||||
agent?: TuiAgent
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
activate?: boolean
|
||||
clientMutationId?: string
|
||||
signal?: AbortSignal
|
||||
@@ -17913,6 +17914,7 @@ export class OrcaRuntimeService {
|
||||
agent?: TuiAgent
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
activate?: boolean
|
||||
clientMutationId?: string
|
||||
signal?: AbortSignal
|
||||
@@ -17946,6 +17948,7 @@ export class OrcaRuntimeService {
|
||||
env: startupCommand.env,
|
||||
startupCommandDelivery: startupCommand.startupCommandDelivery,
|
||||
launchAgent: startupCommand.launchAgent,
|
||||
viewMode: opts.viewMode,
|
||||
targetGroupId: opts.targetGroupId,
|
||||
launchConfig: startupCommand.launchConfig
|
||||
}
|
||||
@@ -17994,6 +17997,7 @@ export class OrcaRuntimeService {
|
||||
...(startupCommand.env ? { env: startupCommand.env } : {}),
|
||||
...(startupCommand.launchConfig ? { launchConfig: startupCommand.launchConfig } : {}),
|
||||
...(startupCommand.launchAgent ? { launchAgent: startupCommand.launchAgent } : {}),
|
||||
...(opts.viewMode ? { viewMode: opts.viewMode } : {}),
|
||||
startupCommandDelivery: startupCommand.startupCommandDelivery,
|
||||
source: 'runtime-session',
|
||||
activate: opts.activate
|
||||
@@ -18057,6 +18061,7 @@ export class OrcaRuntimeService {
|
||||
startupCommandDelivery: startupCommand.startupCommandDelivery,
|
||||
identity: { tabId: pendingSurface.tab.parentTabId, leafId: pendingSurface.tab.leafId },
|
||||
launchAgent: startupCommand.launchAgent,
|
||||
viewMode: opts.viewMode,
|
||||
targetGroupId: opts.targetGroupId,
|
||||
launchConfig: startupCommand.launchConfig
|
||||
}
|
||||
@@ -18180,6 +18185,7 @@ export class OrcaRuntimeService {
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
identity?: { tabId: string; leafId: string; sessionId?: string }
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
targetGroupId?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
} = {}
|
||||
@@ -18240,6 +18246,7 @@ export class OrcaRuntimeService {
|
||||
title: terminal.title ?? livePty.pty.title ?? 'Terminal',
|
||||
...(cwd ? { startupCwd: cwd } : {}),
|
||||
...(opts.launchAgent ? { launchAgent: opts.launchAgent } : {}),
|
||||
...(opts.viewMode ? { viewMode: opts.viewMode } : {}),
|
||||
parentLayout,
|
||||
isActive: activate
|
||||
}
|
||||
|
||||
@@ -130,6 +130,7 @@ export const CreateTerminalTab = WorktreeTabSelector.extend({
|
||||
message: 'Unknown launch agent'
|
||||
})
|
||||
.optional(),
|
||||
viewMode: z.enum(['terminal', 'chat']).optional(),
|
||||
activate: z.boolean().optional(),
|
||||
// Why: idempotency key so a retried create (double-tap, reconnect replay)
|
||||
// returns the in-flight operation instead of spawning a duplicate terminal.
|
||||
|
||||
@@ -148,6 +148,7 @@ describe('session tab RPC methods', () => {
|
||||
agentEnv: { CODEX_PROFILE: 'captured' }
|
||||
},
|
||||
launchAgent: 'codex',
|
||||
viewMode: 'chat',
|
||||
activate: true
|
||||
})
|
||||
)
|
||||
@@ -167,6 +168,7 @@ describe('session tab RPC methods', () => {
|
||||
agentEnv: { CODEX_PROFILE: 'captured' }
|
||||
},
|
||||
launchAgent: 'codex',
|
||||
viewMode: 'chat',
|
||||
activate: true
|
||||
})
|
||||
})
|
||||
|
||||
@@ -53,6 +53,7 @@ export const SESSION_TAB_METHODS: RpcAnyMethod[] = [
|
||||
...(params.launchConfig ? { launchConfig: params.launchConfig } : {}),
|
||||
...(params.launchToken ? { launchToken: params.launchToken } : {}),
|
||||
...(params.launchAgent ? { launchAgent: params.launchAgent } : {}),
|
||||
...(params.viewMode ? { viewMode: params.viewMode } : {}),
|
||||
activate: params.activate,
|
||||
clientMutationId: params.clientMutationId,
|
||||
// Why: a dead client connection must cancel the surface wait instead
|
||||
|
||||
@@ -1760,6 +1760,7 @@ describe('useIpcEvents updater integration', () => {
|
||||
cwd?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
title?: string
|
||||
activate?: boolean
|
||||
presentation?: 'background' | 'focused'
|
||||
@@ -1906,6 +1907,7 @@ describe('useIpcEvents updater integration', () => {
|
||||
cwd?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
title?: string
|
||||
activate?: boolean
|
||||
presentation?: 'background' | 'focused'
|
||||
@@ -2223,11 +2225,16 @@ describe('useIpcEvents updater integration', () => {
|
||||
targetGroupId: 'group-left',
|
||||
title: 'Runtime Terminal',
|
||||
command: 'codex',
|
||||
launchAgent: 'codex',
|
||||
viewMode: 'terminal',
|
||||
activate: true,
|
||||
source: 'runtime-session'
|
||||
})
|
||||
|
||||
expect(createTab).toHaveBeenCalledWith('wt-2', 'group-left', undefined, undefined)
|
||||
expect(createTab).toHaveBeenCalledWith('wt-2', 'group-left', undefined, {
|
||||
launchAgent: 'codex',
|
||||
viewMode: 'terminal'
|
||||
})
|
||||
expect(replyTerminalCreate).toHaveBeenCalledWith({
|
||||
requestId: 'req-runtime-session',
|
||||
tabId: 'tab-new',
|
||||
|
||||
@@ -1684,16 +1684,20 @@ export function useIpcEvents(): void {
|
||||
if (shouldActivate) {
|
||||
activateTerminalInitiatedWorktree(store, worktreeId)
|
||||
}
|
||||
// Why: the paired launch client already resolved the initial mode, so
|
||||
// its explicit choice must win over this host renderer's local default.
|
||||
const tabOptions = data.launchAgent
|
||||
? {
|
||||
...(shouldActivate ? {} : { activate: false, recordInteraction: false }),
|
||||
launchAgent: data.launchAgent,
|
||||
...initialAgentTabViewModeProps(store.settings, {
|
||||
agent: data.launchAgent,
|
||||
nativeChatTranscriptIsLocalReadable: isNativeChatTranscriptLocalReadable(
|
||||
getConnectionIdFromState(store, worktreeId)
|
||||
)
|
||||
}),
|
||||
...(data.viewMode
|
||||
? { viewMode: data.viewMode }
|
||||
: initialAgentTabViewModeProps(store.settings, {
|
||||
agent: data.launchAgent,
|
||||
nativeChatTranscriptIsLocalReadable: isNativeChatTranscriptLocalReadable(
|
||||
getConnectionIdFromState(store, worktreeId)
|
||||
)
|
||||
})),
|
||||
...(data.cwd ? { startupCwd: data.cwd } : {})
|
||||
}
|
||||
: shouldActivate
|
||||
|
||||
@@ -351,6 +351,30 @@ describe('launchAgentInNewTab', () => {
|
||||
expect(mockQueueTabStartupCommand).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('propagates the default chat mode to paired web runtime launches', async () => {
|
||||
mockIsWebRuntimeSessionActive.mockReturnValue(true)
|
||||
store.settings = {
|
||||
agentCmdOverrides: {},
|
||||
agentDefaultArgs: {},
|
||||
agentDefaultEnv: {},
|
||||
activeRuntimeEnvironmentId: 'web-runtime',
|
||||
experimentalNativeChat: true,
|
||||
openAgentTabsInChatByDefault: true
|
||||
}
|
||||
const { launchAgentInNewTab } = await import('./launch-agent-in-new-tab')
|
||||
|
||||
launchAgentInNewTab({ agent: 'codex', worktreeId: 'wt-1' })
|
||||
|
||||
expect(mockCreateWebRuntimeSessionTerminal).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
worktreeId: 'wt-1',
|
||||
environmentId: 'web-runtime',
|
||||
agent: 'codex',
|
||||
viewMode: 'chat'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('surfaces a toast when host agent launch fails in paired web clients', async () => {
|
||||
mockIsWebRuntimeSessionActive.mockReturnValue(true)
|
||||
mockCreateWebRuntimeSessionTerminal.mockResolvedValue(false)
|
||||
|
||||
@@ -201,6 +201,18 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI
|
||||
return null
|
||||
}
|
||||
|
||||
// Why: host-owned paired tabs must receive the same initial-view decision as
|
||||
// local tabs; the remote host cannot infer this client's draft/default choice.
|
||||
const viewModePromptDelivery =
|
||||
hasPrompt && isFollowupPath && promptDelivery === 'auto-submit' ? 'draft' : promptDelivery
|
||||
const initialViewModeProps = initialAgentTabViewModeProps(store.settings, {
|
||||
agent,
|
||||
promptDelivery: viewModePromptDelivery,
|
||||
nativeChatTranscriptIsLocalReadable: isNativeChatTranscriptLocalReadable(
|
||||
getConnectionIdFromState(store, worktreeId)
|
||||
)
|
||||
})
|
||||
|
||||
const runtimeEnvironmentId = getRuntimeEnvironmentIdForWorktree(store, worktreeId)
|
||||
if (isWebRuntimeSessionActive(runtimeEnvironmentId) && pasteDraftAfterLaunch === null) {
|
||||
launchAgentInWebHostTab({
|
||||
@@ -210,6 +222,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI
|
||||
groupId,
|
||||
hasPrompt,
|
||||
startupPlan,
|
||||
...initialViewModeProps,
|
||||
onPromptDelivered
|
||||
})
|
||||
return { tabId: null, startupPlan, pasteDraftAfterLaunch: false }
|
||||
@@ -224,18 +237,10 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI
|
||||
// stays false), so gate the initial chat view like a `draft` launch —
|
||||
// otherwise a default `auto-submit` followup would open native chat with no
|
||||
// submitted turn to render.
|
||||
const viewModePromptDelivery =
|
||||
hasPrompt && isFollowupPath && promptDelivery === 'auto-submit' ? 'draft' : promptDelivery
|
||||
const tab = store.createTab(worktreeId, groupId, undefined, {
|
||||
launchAgent: agent,
|
||||
quickCommandLabel,
|
||||
...initialAgentTabViewModeProps(store.settings, {
|
||||
agent,
|
||||
promptDelivery: viewModePromptDelivery,
|
||||
nativeChatTranscriptIsLocalReadable: isNativeChatTranscriptLocalReadable(
|
||||
getConnectionIdFromState(store, worktreeId)
|
||||
)
|
||||
})
|
||||
...initialViewModeProps
|
||||
})
|
||||
store.queueTabStartupCommand(tab.id, {
|
||||
command: startupPlan.launchCommand,
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createWebRuntimeSessionTerminal,
|
||||
isWebTerminalSurfaceTabId
|
||||
} from '@/runtime/web-runtime-session'
|
||||
import type { TuiAgent } from '../../../shared/types'
|
||||
import type { Tab, TuiAgent } from '../../../shared/types'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
function removeStaleLocalAgentTabsForWebHostLaunch(worktreeId: string): void {
|
||||
@@ -32,16 +32,26 @@ export function launchAgentInWebHostTab(args: {
|
||||
groupId?: string
|
||||
hasPrompt: boolean
|
||||
startupPlan: AgentStartupPlan
|
||||
viewMode?: Tab['viewMode']
|
||||
onPromptDelivered?: () => void
|
||||
}): void {
|
||||
const { agent, worktreeId, environmentId, groupId, hasPrompt, startupPlan, onPromptDelivered } =
|
||||
args
|
||||
const {
|
||||
agent,
|
||||
worktreeId,
|
||||
environmentId,
|
||||
groupId,
|
||||
hasPrompt,
|
||||
startupPlan,
|
||||
viewMode,
|
||||
onPromptDelivered
|
||||
} = args
|
||||
removeStaleLocalAgentTabsForWebHostLaunch(worktreeId)
|
||||
void createWebRuntimeSessionTerminal({
|
||||
worktreeId,
|
||||
environmentId,
|
||||
targetGroupId: groupId,
|
||||
activate: true,
|
||||
...(viewMode ? { viewMode } : {}),
|
||||
...(hasPrompt
|
||||
? {
|
||||
command: startupPlan.launchCommand,
|
||||
|
||||
@@ -490,6 +490,7 @@ describe('createWebRuntimeSessionTerminal', () => {
|
||||
agentEnv: { CODEX_PROFILE: 'captured' }
|
||||
},
|
||||
launchAgent: 'codex',
|
||||
viewMode: 'chat',
|
||||
activate: true
|
||||
})
|
||||
).resolves.toBe(true)
|
||||
@@ -510,6 +511,7 @@ describe('createWebRuntimeSessionTerminal', () => {
|
||||
agentEnv: { CODEX_PROFILE: 'captured' }
|
||||
},
|
||||
launchAgent: 'codex',
|
||||
viewMode: 'chat',
|
||||
activate: true
|
||||
},
|
||||
timeoutMs: 15_000
|
||||
|
||||
@@ -56,6 +56,7 @@ export async function createWebRuntimeSessionTerminal(args: {
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
agent?: TuiAgent
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
activate?: boolean
|
||||
selectWorktree?: boolean
|
||||
}): Promise<boolean> {
|
||||
@@ -85,6 +86,7 @@ export async function createWebRuntimeSessionTerminal(args: {
|
||||
...(args.launchConfig ? { launchConfig: args.launchConfig } : {}),
|
||||
agent: args.agent,
|
||||
...(args.launchAgent ? { launchAgent: args.launchAgent } : {}),
|
||||
...(args.viewMode ? { viewMode: args.viewMode } : {}),
|
||||
activate: args.activate !== false
|
||||
},
|
||||
timeoutMs: 15_000
|
||||
|
||||
@@ -490,6 +490,7 @@ type RuntimeTerminalCreateBaseRequestPayload = {
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchToken?: string
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
startupCommandDelivery?: StartupCommandDelivery
|
||||
title?: string
|
||||
activate?: boolean
|
||||
|
||||
Reference in New Issue
Block a user