From 25d0ef5c6c1a6570ae8eddb6141bc616da2397a2 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Wed, 22 Jul 2026 23:58:39 +0800 Subject: [PATCH] test(code-mode): cover terminal output types --- packages/pty/tool-pty/tests/tools.spec.ts | 86 ++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/packages/pty/tool-pty/tests/tools.spec.ts b/packages/pty/tool-pty/tests/tools.spec.ts index b054a7be90..34bc29d14c 100644 --- a/packages/pty/tool-pty/tests/tools.spec.ts +++ b/packages/pty/tool-pty/tests/tools.spec.ts @@ -5,7 +5,8 @@ import { Session, SessionId } from '@deepseek-ai/dsh-session' import AgentRegistry 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 ToolRegistry, { renderToolsSdk } from '@deepseek-ai/dsh-tools' +import type { ToolSdkSchema } from '@deepseek-ai/dsh-tools/src/ts-types.ts' import PtyService, { PtySessionId } from '@deepseek-ai/dsh-pty' import type { PtyBackend, PtyBackendSession, PtySendOperation, PtySendRequest, PtySessionStatus, PtySignal } from '@deepseek-ai/dsh-pty' import TaskService from '@deepseek-ai/dsh-tasks' @@ -104,6 +105,7 @@ async function setup(tasks: boolean) { } let callNumber = 0 +const TOOL_NAMES = ['terminal_open', 'terminal_send', 'terminal_read', 'terminal_signal', 'terminal_close', 'terminal_list'] as const const testToolSignal = new AbortController().signal function call(ctx: Context, name: string, args: unknown, agent?: Agent) { return ctx.tools.execute({ signal: testToolSignal, callId: CallId(`pty-call-${++callNumber}`), name, arguments: args, ...agent ? { agent } : {} }) @@ -120,7 +122,7 @@ function text(result: { content: { type: string; text?: string }[] }): string { describe('tool-pty foreground surface', () => { it('registers exactly six schemas and drives the full owner-scoped lifecycle', async () => { const { ctx, agent } = await setup(false) - expect(['terminal_open', 'terminal_send', 'terminal_read', 'terminal_signal', 'terminal_close', 'terminal_list'].every(name => ctx.tools.get(name) !== undefined)).toBe(true) + expect(TOOL_NAMES.every(name => ctx.tools.get(name) !== undefined)).toBe(true) const spawned = await call(ctx, 'terminal_open', { type: 'stub', name: 'main' }, agent) expect(text(spawned)).toContain('started terminal session pty-1 (main)') @@ -170,6 +172,86 @@ describe('tool-pty foreground surface', () => { expect(empty).toMatchObject({ isError: false, value: [] }) }) + it('projects every terminal DTO into the generated Code Mode output map', async () => { + const { ctx } = await setup(false) + const schemas = TOOL_NAMES.map((toolName): ToolSdkSchema => { + const definition = ctx.tools.get(toolName) + if (definition === undefined) throw new Error(`missing terminal tool ${toolName}`) + return { + name: definition.name, + description: definition.description, + parameters: definition.parameters, + output: definition.output.schema, + } + }) + const sdk = renderToolsSdk(schemas) + const outputMapStart = sdk.indexOf('interface ToolOutputMap') + const outputMapEnd = sdk.indexOf('\n\ntype ToolName', outputMapStart) + + expect(sdk.slice(outputMapStart, outputMapEnd)).toMatchInlineSnapshot(` + "interface ToolOutputMap { + terminal_close: { + sessionId: string; + outcome: "closed" | "already-closing"; + }; + terminal_list: ({ + sessionId: string; + name?: string; + type: string; + pid?: number; + status: { + kind: "running"; + } | { + kind: "exited"; + exitCode: number | null; + signal: string | null; + }; + })[]; + terminal_open: { + sessionId: string; + name?: string; + type: string; + pid?: number; + status: { + kind: "running"; + } | { + kind: "exited"; + exitCode: number | null; + signal: string | null; + }; + motd: string; + }; + terminal_read: { + text: string; + totalLines: number; + lineBegin: number; + lineEnd: number; + truncated: boolean; + }; + terminal_send: { + kind: "background"; + taskId: string; + } | { + kind: "foreground"; + viewport: string; + waitReason: "stdin_read" | "inferred_idle" | "timeout" | "session_exit"; + sessionStatus: { + kind: "running"; + } | { + kind: "exited"; + exitCode: number | null; + signal: string | null; + }; + truncated: boolean; + }; + terminal_signal: { + delivered: true; + targetPgid: number; + }; + }" + `) + }) + it('fails without an initiating agent and rejects background before writing', async () => { const { ctx, agent, stub } = await setup(false) expect((await call(ctx, 'terminal_open', { type: 'stub' })).isError).toBe(true)