fix: restore OMP commit messages and chat names (#34644)

* fix: deliver OMP generation prompts through owned files

* test: preserve the OMP contract suite entry point

* Preserve complete OMP generation prompts and retry owned cleanup

* Load OMP cleanup fixture module outside test timeout

* Deliver WSL generation prompts inside the execution guest

* Cover deadline expiry before WSL generation authorization
This commit is contained in:
Brennan Benson
2026-10-28 20:55:13 -07:00
committed by GitHub
parent b9f3b2009f
commit da3148053a
25 changed files with 1804 additions and 135 deletions
@@ -1,6 +1,7 @@
import { wslDistroForCommand } from '../../../shared/wsl-paths'
export { wslDistroForCommand } from '../../../shared/wsl-paths'
import { waitForPromiseWithSignal } from '../../../shared/abort-signal-reason'
import { withTimeout } from '../../../shared/promise-timeout-fallback'
import { parseWslPath } from '../../wsl'
import { isWslDirectGitReadCommand } from '../wsl-direct-git-read-commands'
import {
disableWslGitReadEnvironment,
@@ -20,14 +21,6 @@ function wslGitArgs(args: string[], options: GitExecOptions): string[] {
return withWslWindowsCheckoutGitConfig(args, options.cwd, options.wslDistro)
}
/** The WSL distro a Git command with this cwd runs in, or null when it runs on the host. */
export function wslDistroForCommand(cwd: string | undefined, override?: string): string | null {
if (process.platform !== 'win32') {
return null
}
return (cwd ? parseWslPath(cwd)?.distro : undefined) ?? override ?? null
}
export function resolveGitCommand(
args: string[],
options: GitExecOptions,
+22 -4
View File
@@ -1,6 +1,11 @@
import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process'
import { recordSubprocessSpawn } from '../../diagnostics/main-thread-churn-probe'
import { resolveCommand } from './wsl-command-resolution'
import {
commandLineLength,
MAX_COMMAND_LINE_CHARS,
WindowsCommandLineBudgetError
} from '../../../shared/windows-command-line-budget'
import { resolveCommand, withWslPreparation } from './wsl-command-resolution'
export function wslAwareSpawn(
command: string,
@@ -11,15 +16,28 @@ export function wslAwareSpawn(
useWslLoginShell?: boolean
wslShellCommand?: string
cwdFailureExitCode?: number
preparation?: { script: string; args: string[] }
}
): ChildProcess {
const { wslDistro, useWslLoginShell, wslShellCommand, cwdFailureExitCode, ...spawnOptions } =
options
const resolved = resolveCommand(command, args, options.cwd, wslDistro, {
const {
wslDistro,
useWslLoginShell,
wslShellCommand,
cwdFailureExitCode,
preparation,
...spawnOptions
} = options
let resolved = resolveCommand(command, args, options.cwd, wslDistro, {
useWslLoginShell,
wslShellCommand,
...(cwdFailureExitCode === undefined ? {} : { cwdFailureExitCode })
})
if (preparation && resolved.wsl) {
resolved = withWslPreparation(resolved, preparation)
if (commandLineLength([resolved.binary, ...resolved.args]) > MAX_COMMAND_LINE_CHARS) {
throw new WindowsCommandLineBudgetError()
}
}
const spawnStartedAt = performance.now()
const child = spawn(resolved.binary, resolved.args, {
...spawnOptions,
@@ -208,3 +208,23 @@ function withWslProcessGroupTermination(
termination
}
}
/** Prefix only the opt-in preparation; retain the resolver's original guest argv intact. */
export function withWslPreparation(
command: ResolvedCommand,
preparation: { script: string; args: string[] }
): ResolvedCommand {
const execIndex = command.args.indexOf('--exec')
return {
...command,
args: [
...command.args.slice(0, execIndex + 1),
'/bin/sh',
'-c',
preparation.script,
'orca-generation',
...preparation.args,
...command.args.slice(execIndex + 1)
]
}
}
@@ -1,3 +1,4 @@
import { encodePosixPrintfRecords } from '../../shared/posix-printf-encoding'
import { spawnSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import { describe, expect, it } from 'vitest'
@@ -20,6 +21,21 @@ function shimDir(tools: Record<string, string>): string {
return bin
}
it.skipIf(process.platform === 'win32')(
'keeps incremental printf records byte-bounded and lossless at escape/codepoint boundaries',
() => {
const controls = Array.from({ length: 128 }, (_, index) => String.fromCharCode(index)).join('')
const text = `${'x'.repeat(1023)}🌊${controls}\\c\\012\uFEFF'!%$\`\ud800`.repeat(4)
const chunks = [...encodePosixPrintfRecords(text)]
expect(chunks.every((chunk) => Buffer.byteLength(chunk) <= 1024)).toBe(true)
expect(chunks.every((chunk) => !chunk.includes('\n') && !chunk.includes('\0'))).toBe(true)
const result = spawnSync('/bin/sh', ['-c', 'printf "%b" "$@"', 'fixture', ...chunks])
expect(result.status).toBe(0)
expect(result.stdout).toEqual(Buffer.from(text))
expect([...encodePosixPrintfRecords('')]).toEqual([''])
}
)
describe('wrapRemoteCommandForPosixShell', () => {
it('emits one physical line without requiring a remote decoder binary', () => {
const wrapped = wrapRemoteCommandForPosixShell("printf 'a\\n'\nprintf 'b\\n'")
+3 -29
View File
@@ -1,34 +1,8 @@
import { encodePosixPrintfRecords } from '../../shared/posix-printf-encoding'
import { gzipSync } from 'node:zlib'
import { quotePosixShell } from '../../shared/shell-argument-quoting'
const REMOTE_COMMAND_CHUNK_MAX_BYTES = 1_024
const REMOTE_COMMAND_COMPRESSION_THRESHOLD_BYTES = 8_000
const REMOTE_COMMAND_PRINTF_ESCAPED_BYTES = new Set([0x21, 0x27, 0x5c])
function encodeRemoteCommandForPrintf(command: string): string[] {
const chunks: string[] = []
let chunk = ''
let chunkBytes = 0
for (const character of command) {
const codePoint = character.codePointAt(0)!
const isSafePrintableAscii =
codePoint >= 0x20 && codePoint <= 0x7e && !REMOTE_COMMAND_PRINTF_ESCAPED_BYTES.has(codePoint)
const encodedCharacter =
codePoint > 0x7f || isSafePrintableAscii
? character
: `\\0${codePoint.toString(8).padStart(3, '0')}`
const encodedBytes = codePoint > 0x7f ? Buffer.byteLength(character) : encodedCharacter.length
if (chunkBytes + encodedBytes > REMOTE_COMMAND_CHUNK_MAX_BYTES) {
chunks.push(chunk)
chunk = ''
chunkBytes = 0
}
chunk += encodedCharacter
chunkBytes += encodedBytes
}
chunks.push(chunk)
return chunks
}
/** The compressed wrapper's exit when the host has no base64 or gzip; nothing ran. */
export const REMOTE_COMMAND_DECODER_UNAVAILABLE_EXIT = 125
@@ -39,7 +13,7 @@ export function wrapRemoteCommandForPosixShell(
options: { compress?: boolean } = {}
): string {
// csh/tcsh split multiline exec strings; rebuild bounded printf chunks without consuming relay stdin.
const encodedChunks = encodeRemoteCommandForPrintf(command)
const encodedChunks = [...encodePosixPrintfRecords(command)]
const runDecoded = 'decoded=${decoded%_}; exec /bin/sh -c "$decoded"'
const decodeAndRun = `decoded=$(printf %b "$@" && printf _) || exit $?; ${runDecoded}`
const chunkArguments = encodedChunks.map(quotePosixShell).join(' ')
@@ -53,7 +27,7 @@ export function wrapRemoteCommandForPosixShell(
// Compress long scripts before gateways can truncate the exec request.
const compressed = gzipSync(Buffer.from(command), { level: 9 }).toString('base64')
const compressedArguments = encodeRemoteCommandForPrintf(compressed)
const compressedArguments = [...encodePosixPrintfRecords(compressed)]
.map(quotePosixShell)
.join(' ')
// Preserve payload stdin and execute only after gzip validation. Missing decoders consume nothing, permitting plain retry; macOS before 13 needs base64 -D.
@@ -1,9 +1,10 @@
import { wslDistroForCommand } from '../../shared/wsl-paths'
import { spawnProcess } from '@orca/process-host'
import { withCliRuntimeOnPath } from '../../shared/node-cli-command-resolution'
import { resolveCliCommand } from '../codex-cli/command'
import { createProviderSpawnSpec } from '../provider-process/provider-process-supervisor'
import { guardSupervisedProviderGroup } from '../provider-process/provider-group-orphan-guard'
import { SOURCE_CONTROL_KILL_SITE } from './source-control-local-process'
import { SOURCE_CONTROL_KILL_SITE } from './source-control-agent-termination'
import { wslAwareSpawn } from '../git/runner'
import { guestNativeSpawnEnv } from '../ipc/wsl-guest-windows-drive-path'
import type {
@@ -41,8 +42,11 @@ function buildWslLauncherEnv(explicitEnv: NodeJS.ProcessEnv | undefined): NodeJS
}
export const spawnSourceControlAgent: SpawnSourceControlAgent = (input) => {
const wslDistro = input.preparation
? wslDistroForCommand(input.cwd, input.wslDistro)
: input.wslDistro
const spawnEnv = input.env ?? process.env
if (process.platform === 'win32' && input.wslDistro) {
if (process.platform === 'win32' && wslDistro) {
// Apply command assignments after guest login-shell setup, never to the Windows launcher.
const assignments = Object.entries(input.commandEnv ?? {}).map(
([key, value]) => `${key}=${value}`
@@ -56,8 +60,9 @@ export const spawnSourceControlAgent: SpawnSourceControlAgent = (input) => {
env: buildWslLauncherEnv(input.env),
stdio: [input.stdinMode, 'pipe', 'pipe'],
windowsHide: true,
wslDistro: input.wslDistro,
useWslLoginShell: true
wslDistro,
useWslLoginShell: true,
...(input.preparation ? { preparation: input.preparation } : {})
}
) as SpawnedSourceControlAgentProcess
}
@@ -0,0 +1,27 @@
import { stopSupervisedChildProcess } from '../provider-process/supervised-child-process-stop'
import { terminateWindowsProcessTree } from '../windows-process-tree-kill'
import type { SpawnedSourceControlAgentProcess } from './source-control-text-generation-types'
export const SOURCE_CONTROL_KILL_SITE = 'source-control-text-generation'
export async function killSourceControlAgentProcess(
child: SpawnedSourceControlAgentProcess
): Promise<void> {
const pid = child.pid
if (!pid) {
return
}
if (child.supervised) {
await stopSupervisedChildProcess(child, { site: SOURCE_CONTROL_KILL_SITE })
return
}
if (process.platform === 'win32') {
// Tree-walk refusal must not release the home lock having killed nothing; root handle kill cannot hit a recycled PID.
await terminateWindowsProcessTree(pid, { site: SOURCE_CONTROL_KILL_SITE })
}
try {
child.kill('SIGKILL')
} catch {
// The process may exit between the PID check and kill.
}
}
@@ -0,0 +1,26 @@
import type { CommitMessagePlan } from '../../shared/commit-message-plan'
import { commandLineLength, MAX_COMMAND_LINE_CHARS } from '../../shared/windows-command-line-budget'
// Linux caps each argv entry in bytes (128KiB on 4KiB pages), before total argv capacity; reserve headroom for single-prompt arguments.
const LINUX_SINGLE_ARGUMENT_BYTE_BUDGET = 120 * 1024
/** Whether the plan's argv plus reserve fits this host's limit: UTF-16 units on Windows, bytes per argument on Linux. */
export function fitsLocalCommandLine(plan: CommitMessagePlan, reserve = 0): boolean {
if (process.platform === 'win32') {
return commandLineLength([plan.binary, ...plan.args]) + reserve <= MAX_COMMAND_LINE_CHARS
}
if (process.platform === 'linux') {
return plan.args.every(
(arg) => Buffer.byteLength(arg, 'utf8') + reserve <= LINUX_SINGLE_ARGUMENT_BYTE_BUDGET
)
}
return true
}
export function argumentBudgetFailure(plan: CommitMessagePlan): string | null {
if (fitsLocalCommandLine(plan)) {
return null
}
return process.platform === 'win32'
? `${plan.label} prompt is too large for the Windows command line. Stage fewer changes and try again.`
: `${plan.label} prompt is too large to pass as a single command-line argument. Stage fewer changes and try again.`
}
@@ -1,3 +1,4 @@
import { wslDistroForCommand } from '../../shared/wsl-paths'
import { mergeCommandEnvironment } from '../../shared/command-environment'
import type { CommitMessagePlan } from '../../shared/commit-message-plan'
import {
@@ -27,12 +28,16 @@ export function runLocalPlanForAgent(input: {
spawnAgent: SpawnSourceControlAgent
shouldExecute?: () => boolean
}): Promise<InternalTextGenerationResult> {
const wslDistro = input.plan.promptFile
? (wslDistroForCommand(input.target.cwd, input.target.wslDistro) ?? undefined)
: input.target.wslDistro
const target = {
...input.target,
wslDistro,
env: mergeCommandEnvironment(
input.target.env,
input.plan.env,
input.target.wslDistro ? 'linux' : process.platform
wslDistro ? 'linux' : process.platform
)
}
const start = (): LocalProcessExecution<InternalTextGenerationResult> => {
@@ -45,10 +50,10 @@ export function runLocalPlanForAgent(input: {
return runLocalSourceControlPlan({
plan: input.plan,
cwd: input.target.cwd,
env: input.target.wslDistro ? input.target.env : target.env,
env: wslDistro ? input.target.env : target.env,
emptyResultName: input.emptyResultName,
operation: input.operation,
wslDistro: input.target.wslDistro,
wslDistro,
spawnAgent: input.spawnAgent
})
}
@@ -1,9 +1,13 @@
import { killSourceControlAgentProcess } from './source-control-agent-termination'
import { wslDistroForCommand } from '../../shared/wsl-paths'
import { prepareWslPrompt, stageLocalPromptFiles } from './source-control-prompt-files'
import { producePromptRecords } from './source-control-prompt-producer'
import { argumentBudgetFailure } from './source-control-command-line-budget'
export { fitsLocalCommandLine } from './source-control-command-line-budget'
import { removeTree } from '../../shared/windows-transient-lock-removal'
import { WindowsCmdLineBreakError } from '@orca/process-host'
import type { CommitMessagePlan } from '../../shared/commit-message-plan'
import { commandLineLength, MAX_COMMAND_LINE_CHARS } from '../../shared/windows-command-line-budget'
import { WindowsCommandLineBudgetError } from '../../shared/windows-command-line-budget'
import { supervisedProviderSpawnFailure } from '../provider-process/provider-spawn-failure-report'
import { stopSupervisedChildProcess } from '../provider-process/supervised-child-process-stop'
import { terminateWindowsProcessTree } from '../windows-process-tree-kill'
import {
finalizeFromAgentOutput,
userFacingUnsafeWindowsBatchArgs
@@ -21,77 +25,43 @@ import type {
InternalTextGenerationResult,
LocalProcessExecution,
SpawnedSourceControlAgentProcess,
SpawnSourceControlAgent,
TextGenerationOperation
LocalSourceControlPlanInput
} from './source-control-text-generation-types'
export const SOURCE_CONTROL_KILL_SITE = 'source-control-text-generation'
export * from './source-control-agent-termination'
export async function killSourceControlAgentProcess(
child: SpawnedSourceControlAgentProcess
): Promise<void> {
const pid = child.pid
if (!pid) {
return
}
if (child.supervised) {
await stopSupervisedChildProcess(child, { site: SOURCE_CONTROL_KILL_SITE })
return
}
if (process.platform === 'win32') {
// Tree-walk refusal must not release the home lock having killed nothing; root handle kill cannot hit a recycled PID.
await terminateWindowsProcessTree(pid, { site: SOURCE_CONTROL_KILL_SITE })
}
try {
child.kill('SIGKILL')
} catch {
// The process may exit between the PID check and kill.
}
}
// Linux caps each argv entry in bytes (128KiB on 4KiB pages), before total argv capacity; reserve headroom for single-prompt arguments.
const LINUX_SINGLE_ARGUMENT_BYTE_BUDGET = 120 * 1024
/** Whether the plan's argv plus reserve fits this host's limit: UTF-16 units on Windows, bytes per argument on Linux. */
export function fitsLocalCommandLine(plan: CommitMessagePlan, reserve = 0): boolean {
if (process.platform === 'win32') {
return commandLineLength([plan.binary, ...plan.args]) + reserve <= MAX_COMMAND_LINE_CHARS
}
if (process.platform === 'linux') {
return plan.args.every(
(arg) => Buffer.byteLength(arg, 'utf8') + reserve <= LINUX_SINGLE_ARGUMENT_BYTE_BUDGET
)
}
return true
}
function argumentBudgetFailure(plan: CommitMessagePlan): string | null {
if (fitsLocalCommandLine(plan)) {
return null
}
return process.platform === 'win32'
? `${plan.label} prompt is too large for the Windows command line. Stage fewer changes and try again.`
: `${plan.label} prompt is too large to pass as a single command-line argument. Stage fewer changes and try again.`
}
export function runLocalSourceControlPlan(input: {
plan: CommitMessagePlan
cwd: string
env: NodeJS.ProcessEnv | undefined
emptyResultName: string
operation: TextGenerationOperation
wslDistro?: string
spawnAgent: SpawnSourceControlAgent
}): LocalProcessExecution<InternalTextGenerationResult> {
const { plan, cwd, operation } = input
export function runLocalSourceControlPlan(
input: LocalSourceControlPlanInput
): LocalProcessExecution<InternalTextGenerationResult> {
let { plan } = input
const { cwd, operation } = input
const distro = plan.promptFile ? wslDistroForCommand(cwd, input.wslDistro) : null
const guest = distro ? prepareWslPrompt(plan, distro) : undefined
let closed = false
let promptDirectory: string | undefined
let markProcessClosed!: () => void
const processClosed = new Promise<void>((resolve) => {
markProcessClosed = resolve
markProcessClosed = () => {
closed = true
if (promptDirectory) {
const directory = promptDirectory
promptDirectory = undefined
void removeTree(directory).catch((error: unknown) => {
console.error('[commit-message] Failed to remove generation prompt:', error)
})
}
resolve()
}
})
const couldNotStart = `${plan.label} could not be started. Check the agent command in Settings and try again.`
const result = new Promise<InternalTextGenerationResult>((resolve) => {
let child: SpawnedSourceControlAgentProcess
try {
plan =
guest?.plan ??
stageLocalPromptFiles(plan, (directory) => {
promptDirectory = directory
})
// Reject oversized argv before opaque E2BIG spawn failure so users can act on the prompt-size limit.
const budgetFailure = argumentBudgetFailure(plan)
if (budgetFailure) {
@@ -105,12 +75,20 @@ export function runLocalSourceControlPlan(input: {
cwd,
env: input.env,
commandEnv: plan.env,
wslDistro: input.wslDistro,
wslDistro: distro ?? input.wslDistro,
...(guest ? { preparation: guest.preparation } : {}),
stdinMode: 'pipe',
useCwdForNative: true
})
} catch (error) {
markProcessClosed()
if (error instanceof WindowsCommandLineBudgetError) {
resolve({
success: false,
error: `${plan.label} prompt is too large for the Windows command line. Stage fewer changes and try again.`
})
return
}
if (error instanceof WindowsCmdLineBreakError) {
resolve({
success: false,
@@ -123,6 +101,8 @@ export function runLocalSourceControlPlan(input: {
return
}
const stop = new AbortController()
const deadline = Date.now() + SOURCE_CONTROL_GENERATION_TIMEOUT_MS
let stdout = ''
let stderr = ''
let stdoutBytes = 0
@@ -135,7 +115,10 @@ export function runLocalSourceControlPlan(input: {
let terminal = false
let terminationComplete: Promise<void> | null = null
let detachChildListeners = (): void => {}
const active = (): boolean =>
!settled && !terminal && !stop.signal.aborted && Date.now() < deadline
const startTermination = (): void => {
stop.abort()
terminationComplete ??= killSourceControlAgentProcess(child).catch((error: unknown) => {
console.error('[commit-message] Local generator termination failed:', error)
})
@@ -150,6 +133,7 @@ export function runLocalSourceControlPlan(input: {
return
}
settled = true
stop.abort()
if (timer) {
clearTimeout(timer)
timer = null
@@ -187,6 +171,9 @@ export function runLocalSourceControlPlan(input: {
stdout += chunk.toString('utf-8')
}
const onStderrData = (chunk: Buffer): void => {
if (settled) {
return
}
stderrBytes += chunk.byteLength
if (stderrBytes > MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES) {
outputLimitExceeded = true
@@ -216,6 +203,7 @@ export function runLocalSourceControlPlan(input: {
}
const onClose = (code: number | null): void => {
terminal = true
stop.abort()
child.off?.('close', onClose)
markClosedAfterTermination()
if (canceledByUser) {
@@ -255,12 +243,47 @@ export function runLocalSourceControlPlan(input: {
})
)
}
child.stdin?.on?.('error', (error: Error) => {
if (active()) {
startTermination()
onError(error)
}
})
child.stdin?.on?.('close', () => {
if (guest && active() && !child.stdin?.writableEnded) {
startTermination()
onError(new Error('Generation input closed.'))
}
})
child.stdout?.on('data', onStdoutData)
child.stderr?.on('data', onStderrData)
if (guest) {
child.stderr?.on('data', (chunk: Buffer) => {
const proved = guest.observe(chunk, onStderrData)
if (proved) {
promptDirectory = guest.cleanupPath
if (closed) {
markProcessClosed()
}
if (active() && child.stdin) {
void producePromptRecords(child.stdin, guest.records(), stop.signal, active).catch(
(error: unknown) => {
if (active()) {
startTermination()
onError(error instanceof Error ? error : new Error(String(error)))
}
}
)
}
}
})
} else {
child.stderr?.on('data', onStderrData)
}
child.on('error', onError)
child.on('close', onClose)
child.on('exit', () => {
terminal = true
stop.abort()
markClosedAfterTermination()
})
detachChildListeners = () => {
@@ -268,7 +291,11 @@ export function runLocalSourceControlPlan(input: {
child.stderr?.off?.('data', onStderrData)
}
try {
child.stdin?.end(plan.stdinPayload ?? undefined)
if (!guest) {
child.stdin?.end(plan.stdinPayload ?? undefined)
} else if (!child.stdin) {
throw new Error('Generation input is unavailable.')
}
} catch (error) {
startTermination()
onError(error instanceof Error ? error : new Error(String(error)))
@@ -0,0 +1,440 @@
import { spawnProcess } from '@orca/process-host'
import * as agentSpecs from '../../shared/commit-message-agent-spec'
import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control-ai'
import type { SpawnSourceControlAgent } from './source-control-text-generation-types'
import {
generateCommitMessage,
generatePullRequestFields,
generateBranchName
} from './source-control-text-generation-requests'
import { generateConversationName } from './conversation-name-generation-request'
import { generateJiraIssueSummaryFromContext } from './jira-issue-summary-text-generation'
import { existsSync, mkdtempSync, readFileSync } from 'node:fs'
import { rm } from 'node:fs/promises'
import type * as FsPromises from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, expect, it, vi } from 'vitest'
import { planCommitMessageGeneration } from '../../shared/commit-message-plan'
import { cancelLocalGeneration } from './source-control-generation-lanes'
import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from './source-control-generation-limits'
import { runLocalSourceControlPlan } from './source-control-local-process'
import {
createMockDiscoveryChild,
withPlatform
} from './commit-message-text-generation-test-harness'
vi.mock('../windows-process-tree-kill', () => ({
terminateWindowsProcessTree: vi.fn(async () => true)
}))
vi.mock('node:fs/promises', async (importOriginal) => {
const actual = await importOriginal<typeof FsPromises>()
return { ...actual, rm: vi.fn(actual.rm) }
})
const actualFs = await vi.importActual<typeof FsPromises>('node:fs/promises')
afterEach(() => vi.useRealTimers())
// OMP 17.0.5 recorded stdin-only execution: exit 0, empty stdout/stderr, no submitted prompt.
const recordedPrompt = `Name this chat from the user's first request.
Write a short, specific title of at most 48 characters.
Output only the title, with no heading, quotes, or explanation.
First request:
Trace a lost lighthouse signal.
Additional instructions:
Return Lighthouse Signal Trace.
`
it.each(['success', 'failure', 'cancel', 'timeout', 'spawn failure'] as const)(
'delivers the recorded OMP prompt without stdin and removes the owned file after %s',
async (outcome) => {
vi.useFakeTimers()
const prompt = recordedPrompt + 'x'.repeat(200_000)
const planned = planCommitMessageGeneration({ agentId: 'omp', model: 'default' }, prompt)
if (!planned.ok) {
throw new Error(planned.error)
}
const child = createMockDiscoveryChild()
let promptPath = ''
const execution = withPlatform('win32', () =>
runLocalSourceControlPlan({
plan: planned.plan,
cwd: '/omp-file-test',
env: undefined,
operation: 'conversation-name',
emptyResultName: 'chat name',
spawnAgent: ({ args }) => {
promptPath = args[planned.plan.promptFile?.argIndex ?? -1]?.slice(1) ?? ''
expect(new TextDecoder().decode(readFileSync(promptPath))).toBe(prompt)
expect(args.join(' ').length).toBeLessThan(1000)
if (outcome === 'spawn failure') {
throw new Error('recorded spawn failure')
}
return child
}
})
)
if (outcome === 'success') {
// Like the recorded binary, this child never reads stdin; only the explicit file submits input.
child.stdout.emit('data', Buffer.from('Lighthouse Signal Trace'))
} else if (outcome === 'cancel') {
cancelLocalGeneration('conversation-name', '/omp-file-test')
} else if (outcome === 'timeout') {
await vi.advanceTimersByTimeAsync(SOURCE_CONTROL_GENERATION_TIMEOUT_MS)
}
if (outcome !== 'spawn failure') {
expect(child.stdin.end).toHaveBeenCalledWith(undefined)
expect(existsSync(promptPath)).toBe(true)
child.emit('close', outcome === 'failure' ? 1 : 0)
}
const result = await execution.result
await execution.processClosed
expect(result.success).toBe(outcome === 'success')
await vi.waitFor(() => expect(existsSync(dirname(promptPath))).toBe(false))
}
)
// Pinned OMP17 cli/file-processor.ts:16,49-54,119-122: per-file cap, ordered text decoding.
const attachmentReader = String.raw`
const fs = require('node:fs');
const args = process.argv.slice(1);
const paths = args.filter(arg => arg.startsWith('@')).map(arg => arg.slice(1));
const chunks = paths.map(path => fs.readFileSync(path));
const isImage = bytes => ['GIF87a', 'GIF89a'].includes(bytes.subarray(0, 6).toString()) ||
(bytes.subarray(0, 4).toString() === 'RIFF' && bytes.subarray(8, 12).toString() === 'WEBP');
const fragments = chunks.map(bytes => bytes.length > 5 * 1024 * 1024
? '(skipped: too large)' : isImage(bytes) ? '(image instead of text)' : new TextDecoder().decode(bytes));
let stdin = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => { stdin += chunk; });
process.stdin.on('end', () => {
fs.writeFileSync(process.env.OMP_RECEIPT, JSON.stringify({
paths, sizes: chunks.map(bytes => bytes.length), content: paths.length ? fragments.join('') : stdin, stdin,
message: fragments.map((text, index) => '<file name="' + paths[index] + '">\n' + text + '\n</file>\n').join('') + args.at(-1)
}));
process.stdout.write(process.env.OMP_OUTPUT);
if (process.env.OMP_EXIT === '7') process.stderr.write('Fixture provider unavailable');
process.exitCode = Number(process.env.OMP_EXIT);
});
`
const contractParams = {
agentId: 'omp',
model: 'generated/model',
thinkingLevel: 'low',
agentCommandOverride: 'OMP_RECIPE_ENV=kept node /configured/prefix/omp.js --model prefix/model',
agentArgs: '--model recipe/model --thinking high -- recipe-message',
commandInputTemplate: 'Saved recipe instruction\n{basePrompt}'
} as const
const contractTarget = { kind: 'local', cwd: tmpdir() } as const
const commitContext = {
branch: 'feature',
stagedSummary: 'signal.ts | 1 +',
stagedPatch: '+ Trace lighthouse signal'
}
type Consumer = {
label: string
run(
params: ResolvedSourceControlAiGenerationParams,
spawnAgent: SpawnSourceControlAgent
): Promise<unknown>
output: string
expected: object
}
const consumers: Consumer[] = [
{
label: 'commit',
run: (params, spawnAgent) =>
generateCommitMessage({ context: commitContext, params, target: contractTarget, spawnAgent }),
output: 'Trace lighthouse signal.\n\nPreserve request bytes\n',
expected: { message: 'Trace lighthouse signal\n\nPreserve request bytes' }
},
{
label: 'PR fields',
run: (params, spawnAgent) =>
generatePullRequestFields({
context: {
branch: 'feature',
base: 'main',
branchChangedByPreparation: true,
currentTitle: '',
currentBody: '',
currentDraft: false,
commitSummary: 'Trace lighthouse signal',
changeSummary: 'signal.ts',
patch: '+ Trace lighthouse signal'
},
params,
target: contractTarget,
spawnAgent
}),
output: JSON.stringify({
base: 'main',
title: 'Trace lighthouse signal.',
body: 'Preserve request bytes\n',
draft: true
}),
expected: {
fields: {
base: 'main',
title: 'Trace lighthouse signal',
body: 'Preserve request bytes',
draft: true
},
branchChangedByPreparation: true
}
},
{
label: 'branch slug / folder title',
run: (params, spawnAgent) =>
generateBranchName({
context: { firstPrompt: 'Trace lighthouse signal' },
params,
target: contractTarget,
spawnAgent
}),
output: 'Trace Lighthouse Signal!',
expected: { slug: 'trace-lighthouse-signal' }
},
{
label: 'conversation name',
run: (params, spawnAgent) =>
generateConversationName({
context: { firstPrompt: 'Trace lighthouse signal' },
params,
target: contractTarget,
spawnAgent
}),
output: '# Title: "Trace lighthouse signal"\nextra commentary',
expected: { name: 'Trace lighthouse signal' }
},
{
label: 'Jira summary',
run: (params, spawnAgent) =>
generateJiraIssueSummaryFromContext(
{ description: 'Trace lighthouse signal' },
params,
contractTarget,
spawnAgent
),
output: '"Trace lighthouse signal"\nextra commentary',
expected: { summary: 'Trace lighthouse signal' }
}
]
type Receipt = { paths: string[]; sizes: number[]; content: string; stdin: string; message: string }
async function withReader<T>(
output: string,
fail: boolean,
run: (spawnAgent: SpawnSourceControlAgent) => Promise<T>
) {
const directory = mkdtempSync(join(tmpdir(), 'orca-omp-contract-'))
const receiptPath = join(directory, 'receipt.json')
try {
const result = await run((input) => {
expect(input.binary).toBe('node')
expect(input.args.slice(0, 1)).toEqual(['/configured/prefix/omp.js'])
expect(input.args).toContain('recipe/model')
expect(input.args).not.toContain('prefix/model')
expect(input.args).not.toContain('generated/model')
expect(input.args).toContain('high')
expect(input.args).not.toContain('low')
expect(input.args.slice(-2)).toEqual(['--', 'recipe-message'])
expect(input.commandEnv).toEqual({ OMP_RECIPE_ENV: 'kept' })
return spawnProcess({
program: process.execPath,
args: ['-e', attachmentReader, '--', ...input.args],
cwd: input.cwd,
env: {
...process.env,
OMP_RECEIPT: receiptPath,
OMP_OUTPUT: output,
OMP_EXIT: fail ? '7' : '0'
},
stdio: 'pipe'
})
})
const receipt: Receipt = JSON.parse(readFileSync(receiptPath, 'utf8'))
await vi.waitFor(() => {
for (const path of receipt.paths) {
expect(existsSync(dirname(path))).toBe(false)
}
})
return { result, receipt }
} finally {
await rm(directory, { recursive: true, force: true })
}
}
it.each(consumers)(
'preserves $label output and failure classification across stdin/file transport',
async (consumer) => {
const spec = agentSpecs.getCommitMessageAgentSpec('omp')
if (!spec) {
throw new Error('Missing OMP spec')
}
for (const fail of [false, true]) {
const delivered = await withReader(consumer.output, fail, (spawn) =>
consumer.run(contractParams, spawn)
)
expect(delivered.receipt.paths).toHaveLength(1)
expect(delivered.receipt.stdin).toBe('')
expect(delivered.receipt.message.endsWith('recipe-message')).toBe(true)
expect(delivered.receipt.content).toContain('Trace lighthouse signal')
expect(delivered.receipt.content.includes('Saved recipe instruction')).toBe(
consumer.label !== 'Jira summary'
)
if (fail) {
expect(delivered.result).toMatchObject({
success: false,
error: expect.stringContaining('OMP CLI command failed with code 7')
})
} else {
expect(delivered.result).toMatchObject({
success: true,
agentLabel: 'OMP',
...consumer.expected
})
}
const legacy = vi.spyOn(agentSpecs, 'getCommitMessageAgentSpec').mockReturnValue({
...spec,
promptDelivery: 'stdin',
buildArgs: (params) => spec.buildArgs(params).slice(0, -1)
})
try {
const previous = await withReader(consumer.output, fail, (spawn) =>
consumer.run(contractParams, spawn)
)
expect(previous.receipt.paths).toHaveLength(0)
expect(previous.receipt.content).toBe(delivered.receipt.content)
expect(previous.result).toEqual(delivered.result)
} finally {
legacy.mockRestore()
}
}
}
)
it.each(['below', 'at', 'above', 'multibyte', 'BOM run', 'GIF', 'RIFF', 'internal magic'] as const)(
'delivers complete text recipe content: %s',
async (boundary) => {
const cap = 5 * 1024 * 1024
const payloadCap = cap - 3
const patches = {
below: () => 'x'.repeat(payloadCap - 1),
at: () => 'x'.repeat(payloadCap),
above: () => 'x'.repeat(cap + 1),
multibyte: () =>
`${'x'.repeat(payloadCap - 1)}🌊界é${'界'.repeat(Math.ceil(cap / 3))}\uFEFFEND`,
'BOM run': () => `${'\uFEFF'.repeat(Math.ceil(cap / 3))}Preserve every BOM`,
GIF: () => 'GIF89a is the header this change handles. Preserve this as text.',
RIFF: () => 'RIFFxxxxWEBP is the signature this change handles. Preserve this as text.',
'internal magic': () => `${'x'.repeat(payloadCap)}GIF89a preserve this boundary as text.`
}
const patch = patches[boundary]()
const { result, receipt } = await withReader(
'Preserve the complete request',
false,
(spawnAgent) =>
generateCommitMessage({
context: { ...commitContext, stagedPatch: patch },
params: { ...contractParams, commandInputTemplate: '{stagedPatch}' },
target: contractTarget,
spawnAgent
})
)
expect(result).toMatchObject({ success: true, message: 'Preserve the complete request' })
expect(receipt.content).toBe(patch)
expect(receipt.sizes.every((size) => size <= cap)).toBe(true)
expect(receipt.paths).toHaveLength(
boundary === 'multibyte'
? 3
: ['above', 'BOM run', 'internal magic'].includes(boundary)
? 2
: 1
)
if (boundary === 'at') {
expect(receipt.sizes).toEqual([cap])
}
}
)
it.each(['success', 'spawn failure', 'permanent failure'] as const)(
'settles independently of bounded async cleanup after %s',
async (outcome) => {
const originalPlatform = process.platform
const rmMock = vi.mocked(rm)
const errors = vi.spyOn(console, 'error').mockImplementation(() => {})
const locked = Object.assign(new Error('EPERM: prompt handle held'), { code: 'EPERM' })
let directory = ''
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
rmMock.mockClear()
rmMock.mockImplementationOnce(async () => {
throw locked
})
if (outcome === 'permanent failure') {
rmMock.mockRejectedValue(locked)
}
try {
const planned = planCommitMessageGeneration(
{ agentId: 'omp', model: 'default' },
recordedPrompt
)
if (!planned.ok) {
throw new Error(planned.error)
}
const child = createMockDiscoveryChild()
const execution = runLocalSourceControlPlan({
plan: planned.plan,
cwd: '/omp-cleanup',
env: undefined,
operation: 'conversation-name',
emptyResultName: 'chat name',
spawnAgent: ({ args }) => {
directory = dirname(args[planned.plan.promptFile?.argIndex ?? -1].slice(1))
if (outcome === 'spawn failure') {
throw new Error('Fixture spawn failure')
}
return child
}
})
if (outcome !== 'spawn failure') {
child.stdout.emit('data', Buffer.from('Lighthouse Signal Trace'))
child.emit('close', 0)
}
expect((await execution.result).success).toBe(outcome !== 'spawn failure')
await execution.processClosed
expect(rmMock).toHaveBeenCalledTimes(1)
expect(existsSync(directory)).toBe(true)
if (outcome === 'permanent failure') {
await vi.waitFor(
() =>
expect(errors).toHaveBeenCalledWith(
'[commit-message] Failed to remove generation prompt:',
locked
),
{ timeout: 5000 }
)
expect(rmMock).toHaveBeenCalledTimes(5)
expect(errors).toHaveBeenCalledTimes(1)
expect(existsSync(directory)).toBe(true)
} else {
await vi.waitFor(() => expect(existsSync(directory)).toBe(false), { timeout: 2000 })
expect(rmMock).toHaveBeenCalledTimes(2)
}
} finally {
Object.defineProperty(process, 'platform', { configurable: true, value: originalPlatform })
rmMock.mockImplementation(actualFs.rm)
if (directory) {
await actualFs.rm(directory, { recursive: true, force: true })
}
errors.mockRestore()
}
}
)
@@ -0,0 +1,140 @@
import { randomUUID } from 'node:crypto'
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { getUtf8ChunkEndIndex } from '../../shared/utf8-byte-limits'
import { encodePosixPrintfRecords } from '../../shared/posix-printf-encoding'
import { toWindowsWslUncPath } from '../../shared/wsl-paths'
import type { CommitMessagePlan } from '../../shared/commit-message-plan'
// Reserve the transport signature under the native text attachment cap.
const PAYLOAD_BYTES = 5 * 1024 * 1024 - 3
export function* promptFileContents(content: string): Generator<string> {
for (let start = 0; start < content.length;) {
const end = getUtf8ChunkEndIndex(content, start, PAYLOAD_BYTES)
yield `\uFEFF${content.slice(start, end)}`
start = end
}
}
export function stageLocalPromptFiles(
plan: CommitMessagePlan,
own: (directory: string) => void
): CommitMessagePlan {
if (!plan.promptFile) {
return plan
}
const directory = mkdtempSync(join(tmpdir(), 'orca-generation-'))
own(directory)
const attachments: string[] = []
for (const content of promptFileContents(plan.promptFile.content)) {
const path = join(directory, `prompt-${attachments.length + 1}.txt`)
writeFileSync(path, content, { mode: 0o600 })
attachments.push(`@${path}`)
}
const args = [...plan.args]
args.splice(plan.promptFile.argIndex, 1, ...attachments)
return { ...plan, args }
}
// Preparation precedes profiles; only a proved empty allocation may roll back before ACK.
export const WSL_PROMPT_PREPARATION = `
nonce=$1; count=$2; shift 2
dir=/tmp/orca-generation-$nonce
old_umask=$(umask)
umask 077
mkdir "$dir" || exit 1
trap 'rmdir "$dir" 2>/dev/null' EXIT
printf '\\nORCA_GENERATION_READY:%s\\n' "$nonce" >&2 || exit 1
IFS= read -r record && [ "$record" = "A$nonce" ] || exit 1
trap - EXIT
index=1
while [ "$index" -le "$count" ]; do
file=$dir/prompt-$index.txt
: > "$file" || exit 1
while :; do
IFS= read -r record || exit 1
case "$record" in
D*) printf '%b' "\${record#D}" >> "$file" || exit 1 ;;
*) [ "$record" = "F$nonce:$index" ] || exit 1; break ;;
esac
done
index=$((index + 1))
done
IFS= read -r record && [ "$record" = "X$nonce" ] || exit 1
umask "$old_umask"
exec "$@" </dev/null
`
export function prepareWslPrompt(plan: CommitMessagePlan, distro: string) {
const nonce = randomUUID()
const directory = `/tmp/orca-generation-${nonce}`
const attachments: string[] = []
for (const _content of promptFileContents(plan.promptFile?.content ?? '')) {
attachments.push(`@${directory}/prompt-${attachments.length + 1}.txt`)
}
const args = [...plan.args]
args.splice(plan.promptFile?.argIndex ?? 0, 1, ...attachments)
const proof = `ORCA_GENERATION_READY:${nonce}`
let partial = ''
let oversized = false
let proved = false
return {
plan: { ...plan, args },
preparation: { script: WSL_PROMPT_PREPARATION, args: [nonce, String(attachments.length)] },
cleanupPath: toWindowsWslUncPath(directory, distro),
observe(chunk: Buffer, diagnostic?: (bytes: Buffer) => void): boolean {
let firstProof = false
let passthrough = 0
for (let index = 0; index < chunk.length; index++) {
const byte = chunk[index]
if (oversized) {
const end = chunk.indexOf(10, index)
if (end === -1) {
diagnostic?.(chunk.subarray(passthrough))
return firstProof
}
diagnostic?.(chunk.subarray(passthrough, end + 1))
index = end
oversized = false
} else if (byte === 10) {
if (partial === proof) {
if (!proved) {
proved = true
firstProof = true
}
} else {
diagnostic?.(Buffer.from(`${partial}\n`, 'latin1'))
}
partial = ''
} else if (partial.length < proof.length && byte === proof.charCodeAt(partial.length)) {
partial += String.fromCharCode(byte)
} else {
if (partial) {
diagnostic?.(Buffer.from(partial, 'latin1'))
}
partial = ''
oversized = true
passthrough = index
index--
}
if (!oversized) {
passthrough = index + 1
}
}
return firstProof
},
*records(): Generator<string> {
yield `A${nonce}\n`
let index = 0
for (const content of promptFileContents(plan.promptFile?.content ?? '')) {
for (const encoded of encodePosixPrintfRecords(content)) {
yield `D${encoded}\n`
}
yield `F${nonce}:${++index}\n`
}
yield `X${nonce}\n`
}
}
}
@@ -0,0 +1,48 @@
import { setImmediate as yieldToEvents } from 'node:timers/promises'
import type { Writable } from 'node:stream'
/** One bounded record at a time; a stopped attempt also releases an outstanding drain wait. */
export async function producePromptRecords(
stdin: Writable,
records: Iterable<string>,
signal: AbortSignal,
active: () => boolean
): Promise<void> {
let batch = 0
for (const record of records) {
if (!active()) {
return
}
if (!stdin.write(record)) {
await new Promise<void>((resolve) => {
const done = (): void => {
stdin.off('drain', done)
stdin.off('error', done)
stdin.off('close', done)
signal.removeEventListener('abort', done)
resolve()
}
stdin.once('drain', done)
stdin.once('error', done)
stdin.once('close', done)
signal.addEventListener('abort', done, { once: true })
if (!active() || stdin.destroyed || stdin.writableEnded) {
done()
}
})
if (!active() || stdin.destroyed || stdin.writableEnded) {
return
}
}
if (++batch === 16) {
batch = 0
await yieldToEvents()
if (!active()) {
return
}
}
}
if (active()) {
stdin.end()
}
}
@@ -108,6 +108,7 @@ export type SourceControlAgentSpawnInput = {
wslDistro?: string
// WSL applies these in the guest; native callers already merge them into env.
commandEnv?: Record<string, string>
preparation?: { script: string; args: string[] }
stdinMode: 'ignore' | 'pipe'
useCwdForNative: boolean
}
@@ -115,3 +116,13 @@ export type SourceControlAgentSpawnInput = {
export type SpawnSourceControlAgent = (
input: SourceControlAgentSpawnInput
) => SpawnedSourceControlAgentProcess
export type LocalSourceControlPlanInput = {
plan: CommitMessagePlan
cwd: string
env: NodeJS.ProcessEnv | undefined
emptyResultName: string
operation: TextGenerationOperation
wslDistro?: string
spawnAgent: SpawnSourceControlAgent
}
@@ -0,0 +1,302 @@
import { spawnProcess } from '@orca/process-host'
import type * as ChildProcess from 'node:child_process'
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, realpathSync } from 'node:fs'
import { rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { expect, it, vi, afterEach } from 'vitest'
import { quotePosixShell } from '../../shared/shell-argument-quoting'
import { commandLineLength, MAX_COMMAND_LINE_CHARS } from '../../shared/windows-command-line-budget'
import { prepareWslPrompt } from './source-control-prompt-files'
import { producePromptRecords } from './source-control-prompt-producer'
import { planCommitMessageGeneration } from '../../shared/commit-message-plan'
import {
withPlatform,
createMockDiscoveryChild
} from './commit-message-text-generation-test-harness'
import { spawnSourceControlAgent } from './source-control-agent-launch'
import { runLocalPlanForAgent } from './source-control-local-generation'
import { resolveCommand } from '../git/command-runner/wsl-command-resolution'
const { spawnMock } = vi.hoisted(() => ({ spawnMock: vi.fn() }))
vi.mock('node:child_process', async (original) => {
const actual = await original<typeof ChildProcess>()
spawnMock.mockImplementation(actual.spawn)
return { ...actual, spawn: spawnMock }
})
vi.mock('../wsl-background-distro-gate', () => ({ noteWslDistroLaunched: vi.fn() }))
const nativeSpawn = (await vi.importActual<typeof ChildProcess>('node:child_process')).spawn
afterEach(() => {
spawnMock.mockReset().mockImplementation(nativeSpawn)
})
const repo = '\\\\wsl.localhost\\Actual\\home\\dev\\repo'
function planned(agentId: 'omp' | 'codex' = 'omp') {
const result = planCommitMessageGeneration(
{
agentId,
model: 'default',
agentCommandOverride: 'lower=recipe LOWER=UPPER omp --model prefix/model',
agentArgs: '--thinking high --mode text @user.txt -- recipe-message'
},
'request'
)
if (!result.ok) {
throw new Error(result.error)
}
return result.plan
}
it.each([undefined, 'Wrong', 'Actual'])(
'routes UNC before hint %s with Linux assignments retained for OMP only',
async (hint) => {
const request = planned()
const child = createMockDiscoveryChild()
let guestPath = ''
const result = withPlatform('win32', () =>
runLocalPlanForAgent({
agentId: 'omp',
plan: request,
target: {
kind: 'local',
cwd: repo,
wslDistro: hint,
env: { lower: 'base', LOWER: 'BASE' }
},
operation: 'conversation-name',
emptyResultName: 'name',
spawnAgent: (input) => {
expect(input.wslDistro).toBe('Actual')
expect(input.env).toEqual({ lower: 'base', LOWER: 'BASE' })
expect(input.commandEnv).toEqual({ lower: 'recipe', LOWER: 'UPPER' })
expect(input.preparation).toBeDefined()
guestPath = input.args.find((arg) => arg.startsWith('@/tmp/')) ?? ''
queueMicrotask(() => {
child.stdout.emit('data', Buffer.from('Useful name'))
child.emit('close', 0)
})
return child
}
})
)
expect(await result).toMatchObject({ success: true })
expect(guestPath).toContain('@/tmp/orca-generation-')
}
)
it('preserves ordinary hinted WSL and unrelated provider no-hint native routing', async () => {
for (const agentId of ['omp', 'codex'] as const) {
const request = planned(agentId)
const child = createMockDiscoveryChild()
const result = withPlatform('win32', () =>
runLocalPlanForAgent({
agentId: 'custom',
plan: request,
target: {
kind: 'local',
cwd: agentId === 'omp' ? 'C:\\repo' : repo,
...(agentId === 'omp' ? { wslDistro: 'Hint' } : {})
},
operation: 'conversation-name',
emptyResultName: 'name',
spawnAgent: (input) => {
expect(input.wslDistro).toBe(agentId === 'omp' ? 'Hint' : undefined)
expect(!!input.preparation).toBe(agentId === 'omp')
queueMicrotask(() => {
child.stdout.emit('data', Buffer.from('Useful name'))
child.emit('close', 0)
})
return child
}
})
)
expect(await result).toMatchObject({ success: true })
}
})
it('launches the original resolved wrapper once, preserving prefix, recipe, translation and assignment timing', () => {
const request = prepareWslPrompt(planned(), 'Actual')
spawnMock.mockReturnValueOnce(createMockDiscoveryChild())
withPlatform('win32', () => {
const assignments = ['lower=recipe', 'LOWER=UPPER']
const original = resolveCommand(
'/usr/bin/env',
[...assignments, planned().binary, ...request.plan.args],
repo,
'Wrong',
{ useWslLoginShell: true }
)
spawnSourceControlAgent({
binary: planned().binary,
args: request.plan.args,
cwd: repo,
commandEnv: planned().env,
wslDistro: 'Actual',
preparation: request.preparation,
stdinMode: 'pipe',
useCwdForNative: true
})
const [binary, args] = spawnMock.mock.calls.at(-1)!
expect(binary).toBe('wsl.exe')
expect(args.slice(0, 3)).toEqual(['-d', 'Actual', '--exec'])
expect(args.slice(3, 7)).toEqual([
'/bin/sh',
'-c',
request.preparation.script,
'orca-generation'
])
expect(args.slice(9)).toEqual(original.args.slice(3))
expect(original.args.at(-1)).toContain('/home/dev/repo')
expect(original.args.at(-1)).toContain('lower=recipe')
expect(args.join(' ')).toContain('@user.txt')
})
})
it('checks the exact composed Windows argv at the inclusive boundary before spawning', () => {
const request = prepareWslPrompt(planned(), 'Actual')
const launch = (padding: number) =>
withPlatform('win32', () =>
spawnSourceControlAgent({
binary: 'omp',
args: [...request.plan.args, 'x'.repeat(padding)],
cwd: repo,
wslDistro: 'Actual',
preparation: request.preparation,
stdinMode: 'pipe',
useCwdForNative: true
})
)
spawnMock.mockReturnValue(createMockDiscoveryChild())
launch(0)
const [binary, args] = spawnMock.mock.calls.at(-1)!
const slack = MAX_COMMAND_LINE_CHARS - commandLineLength([binary, ...args])
const padding = Math.floor(slack / 3)
launch(padding)
const [atBinary, atArgs] = spawnMock.mock.calls.at(-1)!
expect(commandLineLength([atBinary, ...atArgs])).toBeLessThanOrEqual(MAX_COMMAND_LINE_CHARS)
expect(commandLineLength([atBinary, ...atArgs])).toBeGreaterThan(MAX_COMMAND_LINE_CHARS - 3)
const count = spawnMock.mock.calls.length
expect(() => launch(padding + 1)).toThrow()
expect(spawnMock).toHaveBeenCalledTimes(count)
})
it.each([false, true])(
'prepares before hostile profiles and preserves login invocation/cwd/env (assigned=%s)',
async (assigned) => {
const directory = realpathSync(mkdtempSync(join(tmpdir(), 'orca-c3-profiles-')))
const repoPath = join(directory, 'repo')
const startup = join(directory, 'startup')
const changed = join(directory, 'profile-cwd')
const reportPath = join(directory, 'report.json')
if (!assigned) {
mkdirSync(repoPath)
}
mkdirSync(startup)
mkdirSync(changed)
const shell = join(directory, 'bash')
const profile = join(directory, 'rc')
writeFileSync(
shell,
`#!/bin/sh\nexec /bin/bash --noprofile --rcfile ${quotePosixShell(profile)} -ic "$2"\n`,
{ mode: 0o700 }
)
writeFileSync(
join(directory, 'getent'),
`#!/bin/sh\nprintf '%s\\n' ${quotePosixShell(`user:x:1:1:User:${directory}:${shell}`)}\n`,
{ mode: 0o700 }
)
writeFileSync(
join(directory, '.profile'),
`export PATH=${quotePosixShell(directory)}:"$PATH"\n`
)
writeFileSync(
profile,
`
export STARTUP_CWD="$PWD"
export PROFILE_LOWER="$lower"
if IFS= read -r stolen; then export STOLEN=yes; else export STOLEN=no; fi
alias mkdir='mkdir -pv'
alias printf='printf CORRUPTED'
set -C
command mkdir -p ${quotePosixShell(repoPath)}
cd ${quotePosixShell(changed)}
omp() { ${quotePosixShell(process.execPath)} ${quotePosixShell(join(directory, 'reader.cjs'))} "$@"; }
`
)
writeFileSync(
join(directory, 'reader.cjs'),
String.raw`
const fs=require('node:fs'); const path=process.argv.find(arg=>arg.startsWith('@/tmp/')).slice(1);
fs.writeFileSync(process.env.REPORT, JSON.stringify({startup:process.env.STARTUP_CWD,cwd:process.cwd(),stolen:process.env.STOLEN,profileLower:process.env.PROFILE_LOWER,lower:process.env.lower,content:new TextDecoder().decode(fs.readFileSync(path)),stdin:fs.readFileSync(0).length,mask:process.umask()}));
`
)
const request = prepareWslPrompt(planned(), 'Actual')
// Use the actual resolved native cwd and original wrapper, without starting a guest on this Mac.
const original = withPlatform('win32', () =>
resolveCommand(
assigned ? '/usr/bin/env' : 'omp',
assigned
? [
'lower=recipe',
'LOWER=UPPER',
process.execPath,
join(directory, 'reader.cjs'),
...request.plan.args
]
: request.plan.args,
`\\\\wsl.localhost\\Actual${repoPath.replaceAll('/', '\\')}`,
undefined,
{ useWslLoginShell: true }
)
)
const child = spawnProcess({
program: '/bin/sh',
args: [
'-c',
request.preparation.script,
'fixture',
...request.preparation.args,
...original.args.slice(3)
],
cwd: startup,
env: { ...process.env, HOME: directory, SHELL: shell, REPORT: reportPath, lower: 'base' },
stdio: 'pipe'
})
child.stdin.on('error', () => {})
const stop = new AbortController()
const closed = new Promise<number | null>((resolve, reject) => {
child.on('close', resolve)
child.on('error', reject)
})
child.stderr.on('data', (chunk) => {
if (request.observe(chunk)) {
void producePromptRecords(
child.stdin,
request.records(),
stop.signal,
() => !stop.signal.aborted
)
}
})
try {
expect(await closed).toBe(0)
const receipt = JSON.parse(readFileSync(reportPath, 'utf8'))
expect(receipt).toMatchObject({
startup,
cwd: repoPath,
stolen: 'no',
profileLower: 'base',
lower: assigned ? 'recipe' : 'base',
content: 'request',
stdin: 0
})
expect(receipt.mask).toBe(process.umask())
} finally {
stop.abort()
await rm(`/tmp/orca-generation-${request.preparation.args[0]}`, {
recursive: true,
force: true
})
await rm(directory, { recursive: true, force: true })
}
}
)
@@ -0,0 +1,532 @@
import { spawnProcess } from '@orca/process-host'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { rm } from 'node:fs/promises'
import { join } from 'node:path'
import { Writable } from 'node:stream'
import { afterEach, expect, it, vi } from 'vitest'
import { planCommitMessageGeneration } from '../../shared/commit-message-plan'
import { prepareWslPrompt } from './source-control-prompt-files'
import { producePromptRecords } from './source-control-prompt-producer'
import { runLocalSourceControlPlan } from './source-control-local-process'
import { cancelLocalGeneration } from './source-control-generation-lanes'
import {
MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES,
SOURCE_CONTROL_GENERATION_TIMEOUT_MS
} from './source-control-generation-limits'
import {
withPlatform,
createMockDiscoveryChild
} from './commit-message-text-generation-test-harness'
import type { SourceControlAgentSpawnInput } from './source-control-text-generation-types'
import type * as Removal from '../../shared/windows-transient-lock-removal'
const { removeTreeMock } = vi.hoisted(() => ({ removeTreeMock: vi.fn(async () => {}) }))
vi.mock('../../shared/windows-transient-lock-removal', async (original) => ({
...(await original<typeof Removal>()),
removeTree: removeTreeMock
}))
vi.mock('../windows-process-tree-kill', () => ({
terminateWindowsProcessTree: vi.fn(async () => true)
}))
vi.mock('../wsl-background-distro-gate', () => ({ noteWslDistroLaunched: vi.fn() }))
afterEach(() => {
vi.useRealTimers()
removeTreeMock.mockReset()
removeTreeMock.mockResolvedValue(undefined)
})
const cwd = '\\\\wsl.localhost\\Actual\\home\\dev\\repo'
function plan(content = 'sensitive request') {
const planned = planCommitMessageGeneration({ agentId: 'omp', model: 'default' }, content)
if (!planned.ok) {
throw new Error(planned.error)
}
return planned.plan
}
function start(child = createMockDiscoveryChild(), content?: string) {
let launch: SourceControlAgentSpawnInput | undefined
const execution = withPlatform('win32', () =>
runLocalSourceControlPlan({
plan: plan(content),
cwd,
wslDistro: 'Wrong',
env: undefined,
operation: 'conversation-name',
emptyResultName: 'name',
spawnAgent: (input) => {
launch = input
return child
}
})
)
const nonce = launch?.preparation?.args[0]
if (!nonce) {
throw new Error('Expected preparation')
}
return { child, launch, nonce, execution, proof: `ORCA_GENERATION_READY:${nonce}\n` }
}
function close(child: ReturnType<typeof createMockDiscoveryChild>, code = 0) {
child.stdout.emit('data', Buffer.from('Lighthouse Signal Trace'))
child.emit('exit', code)
child.emit('close', code)
}
it('waits for fragmented exact proof, streams once and cleans only the actual guest after terminal', async () => {
const { child, launch, nonce, execution, proof } = start()
const writes: string[] = []
child.stdin.on('data', (chunk) => writes.push(chunk.toString()))
expect(launch?.wslDistro).toBe('Actual')
expect(launch?.args).toContain(`@/tmp/orca-generation-${nonce}/prompt-1.txt`)
expect(child.stdin.end).not.toHaveBeenCalled()
child.stderr.emit('data', Buffer.from(`${proof.trim()}WRONG\n${'x'.repeat(100_000)}\n`))
child.stderr.emit('data', Buffer.from(proof.slice(0, 17)))
expect(writes).toEqual([])
child.stderr.emit('data', Buffer.from(proof.slice(17)))
child.stderr.emit('data', Buffer.from(proof))
expect(writes.join('')).toBe(`A${nonce}\nDsensitive request\nF${nonce}:1\nX${nonce}\n`)
expect(removeTreeMock).not.toHaveBeenCalled()
close(child)
expect(await execution.result).toMatchObject({ success: true })
await execution.processClosed
expect(removeTreeMock).toHaveBeenCalledExactlyOnceWith(
`\\\\wsl.localhost\\Actual\\tmp\\orca-generation-${nonce}`
)
child.stderr.emit('data', Buffer.from(proof))
expect(removeTreeMock).toHaveBeenCalledTimes(1)
})
it.each(['cancel', 'deadline', 'exit', 'overflow', 'stdin-error', 'stdin-close'] as const)(
'stops pending input on %s, and late proof only cleans without reopening result',
async (route) => {
vi.useFakeTimers()
const { child, execution, proof } = start()
const writes = vi.spyOn(child.stdin, 'write')
if (route === 'cancel') {
cancelLocalGeneration('conversation-name', cwd)
}
if (route === 'deadline') {
await vi.advanceTimersByTimeAsync(SOURCE_CONTROL_GENERATION_TIMEOUT_MS)
}
if (route === 'exit') {
close(child)
}
if (route === 'overflow') {
child.stdout.emit('data', Buffer.alloc(MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES + 1))
}
if (route === 'stdin-error') {
child.stdin.emit('error', new Error('EPIPE'))
}
if (route === 'stdin-close') {
child.stdin.emit('close')
}
child.stderr.emit('data', Buffer.from(proof))
expect(writes).not.toHaveBeenCalled()
close(child)
await execution.result
await execution.processClosed
expect(removeTreeMock).toHaveBeenCalledTimes(1)
child.stdin.emit('error', new Error('late EPIPE'))
}
)
it.each(['cancel', 'deadline', 'exit', 'error', 'close', 'overflow'] as const)(
'releases a never-draining producer on %s without final authorization',
async (route) => {
vi.useFakeTimers()
const { child, execution, proof, nonce } = start(undefined, 'x'.repeat(50_000))
const write = vi.spyOn(child.stdin, 'write').mockReturnValue(false)
child.stderr.emit('data', Buffer.from(proof))
expect(write).toHaveBeenCalledExactlyOnceWith(`A${nonce}\n`)
if (route === 'cancel') {
cancelLocalGeneration('conversation-name', cwd)
}
if (route === 'deadline') {
await vi.advanceTimersByTimeAsync(SOURCE_CONTROL_GENERATION_TIMEOUT_MS)
}
if (route === 'exit') {
child.emit('exit', 1)
}
if (route === 'error') {
child.stdin.emit('error', new Error('EPIPE'))
}
if (route === 'close') {
child.stdin.emit('close')
}
if (route === 'overflow') {
child.stderr.emit('data', Buffer.alloc(MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES + 1))
}
await Promise.resolve()
child.stdin.emit('drain')
close(child, 1)
await execution.result
await execution.processClosed
expect(write).toHaveBeenCalledTimes(1)
expect(child.stdin.listenerCount('drain')).toBe(0)
expect(removeTreeMock).toHaveBeenCalledTimes(1)
}
)
it.each(['missing-proof', 'spawn', 'no-pid', 'missing-stdin'] as const)(
'settles %s without invented ownership',
async (route) => {
vi.useFakeTimers()
const child = createMockDiscoveryChild()
if (route === 'missing-stdin') {
Object.defineProperty(child, 'stdin', { value: null })
}
if (route === 'no-pid') {
Object.defineProperty(child, 'pid', { value: undefined })
}
const execution = withPlatform('win32', () =>
runLocalSourceControlPlan({
plan: plan(),
cwd,
env: undefined,
operation: 'conversation-name',
emptyResultName: 'name',
spawnAgent: () => {
if (route === 'spawn') {
throw new Error('spawn denied')
}
return child
}
})
)
if (route === 'no-pid') {
child.emit('error', Object.assign(new Error('not found'), { code: 'ENOENT' }))
}
if (route === 'missing-proof') {
await vi.advanceTimersByTimeAsync(SOURCE_CONTROL_GENERATION_TIMEOUT_MS)
}
if (route !== 'spawn' && route !== 'no-pid') {
close(child, 1)
}
expect(await execution.result).toMatchObject({ success: false })
await execution.processClosed
expect(removeTreeMock).not.toHaveBeenCalled()
}
)
it.each(['pending', 'rejected'] as const)(
'never gates either promise on %s cleanup, even proof after processClosed',
async (outcome) => {
removeTreeMock.mockImplementation(() =>
outcome === 'pending' ? new Promise(() => {}) : Promise.reject(new Error('share unavailable'))
)
const log = vi.spyOn(console, 'error').mockImplementation(() => {})
try {
const { child, execution, proof } = start()
close(child)
expect(await execution.result).toMatchObject({ success: true })
await execution.processClosed
child.stderr.emit('data', Buffer.from(proof))
child.stderr.emit('data', Buffer.from(proof))
expect(removeTreeMock).toHaveBeenCalledTimes(1)
expect(child.stdin.end).not.toHaveBeenCalled()
await Promise.resolve()
} finally {
log.mockRestore()
}
}
)
const tricky = `${Array.from({ length: 128 }, (_, i) => String.fromCharCode(i)).join('')}\\c\\012\r\n'!%$\`🌊界\ud800`
async function shellFixture(
content: string,
controls?: (records: string[], nonce: string) => string[],
setup?: (directory: string) => void,
onProof?: (directory: string) => void
) {
const guest = prepareWslPrompt(plan(content), 'Actual')
const [nonce] = guest.preparation.args
const directory = `/tmp/orca-generation-${nonce}`
setup?.(directory)
const child = spawnProcess({
program: '/bin/sh',
args: [
'-c',
guest.preparation.script,
'fixture',
...guest.preparation.args,
process.execPath,
'-e',
String.raw`
const fs = require('node:fs');
const paths = process.argv.slice(1).map(arg => arg.slice(1));
const files = paths.map(path => fs.readFileSync(path));
process.stdout.write(JSON.stringify({ content: Buffer.concat(files.map(file => Buffer.from(new TextDecoder().decode(file)))).toString('base64'),
sizes: files.map(file => file.length), modes: paths.map(path => fs.statSync(path).mode & 511), directory: fs.statSync(process.env.FIXTURE_DIRECTORY).mode & 511,
eof: require('node:fs').readFileSync(0).length }));
`,
'--',
...guest.plan.args.filter((arg) => arg.startsWith('@'))
],
stdio: 'pipe',
env: { ...process.env, FIXTURE_DIRECTORY: directory }
})
let stdout = ''
let stderr = ''
child.stdout.on('data', (bytes) => {
stdout += bytes
})
const finished = new Promise<number | null>((resolve, reject) => {
child.once('close', resolve)
child.once('error', reject)
})
child.stdin.on('error', () => {})
child.stderr.on('data', (chunk) => {
stderr += chunk
if (guest.observe(chunk)) {
onProof?.(directory)
const records = [...guest.records()]
child.stdin.end((controls ? controls(records, nonce) : records).join(''))
}
})
try {
const code = await finished
return {
code,
stdout,
stderr,
directory,
existed: existsSync(directory),
sentinel: existsSync(join(directory, 'sentinel'))
? readFileSync(join(directory, 'sentinel'), 'utf8')
: null
}
} finally {
await rm(directory, { recursive: true, force: true })
}
}
it.each(['', tricky.repeat(12), `${'x'.repeat(5 * 1024 * 1024 - 4)}🌊GIF89a${tricky}`])(
'materializes exact native text with private modes across encoding and file boundaries',
async (content) => {
const result = await shellFixture(content)
expect(result.code).toBe(0)
const receipt = JSON.parse(result.stdout)
expect(Buffer.from(receipt.content, 'base64').toString()).toBe(Buffer.from(content).toString())
expect(receipt.modes.every((mode: number) => mode === 0o600)).toBe(true)
expect(receipt.directory).toBe(0o700)
expect(receipt.eof).toBe(0)
expect(receipt.sizes.every((size: number) => size <= 5 * 1024 * 1024)).toBe(true)
expect(result.existed).toBe(true)
}
)
it.each([
'collision',
'partial-data',
'wrong-end',
'missing-end',
'partial-execute',
'extra-data',
'wrong-ack'
] as const)('does not execute on %s, and never adopts/deletes a collision', async (route) => {
const result = await shellFixture(
'private data',
(records, nonce) => {
if (route === 'partial-data') {
return [`A${nonce}\nDpartial`]
}
if (route === 'wrong-end') {
return records.map((record) => (record.startsWith('F') ? `F${nonce}:2\n` : record))
}
if (route === 'missing-end') {
return records.filter((record) => !record.startsWith('F'))
}
if (route === 'partial-execute') {
return records.map((record) => (record.startsWith('X') ? record.trim() : record))
}
if (route === 'extra-data') {
return [...records.slice(0, -1), 'Dextra\n', ...records.slice(-1)]
}
if (route === 'wrong-ack') {
return ['Ano\n']
}
return records
},
(directory) => {
if (route === 'collision') {
mkdirSync(directory)
writeFileSync(join(directory, 'sentinel'), 'untouched')
}
}
)
expect(result.code).not.toBe(0)
expect(result.stdout).toBe('')
if (route === 'collision') {
expect(result.stderr).not.toContain('ORCA_GENERATION_READY:')
expect(result.sentinel).toBe('untouched')
}
if (route === 'wrong-ack') {
expect(result.existed).toBe(false)
}
})
it.each([false, true])(
'bounds real producer work at N and 10N (backpressure=%s)',
async (backpressure) => {
const results: number[] = []
for (const size of [20_000, 200_000]) {
const guest = prepareWslPrompt(
plan(tricky.repeat(Math.ceil(size / tricky.length)).slice(0, size)),
'Actual'
)
let writes = 0
let maxRecord = 0
let pending = 0
let maxPending = 0
let maxQueued = 0
const stream = new Writable({
highWaterMark: 2048,
write(chunk, _encoding, callback) {
writes++
maxRecord = Math.max(maxRecord, chunk.length)
maxPending = Math.max(maxPending, ++pending)
const complete = () => {
maxQueued = Math.max(maxQueued, stream.writableLength)
pending--
callback()
}
if (backpressure) {
setImmediate(complete)
} else {
complete()
}
}
})
let yieldedAt = 0
setImmediate(() => {
yieldedAt = writes
})
const finished = new Promise<void>((resolve) => stream.once('finish', resolve))
await producePromptRecords(stream, guest.records(), new AbortController().signal, () => true)
await finished
expect(maxQueued).toBeLessThanOrEqual(2048 + 1026)
expect(yieldedAt).toBeGreaterThan(0)
expect(yieldedAt).toBeLessThan(writes)
expect(maxRecord).toBeLessThanOrEqual(1026)
expect(maxPending).toBe(1)
results.push(writes)
}
expect(results[1]).toBeLessThanOrEqual(results[0] * 10 + 10)
expect(results[1]).toBeGreaterThan(results[0] * 8)
}
)
it('fails a real post-proof storage redirect without running a partial request', async () => {
const result = await shellFixture('private data', undefined, undefined, (directory) =>
mkdirSync(join(directory, 'prompt-1.txt'))
)
expect(result.code).not.toBe(0)
expect(result.stdout).toBe('')
expect(result.existed).toBe(true)
})
it.each([
{ boundary: 'data', stopping: 'cancel' },
{ boundary: 'final', stopping: 'cancel' },
{ boundary: 'data', stopping: 'deadline' },
{ boundary: 'final', stopping: 'deadline' }
] as const)(
'does not authorize after $stopping at $boundary write continuation',
async ({ boundary, stopping }) => {
vi.useFakeTimers()
const { child, execution, proof, nonce } = start()
const writes: string[] = []
vi.spyOn(child.stdin, 'write').mockImplementation((chunk) => {
const record = String(chunk)
writes.push(record)
if (record.startsWith(boundary === 'data' ? 'D' : 'F')) {
if (stopping === 'cancel') {
cancelLocalGeneration('conversation-name', cwd)
} else {
vi.advanceTimersByTime(SOURCE_CONTROL_GENERATION_TIMEOUT_MS)
}
}
return true
})
child.stderr.emit('data', Buffer.from(proof))
close(child)
expect(await execution.result).toMatchObject(
stopping === 'cancel'
? { success: false, canceled: true }
: { success: false, error: 'Generation timed out after 60s.' }
)
await execution.processClosed
expect(writes).not.toContain(`X${nonce}\n`)
}
)
it.each(['success', 'nonzero', 'empty', 'storage', 'pipe-close'] as const)(
'runs the production attempt through real private shell preparation and %s',
async (outcome) => {
let directory = ''
let actualPipeError = false
const execution = withPlatform('win32', () =>
runLocalSourceControlPlan({
plan: plan(tricky.repeat(40)),
cwd,
env: undefined,
operation: 'conversation-name',
emptyResultName: 'name',
spawnAgent: (input) => {
const preparation = input.preparation
if (!preparation) {
throw new Error('Missing preparation')
}
directory = `/tmp/orca-generation-${preparation.args[0]}`
const argv =
outcome === 'pipe-close'
? [
'-c',
`exec 0<&-; printf 'ORCA_GENERATION_READY:%s\\n' "$1" >&2; sleep 0.1`,
'fixture',
preparation.args[0]
]
: [
'-c',
preparation.script,
'fixture',
...preparation.args,
'/bin/sh',
'-c',
outcome === 'empty'
? ':'
: `printf '%s' 'Useful name'; exit ${outcome === 'nonzero' ? 7 : 0}`
]
const child = withPlatform('darwin', () =>
spawnProcess({ program: '/bin/sh', args: argv, stdio: 'pipe' })
)
child.stdin.on('error', (error) => {
actualPipeError ||= 'code' in error && error.code === 'EPIPE'
})
if (outcome === 'storage') {
child.stderr.once('data', () => mkdirSync(join(directory, 'prompt-1.txt')))
}
return child
}
})
)
try {
const result = await execution.result
await execution.processClosed
expect(result.success).toBe(outcome === 'success')
expect(removeTreeMock).toHaveBeenCalledTimes(1)
if (outcome === 'pipe-close') {
expect(actualPipeError).toBe(true)
}
if (outcome === 'success') {
const file = readFileSync(join(directory, 'prompt-1.txt'))
expect(new TextDecoder().decode(file)).toBe(Buffer.from(tricky.repeat(40)).toString())
}
} finally {
if (directory) {
await rm(directory, { recursive: true, force: true })
}
}
}
)
@@ -15,6 +15,16 @@ describe('planSourceControlCommitMessageGeneration', () => {
).toEqual({ ok: false, error: 'Command input is empty.' })
})
it('describes OMP file delivery in dry-run plans', () => {
const result = planSourceControlCommitMessageGeneration({
agentId: 'omp',
model: 'gpt-5.5',
thinkingLevel: 'low'
})
expect(result.ok && result.delivery).toContain('temporary file attachment')
})
it('expands linkedIssue when validating commit and pull-request recipes', () => {
for (const actionId of ['commitMessage', 'pullRequest'] as const) {
// Why: `{prompt}` puts the rendered template in argv, so commandLabel shows it.
@@ -75,8 +75,9 @@ export function planSourceControlTextGeneration(
if (!planned.ok) {
return { ok: false, error: planned.error }
}
const delivery =
planned.plan.stdinPayload === null
const delivery = planned.plan.promptFile
? 'Prompt is delivered as a temporary file attachment.'
: planned.plan.stdinPayload === null
? 'Prompt is delivered as command arguments.'
: 'Prompt is piped to the agent over stdin.'
return {
+6 -5
View File
@@ -44,8 +44,8 @@ export type CommitMessageAgentSpec = {
id: TuiAgent
label: string
binary: string
/** Where the prompt is delivered. Large diffs go via stdin to avoid argv limits. */
promptDelivery: 'argv' | 'stdin'
/** Transport for the prompt; stdin and file delivery avoid argv limits. */
promptDelivery: 'argv' | 'stdin' | 'file'
buildArgs: (params: {
prompt: string
model: string
@@ -94,8 +94,8 @@ export const COMMIT_MESSAGE_AGENT_SPECS: Partial<Record<TuiAgent, CommitMessageA
id: 'omp',
label: 'OMP',
binary: 'omp',
promptDelivery: 'stdin',
buildArgs: ({ model, thinkingLevel }) => [
promptDelivery: 'file',
buildArgs: ({ prompt, model, thinkingLevel }) => [
'--print',
'--no-session',
'--no-tools',
@@ -105,7 +105,8 @@ export const COMMIT_MESSAGE_AGENT_SPECS: Partial<Record<TuiAgent, CommitMessageA
'--mode',
'text',
...(model && model !== 'default' ? ['--model', model] : []),
...(thinkingLevel ? ['--thinking', thinkingLevel] : [])
...(thinkingLevel ? ['--thinking', thinkingLevel] : []),
prompt
],
singletonOptions: [['--model'], ['--thinking']],
modelSource: 'dynamic',
+31 -8
View File
@@ -18,13 +18,13 @@ describe('OMP Source Control AI', () => {
})
}
)
it('plans stdin delivery with tools and extensions disabled', () => {
it('plans file delivery with tools and extensions disabled', () => {
const spec = getCommitMessageAgentSpec('omp')
expect(spec?.promptDelivery).toBe('stdin')
expect(spec?.promptDelivery).toBe('file')
if (!spec) {
throw new Error('Missing OMP spec')
}
const args = spec.buildArgs({ prompt: 'large diff', model: 'default' })
const args = spec.buildArgs({ prompt: '@{prompt-file}', model: 'default' })
expect(args).toEqual([
'--print',
'--no-session',
@@ -33,11 +33,23 @@ describe('OMP Source Control AI', () => {
'--no-skills',
'--no-rules',
'--mode',
'text'
'text',
'@{prompt-file}'
])
expect(
spec?.buildArgs({ prompt: '', model: 'provider/exact-model', thinkingLevel: 'low' })
).toEqual([...args, '--model', 'provider/exact-model', '--thinking', 'low'])
spec?.buildArgs({
prompt: '@{prompt-file}',
model: 'provider/exact-model',
thinkingLevel: 'low'
})
).toEqual([
...args.slice(0, -1),
'--model',
'provider/exact-model',
'--thinking',
'low',
'@{prompt-file}'
])
})
it('discovers provider-qualified models through the existing OMP JSON parser', () => {
const discovery = getCommitMessageAgentSpec('omp')?.modelDiscovery
@@ -58,8 +70,10 @@ describe('OMP Source Control AI', () => {
{
agentId: 'omp',
model: 'provider/model',
thinkingLevel: 'medium',
backslash,
agentArgs: '--model provider/override'
agentCommandOverride: 'OMP_ENV=value omp --model provider/prefix --thinking high',
agentArgs: '--model provider/override --thinking low --verbose --'
},
prompt
)
@@ -67,7 +81,16 @@ describe('OMP Source Control AI', () => {
if (!result.ok) {
throw new Error(result.error)
}
expect(result.plan.stdinPayload).toBe(prompt)
expect(result.plan.stdinPayload).toBeNull()
expect(result.plan.promptFile).toEqual({
content: prompt,
argIndex: result.plan.args.indexOf('@{prompt-file}')
})
expect(result.plan.env).toEqual({ OMP_ENV: 'value' })
expect(result.plan.args.slice(-3)).toEqual(['@{prompt-file}', '--verbose', '--'])
expect(result.plan.args.filter((arg) => arg === '--thinking')).toHaveLength(1)
expect(result.plan.args).toContain('low')
expect(result.plan.args).not.toContain('high')
expect(result.plan.args).not.toContain(prompt)
expect(result.plan.args.filter((arg) => arg === '--model')).toHaveLength(1)
expect(result.plan.args).toContain('provider/override')
+13 -2
View File
@@ -36,6 +36,8 @@ export type CommitMessagePlan = {
args: string[]
/** Non-null when the prompt should be piped via stdin. */
stdinPayload: string | null
/** Execution-host-owned prompt attachment, materialized only when starting the process. */
promptFile?: { content: string; argIndex: number }
/** Human-readable label used in error prefixes (e.g. "Claude failed: ..."). */
label: string
/** Leading command assignments, applied on the execution host. */
@@ -137,7 +139,7 @@ function applySingletonOptionOverrides(args: {
function insertAdditionalAgentArgs(args: {
baseArgs: string[]
agentArgs: string[]
promptDelivery: 'argv' | 'stdin'
promptDelivery: 'argv' | 'stdin' | 'file'
prompt: string
}): string[] {
if (!args.agentArgs.length) {
@@ -221,7 +223,8 @@ export function planCommitMessageGeneration(
}
}
const argvPrompt = spec.promptDelivery === 'argv' ? prompt : ''
const argvPrompt =
spec.promptDelivery === 'file' ? '@{prompt-file}' : spec.promptDelivery === 'argv' ? prompt : ''
const agentArgs = planAdditionalAgentArgs(input.agentArgs, input.backslash)
if (!agentArgs.ok) {
return agentArgs
@@ -287,6 +290,14 @@ export function planCommitMessageGeneration(
: spec.promptDelivery === 'stdin'
? prompt
: null,
...(spec.promptDelivery === 'file'
? {
promptFile: {
content: prompt,
argIndex: merged.prefixArgs.length + merged.generatedArgs.length - 1
}
}
: {}),
label: spec.label,
...(formatOption?.value === 'json' ? { outputFormat: 'opencode-json' as const } : {}),
...(kimiFormatOption?.value === 'stream-json'
@@ -64,7 +64,11 @@ for (const hostKey of ['local', 'ssh:omp-host']) {
expect(resolved.value.params.model).toBe(selectedModel ?? 'default')
const args = spec.buildArgs({ prompt: '', model: resolved.value.params.model })
if (selectedModel === 'provider/exact-model') {
expect(args.slice(-2)).toEqual(['--model', 'provider/exact-model'])
const modelIndex = args.indexOf('--model')
expect(args.slice(modelIndex, modelIndex + 2)).toEqual([
'--model',
'provider/exact-model'
])
} else {
expect(args).not.toContain('--model')
}
+25
View File
@@ -0,0 +1,25 @@
export const POSIX_PRINTF_RECORD_MAX_BYTES = 1_024
const PRINTF_ESCAPED_BYTES = new Set([0x21, 0x27, 0x5c])
export function* encodePosixPrintfRecords(command: string): Generator<string> {
let chunk = ''
let chunkBytes = 0
for (const character of command) {
const codePoint = character.codePointAt(0)!
const isSafePrintableAscii =
codePoint >= 0x20 && codePoint <= 0x7e && !PRINTF_ESCAPED_BYTES.has(codePoint)
const encodedCharacter =
codePoint > 0x7f || isSafePrintableAscii
? character
: `\\0${codePoint.toString(8).padStart(3, '0')}`
const encodedBytes = codePoint > 0x7f ? Buffer.byteLength(character) : encodedCharacter.length
if (chunkBytes + encodedBytes > POSIX_PRINTF_RECORD_MAX_BYTES) {
yield chunk
chunk = ''
chunkBytes = 0
}
chunk += encodedCharacter
chunkBytes += encodedBytes
}
yield chunk
}
@@ -46,3 +46,5 @@ function countEscapes(arg: string): number {
}
return count
}
export class WindowsCommandLineBudgetError extends Error {}
+8
View File
@@ -162,3 +162,11 @@ export function uncRouteKey(cwd: string): string | null {
const server = cwd.slice(2).split(/[\\/]/, 1)[0]
return `unc:${server.toLowerCase()}`
}
/** Match command routing: a native UNC cwd takes precedence over a distro hint. */
export function wslDistroForCommand(cwd: string | undefined, override?: string): string | null {
if (process.platform !== 'win32') {
return null
}
return (cwd ? parseWslUncPath(cwd)?.distro : undefined) ?? override ?? null
}