From 681ec4bcbfca2a1e59f90a5b3812b9bbf74aef00 Mon Sep 17 00:00:00 2001 From: Neil Date: Mon, 14 Sep 2026 03:52:20 -0700 Subject: [PATCH] fix(omp): load task prefill through the status extension --- ...agent-status-extension-omp-prefill.test.ts | 63 +++++++++++++++++++ .../pi/agent-status-extension-test-harness.ts | 2 + src/main/pi/agent-status-handler-source.ts | 2 + src/main/pi/prefill-extension-source.test.ts | 14 +++++ src/main/pi/prefill-extension-source.ts | 19 +++--- 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 src/main/pi/agent-status-extension-omp-prefill.test.ts create mode 100644 src/main/pi/prefill-extension-source.test.ts diff --git a/src/main/pi/agent-status-extension-omp-prefill.test.ts b/src/main/pi/agent-status-extension-omp-prefill.test.ts new file mode 100644 index 00000000000..5e8f9118944 --- /dev/null +++ b/src/main/pi/agent-status-extension-omp-prefill.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it, vi } from 'vitest' +import { createAgentStatusExtensionHarness } from './agent-status-extension-test-harness' + +describe('OMP prefill through the explicitly loaded status extension', () => { + it('sets a reasonless startup draft once without submitting', async () => { + const h = createAgentStatusExtensionHarness({ + kind: 'omp', + env: { ORCA_OMP_PREFILL: 'Fix task\nKeep details' } + }) + const setEditorText = vi.fn() + await h.callHook('session_start', {}, { ui: { setEditorText } }) + expect(setEditorText).toHaveBeenCalledExactlyOnceWith('Fix task\nKeep details') + expect(h.processEnv.ORCA_OMP_PREFILL).toBeUndefined() + await h.callHook('session_start', {}, { ui: { setEditorText } }) + h.reload() + await h.callHook('session_start', {}, { ui: { setEditorText } }) + expect(setEditorText).toHaveBeenCalledTimes(1) + expect(h.fetchMock).not.toHaveBeenCalled() + }) + + it('preserves the draft until an editor is available', async () => { + const h = createAgentStatusExtensionHarness({ kind: 'omp', env: { ORCA_OMP_PREFILL: 'Draft' } }) + await h.callHook('session_start', {}, {}) + expect(h.processEnv.ORCA_OMP_PREFILL).toBe('Draft') + const noopEditor = vi.fn() + await h.callHook('session_start', {}, { hasUI: false, ui: { setEditorText: noopEditor } }) + expect(noopEditor).not.toHaveBeenCalled() + expect(h.processEnv.ORCA_OMP_PREFILL).toBe('Draft') + const setEditorText = vi.fn() + await h.callHook('session_start', {}, { ui: { setEditorText } }) + expect(setEditorText).toHaveBeenCalledExactlyOnceWith('Draft') + }) + + it('never consumes a Pi draft in an OMP process', async () => { + const h = createAgentStatusExtensionHarness({ + kind: 'omp', + env: { ORCA_PI_PREFILL: 'Pi only' } + }) + const setEditorText = vi.fn() + await h.callHook('session_start', {}, { ui: { setEditorText } }) + expect(setEditorText).not.toHaveBeenCalled() + expect(h.processEnv.ORCA_PI_PREFILL).toBe('Pi only') + }) + + it('leaves the draft alone outside an Orca pane', async () => { + const h = createAgentStatusExtensionHarness({ + kind: 'omp', + env: { ORCA_PANE_KEY: undefined, ORCA_OMP_PREFILL: 'Draft' } + }) + const setEditorText = vi.fn() + await h.callHook('session_start', {}, { ui: { setEditorText } }) + expect(setEditorText).not.toHaveBeenCalled() + expect(h.processEnv.ORCA_OMP_PREFILL).toBe('Draft') + }) + it('does not let a nested process consume the owner draft', () => { + const h = createAgentStatusExtensionHarness({ + kind: 'omp', + env: { ORCA_PI_STATUS_OWNED: '999', ORCA_OMP_PREFILL: 'Owner draft' } + }) + expect(h.handlers.session_start).toBeUndefined() + expect(h.processEnv.ORCA_OMP_PREFILL).toBe('Owner draft') + }) +}) diff --git a/src/main/pi/agent-status-extension-test-harness.ts b/src/main/pi/agent-status-extension-test-harness.ts index 810bc3d04d5..50aeb359487 100644 --- a/src/main/pi/agent-status-extension-test-harness.ts +++ b/src/main/pi/agent-status-extension-test-harness.ts @@ -6,6 +6,8 @@ import { vi } from 'vitest' import { getPiAgentStatusExtensionSource } from './agent-status-extension-source' export type HookContext = { + hasUI?: boolean + ui?: { setEditorText?: (text: string) => void } isIdle?: () => boolean sessionManager?: { getSessionId?: () => unknown diff --git a/src/main/pi/agent-status-handler-source.ts b/src/main/pi/agent-status-handler-source.ts index 5a778a1c81f..14ffeda9b81 100644 --- a/src/main/pi/agent-status-handler-source.ts +++ b/src/main/pi/agent-status-handler-source.ts @@ -1,3 +1,4 @@ +import { getPiPrefillHandlerSourceLines } from './prefill-extension-source' import type { PiAgentKind } from '../../shared/pi-agent-kind' import { getPiAgentStatusUiPromptHandlerSourceLines } from './agent-status-ui-prompt-source' @@ -115,6 +116,7 @@ export function getPiAgentStatusHandlerSourceLines(kind: PiAgentKind): string[] ' if (ownerPid && ownerPid !== selfPid && isStatusOwnerAlive(ownerPid)) return', ` process.env.${ownerEnv} = selfPid`, ...sessionStartHandler, + ...(kind === 'omp' ? getPiPrefillHandlerSourceLines('omp') : []), ` pi.on('before_agent_start', (event${ctxParam}) => {`, ...captureSessionMetadata, " post('before_agent_start', { prompt: event.prompt ?? '' })", diff --git a/src/main/pi/prefill-extension-source.test.ts b/src/main/pi/prefill-extension-source.test.ts new file mode 100644 index 00000000000..9e36edd69bf --- /dev/null +++ b/src/main/pi/prefill-extension-source.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from 'vitest' +import { getPiPrefillExtensionSource } from './prefill-extension-source' + +describe('getPiPrefillExtensionSource', () => { + it('accepts OMP session_start events without a reason field', () => { + const source = getPiPrefillExtensionSource('omp') + expect(source).toContain('process.env.ORCA_OMP_PREFILL') + expect(source).not.toContain("event.reason !== 'startup'") + }) + + it('keeps Pi startup reason filtering', () => { + expect(getPiPrefillExtensionSource('pi')).toContain("event.reason !== 'startup'") + }) +}) diff --git a/src/main/pi/prefill-extension-source.ts b/src/main/pi/prefill-extension-source.ts index 6741457791b..ad7aa6e9c1d 100644 --- a/src/main/pi/prefill-extension-source.ts +++ b/src/main/pi/prefill-extension-source.ts @@ -13,20 +13,23 @@ const PREFILL_ENV_VAR_BY_KIND: Record = { } export function getPiPrefillExtensionSource(kind: PrefillAgentKind): string { + return ['export default function (pi) {', ...getPiPrefillHandlerSourceLines(kind), '}', ''].join( + '\n' + ) +} + +export function getPiPrefillHandlerSourceLines(kind: PrefillAgentKind): string[] { const envVar = PREFILL_ENV_VAR_BY_KIND[kind] return [ - 'export default function (pi) {', " pi.on('session_start', async (event, ctx) => {", - ' if (!process.env.ORCA_PANE_KEY) return', - " if (event.reason !== 'startup') return", + ' if (!process.env.ORCA_PANE_KEY || ctx?.hasUI === false) return', + ...(kind === 'pi' ? [" if (event.reason !== 'startup') return"] : []), ` const prefill = process.env.${envVar}`, - ' if (!prefill) return', + " if (!prefill || typeof ctx?.ui?.setEditorText !== 'function') return", ` delete process.env.${envVar}`, ' try {', ' ctx.ui.setEditorText(prefill)', ' } catch {}', - ' })', - '}', - '' - ].join('\n') + ' })' + ] }