| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- import type { Context } from 'cordis'
- import type {
- GenerateOptions,
- LlmModelInfo,
- LlmResolvedModelInfo,
- StreamChunk,
- } from '@deepseek-ai/dsh-llm'
- import { CallId, LlmAdapter, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
- const CONTROL_PROBE = '\u001b]2;MODEL_CONTROLLED\u0007\u001b[999CMODEL_CURSOR\u009b31mMODEL_C1'
- const INITIAL_TEXT = `I need one decision before I continue. ${CONTROL_PROBE}`
- const FINAL_TEXT = 'Decision received. Scripted TUI run complete.'
- const DEFAULT_MODE_PROBE = 'Confirm the scripted run left plan mode.'
- const DEFAULT_MODE_TEXT = 'Default mode confirmed.'
- // The `skill` scenario types `/skill:scripted-skill`; the manual-invocation front
- // door delivers the loaded skill as a user turn wrapped in `<skill name="…">`. The
- // body marker below lives in the fixture skill, so echoing it back proves the whole
- // block (name attribute plus body) reached the model, not just the command text.
- const SKILL_BLOCK_OPEN = '<skill name="scripted-skill">'
- const SKILL_BODY_MARKER = 'SCRIPTED SKILL BODY MARKER'
- const SKILL_RECEIVED_TEXT = 'Scripted skill body received.'
- const TITLE_TEXT = 'scripted session title'
- // The failing-bash scenario proves the terminal card reports a non-zero exit
- // exactly once: the model-facing result carries the `[exit code: N]` marker, and
- // the card turns it into its own `[exit N]` pill instead of showing both.
- const BASH_FAILURE_PROBE = 'Run the failing scripted command.'
- const BASH_FAILURE_COMMAND = 'printf "SCRIPTED_BASH_FAILED\\n"; exit 3'
- const BASH_FAILURE_TEXT = 'Scripted bash failure observed.'
- const BASH_FAILURE_CALL_ID = CallId('call-bash-failure')
- function textChunks(text: string): StreamChunk[] {
- return [
- { type: 'block-start', index: 0, blockType: 'text' },
- ...Array.from(text, (char): StreamChunk => ({ type: 'text-delta', index: 0, text: char })),
- { type: 'block-end', index: 0, block: { type: 'text', text } },
- { type: 'usage', usage: { inputTokens: 20, outputTokens: text.length } },
- { type: 'finish', reason: { kind: 'stop' } },
- ]
- }
- /** Keyless adapter for the real-PTY TUI tests: the two-step conversation and the `/skill:` round-trip. */
- class ScriptedTuiAdapter extends LlmAdapter {
- override listModels(provider: string): Promise<readonly LlmModelInfo[]> {
- return Promise.resolve([
- { provider, id: 'tui-scripted-model', name: 'Scripted Base' },
- { provider, id: 'tui-scripted-model-pro', name: 'Scripted Pro' },
- ])
- }
- override resolveModel(
- provider: string,
- model: string,
- ): Promise<LlmResolvedModelInfo> {
- return Promise.resolve({
- provider,
- id: model,
- name: model === 'tui-scripted-model-pro' ? 'Scripted Pro' : 'Scripted Base',
- context: { contextWindow: 128_000 },
- ...model !== 'tui-scripted-model-pro'
- ? {}
- : {
- reasoning: {
- efforts: [
- { id: ReasoningEffortId('off'), name: 'Off' },
- { id: ReasoningEffortId('high'), name: 'High' },
- { id: ReasoningEffortId('max'), name: 'Max' },
- ],
- defaultEffort: ReasoningEffortId('high'),
- },
- },
- })
- }
- override async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
- // The session-title provider's auxiliary request carries no tool schemas,
- // unlike every agent turn; answer it with a fixed title so the PTY test can
- // assert the logged title reaches the terminal window title.
- if ((options.tools?.length ?? 0) === 0) {
- for (const chunk of textChunks(TITLE_TEXT)) yield chunk
- return
- }
- if (
- options.model !== 'tui-scripted-model-pro'
- || !options.system?.includes('tui-scripted-model-pro')
- || options.reasoningEffort !== ReasoningEffortId('max')
- ) {
- throw new Error('the scripted TUI request did not apply the selected model and reasoning effort')
- }
- const lastMessage = options.messages.at(-1)
- // The loop appends plugin-sourced context (the plan-mode notice, the
- // tool-skill catalog) AFTER the admitted prompt, so the scripted trigger
- // may sit one or more user messages back: scan the whole trailing run of
- // user-role messages since the last assistant message.
- const trailingUserTexts: string[] = []
- for (let index = options.messages.length - 1; index >= 0; index--) {
- const message = options.messages[index]
- if (message?.role !== 'user') break
- for (const block of message.content) {
- if (block.type === 'text') trailingUserTexts.push(block.text)
- }
- }
- const lastText = trailingUserTexts.join('\n')
- if (lastText.includes(DEFAULT_MODE_PROBE)) {
- if (options.system?.includes('Stay in plan mode for this scripted TUI test.')) {
- throw new Error('the scripted TUI request retained plan guidance after /plan off')
- }
- for (const chunk of textChunks(DEFAULT_MODE_TEXT)) yield chunk
- return
- }
- if (lastText.includes(SKILL_BLOCK_OPEN)) {
- const ack = lastText.includes(SKILL_BODY_MARKER)
- ? SKILL_RECEIVED_TEXT
- : 'Scripted skill block arrived without its body.'
- for (const chunk of textChunks(ack)) yield chunk
- return
- }
- const blocks = lastMessage?.content ?? []
- if (blocks.some(block => block.type === 'tool-result')) {
- const answered = blocks.some(block => block.type === 'tool-result' && block.toolCallId === BASH_FAILURE_CALL_ID)
- for (const chunk of textChunks(answered ? BASH_FAILURE_TEXT : FINAL_TEXT)) yield chunk
- return
- }
- if (lastText.includes(BASH_FAILURE_PROBE)) {
- const bashArgs = JSON.stringify({ command: BASH_FAILURE_COMMAND, description: 'Run the failing scripted command' })
- yield { type: 'block-start', index: 0, blockType: 'tool-call' }
- yield { type: 'tool-call-delta', index: 0, id: BASH_FAILURE_CALL_ID, name: 'bash', argumentsDelta: bashArgs }
- yield {
- type: 'block-end',
- index: 0,
- block: { type: 'tool-call', id: BASH_FAILURE_CALL_ID, name: 'bash', arguments: bashArgs },
- }
- yield { type: 'usage', usage: { inputTokens: 20, outputTokens: 10 } }
- yield { type: 'finish', reason: { kind: 'tool-calls' } }
- return
- }
- const args = JSON.stringify({
- questions: [{
- id: 'mode',
- header: 'Execution mode',
- question: 'How should the scripted run proceed?',
- options: [
- { label: 'Safe', description: 'Use the guarded path.' },
- { label: 'Fast', description: 'Use the shorter path.' },
- ],
- }],
- })
- const callId = CallId('call-ask-mode')
- yield { type: 'block-start', index: 0, blockType: 'text' }
- for (const char of INITIAL_TEXT) yield { type: 'text-delta', index: 0, text: char }
- yield { type: 'block-end', index: 0, block: { type: 'text', text: INITIAL_TEXT } }
- yield { type: 'block-start', index: 1, blockType: 'tool-call' }
- yield { type: 'tool-call-delta', index: 1, id: callId, name: 'ask_user_question', argumentsDelta: args }
- yield {
- type: 'block-end',
- index: 1,
- block: { type: 'tool-call', id: callId, name: 'ask_user_question', arguments: args },
- }
- yield { type: 'usage', usage: { inputTokens: 20, outputTokens: 10 } }
- yield { type: 'finish', reason: { kind: 'tool-calls' } }
- }
- }
- export const name = 'tui-scripted-llm'
- export const inject = ['llm']
- /** Register the network-free adapter used by the PTY fixture. */
- export function apply(ctx: Context): void {
- ctx.llm.registerAdapter(['tui-scripted'], new ScriptedTuiAdapter())
- }
|