mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 00:02:10 +00:00
fix(mobile): send an AI button's saved agent arguments with its launch
The desktop's direct launches for "Fix checks", "Resolve conflicts" and commit recovery pass the action's saved agent arguments to agent.launch; the phone honoured the saved agent but dropped its arguments. It now sends them the same way: absent when none are saved, so the host keeps the user's configured defaults. A host that predates the field ignores it.
This commit is contained in:
@@ -50,6 +50,7 @@ export async function launchAgentInExistingWorkspace(args: {
|
||||
hostCapabilities: readonly string[] | null | undefined
|
||||
worktreeId: string
|
||||
agent: TuiAgent
|
||||
agentArgs?: string
|
||||
prompt?: AgentLaunchPrompt
|
||||
launchSource?: string
|
||||
// Injected in tests; each call is one new operation, so a later tap never replays this one.
|
||||
@@ -62,6 +63,7 @@ export async function launchAgentInExistingWorkspace(args: {
|
||||
agent: args.agent,
|
||||
worktreeId: args.worktreeId,
|
||||
operationId: (args.mintOperationId ?? structuredSessionOperationId)(),
|
||||
...(args.agentArgs !== undefined ? { agentArgs: args.agentArgs } : {}),
|
||||
...(args.prompt ? { prompt: args.prompt } : {}),
|
||||
...(args.launchSource ? { launchSource: args.launchSource } : {})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
|
||||
import type { GlobalSettings } from '../../../src/shared/global-settings-types'
|
||||
import type { Repo } from '../../../src/shared/repo-types'
|
||||
import type { SourceControlLaunchActionId } from '../../../src/shared/source-control-ai-actions'
|
||||
import type {
|
||||
SourceControlActionRecipe,
|
||||
SourceControlLaunchActionId
|
||||
} from '../../../src/shared/source-control-ai-actions'
|
||||
import { resolveSourceControlActionRecipe } from '../../../src/shared/source-control-ai'
|
||||
import {
|
||||
pickSourceControlLaunchAgent,
|
||||
@@ -17,8 +20,8 @@ import { isMobileTuiAgent } from '../tasks/mobile-tui-agents'
|
||||
import type { MobileAgentLaunchContext } from './mobile-new-tab-agent-loader'
|
||||
|
||||
export type MobileSourceControlLaunchAgent =
|
||||
| { kind: 'agent'; agent: TuiAgent }
|
||||
| { kind: 'unavailable'; message: string }
|
||||
/** `agentArgs`: the recipe's saved launch arguments, sent as the desktop sends them. */
|
||||
{ kind: 'agent'; agent: TuiAgent; agentArgs?: string } | { kind: 'unavailable'; message: string }
|
||||
|
||||
type LaunchAgentSettings = Pick<
|
||||
GlobalSettings,
|
||||
@@ -32,7 +35,8 @@ export function resolveMobileSourceControlLaunchAgent(
|
||||
): MobileSourceControlLaunchAgent {
|
||||
const settings = readLaunchAgentSettings(context.settings)
|
||||
const detectedAgents = context.detectedAgents.filter(isMobileTuiAgent)
|
||||
const savedAgent = actionId ? readSavedAgent(settings, context.repo, actionId) : null
|
||||
const recipe = actionId ? readSavedRecipe(settings, context.repo, actionId) : null
|
||||
const savedAgent = readSourceControlLaunchRecipeAgentId(recipe)
|
||||
// Why: the phone has no pre-launch dialog showing the pick, so a saved agent that can't run here
|
||||
// is an error, as on the desktop's direct launch, rather than a silent swap to another agent.
|
||||
if (
|
||||
@@ -51,7 +55,11 @@ export function resolveMobileSourceControlLaunchAgent(
|
||||
disabledAgents: settings?.disabledTuiAgents
|
||||
})
|
||||
return agent
|
||||
? { kind: 'agent', agent }
|
||||
? {
|
||||
kind: 'agent',
|
||||
agent,
|
||||
...(recipe?.agentArgs !== undefined ? { agentArgs: recipe.agentArgs } : {})
|
||||
}
|
||||
: { kind: 'unavailable', message: 'No enabled AI agent was detected on this workspace host.' }
|
||||
}
|
||||
|
||||
@@ -63,15 +71,13 @@ function readLaunchAgentSettings(settings: unknown): LaunchAgentSettings | null
|
||||
return settings as LaunchAgentSettings
|
||||
}
|
||||
|
||||
function readSavedAgent(
|
||||
function readSavedRecipe(
|
||||
settings: LaunchAgentSettings | null,
|
||||
repo: unknown,
|
||||
actionId: SourceControlLaunchActionId
|
||||
): TuiAgent | null {
|
||||
): SourceControlActionRecipe | null {
|
||||
try {
|
||||
return readSourceControlLaunchRecipeAgentId(
|
||||
resolveSourceControlActionRecipe({ settings, repo: readRepoOverrides(repo), actionId })
|
||||
)
|
||||
return resolveSourceControlActionRecipe({ settings, repo: readRepoOverrides(repo), actionId })
|
||||
} catch {
|
||||
// A recipe the phone cannot read must not block the button; the default agent still applies.
|
||||
return null
|
||||
|
||||
@@ -106,6 +106,8 @@ describe('launchAgentWithPrompt', () => {
|
||||
prompt: { text: 'Fix the failing checks', delivery: 'submit' },
|
||||
launchSource: 'task_page'
|
||||
})
|
||||
// No saved arguments: the host applies the user's configured defaults.
|
||||
expect(launchParams(sendRequest)).not.toHaveProperty('agentArgs')
|
||||
expect(sendRequest.mock.calls.some(([method]) => method === 'terminal.send')).toBe(false)
|
||||
expect(
|
||||
sendRequest.mock.calls.some(([method]) => method === 'session.tabs.createTerminal')
|
||||
@@ -124,6 +126,18 @@ describe('launchAgentWithPrompt', () => {
|
||||
expect(launchParams(sendRequest)).toMatchObject({ agent: 'claude' })
|
||||
})
|
||||
|
||||
it("sends the action's saved agent arguments, as the desktop does", async () => {
|
||||
const { client, sendRequest } = hostClient({
|
||||
settings: {
|
||||
defaultTuiAgent: 'claude',
|
||||
sourceControlAi: { actions: { fixChecks: { agentArgs: '--model opus' } } }
|
||||
},
|
||||
launch: launchedWith({ delivery: 'submit', outcome: 'handed-to-terminal' })
|
||||
})
|
||||
await run(client)
|
||||
expect(launchParams(sendRequest)).toMatchObject({ agent: 'claude', agentArgs: '--model opus' })
|
||||
})
|
||||
|
||||
it('refuses rather than swaps when the saved agent is not on this host', async () => {
|
||||
const { client, sendRequest } = hostClient({
|
||||
settings: { sourceControlAi: { actions: { fixChecks: { agentId: 'gemini' } } } },
|
||||
|
||||
@@ -52,6 +52,7 @@ export async function launchAgentWithPrompt(args: {
|
||||
hostCapabilities: args.hostCapabilities,
|
||||
worktreeId: args.worktreeId,
|
||||
agent: resolved.agent,
|
||||
...(resolved.agentArgs !== undefined ? { agentArgs: resolved.agentArgs } : {}),
|
||||
prompt: { text: args.prompt, delivery: 'submit' },
|
||||
launchSource: args.launchSource
|
||||
})
|
||||
|
||||
@@ -80,6 +80,7 @@ export function agentLaunchExistingParams(args: {
|
||||
agent: TuiAgent
|
||||
worktreeId: string
|
||||
operationId: string
|
||||
agentArgs?: string
|
||||
prompt?: AgentLaunchPrompt
|
||||
launchSource?: string
|
||||
}): RpcSendParams<'agent.launchReplay'> {
|
||||
@@ -87,6 +88,7 @@ export function agentLaunchExistingParams(args: {
|
||||
agent: args.agent,
|
||||
operationId: args.operationId,
|
||||
target: { kind: 'existing', worktree: `id:${args.worktreeId}` },
|
||||
...(args.agentArgs !== undefined ? { agentArgs: args.agentArgs } : {}),
|
||||
...(args.prompt ? { prompt: args.prompt } : {}),
|
||||
...(args.launchSource ? { launchSource: args.launchSource } : {})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user