chore: establish YCode replication baseline and TODO

This commit is contained in:
xiaohan.yuan committed 2026-10-17 02:43:49 +08:00
commit 6dc59f89ba
1118 files changed
+196002

No files matched your search

+293
View File
@@ -0,0 +1,293 @@
import { EventEmitter } from 'node:events'
import { mkdtemp, rm, stat } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { build } from 'esbuild'
interface FakeChildState {
exePath: string
args: string[]
cwd: string
child: EventEmitter & {
pid: number
exitCode: number | null
signalCode: string | null
killed: boolean
stdin: { write: (payload: string) => boolean; end: () => void }
kill: () => boolean
}
}
interface KernelTestState {
children: FakeChildState[]
readers: EventEmitter[]
stdinWrites: string[]
autoExitOnEnd: boolean
stdinEnds: number
readerCloses: number
killCalls: number
}
let failures = 0
function check(label: string, condition: boolean, detail = ''): void {
if (condition) console.log(` ✓ ${label}`)
else {
failures++
console.error(` ✗ ${label}${detail ? ` — ${detail}` : ''}`)
}
}
async function waitFor(predicate: () => boolean, timeoutMs = 500): Promise<boolean> {
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
if (predicate()) return true
await new Promise((resolve) => setTimeout(resolve, 5))
}
return predicate()
}
async function buildKernelBundle(outfile: string): Promise<void> {
await build({
entryPoints: [join(process.cwd(), 'src', 'main', 'omp.ts')],
outfile,
bundle: true,
platform: 'node',
format: 'esm',
logLevel: 'silent',
plugins: [
{
name: 'omp-session-kernel-mocks',
setup(pluginBuild) {
pluginBuild.onResolve({ filter: /^node:child_process$/ }, () => ({
path: 'mock-child-process',
namespace: 'omp-session-mock'
}))
pluginBuild.onLoad({ filter: /^mock-child-process$/, namespace: 'omp-session-mock' }, () => ({
loader: 'js',
contents: `
import { EventEmitter } from 'node:events';
const state = globalThis.__YCODE_OMP_SESSION_TEST__;
class FakeChild extends EventEmitter {
constructor(exePath, args, cwd) {
super();
this.pid = 100 + state.children.length;
this.exitCode = null;
this.signalCode = null;
this.killed = false;
this.stdin = {
write: (payload) => {
state.stdinWrites.push(payload);
let command;
try { command = JSON.parse(payload); } catch { return true; }
if (typeof command.id === 'string' && command.type !== 'host_tool_result') {
const reader = state.readers[state.readers.length - 1];
const data = command.type === 'set_host_tools'
? { toolNames: (command.tools || []).map((tool) => tool.name) }
: command.type === 'get_branch_messages'
? { messages: [{ entryId: 'user-entry-1', text: 'saved prompt' }] }
: command.type === 'get_tree' ? { tree: [], leafId: null }
: command.type === 'get_messages' ? { messages: [] } : {};
setImmediate(() => reader?.emit('line', JSON.stringify({
id: command.id, type: 'response', command: command.type, success: true, data
})));
}
return true;
},
end: () => {
state.stdinEnds++;
if (state.autoExitOnEnd) setTimeout(() => this.finish(0, null), 5);
}
};
this.stdout = {};
this.stderr = new EventEmitter();
state.children.push({ exePath, args, cwd, child: this });
}
finish(code, signal) {
if (this.exitCode !== null || this.signalCode !== null) return;
this.exitCode = code;
this.signalCode = signal;
this.emit('exit', code, signal);
this.emit('close', code, signal);
}
kill() {
this.killed = true;
state.killCalls++;
this.finish(null, 'SIGTERM');
return true;
}
}
export function spawn(exePath, args, options) {
return new FakeChild(exePath, args, options.cwd);
}
`
}))
pluginBuild.onResolve({ filter: /^node:readline$/ }, () => ({
path: 'mock-readline',
namespace: 'omp-session-mock'
}))
pluginBuild.onLoad({ filter: /^mock-readline$/, namespace: 'omp-session-mock' }, () => ({
loader: 'js',
contents: `
import { EventEmitter } from 'node:events';
const state = globalThis.__YCODE_OMP_SESSION_TEST__;
export function createInterface() {
const reader = new EventEmitter();
reader.close = () => { state.readerCloses++; };
state.readers.push(reader);
return reader;
}
`
}))
}
}
]
})
}
async function main(): Promise<void> {
const tempRoot = await mkdtemp(join(tmpdir(), 'ycode-omp-session-regression-'))
const state: KernelTestState = {
children: [],
readers: [],
stdinWrites: [],
autoExitOnEnd: true,
stdinEnds: 0,
readerCloses: 0,
killCalls: 0
}
const globalWithMock = globalThis as typeof globalThis & { __YCODE_OMP_SESSION_TEST__?: KernelTestState }
const previous = globalWithMock.__YCODE_OMP_SESSION_TEST__
globalWithMock.__YCODE_OMP_SESSION_TEST__ = state
try {
const outfile = join(tempRoot, 'omp-kernel-regression.mjs')
await buildKernelBundle(outfile)
const { OmpKernel, parseFirstProfileModel } = await import(pathToFileURL(outfile).href)
const yaml = [
'providers:',
' local-probe:',
' api: openai-completions',
' models:',
' - id: probe-model',
' name: Probe model',
' second-provider:',
' models:',
' - id: second-model'
].join('\n')
check('profile model resolution returns provider/model, not profile/model',
parseFirstProfileModel(yaml) === 'local-probe/probe-model')
check('model resolution returns null when models.yml has no configured provider model',
parseFirstProfileModel('providers:\n empty-provider:\n models: []') === null)
const sessionPath = join(tempRoot, 'native', 'session-one.jsonl')
const selectedModel = 'local-probe/probe-model'
const persistent = new OmpKernel(process.execPath, tempRoot, 'ycode', sessionPath, selectedModel)
const persistentStart = persistent.start()
check('session parent directory is created before spawn', await waitFor(() => state.children.length === 1) &&
(await stat(dirname(sessionPath))).isDirectory())
check('persistent start reaches the ready frame', state.readers.length === 1)
state.readers[0]?.emit('line', JSON.stringify({ type: 'ready' }))
await persistentStart
const persistentArgs = state.children[0]?.args ?? []
check('persistent mode passes the configured provider/model selection',
persistentArgs.includes(`--model=${selectedModel}`))
check('built-in OMP tools are disabled by default so execution stays behind the host broker',
persistentArgs.includes('--no-tools'))
check('persistent mode passes the native resume path and session directory',
persistentArgs.includes(`--resume=${sessionPath}`) && persistentArgs.includes(`--session-dir=${dirname(sessionPath)}`))
check('persistent mode omits --no-session', !persistentArgs.includes('--no-session'))
const hostToolsAck = await persistent.setHostTools([{
name: 'read',
description: 'Read workspace text',
parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] }
}])
const branchMessages = await persistent.getBranchMessages()
const branchAck = await persistent.branch('user-entry-1')
await persistent.respondToHostTool('host-request-1', 'tool result')
const image = { type: 'image' as const, data: 'c21hbGw=', mimeType: 'image/png' }
const promptAck = await persistent.prompt('inspect this image', [image])
const writtenFrames = state.stdinWrites.map((payload) => JSON.parse(payload) as Record<string, unknown>)
const promptFrame = writtenFrames.find((frame) => frame.type === 'prompt')
const hostResultFrame = writtenFrames.find((frame) => frame.type === 'host_tool_result')
check('host tools, branch history, and images use their typed RPC helpers',
hostToolsAck.success === true && branchMessages.data.messages[0]?.entryId === 'user-entry-1' &&
branchAck.success === true && promptAck.success === true &&
Array.isArray(promptFrame?.images) &&
JSON.stringify((promptFrame.images as unknown[])[0]) === JSON.stringify(image) &&
hostResultFrame?.id === 'host-request-1')
let sawExit = false
persistent.once('exit', () => { sawExit = true })
await persistent.dispose()
check('dispose ends stdin and waits until the child exits', state.stdinEnds === 1 && sawExit)
check('kernel event listeners are removed after child exit', persistent.eventNames().length === 0)
const ephemeral = new OmpKernel(process.execPath, tempRoot, 'ycode', undefined, selectedModel)
const ephemeralStart = ephemeral.start()
check('ephemeral start creates a second child', await waitFor(() => state.children.length === 2))
state.readers[1]?.emit('line', JSON.stringify({ type: 'ready' }))
await ephemeralStart
const ephemeralArgs = state.children[1]?.args ?? []
check('three-argument constructors keep --no-session behavior',
ephemeralArgs.includes('--no-session') && !ephemeralArgs.some((arg) => arg.startsWith('--resume=')) &&
ephemeralArgs.includes('--no-tools'))
await ephemeral.dispose()
const missingModel = new OmpKernel(process.execPath, tempRoot, 'ycode', undefined, null)
let missingModelError: unknown
try {
await missingModel.start()
} catch (error) {
missingModelError = error
}
check('a profile without provider/model fails before spawn',
missingModelError instanceof Error && missingModelError.message.includes('provider/model') && state.children.length === 2)
state.autoExitOnEnd = false
const forceKilled = new OmpKernel(process.execPath, tempRoot, 'ycode', join(tempRoot, 'native', 'session-three.jsonl'), selectedModel)
const forceKillStart = forceKilled.start()
check('force-kill case starts a third child', await waitFor(() => state.children.length === 3))
state.readers[2]?.emit('line', JSON.stringify({ type: 'ready' }))
await forceKillStart
check('built-in OMP tools remain disabled for every kernel session',
state.children[2]?.args.includes('--no-tools') === true)
const killStartedAt = Date.now()
const keepAlive = setTimeout(() => undefined, 1800)
await forceKilled.dispose()
clearTimeout(keepAlive)
const killDelay = Date.now() - killStartedAt
check('stalled child is killed after the 1500ms grace period',
state.killCalls === 1 && state.children[2]?.child.killed === true && killDelay >= 1400 && killDelay < 2500,
`${killDelay}ms`)
const recoverable = new OmpKernel(process.execPath, tempRoot, 'ycode', sessionPath, selectedModel)
const firstStart = recoverable.start()
check('recoverable kernel creates a fresh sidecar', await waitFor(() => state.children.length === 4))
state.readers[3]?.emit('line', JSON.stringify({ type: 'ready' }))
await firstStart
state.children[3]?.child.finish(1, null)
const recoveredStart = recoverable.start()
check('kernel can restart after its sidecar exits', await waitFor(() => state.children.length === 5))
state.readers[4]?.emit('line', JSON.stringify({ type: 'ready' }))
await recoveredStart
const recoveredArgs = state.children[4]?.args ?? []
check('restarted sidecar reuses the same resume path and provider/model',
recoveredArgs.includes(`--resume=${sessionPath}`) && recoveredArgs.includes(`--model=${selectedModel}`))
await recoverable.dispose()
console.log(failures === 0 ? '\nOMP session regression checks passed.\n' : `\n${failures} OMP session regression check(s) failed.\n`)
process.exitCode = failures === 0 ? 0 : 1
} finally {
globalWithMock.__YCODE_OMP_SESSION_TEST__ = previous
await rm(tempRoot, { recursive: true, force: true })
}
}
main().catch((error: unknown) => {
console.error('OMP session regression checks aborted:', error)
process.exitCode = 1
})