mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 08:02:21 +00:00
Submit initial OpenCode 2.0.12 prompts through the native startup plugin (#25428)
* fix(opencode): submit initial prompts for reviewed 2.0.12 Route the reported release through the existing native startup intent. Retain conservative behavior for other unreviewed versions. * Wait for OpenCode startup fixture file reads in the hydration test --------- Co-authored-by: nwparker <nwparker@users.noreply.github.com>
This commit is contained in:
@@ -91,32 +91,40 @@ describe('execution-host OpenCode launch preparation', () => {
|
||||
expect(finalEnv.OPENCODE_CONFIG_DIR).toBe('/private/owned-overlay')
|
||||
expect(finalEnv.ORCA_OPENCODE_SOURCE_CONFIG_DIR).toBe('/private/real-source')
|
||||
})
|
||||
it('removes only the automatic verified v2 prompt argument, retaining explicit run and manual flags', async () => {
|
||||
probe.mockResolvedValue(getOpenCodeCliCapabilities('2.0.16'))
|
||||
const env = {
|
||||
ORCA_AGENT_LAUNCH_TOKEN: 'admitted-launch',
|
||||
[OPENCODE_STARTUP_PROMPT_SHA256_ENV]: createHash('sha256').update('task').digest('hex'),
|
||||
[OPENCODE_STARTUP_PROMPT_BODY_ENV]: 'task',
|
||||
[OPENCODE_STARTUP_PROMPT_SHELL_ENV]: 'posix'
|
||||
it.each(['2.0.12', '2.0.16'])(
|
||||
'removes only the automatic %s prompt argument, retaining explicit run and manual flags',
|
||||
async (version) => {
|
||||
probe.mockResolvedValue(getOpenCodeCliCapabilities(version))
|
||||
const env = {
|
||||
ORCA_AGENT_LAUNCH_TOKEN: 'admitted-launch',
|
||||
[OPENCODE_STARTUP_PROMPT_SHA256_ENV]: createHash('sha256').update('task').digest('hex'),
|
||||
[OPENCODE_STARTUP_PROMPT_BODY_ENV]: 'task',
|
||||
[OPENCODE_STARTUP_PROMPT_SHELL_ENV]: 'posix'
|
||||
}
|
||||
const options = { env, envToDelete: [], isFreshLaunch: true }
|
||||
expect(
|
||||
(
|
||||
await prepareOpenCodePtyLaunch({
|
||||
...options,
|
||||
command: "opencode --standalone --prompt 'task'"
|
||||
})
|
||||
).command
|
||||
).toBe('opencode --standalone')
|
||||
expect(
|
||||
(await prepareOpenCodePtyLaunch({ ...options, command: "opencode run --prompt 'task'" }))
|
||||
.command
|
||||
).toBe("opencode run --prompt 'task'")
|
||||
expect(
|
||||
(
|
||||
await prepareOpenCodePtyLaunch({
|
||||
...options,
|
||||
env: {},
|
||||
command: "opencode --prompt 'task'"
|
||||
})
|
||||
).command
|
||||
).toBe("opencode --prompt 'task'")
|
||||
}
|
||||
const options = { env, envToDelete: [], isFreshLaunch: true }
|
||||
expect(
|
||||
(
|
||||
await prepareOpenCodePtyLaunch({
|
||||
...options,
|
||||
command: "opencode --standalone --prompt 'task'"
|
||||
})
|
||||
).command
|
||||
).toBe('opencode --standalone')
|
||||
expect(
|
||||
(await prepareOpenCodePtyLaunch({ ...options, command: "opencode run --prompt 'task'" }))
|
||||
.command
|
||||
).toBe("opencode run --prompt 'task'")
|
||||
expect(
|
||||
(await prepareOpenCodePtyLaunch({ ...options, env: {}, command: "opencode --prompt 'task'" }))
|
||||
.command
|
||||
).toBe("opencode --prompt 'task'")
|
||||
})
|
||||
)
|
||||
it.each(['inherited', 'explicit', 'deleted'] as const)(
|
||||
'passes the %s config environment used by the execution-host version probe to the prompt installer',
|
||||
async (selection) => {
|
||||
@@ -178,9 +186,16 @@ describe('execution-host OpenCode launch preparation', () => {
|
||||
expect(result.env).not.toHaveProperty('ORCA_OPENCODE_STARTUP_PROMPT_NONCE')
|
||||
}
|
||||
)
|
||||
it.each(['1.1.23', '2.0.16', '2.0.17', 'unknown'])(
|
||||
'gates native intent against the executing %s capability',
|
||||
async (version) => {
|
||||
it.each([
|
||||
{ version: '1.1.23', nativeIntent: false },
|
||||
{ version: '2.0.12', nativeIntent: true },
|
||||
{ version: '2.0.16', nativeIntent: true },
|
||||
{ version: '2.0.17', nativeIntent: false },
|
||||
{ version: '2.0.21', nativeIntent: false },
|
||||
{ version: 'unknown', nativeIntent: false }
|
||||
])(
|
||||
'gates native intent against the executing $version capability',
|
||||
async ({ version, nativeIntent }) => {
|
||||
probe.mockResolvedValue(getOpenCodeCliCapabilities(version))
|
||||
const envToDelete: string[] = []
|
||||
const fingerprint = createHash('sha256').update('task').digest('hex')
|
||||
@@ -195,10 +210,8 @@ describe('execution-host OpenCode launch preparation', () => {
|
||||
envToDelete,
|
||||
isFreshLaunch: true
|
||||
})
|
||||
expect(env?.[OPENCODE_STARTUP_PROMPT_SHA256_ENV]).toBe(
|
||||
version === '2.0.16' ? fingerprint : undefined
|
||||
)
|
||||
expect(envToDelete.includes(OPENCODE_STARTUP_PROMPT_SHA256_ENV)).toBe(version !== '2.0.16')
|
||||
expect(env?.[OPENCODE_STARTUP_PROMPT_SHA256_ENV]).toBe(nativeIntent ? fingerprint : undefined)
|
||||
expect(envToDelete.includes(OPENCODE_STARTUP_PROMPT_SHA256_ENV)).toBe(!nativeIntent)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -266,7 +279,7 @@ describe('execution-host OpenCode launch preparation', () => {
|
||||
expect(finalEnv).not.toHaveProperty('KEEP_DELETED')
|
||||
})
|
||||
|
||||
it.each(['1.1.23', '2.0.16'])(
|
||||
it.each(['1.1.23', '2.0.12', '2.0.16'])(
|
||||
'selects the probed %s plugin for the execution host',
|
||||
async (version) => {
|
||||
const capabilities = getOpenCodeCliCapabilities(version)
|
||||
|
||||
@@ -42,7 +42,7 @@ class Editor extends EventEmitter {
|
||||
|
||||
type FixtureLocation = { directory: string; workspaceID?: string }
|
||||
|
||||
function fixture() {
|
||||
function fixture(version = '2.0.16') {
|
||||
const editor = new Editor()
|
||||
const input = new EventEmitter()
|
||||
const memory = { settled: false, expiresAt: Date.now() + 20000 }
|
||||
@@ -52,7 +52,7 @@ function fixture() {
|
||||
const sync = vi.fn(async (_location: FixtureLocation) => {})
|
||||
const dispatch = vi.fn(() => editor.replace(''))
|
||||
const ctx = {
|
||||
app: { version: '2.0.16' },
|
||||
app: { version },
|
||||
renderer: { keyInput: input, currentFocusedEditor: editor },
|
||||
storage: { memory: () => [memory, (mutate: (draft: typeof memory) => void) => mutate(memory)] },
|
||||
keymap: { dispatch },
|
||||
@@ -90,6 +90,41 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
describe('installed-version native prompt intent plugin', () => {
|
||||
it.each(['2.0.12', '2.0.16'])(
|
||||
'hydrates the source-reviewed %s location and delivers the same intent only once',
|
||||
async (version) => {
|
||||
const f = fixture(version)
|
||||
let hydrate = () => {}
|
||||
f.sync.mockImplementation(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
hydrate = resolve
|
||||
})
|
||||
)
|
||||
const insert = vi.spyOn(f.editor, 'insertText')
|
||||
const dispose = await setup(f.ctx)
|
||||
try {
|
||||
await vi.advanceTimersByTimeAsync(500)
|
||||
expect(f.sync).toHaveBeenCalledExactlyOnceWith(f.ctx.location)
|
||||
expect(claim).not.toHaveBeenCalled()
|
||||
expect(insert).not.toHaveBeenCalled()
|
||||
hydrate()
|
||||
await vi.advanceTimersByTimeAsync(1000)
|
||||
await vi.waitFor(() => expect(claim).toHaveBeenCalledTimes(1))
|
||||
expect(insert).toHaveBeenCalledExactlyOnceWith(prompt)
|
||||
expect(f.dispatch).toHaveBeenCalledExactlyOnceWith('prompt.submit')
|
||||
expect(f.memory.settled).toBe(true)
|
||||
const reloadDispose = await setup(f.ctx)
|
||||
await vi.advanceTimersByTimeAsync(500)
|
||||
expect(claim).toHaveBeenCalledTimes(1)
|
||||
expect(f.dispatch).toHaveBeenCalledTimes(1)
|
||||
await reloadDispose()
|
||||
} finally {
|
||||
await dispose()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it('waits for the current home location after the startup directory changes', async () => {
|
||||
const f = fixture()
|
||||
let location: FixtureLocation = { directory: '/private/home/private-folder' }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { OPENCODE_NATIVE_PROMPT_VERSIONS } from '../../shared/opencode-cli-version'
|
||||
import { cancelUnreadResponseBody } from '../lib/unread-response-body'
|
||||
import { parseAgentHookEndpointFile } from '../../shared/agent-hook-endpoint-file'
|
||||
import {
|
||||
@@ -40,7 +41,7 @@ async function submitStartupPrompt(ctx) {
|
||||
const nonce = process.env.${OPENCODE_STARTUP_PROMPT_NONCE_ENV};
|
||||
const endpoint = process.env.${OPENCODE_STARTUP_PROMPT_ENDPOINT_ENV};
|
||||
const prompt = process.env.${OPENCODE_STARTUP_PROMPT_BODY_ENV};
|
||||
if (ctx?.app?.version !== "2.0.16" || !/^[a-f0-9]{64}$/.test(digest || "") || !nonce || !endpoint || !prompt) return noop;
|
||||
if (!${JSON.stringify(OPENCODE_NATIVE_PROMPT_VERSIONS)}.includes(ctx?.app?.version) || !/^[a-f0-9]{64}$/.test(digest || "") || !nonce || !endpoint || !prompt) return noop;
|
||||
const input = ctx.renderer?.keyInput;
|
||||
if (typeof ctx.storage?.memory !== "function" || typeof input?.on !== "function" ||
|
||||
typeof input?.off !== "function" || typeof ctx.keymap?.dispatch !== "function" ||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import { getOpenCodeCliCapabilities, parseOpenCodeCliVersion } from './opencode-cli-version'
|
||||
|
||||
it.each(['1.1.23', 'opencode v2.0.16', '2.0.0-beta.1+build'])('parses CLI output %s', (output) => {
|
||||
expect(parseOpenCodeCliVersion(output)).toBe(output.replace(/^opencode v/, ''))
|
||||
})
|
||||
it.each(['1.1.23', 'opencode v2.0.12', 'opencode v2.0.16', '2.0.0-beta.1+build'])(
|
||||
'parses CLI output %s',
|
||||
(output) => {
|
||||
expect(parseOpenCodeCliVersion(output)).toBe(output.replace(/^opencode v/, ''))
|
||||
}
|
||||
)
|
||||
it.each([null, '', 'Error 2.0.16', '1.1', '2.0.16\nwarning'])(
|
||||
'does not mistake other output for a version',
|
||||
(output) => {
|
||||
@@ -21,6 +24,11 @@ it('keeps future versions unknown instead of assuming a plugin loader or prompt
|
||||
pluginApi: 'v1',
|
||||
promptMode: 'submit'
|
||||
})
|
||||
expect(getOpenCodeCliCapabilities('opencode v2.0.12')).toEqual({
|
||||
version: '2.0.12',
|
||||
pluginApi: 'v2',
|
||||
promptMode: 'prefill'
|
||||
})
|
||||
expect(getOpenCodeCliCapabilities('opencode v2.0.16')).toEqual({
|
||||
version: '2.0.16',
|
||||
pluginApi: 'v2',
|
||||
@@ -29,6 +37,7 @@ it('keeps future versions unknown instead of assuming a plugin loader or prompt
|
||||
})
|
||||
|
||||
it('does not opt unverified v2 builds into an extra prompt submission', () => {
|
||||
expect(getOpenCodeCliCapabilities('2.0.17').promptMode).toBe('unknown')
|
||||
expect(getOpenCodeCliCapabilities('2.0.16-beta').promptMode).toBe('unknown')
|
||||
for (const version of ['2.0.13', '2.0.15', '2.0.17', '2.0.21', '2.0.12-beta', '2.0.16-beta']) {
|
||||
expect(getOpenCodeCliCapabilities(version).promptMode).toBe('unknown')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { isValidAppVersion } from './app-version'
|
||||
|
||||
export const OPENCODE_NATIVE_PROMPT_VERSIONS = ['2.0.12', '2.0.16'] as const
|
||||
|
||||
export type OpenCodeCliCapabilities = {
|
||||
version: string | null
|
||||
pluginApi: 'v1' | 'v2' | 'unknown'
|
||||
@@ -21,6 +23,11 @@ export function getOpenCodeCliCapabilities(
|
||||
return {
|
||||
version,
|
||||
pluginApi: major === '1' ? 'v1' : major === '2' ? 'v2' : 'unknown',
|
||||
promptMode: major === '1' ? 'submit' : version === '2.0.16' ? 'prefill' : 'unknown'
|
||||
promptMode:
|
||||
major === '1'
|
||||
? 'submit'
|
||||
: OPENCODE_NATIVE_PROMPT_VERSIONS.some((supported) => supported === version)
|
||||
? 'prefill'
|
||||
: 'unknown'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user