diff --git a/packages/context/tmux-context/tests/fixtures/tmux-context-driver.ts b/packages/context/tmux-context/tests/fixtures/tmux-context-driver.ts deleted file mode 100644 index 45a8aa147a..0000000000 --- a/packages/context/tmux-context/tests/fixtures/tmux-context-driver.ts +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node -/** Test driver that sends two turns through one Headless Loader composition. */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ - -import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot' -import { runOneShot } from '@deepseek-ai/dsh-cli-demo/src/cli.ts' - -const configPath = process.argv[2] -if (configPath === undefined) throw new Error('tmux-context driver requires a config path') - -const ctx = await boot('tmux-context-e2e', resolveConfigPath(configPath, undefined)) -try { - await runOneShot(ctx, { task: 'first' }) - await runOneShot(ctx, { task: 'second' }) -} finally { - await ctx.fiber.dispose() -} diff --git a/packages/context/tmux-context/tests/fixtures/tmux-context-mock-bash.ts b/packages/context/tmux-context/tests/fixtures/tmux-context-mock-bash.ts deleted file mode 100644 index 3e9540abff..0000000000 --- a/packages/context/tmux-context/tests/fixtures/tmux-context-mock-bash.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Context } from 'cordis' -import { BashExecutor } from '@deepseek-ai/dsh-bash' -import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from '@deepseek-ai/dsh-bash' - -/** - * Deterministic `ctx.bash` for the tmux-context Loader fixture: any command - * (the plugin's `tmux display-message`) returns a fixed tab-delimited reading, - * so the injected tmux location is stable without a real tmux server. `start()` - * throws — tmux-context must never spawn a background process. - */ -class TmuxMockBash extends BashExecutor { - override resolve(request: BashExecRequest): BashExecSpec { - return { - command: request.command, - workdir: request.workdir ?? process.cwd(), - timeoutMs: request.timeoutMs ?? 60_000, - stdoutMaxBytes: request.stdoutMaxBytes ?? 64_000, - signal: request.signal, - sandboxPolicy: request.sandboxPolicy, - } - } - - override run(_spec: BashExecSpec): Promise { - const line = ['work', '0', 'editor', '1', '%3', '1', '1', 'a1b2,80x24,0,0,4'].join('\\t') - return Promise.resolve({ - exitCode: 0, - signal: null, - timedOut: false, - aborted: false, - timeoutMs: 60_000, - stdout: { text: `${line}\n`, truncated: false }, - stderr: { text: '', truncated: false }, - }) - } - - override start(): BashProcess { - throw new Error('tmux-context must never start a background task') - } -} - -export const name = 'tmux-context-mock-bash' - -/** Register the deterministic `ctx.bash` executor for the fixture. */ -export function apply(ctx: Context): void { - ctx.plugin(TmuxMockBash) -} diff --git a/packages/context/tmux-context/tests/fixtures/tmux-context-mock-llm.ts b/packages/context/tmux-context/tests/fixtures/tmux-context-mock-llm.ts deleted file mode 100644 index 2f6c7a6408..0000000000 --- a/packages/context/tmux-context/tests/fixtures/tmux-context-mock-llm.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { Context } from 'cordis' -import { LlmAdapter, type StreamChunk } from '@deepseek-ai/dsh-llm' - -/** Deterministic one-step adapter for the tmux-context Loader fixture. */ -class TmuxContextMockAdapter extends LlmAdapter { - async * stream(): AsyncIterable { - const text = 'tmux context sampled' - yield { type: 'block-start', index: 0, blockType: 'text' } - yield { type: 'text-delta', index: 0, text } - yield { type: 'block-end', index: 0, block: { type: 'text', text } } - yield { type: 'usage', usage: { inputTokens: 1, outputTokens: 1 } } - yield { type: 'finish', reason: { kind: 'stop' } } - } -} - -export const name = 'tmux-context-mock-llm' -export const inject = ['llm'] - -/** Register the test-only `tmux-context-mock` adapter. */ -export function apply(ctx: Context): void { - ctx.llm.registerAdapter(['tmux-context-mock'], new TmuxContextMockAdapter()) -} diff --git a/packages/context/tmux-context/tests/fixtures/tmux-context.cordis.yml b/packages/context/tmux-context/tests/fixtures/tmux-context.cordis.yml deleted file mode 100644 index 419922a0e3..0000000000 --- a/packages/context/tmux-context/tests/fixtures/tmux-context.cordis.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Test-only composition: keep tmux-context opt-in while exercising its real Loader/app path. -# A deterministic mock ctx.bash returns a fixed tmux reading, so the injected location -# is stable without a real tmux server on the test host. -- id: tmux-context-mock-llm - name: './tmux-context-mock-llm.ts' - -- id: bash - name: './tmux-context-mock-bash.ts' - -- id: tmux-context - name: '@deepseek-ai/dsh-tmux-context' - -- id: cli-agent - name: '@deepseek-ai/dsh-cli-demo' - config: - provider: tmux-context-mock - model: tmux-context-mock - persona: 'Test the tmux-context plugin.' - persistenceRoot: './.sessions' - persistenceCompression: 'none' - workspaceContext: false diff --git a/packages/context/tmux-context/tests/tmux-context.e2e.ts b/packages/context/tmux-context/tests/tmux-context.e2e.ts deleted file mode 100644 index 386a326624..0000000000 --- a/packages/context/tmux-context/tests/tmux-context.e2e.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { readFile, readdir } from 'node:fs/promises' -import { join } from 'node:path' -import { fileURLToPath } from 'node:url' -import { describe, expect, it } from 'vitest' -import { type SessionEvent } from '@deepseek-ai/dsh-session' -import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke' - -const driver = fileURLToPath(new URL('./fixtures/tmux-context-driver.ts', import.meta.url)) -const configPath = fileURLToPath(new URL('./fixtures/tmux-context.cordis.yml', import.meta.url)) -const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url)) - -async function jsonlFiles(dir: string): Promise { - const entries = await readdir(dir, { withFileTypes: true }) - const paths = await Promise.all(entries.map(async (entry) => { - const path = join(dir, entry.name) - if (entry.isDirectory()) return jsonlFiles(path) - return entry.isFile() && entry.name.endsWith('.jsonl') ? [path] : [] - })) - return paths.flat() -} - -describe('tmux-context through a real headless cordis.yml', () => { - it('injects one ordered tmux-location event on the first turn and suppresses the unchanged second', async () => { - let events: SessionEvent[] = [] - const { stderr } = await runLoaderSmoke({ - label: 'tmux-context headless smoke', - tempDirPrefix: 'tmux-context-e2e-', - binScript: driver, - libBinScript: driver, - configPath, - tsconfigPath: repoTsconfig, - inspect: async (cwd) => { - const logs = await jsonlFiles(join(cwd, '.sessions')) - expect(logs).toHaveLength(1) - const lines = (await readFile(logs[0] as string, 'utf8')).trimEnd().split('\n') - events = lines.slice(1).map(line => JSON.parse(line) as SessionEvent) - }, - }) - expect(stderr).not.toContain('UNHANDLED') - expect(events.filter(event => event.type === 'turn/end')).toHaveLength(2) - - const contexts = events.filter( - (event): event is SessionEvent<'user/message'> => - event.type === 'user/message' - && event.data.source.kind === 'plugin' - && event.data.source.plugin === 'tmux-context') - // Two identical-state turns: the location injects once and is suppressed after. - expect(contexts).toHaveLength(1) - - const [reading] = contexts - if (reading === undefined) throw new Error('missing tmux-context reading') - const starts = events.filter(event => event.type === 'step/start') - expect(reading.seq).toBeLessThan(starts[0]!.seq) - expect(reading.surfaceOp).toBe('append') - - const text = reading.data.content - .filter(block => block.type === 'text') - .map(block => block.text) - .join('\n') - expect(text).toBe( - 'tmux location (turn 1):\n' - + 'session work, window 0 "editor", pane 1 %3\n' - + 'window active=1, pane active=1, layout a1b2,80x24,0,0,4', - ) - - const headers = events.filter(event => event.type === 'request/header') - expect(JSON.stringify(headers)).not.toContain('tmux location (turn') - }, LOADER_SMOKE_TEST_TIMEOUT_MS) -})