| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import { mkdtemp, rm, writeFile } from 'node:fs/promises'
- import { tmpdir } from 'node:os'
- import { join } from 'node:path'
- import { pathToFileURL } from 'node:url'
- import { afterEach, describe, expect, it } from 'vitest'
- import { Context } from 'cordis'
- import Loader from '@cordisjs/plugin-loader'
- import Include from '@cordisjs/plugin-include'
- import { CallId } from '@deepseek-ai/dsh-llm'
- import { Session, SessionId } from '@deepseek-ai/dsh-session'
- import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent'
- import type { Agent } from '@deepseek-ai/dsh-agent'
- import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
- import ToolRegistry from '@deepseek-ai/dsh-tools'
- import PtyService from '@deepseek-ai/dsh-pty'
- import SandboxProvider from '@deepseek-ai/dsh-sandbox'
- import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
- import SandboxPolicyService from '@deepseek-ai/dsh-sandbox-policy'
- import * as PtyLocal from '@deepseek-ai/dsh-pty-local'
- import * as ToolPty from '@deepseek-ai/dsh-tool-pty'
- let root: string | undefined
- let context: Context | undefined
- afterEach(async () => {
- await context?.fiber.dispose()
- context = undefined
- if (root !== undefined) await rm(root, { recursive: true, force: true })
- root = undefined
- })
- class PassthroughSandbox extends SandboxProvider {
- confine(argv: readonly string[], _policy: SandboxPolicy): ConfinedArgv {
- return { argv: [...argv], enforcement: 'full', denialSignatures: [], runnerFailureRules: [] }
- }
- }
- function agent(ctx: Context): Agent {
- const scope = ctx.plugin(() => {})
- const id = SessionId('pty-loader-agent')
- const session = Session.create(id)
- const value: Agent = {
- id, options: {}, session, inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }),
- status: 'idle',
- ctx: scope.ctx,
- send: () => {},
- followup: () => {}, steer: () => {}, inject: () => {}, cancel() {},
- runMaintenance: task => task(new AbortController().signal),
- whenIdle: () => Promise.resolve(),
- }
- ctx.agents.register(value)
- return value
- }
- function resultText(result: { content: { type: string; text?: string }[] }): string {
- return result.content.filter(block => block.type === 'text').map(block => block.text).join('')
- }
- const suite = process.platform === 'linux' || process.platform === 'darwin' ? describe : describe.skip
- suite('terminal real Loader composition through cordis.yml', () => {
- it('boots cordis.yml and preserves shell state across real tool calls', async () => {
- root = await mkdtemp(join(tmpdir(), 'dsh-pty-loader-'))
- const configPath = join(root, 'cordis.yml')
- await writeFile(configPath, [
- "- name: '@deepseek-ai/dsh-agent'",
- "- name: '@deepseek-ai/dsh-system-prompt'",
- "- name: '@deepseek-ai/dsh-tools'",
- "- name: '@deepseek-ai/dsh-pty'",
- "- name: '@deepseek-ai/dsh-test-sandbox'",
- "- name: '@deepseek-ai/dsh-sandbox-policy'",
- ' config:',
- ' mode: danger-full-access',
- ` workspaceRoot: ${JSON.stringify(root)}`,
- "- name: '@deepseek-ai/dsh-pty-local'",
- ' config:',
- ' pollIntervalMs: 10',
- ' exactProbeAfterMs: 20',
- ' idleSilenceMs: 250',
- ' handoffGraceMs: 250',
- ' timeoutMs: 2000',
- ' disposeGraceMs: 500',
- "- name: '@deepseek-ai/dsh-tool-pty'",
- '',
- ].join('\n'))
- context = new Context()
- context.baseUrl = pathToFileURL(root).href + '/'
- await context.plugin(Loader)
- context.loader.builtins.include = Include
- const modules = new Map<string, unknown>([
- ['@deepseek-ai/dsh-agent', AgentRegistry],
- ['@deepseek-ai/dsh-system-prompt', SystemPrompt],
- ['@deepseek-ai/dsh-tools', ToolRegistry],
- ['@deepseek-ai/dsh-pty', PtyService],
- ['@deepseek-ai/dsh-test-sandbox', PassthroughSandbox],
- ['@deepseek-ai/dsh-sandbox-policy', SandboxPolicyService],
- ['@deepseek-ai/dsh-pty-local', PtyLocal],
- ['@deepseek-ai/dsh-tool-pty', ToolPty],
- ])
- context.loader.internal = {
- version: 'v2',
- async import(specifier: string) {
- if (!modules.has(specifier)) throw new Error(`unexpected Loader import: ${specifier}`)
- return modules.get(specifier)
- },
- } as unknown as NonNullable<typeof context.loader.internal>
- await context.loader.create({ name: 'cordis:include', config: { path: pathToFileURL(configPath).href } })
- await context.loader.await()
- const owner = agent(context)
- const signal = new AbortController().signal
- const spawn = await context.tools.execute({
- signal, callId: CallId('spawn'), name: 'terminal_open', arguments: { type: 'shell', name: 'main', cwd: root }, agent: owner,
- })
- expect(resultText(spawn)).toContain('started terminal session pty-1 (main)')
- await context.tools.execute({
- signal, callId: CallId('state'), name: 'terminal_send', arguments: { sessionId: 'pty-1', text: 'export KEEP=loader; cd /' }, agent: owner,
- })
- const read = await context.tools.execute({
- signal, callId: CallId('read'), name: 'terminal_send', arguments: { sessionId: 'pty-1', text: 'printf "cwd=%s keep=%s\\n" "$PWD" "$KEEP"' }, agent: owner,
- })
- expect(resultText(read)).toContain('cwd=/ keep=loader')
- expect(context.pty.list(owner)).toHaveLength(1)
- }, 15_000)
- })
|