Merge remote-tracking branch 'origin/master' into codex/pr332-merge-master-20260719

# Conflicts:
#	docs/cookbook/extension-cookbook.i18n.yaml
This commit is contained in:
Tianyi Cui
2026-07-19 15:07:58 +08:00
63 changed files with 2471 additions and 52 deletions

View File

@@ -2,7 +2,7 @@
A replay LLM plugin for keyless snapshot tests. It yields model streams reconstructed from a recorded **session JSONL** fixture, so a test can boot the real agent against a fixed model transcript with no API key. With `providers` configured it registers a replay-only adapter whose catalog is visible to clients such as ACP editors; without `providers` it installs the catch-all `llm/stream` waterfall used by tests that do not need discovery.
Its consumer is the ACP snapshot harness in `examples/acp-agent`, which loads this plugin (via `cordis.snapshot.yml`) in place of a real LLM adapter. The package exists so its derive/parse/replay logic falls under the per-file 100% coverage gate on `packages/*/src` (the same logic, while it lived under `examples/`, was outside the gate).
Its consumers are the ACP snapshot harness in `examples/acp-agent` and the `stream-json` snapshot in `examples/headless-agent`; each loads this plugin in place of a real LLM adapter. Keeping derivation and replay here places that logic under the per-file 100% coverage gate on `packages/*/src`.
## How the fixture works

View File

@@ -2,7 +2,7 @@
Shared subprocess harness for tests that boot an app and `cordis.yml` through the Cordis Loader. `resolveExampleLaunch` selects local `src` mode (tsx and root tsconfig paths) or CI `lib` mode (plain Node and package exports) from an explicit mode or `DSH_EXAMPLE_MODE`.
`runLoaderSmoke` owns the isolated cwd, DSH homes, stdin, diagnostics, deadline, termination, and cleanup. It returns both streams after a zero exit and rejects with both streams on failure.
`runLoaderSmoke` accepts bin and config paths, optional complete bin arguments, environment overrides, stdin, pre-run setup, and pre-cleanup inspection. It owns the isolated cwd, DSH homes, diagnostics, deadline, termination, EOF, and cleanup; it returns both streams after a zero exit and rejects with both streams on failure.
This is support-tier test infrastructure, not product API.

View File

@@ -1,14 +1,12 @@
/**
* Shared subprocess harness for keyless example smokes that boot a real
* `cordis.yml` through the stdio-agent bin and Cordis Loader.
* `cordis.yml` through an app bin and Cordis Loader.
*
* It also owns the mode-aware launch resolver every example subprocess harness shares
* ({@link resolveExampleLaunch}): booting an example bin from TypeScript source under `tsx` (the
* zero-build dev path, resolving `@deepseek-ai/dsh-*` / `@cordisjs/*` through the tsconfig `paths`
* map) or from built `lib/` under plain Node (resolving bare packages through real `exports`, as an
* installed consumer does, while Node type-strips relative example-local TypeScript plugins).
* Consolidating that spawn glue here retires the copies in the ACP snapshot harness and the example
* e2e drivers (the `TODO(acp-test-harness)`).
*
* @module @deepseek-ai/dsh-loader-smoke
*/
@@ -126,12 +124,14 @@ export interface LoaderSmokeOptions {
readonly label: string
/** Prefix for the isolated temporary process cwd. */
readonly tempDirPrefix: string
/** Absolute stdio-agent bin SOURCE path (`<pkg>/src/bin.ts`); the `lib` bin is derived from it. */
/** Absolute app-bin source path (`<pkg>/src/bin.ts`); the `lib` bin is derived from it. */
readonly binScript: string
/** Explicit plain-Node entry for `lib` mode; intended for test fixtures outside a package `src/` tree. */
readonly libBinScript?: string | undefined
/** Absolute real Loader config path. */
/** Absolute real Loader config path, passed as the sole bin argument by default. */
readonly configPath: string
/** Complete argv after the bin path; overrides the default `[configPath]`. */
readonly binArgs?: readonly string[]
/** Absolute repo tsconfig path used for unbuilt workspace-package resolution (required in `src` mode). */
readonly tsconfigPath: string
/** Boot from source via tsx (`src`) or built lib via plain Node (`lib`); defaults to the environment's mode. */
@@ -142,6 +142,10 @@ export interface LoaderSmokeOptions {
readonly stdinLines?: readonly string[]
/** Process deadline override for harness tests. */
readonly processTimeoutMs?: number
/** Optional world-state setup run in the isolated cwd before process start. */
readonly prepare?: (cwd: string) => Promise<void> | void
/** Optional world-state assertion run in the isolated cwd before cleanup. */
readonly inspect?: (cwd: string) => Promise<void> | void
}
/** Captured output from a Loader smoke that exited successfully. */
@@ -162,17 +166,18 @@ export interface LoaderSmokeResult {
export async function runLoaderSmoke(options: LoaderSmokeOptions): Promise<LoaderSmokeResult> {
const cwd = await mkdtemp(join(tmpdir(), options.tempDirPrefix))
const processTimeoutMs = options.processTimeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS
const launch = resolveExampleLaunch({
srcBin: options.binScript,
libBin: options.libBinScript,
configArgs: [options.configPath],
...options.mode !== undefined ? { mode: options.mode } : {},
tsconfigPath: options.tsconfigPath,
exposeInternals: true,
env: { DSH_HOME: join(cwd, '.dsh'), DSH_AGENTS_HOME: join(cwd, '.agents'), ...options.env },
})
try {
return await new Promise((resolve, reject) => {
await options.prepare?.(cwd)
const launch = resolveExampleLaunch({
srcBin: options.binScript,
libBin: options.libBinScript,
configArgs: options.binArgs ?? [options.configPath],
...options.mode !== undefined ? { mode: options.mode } : {},
tsconfigPath: options.tsconfigPath,
exposeInternals: true,
env: { DSH_HOME: join(cwd, '.dsh'), DSH_AGENTS_HOME: join(cwd, '.agents'), ...options.env },
})
const result = await new Promise<LoaderSmokeResult>((resolve, reject) => {
const child = spawn(launch.command, launch.args, {
cwd,
env: { ...process.env, ...launch.env },
@@ -217,6 +222,8 @@ export async function runLoaderSmoke(options: LoaderSmokeOptions): Promise<Loade
child.stdin.end((options.stdinLines ?? []).map(line => `${line}\n`).join(''))
})
await options.inspect?.(cwd)
return result
} finally {
await rm(cwd, { recursive: true, force: true })
}

View File

@@ -6,6 +6,7 @@ process.stdin.on('data', (chunk: string) => { input += chunk })
process.stdin.on('end', () => {
console.log(JSON.stringify({
configPath: process.argv[2],
args: process.argv.slice(2),
cwd: process.cwd(),
dshHome: process.env.DSH_HOME,
agentsHome: process.env.DSH_AGENTS_HOME,

View File

@@ -1,4 +1,6 @@
import { existsSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
@@ -22,6 +24,7 @@ describe('runLoaderSmoke', () => {
})
const output = JSON.parse(result.stdout) as {
configPath: string
args: string[]
cwd: string
dshHome: string
agentsHome: string
@@ -30,6 +33,7 @@ describe('runLoaderSmoke', () => {
}
expect(output).toMatchObject({
configPath,
args: [configPath],
marker: 'present',
input: 'one\ntwo\n',
})
@@ -39,6 +43,30 @@ describe('runLoaderSmoke', () => {
expect(existsSync(output.cwd)).toBe(false)
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('passes an arbitrary bin argv and inspects world state before cleanup', async () => {
let inspected = ''
let marker = ''
const result = await runLoaderSmoke({
label: 'argv fixture',
tempDirPrefix: 'loader-smoke-argv-',
binScript: fixture('success'),
libBinScript: fixture('success'),
configPath,
binArgs: ['--config', configPath, '--output-format', 'json', 'task with spaces'],
tsconfigPath,
prepare: cwd => writeFile(join(cwd, 'marker.txt'), 'prepared'),
inspect: async (cwd) => {
inspected = cwd
marker = await readFile(join(cwd, 'marker.txt'), 'utf8')
},
})
const output = JSON.parse(result.stdout) as { args: string[]; cwd: string }
expect(output.args).toEqual(['--config', configPath, '--output-format', 'json', 'task with spaces'])
expect(canonicalTempPath(inspected)).toBe(canonicalTempPath(output.cwd))
expect(marker).toBe('prepared')
expect(existsSync(inspected)).toBe(false)
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('rejects a non-zero exit with captured diagnostics', async () => {
await expect(runLoaderSmoke({
label: 'failure fixture',