refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:36:22 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 deletions

View File

@@ -3,26 +3,26 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import LlmService, { createUserMessage, CallId, HarnessError } from '@deepseek-ai/dsh-llm'
import LlmRuntime, { createUserMessage, CallId, HarnessError } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { RUN_CODE_NAME, defineTool } from '@deepseek-ai/dsh-tools'
import ToolRuntime, { RUN_CODE_NAME, defineTool } from '@deepseek-ai/dsh-tools'
import type { ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
import * as BashEnvPlugin from '@deepseek-ai/dsh-bash-env'
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
import * as BashEnvPlugin from '@deepseek-ai/dsh-shell-env'
import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
import * as ToolBash from '@deepseek-ai/dsh-tool-bash'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import { WorkerCodeRuntime } from '@deepseek-ai/dsh-code-runtime-worker'
import { WorkerThreadCodeRuntime } from '@deepseek-ai/dsh-code-runtime-worker-thread'
import LocalFileSystem from '@deepseek-ai/dsh-fs-local'
import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import * as WorkspaceContext from '@deepseek-ai/dsh-workspace-context'
import LocalTaskService from '@deepseek-ai/dsh-tasks-local'
import * as ToolTasks from '@deepseek-ai/dsh-tool-tasks'
import * as WorkspaceContext from '@deepseek-ai/dsh-agent-instructions'
import LocalJobRegistry from '@deepseek-ai/dsh-jobs-local'
import * as ToolTasks from '@deepseek-ai/dsh-tool-jobs'
import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
/**
@@ -50,34 +50,34 @@ afterEach(async () => {
async function codeModeHarness(cwd: string): Promise<Context> {
const harness = new Context()
await harness.plugin(LlmService)
await harness.plugin(LlmRuntime)
await harness.plugin(SessionStore)
await harness.plugin(SystemPrompt, { persona: PERSONA })
await harness.plugin(ToolRegistry, { mode: 'code' })
await harness.plugin(ToolRuntime, { mode: 'code' })
await harness.plugin(AgentRegistry)
await harness.plugin(AgentLoop, { agents: [] })
await harness.plugin(LlmDeepSeek)
await harness.plugin(LocalSubprocessService)
await harness.plugin(LocalSubprocessRuntime)
await harness.plugin(BashEnvPlugin)
await harness.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 })
await harness.plugin(ToolBash)
await harness.plugin(WorkerCodeRuntime, {})
await harness.plugin(WorkerThreadCodeRuntime, {})
return harness
}
async function workspaceCodeModeHarness(): Promise<Context> {
const harness = new Context()
await harness.plugin(LlmService)
await harness.plugin(LlmRuntime)
await harness.plugin(SessionStore)
await harness.plugin(SystemPrompt, { persona: PERSONA })
await harness.plugin(ToolRegistry, { mode: 'code' })
await harness.plugin(ToolRuntime, { mode: 'code' })
await harness.plugin(AgentRegistry)
await harness.plugin(LocalFileSystem, { cwd: '/' })
await harness.plugin(ToolFs)
await harness.plugin(WorkspaceContext, { maxBytes: 65536 })
await harness.plugin(AgentLoop, { agents: [] })
await harness.plugin(LlmDeepSeek, { models: [{ id: 'deepseek-v4-flash' }] })
await harness.plugin(WorkerCodeRuntime, {})
await harness.plugin(WorkerThreadCodeRuntime, {})
return harness
}
@@ -108,17 +108,17 @@ function completion(result: ToolExecutionResult): unknown {
async function typedCodeModeHarness(): Promise<Context> {
const harness = new Context()
await harness.plugin(SystemPrompt)
await harness.plugin(ToolRegistry, { mode: 'code' })
await harness.plugin(WorkerCodeRuntime, {})
await harness.plugin(ToolRuntime, { mode: 'code' })
await harness.plugin(WorkerThreadCodeRuntime, {})
return harness
}
/** Keyless real-worker harness with the task-owned bash lifecycle. */
async function backgroundCodeModeHarness(cwd: string): Promise<Context> {
const harness = await typedCodeModeHarness()
await harness.plugin(LocalTaskService)
await harness.plugin(LocalJobRegistry)
await harness.plugin(ToolTasks, {})
await harness.plugin(LocalSubprocessService)
await harness.plugin(LocalSubprocessRuntime)
await harness.plugin(BashEnvPlugin)
await harness.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 })
await harness.plugin(ToolBash)
@@ -179,30 +179,30 @@ describe('Code Mode typed values: keyless real-worker contracts', () => {
})
})
it('returns a background task id, settles the outer run, and polls that id to completion', async () => {
it('returns a background job id, settles the outer run, and polls that id to completion', async () => {
workdir = await mkdtemp(join(tmpdir(), 'dsh-code-mode-background-'))
ctx = await backgroundCodeModeHarness(workdir)
const taskId = completion(await runCode(ctx, `
const jobId = completion(await runCode(ctx, `
const started = await tools.bash({
command: "sleep 0.2; printf 'background-complete\\n'",
description: 'Run completion marker in background',
run_in_background: true,
});
return started.taskId;
return started.jobId;
`))
expect(taskId).toBe('bash-1')
expect(jobId).toBe('bash-1')
const polled = completion(await runCode(ctx, `
return await tools.task_output({ task_id: ${JSON.stringify(taskId)}, wait: true, timeout_ms: 5000 });
return await tools.job_output({ job_id: ${JSON.stringify(jobId)}, wait: true, timeout_ms: 5000 });
`))
if (typeof polled !== 'object' || polled === null || Array.isArray(polled)) throw new Error('invalid task_output completion')
if (typeof polled !== 'object' || polled === null || Array.isArray(polled)) throw new Error('invalid job_output completion')
const taskOutput = polled as Record<string, unknown>
expect(taskOutput.text).toContain('background-complete')
expect(taskOutput.task).toMatchObject({ id: taskId, kind: 'bash', status: 'completed' })
expect(taskOutput.job).toMatchObject({ id: jobId, kind: 'bash', status: 'completed' })
}, 15_000)
it('pre-abort spawns nothing; post-publication abort leaves task_kill as the cancellation owner', async () => {
it('pre-abort spawns nothing; post-publication abort leaves job_kill as the cancellation owner', async () => {
workdir = await mkdtemp(join(tmpdir(), 'dsh-code-mode-task-cancel-'))
ctx = await backgroundCodeModeHarness(workdir)
@@ -212,31 +212,31 @@ describe('Code Mode typed values: keyless real-worker contracts', () => {
return await tools.bash({ command: 'sleep 10', description: 'Must never start', run_in_background: true });
`, pre.signal)
expect(preResult.isError).toBe(true)
expect(ctx.tasks.list()).toEqual([])
expect(ctx.jobs.list()).toEqual([])
const afterPublication = new AbortController()
const running = runCode(ctx, `
const started = await tools.bash({ command: 'sleep 10', description: 'Wait for explicit task kill', run_in_background: true });
console.log(started.taskId);
console.log(started.jobId);
await new Promise(() => {});
`, afterPublication.signal)
for (let attempt = 0; attempt < 100 && ctx.tasks.list().length === 0; attempt++) {
for (let attempt = 0; attempt < 100 && ctx.jobs.list().length === 0; attempt++) {
await new Promise(resolve => setTimeout(resolve, 10))
}
const task = ctx.tasks.list()[0]
expect(task).toMatchObject({ id: 'bash-1', status: 'running' })
const job = ctx.jobs.list()[0]
expect(job).toMatchObject({ id: 'bash-1', status: 'running' })
afterPublication.abort('outer-call-cancelled')
expect((await running).isError).toBe(true)
expect(ctx.tasks.list()[0]).toMatchObject({ id: task!.id, status: 'running' })
expect(ctx.jobs.list()[0]).toMatchObject({ id: job!.id, status: 'running' })
const killed = completion(await runCode(ctx, `
return await tools.task_kill({ task_id: ${JSON.stringify(task!.id)}, reason: 'test owns cancellation' });
return await tools.job_kill({ job_id: ${JSON.stringify(job!.id)}, reason: 'test owns cancellation' });
`))
expect(killed).toMatchObject({ outcome: 'cancellation-requested', task: { id: task!.id } })
expect(killed).toMatchObject({ outcome: 'cancellation-requested', job: { id: job!.id } })
const settled = completion(await runCode(ctx, `
return await tools.task_output({ task_id: ${JSON.stringify(task!.id)}, wait: true, timeout_ms: 5000 });
return await tools.job_output({ job_id: ${JSON.stringify(job!.id)}, wait: true, timeout_ms: 5000 });
`))
expect(settled).toMatchObject({ task: { id: task!.id, status: 'killed' } })
expect(settled).toMatchObject({ job: { id: job!.id, status: 'killed' } })
}, 15_000)
it('keeps foreground bash coupled to the outer signal', async () => {
@@ -251,7 +251,7 @@ describe('Code Mode typed values: keyless real-worker contracts', () => {
const result = await pending
expect(result.isError).toBe(true)
expect(Date.now() - startedAt).toBeLessThan(5_000)
expect(ctx.tasks.list()).toEqual([])
expect(ctx.jobs.list()).toEqual([])
}, 15_000)
it('uses cordis_mount DTO ids directly for running and pending temporary Plugins, then confirms removal', async () => {
@@ -382,9 +382,9 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('Code Mode: real model writes a p
const outerResult = events.find(event => event.type === 'tool/result')
const workspaceContext = await vi.waitFor(() => {
const splice = handle.agent.session.events.findLast(event => event.type === 'agent/inbox/spliced'
&& event.data.inserted.some(message => message.source.kind === 'workspace-instructions'))
&& event.data.inserted.some(message => message.source.kind === 'agent-instructions'))
const inserted = splice?.type === 'agent/inbox/spliced'
? splice.data.inserted.find(message => message.source.kind === 'workspace-instructions')
? splice.data.inserted.find(message => message.source.kind === 'agent-instructions')
: undefined
expect(inserted).toBeDefined()
return inserted!

View File

@@ -59,14 +59,14 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compa
const events = [...agent.session.events]
// A compaction ran: the start…end bracket landed in the real log.
const starts = events.filter(e => e.type === 'compact/start')
const ends = events.filter(e => e.type === 'compact/end')
const starts = events.filter(e => e.type === 'compaction/start')
const ends = events.filter(e => e.type === 'compaction/end')
expect(starts.length).toBeGreaterThan(0)
expect(ends.length).toBe(starts.length) // every start was released
// It succeeded at least once: a `compact/summary` event describing the summary and a
// It succeeded at least once: a `compaction/summary` event describing the summary and a
// replace-op user/message (the surface mutation) both landed.
const summaries = events.filter(e => e.type === 'compact/summary')
const summaries = events.filter(e => e.type === 'compaction/summary')
expect(summaries.length).toBeGreaterThan(0)
const replaceNode = events.find((e) => {
const se = e as unknown as { type: string; surfaceOp?: unknown }

View File

@@ -20,7 +20,7 @@
workspaceContext: false
dshHome: './.dsh-home'
skills:
local:
filesystem:
agentsHome: './.agents-home'
persona: 'Keyless headless-agent smoke.'
- id: persistence

View File

@@ -6,8 +6,8 @@ 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'
import type {} from '@deepseek-ai/dsh-lsp-stdio'
import type {} from '@deepseek-ai/dsh-terminal-bash'
const configPath = process.argv[2]
if (configPath === undefined) throw new Error('usage: bin.ts <cordis.yml>')
@@ -32,7 +32,7 @@ const owner: Agent = {
whenIdle: () => Promise.resolve(),
}
const unregisterOwner = ctx.agents.register(owner)
let terminalId: Awaited<ReturnType<typeof ctx.pty.spawn>>['sessionId'] | undefined
let terminalId: Awaited<ReturnType<typeof ctx.terminals.spawn>>['sessionId'] | undefined
try {
const sandbox = await ctx.e2b.getSandbox()
const fromFs = await ctx.fs.resolve('from-fs.txt')
@@ -46,12 +46,12 @@ try {
{ 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' }))
const bashRead = await ctx.shell.run(ctx.shell.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" }))
const bashWrite = await ctx.shell.run(ctx.shell.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)}`)
}
@@ -137,13 +137,13 @@ try {
workspaceRoot: process.cwd(),
})
const terminal = await ctx.pty.spawn(owner, { type: 'shell' })
const terminal = await ctx.terminals.spawn(owner, { type: 'shell' })
terminalId = terminal.sessionId
const terminalEcho = await ctx.pty.startSend(owner, terminal.sessionId, {
const terminalEcho = await ctx.terminals.startSend(owner, terminal.sessionId, {
text: "printf 'PTY-你好\\n'",
submit: true,
}).done
const sleeping = ctx.pty.startSend(owner, terminal.sessionId, {
const sleeping = ctx.terminals.startSend(owner, terminal.sessionId, {
text: "printf 'DSH_SLEEP_%s\\n' READY; sleep 30",
submit: true,
})
@@ -161,17 +161,17 @@ try {
}
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 terminalSignal = await ctx.terminals.signal(owner, terminal.sessionId, 'SIGINT')
const interrupted = await sleeping.done
const stubborn = await ctx.pty.startSend(owner, terminal.sessionId, {
const stubborn = await ctx.terminals.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')
const terminalScrollback = ctx.terminals.read(owner, terminal.sessionId, { count: 50 })
await ctx.terminals.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'
@@ -195,7 +195,7 @@ try {
},
})}\n`)
} finally {
if (terminalId !== undefined) await ctx.pty.kill(owner, terminalId, 'fixture cleanup').catch(() => false)
if (terminalId !== undefined) await ctx.terminals.kill(owner, terminalId, 'fixture cleanup').catch(() => false)
unregisterOwner()
await ownerFiber.dispose()
await ctx.fiber.dispose()

View File

@@ -28,10 +28,10 @@
workspaceRoot: !!js process.cwd()
- id: pty
name: '@deepseek-ai/dsh-pty'
name: '@deepseek-ai/dsh-terminal'
- id: pty-local
name: '@deepseek-ai/dsh-pty-local'
- id: terminal-bash
name: '@deepseek-ai/dsh-terminal-bash'
config:
pollIntervalMs: 25
exactProbeAfterMs: 150
@@ -43,8 +43,8 @@
- id: lsp
name: '@deepseek-ai/dsh-lsp'
- id: lsp-local
name: '@deepseek-ai/dsh-lsp-local'
- id: lsp-stdio
name: '@deepseek-ai/dsh-lsp-stdio'
config:
servers:
fixture:

View File

@@ -14,7 +14,7 @@ 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')
if (configPath === undefined) throw new Error('session-telemetry-otel driver requires a config path')
const captures: unknown[] = []
const server = createServer((request, response) => {
@@ -39,7 +39,7 @@ try {
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')
if (agent === undefined) throw new Error('session-telemetry-otel driver requires one root agent')
recordFeedback(agent.session, 'fixture feedback')
if (mode === 'FEEDBACK_ONLY') {
await runFixtureTurn(ctx, { task: 'post-feedback private suffix' })

View File

@@ -1,7 +1,7 @@
# Test-only composition: session-telemetry-otel through the real Loader/app
# 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.
# session-telemetry/record waterfall — the seam itself ships no rules.
- id: logger-console
name: '@deepseek-ai/cordis-plugin-logger-console'
config:
@@ -23,7 +23,7 @@
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: telemetry-otel
- id: session-telemetry-otel
name: '@deepseek-ai/dsh-session-telemetry-otel'
config:
mode: !!js process.env.DSH_TELEMETRY_E2E_MODE || 'FULL'

View File

@@ -3,7 +3,7 @@ import type { Context } from '@deepseek-ai/cordis'
/**
* Deployment-style redaction rule for the telemetry e2e: scrubs the fixture
* credential from body strings, exactly as a real deployment would mount its
* own rules on the `telemetry/record` waterfall.
* own rules on the `session-telemetry/record` waterfall.
*/
const SECRET = /sk-e2efixture[0-9]+/g
@@ -22,7 +22,7 @@ export const name = 'telemetry-redact-rule'
/** Mount the fixture scrub rule onto the redact waterfall. */
export function apply(ctx: Context): void {
ctx.on('telemetry/record', (_record, next) => {
ctx.on('session-telemetry/record', (_record, next) => {
const record = next()
return { ...record, body: scrub(record.body) }
})

View File

@@ -1,5 +1,5 @@
/**
* Loader fixture that resumes the seeded workspace-context session.
* Loader fixture that resumes the seeded agent-instructions session.
* @module workspace-context-resume-agent
*/

View File

@@ -4,17 +4,17 @@ import type { Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
import * as BashEnvPlugin from '@deepseek-ai/dsh-bash-env'
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
import * as BashEnvPlugin from '@deepseek-ai/dsh-shell-env'
import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
import * as ToolBash from '@deepseek-ai/dsh-tool-bash'
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
import ToolResultPruneService from '@deepseek-ai/dsh-compact-tool-result-prune'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import ToolResultPruner from '@deepseek-ai/dsh-compaction-tool-result-pruner'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import * as SessionCheckpointPolicy from '@deepseek-ai/dsh-session-checkpoint-policy'
import { BasicCompactService } from '@deepseek-ai/dsh-compact-basic'
import type { BasicCompactConfig } from '@deepseek-ai/dsh-compact-basic'
import { BasicCompactionEngine } from '@deepseek-ai/dsh-compaction-basic'
import type { BasicCompactionConfig } from '@deepseek-ai/dsh-compaction-basic'
/**
* Shared harness for the headless-agent e2e suites: the full plugin stack
@@ -44,11 +44,11 @@ export interface CodingHarnessOptions {
/** Durable JSONL persistence root (the resume suite needs it; others stay file-free). */
persistenceRoot?: string
/**
* Load {@link BasicCompactService} with this config so the compaction e2e can
* Load {@link BasicCompactionEngine} with this config so the compaction e2e can
* trigger compaction at a small, controlled history size. Omitted ⇒ no
* compaction plugin (the default suites run without it).
*/
compact?: BasicCompactConfig
compact?: BasicCompactionConfig
/** Test-only context capacity advertised for `deepseek-v4-flash`. */
modelContextWindow?: number
}
@@ -62,22 +62,22 @@ export async function codingHarness(workdir: string, options: CodingHarnessOptio
await ctx.plugin(LlmDeepSeek, options.modelContextWindow === undefined ? {} : {
models: [{ id: 'deepseek-v4-flash', contextWindow: options.modelContextWindow }],
})
await ctx.plugin(LocalSubprocessService)
await ctx.plugin(LocalSubprocessRuntime)
await ctx.plugin(BashEnvPlugin)
await ctx.plugin(LocalBashExecutor, { cwd: workdir, timeoutMs: 30_000 })
await ctx.plugin(ToolBash)
await ctx.plugin(ToolTodo, { allowParallelInProgress: true })
// Compaction is opt-in: only the compaction e2e loads the reusable meter and backend.
if (options.compact !== undefined) {
await ctx.plugin(TokenMeterService)
await ctx.plugin(ToolResultPruneService)
await ctx.plugin(BasicCompactService, options.compact)
await ctx.plugin(TokenMeter)
await ctx.plugin(ToolResultPruner)
await ctx.plugin(BasicCompactionEngine, options.compact)
}
// Durable JSONL persistence is opt-in: only the resume e2e needs it, and the
// other suites stay file-free. Loaded last so a resume's deferred
// `ctx.inject(['sessionPersistence'])` resolves once this is present.
if (options.persistenceRoot !== undefined) {
await ctx.plugin(SessionPersistenceJsonl, { root: options.persistenceRoot })
await ctx.plugin(JsonlSessionPersistence, { root: options.persistenceRoot })
await ctx.plugin(SessionCheckpointPolicy)
}
return ctx

View File

@@ -354,17 +354,17 @@ describe('headless stream-json snapshots', () => {
if (actual === undefined) throw new Error('compaction snapshot did not persist its session')
const records = parseJsonl(actual.content)
const types = records.map(record => record.type)
expect(types.filter(type => type === 'compact/start')).toHaveLength(1)
expect(types.filter(type => type === 'compact/summary')).toHaveLength(1)
expect(types.filter(type => type === 'compact/end')).toHaveLength(1)
const start = types.indexOf('compact/start')
const summary = types.indexOf('compact/summary')
expect(types.filter(type => type === 'compaction/start')).toHaveLength(1)
expect(types.filter(type => type === 'compaction/summary')).toHaveLength(1)
expect(types.filter(type => type === 'compaction/end')).toHaveLength(1)
const start = types.indexOf('compaction/start')
const summary = types.indexOf('compaction/summary')
const replacement = records.findIndex((record) => {
if (record.type !== 'user/message') return false
const surfaceOp = record.surfaceOp as JsonObject | undefined
return surfaceOp?.op === 'replace'
})
const end = types.indexOf('compact/end')
const end = types.indexOf('compaction/end')
expect(start).toBeLessThan(summary)
expect(summary).toBeLessThan(replacement)
expect(replacement).toBeLessThan(end)
@@ -785,7 +785,7 @@ describe('headless stream-json snapshots', () => {
const childReplay = join(settlementScenarioDir, 'child.replay.jsonl')
const childExpected = join(settlementScenarioDir, 'child.expected.jsonl')
const streamExpected = join(settlementScenarioDir, 'stream-json.expected.jsonl')
const task = 'Start one continuable background subagent and answer from its completion notice. Do not call list_agents, send_message, task_output, or task_list.'
const task = 'Start one continuable background subagent and answer from its completion notice. Do not call list_agents, send_message, job_output, or job_list.'
let runCwd = ''
const result = await runLoaderSmoke({
label: 'continuable settlement headless stream-json snapshot',

View File

@@ -6,7 +6,7 @@ import { normalizeSessionLog, scrubRequestHeaders, type NormalizeContext } from
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
import { createUserMessage, CallId , createMessage } from '@deepseek-ai/dsh-llm'
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import { describe, expect, it } from 'vitest'
const fixtureDir = join(dirname(fileURLToPath(import.meta.url)), 'semantic-checkpoint-snapshots/tool-outcome-unknown')
@@ -23,7 +23,7 @@ const task = 'Continue safely from the interrupted operation.'
async function seedInterruptedSession(root: string, cwd: string): Promise<string> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await ctx.plugin(JsonlSessionPersistence, { root, compression: 'none' })
const meta: SessionHeader = {
version: SESSION_FORMAT_VERSION,
id: sessionId,

View File

@@ -16,7 +16,7 @@ import SessionStore, {
type SessionEvent,
type SessionHeader,
} from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import { describe, expect, it } from 'vitest'
const fixtureDir = join(dirname(fileURLToPath(import.meta.url)), 'workspace-context-resume-snapshots/offline-edit')
@@ -31,7 +31,7 @@ const sessionId = SessionId('workspace-context-resume')
async function seedSession(root: string, cwd: string, version: number, events: SessionEvent[]): Promise<string> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await ctx.plugin(JsonlSessionPersistence, { root, compression: 'none' })
const meta: SessionHeader = { version, id: sessionId, createdAt: 1, cwd }
try {
await ctx.sessionPersistence.create(meta)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
"steps": [
{
"op": "prompt",
"text": "Establish a durable compaction premise before continuing. Record every part of this historical evidence: the snapshot uses keyless replay; persistence uses JSONL; the assembled headless application loads its real Cordis composition; model-visible inputs remain logged; tool calls and results remain paired and ordered; context overflow retains the original failure while recovery is attempted; compaction opens with compact/start and closes with compact/end; a successful auxiliary summary records compact/summary provenance; the replacement surface shadows only an older balanced range; the checkpoint remains smaller than the history it replaces; the newest tool result remains verbatim; the retried request sees that checkpoint; the final response proves the same turn continued; deterministic snapshot evidence stays separate from the live-provider smoke; and no external API key is needed. Emit one alpha marker through bash, then finish the task after any required recovery with the exact words COMPACTION RECOVERED."
"text": "Establish a durable compaction premise before continuing. Record every part of this historical evidence: the snapshot uses keyless replay; persistence uses JSONL; the assembled headless application loads its real Cordis composition; model-visible inputs remain logged; tool calls and results remain paired and ordered; context overflow retains the original failure while recovery is attempted; compaction opens with compaction/start and closes with compaction/end; a successful auxiliary summary records compaction/summary provenance; the replacement surface shadows only an older balanced range; the checkpoint remains smaller than the history it replaces; the newest tool result remains verbatim; the retried request sees that checkpoint; the final response proves the same turn continued; deterministic snapshot evidence stays separate from the live-provider smoke; and no external API key is needed. Emit one alpha marker through bash, then finish the task after any required recovery with the exact words COMPACTION RECOVERED."
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"agent/inbox/spliced","seq":0,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Establish a durable compaction premise before continuing. Record every part of this historical evidence: the snapshot uses keyless replay; persistence uses JSONL; the assembled headless application loads its real Cordis composition; model-visible inputs remain logged; tool calls and results remain paired and ordered; context overflow retains the original failure while recovery is attempted; compaction opens with compact/start and closes with compact/end; a successful auxiliary summary records compact/summary provenance; the replacement surface shadows only an older balanced range; the checkpoint remains smaller than the history it replaces; the newest tool result remains verbatim; the retried request sees that checkpoint; the final response proves the same turn continued; deterministic snapshot evidence stays separate from the live-provider smoke; and no external API key is needed. Emit one alpha marker through bash, then finish the task after any required recovery with the exact words COMPACTION RECOVERED."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"agent/inbox/spliced","seq":0,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Establish a durable compaction premise before continuing. Record every part of this historical evidence: the snapshot uses keyless replay; persistence uses JSONL; the assembled headless application loads its real Cordis composition; model-visible inputs remain logged; tool calls and results remain paired and ordered; context overflow retains the original failure while recovery is attempted; compaction opens with compaction/start and closes with compaction/end; a successful auxiliary summary records compaction/summary provenance; the replacement surface shadows only an older balanced range; the checkpoint remains smaller than the history it replaces; the newest tool result remains verbatim; the retried request sees that checkpoint; the final response proves the same turn continued; deterministic snapshot evidence stays separate from the live-provider smoke; and no external API key is needed. Emit one alpha marker through bash, then finish the task after any required recovery with the exact words COMPACTION RECOVERED."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"turn/start","seq":1,"time":0,"data":{"turn":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"agent/inbox/spliced","seq":2,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/start","seq":3,"time":0,"data":{"turn":1,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":4,"time":0,"data":{"content":[{"type":"text","text":"Establish a durable compaction premise before continuing. Record every part of this historical evidence: the snapshot uses keyless replay; persistence uses JSONL; the assembled headless application loads its real Cordis composition; model-visible inputs remain logged; tool calls and results remain paired and ordered; context overflow retains the original failure while recovery is attempted; compaction opens with compact/start and closes with compact/end; a successful auxiliary summary records compact/summary provenance; the replacement surface shadows only an older balanced range; the checkpoint remains smaller than the history it replaces; the newest tool result remains verbatim; the retried request sees that checkpoint; the final response proves the same turn continued; deterministic snapshot evidence stays separate from the live-provider smoke; and no external API key is needed. Emit one alpha marker through bash, then finish the task after any required recovery with the exact words COMPACTION RECOVERED."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":4,"time":0,"data":{"content":[{"type":"text","text":"Establish a durable compaction premise before continuing. Record every part of this historical evidence: the snapshot uses keyless replay; persistence uses JSONL; the assembled headless application loads its real Cordis composition; model-visible inputs remain logged; tool calls and results remain paired and ordered; context overflow retains the original failure while recovery is attempted; compaction opens with compaction/start and closes with compaction/end; a successful auxiliary summary records compaction/summary provenance; the replacement surface shadows only an older balanced range; the checkpoint remains smaller than the history it replaces; the newest tool result remains verbatim; the retried request sees that checkpoint; the final response proves the same turn continued; deterministic snapshot evidence stays separate from the live-provider smoke; and no external API key is needed. Emit one alpha marker through bash, then finish the task after any required recovery with the exact words COMPACTION RECOVERED."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"session/title","seq":5,"time":0,"data":{"title":"Establish a durable compaction premise","messageSeqs":[4],"source":{"kind":"fallback"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"request/header","seq":6,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"request/context","seq":7,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}}}
@@ -17,10 +17,10 @@
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/end","seq":16,"time":0,"data":{"turn":1,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/start","seq":17,"time":0,"data":{"turn":1,"step":2}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":18,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"error","failure":{"message":"snapshot request exceeded the model context window","code":"CONTEXT_WINDOW_EXCEEDED"}}}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"compact/start","seq":19,"time":0,"data":{"compactionId":"{{sessionId}}","turn":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"compact/summary","seq":20,"time":0,"data":{"compactionId":"{{sessionId}}","summary":[{"type":"text","text":"The request established a durable compaction premise."}],"rawOutput":[{"type":"text","text":"The request established a durable compaction premise."}],"llmStreamCall":true,"shadowedRange":{"start":4,"end":4},"shadowedSeqs":[4],"shadowedTokenCount":264,"provider":"deepseek-official","model":"deepseek-v4-flash","maxTokens":32,"usage":{"inputTokens":20,"outputTokens":4}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"compaction/start","seq":19,"time":0,"data":{"compactionId":"{{sessionId}}","turn":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"compaction/summary","seq":20,"time":0,"data":{"compactionId":"{{sessionId}}","summary":[{"type":"text","text":"The request established a durable compaction premise."}],"rawOutput":[{"type":"text","text":"The request established a durable compaction premise."}],"llmStreamCall":true,"shadowedRange":{"start":4,"end":4},"shadowedSeqs":[4],"shadowedTokenCount":266,"provider":"deepseek-official","model":"deepseek-v4-flash","maxTokens":32,"usage":{"inputTokens":20,"outputTokens":4}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":21,"time":0,"data":{"content":[{"type":"text","text":"This is an automatically generated checkpoint condensing an earlier span of the conversation to free up context. Treat the captured context as established background and build on it without restating it. Continue the task directly from the messages that follow, without acknowledging this checkpoint.\n\n<compacted-summary>"},{"type":"text","text":"The request established a durable compaction premise."},{"type":"text","text":"</compacted-summary>"}],"source":{"kind":"plugin","plugin":"compact","compactionId":"{{sessionId}}"},"role":"user","id":"{{sessionId}}"},"sourceEventSeqs":[19,20,4],"surfaceOp":{"op":"replace","start":4,"end":4}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"compact/end","seq":22,"time":0,"data":{"compactionId":"{{sessionId}}","turn":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"compaction/end","seq":22,"time":0,"data":{"compactionId":"{{sessionId}}","turn":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"COMPACTION RECOVERED"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"COMPACTION RECOVERED"}}}}}

View File

@@ -11,7 +11,7 @@
{"type":"session/title","seq":9,"time":0,"data":{"title":"Prove the product headless profile","messageSeqs":[7],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":10,"time":0,"data":{"header":{"config":{"provider":"cli-mock","model":"cli-mock","reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":11,"time":0,"data":{"provider":"cli-mock","model":"cli-mock"}}
{"type":"session/title-llm-request","seq":12,"time":0,"data":{"titleProvider":"session-title-first-message-llm","messageSeqs":[7],"route":{"provider":"cli-mock","model":"cli-mock"},"system":"Create a concise title for an AI coding-assistant session from the supplied human messages.\nReturn only the title on one line, **in plain text of natural language**, with no quotes, prefix, explanation, Markdown, XML, or terminal control codes. No code is allowed.\nUse the language of the messages.\nAim for about 5 words in non-CJK languages or 10 CJK characters.","messages":[{"content":[{"type":"text","text":"Generate the session title from this JSON array of human messages:\n[{\"seq\":7,\"text\":\"Prove the product headless profile path with one real tool round trip.\"}]"}],"source":{"kind":"plugin","plugin":"dsh-session-title-llm"},"role":"user","id":"{{sessionId}}"}],"maxTokens":64}}
{"type":"session/title-llm-request","seq":12,"time":0,"data":{"titleProvider":"session-title-first-prompt-llm","messageSeqs":[7],"route":{"provider":"cli-mock","model":"cli-mock"},"system":"Create a concise title for an AI coding-assistant session from the supplied human messages.\nReturn only the title on one line, **in plain text of natural language**, with no quotes, prefix, explanation, Markdown, XML, or terminal control codes. No code is allowed.\nUse the language of the messages.\nAim for about 5 words in non-CJK languages or 10 CJK characters.","messages":[{"content":[{"type":"text","text":"Generate the session title from this JSON array of human messages:\n[{\"seq\":7,\"text\":\"Prove the product headless profile path with one real tool round trip.\"}]"}],"source":{"kind":"plugin","plugin":"dsh-session-title-llm"},"role":"user","id":"{{sessionId}}"}],"maxTokens":64}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"cli-smoke-call","name":"bash","argumentsDelta":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}}
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"cli-smoke-call","name":"bash","arguments":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}}}

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"agent/inbox/spliced","seq":0,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Start one continuable background subagent and answer from its completion notice. Do not call list_agents, send_message, task_output, or task_list."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"agent/inbox/spliced","seq":0,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Start one continuable background subagent and answer from its completion notice. Do not call list_agents, send_message, job_output, or job_list."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"turn/start","seq":1,"time":0,"data":{"turn":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"agent/inbox/spliced","seq":2,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/start","seq":3,"time":0,"data":{"turn":1,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":4,"time":0,"data":{"content":[{"type":"text","text":"Start one continuable background subagent and answer from its completion notice. Do not call list_agents, send_message, task_output, or task_list."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":4,"time":0,"data":{"content":[{"type":"text","text":"Start one continuable background subagent and answer from its completion notice. Do not call list_agents, send_message, job_output, or job_list."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"session/title","seq":5,"time":0,"data":{"title":"Start one continuable background subagen","messageSeqs":[4],"source":{"kind":"fallback"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"request/header","seq":6,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"request/context","seq":7,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}}

View File

@@ -1,6 +1,6 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":0,"data":{"turn":1}}
{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Start a background task."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Start a background job."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"turn/end","seq":2,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":3,"time":0,"data":{}}
{"type":"agent/inbox/spliced","seq":4,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Call list_agents once and report what it shows."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}
@@ -8,7 +8,7 @@
{"type":"agent/inbox/spliced","seq":6,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":7,"time":0,"data":{"turn":2,"step":1}}
{"type":"user/message","seq":8,"time":0,"data":{"content":[{"type":"text","text":"Call list_agents once and report what it shows."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":9,"time":0,"data":{"title":"Start a background task.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"session/title","seq":9,"time":0,"data":{"title":"Start a background job.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":10,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":11,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}

View File

@@ -12,7 +12,7 @@ import { normalizeSessionLog, scrubRequestHeaders, type NormalizeContext } from
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import { describe, expect, it } from 'vitest'
const fixtureDir = fileURLToPath(new URL('./subagent-diagnostic-snapshots/descriptorless-child', import.meta.url))
@@ -34,7 +34,7 @@ const task = 'Call list_agents once and report what it shows.'
async function seedDescriptorlessChild(root: string, cwd: string): Promise<void> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await ctx.plugin(JsonlSessionPersistence, { root, compression: 'none' })
const parentMeta: SessionHeader = {
version: SESSION_FORMAT_VERSION,
id: parentId,
@@ -44,7 +44,7 @@ async function seedDescriptorlessChild(root: string, cwd: string): Promise<void>
}
const parentEvents: SessionEvent[] = [
{ type: 'turn/start', seq: 0, time: 10, data: { turn: 1 } },
{ type: 'user/message', seq: 1, time: 11, data: createUserMessage({ content: [{ type: 'text', text: 'Start a background task.' }], source: { kind: 'user' } }), surfaceOp: 'append' },
{ type: 'user/message', seq: 1, time: 11, data: createUserMessage({ content: [{ type: 'text', text: 'Start a background job.' }], source: { kind: 'user' } }), surfaceOp: 'append' },
{ type: 'turn/end', seq: 2, time: 12, data: { turn: 1, reason: { kind: 'completed' } } },
]
const childMeta: SessionHeader = {

View File

@@ -11,7 +11,7 @@ import { normalizeSessionLog, scrubRequestHeaders, type NormalizeContext } from
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import { describe, expect, it } from 'vitest'
const fixtureDir = fileURLToPath(new URL('./subagent-inheritance-snapshots/parent-override', import.meta.url))
@@ -30,7 +30,7 @@ const task = 'Delegate the write probe to a subagent.'
async function seedReadOnlyParent(root: string, cwd: string): Promise<void> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await ctx.plugin(JsonlSessionPersistence, { root, compression: 'none' })
const meta: SessionHeader = {
version: SESSION_FORMAT_VERSION,
id: sessionId,

View File

@@ -1,7 +1,7 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":0,"data":{"turn":1}}
{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Remember the workspace instruction."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":2,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nOld workspace instruction.\n</system-reminder>"}],"source":{"kind":"workspace-instructions","form":"instructions","baseline":true,"baselineIdentity":"{\"projectRoot\":\"\",\"projectRootMarkers\":[\".git\"],\"maxBytes\":65536,\"maxSourceBytes\":1048576,\"instructionFileCandidates\":[\"AGENTS.md\",\"CLAUDE.md\"],\"localInstructionFileCandidates\":[\"AGENTS.local.md\",\"CLAUDE.local.md\"]}","changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"ba65bdb41810f4d0129129dcbd6cadcd643c069d"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":2,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nOld workspace instruction.\n</system-reminder>"}],"source":{"kind":"agent-instructions","form":"instructions","baseline":true,"baselineIdentity":"{\"projectRoot\":\"\",\"projectRootMarkers\":[\".git\"],\"maxBytes\":65536,\"maxSourceBytes\":1048576,\"instructionFileCandidates\":[\"AGENTS.md\",\"CLAUDE.md\"],\"localInstructionFileCandidates\":[\"AGENTS.local.md\",\"CLAUDE.local.md\"]}","changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"ba65bdb41810f4d0129129dcbd6cadcd643c069d"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"turn/end","seq":3,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":4,"time":0,"data":{}}
{"type":"agent/inbox/spliced","seq":5,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Acknowledge the current workspace instruction."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}
@@ -9,7 +9,7 @@
{"type":"agent/inbox/spliced","seq":7,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":8,"time":0,"data":{"turn":2,"step":1}}
{"type":"user/message","seq":9,"time":0,"data":{"content":[{"type":"text","text":"Acknowledge the current workspace instruction."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":10,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nUpdated instructions from: AGENTS.md\n\nThis file changed after it was loaded. Use the following content instead of the previously loaded instructions from this file.\n\nNew workspace instruction after offline edit.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","form":"instructions","changes":[{"action":"replace","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"d8375b516f158718bd3463bc8eb7ed42c011b29f"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":10,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nUpdated instructions from: AGENTS.md\n\nThis file changed after it was loaded. Use the following content instead of the previously loaded instructions from this file.\n\nNew workspace instruction after offline edit.\n\n</system-reminder>"}],"source":{"kind":"agent-instructions","form":"instructions","changes":[{"action":"replace","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"d8375b516f158718bd3463bc8eb7ed42c011b29f"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":11,"time":0,"data":{"title":"Remember the workspace instruction.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":12,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}"},"reason":"initial"}}
{"type":"request/context","seq":13,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}

View File

@@ -1,7 +1,7 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":0,"data":{"turn":1}}
{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Remember the workspace instruction."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":2,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: CLAUDE.md\n\nOld CLAUDE rule.\n\nInstructions from: AGENTS.md\n\nOld AGENTS rule.\n</system-reminder>"}],"source":{"kind":"workspace-instructions","form":"instructions","baseline":true,"baselineIdentity":"{\"projectRoot\":\"\",\"projectRootMarkers\":[\".git\"],\"maxBytes\":65536,\"maxSourceBytes\":1048576,\"instructionFileCandidates\":[\"CLAUDE.md\",\"AGENTS.md\"],\"localInstructionFileCandidates\":[\"AGENTS.local.md\",\"CLAUDE.local.md\"]}","changes":[{"action":"set","scope":".\u0000CLAUDE.md","path":"CLAUDE.md","digest":"b525eb8a6d3660b732dad4b0aff1b7c63ab32890"},{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"3113bd093ae91976207dcef7390bdc0b2bfcfa10"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":2,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: CLAUDE.md\n\nOld CLAUDE rule.\n\nInstructions from: AGENTS.md\n\nOld AGENTS rule.\n</system-reminder>"}],"source":{"kind":"agent-instructions","form":"instructions","baseline":true,"baselineIdentity":"{\"projectRoot\":\"\",\"projectRootMarkers\":[\".git\"],\"maxBytes\":65536,\"maxSourceBytes\":1048576,\"instructionFileCandidates\":[\"CLAUDE.md\",\"AGENTS.md\"],\"localInstructionFileCandidates\":[\"AGENTS.local.md\",\"CLAUDE.local.md\"]}","changes":[{"action":"set","scope":".\u0000CLAUDE.md","path":"CLAUDE.md","digest":"b525eb8a6d3660b732dad4b0aff1b7c63ab32890"},{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"3113bd093ae91976207dcef7390bdc0b2bfcfa10"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"turn/end","seq":3,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":4,"time":0,"data":{}}
{"type":"agent/inbox/spliced","seq":5,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Acknowledge the current workspace instruction."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}
@@ -9,7 +9,7 @@
{"type":"agent/inbox/spliced","seq":7,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":8,"time":0,"data":{"turn":2,"step":1}}
{"type":"user/message","seq":9,"time":0,"data":{"content":[{"type":"text","text":"Acknowledge the current workspace instruction."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":10,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nThis complete workspace instruction baseline replaces all earlier workspace instruction baselines. The following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nCurrent AGENTS rule.\n\n\nInstructions from: CLAUDE.md\n\nCurrent CLAUDE rule.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","form":"instructions","baseline":true,"baselineIdentity":"{\"projectRoot\":\"\",\"projectRootMarkers\":[\".git\"],\"maxBytes\":65536,\"maxSourceBytes\":1048576,\"instructionFileCandidates\":[\"AGENTS.md\",\"CLAUDE.md\"],\"localInstructionFileCandidates\":[\"AGENTS.local.md\",\"CLAUDE.local.md\"]}","changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"7f53d2327837129750aef117f9754a001c46cf68"},{"action":"set","scope":".\u0000CLAUDE.md","path":"CLAUDE.md","digest":"5b1e9e3fd759eee6b43ceff899e47fb10c64701a"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":10,"time":0,"data":{"content":[{"type":"text","text":"<system-reminder>\nThis complete workspace instruction baseline replaces all earlier workspace instruction baselines. The following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nCurrent AGENTS rule.\n\n\nInstructions from: CLAUDE.md\n\nCurrent CLAUDE rule.\n\n</system-reminder>"}],"source":{"kind":"agent-instructions","form":"instructions","baseline":true,"baselineIdentity":"{\"projectRoot\":\"\",\"projectRootMarkers\":[\".git\"],\"maxBytes\":65536,\"maxSourceBytes\":1048576,\"instructionFileCandidates\":[\"AGENTS.md\",\"CLAUDE.md\"],\"localInstructionFileCandidates\":[\"AGENTS.local.md\",\"CLAUDE.local.md\"]}","changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"7f53d2327837129750aef117f9754a001c46cf68"},{"action":"set","scope":".\u0000CLAUDE.md","path":"CLAUDE.md","digest":"5b1e9e3fd759eee6b43ceff899e47fb10c64701a"}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":11,"time":0,"data":{"title":"Remember the workspace instruction.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":12,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}"},"reason":"initial"}}
{"type":"request/context","seq":13,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}

View File

@@ -17,9 +17,9 @@ import SessionStore, {
type SessionEvent,
type SessionHeader,
} from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import { renderWorkspaceContext } from '@deepseek-ai/dsh-workspace-context'
import { resolveConfig, workspaceBaselineIdentity } from '@deepseek-ai/dsh-workspace-context/src/config.ts'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import { renderWorkspaceContext } from '@deepseek-ai/dsh-agent-instructions'
import { resolveConfig, workspaceBaselineIdentity } from '@deepseek-ai/dsh-agent-instructions/src/config.ts'
import { describe, expect, it } from 'vitest'
const fixtureDir = join(dirname(fileURLToPath(import.meta.url)), 'workspace-context-resume-snapshots/offline-edit')
@@ -47,7 +47,7 @@ async function seedVisibleBaseline(
): Promise<string> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
await ctx.plugin(JsonlSessionPersistence, { root, compression: 'none' })
const meta: SessionHeader = {
version: SESSION_FORMAT_VERSION,
id: sessionId,
@@ -84,7 +84,7 @@ async function seedVisibleBaseline(
data: createUserMessage({
content: [{ type: 'text', text: baseline.text }],
source: {
kind: 'workspace-instructions',
kind: 'agent-instructions',
form: 'instructions',
baseline: true,
baselineIdentity: workspaceBaselineIdentity(config, cwd, cwd),
@@ -111,12 +111,12 @@ async function seedVisibleBaseline(
}
}
describe('workspace-context resume snapshot', () => {
describe('agent-instructions resume snapshot', () => {
it('appends an offline replacement without duplicating the visible baseline', async () => {
let cwd = ''
let sessionPath = ''
const result = await runLoaderSmoke({
label: 'workspace-context resume headless stream-json snapshot',
label: 'agent-instructions resume headless stream-json snapshot',
tempDirPrefix: 'dsh-workspace-context-resume-',
binScript,
libBinScript: binScript,
@@ -147,7 +147,7 @@ describe('workspace-context resume snapshot', () => {
}
})
const workspaceEvents = records.filter(record => record.type === 'user/message'
&& record.data?.source?.kind === 'workspace-instructions')
&& record.data?.source?.kind === 'agent-instructions')
expect(workspaceEvents.filter(record => record.data?.source?.baseline === true)).toHaveLength(1)
expect(workspaceEvents.filter(record => record.data?.source?.baseline !== true)).toHaveLength(1)
expect(workspaceEvents.at(-1)?.data?.source?.changes).toMatchObject([{
@@ -173,7 +173,7 @@ describe('workspace-context resume snapshot', () => {
let cwd = ''
let sessionPath = ''
const result = await runLoaderSmoke({
label: 'workspace-context precedence-change resume snapshot',
label: 'agent-instructions precedence-change resume snapshot',
tempDirPrefix: 'dsh-workspace-context-precedence-',
binScript,
libBinScript: binScript,
@@ -214,7 +214,7 @@ describe('workspace-context resume snapshot', () => {
}
})
const baselines = records.filter(record => record.type === 'user/message'
&& record.data?.source?.kind === 'workspace-instructions'
&& record.data?.source?.kind === 'agent-instructions'
&& record.data.source.baseline === true)
expect(baselines).toHaveLength(2)
const replacement = JSON.stringify(baselines.at(-1)?.data?.content)