diff --git a/packages/sdk-java/pom.xml b/packages/sdk-java/pom.xml
index 1d1c7c25a..45c9ea895 100644
--- a/packages/sdk-java/pom.xml
+++ b/packages/sdk-java/pom.xml
@@ -27,6 +27,7 @@
3.6.0
5.14.1
1.3.16
+ 2.0.60
@@ -51,6 +52,11 @@
commons-lang3
3.20.0
+
+ com.alibaba.fastjson2
+ fastjson2
+ ${fastjson2.version}
+
org.junit.jupiter
junit-jupiter
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/Options.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/Options.java
new file mode 100644
index 000000000..82b0a4652
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/Options.java
@@ -0,0 +1,6 @@
+package com.alibaba.qwen.code.cli;
+
+import com.alibaba.qwen.code.cli.transport.TransportOptions;
+
+public class Options extends TransportOptions {
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/QwenCli.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/QwenCli.java
new file mode 100644
index 000000000..065ff9e73
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/QwenCli.java
@@ -0,0 +1,54 @@
+package com.alibaba.qwen.code.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.qwen.code.cli.protocol.message.Message;
+import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
+import com.alibaba.qwen.code.cli.session.Session;
+import com.alibaba.qwen.code.cli.session.event.SessionEventSimpleConsumers;
+import com.alibaba.qwen.code.cli.transport.Transport;
+import com.alibaba.qwen.code.cli.transport.process.ProcessTransport;
+
+public class QwenCli {
+ public static List query(String prompt) {
+ Transport transport;
+ try {
+ transport = new ProcessTransport();
+ } catch (Exception e) {
+ throw new RuntimeException("initialized ProcessTransport error!", e);
+ }
+
+ Session session;
+ try {
+ session = new Session(transport);
+ } catch (Exception e) {
+ throw new RuntimeException("initialized Session error!", e);
+ }
+
+ final List response = new ArrayList<>();
+ try {
+ session.sendPrompt(prompt, new SessionEventSimpleConsumers() {
+ @Override
+ public void onSystemMessage(SDKSystemMessage systemMessage) {
+ response.add(systemMessage);
+ }
+
+ @Override
+ public void onAssistantMessage(SDKAssistantMessage assistantMessage) {
+ response.add(assistantMessage);
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException("sendPrompt error!", e);
+ }
+
+ try {
+ session.close();
+ } catch (Exception e) {
+ throw new RuntimeException("close Session error!", e);
+ }
+ return response;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/CLIPermissionDenial.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/CLIPermissionDenial.java
new file mode 100644
index 000000000..4abd68bc3
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/CLIPermissionDenial.java
@@ -0,0 +1,38 @@
+package com.alibaba.qwen.code.cli.protocol.data;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+
+public class CLIPermissionDenial {
+ @JSONField(name = "tool_name")
+ private String toolName;
+
+ @JSONField(name = "tool_use_id")
+ private String toolUseId;
+
+ @JSONField(name = "tool_input")
+ private Object toolInput;
+
+ public String getToolName() {
+ return toolName;
+ }
+
+ public void setToolName(String toolName) {
+ this.toolName = toolName;
+ }
+
+ public String getToolUseId() {
+ return toolUseId;
+ }
+
+ public void setToolUseId(String toolUseId) {
+ this.toolUseId = toolUseId;
+ }
+
+ public Object getToolInput() {
+ return toolInput;
+ }
+
+ public void setToolInput(Object toolInput) {
+ this.toolInput = toolInput;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/Capabilities.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/Capabilities.java
new file mode 100644
index 000000000..13200b654
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/Capabilities.java
@@ -0,0 +1,60 @@
+package com.alibaba.qwen.code.cli.protocol.data;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+
+public class Capabilities {
+ @JSONField(name = "can_handle_can_use_tool")
+ boolean canHandleCanUseTool;
+
+ @JSONField(name = "can_handle_hook_callback")
+ boolean canHandleHookCallback;
+
+ @JSONField(name = "can_set_permission_mode")
+ boolean canSetPermissionMode;
+
+ @JSONField(name = "can_set_model")
+ boolean canSetModel;
+
+ @JSONField(name = "can_handle_mcp_message")
+ boolean canHandleMcpMessage;
+
+ public boolean isCanHandleCanUseTool() {
+ return canHandleCanUseTool;
+ }
+
+ public void setCanHandleCanUseTool(boolean canHandleCanUseTool) {
+ this.canHandleCanUseTool = canHandleCanUseTool;
+ }
+
+ public boolean isCanHandleHookCallback() {
+ return canHandleHookCallback;
+ }
+
+ public void setCanHandleHookCallback(boolean canHandleHookCallback) {
+ this.canHandleHookCallback = canHandleHookCallback;
+ }
+
+ public boolean isCanSetPermissionMode() {
+ return canSetPermissionMode;
+ }
+
+ public void setCanSetPermissionMode(boolean canSetPermissionMode) {
+ this.canSetPermissionMode = canSetPermissionMode;
+ }
+
+ public boolean isCanSetModel() {
+ return canSetModel;
+ }
+
+ public void setCanSetModel(boolean canSetModel) {
+ this.canSetModel = canSetModel;
+ }
+
+ public boolean isCanHandleMcpMessage() {
+ return canHandleMcpMessage;
+ }
+
+ public void setCanHandleMcpMessage(boolean canHandleMcpMessage) {
+ this.canHandleMcpMessage = canHandleMcpMessage;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/ExtendedUsage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/ExtendedUsage.java
new file mode 100644
index 000000000..4965f4b8c
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/ExtendedUsage.java
@@ -0,0 +1,67 @@
+package com.alibaba.qwen.code.cli.protocol.data;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+
+public class ExtendedUsage extends Usage {
+ @JSONField(name = "server_tool_use")
+ private ServerToolUse serverToolUse;
+
+ @JSONField(name = "service_tier")
+ private String serviceTier;
+
+ @JSONField(name = "cache_creation")
+ private CacheCreation cacheCreation;
+
+ public ServerToolUse getServerToolUse() {
+ return serverToolUse;
+ }
+
+ public void setServerToolUse(ServerToolUse serverToolUse) {
+ this.serverToolUse = serverToolUse;
+ }
+
+ public String getServiceTier() {
+ return serviceTier;
+ }
+
+ public void setServiceTier(String serviceTier) {
+ this.serviceTier = serviceTier;
+ }
+
+ public CacheCreation getCacheCreation() {
+ return cacheCreation;
+ }
+
+ public void setCacheCreation(CacheCreation cacheCreation) {
+ this.cacheCreation = cacheCreation;
+ }
+
+ public static class ServerToolUse {
+ @JSONField(name = "web_search_requests")
+ private int webSearchRequests;
+ }
+
+ public static class CacheCreation {
+ @JSONField(name = "ephemeral_1h_input_tokens")
+ private int ephemeral1hInputTokens;
+
+ @JSONField(name = "ephemeral_5m_input_tokens")
+ private int ephemeral5mInputTokens;
+
+ public int getEphemeral1hInputTokens() {
+ return ephemeral1hInputTokens;
+ }
+
+ public void setEphemeral1hInputTokens(int ephemeral1hInputTokens) {
+ this.ephemeral1hInputTokens = ephemeral1hInputTokens;
+ }
+
+ public int getEphemeral5mInputTokens() {
+ return ephemeral5mInputTokens;
+ }
+
+ public void setEphemeral5mInputTokens(int ephemeral5mInputTokens) {
+ this.ephemeral5mInputTokens = ephemeral5mInputTokens;
+ }
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/InitializeConfig.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/InitializeConfig.java
new file mode 100644
index 000000000..ccafed4f0
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/InitializeConfig.java
@@ -0,0 +1,40 @@
+package com.alibaba.qwen.code.cli.protocol.data;
+
+public class InitializeConfig {
+ String hooks;
+ String sdkMcpServers;
+ String mcpServers;
+ String agents;
+
+ public String getHooks() {
+ return hooks;
+ }
+
+ public void setHooks(String hooks) {
+ this.hooks = hooks;
+ }
+
+ public String getSdkMcpServers() {
+ return sdkMcpServers;
+ }
+
+ public void setSdkMcpServers(String sdkMcpServers) {
+ this.sdkMcpServers = sdkMcpServers;
+ }
+
+ public String getMcpServers() {
+ return mcpServers;
+ }
+
+ public void setMcpServers(String mcpServers) {
+ this.mcpServers = mcpServers;
+ }
+
+ public String getAgents() {
+ return agents;
+ }
+
+ public void setAgents(String agents) {
+ this.agents = agents;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/ModelUsage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/ModelUsage.java
new file mode 100644
index 000000000..22787f232
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/ModelUsage.java
@@ -0,0 +1,58 @@
+package com.alibaba.qwen.code.cli.protocol.data;
+
+public class ModelUsage {
+ private int inputTokens;
+ private int outputTokens;
+ private int cacheReadInputTokens;
+ private int cacheCreationInputTokens;
+ private int webSearchRequests;
+ private int contextWindow;
+
+ public int getInputTokens() {
+ return inputTokens;
+ }
+
+ public void setInputTokens(int inputTokens) {
+ this.inputTokens = inputTokens;
+ }
+
+ public int getOutputTokens() {
+ return outputTokens;
+ }
+
+ public void setOutputTokens(int outputTokens) {
+ this.outputTokens = outputTokens;
+ }
+
+ public int getCacheReadInputTokens() {
+ return cacheReadInputTokens;
+ }
+
+ public void setCacheReadInputTokens(int cacheReadInputTokens) {
+ this.cacheReadInputTokens = cacheReadInputTokens;
+ }
+
+ public int getCacheCreationInputTokens() {
+ return cacheCreationInputTokens;
+ }
+
+ public void setCacheCreationInputTokens(int cacheCreationInputTokens) {
+ this.cacheCreationInputTokens = cacheCreationInputTokens;
+ }
+
+ public int getWebSearchRequests() {
+ return webSearchRequests;
+ }
+
+ public void setWebSearchRequests(int webSearchRequests) {
+ this.webSearchRequests = webSearchRequests;
+ }
+
+ public int getContextWindow() {
+ return contextWindow;
+ }
+
+ public void setContextWindow(int contextWindow) {
+ this.contextWindow = contextWindow;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/Usage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/Usage.java
new file mode 100644
index 000000000..1222b16f2
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/data/Usage.java
@@ -0,0 +1,56 @@
+package com.alibaba.qwen.code.cli.protocol.data;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+
+public class Usage {
+ @JSONField(name = "input_tokens")
+ private Integer inputTokens;
+ @JSONField(name = "output_tokens")
+ private Integer outputTokens;
+ @JSONField(name = "cache_creation_input_tokens")
+ private Integer cacheCreationInputTokens;
+ @JSONField(name = "cache_read_input_tokens")
+ private Integer cacheReadInputTokens;
+ @JSONField(name = "total_tokens")
+ private Integer totalTokens;
+
+ public Integer getInputTokens() {
+ return inputTokens;
+ }
+
+ public void setInputTokens(Integer inputTokens) {
+ this.inputTokens = inputTokens;
+ }
+
+ public Integer getOutputTokens() {
+ return outputTokens;
+ }
+
+ public void setOutputTokens(Integer outputTokens) {
+ this.outputTokens = outputTokens;
+ }
+
+ public Integer getCacheCreationInputTokens() {
+ return cacheCreationInputTokens;
+ }
+
+ public void setCacheCreationInputTokens(Integer cacheCreationInputTokens) {
+ this.cacheCreationInputTokens = cacheCreationInputTokens;
+ }
+
+ public Integer getCacheReadInputTokens() {
+ return cacheReadInputTokens;
+ }
+
+ public void setCacheReadInputTokens(Integer cacheReadInputTokens) {
+ this.cacheReadInputTokens = cacheReadInputTokens;
+ }
+
+ public Integer getTotalTokens() {
+ return totalTokens;
+ }
+
+ public void setTotalTokens(Integer totalTokens) {
+ this.totalTokens = totalTokens;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/Message.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/Message.java
new file mode 100644
index 000000000..de43924df
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/Message.java
@@ -0,0 +1,5 @@
+package com.alibaba.qwen.code.cli.protocol.message;
+
+public interface Message {
+ String getType();
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/MessageBase.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/MessageBase.java
new file mode 100644
index 000000000..c66df12c4
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/MessageBase.java
@@ -0,0 +1,22 @@
+package com.alibaba.qwen.code.cli.protocol.message;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(alphabetic = false, typeKey = "type", typeName = "MessageBase")
+public class MessageBase implements Message{
+ protected String type;
+
+ public String toString() {
+ return JSON.toJSONString(this);
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKResultMessage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKResultMessage.java
new file mode 100644
index 000000000..dfa2275ff
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKResultMessage.java
@@ -0,0 +1,151 @@
+package com.alibaba.qwen.code.cli.protocol.message;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+import com.alibaba.qwen.code.cli.protocol.data.CLIPermissionDenial;
+import com.alibaba.qwen.code.cli.protocol.data.ExtendedUsage;
+import com.alibaba.qwen.code.cli.protocol.data.Usage;
+
+@JSONType(typeKey = "type", typeName = "result")
+public class SDKResultMessage extends MessageBase {
+ private String subtype; // 'error_max_turns' | 'error_during_execution'
+ private String uuid;
+
+ @JSONField(name = "session_id")
+ private String sessionId;
+
+ @JSONField(name = "is_error")
+ private boolean isError = true;
+
+ @JSONField(name = "duration_ms")
+ private Long durationMs;
+
+ @JSONField(name = "duration_api_ms")
+ private Long durationApiMs;
+
+ @JSONField(name = "num_turns")
+ private Integer numTurns;
+ private ExtendedUsage usage;
+ private Map modelUsage;
+
+ @JSONField(name = "permission_denials")
+ private List permissionDenials;
+ private Error error;
+
+ public SDKResultMessage() {
+ super();
+ this.type = "result";
+ }
+
+ public String getSubtype() {
+ return subtype;
+ }
+
+ public void setSubtype(String subtype) {
+ this.subtype = subtype;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public boolean isError() {
+ return isError;
+ }
+
+ public void setError(boolean error) {
+ isError = error;
+ }
+
+ public Long getDurationMs() {
+ return durationMs;
+ }
+
+ public void setDurationMs(Long durationMs) {
+ this.durationMs = durationMs;
+ }
+
+ public Long getDurationApiMs() {
+ return durationApiMs;
+ }
+
+ public void setDurationApiMs(Long durationApiMs) {
+ this.durationApiMs = durationApiMs;
+ }
+
+ public Integer getNumTurns() {
+ return numTurns;
+ }
+
+ public void setNumTurns(Integer numTurns) {
+ this.numTurns = numTurns;
+ }
+
+ public ExtendedUsage getUsage() {
+ return usage;
+ }
+
+ public void setUsage(ExtendedUsage usage) {
+ this.usage = usage;
+ }
+
+ public Map getModelUsage() {
+ return modelUsage;
+ }
+
+ public void setModelUsage(Map modelUsage) {
+ this.modelUsage = modelUsage;
+ }
+
+ public List getPermissionDenials() {
+ return permissionDenials;
+ }
+
+ public void setPermissionDenials(List permissionDenials) {
+ this.permissionDenials = permissionDenials;
+ }
+
+ public Error getError() {
+ return error;
+ }
+
+ public void setError(Error error) {
+ this.error = error;
+ }
+
+ public static class Error {
+ private String type;
+ private String message;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKSystemMessage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKSystemMessage.java
new file mode 100644
index 000000000..22870cb85
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKSystemMessage.java
@@ -0,0 +1,213 @@
+package com.alibaba.qwen.code.cli.protocol.message;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "system")
+public class SDKSystemMessage extends MessageBase {
+ private String subtype;
+ private String uuid;
+ @JSONField(name = "session_id")
+ private String sessionId;
+ private Object data;
+ private String cwd;
+ private List tools;
+ @JSONField(name = "mcp_servers")
+ private List mcpServers;
+ private String model;
+ @JSONField(name = "permission_mode")
+ private String permissionMode;
+ @JSONField(name = "slash_commands")
+ private List slashCommands;
+ @JSONField(name = "qwen_code_version")
+ private String qwenCodeVersion;
+ @JSONField(name = "output_style")
+ private String outputStyle;
+ private List agents;
+ private List skills;
+ private Map capabilities;
+ @JSONField(name = "compact_metadata")
+ private CompactMetadata compactMetadata;
+
+ public SDKSystemMessage() {
+ super();
+ this.type = "system";
+ }
+
+ public String getSubtype() {
+ return subtype;
+ }
+
+ public void setSubtype(String subtype) {
+ this.subtype = subtype;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public String getCwd() {
+ return cwd;
+ }
+
+ public void setCwd(String cwd) {
+ this.cwd = cwd;
+ }
+
+ public List getTools() {
+ return tools;
+ }
+
+ public void setTools(List tools) {
+ this.tools = tools;
+ }
+
+ public List getMcpServers() {
+ return mcpServers;
+ }
+
+ public void setMcpServers(List mcpServers) {
+ this.mcpServers = mcpServers;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public String getPermissionMode() {
+ return permissionMode;
+ }
+
+ public void setPermissionMode(String permissionMode) {
+ this.permissionMode = permissionMode;
+ }
+
+ public List getSlashCommands() {
+ return slashCommands;
+ }
+
+ public void setSlashCommands(List slashCommands) {
+ this.slashCommands = slashCommands;
+ }
+
+ public String getQwenCodeVersion() {
+ return qwenCodeVersion;
+ }
+
+ public void setQwenCodeVersion(String qwenCodeVersion) {
+ this.qwenCodeVersion = qwenCodeVersion;
+ }
+
+ public String getOutputStyle() {
+ return outputStyle;
+ }
+
+ public void setOutputStyle(String outputStyle) {
+ this.outputStyle = outputStyle;
+ }
+
+ public List getAgents() {
+ return agents;
+ }
+
+ public void setAgents(List agents) {
+ this.agents = agents;
+ }
+
+ public List getSkills() {
+ return skills;
+ }
+
+ public void setSkills(List skills) {
+ this.skills = skills;
+ }
+
+ public Map getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public CompactMetadata getCompactMetadata() {
+ return compactMetadata;
+ }
+
+ public void setCompactMetadata(CompactMetadata compactMetadata) {
+ this.compactMetadata = compactMetadata;
+ }
+
+ public static class McpServer {
+ private String name;
+ private String status;
+
+ // Getters and setters
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+ }
+
+ public static class CompactMetadata {
+ private String trigger;
+
+ @JSONField(name = "pre_tokens")
+ private Integer preTokens;
+
+ // Getters and setters
+ public String getTrigger() {
+ return trigger;
+ }
+
+ public void setTrigger(String trigger) {
+ this.trigger = trigger;
+ }
+
+ public Integer getPreTokens() {
+ return preTokens;
+ }
+
+ public void setPreTokens(Integer preTokens) {
+ this.preTokens = preTokens;
+ }
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKUserMessage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKUserMessage.java
new file mode 100644
index 000000000..e896b08c4
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/SDKUserMessage.java
@@ -0,0 +1,90 @@
+package com.alibaba.qwen.code.cli.protocol.message;
+
+import java.util.Map;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "user")
+public class SDKUserMessage extends MessageBase {
+ private String uuid;
+
+ @JSONField(name = "session_id")
+ private String sessionId;
+ private final APIUserMessage message = new APIUserMessage();
+
+ @JSONField(name = "parent_tool_use_id")
+ private String parentToolUseId;
+ private Map options;
+
+ public SDKUserMessage() {
+ super();
+ this.setType("user");
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public SDKUserMessage setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+
+ public SDKUserMessage setContent(String content) {
+ message.setContent(content);
+ return this;
+ }
+
+ public String getContent() {
+ return message.getContent();
+ }
+
+ public String getParentToolUseId() {
+ return parentToolUseId;
+ }
+
+ public SDKUserMessage setParentToolUseId(String parentToolUseId) {
+ this.parentToolUseId = parentToolUseId;
+ return this;
+ }
+
+ public Map getOptions() {
+ return options;
+ }
+
+ public SDKUserMessage setOptions(Map options) {
+ this.options = options;
+ return this;
+ }
+
+ public static class APIUserMessage {
+ private String role = "user";
+ private String content;
+
+ // Getters and Setters
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/APIAssistantMessage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/APIAssistantMessage.java
new file mode 100644
index 000000000..5a0b3776c
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/APIAssistantMessage.java
@@ -0,0 +1,76 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant;
+
+import java.util.List;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.qwen.code.cli.protocol.data.Usage;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.block.ContentBlock;
+
+public class APIAssistantMessage {
+ private String id;
+ private String type = "message";
+ private String role = "assistant";
+ private String model;
+ private List content;
+
+ @JSONField(name = "stop_reason")
+ private String stopReason;
+ private Usage usage;
+
+ // Getters and setters
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public String getStopReason() {
+ return stopReason;
+ }
+
+ public void setStopReason(String stopReason) {
+ this.stopReason = stopReason;
+ }
+
+ public Usage getUsage() {
+ return usage;
+ }
+
+ public void setUsage(Usage usage) {
+ this.usage = usage;
+ }
+
+ public List getContent() {
+ return content;
+ }
+
+ public void setContent(List content) {
+ this.content = content;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/SDKAssistantMessage.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/SDKAssistantMessage.java
new file mode 100644
index 000000000..7e906fc44
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/SDKAssistantMessage.java
@@ -0,0 +1,49 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
+
+@JSONType(typeKey = "type", typeName = "assistant")
+public class SDKAssistantMessage extends MessageBase {
+ private String uuid;
+
+ @JSONField(name = "session_id")
+ private String sessionId;
+ private APIAssistantMessage message;
+
+ @JSONField(name = "parent_tool_use_id")
+ private String parentToolUseId;
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public APIAssistantMessage getMessage() {
+ return message;
+ }
+
+ public void setMessage(APIAssistantMessage message) {
+ this.message = message;
+ }
+
+ public String getParentToolUseId() {
+ return parentToolUseId;
+ }
+
+ public void setParentToolUseId(String parentToolUseId) {
+ this.parentToolUseId = parentToolUseId;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/Annotation.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/Annotation.java
new file mode 100644
index 000000000..5e8b9a2b5
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/Annotation.java
@@ -0,0 +1,28 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+
+public class Annotation {
+ @JSONField(name = "type")
+ private String type;
+
+ @JSONField(name = "value")
+ private String value;
+
+ // Getters and setters
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ContentBlock.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ContentBlock.java
new file mode 100644
index 000000000..3e72ad7d0
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ContentBlock.java
@@ -0,0 +1,32 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
+
+import java.util.List;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "ContentBlock", seeAlso = { TextBlock.class, ToolResultBlock.class, ThinkingBlock.class, ToolUseBlock.class })
+public class ContentBlock {
+ protected String type;
+ protected List annotations;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List annotations) {
+ this.annotations = annotations;
+ }
+
+ public String toString() {
+ return JSON.toJSONString(this);
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/TextBlock.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/TextBlock.java
new file mode 100644
index 000000000..86e5513d3
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/TextBlock.java
@@ -0,0 +1,16 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
+
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "text")
+public class TextBlock extends ContentBlock {
+ private String text;
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ThinkingBlock.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ThinkingBlock.java
new file mode 100644
index 000000000..fa479563f
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ThinkingBlock.java
@@ -0,0 +1,25 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
+
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "thinking")
+public class ThinkingBlock extends ContentBlock{
+ private String thinking;
+ private String signature;
+
+ public String getThinking() {
+ return thinking;
+ }
+
+ public void setThinking(String thinking) {
+ this.thinking = thinking;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ public void setSignature(String signature) {
+ this.signature = signature;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ToolResultBlock.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ToolResultBlock.java
new file mode 100644
index 000000000..3d7acfea2
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ToolResultBlock.java
@@ -0,0 +1,40 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "tool_result")
+public class ToolResultBlock extends ContentBlock {
+ @JSONField(name = "tool_use_id")
+ private String toolUseId;
+
+ @JSONField(name = "content")
+ private Object content; // Can be String or List
+
+ @JSONField(name = "is_error")
+ private Boolean isError;
+
+ public String getToolUseId() {
+ return toolUseId;
+ }
+
+ public void setToolUseId(String toolUseId) {
+ this.toolUseId = toolUseId;
+ }
+
+ public Object getContent() {
+ return content;
+ }
+
+ public void setContent(Object content) {
+ this.content = content;
+ }
+
+ public Boolean getIsError() {
+ return isError;
+ }
+
+ public void setIsError(Boolean isError) {
+ this.isError = isError;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ToolUseBlock.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ToolUseBlock.java
new file mode 100644
index 000000000..58a3bd4fc
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/assistant/block/ToolUseBlock.java
@@ -0,0 +1,49 @@
+package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson2.annotation.JSONType;
+
+@JSONType(typeKey = "type", typeName = "tool_use")
+public class ToolUseBlock extends ContentBlock {
+ private String id;
+ private String name;
+ private Map input;
+ private List annotations;
+
+ // 构造函数
+ public ToolUseBlock() {}
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map getInput() {
+ return input;
+ }
+
+ public void setInput(Map input) {
+ this.input = input;
+ }
+
+ public List getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(List annotations) {
+ this.annotations = annotations;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlInitializeRequest.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlInitializeRequest.java
new file mode 100644
index 000000000..3d217289c
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlInitializeRequest.java
@@ -0,0 +1,28 @@
+package com.alibaba.qwen.code.cli.protocol.message.control;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.qwen.code.cli.protocol.data.InitializeConfig;
+
+public class CLIControlInitializeRequest {
+ String subtype = "initialize";
+
+ @JSONField(unwrapped = true)
+ InitializeConfig initializeConfig = new InitializeConfig();
+
+ public String getSubtype() {
+ return subtype;
+ }
+
+ public void setSubtype(String subtype) {
+ this.subtype = subtype;
+ }
+
+ public InitializeConfig getInitializeConfig() {
+ return initializeConfig;
+ }
+
+ public CLIControlInitializeRequest setInitializeConfig(InitializeConfig initializeConfig) {
+ this.initializeConfig = initializeConfig;
+ return this;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlInitializeResponse.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlInitializeResponse.java
new file mode 100644
index 000000000..284781a76
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlInitializeResponse.java
@@ -0,0 +1,24 @@
+package com.alibaba.qwen.code.cli.protocol.message.control;
+
+import com.alibaba.qwen.code.cli.protocol.data.Capabilities;
+
+public class CLIControlInitializeResponse {
+ String subtype = "initialize";
+ Capabilities capabilities;
+
+ public String getSubtype() {
+ return subtype;
+ }
+
+ public void setSubtype(String subtype) {
+ this.subtype = subtype;
+ }
+
+ public Capabilities getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Capabilities capabilities) {
+ this.capabilities = capabilities;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlRequest.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlRequest.java
new file mode 100644
index 000000000..e6ea7b956
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlRequest.java
@@ -0,0 +1,44 @@
+package com.alibaba.qwen.code.cli.protocol.message.control;
+
+import java.util.UUID;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
+
+@JSONType(typeKey = "type", typeName = "control_request")
+public class CLIControlRequest extends MessageBase {
+ @JSONField(name = "request_id")
+ private String requestId = UUID.randomUUID().toString();
+
+ private R request;
+
+ public CLIControlRequest() {
+ super();
+ type = "control_request";
+ }
+
+ public static CLIControlRequest create(T request) {
+ CLIControlRequest controlRequest = new CLIControlRequest<>();
+ controlRequest.setRequest(request);
+ return controlRequest;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public CLIControlRequest setRequestId(String requestId) {
+ this.requestId = requestId;
+ return this;
+ }
+
+ public R getRequest() {
+ return request;
+ }
+
+ public CLIControlRequest setRequest(R request) {
+ this.request = request;
+ return this;
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlResponse.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlResponse.java
new file mode 100644
index 000000000..5e193e2cd
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/message/control/CLIControlResponse.java
@@ -0,0 +1,54 @@
+package com.alibaba.qwen.code.cli.protocol.message.control;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONType;
+import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
+
+@JSONType(typeKey = "type", typeName = "control_response")
+public class CLIControlResponse extends MessageBase {
+ private Response response;
+
+ public CLIControlResponse() {
+ super();
+ this.type = "control_response";
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+
+ public void setResponse(Response response) {
+ this.response = response;
+ }
+
+ public static class Response {
+ @JSONField(name = "request_id")
+ private String requestId;
+ private String subtype;
+ R response;
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getSubtype() {
+ return subtype;
+ }
+
+ public void setSubtype(String subtype) {
+ this.subtype = subtype;
+ }
+
+ public R getResponse() {
+ return response;
+ }
+
+ public void setResponse(R response) {
+ this.response = response;
+ }
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/protocol.ts b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/protocol.ts
new file mode 100644
index 000000000..e5eeb1212
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/protocol/protocol.ts
@@ -0,0 +1,594 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
+export interface Annotation {
+ type: string;
+ value: string;
+}
+
+export interface Usage {
+ input_tokens: number;
+ output_tokens: number;
+ cache_creation_input_tokens?: number;
+ cache_read_input_tokens?: number;
+ total_tokens?: number;
+}
+
+export interface ExtendedUsage extends Usage {
+ server_tool_use?: {
+ web_search_requests: number;
+ };
+ service_tier?: string;
+ cache_creation?: {
+ ephemeral_1h_input_tokens: number;
+ ephemeral_5m_input_tokens: number;
+ };
+}
+
+export interface ModelUsage {
+ inputTokens: number;
+ outputTokens: number;
+ cacheReadInputTokens: number;
+ cacheCreationInputTokens: number;
+ webSearchRequests: number;
+ contextWindow: number;
+}
+
+export interface CLIPermissionDenial {
+ tool_name: string;
+ tool_use_id: string;
+ tool_input: unknown;
+}
+
+export interface TextBlock {
+ type: 'text';
+ text: string;
+ annotations?: Annotation[];
+}
+
+export interface ThinkingBlock {
+ type: 'thinking';
+ thinking: string;
+ signature?: string;
+ annotations?: Annotation[];
+}
+
+export interface ToolUseBlock {
+ type: 'tool_use';
+ id: string;
+ name: string;
+ input: unknown;
+ annotations?: Annotation[];
+}
+
+export interface ToolResultBlock {
+ type: 'tool_result';
+ tool_use_id: string;
+ content?: string | ContentBlock[];
+ is_error?: boolean;
+ annotations?: Annotation[];
+}
+
+export type ContentBlock =
+ | TextBlock
+ | ThinkingBlock
+ | ToolUseBlock
+ | ToolResultBlock;
+
+export interface APIUserMessage {
+ role: 'user';
+ content: string | ContentBlock[];
+}
+
+export interface APIAssistantMessage {
+ id: string;
+ type: 'message';
+ role: 'assistant';
+ model: string;
+ content: ContentBlock[];
+ stop_reason?: string | null;
+ usage: Usage;
+}
+
+export interface SDKUserMessage {
+ type: 'user';
+ uuid?: string;
+ session_id: string;
+ message: APIUserMessage;
+ parent_tool_use_id: string | null;
+ options?: Record;
+}
+
+export interface SDKAssistantMessage {
+ type: 'assistant';
+ uuid: string;
+ session_id: string;
+ message: APIAssistantMessage;
+ parent_tool_use_id: string | null;
+}
+
+export interface SDKSystemMessage {
+ type: 'system';
+ subtype: string;
+ uuid: string;
+ session_id: string;
+ data?: unknown;
+ cwd?: string;
+ tools?: string[];
+ mcp_servers?: Array<{
+ name: string;
+ status: string;
+ }>;
+ model?: string;
+ permission_mode?: string;
+ slash_commands?: string[];
+ qwen_code_version?: string;
+ output_style?: string;
+ agents?: string[];
+ skills?: string[];
+ capabilities?: Record;
+ compact_metadata?: {
+ trigger: 'manual' | 'auto';
+ pre_tokens: number;
+ };
+}
+
+export interface SDKResultMessageSuccess {
+ type: 'result';
+ subtype: 'success';
+ uuid: string;
+ session_id: string;
+ is_error: false;
+ duration_ms: number;
+ duration_api_ms: number;
+ num_turns: number;
+ result: string;
+ usage: ExtendedUsage;
+ modelUsage?: Record;
+ permission_denials: CLIPermissionDenial[];
+ [key: string]: unknown;
+}
+
+export interface SDKResultMessageError {
+ type: 'result';
+ subtype: 'error_max_turns' | 'error_during_execution';
+ uuid: string;
+ session_id: string;
+ is_error: true;
+ duration_ms: number;
+ duration_api_ms: number;
+ num_turns: number;
+ usage: ExtendedUsage;
+ modelUsage?: Record;
+ permission_denials: CLIPermissionDenial[];
+ error?: {
+ type?: string;
+ message: string;
+ [key: string]: unknown;
+ };
+ [key: string]: unknown;
+}
+
+export type SDKResultMessage = SDKResultMessageSuccess | SDKResultMessageError;
+
+export interface MessageStartStreamEvent {
+ type: 'message_start';
+ message: {
+ id: string;
+ role: 'assistant';
+ model: string;
+ };
+}
+
+export interface ContentBlockStartEvent {
+ type: 'content_block_start';
+ index: number;
+ content_block: ContentBlock;
+}
+
+export type ContentBlockDelta =
+ | {
+ type: 'text_delta';
+ text: string;
+ }
+ | {
+ type: 'thinking_delta';
+ thinking: string;
+ }
+ | {
+ type: 'input_json_delta';
+ partial_json: string;
+ };
+
+export interface ContentBlockDeltaEvent {
+ type: 'content_block_delta';
+ index: number;
+ delta: ContentBlockDelta;
+}
+
+export interface ContentBlockStopEvent {
+ type: 'content_block_stop';
+ index: number;
+}
+
+export interface MessageStopStreamEvent {
+ type: 'message_stop';
+}
+
+export type StreamEvent =
+ | MessageStartStreamEvent
+ | ContentBlockStartEvent
+ | ContentBlockDeltaEvent
+ | ContentBlockStopEvent
+ | MessageStopStreamEvent;
+
+export interface SDKPartialAssistantMessage {
+ type: 'stream_event';
+ uuid: string;
+ session_id: string;
+ event: StreamEvent;
+ parent_tool_use_id: string | null;
+}
+
+export type PermissionMode = 'default' | 'plan' | 'auto-edit' | 'yolo';
+
+/**
+ * TODO: Align with `ToolCallConfirmationDetails`
+ */
+export interface PermissionSuggestion {
+ type: 'allow' | 'deny' | 'modify';
+ label: string;
+ description?: string;
+ modifiedInput?: unknown;
+}
+
+export interface HookRegistration {
+ event: string;
+ callback_id: string;
+}
+
+export interface HookCallbackResult {
+ shouldSkip?: boolean;
+ shouldInterrupt?: boolean;
+ suppressOutput?: boolean;
+ message?: string;
+}
+
+export interface CLIControlInterruptRequest {
+ subtype: 'interrupt';
+}
+
+export interface CLIControlPermissionRequest {
+ subtype: 'can_use_tool';
+ tool_name: string;
+ tool_use_id: string;
+ input: unknown;
+ permission_suggestions: PermissionSuggestion[] | null;
+ blocked_path: string | null;
+}
+
+export enum AuthProviderType {
+ DYNAMIC_DISCOVERY = 'dynamic_discovery',
+ GOOGLE_CREDENTIALS = 'google_credentials',
+ SERVICE_ACCOUNT_IMPERSONATION = 'service_account_impersonation',
+}
+
+export interface MCPServerConfig {
+ command?: string;
+ args?: string[];
+ env?: Record;
+ cwd?: string;
+ url?: string;
+ httpUrl?: string;
+ headers?: Record;
+ tcp?: string;
+ timeout?: number;
+ trust?: boolean;
+ description?: string;
+ includeTools?: string[];
+ excludeTools?: string[];
+ extensionName?: string;
+ oauth?: Record;
+ authProviderType?: AuthProviderType;
+ targetAudience?: string;
+ targetServiceAccount?: string;
+}
+
+/**
+ * SDK MCP Server configuration
+ *
+ * SDK MCP servers run in the SDK process and are connected via in-memory transport.
+ * Tool calls are routed through the control plane between SDK and CLI.
+ */
+export interface SDKMcpServerConfig {
+ /**
+ * Type identifier for SDK MCP servers
+ */
+ type: 'sdk';
+ /**
+ * Server name for identification and routing
+ */
+ name: string;
+ /**
+ * The MCP Server instance created by createSdkMcpServer()
+ */
+ instance: McpServer;
+}
+
+/**
+ * Wire format for SDK MCP servers sent to the CLI
+ */
+export type WireSDKMcpServerConfig = Omit;
+
+export interface CLIControlInitializeRequest {
+ subtype: 'initialize';
+ hooks?: HookRegistration[] | null;
+ /**
+ * SDK MCP servers config
+ * These are MCP servers running in the SDK process, connected via control plane.
+ * External MCP servers are configured separately in settings, not via initialization.
+ */
+ sdkMcpServers?: Record;
+ /**
+ * External MCP servers that should be managed by the CLI.
+ */
+ mcpServers?: Record;
+ agents?: SubagentConfig[];
+}
+
+export interface CLIControlSetPermissionModeRequest {
+ subtype: 'set_permission_mode';
+ mode: PermissionMode;
+}
+
+export interface CLIHookCallbackRequest {
+ subtype: 'hook_callback';
+ callback_id: string;
+ input: unknown;
+ tool_use_id: string | null;
+}
+
+export interface CLIControlMcpMessageRequest {
+ subtype: 'mcp_message';
+ server_name: string;
+ message: {
+ jsonrpc?: string;
+ method: string;
+ params?: Record;
+ id?: string | number | null;
+ };
+}
+
+export interface CLIControlSetModelRequest {
+ subtype: 'set_model';
+ model: string;
+}
+
+export interface CLIControlMcpStatusRequest {
+ subtype: 'mcp_server_status';
+}
+
+export interface CLIControlSupportedCommandsRequest {
+ subtype: 'supported_commands';
+}
+
+export type ControlRequestPayload =
+ | CLIControlInterruptRequest
+ | CLIControlPermissionRequest
+ | CLIControlInitializeRequest
+ | CLIControlSetPermissionModeRequest
+ | CLIHookCallbackRequest
+ | CLIControlMcpMessageRequest
+ | CLIControlSetModelRequest
+ | CLIControlMcpStatusRequest
+ | CLIControlSupportedCommandsRequest;
+
+export interface CLIControlRequest {
+ type: 'control_request';
+ request_id: string;
+ request: ControlRequestPayload;
+}
+
+export interface PermissionApproval {
+ allowed: boolean;
+ reason?: string;
+ modifiedInput?: unknown;
+}
+
+export interface ControlResponse {
+ subtype: 'success';
+ request_id: string;
+ response: unknown;
+}
+
+export interface ControlErrorResponse {
+ subtype: 'error';
+ request_id: string;
+ error: string | { message: string; [key: string]: unknown };
+}
+
+export interface CLIControlResponse {
+ type: 'control_response';
+ response: ControlResponse | ControlErrorResponse;
+}
+
+export interface ControlCancelRequest {
+ type: 'control_cancel_request';
+ request_id?: string;
+}
+
+export type ControlMessage =
+ | CLIControlRequest
+ | CLIControlResponse
+ | ControlCancelRequest;
+
+/**
+ * Union of all SDK message types
+ */
+export type SDKMessage =
+ | SDKUserMessage
+ | SDKAssistantMessage
+ | SDKSystemMessage
+ | SDKResultMessage
+ | SDKPartialAssistantMessage;
+
+export function isSDKUserMessage(msg: any): msg is SDKUserMessage {
+ return (
+ msg && typeof msg === 'object' && msg.type === 'user' && 'message' in msg
+ );
+}
+
+export function isSDKAssistantMessage(msg: any): msg is SDKAssistantMessage {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'assistant' &&
+ 'uuid' in msg &&
+ 'message' in msg &&
+ 'session_id' in msg &&
+ 'parent_tool_use_id' in msg
+ );
+}
+
+export function isSDKSystemMessage(msg: any): msg is SDKSystemMessage {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'system' &&
+ 'subtype' in msg &&
+ 'uuid' in msg &&
+ 'session_id' in msg
+ );
+}
+
+export function isSDKResultMessage(msg: any): msg is SDKResultMessage {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'result' &&
+ 'subtype' in msg &&
+ 'duration_ms' in msg &&
+ 'is_error' in msg &&
+ 'uuid' in msg &&
+ 'session_id' in msg
+ );
+}
+
+export function isSDKPartialAssistantMessage(
+ msg: any,
+): msg is SDKPartialAssistantMessage {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'stream_event' &&
+ 'uuid' in msg &&
+ 'session_id' in msg &&
+ 'event' in msg &&
+ 'parent_tool_use_id' in msg
+ );
+}
+
+export function isControlRequest(msg: any): msg is CLIControlRequest {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'control_request' &&
+ 'request_id' in msg &&
+ 'request' in msg
+ );
+}
+
+export function isControlResponse(msg: any): msg is CLIControlResponse {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'control_response' &&
+ 'response' in msg
+ );
+}
+
+export function isControlCancel(msg: any): msg is ControlCancelRequest {
+ return (
+ msg &&
+ typeof msg === 'object' &&
+ msg.type === 'control_cancel_request' &&
+ 'request_id' in msg
+ );
+}
+
+export function isTextBlock(block: any): block is TextBlock {
+ return block && typeof block === 'object' && block.type === 'text';
+}
+
+export function isThinkingBlock(block: any): block is ThinkingBlock {
+ return block && typeof block === 'object' && block.type === 'thinking';
+}
+
+export function isToolUseBlock(block: any): block is ToolUseBlock {
+ return block && typeof block === 'object' && block.type === 'tool_use';
+}
+
+export function isToolResultBlock(block: any): block is ToolResultBlock {
+ return block && typeof block === 'object' && block.type === 'tool_result';
+}
+
+export type SubagentLevel = 'session';
+
+export interface ModelConfig {
+ model?: string;
+ temp?: number;
+ top_p?: number;
+}
+
+export interface RunConfig {
+ max_time_minutes?: number;
+ max_turns?: number;
+}
+
+export interface SubagentConfig {
+ name: string;
+ description: string;
+ tools?: string[];
+ systemPrompt: string;
+ level: SubagentLevel;
+ filePath?: string;
+ modelConfig?: Partial;
+ runConfig?: Partial;
+ color?: string;
+ readonly isBuiltin?: boolean;
+}
+
+/**
+ * @license
+ * Copyright 2025 Qwen Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * Control Request Types
+ *
+ * Centralized enum for all control request subtypes supported by the CLI.
+ * This enum should be kept in sync with the controllers in:
+ * - packages/cli/src/services/control/controllers/systemController.ts
+ * - packages/cli/src/services/control/controllers/permissionController.ts
+ * - packages/cli/src/services/control/controllers/mcpController.ts
+ * - packages/cli/src/services/control/controllers/hookController.ts
+ */
+export enum ControlRequestType {
+ // SystemController requests
+ INITIALIZE = 'initialize',
+ INTERRUPT = 'interrupt',
+ SET_MODEL = 'set_model',
+ SUPPORTED_COMMANDS = 'supported_commands',
+
+ // PermissionController requests
+ CAN_USE_TOOL = 'can_use_tool',
+ SET_PERMISSION_MODE = 'set_permission_mode',
+
+ // MCPController requests
+ MCP_MESSAGE = 'mcp_message',
+ MCP_SERVER_STATUS = 'mcp_server_status',
+
+ // HookController requests
+ HOOK_CALLBACK = 'hook_callback',
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/Session.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/Session.java
new file mode 100644
index 000000000..346b0f1f2
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/Session.java
@@ -0,0 +1,89 @@
+package com.alibaba.qwen.code.cli.session;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.TypeReference;
+import com.alibaba.qwen.code.cli.protocol.data.Capabilities;
+import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
+import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
+import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeRequest;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeResponse;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
+import com.alibaba.qwen.code.cli.session.event.SessionEventConsumers;
+import com.alibaba.qwen.code.cli.session.exception.SessionCloseException;
+import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
+import com.alibaba.qwen.code.cli.session.exception.SessionStartException;
+import com.alibaba.qwen.code.cli.transport.Transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Session {
+ private final Transport transport;
+ private Capabilities capabilities;
+ private static final Logger log = LoggerFactory.getLogger(Session.class);
+
+ public Session(Transport transport) throws SessionStartException {
+ if (transport == null || !transport.isAvailable()) {
+ throw new SessionStartException("Transport is not available");
+ }
+ this.transport = transport;
+ start();
+ }
+
+ private void start() throws SessionStartException {
+ try {
+ String response = transport.inputWaitForOneLine(CLIControlRequest.create(new CLIControlInitializeRequest()).toString());
+ CLIControlResponse cliControlResponse = JSON.parseObject(response, new TypeReference>() {});
+ this.capabilities = cliControlResponse.getResponse().getResponse().getCapabilities();
+ } catch (Exception e) {
+ throw new SessionStartException("Failed to initialize the session", e);
+ }
+ }
+
+ public void close() throws SessionCloseException {
+ try {
+ transport.close();
+ } catch (Exception e) {
+ throw new SessionCloseException("Failed to close the session", e);
+ }
+ }
+
+ public Capabilities getCapabilities() {
+ return capabilities;
+ }
+
+ public void sendPrompt(String prompt, SessionEventConsumers sessionEventConsumers) throws SessionSendPromptException {
+ if (!transport.isAvailable()) {
+ throw new SessionSendPromptException("Session is not available");
+ }
+
+ try {
+ transport.inputWaitForMultiLine(new SDKUserMessage().setContent(prompt).toString(), (line) -> {
+ log.debug("read a message from agent {}", line);
+ JSONObject jsonObject = JSON.parseObject(line);
+
+ String messageType = jsonObject.getString("type");
+ if ("system".equals(messageType)) {
+ sessionEventConsumers.onSystemMessage(JSON.parseObject(line, SDKSystemMessage.class));
+ return false;
+ } else if ("assistant".equals(messageType)) {
+ sessionEventConsumers.onAssistantMessage(JSON.parseObject(line, SDKAssistantMessage.class));
+ return false;
+ } else if ("result".equals(messageType)) {
+ sessionEventConsumers.onResultMessage(JSON.parseObject(line, SDKResultMessage.class));
+ return true;
+ } else {
+ log.warn("unknown message type: {}", messageType);
+ sessionEventConsumers.onOtherMessage(line);
+ return false;
+ }
+ });
+ } catch (Exception e) {
+ throw new SessionSendPromptException("Failed to send prompt", e);
+ }
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/event/SessionEventConsumers.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/event/SessionEventConsumers.java
new file mode 100644
index 000000000..9f9bb6fc1
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/event/SessionEventConsumers.java
@@ -0,0 +1,15 @@
+package com.alibaba.qwen.code.cli.session.event;
+
+import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
+import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
+
+public interface SessionEventConsumers {
+ void onSystemMessage(SDKSystemMessage systemMessage);
+
+ void onResultMessage(SDKResultMessage resultMessage);
+
+ void onAssistantMessage(SDKAssistantMessage assistantMessage);
+
+ void onOtherMessage(String message);
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/event/SessionEventSimpleConsumers.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/event/SessionEventSimpleConsumers.java
new file mode 100644
index 000000000..584354d43
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/event/SessionEventSimpleConsumers.java
@@ -0,0 +1,23 @@
+package com.alibaba.qwen.code.cli.session.event;
+
+import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
+import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
+
+public class SessionEventSimpleConsumers implements SessionEventConsumers {
+ @Override
+ public void onSystemMessage(SDKSystemMessage systemMessage) {
+ }
+
+ @Override
+ public void onResultMessage(SDKResultMessage resultMessage) {
+ }
+
+ @Override
+ public void onAssistantMessage(SDKAssistantMessage assistantMessage) {
+ }
+
+ @Override
+ public void onOtherMessage(String message) {
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionCloseException.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionCloseException.java
new file mode 100644
index 000000000..3db39e793
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionCloseException.java
@@ -0,0 +1,22 @@
+package com.alibaba.qwen.code.cli.session.exception;
+
+public class SessionCloseException extends Exception {
+ public SessionCloseException() {
+ }
+
+ public SessionCloseException(String message) {
+ super(message);
+ }
+
+ public SessionCloseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SessionCloseException(Throwable cause) {
+ super(cause);
+ }
+
+ public SessionCloseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionSendPromptException.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionSendPromptException.java
new file mode 100644
index 000000000..74de3bba7
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionSendPromptException.java
@@ -0,0 +1,22 @@
+package com.alibaba.qwen.code.cli.session.exception;
+
+public class SessionSendPromptException extends Exception {
+ public SessionSendPromptException() {
+ }
+
+ public SessionSendPromptException(String message) {
+ super(message);
+ }
+
+ public SessionSendPromptException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SessionSendPromptException(Throwable cause) {
+ super(cause);
+ }
+
+ public SessionSendPromptException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionStartException.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionStartException.java
new file mode 100644
index 000000000..9d30f2367
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/session/exception/SessionStartException.java
@@ -0,0 +1,22 @@
+package com.alibaba.qwen.code.cli.session.exception;
+
+public class SessionStartException extends Exception {
+ public SessionStartException() {
+ }
+
+ public SessionStartException(String message) {
+ super(message);
+ }
+
+ public SessionStartException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SessionStartException(Throwable cause) {
+ super(cause);
+ }
+
+ public SessionStartException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/Transport.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/Transport.java
new file mode 100644
index 000000000..5b66cbc90
--- /dev/null
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/Transport.java
@@ -0,0 +1,18 @@
+package com.alibaba.qwen.code.cli.transport;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+
+public interface Transport {
+ void close() throws IOException;
+
+ boolean isAvailable();
+
+ String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException;
+
+ void inputWaitForMultiLine(String message, Function callBackFunction) throws IOException;
+
+ void inputNoWaitResponse(String message) throws IOException;
+}
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransport.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransport.java
index 3adc1072b..b7d62b5d7 100644
--- a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransport.java
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransport.java
@@ -1,5 +1,6 @@
package com.alibaba.qwen.code.cli.transport.process;
+import com.alibaba.qwen.code.cli.transport.Transport;
import com.alibaba.qwen.code.cli.transport.TransportOptions;
import org.apache.commons.lang3.exception.ContextedRuntimeException;
@@ -19,7 +20,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
-public class ProcessTransport {
+public class ProcessTransport implements Transport {
private static final Logger log = LoggerFactory.getLogger(ProcessTransport.class);
TransportOptionsAdapter transportOptionsAdapter;
@@ -31,6 +32,10 @@ public class ProcessTransport {
protected BufferedReader processOutput;
protected BufferedReader processError;
+ public ProcessTransport() throws IOException {
+ this(new TransportOptions());
+ }
+
public ProcessTransport(TransportOptions transportOptions) throws IOException {
this.transportOptionsAdapter = new TransportOptionsAdapter(transportOptions);
turnTimeoutMs = transportOptionsAdapter.getHandledTransportOptions().getTurnTimeoutMs();
@@ -56,6 +61,7 @@ public class ProcessTransport {
startErrorReading();
}
+ @Override
public void close() throws IOException {
if (processInput != null) {
processInput.close();
@@ -71,6 +77,12 @@ public class ProcessTransport {
}
}
+ @Override
+ public boolean isAvailable() {
+ return process != null && process.isAlive();
+ }
+
+ @Override
public String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException {
return inputWaitForOneLine(message, turnTimeoutMs);
}
@@ -106,6 +118,7 @@ public class ProcessTransport {
}
}
+ @Override
public void inputWaitForMultiLine(String message, Function callBackFunction) throws IOException {
inputWaitForMultiLine(message, callBackFunction, turnTimeoutMs);
}
@@ -132,6 +145,7 @@ public class ProcessTransport {
}
}
+ @Override
public void inputNoWaitResponse(String message) throws IOException {
log.debug("input message to agent: {}", message);
processInput.write(message);
diff --git a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/TransportOptionsAdapter.java b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/TransportOptionsAdapter.java
index 66113e8cd..4c6b7d48d 100644
--- a/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/TransportOptionsAdapter.java
+++ b/packages/sdk-java/src/main/java/com/alibaba/qwen/code/cli/transport/process/TransportOptionsAdapter.java
@@ -76,7 +76,9 @@ class TransportOptionsAdapter {
}
private TransportOptions addDefaultTransportOptions(TransportOptions userTransportOptions) {
- TransportOptions transportOptions = userTransportOptions.clone();
+ TransportOptions transportOptions = Optional.ofNullable(userTransportOptions)
+ .map(TransportOptions::clone)
+ .orElse(new TransportOptions());
if (StringUtils.isBlank(transportOptions.getPathToQwenExecutable())) {
transportOptions.setPathToQwenExecutable("qwen");
diff --git a/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/QwenCliTest.java b/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/QwenCliTest.java
new file mode 100644
index 000000000..01295ce6c
--- /dev/null
+++ b/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/QwenCliTest.java
@@ -0,0 +1,23 @@
+package com.alibaba.qwen.code.cli;
+
+import java.util.List;
+
+import com.alibaba.qwen.code.cli.protocol.message.Message;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
+
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class QwenCliTest {
+
+ private static final Logger log = LoggerFactory.getLogger(QwenCliTest.class);
+ @Test
+ void query() {
+ List result = QwenCli.query("hello world");
+ log.info("result: {}", result);
+ assertNotNull(result);
+ }
+}
diff --git a/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/session/SessionTest.java b/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/session/SessionTest.java
new file mode 100644
index 000000000..69898b948
--- /dev/null
+++ b/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/session/SessionTest.java
@@ -0,0 +1,58 @@
+package com.alibaba.qwen.code.cli.session;
+
+import java.io.IOException;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
+import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
+import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
+import com.alibaba.qwen.code.cli.session.event.SessionEventConsumers;
+import com.alibaba.qwen.code.cli.session.event.SessionEventSimpleConsumers;
+import com.alibaba.qwen.code.cli.session.exception.SessionCloseException;
+import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
+import com.alibaba.qwen.code.cli.session.exception.SessionStartException;
+import com.alibaba.qwen.code.cli.transport.Transport;
+import com.alibaba.qwen.code.cli.transport.process.ProcessTransport;
+
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class SessionTest {
+
+ private static final Logger log = LoggerFactory.getLogger(SessionTest.class);
+ @Test
+ void sendPrompt() throws IOException, SessionStartException, SessionSendPromptException, SessionCloseException {
+ Transport transport = new ProcessTransport();
+ Session session = new Session(transport);
+ session.sendPrompt("hello world", new SessionEventSimpleConsumers() {
+ @Override
+ public void onSystemMessage(SDKSystemMessage systemMessage) {
+ log.info("systemMessage: {}", systemMessage);
+ }
+
+ @Override
+ public void onResultMessage(SDKResultMessage resultMessage) {
+ log.info("resultMessage: {}", resultMessage);
+ }
+
+ @Override
+ public void onAssistantMessage(SDKAssistantMessage assistantMessage) {
+ log.info("assistantMessage: {}", assistantMessage);
+ }
+
+ @Override
+ public void onOtherMessage(String message) {
+ log.info("otherMessage: {}", message);
+ }
+ });
+ session.close();
+ }
+
+ @Test
+ void testJSON() {
+ String json = "{\"type\":\"assistant\",\"uuid\":\"ed8374fe-a4eb-4fc0-9780-9bd2fd831cda\",\"session_id\":\"166badc0-e6d3-4978-ae47-4ccd51c468ef\",\"message\":{\"content\":[{\"text\":\"Hello! How can I help you with the Qwen Code SDK for Java today?\",\"type\":\"text\"}],\"id\":\"ed8374fe-a4eb-4fc0-9780-9bd2fd831cda\",\"model\":\"qwen3-coder-plus\",\"role\":\"assistant\",\"type\":\"message\",\"usage\":{\"cache_read_input_tokens\":12766,\"input_tokens\":12770,\"output_tokens\":17,\"total_tokens\":12787}}}";
+ SDKAssistantMessage assistantMessage = JSON.parseObject(json, SDKAssistantMessage.class);
+ log.info("the assistantMessage: {}", assistantMessage);
+ }
+}
diff --git a/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransportTest.java b/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransportTest.java
index bdedf3047..721b203db 100644
--- a/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransportTest.java
+++ b/packages/sdk-java/src/test/java/com/alibaba/qwen/code/cli/transport/process/ProcessTransportTest.java
@@ -1,29 +1,86 @@
package com.alibaba.qwen.code.cli.transport.process;
import java.io.IOException;
+import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.TypeReference;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeRequest;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeResponse;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
+import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
+import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
+import com.alibaba.qwen.code.cli.transport.Transport;
import com.alibaba.qwen.code.cli.transport.TransportOptions;
import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
class ProcessTransportTest {
+ private static final Logger logger = LoggerFactory.getLogger(ProcessTransportTest.class);
+
@Test
void shouldStartAndCloseSuccessfully() throws IOException {
TransportOptions transportOptions = new TransportOptions();
- ProcessTransport processTransport = new ProcessTransport(transportOptions);
- processTransport.close();
+ Transport transport = new ProcessTransport(transportOptions);
+ transport.close();
}
@Test
void shouldInputWaitForOneLineSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
TransportOptions transportOptions = new TransportOptions();
- ProcessTransport processTransport = new ProcessTransport(transportOptions);
+ Transport transport = new ProcessTransport(transportOptions);
String message = "{\"type\": \"control_request\", \"request_id\": \"1\", \"request\": {\"subtype\": \"initialize\"} }";
- System.out.println(processTransport.inputWaitForOneLine(message));
+ System.out.println(transport.inputWaitForOneLine(message));
+ }
+
+ @Test
+ void shouldInitializeSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ Transport transport = new ProcessTransport();
+
+ String message = CLIControlRequest.create(new CLIControlInitializeRequest()).toString();
+ String responseMsg = transport.inputWaitForOneLine(message);
+ logger.info("responseMsg: {}", responseMsg);
+ CLIControlResponse response = JSON.parseObject(responseMsg,
+ new TypeReference>() {});
+ logger.info("response: {}", response);
+ }
+
+ @Test
+ void shouldSdkMessageSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ Transport transport = new ProcessTransport();
+ String message = CLIControlRequest.create(new CLIControlInitializeRequest()).toString();
+ transport.inputWaitForOneLine(message);
+
+ String sessionId = "session-" + UUID.randomUUID().toString();
+ String userMessage = new SDKUserMessage().setSessionId(sessionId).setContent("hello world").toString();
+ transport.inputWaitForMultiLine(userMessage, line -> {
+ return "result".equals(JSON.parseObject(line).getString("type"));
+ });
+
+ String userMessage2 = new SDKUserMessage().setSessionId(sessionId).setContent("请使用中文").toString();
+ transport.inputWaitForMultiLine(userMessage2, line -> {
+ return "result".equals(JSON.parseObject(line).getString("type"));
+ });
+
+ String userMessage3 = new SDKUserMessage().setSessionId(sessionId).setContent("当前工作区有多少个文件").toString();
+ transport.inputWaitForMultiLine(userMessage3, line -> {
+ return "result".equals(JSON.parseObject(line).getString("type"));
+ });
+
+ String userMessage4 = new SDKUserMessage().setSessionId("session-sec" + UUID.randomUUID()).setContent("有多少个xml文件").toString();
+ transport.inputWaitForMultiLine(userMessage4, line -> {
+ return "result".equals(JSON.parseObject(line).getString("type"));
+ });
+
+ transport.inputWaitForOneLine(CLIControlRequest.create(new CLIControlInitializeRequest()).toString());
+ transport.inputWaitForMultiLine(new SDKUserMessage().setContent("您好").toString(),
+ line -> "result".equals(JSON.parseObject(line).getString("type")));
}
}