Basic Multi-Turn Example
import { query, type SDKUserMessage } from '@qwen-code/sdk';
async function* conversation(): AsyncIterable<SDKUserMessage> {
yield {
type: 'user',
session_id: 'my-session',
message: { role: 'user', content: 'Create a file called test.txt' },
parent_tool_use_id: null,
};
// Wait a bit before next message
await new Promise(resolve => setTimeout(resolve, 1000));
yield {
type: 'user',
session_id: 'my-session',
message: { role: 'user', content: 'Now read the file back to me' },
parent_tool_use_id: null,
};
yield {
type: 'user',
session_id: 'my-session',
message: { role: 'user', content: 'Delete the file' },
parent_tool_use_id: null,
};
}
const result = query({
prompt: conversation(),
options: {
permissionMode: 'auto-edit',
},
});
for await (const message of result) {
if (message.type === 'assistant') {
console.log('\nAssistant:', message.message.content);
}
}
Interactive CLI-Style Conversation
import { query, type SDKUserMessage } from '@qwen-code/sdk';
import * as readline from 'readline';
function getUserInput(prompt: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) => {
rl.question(prompt, (answer) => {
rl.close();
resolve(answer);
});
});
}
async function* interactiveSession(): AsyncIterable<SDKUserMessage> {
const sessionId = 'interactive-' + Date.now();
while (true) {
const input = await getUserInput('\nYou: ');
if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
break;
}
yield {
type: 'user',
session_id: sessionId,
message: { role: 'user', content: input },
parent_tool_use_id: null,
};
}
}
async function main() {
console.log('Interactive session started. Type "exit" to quit.\n');
const result = query({
prompt: interactiveSession(),
options: {
permissionMode: 'default',
canUseTool: async (toolName, input) => {
// Ask user for confirmation
const answer = await getUserInput(
`\nAllow ${toolName}? (y/n): `
);
if (answer.toLowerCase() === 'y') {
return { behavior: 'allow', updatedInput: input };
}
return { behavior: 'deny', message: 'User denied' };
},
},
});
for await (const message of result) {
if (message.type === 'assistant') {
console.log('\nAssistant:');
for (const block of message.message.content) {
if (block.type === 'text') {
console.log(block.text);
}
}
}
}
console.log('\nSession ended.');
}
main().catch(console.error);
Conditional Multi-Turn Based on Results
import { query, type SDKUserMessage, isSDKResultMessage } from '@qwen-code/sdk';
class ConversationManager {
private sessionId: string;
private messages: SDKUserMessage[] = [];
constructor() {
this.sessionId = 'session-' + Date.now();
}
async *generateMessages(): AsyncIterable<SDKUserMessage> {
// First message
yield {
type: 'user',
session_id: this.sessionId,
message: { role: 'user', content: 'List all TypeScript files' },
parent_tool_use_id: null,
};
// Wait for AI to respond before next message
await new Promise(resolve => setTimeout(resolve, 2000));
yield {
type: 'user',
session_id: this.sessionId,
message: { role: 'user', content: 'Now check if any have linting errors' },
parent_tool_use_id: null,
};
}
async run() {
const result = query({
prompt: this.generateMessages(),
options: {
permissionMode: 'default',
allowedTools: ['list_files', 'read_file', 'ShellTool(npx eslint)'],
},
});
for await (const message of result) {
if (message.type === 'assistant') {
console.log('\nAssistant:', message.message.content);
} else if (isSDKResultMessage(message)) {
console.log('\nConversation completed!');
console.log('Total turns:', message.num_turns);
}
}
}
}
const manager = new ConversationManager();
await manager.run();
Multi-Turn with State Management
import { query, type SDKUserMessage } from '@qwen-code/sdk';
interface ConversationState {
tasksCompleted: string[];
currentTask: string | null;
}
async function* taskBasedConversation(
tasks: string[]
): AsyncIterable<SDKUserMessage> {
const sessionId = 'task-session';
const state: ConversationState = {
tasksCompleted: [],
currentTask: null,
};
for (const task of tasks) {
state.currentTask = task;
yield {
type: 'user',
session_id: sessionId,
message: {
role: 'user',
content: `Task ${state.tasksCompleted.length + 1}: ${task}`,
},
parent_tool_use_id: null,
};
// Wait between tasks
await new Promise(resolve => setTimeout(resolve, 1500));
state.tasksCompleted.push(task);
console.log(`\nCompleted: ${task}`);
}
// Summary task
yield {
type: 'user',
session_id: sessionId,
message: {
role: 'user',
content: `Summarize what you did in all ${state.tasksCompleted.length} tasks`,
},
parent_tool_use_id: null,
};
}
const tasks = [
'Create a README.md file',
'Add a description to the README',
'Create a package.json file',
];
const result = query({
prompt: taskBasedConversation(tasks),
options: {
permissionMode: 'auto-edit',
},
});
for await (const message of result) {
if (message.type === 'assistant') {
console.log('\nAssistant:', message.message.content);
}
}
Multi-Turn with Dynamic Session ID
import { query, type SDKUserMessage } from '@qwen-code/sdk';
import { randomUUID } from 'crypto';
class MultiTurnSession {
private sessionId: string;
constructor() {
this.sessionId = randomUUID();
}
async *conversation(
messages: string[]
): AsyncIterable<SDKUserMessage> {
for (const content of messages) {
yield {
type: 'user',
session_id: this.sessionId,
message: { role: 'user', content },
parent_tool_use_id: null,
};
// Delay between messages
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
async run(messages: string[]) {
console.log('Session ID:', this.sessionId);
const result = query({
prompt: this.conversation(messages),
options: {
sessionId: this.sessionId,
permissionMode: 'yolo',
},
});
for await (const message of result) {
if (message.type === 'assistant') {
console.log('\nAssistant:', message.message.content);
}
}
return this.sessionId;
}
}
const session = new MultiTurnSession();
const sessionId = await session.run([
'What is the current time?',
'Create a timestamp.txt file with that time',
'Read the file back to confirm',
]);
console.log('\nYou can resume this session with ID:', sessionId);
Resuming Previous Sessions
import { query, type SDKUserMessage } from '@qwen-code/sdk';
// First session
async function* firstSession(): AsyncIterable<SDKUserMessage> {
yield {
type: 'user',
session_id: 'persistent-session',
message: { role: 'user', content: 'Create a counter.txt file with "0"' },
parent_tool_use_id: null,
};
}
const result1 = query({
prompt: firstSession(),
options: {
sessionId: 'persistent-session',
permissionMode: 'yolo',
},
});
for await (const message of result1) {
if (message.type === 'result') {
console.log('First session result:', message.result);
}
}
// Later: Resume the same session
async function* resumedSession(): AsyncIterable<SDKUserMessage> {
yield {
type: 'user',
session_id: 'persistent-session',
message: {
role: 'user',
content: 'Read counter.txt and increment it by 1',
},
parent_tool_use_id: null,
};
}
const result2 = query({
prompt: resumedSession(),
options: {
resume: 'persistent-session', // Resume previous session
permissionMode: 'yolo',
},
});
for await (const message of result2) {
if (message.type === 'result') {
console.log('Resumed session result:', message.result);
}
}
Error Handling in Multi-Turn
import { query, type SDKUserMessage, isAbortError } from '@qwen-code/sdk';
async function* conversation(): AsyncIterable<SDKUserMessage> {
const sessionId = 'error-handling';
try {
yield {
type: 'user',
session_id: sessionId,
message: { role: 'user', content: 'First task' },
parent_tool_use_id: null,
};
await new Promise(resolve => setTimeout(resolve, 1000));
yield {
type: 'user',
session_id: sessionId,
message: { role: 'user', content: 'Second task' },
parent_tool_use_id: null,
};
} catch (error) {
console.error('Error in conversation generator:', error);
}
}
try {
const result = query({
prompt: conversation(),
options: {
permissionMode: 'default',
},
});
for await (const message of result) {
if (message.type === 'result' && message.is_error) {
console.error('Query error:', message.error);
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('Conversation was aborted');
} else {
console.error('Unexpected error:', error);
}
}
See Also
- Single-Turn Example - Simple queries
- Custom Permissions - Permission handling
- Query Instance Methods - Control methods
