Skip to Content
EntwicklerhandbuchTypeScript SDK

Typescript SDK

@qwen-code/sdk

Ein minimales experimentelles TypeScript SDK für den programmatischen Zugriff auf Qwen Code.

Du kannst gerne einen Feature-Wunsch, ein Issue oder einen PR einreichen.

Installation

npm install @qwen-code/sdk

Voraussetzungen

  • Node.js >= 22.0.0
  • Qwen Code  >= 0.4.0 (stabil). Das SDK verwendet standardmäßig seine mitgelieferte CLI; setze pathToQwenExecutable nur, wenn du eine eigene qwen-Binärdatei oder ein CLI-Bundle ausführen möchtest.

Schnellstart

import { query } from '@qwen-code/sdk'; // Single-turn query const result = query({ prompt: 'What files are in the current directory?', options: { cwd: '/path/to/project', }, }); // Iterate over messages for await (const message of result) { if (message.type === 'assistant') { console.log('Assistant:', message.message.content); } else if (message.type === 'result') { console.log('Result:', message.result); } }

API-Referenz

query(config)

Erstellt eine neue Query-Sitzung mit Qwen Code.

Parameter

  • prompt: string | AsyncIterable<SDKUserMessage> – Der zu sendende Prompt. Verwende einen String für Single-Turn-Abfragen oder eine async iterable für Multi-Turn-Konversationen.
  • options: QueryOptions – Konfigurationsoptionen für die Query-Sitzung.

QueryOptions

OptionTypStandardBeschreibung
cwdstringprocess.cwd()Das Arbeitsverzeichnis für die Query-Sitzung. Bestimmt den Kontext, in dem Dateioperationen und Befehle ausgeführt werden.
modelstring-Das zu verwendende KI-Modell (z. B. 'qwen-max', 'qwen-plus', 'qwen-turbo'). Überschreibt die Umgebungsvariablen OPENAI_MODEL und QWEN_MODEL.
pathToQwenExecutablestringMitgelieferte CLIPfad zur ausführbaren Qwen Code-Datei. Unterstützt mehrere Formate: 'qwen' (native Binärdatei aus PATH), '/pfad/zu/qwen' (expliziter Pfad), '/pfad/zu/cli.js' (Node.js-Bundle), 'node:/pfad/zu/cli.js' (Node.js-Laufzeitumgebung erzwingen), 'bun:/pfad/zu/cli.js' (Bun-Laufzeitumgebung erzwingen). Wenn nicht angegeben, verwendet das SDK die mit dem Paket mitgelieferte CLI.
permissionMode'default' | 'plan' | 'auto-edit' | 'auto' | 'yolo''default'Berechtigungsmodus zur Steuerung der Ausführungsgenehmigung von Tools. Siehe Berechtigungsmodi für Details.
canUseToolCanUseTool-Benutzerdefinierter Berechtigungs-Handler für die Genehmigung der Tool-Ausführung. Wird aufgerufen, wenn ein Tool eine Bestätigung benötigt. Muss innerhalb von 60 Sekunden antworten, andernfalls wird die Anfrage automatisch abgelehnt. Siehe Benutzerdefinierter Berechtigungs-Handler.
envRecord<string, string>-Umgebungsvariablen, die an den Qwen Code-Prozess übergeben werden. Werden mit der aktuellen Prozessumgebung zusammengeführt.
systemPromptstring | QuerySystemPromptPreset-System-Prompt-Konfiguration für die Hauptsitzung. Verwende einen String, um den eingebauten Qwen Code-System-Prompt vollständig zu überschreiben, oder ein Preset-Objekt, um den eingebauten Prompt zu behalten und zusätzliche Anweisungen anzuhängen.
mcpServersRecord<string, McpServerConfig>-MCP-Server (Model Context Protocol), mit denen verbunden werden soll. Unterstützt externe Server (stdio/SSE/HTTP) und SDK-eingebettete Server. Externe Server werden mit Transport-Optionen wie command, args, url, httpUrl usw. konfiguriert. SDK-Server verwenden { type: 'sdk', name: string, instance: Server }.
abortControllerAbortController-Controller zum Abbrechen der Query-Sitzung. Rufe abortController.abort() auf, um die Sitzung zu beenden und Ressourcen freizugeben.
debugbooleanfalseAktiviert den Debug-Modus für ausführliche Protokollierung durch den CLI-Prozess.
maxSessionTurnsnumber-1 (unbegrenzt)Maximale Anzahl von Konversationsdurchläufen, bevor die Sitzung automatisch beendet wird. Muss eine ganze Zahl sein. Ein Durchlauf besteht aus einer Benutzernachricht und einer Assistant-Antwort.
coreToolsstring[]-Verwendet die alte coreTools-/CLI --core-tools-Allowlist-Semantik. Wenn angegeben, werden nur passende Core-Tools für die Sitzung registriert. Dies ist die einzige Allowlist-Option, die die Registrierung eingebauter Tools einschränkt; eine Whole-Tool permissions.deny-/excludeTools-Regel (und tools.disabled in settings.json) entfernt ein Tool ebenfalls aus der Registry. permissions.allow in settings.json ist reine Auto-Genehmigung und entfernt, degradiert oder versteckt niemals ein Tool (#10075). Um das Schema eines Tools aus der anfänglichen Modell-Anfrage herauszuhalten, verwende tools.eager in settings.json (erfordert Neustart, #9827) — tool_search, structured_output, Plan-Mode-Lifecycle-Tools, task_stop, mcp__* und computer_use__*-Tools sind von dieser Allowlist ausgenommen und behalten ihr normales Laden; um es vollständig zu entfernen, verwende eine Whole-Tool excludeTools-/permissions.deny-Regel – eine Regel mit einem Spezifizierer (wie 'Bash(rm *)') verweigert nur passende Aufrufe zur Laufzeit. MCP-Tools sind von der deny-basierten Entfernung ausgenommen: Verstecke sie stattdessen mit den serverbezogenen excludeTools-/tools.disabled-Filtern (deny blockiert weiterhin deren Aufrufe zur Laufzeit). Beispiel: ['read_file', 'edit', 'run_shell_command'].
excludeToolsstring[]-Entspricht permissions.deny in settings.json. Ausgeschlossene Tools geben sofort einen Berechtigungsfehler zurück. Hat höchste Priorität gegenüber allen anderen Berechtigungseinstellungen. Unterstützt Toolnamen-Alias und Mustervergleich: Toolname ('write_file'), Shell-Befehlspräfix ('Bash(rm *)') oder Pfadmuster ('Read(.env)', 'Edit(/src/**)').
allowedToolsstring[]-Entspricht permissions.allow in settings.json für die Auto-Genehmigung. Passende Tools umgehen den canUseTool-Callback und werden automatisch ausgeführt. Gilt nur, wenn das Tool eine Bestätigung erfordert. Wie permissions.allow ist dies reine Auto-Genehmigung und beeinflusst niemals, welche Tools registriert sind oder welche Schemas gesendet werden (#10075). Unterstützt denselben Mustervergleich wie excludeTools. Beispiel: ['Bash(git status)', 'Bash(npm test)'].
authType'openai' | 'anthropic' | 'qwen-oauth' | 'gemini' | 'vertex-ai'-Authentifizierungstyp für den KI-Dienst. Wenn angegeben, leitet das SDK ihn als --auth-type an die CLI weiter.
agentsSubagentConfig[]-Konfiguration für Subagenten, die während der Sitzung aufgerufen werden können. Subagenten sind spezialisierte KI-Agenten für bestimmte Aufgaben oder Bereiche.
includePartialMessagesbooleanfalseWenn true, sendet das SDK unvollständige Nachrichten während der Generierung, was Echtzeit-Streaming der KI-Antwort ermöglicht.
resumestring-Setze eine vorherige Sitzung durch Angabe ihrer Sitzungs-ID fort. Entspricht dem --resume-Flag der CLI.
sessionIdstring-Gib eine Sitzungs-ID für die neue Sitzung an. Stellt sicher, dass SDK und CLI dieselbe ID verwenden, ohne den Verlauf fortzusetzen. Entspricht dem --session-id-Flag der CLI.

[!note] Bei coreTools funktionieren auch Aliase wie Read, Edit und Bash, aber Aufrufspezifizierer wie Bash(git *) werden entfernt. coreTools schränkt die Tool-Registrierung ein, nicht die Aufrufmuster.

Timeouts

Das SDK erzwingt die folgenden Standard-Timeout-Werte:

TimeoutStandardBeschreibung
canUseTool1 MinuteMaximale Zeit für die Antwort des canUseTool-Callbacks. Bei Überschreitung wird die Tool-Anfrage automatisch abgelehnt.
mcpRequest1 MinuteMaximale Zeit für den Abschluss von SDK-MCP-Toolaufrufen.
controlRequest1 MinuteMaximale Zeit für den Abschluss von Steuerungsoperationen wie initialize(), setModel(), setPermissionMode(), getContextUsage() und interrupt().
streamClose1 MinuteMaximale Wartezeit für den Abschluss der Initialisierung vor dem Schließen von CLI stdin im Multi-Turn-Modus mit SDK-MCP-Servern.

Du kannst diese Timeouts über die Option timeout anpassen:

import { query } from '@qwen-code/sdk'; const q = query({ prompt: 'Your prompt', options: { timeout: { canUseTool: 60000, // 60 seconds for permission callback mcpRequest: 600000, // 10 minutes for MCP tool calls controlRequest: 60000, // 60 seconds for control requests streamClose: 15000, // 15 seconds for stream close wait }, }, });

Nachrichtentypen

Das SDK bietet Typwächter zur Identifizierung verschiedener Nachrichtentypen:

import { isSDKUserMessage, isSDKAssistantMessage, isSDKSystemMessage, isSDKResultMessage, isSDKPartialAssistantMessage, } from '@qwen-code/sdk'; for await (const message of result) { if (isSDKAssistantMessage(message)) { // Assistant-Nachricht verarbeiten } else if (isSDKResultMessage(message)) { // Ergebnisnachricht verarbeiten } }

Methoden der Query-Instanz

Die von query() zurückgegebene Query-Instanz stellt mehrere Methoden bereit:

const q = query({ prompt: 'Hallo', options: {} }); // Sitzungs-ID abrufen const sessionId = q.getSessionId(); // Prüfen, ob geschlossen const closed = q.isClosed(); // Aktuelle Operation unterbrechen await q.interrupt(); // Berechtigungsmodus während der Sitzung ändern await q.setPermissionMode('yolo'); // Modell während der Sitzung ändern await q.setModel('qwen-max'); // Nutzung des Kontextfensters abrufen (Token-Anzahl pro Kategorie) const usage = await q.getContextUsage(); // true übergeben, um anzuzeigen, dass Details pro Element angezeigt werden sollen const detail = await q.getContextUsage(true); // Sitzung schließen await q.close();

interrupt() bricht nur den aktiven Turn ab. Bei einer Multi-Turn-Query, die mit einem asynchronen iterierbaren Prompt erstellt wurde, bleiben die Query und ihr Input-Stream offen, sodass spätere Nachrichten aus dem Iterable normal verarbeitet werden. Verwende close() oder breche den konfigurierten AbortController ab, wenn du die gesamte Session beenden möchtest.

Vom Caller bereitgestellte Session-IDs im Daemon

DaemonClient.createOrAttachSession akzeptiert eine optionale sessionId für Caller, die eine Identität vor der Session-Erstellung persistieren müssen:

import { DaemonClient } from '@qwen-code/sdk'; const daemon = new DaemonClient({ baseUrl: 'http://127.0.0.1:4170' }); const session = await daemon.createOrAttachSession({ workspaceCwd: '/path/to/project', sessionId: '550E8400-E29B-41D4-A716-446655440000', }); console.log(session.sessionId); // 550e8400-e29b-41d4-a716-446655440000

Das SDK benötigt die session_id_override-Capability des Daemons vor dem Senden der Mutation. Der REST-Modus serialisiert sessionId direkt; ein aktiver ACP-Adapter mappt es auf session/new._meta["qwen-code/sessionId"]. Das SDK überprüft die Erfolgsantwort und wirft DaemonSessionIdProtocolError, wenn der Daemon eine andere ID zurückgibt.

Diese Option erzeugt immer eine neue Thread-Session und ist kein idempotentes Attach. Wenn das Ergebnis der Erstellung mehrdeutig ist, verwende die bekannte ID mit Load oder Resume. Das Weglassen der Option behält das bestehende Create-or-Attach-Verhalten bei.

Kommunikation mit laufenden Sessions

@qwen-code/sdk/peer ermöglicht es einem Programm, das keine Qwen Code-Session ist, sich den Sessions anzuschließen, die als derselbe Benutzer auf derselben Maschine laufen – ein Voice-Frontend, ein Relay, ein Build-Watcher. Das Programm erscheint in qwen sessions ps und in der list_agents jeder Session, die agents.crossSessionMessaging aktiviert hat – was auch ermöglicht, dass diese Sessions ihm per Namen mit send_message Nachrichten senden können. Es kann ihnen ebenfalls Nachrichten senden. Es läuft nur auf Node und benötigt nichts außer Node selbst.

import { PeerEndpoint } from '@qwen-code/sdk/peer'; const endpoint = await PeerEndpoint.start({ name: 'voice-bridge', onMessage: (message) => console.log(`${message.fromName}: ${message.content}`), }); const [session] = await endpoint.list(); if (session) { const sent = await endpoint.send({ to: session.address, content: 'What are you working on?', }); if (sent.kind === 'sent') { const receipt = await endpoint.awaitReceipt(sent.msgId, { final: true }); console.log(receipt?.status); // delivered, denied, refused, ... } } await endpoint.close();

Eine solche Nachricht wird zur Überprüfung durch den Benutzer der Session zurückgehalten. Um eine Session ohne diese Überprüfung zu steuern, erstelle ein Controller-Token mit qwen sessions controllers add --label voice-bridge, übergib es dem Endpoint und markiere die Sends, die es präsentieren sollen:

const endpoint = await PeerEndpoint.start({ name: 'voice-bridge', controllerToken: process.env['QWEN_CONTROLLER_TOKEN'], }); await endpoint.send({ to: 'my-app-3f', content: 'run the tests', controller: true, });

Wichtige Hinweise:

  • Eine Session hat nur dann eine Inbox, wenn ihre agents.crossSessionMessaging-Einstellung aktiviert ist, und diese Einstellung ist standardmäßig deaktiviert. Ohne sie erscheint die Session nicht in list(), und ihre eigenen list_agents und send_message können das Programm ebenfalls nicht sehen oder erreichen. qwen sessions ps listet das Programm unabhängig davon auf.
  • Eine Nachricht wird genau in zwei Fällen ohne Überprüfung zugestellt: Der Send präsentiert ein Controller-Token (controller: true), oder sein fromMode benennt die eigene Review-Klasse der empfangenden Session. fromMode ist eine Behauptung, die nichts authentifiziert, daher sollte ein Programm, das keine Coding-Session ist, es weglassen. Nichts im Record – nicht kind, nicht name – erzwingt die Zustellung. Die agents.crossSessionInbound-Einstellung der empfangenden Session hat Vorrang vor beiden: hold oder refuse dort gewinnt gegenüber einem Controller-Token.
  • Markiere nur die Sends, die zur Steuerung einer Session bestimmt sind. Adressen werden aus Records aufgelöst, die jedes als dein Benutzer laufende Programm schreiben kann, daher präsentiert ein Controller-Send das Token demjenigen Prozess, dessen Record auf diese Adresse antwortet. Ein Controller-Send an einen anderen Peer-Endpoint wird ungelesen verworfen, da die Inbox eines Endpoints nur sein eigenes Token akzeptiert.
  • Die Inbox des Endpoints wendet keinen der Schutzmechanismen an, die eine Qwen Code-Session auf ihre eigene anwendet: kein Rate-Limit, keine Holds und kein Duplicate-Window über die letzten 200 beantworteten Nachrichten hinaus. Jede Nachricht wird als delivered beantwortet und bei Eingang an onMessage übergeben, also wende dort deine eigenen Limits an, wenn du sie benötigst. Ohne onMessage wird jede Nachricht als refused beantwortet.
  • Rufe close() vor dem Beenden auf, einschließlich aus deinen eigenen Signal-Handlern. Ein Prozess, der ohne Schließen getötet wird, hinterlässt seinen Record, bis eine Qwen Code-Session das Verzeichnis auflistet und sieht, dass der Prozess nicht mehr existiert.
  • Nur UNIX-Domain-Sockets: Windows wird noch nicht unterstützt.

Das Record-Schema, Wire-Format und Receipt-States sind dokumentiert in Cross-Session Protocol.

Berechtigungsmodi

Das SDK unterstützt verschiedene Berechtigungsmodi zur Steuerung der Tool-Ausführung:

  • default: Schreib-Tools werden abgelehnt, sofern sie nicht über den canUseTool-Callback oder in allowedTools genehmigt werden. Schreibgeschützte Tools werden ohne Bestätigung ausgeführt.
  • plan: Blockiert alle Schreib-Tools und weist die KI an, zuerst einen Plan vorzulegen.
  • auto-edit: Bearbeitungstools (edit, write_file, notebook_edit) werden automatisch genehmigt, während andere Tools eine Bestätigung erfordern.
  • auto: Verwendet den eingebauten Klassifikator, um sichere Tool-Aufrufe automatisch zu genehmigen und riskante zu blockieren, mit Fallback auf manuelle Genehmigung nach wiederholten Policy-Blockaden oder Klassifikator-Ausfällen.
  • yolo: Alle Tools werden automatisch ohne Bestätigung ausgeführt.

Berechtigungsprioritätskette

Entscheidungspriorität (höchste zuerst): deny > ask > allow > (Standard-/Interaktivmodus)

Die erste passende Regel gewinnt.

  1. excludeTools / permissions.deny – Blockiert Tools vollständig (gibt Berechtigungsfehler zurück)
  2. permissions.ask – Erfordert immer eine Benutzerbestätigung
  3. permissionMode: 'plan' – Blockiert alle nicht schreibgeschützten Tools
  4. permissionMode: 'yolo' – Genehmigt alle Tools automatisch
  5. allowedTools / permissions.allow – Genehmigt passende Tools automatisch
  6. permissionMode: 'auto' – Klassifikator-vermittelte Genehmigung für verbleibende Tools
  7. canUseTool-Callback – Benutzerdefinierte Genehmigungslogik (wenn angegeben, wird er nicht für genehmigte Tools aufgerufen)
  8. Standardverhalten – Automatische Ablehnung im SDK-Modus (Schreib-Tools erfordern explizite Genehmigung)

Beispiele

Mehrfach-Dialog

import { query, type SDKUserMessage } from '@qwen-code/sdk'; async function* generateMessages(): AsyncIterable<SDKUserMessage> { yield { type: 'user', session_id: 'my-session', message: { role: 'user', content: 'Erstelle eine Datei hello.txt' }, parent_tool_use_id: null, }; // Auf eine Bedingung oder Benutzereingabe warten yield { type: 'user', session_id: 'my-session', message: { role: 'user', content: 'Lies jetzt die Datei zurück' }, parent_tool_use_id: null, }; } const result = query({ prompt: generateMessages(), options: { permissionMode: 'auto-edit', }, }); for await (const message of result) { console.log(message); }

Benutzerdefinierter Berechtigungs-Handler

import { query, type CanUseTool } from '@qwen-code/sdk'; const canUseTool: CanUseTool = async (toolName, input, { signal }) => { // Alle Leseoperationen erlauben if (toolName.startsWith('read_')) { return { behavior: 'allow', updatedInput: input }; } // Benutzer bei Schreiboperationen um Bestätigung bitten (in einer echten Anwendung) const userApproved = await promptUser(`Erlaube ${toolName}?`); if (userApproved) { return { behavior: 'allow', updatedInput: input }; } return { behavior: 'deny', message: 'Benutzer hat die Operation abgelehnt' }; }; const result = query({ prompt: 'Erstelle eine neue Datei', options: { canUseTool, }, });

Mit externen MCP-Servern

import { query } from '@qwen-code/sdk'; const result = query({ prompt: 'Verwende das benutzerdefinierte Tool von meinem MCP-Server', options: { mcpServers: { 'mein-server': { command: 'node', args: ['pfad/zu/mcp-server.js'], env: { PORT: '3000' }, }, }, }, });

System-Prompt überschreiben

import { query } from '@qwen-code/sdk'; const result = query({ prompt: 'Sag Hallo in einem Satz.', options: { systemPrompt: 'Du bist ein knapper Assistent. Antworte in genau einem Satz.', }, });

An den eingebauten System-Prompt anhängen

import { query } from '@qwen-code/sdk'; const result = query({ prompt: 'Überprüfe das aktuelle Verzeichnis.', options: { systemPrompt: { type: 'preset', preset: 'qwen_code', append: 'Sei knapp und konzentriere dich auf konkrete Ergebnisse.', }, }, });

Mit SDK-eingebetteten MCP-Servern

Das SDK bietet tool und createSdkMcpServer, um MCP-Server zu erstellen, die im selben Prozess wie deine SDK-Anwendung laufen. Dies ist nützlich, wenn du benutzerdefinierte Tools für die KI bereitstellen möchtest, ohne einen separaten Serverprozess auszuführen.

tool(name, description, inputSchema, handler)

Erstellt eine Tool-Definition mit Typinferenz über Zod-Schema.

ParameterTypBeschreibung
namestringTool-Name (1-64 Zeichen, beginnt mit Buchstaben, alphanumerisch und Unterstriche)
descriptionstringFür Menschen lesbare Beschreibung der Funktion des Tools
inputSchemaZodRawShapeZod-Schema-Objekt, das die Eingabeparameter des Tools definiert
handler(args, extra) => Promise<Result>Asynchrone Funktion, die das Tool ausführt und MCP-Inhaltsblöcke zurückgibt

Der Handler muss ein CallToolResult-Objekt mit folgender Struktur zurückgeben:

{ content: Array< | { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string } | { type: 'resource'; uri: string; mimeType?: string; text?: string } >; isError?: boolean; }

createSdkMcpServer(options)

Erstellt eine SDK-eingebettete MCP-Serverinstanz.

OptionTypStandardBeschreibung
namestringErforderlichEindeutiger Name für den MCP-Server
versionstring'1.0.0'Serverversion
toolsSdkMcpToolDefinition[]-Array von Tools, die mit tool() erstellt wurden

Gibt ein McpSdkServerConfigWithInstance-Objekt zurück, das direkt an die mcpServers-Option übergeben werden kann.

Beispiel

import { z } from 'zod'; import { query, tool, createSdkMcpServer } from '@qwen-code/sdk'; // Definiere ein Tool mit Zod-Schema const calculatorTool = tool( 'calculate_sum', 'Addiere zwei Zahlen', { a: z.number(), b: z.number() }, async (args) => ({ content: [{ type: 'text', text: String(args.a + args.b) }], }), ); // Erstelle den MCP-Server const server = createSdkMcpServer({ name: 'calculator', tools: [calculatorTool], }); // Verwende den Server in einer Abfrage const result = query({ prompt: 'Was ist 42 + 17?', options: { permissionMode: 'yolo', mcpServers: { calculator: server, }, }, }); for await (const message of result) { console.log(message); }

Eine Abfrage abbrechen

import { query, isAbortError } from '@qwen-code/sdk'; const abortController = new AbortController(); const result = query({ prompt: 'Lang laufende Aufgabe...', options: { abortController, }, }); // Abbruch nach 5 Sekunden setTimeout(() => abortController.abort(), 5000); try { for await (const message of result) { console.log(message); } } catch (error) { if (isAbortError(error)) { console.log('Abfrage wurde abgebrochen'); } else { throw error; } }

Fehlerbehandlung

Das SDK stellt eine AbortError-Klasse zur Behandlung abgebrochener Abfragen bereit:

import { AbortError, isAbortError } from '@qwen-code/sdk'; try { // ... Abfrageoperationen } catch (error) { if (isAbortError(error)) { // Abort behandeln } else { // Andere Fehler behandeln } }
Last updated on