mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
* fix: wait for OpenCode worker composer before first dispatch Reuse captured composer readiness on local and paired execution hosts and revoke launching-shell paste anchors. Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> * feat(opencode): probe execution-host CLI capabilities * fix(opencode): select plugin default for execution host loader * fix(opencode): limit prompt prefill capability to verified release * feat(opencode): probe launch capabilities on the execution host * fix(opencode): select plugin loader for the launched host binary * fix(opencode): match WSL probe cwd and declared guest environment * fix(opencode): preserve launch environment deletion boundaries * wip(opencode): authorize native startup prompt intent at execution owner * fix(opencode): atomically replace status plugin entrypoints * fix(opencode): retain plugin permissions across restrictive umasks * test(opencode): resolve permission fixture from primary cwd * feat(opencode): install startup prompt plugin independently of status hooks * fix(opencode): wait for admitted startup intent and preserve failed-launch briefs * fix(opencode): unsubscribe hook settings during async host shutdown * STRICT launch CI contract correction * CAPS launch CI contract correction * INTENT launch CI contract correction * test: initialize Claude prompt state in output retention fixture * Wait for OpenCode location hydration in intent startup * Bind OpenCode startup readiness to the current location in intent startup * Retry interrupted OpenCode startup prompt claims --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com> Co-authored-by: Orca startup hydration review <agents@stably.ai> Co-authored-by: Orca <dev@stably.ai>
140 lines
5.1 KiB
TypeScript
140 lines
5.1 KiB
TypeScript
import { addWslEnvKeys } from '../../shared/wsl-env'
|
|
import type { TuiAgent } from '../../shared/tui-agent'
|
|
import { randomUUID, createHash } from 'node:crypto'
|
|
import { agentHookServer } from '../agent-hooks/server'
|
|
import { tokenizeStartupCommand } from '../../shared/tui-agent-startup-shell'
|
|
import { isOpenCodeRunCommand } from '../../shared/opencode-headless-command'
|
|
import {
|
|
OPENCODE_STARTUP_PROMPT_SHA256_ENV,
|
|
OPENCODE_STARTUP_PROMPT_NONCE_ENV,
|
|
OPENCODE_STARTUP_PROMPT_ENDPOINT_ENV,
|
|
OPENCODE_STARTUP_PROMPT_BODY_ENV,
|
|
OPENCODE_STARTUP_PROMPT_SHELL_ENV
|
|
} from '../../shared/opencode-startup-prompt'
|
|
|
|
const intentKeys = [
|
|
OPENCODE_STARTUP_PROMPT_SHA256_ENV,
|
|
OPENCODE_STARTUP_PROMPT_NONCE_ENV,
|
|
OPENCODE_STARTUP_PROMPT_ENDPOINT_ENV,
|
|
OPENCODE_STARTUP_PROMPT_BODY_ENV,
|
|
OPENCODE_STARTUP_PROMPT_SHELL_ENV
|
|
]
|
|
|
|
import { deleteRequestedEnvKeys } from '../ipc/pty/host-env/path'
|
|
import { probeOpenCodeLaunchCapabilities } from './opencode-launch-capabilities'
|
|
import { reserveOpenCodeStartupPrompt } from './opencode-startup-prompt-owner'
|
|
import { installOpenCodeStartupPromptForLaunch } from './opencode-startup-prompt-installer'
|
|
|
|
export async function prepareOpenCodePtyLaunch(options: {
|
|
command: string | undefined
|
|
agent?: TuiAgent
|
|
env: Record<string, string> | undefined
|
|
envToDelete: string[]
|
|
cwd?: string
|
|
connectionId?: string | null
|
|
isFreshLaunch: boolean
|
|
wsl?: { distro?: string }
|
|
}): Promise<{ env: Record<string, string> | undefined; command: string | undefined }> {
|
|
let command = options.command
|
|
const env = options.env ? { ...options.env } : undefined
|
|
const requestedPrompt = env?.[OPENCODE_STARTUP_PROMPT_SHA256_ENV]
|
|
const body = env?.[OPENCODE_STARTUP_PROMPT_BODY_ENV]
|
|
const shell = env?.[OPENCODE_STARTUP_PROMPT_SHELL_ENV]
|
|
if (env) {
|
|
delete env.ORCA_OPENCODE_PLUGIN_API
|
|
for (const key of intentKeys) {
|
|
delete env[key]
|
|
}
|
|
}
|
|
// Providers merge their own ambient environment after this preparation.
|
|
if (!options.envToDelete.includes('ORCA_OPENCODE_PLUGIN_API')) {
|
|
options.envToDelete.push('ORCA_OPENCODE_PLUGIN_API')
|
|
}
|
|
for (const key of intentKeys) {
|
|
if (!options.envToDelete.includes(key)) {
|
|
options.envToDelete.push(key)
|
|
}
|
|
}
|
|
if (options.connectionId || !options.isFreshLaunch) {
|
|
return { env, command }
|
|
}
|
|
const probeEnv: Record<string, string> = {}
|
|
for (const [key, value] of Object.entries({ ...process.env, ...env })) {
|
|
if (value !== undefined) {
|
|
probeEnv[key] = value
|
|
}
|
|
}
|
|
deleteRequestedEnvKeys(probeEnv, options.envToDelete)
|
|
const capabilities = await probeOpenCodeLaunchCapabilities({
|
|
...options,
|
|
env: probeEnv
|
|
})
|
|
if (!capabilities || capabilities.pluginApi === 'unknown') {
|
|
return { env, command }
|
|
}
|
|
const launchEnv: Record<string, string> = {
|
|
...env,
|
|
ORCA_OPENCODE_PLUGIN_API: capabilities.pluginApi
|
|
}
|
|
options.envToDelete.splice(options.envToDelete.indexOf('ORCA_OPENCODE_PLUGIN_API'), 1)
|
|
if (
|
|
capabilities.promptMode === 'prefill' &&
|
|
!options.wsl &&
|
|
launchEnv.ORCA_AGENT_LAUNCH_TOKEN &&
|
|
requestedPrompt &&
|
|
body &&
|
|
command &&
|
|
(shell === 'posix' || shell === 'powershell' || shell === 'cmd') &&
|
|
createHash('sha256').update(body).digest('hex') === requestedPrompt
|
|
) {
|
|
const parsed = tokenizeStartupCommand(command, shell)
|
|
const last = parsed.ok ? parsed.tokens.length - 1 : -1
|
|
if (
|
|
parsed.ok &&
|
|
!isOpenCodeRunCommand(parsed.tokens, shell) &&
|
|
parsed.tokens.filter((token) => token === '--prompt').length === 1 &&
|
|
parsed.tokens[last - 1] === '--prompt' &&
|
|
parsed.tokens[last] === body &&
|
|
!parsed.spans[last].divergesFromShell &&
|
|
!parsed.spans[last - 1].divergesFromShell
|
|
) {
|
|
const endpoint = agentHookServer.endpointFilePath
|
|
if (endpoint) {
|
|
launchEnv[OPENCODE_STARTUP_PROMPT_SHA256_ENV] = requestedPrompt
|
|
launchEnv[OPENCODE_STARTUP_PROMPT_BODY_ENV] = body
|
|
launchEnv[OPENCODE_STARTUP_PROMPT_NONCE_ENV] = randomUUID()
|
|
launchEnv[OPENCODE_STARTUP_PROMPT_ENDPOINT_ENV] = endpoint
|
|
if (installOpenCodeStartupPromptForLaunch(launchEnv, false, probeEnv)) {
|
|
for (const key of [
|
|
'OPENCODE_CONFIG_DIR',
|
|
'ORCA_OPENCODE_CONFIG_DIR',
|
|
'ORCA_OPENCODE_SOURCE_CONFIG_DIR'
|
|
]) {
|
|
const deletion = options.envToDelete.indexOf(key)
|
|
if (launchEnv[key] && deletion !== -1) {
|
|
options.envToDelete.splice(deletion, 1)
|
|
}
|
|
}
|
|
}
|
|
const nonce = launchEnv[OPENCODE_STARTUP_PROMPT_NONCE_ENV]
|
|
if (nonce && reserveOpenCodeStartupPrompt(nonce, requestedPrompt)) {
|
|
command = command.slice(0, parsed.spans[last - 1].start).trimEnd()
|
|
} else {
|
|
for (const key of intentKeys) {
|
|
delete launchEnv[key]
|
|
}
|
|
}
|
|
for (const key of intentKeys) {
|
|
if (launchEnv[key]) {
|
|
options.envToDelete.splice(options.envToDelete.indexOf(key), 1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (options.wsl) {
|
|
addWslEnvKeys(launchEnv, ['ORCA_OPENCODE_PLUGIN_API'])
|
|
}
|
|
return { env: launchEnv, command }
|
|
}
|