Merge remote-tracking branch 'origin/master' into codex/fix-deepseek-keepalive

# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.i18n.yaml
#	examples/headless-agent/tests/fixtures/deepseek-defaults.cordis.yml
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/util/timeout/README.i18n.yaml
This commit is contained in:
fz
2026-08-09 15:00:08 +08:00
4661 changed files with 164891 additions and 79798 deletions

View File

@@ -29,6 +29,10 @@ class CliMockAdapter extends LlmAdapter {
}
async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
if (process.env.DSH_CLI_MOCK_FAILURE === '1') {
yield { type: 'finish', reason: { kind: 'error', failure: { code: 'SERVER', message: 'CLI mock provider failed' } } }
return
}
const toolResult = options.messages.at(-1)?.content.find(block => block.type === 'tool-result')
if (toolResult === undefined) {
const args = JSON.stringify({ command: 'printf CLI_TOOL_ROUND_TRIP', description: 'Prove the CLI tool round trip.' })
@@ -59,7 +63,7 @@ export const inject = ['llm']
/** Register the keyless `cli-mock` adapter. */
export function apply(ctx: Context): void {
ctx.llm.registerAdapter(['cli-mock'], new CliMockAdapter())
ctx.on('agent/request', async (_agent, _turn, step, _signal, next) => {
ctx.on('agent/request', async ({ step }, next) => {
const config = await next()
return step === 2 ? { ...config, reasoningEffort: OFF } : config
})

View File

@@ -12,15 +12,21 @@
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- id: cli-agent
name: '@deepseek-ai/dsh-cli-demo'
- id: agent-spine
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
provider: cli-mock
model: cli-mock
persistenceRoot: './.sessions'
agents:
- id: main
provider: cli-mock
model: cli-mock
cwd: !!js process.cwd()
workspaceContext: false
dshHome: './.dsh-home'
skills:
local:
agentsHome: './.agents-home'
persona: 'Keyless headless-agent smoke.'
- id: persistence
name: '@deepseek-ai/dsh-session-persistence-jsonl'
config:
root: './.sessions'

View File

@@ -5,14 +5,18 @@
patches:
- id: llm-deepseek
config:
apiKey: snapshot-key
baseURL: !!js process.env.DSH_SNAPSHOT_BASE_URL
thinking: disabled
streamIdleTimeoutMs: 150
- id: cli-agent
- id: agent-spine
config:
provider: deepseek-official
model: deepseek-v4-flash
persistenceRoot: './.sessions'
agents:
- id: main
provider: deepseek-official
model: deepseek-v4-flash
cwd: !!js process.cwd()
workspaceContext: false
persona: 'Keyless DeepSeek adapter defaults snapshot.'
- id: persistence
config:
root: './.sessions'

View File

@@ -0,0 +1,8 @@
- id: agent-default-model
config:
provider: cli-mock
model: cli-mock
- insert:
- id: cli-mock-llm
name: './snapshot-fixtures/cli-mock-llm.ts'

View File

@@ -0,0 +1,202 @@
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { boot } from '@deepseek-ai/dsh-app-boot'
import { Inbox } from '@deepseek-ai/dsh-agent'
import type { Agent } from '@deepseek-ai/dsh-agent'
import { Session, SessionId } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-fs-e2b'
import type {} from '@deepseek-ai/dsh-bash-local'
import type {} from '@deepseek-ai/dsh-lsp-local'
import type {} from '@deepseek-ai/dsh-pty-local'
const configPath = process.argv[2]
if (configPath === undefined) throw new Error('usage: bin.ts <cordis.yml>')
const ctx = await boot('e2b-composition', resolve(configPath))
const ownerFiber = ctx.plugin(() => {})
const ownerId = SessionId('e2b-live-owner')
const session = Session.create(ownerId)
const owner: Agent = {
id: ownerId,
options: {},
session,
inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }),
status: 'idle',
ctx: ownerFiber.ctx,
send() {},
followup() {},
steer() {},
inject() {},
cancel() {},
runMaintenance: task => task(new AbortController().signal),
whenIdle: () => Promise.resolve(),
}
const unregisterOwner = ctx.agents.register(owner)
let terminalId: Awaited<ReturnType<typeof ctx.pty.spawn>>['sessionId'] | undefined
try {
const sandbox = await ctx.e2b.getSandbox()
const fromFs = await ctx.fs.resolve('from-fs.txt')
const written = await ctx.fs.writeText(fromFs, 'written-by-fs\n', { kind: 'createIfAbsent' })
const observed = await ctx.fs.stat(fromFs)
if (observed?.version !== written.version) {
throw new Error(`E2B rename did not preserve version metadata: ${JSON.stringify({ written, observed })}`)
}
await ctx.fs.editText(
fromFs,
{ oldString: 'written-by-fs', newString: 'versioned-by-fs', replaceAll: false },
{ version: observed.version },
)
const bashRead = await ctx.bash.run(ctx.bash.resolve({ command: 'cat from-fs.txt' }))
if (bashRead.exitCode !== 0 || bashRead.stdout.text !== 'versioned-by-fs\n') {
throw new Error(`E2B Bash could not read the FS write: ${JSON.stringify(bashRead)}`)
}
const bashWrite = await ctx.bash.run(ctx.bash.resolve({ command: "printf 'written-by-bash\\n' > from-bash.txt" }))
if (bashWrite.exitCode !== 0) {
throw new Error(`E2B Bash could not write the shared filesystem: ${JSON.stringify(bashWrite)}`)
}
const fromBash = await ctx.fs.resolve('from-bash.txt')
const fsRead = await ctx.fs.readText(fromBash)
const environmentHandle = ctx.subprocess.spawn({
argv: ['env'],
cwd: process.cwd(),
stdio: { stdin: 'ignore', stdout: { maxBytes: 65_536 }, stderr: { maxBytes: 4_096 } },
graceMs: 500,
env: {
'FOO-BAR': 'hyphen-value',
DSH_EXPLICIT: 'managed-value',
TOKEN_EXPLICIT: 'credential-value',
},
})
const environmentOutcome = await environmentHandle.done
const environmentText = environmentHandle.collected.stdout?.readFrom(0).text
if (environmentOutcome.exitCode !== 0 || environmentText === undefined) {
throw new Error(`E2B subprocess environment probe failed: ${JSON.stringify(environmentOutcome)}`)
}
const environmentLines = new Set(environmentText.trimEnd().split('\n'))
const explicitEnvironment = [
'FOO-BAR=hyphen-value',
'DSH_EXPLICIT=managed-value',
'TOKEN_EXPLICIT=credential-value',
].every(entry => environmentLines.has(entry))
if (!explicitEnvironment) throw new Error(`E2B subprocess dropped an explicit environment entry: ${environmentText}`)
const splitUtf8Handle = ctx.subprocess.spawn({
argv: ['bash', '-c', "printf '\\344'; sleep 0.05; printf '\\275'; sleep 0.05; printf '\\240'; sleep 0.05; printf '\\345'; sleep 0.05; printf '\\245'; sleep 0.05; printf '\\275'"],
cwd: process.cwd(),
stdio: { stdin: 'ignore', stdout: { maxBytes: 32 }, stderr: { maxBytes: 4_096 } },
graceMs: 500,
env: {},
})
const splitUtf8Outcome = await splitUtf8Handle.done
const splitUtf8Output = splitUtf8Handle.collected.stdout?.readFrom(0).text
if (splitUtf8Outcome.exitCode !== 0 || splitUtf8Output !== '你好') {
throw new Error(`E2B subprocess corrupted split UTF-8 output: ${JSON.stringify({ splitUtf8Outcome, splitUtf8Output })}`)
}
const outputDrainStarted = Date.now()
const outputDrainHandle = ctx.subprocess.spawn({
argv: ['bash', '-c', "bash -c 'exec -a dsh-output-drain-descendant sleep 30' & printf 'leader-done\\n'"],
cwd: process.cwd(),
stdio: { stdin: 'ignore', stdout: { maxBytes: 64 }, stderr: { maxBytes: 4_096 } },
graceMs: 250,
env: {},
})
const outputDrainOutcome = await outputDrainHandle.done
const outputDrainText = outputDrainHandle.collected.stdout?.readFrom(0).text
const outputDrainElapsedMs = Date.now() - outputDrainStarted
outputDrainHandle.terminate()
const outputDrainExited = await outputDrainHandle.waitForExit(AbortSignal.timeout(5_000))
const outputDrainProcesses = await sandbox.commands.list()
const outputDrainClean = !outputDrainProcesses.some(processInfo =>
JSON.stringify([processInfo.cmd, processInfo.args]).includes('dsh-output-drain-descendant'),
)
if (outputDrainOutcome.exitCode !== 0 || outputDrainText !== 'leader-done\n'
|| outputDrainElapsedMs >= 10_000 || !outputDrainExited || !outputDrainClean) {
throw new Error(`E2B subprocess did not bound descendant-held output: ${JSON.stringify({
outputDrainOutcome, outputDrainText, outputDrainElapsedMs, outputDrainExited, outputDrainClean,
})}`)
}
const lspFixture = await readFile(new URL('./fixture-lsp.mjs', import.meta.url), 'utf8')
const remoteLspFixture = await ctx.fs.resolve('fixture-lsp.mjs')
await ctx.fs.writeText(remoteLspFixture, lspFixture, { kind: 'createIfAbsent' })
const remoteSource = await ctx.fs.resolve('multibyte # file.ts')
await ctx.fs.writeText(remoteSource, 'const café = "你好"\nconsole.log(café)\n', { kind: 'createIfAbsent' })
const hover = await ctx.lsp.query({
operation: 'hover',
filePath: 'multibyte # file.ts',
position: { line: 0, character: 7 },
workspaceRoot: process.cwd(),
})
const definition = await ctx.lsp.query({
operation: 'goToDefinition',
filePath: 'multibyte # file.ts',
position: { line: 0, character: 7 },
workspaceRoot: process.cwd(),
})
const terminal = await ctx.pty.spawn(owner, { type: 'shell' })
terminalId = terminal.sessionId
const terminalEcho = await ctx.pty.startSend(owner, terminal.sessionId, {
text: "printf 'PTY-你好\\n'",
submit: true,
}).done
const sleeping = ctx.pty.startSend(owner, terminal.sessionId, {
text: "printf 'DSH_SLEEP_%s\\n' READY; sleep 30",
submit: true,
})
let sleepReadyOutput = ''
const sleepReadyDeadline = Date.now() + 5_000
while (!sleepReadyOutput.includes('DSH_SLEEP_READY\n')) {
sleepReadyOutput += sleeping.readOutput().delta
if (sleepReadyOutput.includes('DSH_SLEEP_READY\n')) break
const settled = await Promise.race([
sleeping.done.then(result => ({ result })),
new Promise<undefined>(resolveDelay => setTimeout(() => { resolveDelay(undefined) }, 25)),
])
if (settled !== undefined) {
throw new Error(`E2B PTY successor settled before executing: ${JSON.stringify(settled.result)}`)
}
if (Date.now() >= sleepReadyDeadline) throw new Error(`E2B PTY successor did not execute: ${sleepReadyOutput}`)
}
const terminalSignal = await ctx.pty.signal(owner, terminal.sessionId, 'SIGINT')
const interrupted = await sleeping.done
const stubborn = await ctx.pty.startSend(owner, terminal.sessionId, {
text: "bash -c 'trap \"\" TERM; exec sleep 30' & printf 'DSH_STUBBORN_PID=%s\\n' \"$!\"",
submit: true,
}).done
const stubbornMatch = /DSH_STUBBORN_PID=([1-9][0-9]*)/.exec(stubborn.viewport)
if (stubbornMatch?.[1] === undefined) throw new Error(`E2B PTY did not report its stubborn child: ${stubborn.viewport}`)
const stubbornPid = Number(stubbornMatch[1])
const terminalScrollback = ctx.pty.read(owner, terminal.sessionId, { count: 50 })
await ctx.pty.kill(owner, terminal.sessionId, 'live E2B composition complete')
terminalId = undefined
const stubbornProbe = await sandbox.commands.run(`if kill -0 ${stubbornPid} 2>/dev/null; then printf alive; else printf gone; fi`)
const terminalTreeCleanup = stubbornProbe.stdout === 'gone'
if (!terminalTreeCleanup) throw new Error(`E2B PTY left process ${stubbornPid} alive after close`)
process.stdout.write(`${JSON.stringify({
sandboxId: (await ctx.e2b.getSandbox()).sandboxId,
bashRead: bashRead.stdout.text,
fsRead,
explicitEnvironment,
splitUtf8Output,
hover,
definition,
terminal: {
motd: terminal.motd,
echo: terminalEcho,
signal: terminalSignal,
interrupted,
treeCleanup: terminalTreeCleanup,
scrollback: terminalScrollback.text,
},
})}\n`)
} finally {
if (terminalId !== undefined) await ctx.pty.kill(owner, terminalId, 'fixture cleanup').catch(() => false)
unregisterOwner()
await ownerFiber.dispose()
await ctx.fiber.dispose()
}

View File

@@ -0,0 +1,57 @@
# One-world invariant (same pairing as examples/headless-agent/e2b.cordis.yml):
# e2b.cwd and sandbox-policy.workspaceRoot must name the same remote directory,
# which is also bash-local's implicit default workdir.
- id: e2b
name: '@deepseek-ai/dsh-e2b'
config:
cwd: !!js process.cwd()
timeoutMs: 180000
- id: subprocess-e2b
name: '@deepseek-ai/dsh-subprocess-e2b'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
config:
timeoutMs: 30000
- id: fs-e2b
name: '@deepseek-ai/dsh-fs-e2b'
- id: agents
name: '@deepseek-ai/dsh-agent'
- id: sandbox-policy
name: '@deepseek-ai/dsh-sandbox-policy'
config:
mode: danger-full-access
workspaceRoot: !!js process.cwd()
- id: pty
name: '@deepseek-ai/dsh-pty'
- id: pty-local
name: '@deepseek-ai/dsh-pty-local'
config:
pollIntervalMs: 25
exactProbeAfterMs: 150
idleSilenceMs: 2000
handoffGraceMs: 500
timeoutMs: 5000
disposeGraceMs: 1000
- id: lsp
name: '@deepseek-ai/dsh-lsp'
- id: lsp-local
name: '@deepseek-ai/dsh-lsp-local'
config:
servers:
fixture:
command: node
args:
- !!js process.cwd() + '/fixture-lsp.mjs'
extensionToLanguage:
.ts: typescript
shutdownTimeoutMs: 1000
killGraceMs: 500

View File

@@ -0,0 +1,85 @@
import { Buffer } from 'node:buffer'
let pending = Buffer.alloc(0)
let source = ''
let sourceUri = ''
function send(message) {
const body = Buffer.from(JSON.stringify(message))
process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`)
process.stdout.write(body)
}
function respond(id, result) {
send({ jsonrpc: '2.0', id, result })
}
function dispatch(message) {
switch (message.method) {
case 'initialize':
respond(message.id, {
capabilities: {
positionEncoding: 'utf-16',
textDocumentSync: { openClose: true, change: 1 },
definitionProvider: true,
referencesProvider: true,
implementationProvider: true,
hoverProvider: true,
},
})
return
case 'textDocument/didOpen':
source = message.params.textDocument.text
sourceUri = message.params.textDocument.uri
return
case 'textDocument/didClose':
source = ''
sourceUri = ''
return
case 'textDocument/hover':
if (!source.includes('const café = "你好"')) {
send({ jsonrpc: '2.0', id: message.id, error: { code: -32000, message: 'multibyte source was corrupted' } })
return
}
respond(message.id, {
contents: { kind: 'markdown', value: '**remote hover** 你好 café' },
range: { start: { line: 0, character: 6 }, end: { line: 0, character: 10 } },
})
return
case 'textDocument/definition':
case 'textDocument/references':
case 'textDocument/implementation':
respond(message.id, [{
uri: sourceUri,
range: { start: { line: 0, character: 6 }, end: { line: 0, character: 10 } },
}])
return
case 'shutdown':
respond(message.id, null)
return
case 'exit':
process.exit(0)
return
}
}
function drain() {
for (;;) {
const headerEnd = pending.indexOf('\r\n\r\n')
if (headerEnd < 0) return
const header = pending.subarray(0, headerEnd).toString('ascii')
const match = /(?:^|\r\n)Content-Length: ([0-9]+)(?:\r\n|$)/i.exec(header)
if (!match) throw new Error('missing Content-Length')
const length = Number(match[1])
const bodyStart = headerEnd + 4
if (pending.length < bodyStart + length) return
const body = pending.subarray(bodyStart, bodyStart + length)
pending = pending.subarray(bodyStart + length)
dispatch(JSON.parse(body.toString('utf8')))
}
}
process.stdin.on('data', chunk => {
pending = Buffer.concat([pending, chunk])
drain()
})

View File

@@ -17,12 +17,22 @@
- id: seed-goal
name: './seed-goal.ts'
- id: cli-agent
name: '@deepseek-ai/dsh-cli-demo'
- id: agent-spine
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
provider: cli-mock
model: cli-mock
agents:
- id: main
provider: cli-mock
model: cli-mock
cwd: !!js process.cwd()
persona: 'Test the persisted goal domain.'
persistenceRoot: './.sessions'
persistenceCompression: none
workspaceContext: false
- id: persistence
name: '@deepseek-ai/dsh-session-persistence-jsonl'
config:
root: './.sessions'
compression: none
- id: checkpoint-policy
name: '@deepseek-ai/dsh-session-checkpoint-policy'

View File

@@ -7,11 +7,13 @@ export const name = 'seed-goal'
export const inject = ['goals']
export function apply(ctx: Context): void {
ctx.on('agent/step', (agent) => {
if (ctx.goals.get(agent) !== undefined) return
ctx.goals.create(agent, {
objective: 'Prove the composed goal survives in the session log',
maxGoalRounds: 7,
})
ctx.on('agent/pre-step', ({ agent }, next) => {
if (ctx.goals.get(agent) === undefined) {
ctx.goals.create(agent, {
objective: 'Prove the composed goal survives in the session log',
maxGoalRounds: 7,
})
}
return next()
})
}

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env node
/** Snapshot-only Loader driver: stream one fixture turn as canonical JSONL. */
import type { Context } from 'cordis'
import { boot, installFailLoud, loadEnv, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
import { runFixtureTurn } from '@deepseek-ai/dsh-loader-smoke'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
const NAME = 'headless-test-driver'
const [configPath, ...taskParts] = process.argv.slice(2)
if (configPath === undefined || taskParts.length === 0 || taskParts.every(part => part.trim() === '')) {
throw new Error(`${NAME}: expected <config-path> <task...>`)
}
const uninstallFailLoud = installFailLoud(NAME)
let ctx: Context | undefined
try {
loadEnv(NAME)
ctx = await boot(NAME, resolveConfigPath(configPath, undefined))
const result = await runFixtureTurn(ctx, {
task: taskParts.join(' '),
onEvent: (sessionId: string, event: SessionEvent) => {
process.stdout.write(`${JSON.stringify({ type: 'session_event', sessionId, event })}\n`)
},
})
process.stdout.write(`${JSON.stringify(result)}\n`)
} catch (error: unknown) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
process.exitCode = 1
} finally {
await ctx?.fiber.dispose()
uninstallFailLoud()
}

View File

@@ -1,9 +1,19 @@
// Generated by dsh-plugin-prepare. Do not edit.
const manifest = {"name":"headless-repository-fixture","skills":["dsh-plugin-assets/skills/0"]}
// Value mirror: Cordis const enum FiberState.ACTIVE; keep aligned with dsh-repository-plugin source.ts.
const FIBER_ACTIVE = 2
export const name = "headless-repository-fixture"
export const inject = ["loader","skills"]
async function mount(ctx, plugin, label, config) {
const fiber = ctx.plugin(plugin, config)
await fiber
if (fiber.state !== FIBER_ACTIVE) {
const missing = Object.keys(fiber.inject).filter(service => fiber.ctx.get(service) === undefined)
throw new Error(`${label} did not activate (waiting for services: ${missing.join(', ') || 'unknown'})`)
}
}
export async function apply(ctx) {
const runtime = ctx.loader.builtins["dsh-repository-plugin"]
if (runtime === undefined) throw new Error("missing Cordis builtin dsh-repository-plugin")
await ctx.plugin(runtime, { baseUrl: import.meta.url, manifest })
await mount(ctx, runtime, 'repository Plugin runtime', { baseUrl: import.meta.url, manifest })
}

View File

@@ -0,0 +1,26 @@
/**
* Loader fixture that resumes the seeded diagnostic-scenario parent before
* CLI dispatch, so `list_agents` runs against its pre-seeded cold child.
* @module subagent-diagnostic-agent
*/
import type { Context } from 'cordis'
import type { SessionId } from '@deepseek-ai/dsh-session'
/** Fixture plugin name. */
export const name = 'subagent-diagnostic-agent'
/** Services that must exist before the fixture resumes its agent. */
export const inject = ['agents', 'agentLoop', 'sessionPersistence']
/**
* Resume the seeded session and bind its exact handle to this fixture's lifetime.
* @param ctx - settled agent and persistence services from the Loader tree.
* @returns after the resumed agent is published.
*/
export async function apply(ctx: Context): Promise<void> {
const handle = await ctx.agents.resume({
resumeSessionId: 'subagent-diagnostic-parent' as SessionId,
agentOptions: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },
})
ctx.effect(() => () => handle.dispose(), 'subagent-diagnostic-agent.handle')
}

View File

@@ -10,7 +10,8 @@ import { writeFile } from 'node:fs/promises'
import { createServer } from 'node:http'
import { once } from 'node:events'
import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
import { runOneShot } from '@deepseek-ai/dsh-cli-demo/src/cli.ts'
import { recordFeedback } from '@deepseek-ai/dsh-command-feedback'
import { runFixtureTurn } from '@deepseek-ai/dsh-loader-smoke'
const configPath = process.argv[2]
if (configPath === undefined) throw new Error('telemetry-otel driver requires a config path')
@@ -34,7 +35,16 @@ const ctx = await boot('telemetry-otel-e2e', resolveConfigPath(configPath, undef
try {
// The fixture credential rides the model-visible user message; the exported
// copy must scrub it while the canonical log keeps the original bytes.
await runOneShot(ctx, { task: 'prove telemetry with key sk-e2efixture1234567890' })
await runFixtureTurn(ctx, { task: 'prove telemetry with key sk-e2efixture1234567890' })
const mode = process.env.DSH_TELEMETRY_E2E_MODE ?? 'FULL'
if (mode !== 'FULL') {
const [agent] = ctx.get('agents')?.roots() ?? []
if (agent === undefined) throw new Error('telemetry-otel driver requires one root agent')
recordFeedback(agent.session, 'fixture feedback')
if (mode === 'FEEDBACK_ONLY') {
await runFixtureTurn(ctx, { task: 'post-feedback private suffix' })
}
}
} finally {
await ctx.fiber.dispose()
}

View File

@@ -2,6 +2,14 @@
# path, exporting to the mock OTLP collector the driver starts (url via env).
# The redact-rule entry models a deployment mounting its own scrub rule on the
# telemetry/record waterfall — the seam itself ships no rules.
- id: logger-console
name: '@cordisjs/plugin-logger-console'
config:
colors: false
levels:
default: 3
showTime: ''
- id: cli-mock-llm
name: './cli-mock-llm.ts'
@@ -18,15 +26,26 @@
- id: telemetry-otel
name: '@deepseek-ai/dsh-session-telemetry-otel'
config:
mode: !!js process.env.DSH_TELEMETRY_E2E_MODE || 'FULL'
exporter:
url: !!js process.env.DSH_TELEMETRY_E2E_URL
- id: cli-agent
name: '@deepseek-ai/dsh-cli-demo'
- id: agent-spine
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
provider: cli-mock
model: cli-mock
agents:
- id: main
provider: cli-mock
model: cli-mock
cwd: !!js process.cwd()
persona: 'Test the session-telemetry-otel plugin.'
persistenceRoot: './.sessions'
persistenceCompression: 'none'
workspaceContext: false
- id: persistence
name: '@deepseek-ai/dsh-session-persistence-jsonl'
config:
root: './.sessions'
compression: 'none'
- id: checkpoint-policy
name: '@deepseek-ai/dsh-session-checkpoint-policy'

View File

@@ -2,15 +2,15 @@
/** Test driver that sends two turns through one Headless Loader composition. */
import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
import { runOneShot } from '@deepseek-ai/dsh-cli-demo/src/cli.ts'
import { runFixtureTurn } from '@deepseek-ai/dsh-loader-smoke'
const configPath = process.argv[2]
if (configPath === undefined) throw new Error('time-context driver requires a config path')
const ctx = await boot('time-context-e2e', resolveConfigPath(configPath, undefined))
try {
await runOneShot(ctx, { task: 'first' })
await runOneShot(ctx, { task: 'second' })
await runFixtureTurn(ctx, { task: 'first' })
await runFixtureTurn(ctx, { task: 'second' })
} finally {
await ctx.fiber.dispose()
}

View File

@@ -12,12 +12,22 @@
- id: time-context
name: '@deepseek-ai/dsh-time-context'
- id: cli-agent
name: '@deepseek-ai/dsh-cli-demo'
- id: agent-spine
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
provider: time-context-mock
model: time-context-mock
agents:
- id: main
provider: time-context-mock
model: time-context-mock
cwd: !!js process.cwd()
persona: 'Test the time-context plugin.'
persistenceRoot: './.sessions'
persistenceCompression: 'none'
workspaceContext: false
- id: persistence
name: '@deepseek-ai/dsh-session-persistence-jsonl'
config:
root: './.sessions'
compression: 'none'
- id: checkpoint-policy
name: '@deepseek-ai/dsh-session-checkpoint-policy'

View File

@@ -0,0 +1,25 @@
/**
* Loader fixture that resumes the seeded workspace-context session.
* @module workspace-context-resume-agent
*/
import type { Context } from 'cordis'
import type { SessionId } from '@deepseek-ai/dsh-session'
/** Fixture plugin name. */
export const name = 'workspace-context-resume-agent'
/** Services that must exist before the fixture resumes its agent. */
export const inject = ['agents', 'agentLoop', 'sessionPersistence']
/**
* Resume the seeded session and bind its handle to this fixture's lifetime.
* @param ctx - settled agent and persistence services from the Loader tree.
* @returns after the resumed agent is published.
*/
export async function apply(ctx: Context): Promise<void> {
const handle = await ctx.agents.resume({
resumeSessionId: 'workspace-context-resume' as SessionId,
agentOptions: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },
})
ctx.effect(() => () => handle.dispose(), 'workspace-context-resume-agent.handle')
}