fix(opencode): pass run prompts as positional messages

Preserve run flags and use the existing shell quoting and run-command detector
to append the initial message after --, reusing an existing separator.
TUI launches retain their version-selected prompt transport and draft behavior.

Original run-order work: @coelho-doti (#13065, tracked in #17551).
This commit is contained in:
Neil
2026-10-02 20:56:53 -07:00
parent 1679ae3bb6
commit 23fc08b4e9
3 changed files with 115 additions and 13 deletions
+50
View File
@@ -0,0 +1,50 @@
import type { AgentStartupPlan } from './tui-agent-startup'
import type { SleepingAgentLaunchConfig } from './agent-session-resume'
import type { SessionOptionValue } from './native-chat-session-options'
import type { TuiAgent } from './tui-agent'
import { TUI_AGENT_CONFIG } from './tui-agent-config'
import { tokenizeStartupCommand, type AgentStartupShell } from './tui-agent-startup-shell'
import { isOpenCodeRunCommand } from './opencode-headless-command'
import { openCodeStartupPromptEnv } from './opencode-startup-prompt'
export function appliedSessionOptionProps(values: Record<string, SessionOptionValue>) {
return Object.keys(values).length > 0 ? { sessionOptions: { ...values } } : {}
}
export function buildFlagPromptStartupPlan(args: {
agent: TuiAgent
launchCommand: string
quotedPrompt: string
prompt: string
shell: AgentStartupShell
launchConfig: SleepingAgentLaunchConfig
sessionOptions: Record<string, SessionOptionValue>
agentEnv: Record<string, string> | null | undefined
}): AgentStartupPlan {
const parsed = tokenizeStartupCommand(args.launchCommand, args.shell)
const isOpenCodeRun =
(args.agent === 'opencode' || args.agent === 'opencode2') &&
parsed.ok &&
isOpenCodeRunCommand(parsed.tokens)
// OpenCode run takes a positional message; --prompt belongs to its TUI.
const promptSuffix = isOpenCodeRun
? parsed.tokens.includes('--')
? ` ${args.quotedPrompt}`
: ` -- ${args.quotedPrompt}`
: ` --prompt ${args.quotedPrompt}`
return {
agent: args.agent,
launchCommand: `${args.launchCommand}${promptSuffix}`,
expectedProcess: TUI_AGENT_CONFIG[args.agent].expectedProcess,
followupPrompt: null,
launchConfig: args.launchConfig,
...appliedSessionOptionProps(args.sessionOptions),
...openCodeStartupPromptEnv(
args.agent,
args.launchCommand,
args.shell,
args.prompt,
args.agentEnv
)
}
}
+56 -1
View File
@@ -6,6 +6,7 @@ import {
OPENCODE_STARTUP_PROMPT_BODY_ENV,
OPENCODE_STARTUP_PROMPT_SHELL_ENV
} from './opencode-startup-prompt'
import { tokenizeStartupCommand } from './tui-agent-startup-shell'
describe('native OpenCode startup submission intent', () => {
it('binds the exact trimmed native prompt to host-selectable transport', () => {
@@ -33,9 +34,63 @@ describe('native OpenCode startup submission intent', () => {
platform: 'linux'
})
expect(plan?.env).toBeUndefined()
expect(plan?.launchCommand).toContain('run --prompt')
expect(plan?.launchCommand).toContain("run -- 'task'")
})
it.each(['posix', 'powershell', 'cmd'] as const)(
'keeps run and its flags before a positional message in %s',
(shell) => {
for (const agent of ['opencode', 'opencode2'] as const) {
const prompt = '--literal task with unicode é'
const plan = buildAgentStartupPlan({
agent,
prompt,
cmdOverrides: { [agent]: 'opencode --log-level debug run --standalone' },
platform: shell === 'posix' ? 'linux' : 'win32',
shell,
agentEnv: { CUSTOM_CONFIG: 'kept' }
})
expect(plan).not.toBeNull()
const parsed = tokenizeStartupCommand(plan?.launchCommand ?? '', shell)
expect(parsed.ok).toBe(true)
if (!parsed.ok) {
throw new Error(parsed.error)
}
expect(parsed.tokens).toEqual([
'opencode',
'--log-level',
'debug',
'run',
'--standalone',
'--',
prompt
])
expect(plan?.env).toEqual({ CUSTOM_CONFIG: 'kept' })
expect(plan?.followupPrompt).toBeNull()
}
}
)
it.each(['posix', 'powershell', 'cmd'] as const)(
'reuses an existing run message separator in %s',
(shell) => {
const plan = buildAgentStartupPlan({
agent: 'opencode',
prompt: '--literal task',
cmdOverrides: { opencode: 'opencode run --standalone --' },
platform: shell === 'posix' ? 'linux' : 'win32',
shell
})
const parsed = tokenizeStartupCommand(plan?.launchCommand ?? '', shell)
expect(parsed.ok).toBe(true)
if (!parsed.ok) {
throw new Error(parsed.error)
}
expect(parsed.tokens).toEqual(['opencode', 'run', '--standalone', '--', '--literal task'])
expect(plan?.env).toBeUndefined()
}
)
it('never gives an editable draft or empty launch an automatic submission intent', () => {
const args = { agent: 'opencode' as const, cmdOverrides: {}, platform: 'linux' as const }
expect(
+9 -12
View File
@@ -17,7 +17,7 @@ import { inlineAgentDraftFitsPlatform } from './agent-draft-platform-limit'
import type { TuiAgent } from './tui-agent'
import type { SessionOptionValue } from './native-chat-session-options'
import { resolveAgentLaunchCommand } from './tui-agent-launch-command'
import { openCodeStartupPromptEnv } from './opencode-startup-prompt'
import { appliedSessionOptionProps, buildFlagPromptStartupPlan } from './flag-prompt-startup'
export { buildAgentResumeStartupPlan } from './tui-agent-resume-startup'
@@ -36,10 +36,6 @@ export type AgentStartupPlan = {
sessionOptions?: Record<string, SessionOptionValue>
}
function appliedSessionOptionProps(values: Record<string, SessionOptionValue>) {
return Object.keys(values).length > 0 ? { sessionOptions: { ...values } } : {}
}
export function buildAgentStartupPlan(args: {
agent: TuiAgent
prompt: string
@@ -117,15 +113,16 @@ export function buildAgentStartupPlan(args: {
}
if (config.promptInjectionMode === 'flag-prompt') {
return {
return buildFlagPromptStartupPlan({
agent,
launchCommand: `${launchCommand} --prompt ${quotedPrompt}`,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchCommand,
quotedPrompt,
prompt: trimmedPrompt,
shell,
launchConfig,
...appliedSessionOptionProps(baseCommand.appliedSessionOptions),
...openCodeStartupPromptEnv(agent, launchCommand, shell, trimmedPrompt, args.agentEnv)
}
sessionOptions: baseCommand.appliedSessionOptions,
agentEnv: args.agentEnv
})
}
if (config.promptInjectionMode === 'hermes-query') {