Merge remote-tracking branch 'origin/master' into codex/simp-snapshot-fixture-inventory

This commit is contained in:
Tianyi Cui
2026-07-18 13:43:12 +08:00
38 files changed
+821 -191

No files matched your search

+2 -2
View File
@@ -1,6 +1,6 @@
# AGENTS.md — Examples
Runnable harness compositions. **Examples are NOT workspaces**: their private `package.json` files are dependency-free stubs, and the cordis Loader boots each `cordis.yml` unbuilt through `tsx` plus the root tsconfig paths.
Runnable harness compositions. `examples/` is one workspace member and the module-resolution root for runnable and test Cordis configs; it is not a build target. [package.json](package.json) declares the packages loaded by those configs, while each leaf's private `package.json` remains metadata only.
Extract reusable logic into `packages/`, where per-file coverage and README gates apply. Examples keep only `cordis.yml` wiring, demo artifacts, and e2e/snapshot scenarios; app package bins own boot glue.
@@ -13,7 +13,7 @@ Each example has both:
Mock-only examples require only the keyless tier; state that exception in the test.
Keyless stdio smokes use `@deepseek-ai/dsh-loader-smoke` for isolation, root-tsconfig loading, subprocess lifecycle, diagnostics, EOF, and cleanup; tests supply paths, environment, input, and assertions.
Keyless stdio smokes use `@deepseek-ai/dsh-loader-smoke`; tests supply paths, environment, input, and assertions. Every checked-in test Cordis config lives under its corresponding `examples/<agent>/` leaf. Map a package-owned config to `examples/<agent>/tests/fixtures/<group>/<package>/cordis.yml`, keep its driver and assertions package-local, and declare every package it names in both root `tsconfig.json` references and `examples/package.json`.
Do not inventory example tests here; the `tests/` trees and root scripts are authoritative.
+22 -22
View File
@@ -15,6 +15,7 @@ import {
type RequestPermissionResponse,
type SessionNotification,
} from '@agentclientprotocol/sdk'
import { resolveExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
/**
* Boots examples/acp-agent as an ACP subprocess. The key-gated prompt leg
@@ -25,8 +26,6 @@ import {
// The child runs from a temp cwd, so its bin and config path are absolute.
const binScript = fileURLToPath(new URL('../../../packages/examples/acp-demo/src/bin.ts', import.meta.url))
const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
// Resolve tsx absolutely because the subprocess runs outside the repo.
const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
// The root tsconfig supplies unbuilt workspace `paths`; making it explicit
// avoids accidental resolution through stale built output.
const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
@@ -38,24 +37,21 @@ interface Spawned {
stderr: string[]
}
// TODO(acp-test-harness): this subprocess/client boot glue is duplicated with
// hooks.e2e.ts and partly with dsh-acp-snapshot's harness. Migrate both e2e
// files onto that launcher before the TSX/env/permission-stub details drift.
function spawnAcpAgent(cwd: string, env: NodeJS.ProcessEnv = process.env): Spawned {
const child = spawn(
process.execPath,
['--import', tsxLoader, binScript, '--config', configPath],
{
cwd,
env: {
...env,
TSX_TSCONFIG_PATH: repoTsconfig,
DSH_PERMISSION_MODE: 'danger-full-access',
DSH_HOME: join(cwd, '.dsh'),
DSH_AGENTS_HOME: join(cwd, '.agents'),
},
stdio: ['pipe', 'pipe', 'pipe'],
const launch = resolveExampleLaunch({
srcBin: binScript,
configArgs: ['--config', configPath],
tsconfigPath: repoTsconfig,
env: {
DSH_PERMISSION_MODE: 'danger-full-access',
DSH_HOME: join(cwd, '.dsh'),
DSH_AGENTS_HOME: join(cwd, '.agents'),
},
})
const child = spawn(
launch.command,
launch.args,
{ cwd, env: { ...env, ...launch.env }, stdio: ['pipe', 'pipe', 'pipe'] },
)
const stderr: string[] = []
child.stderr.setEncoding('utf8')
@@ -138,16 +134,20 @@ describe('acp-agent over real stdio (no key required)', () => {
workdir = await mkdtemp(join(tmpdir(), 'acp-e2e-'))
// Collect raw stdout bytes directly (bypass the SDK framing) to inspect.
// A dummy key boots the adapter; this purity test sends no prompt and makes no model call.
const child = spawn(process.execPath, ['--import', tsxLoader, binScript, '--config', configPath], {
cwd: workdir,
const launch = resolveExampleLaunch({
srcBin: binScript,
configArgs: ['--config', configPath],
tsconfigPath: repoTsconfig,
env: {
...process.env,
DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY ?? 'sk-dummy-for-boot',
TSX_TSCONFIG_PATH: repoTsconfig,
DSH_PERMISSION_MODE: 'danger-full-access',
DSH_HOME: join(workdir, '.dsh'),
DSH_AGENTS_HOME: join(workdir, '.agents'),
},
})
const child = spawn(launch.command, launch.args, {
cwd: workdir,
env: { ...process.env, ...launch.env },
stdio: ['pipe', 'pipe', 'pipe'],
})
const out: string[] = []
+14 -12
View File
@@ -15,6 +15,7 @@ import {
type RequestPermissionResponse,
type SessionNotification,
} from '@agentclientprotocol/sdk'
import { resolveExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
/**
* Exercises the default ACP composition through the real bin and Loader. The
@@ -28,9 +29,8 @@ import {
const binScript = fileURLToPath(new URL('../../../packages/examples/acp-demo/src/bin.ts', import.meta.url))
const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
// The subprocess runs from a temp cwd outside the repo; point tsx at the repo
// tsconfig so the unbuilt `paths` map resolves (see examples/AGENTS.md).
// tsconfig so the unbuilt `paths` map resolves in src mode (see examples/AGENTS.md).
const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
// Without a usable bwrap/Seatbelt runner, the strict attempt fails closed with
@@ -55,17 +55,19 @@ interface Spawned {
/** Boot the example as an ACP subprocess; the scripted client answers every permission prompt with `answer`. */
function spawnAcpAgent(cwd: string, answer: 'allow-once' | 'reject-once'): Spawned {
const launch = resolveExampleLaunch({
srcBin: binScript,
configArgs: ['--config', configPath],
tsconfigPath: repoTsconfig,
// A dummy key lets the deepseek adapter boot keyless (presence-checked at
// apply, used only on a real model call); the with-key tests carry the
// real key, so the fallback is inert there.
env: { DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY || 'sk-dummy-for-boot' },
})
const child = spawn(
process.execPath,
['--import', tsxLoader, binScript, '--config', configPath],
{
cwd,
// A dummy key lets the deepseek adapter boot keyless (presence-checked at
// apply, used only on a real model call); the with-key tests carry the
// real key, so the fallback is inert there.
env: { ...process.env, DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY || 'sk-dummy-for-boot', TSX_TSCONFIG_PATH: repoTsconfig },
stdio: ['pipe', 'pipe', 'pipe'],
},
launch.command,
launch.args,
{ cwd, env: { ...process.env, ...launch.env }, stdio: ['pipe', 'pipe', 'pipe'] },
)
const stderr: string[] = []
child.stderr.setEncoding('utf8')
+10 -4
View File
@@ -15,6 +15,7 @@ import {
type RequestPermissionResponse,
type SessionNotification,
} from '@agentclientprotocol/sdk'
import { resolveExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
/**
* With-key e2e for the Claude hook bridge. The process-level `./hooks.json` is
@@ -25,7 +26,6 @@ import {
const binScript = fileURLToPath(new URL('../../../packages/examples/acp-demo/src/bin.ts', import.meta.url))
const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
interface Spawned {
@@ -36,10 +36,16 @@ interface Spawned {
}
function spawnAcpAgent(cwd: string): Spawned {
const launch = resolveExampleLaunch({
srcBin: binScript,
configArgs: ['--config', configPath],
tsconfigPath: repoTsconfig,
env: { DSH_PERMISSION_MODE: 'danger-full-access' },
})
const child = spawn(
process.execPath,
['--import', tsxLoader, binScript, '--config', configPath],
{ cwd, env: { ...process.env, TSX_TSCONFIG_PATH: repoTsconfig, DSH_PERMISSION_MODE: 'danger-full-access' }, stdio: ['pipe', 'pipe', 'pipe'] },
launch.command,
launch.args,
{ cwd, env: { ...process.env, ...launch.env }, stdio: ['pipe', 'pipe', 'pipe'] },
)
const stderr: string[] = []
child.stderr.setEncoding('utf8')
@@ -0,0 +1,19 @@
# Test-only composition: keep time-context opt-in while exercising its real Loader/app path.
- id: mock-llm
name: '../../../../src/mock-llm.ts'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: time-context
name: '@deepseek-ai/dsh-time-context'
- id: stdio-agent
name: '@deepseek-ai/dsh-stdio-demo'
config:
provider: mock
model: mock-echo
persona: 'Test the time-context plugin.'
welcome: 'time-context e2e ready.'
persistenceRoot: './.sessions'
workspaceContext: false
+46
View File
@@ -0,0 +1,46 @@
{
"name": "dsh-examples",
"private": true,
"version": "0.0.1",
"type": "module",
"description": "Workspace umbrella for runnable demos and example-owned test compositions: declares their cordis.yml packages so plain Node resolves real exports→lib. Not a build target.",
"dependencies": {
"@cordisjs/plugin-hmr": "workspace:*",
"@cordisjs/plugin-include": "workspace:*",
"@deepseek-ai/dsh-acp-demo": "workspace:*",
"@deepseek-ai/dsh-bash-local": "workspace:*",
"@deepseek-ai/dsh-bash-sandbox": "workspace:*",
"@deepseek-ai/dsh-code-runtime-worker": "workspace:*",
"@deepseek-ai/dsh-compact-basic": "workspace:*",
"@deepseek-ai/dsh-fs-local": "workspace:*",
"@deepseek-ai/dsh-fs-policy": "workspace:*",
"@deepseek-ai/dsh-hooks-claude": "workspace:*",
"@deepseek-ai/dsh-hooks-codex": "workspace:*",
"@deepseek-ai/dsh-llm": "workspace:*",
"@deepseek-ai/dsh-llm-deepseek": "workspace:*",
"@deepseek-ai/dsh-llm-replay": "workspace:*",
"@deepseek-ai/dsh-permission": "workspace:*",
"@deepseek-ai/dsh-repeat-tool-guard": "workspace:*",
"@deepseek-ai/dsh-sandbox-local": "workspace:*",
"@deepseek-ai/dsh-spill-local": "workspace:*",
"@deepseek-ai/dsh-spill-policy": "workspace:*",
"@deepseek-ai/dsh-stdio-demo": "workspace:*",
"@deepseek-ai/dsh-subagent": "workspace:*",
"@deepseek-ai/dsh-subagent-fork": "workspace:*",
"@deepseek-ai/dsh-subagent-spawn": "workspace:*",
"@deepseek-ai/dsh-time-context": "workspace:*",
"@deepseek-ai/dsh-timeout-policy": "workspace:*",
"@deepseek-ai/dsh-token-meter": "workspace:*",
"@deepseek-ai/dsh-tool-cordis": "workspace:*",
"@deepseek-ai/dsh-tool-fs": "workspace:*",
"@deepseek-ai/dsh-tool-fs-search": "workspace:*",
"@deepseek-ai/dsh-tool-subagent": "workspace:*",
"@deepseek-ai/dsh-tool-todo": "workspace:*",
"@deepseek-ai/dsh-tool-workflow": "workspace:*",
"@deepseek-ai/dsh-tools": "workspace:*",
"@deepseek-ai/dsh-user-approval": "workspace:*",
"@deepseek-ai/dsh-web": "workspace:*",
"@deepseek-ai/dsh-web-fetch-local": "workspace:*",
"@deepseek-ai/dsh-workflow-workerthread": "workspace:*"
}
}