Skip to main content
参考

SDK References - Java

Java SDK 的 Maven 坐标为 com.qoder.sdk:qoder-agent-sdk,包名为 com.qoder.sdk.*。运行环境为 JRE 8+,core 构件没有第三方运行时依赖。 SDK 在本机启动 qodercli 子进程执行任务。未指定 CLI 路径时,首次启动会下载本 SDK 版本锁定的 CLI,校验 SHA-256 后缓存复用。托管平台为 macOS arm64/x64、Linux x64/arm64(glibc 与 musl)和 Windows x64。Windows x64 要求 Windows 10 1809 / Windows Server 2019 及以上;SDK 不检查系统版本,更早的系统需要自行指定 CLI。也可以用 qoderCliPath 或环境变量 QODERCLI_PATH 指定已安装的 CLI;当前平台不在托管范围时(例如 Windows arm64),SDK 在 ~/.local/bin 和 PATH 中查找。完整查找顺序见 ManagedRuntimeOptions。 下文的声明代码块省略方法体和 import。安装和第一个可运行示例见快速开始,与 TypeScript、Python 的能力差异见语言差异,内置工具名见内置工具列表。

Entry Points

QoderClient

SDK 主入口,用于创建一次性查询、多轮会话和预热启动。线程安全,可以复用;关闭时级联关闭它创建的全部查询和会话。
public final class QoderClient implements AutoCloseable {
    public static Builder builder();
    public QoderQuery query(OneShotQueryRequest request);
    public CompletionStage<QoderSession> openSession(SessionOptions options);
    public CompletionStage<PreparedQuery> prepare(SessionOptions options);
    public SessionRepository sessions();
    public SessionRepository sessions(SessionOptions options);
    public CompletionStage<Path> installRuntime();
    public CompletionStage<Integer> pruneRuntimes();
    public CompletionStage<Void> closeAsync();
    public void close();
}
方法返回类型说明
builder()QoderClient.Builder创建构建器
query(OneShotQueryRequest)QoderQuery发起一次性查询;同步返回句柄,CLI 异步启动
openSession(SessionOptions)CompletionStage<QoderSession>打开多轮会话,初始化握手完成后返回
prepare(SessionOptions)CompletionStage<PreparedQuery>预先启动 CLI 并完成认证和初始化,不发送输入
sessions()SessionRepository按客户端默认存储和项目目录获取会话仓库
sessions(SessionOptions)SessionRepository按指定选项的 sessionStorage 和 workingDirectory 获取会话仓库,不启动 CLI;传 null 等同于 sessions()
installRuntime()CompletionStage<Path>只安装或复用托管 CLI,返回可执行文件路径,不启动 CLI,见 ManagedRuntimeOptions
pruneRuntimes()CompletionStage<Integer>删除锁定版本以外的托管 CLI,返回删除的目录数
closeAsync()CompletionStage<Void>异步级联关闭,幂等
close()void阻塞式关闭;在 SDK 线程或回调中调用的限制见线程模型与资源释放
参数错误和本地状态错误同步抛出,例如 null 参数、build() 校验失败。启动和运行中的失败通过返回的 CompletionStage 或消息流交付。SDK 自定义异常都继承 QoderException,Java 标准的参数和状态异常保持原类型。

QoderClient.Builder

方法参数类型说明
auth(Auth)Auth必填,认证方式
callbackExecutor(Executor)Executor权限、MCP、模型策略、认证 token 和 Memory 回调的执行器;Hook 使用 SDK 独立线程
diagnosticsListener(DiagnosticsListener)DiagnosticsListener客户端级诊断监听器
sessionStore(SessionStore)SessionStore客户端默认的外部会话存储,供会话仓库和所有会话使用;会话选项可以覆盖
projectDirectory(Path)Path会话仓库的项目目录,也是未设置 workingDirectory 的会话的默认工作目录
managedRuntime(ManagedRuntimeOptions)ManagedRuntimeOptions托管 CLI 的缓存目录、下载策略、镜像和超时;null 恢复默认
build()—构建客户端;未设置 auth 时抛出 InvalidOptionsException

QoderQuery

一次性查询句柄,由 QoderClient.query() 或 PreparedQuery.toQuery() 返回。
public final class QoderQuery implements AutoCloseable {
    public MessageStream messages();
    public CompletionStage<InitializationResult> initialization();
    public CompletionStage<InterruptResult> interrupt();
    public CompletionStage<Void> flushMemory();
    public CompletionStage<Void> refreshMemory();
    public CompletionStage<Void> closeAsync();
    public void close();
}
方法返回类型说明
messages()MessageStream获取消息流
initialization()CompletionStage<InitializationResult>初始化握手结果
interrupt()CompletionStage<InterruptResult>中断正在运行的轮次
flushMemory()CompletionStage<Void>等待已触发的 Memory 处理结束;完成不代表全部写入成功
refreshMemory()CompletionStage<Void>重新加载 Memory 消费配置
closeAsync() / close()CompletionStage<Void> / void关闭查询并释放资源
CLI 在本轮 result 之后退出,但仍应把消息流消费到结束,然后关闭句柄或游标。启用 Memory 时,ResultMessage 之后还可能收到 Memory 消息。ResultOutcome.ERROR 表示本轮失败,没有抛异常不等于成功。

QoderSession

多轮会话,由 QoderClient.openSession() 或 PreparedQuery.toSession() 返回。
public final class QoderSession implements AutoCloseable {
    public MessageStream messages();
    public CompletionStage<InitializationResult> initialization();
    public CompletionStage<MessageId> send(UserInput input);
    public MessageSubmission submit(UserInput input);
    public CompletionStage<Void> completeInput();
    public CompletionStage<Boolean> cancelQueuedMessage(MessageId id);
    public CompletionStage<InterruptResult> interrupt();
    public CompletionStage<Void> setPermissionMode(PermissionMode mode);
    public CompletionStage<PlanModeSnapshot> getPlanMode();
    public CompletionStage<PlanModeSnapshot> setPlanMode(boolean active);
    public CompletionStage<Void> setPermissionModePreservingPlan(PermissionMode mode);
    public CompletionStage<Void> setProxy(ProxySettings proxy);
    public CompletionStage<Void> addDirectories(List<Path> directories);
    public CompletionStage<Void> applyFlagSettings(FlagSettings settings);
    public CompletionStage<SessionTitle> generateSessionTitle();
    public CompletionStage<SessionTitle> generateSessionTitle(String description);
    public CompletionStage<Void> seedReadState(ReadStateSeed seed);
    public CompletionStage<List<String>> supportedCommands();
    public CompletionStage<List<CommandInfo>> commandCatalog();
    public CompletionStage<Optional<ModelCatalogSnapshot>> modelCatalogSnapshot();
    public CompletionStage<List<CommandInfo>> reloadSkills();
    public CompletionStage<List<ModelInfo>> getAvailableModels();
    public CompletionStage<List<ModelInfo>> getAvailableModels(ModelCatalogOptions options);
    public CompletionStage<Boolean> backgroundTasks();
    public CompletionStage<Boolean> backgroundTasks(String toolUseId);
    public CompletionStage<Void> stopTask(TaskId taskId);
    public CompletionStage<Void> reloadPlugins();
    public CompletionStage<List<McpServerStatus>> mcpServerStatus();
    public CompletionStage<McpAuthenticationResult> beginMcpAuthentication(String serverName);
    public CompletionStage<McpAuthenticationResult> beginMcpAuthentication(String serverName, McpAuthenticationOptions options);
    public CompletionStage<Void> mcpSubmitOAuthCallbackUrl(String serverName, String callbackUrl);
    public CompletionStage<Void> rewindHistory(MessageId toMessageId);
    public CompletionStage<RewindFilesResult> rewindFiles(MessageId messageId, RewindFilesOptions options);
    public CompletionStage<Void> flushMemory();
    public CompletionStage<Void> refreshMemory();
    public CompletionStage<Void> closeAsync();
    public void close();
}
方法返回类型说明
messages()MessageStream获取消息流,只能消费一次
initialization()CompletionStage<InitializationResult>初始化握手结果,包括 CLI 能力和版本
send(UserInput)CompletionStage<MessageId>输入写入 CLI 后完成;不表示回答结束
submit(UserInput)MessageSubmission同步返回分配的 MessageId,写入结果通过 written() 观察;需要在写入前取得 ID 时使用
completeInput()CompletionStage<Void>拒绝后续输入,等待已接受的输入写完;不等待回答
cancelQueuedMessage(MessageId)CompletionStage<Boolean>取消还没写入 CLI 的排队输入;已开始写入时,CLI 支持对应能力才会尝试远端取消,否则返回 false
interrupt()CompletionStage<InterruptResult>中断当前轮次,会话保持可用
setPermissionMode(PermissionMode)CompletionStage<Void>运行中切换权限模式;切到 bypass 需要会话已设置 allowDangerouslySkipPermissions(true)
getPlanMode() / setPlanMode(boolean)CompletionStage<PlanModeSnapshot>查询或切换独立 Plan 状态,返回 CLI 确认的值,见独立 Plan
setPermissionModePreservingPlan(PermissionMode)CompletionStage<Void>切换权限模式并保留 Plan 状态;不接受 PLAN
setProxy(ProxySettings)CompletionStage<Void>设置 CLI 的网络代理
addDirectories(List<Path>)CompletionStage<Void>允许 CLI 访问额外目录
applyFlagSettings(FlagSettings)CompletionStage<Void>应用特性开关
generateSessionTitle([String])CompletionStage<SessionTitle>让 CLI 生成会话标题,可附带描述
seedReadState(ReadStateSeed)CompletionStage<Void>告诉 CLI 哪些文件用户已经看过;本地校验全部通过后才发送
supportedCommands()CompletionStage<List<String>>当前命令目录中的命令名,不发请求
commandCatalog()CompletionStage<List<CommandInfo>>当前命令目录,不发请求
modelCatalogSnapshot()CompletionStage<Optional<ModelCatalogSnapshot>>CLI 最近提供的模型目录;没收到过时为空,不发请求
reloadSkills()CompletionStage<List<CommandInfo>>让 CLI 重新加载 skills,返回 skills 列表
getAvailableModels([ModelCatalogOptions])CompletionStage<List<ModelInfo>>向 CLI 查询可用模型,可选择是否使用缓存,见 ModelCatalogOptions
backgroundTasks([String])CompletionStage<Boolean>把前台任务(或指定 tool use 的任务)移到后台,返回是否成功
stopTask(TaskId)CompletionStage<Void>停止后台任务
reloadPlugins()CompletionStage<Void>让 CLI 重新加载插件
mcpServerStatus()CompletionStage<List<McpServerStatus>>全部 MCP 服务器的状态
beginMcpAuthentication(String[, McpAuthenticationOptions])CompletionStage<McpAuthenticationResult>为 MCP 服务器发起 OAuth,返回结果或需要用户打开的授权 URL
mcpSubmitOAuthCallbackUrl(String, String)CompletionStage<Void>把完整的 OAuth 回调 URL 交给 CLI,由 CLI 校验、换取凭据并重连
rewindHistory(MessageId)CompletionStage<Void>把对话历史回退到指定消息
rewindFiles(MessageId, RewindFilesOptions)CompletionStage<RewindFilesResult>把检查点跟踪的文件恢复到指定消息时的状态,见 Checkpoint
flushMemory() / refreshMemory()CompletionStage<Void>同 QoderQuery
closeAsync() / close()CompletionStage<Void> / void关闭会话并释放资源
部分方法需要 CLI 支持对应能力,否则以 UnsupportedCliCapabilityException 失败,不会发送请求,见能力要求。关闭会话时,还没写入的排队输入以 SessionClosedException 结束;需要确保已接受的输入写入 CLI,先等待 completeInput()。

运行时目录

方法数据来源是否发请求
commandCatalog() / supportedCommands()初始化时的命令目录;CLI 推送命令变更后整体替换否
modelCatalogSnapshot()初始化或 CLI 推送的最新完整模型目录否
getAvailableModels(...)本次查询结果,不更新上面的快照是
reloadSkills()重新加载后的 skills 列表,不替换完整命令目录是
推送的目录总是整体替换旧值,空列表表示 CLI 明确清空了目录。modelCatalogSnapshot() 返回 Optional.empty() 表示还没收到目录。需要当前账号最新的模型列表时,调用 getAvailableModels(...)。

CommandInfo

命令或 skill 的元数据,位于 com.qoder.sdk.command。
public final class CommandInfo {
    public static CommandInfo of(String name, JsonObject raw);
    public String name();
    public Optional<String> description();
    public Optional<String> argumentHint();
    public JsonObject raw();
}
name() 一定有值;raw() 保留 CLI 返回的全部字段,包括 SDK 未建模的字段。

PreparedQuery

已完成启动、认证和初始化的 CLI,由 QoderClient.prepare() 返回,用于消除首次请求的启动延迟。只能使用一次:转为查询、转为会话或直接关闭。
public final class PreparedQuery implements AutoCloseable {
    public CompletionStage<InitializationResult> initialization();
    public QoderQuery toQuery(UserInput input);
    public QoderSession toSession();
    public CompletionStage<Void> closeAsync();
    public void close();
}
方法返回类型说明
initialization()CompletionStage<InitializationResult>初始化握手结果
toQuery(UserInput)QoderQuery转为一次性查询
toSession()QoderSession转为多轮会话
closeAsync() / close()CompletionStage<Void> / void丢弃未使用的预热启动
已转换或已关闭后再转换会抛出 IllegalStateException。转换后,资源由新的 QoderQuery 或 QoderSession 负责释放,关闭 PreparedQuery 不再影响它们。toQuery() 的输入被拒绝时,返回的查询的消息流以该错误结束。

MessageStream

查询或会话的消息流,支持两种互斥的消费方式:用 openCursor() 拉取,或用 consume() 由 SDK 推送。
public final class MessageStream {
    public MessageCursor openCursor();
    public CompletionStage<Void> consume(MessageHandler handler, Executor executor);
}
方法返回类型说明
openCursor()MessageCursor打开拉取游标,按顺序逐条读取
consume(MessageHandler, Executor)CompletionStage<Void>在给定执行器上按顺序逐条调用处理器;上一条处理完才投递下一条
consume() 返回的 stage 在流结束时正常完成。以下情况异常完成,并关闭所属的查询或会话:
情况异常
处理器抛出异常CallbackExecutionException
执行器拒绝任务CallbackRejectedException
消费线程被中断ConsumerInterruptedException
读取或解码消息失败流的失败原因;非 SDK 异常包装为错误码 MESSAGE_DELIVERY_FAILED
每个流只能消费一次,openCursor() 和 consume() 二选一;第二次订阅抛出 MessageStreamAlreadyConsumedException,第一次订阅即使失败也算已消费。CLI 停止输出后,已缓冲的消息仍可读取。

MessageCursor

拉取游标。
public interface MessageCursor extends AutoCloseable {
    boolean hasNext();
    SdkMessage next();
    void close();
}
方法返回类型说明
hasNext()boolean阻塞等待下一条消息;流结束返回 false,流已失败时抛出失败原因
next()SdkMessage返回下一条消息;流已结束时抛出 NoSuchElementException
close()void关闭游标,同时异步关闭所属的查询或会话
多轮会话应跨轮复用同一个游标,每轮读到 ResultMessage 即可发送下一轮,不要每轮关闭再重开。某条消息读取失败后流就此终止,之后每次 hasNext() / next() 都抛出同一个异常;此时关闭游标,需要时用 resume 新建会话。消费线程被中断时抛出 ConsumerInterruptedException。

MessageHandler

推送模式的消息处理器。
@FunctionalInterface
public interface MessageHandler {
    void onMessage(SdkMessage message) throws Exception;
}
处理器抛出的异常使 consume() 返回的 stage 以 CallbackExecutionException 完成。使用直接执行器(如 Runnable::run)时,处理器运行在 SDK 的投递线程上,必须尽快返回。

线程模型与资源释放

  1. 异步方法返回 CompletionStage,每次调用得到独立的观察结果。取消或手工完成 toCompletableFuture() 只影响这一次等待,不会取消 SDK 操作。要停止操作,使用 interrupt()、cancelQueuedMessage() 或 closeAsync()。
  2. openSession() 和 prepare() 返回的句柄一旦交付就归应用所有,即使从未调用 get(),也要负责关闭。观察 future 在交付前已被取消时,SDK 会自动关闭启动好的资源。
  3. 每个查询、会话和预热启动各占一个 CLI 子进程,应用应限制并发数量。QoderClient 本身开销很小,可以复用;关闭客户端会级联关闭它创建的全部资源。
  4. 权限、MCP、模型策略、认证 token 和 Memory 回调在 callbackExecutor 上运行,Hook 使用 SDK 独立线程。未配置 callbackExecutor 时,同一会话的这些回调在一条 SDK 线程上串行执行,一个回调阻塞会推迟其他回调,可能导致超时。回调应尽快返回,耗时工作交给应用自己的执行器,通过返回的 stage 报告完成。
  5. 不要在 SDK 回调、推送处理器或 SDK 线程中阻塞等待;在这些位置关闭所属会话或客户端时,调用 closeAsync() 后直接返回。在 SDK 自有线程或推送处理器中调用阻塞式 close(),会先发起异步关闭,再抛出 IllegalBlockingCloseException。SDK 自有线程包括未配置 callbackExecutor 时运行回调的线程,以及运行 Hook 和诊断监听器的线程;会话或查询的 close() 只检查它自己的这些线程,客户端的 close() 检查它创建的全部会话和查询。推送处理器即使运行在应用执行器上也会抛出。配置了自定义 callbackExecutor 时,回调运行在应用线程上,在其中调用 close() 不会抛出该异常,而是阻塞到关闭完成。
  6. closeAsync() 完成表示 SDK 已停止接收输入,排队输入和控制请求已经结算,CLI 进程已走完关闭流程(关闭 stdin,超时后依次请求终止和强制终止)。它不等待已在运行的应用回调或 continuation 返回,也不保证 CLI 的后代进程已退出、临时文件已删除。启用 Memory 时,空闲会话关闭前会先完成 Memory 收尾,最长等待 drainTimeoutMs(默认 60 秒)。

Configuration

SessionOptions

查询和会话的不可变选项,用于 openSession()、prepare() 和 OneShotQueryRequest。
public final class SessionOptions {
    public static Builder builder();
    public static SessionOptions defaults();   // 等同于 builder().build()
    public Builder toBuilder();                // 以当前值预填新的 Builder
    // 每个 Builder 设置项都有同名 getter,例如 workingDirectory()、maxTurns();
    // resume(String) 对应 resumeSessionId()。未设置的可选项返回 Optional.empty()。
}

SessionOptions.Builder

分组方法默认值说明
RuntimeworkingDirectory(Path)客户端 projectDirectory,其次进程当前目录CLI 工作目录
RuntimeqoderCliPath(Path)托管 CLI显式指定 CLI 可执行文件;设置后不下载托管 CLI,路径无效直接失败
RuntimeputEnvironment(String, String)—添加一个传给 CLI 的环境变量;代理 URL 变量自动按机密处理
RuntimeputProxyEnvironment(String, SecretValue)—以 SecretValue 添加代理 URL 变量;变量名必须是可识别的代理变量(如 HTTPS_PROXY),否则抛 IllegalArgumentException
Runtimeenvironment(Map<String, String>)—批量添加环境变量,追加而不是替换
PromptsystemPrompt(String)不替换替换系统提示词
PromptappendSystemPrompt(String)不追加追加到系统提示词末尾
PromptcustomContext(Map<String, String>)未设置本会话输入的默认自定义上下文,见 UserInput
PromptmaxTurns(int)不限制最大轮次,必须为正数
PromptincludePartialMessages(boolean)false投递增量的 PartialMessage,见流式输出
ModelmodelSelection(ModelSelection)CLI 默认模型固定模型、动态回调或结构化策略,见 ModelSelection
Toolstools(List<String>)CLI 默认基础工具列表;空列表等同于未设置
ToolsallowedTools(List<String>)—预授权放行的工具
ToolsdisallowedTools(List<String>)—禁止的工具,优先级高于 allowedTools
PermissionpermissionMode(PermissionMode)由 CLI 决定权限模式,见 PermissionMode
PermissionplanMode(boolean)未设置独立 Plan 的启动状态;与 permissionMode(PLAN) 互斥,见独立 Plan
PermissionallowDangerouslySkipPermissions(boolean)falseBYPASS_PERMISSIONS 的第二道开关
PermissioncanUseTool(ToolPermissionHandler)无工具权限回调,见 ToolPermissionHandler
MCPmcpServers(List<McpServerConfig>)—MCP 服务器配置,名称不能重复,见 McpServerConfig
MCPallowedMcpServerNames(List<String>)—允许使用的 MCP 服务器名称
MCPonElicitation(ElicitationHandler)无MCP elicitation 回调,见 ElicitationHandler
Hookshooks(List<HookRegistration>)—Hook 注册,见 HookRegistration
HooksincludeHookEvents(boolean)false在消息流中投递 Hook 通知消息;不影响 Hook 回调本身
Sessionresume(String)—按会话 ID 恢复;ID 只能包含 ASCII 字母、数字、_ 和 -
SessioncontinueSession(boolean)false继续最近一次会话;配置了可列举的外部存储时,按存储中的时间戳选择
SessionforkSession(boolean)false与 resume 或 continueSession 配合,分叉为新会话
SessionsessionStorage(SessionStorage)SessionStorage.inherit()继承客户端设置、使用 CLI 本地存储或外部存储,见 SessionStorage
SessionsessionMirrorOptions(SessionMirrorOptions)defaults()外部存储镜像的批量和等待上限,见 SessionMirrorOptions
SessionfileCheckpointing(boolean)false启用文件检查点,见 Checkpoint
Memorymemory(MemoryOptions)不启用原生或应用托管的 Memory,见 Memory
ControlcontrolRequestTimeoutMs(long)60000控制请求超时(毫秒),0 表示不限制,负数非法
ControlresolveModelTimeoutMs(long)500动态模型回调的超时(毫秒),0 表示不限制,负数非法
Controldebug(boolean)false开启 CLI 调试输出
ControldiagnosticsListener(DiagnosticsListener)—会话级诊断监听器
两个默认值也以常量提供:Builder.DEFAULT_CONTROL_REQUEST_TIMEOUT_MS(60000L)、Builder.DEFAULT_RESOLVE_MODEL_TIMEOUT_MS(500L)。 maxTurns() 收到非正数时立即抛出 IllegalArgumentException。build() 遇到以下情况抛出 InvalidOptionsException:
  • BYPASS_PERMISSIONS 未同时设置 allowDangerouslySkipPermissions(true)。
  • 外部 sessionStorage 与 fileCheckpointing(true) 同时设置。
  • planMode(...) 与 permissionMode(PLAN) 同时设置,包括 planMode(false)。
  • resume 与 continueSession 同时设置;forkSession 未配合 resume 或 continueSession;resume 的 ID 含非法字符。
  • 环境变量名为空、含 = 或 NUL,或值含 NUL。
  • mcpServers 含 null 或重名服务器。
build() 成功不代表 CLI 支持所选功能。planMode、customContext 以及外部存储上的 forkSession 等需要 CLI 能力的设置,在启动或发送时检查,见能力要求。 CLI 子进程默认只继承 PATH,其他变量需要显式传入,见子进程环境。

OneShotQueryRequest

一次性查询的请求,包含一条 UserInput 和一组 SessionOptions。
public final class OneShotQueryRequest {
    public static OneShotQueryRequest of(String prompt);
    public static Builder builder();
    public UserInput input();
    public SessionOptions options();

    public static final class Builder {
        public Builder prompt(String prompt);
        public Builder input(UserInput input);
        public Builder options(SessionOptions options);
        public OneShotQueryRequest build();
    }
}
of(prompt) 使用单条文本输入和默认选项。Builder 的 prompt() 和 input() 只能设置其中一个,重复设置抛出 InvalidOptionsException;两者都没设置时 build() 抛出同一异常。未设置 options() 时使用默认选项。

UserInput

发给 CLI 的一条用户输入,包含内容块、优先级、是否触发回复,以及可选的自定义上下文和 skill 选择。不可变。
public final class UserInput {
    public static UserInput text(String text);
    public static Builder builder();
    public List<Content> content();
    public SendPriority priority();
    public boolean shouldQuery();
    public Optional<Map<String, String>> customContext();
    public Optional<Boolean> clientComposed();
    public List<SelectedSkill> selectedSkills();
}
方法返回类型说明
text(String)UserInput单个文本块,优先级 NOW,shouldQuery 为 true
content()List<Content>内容块列表,不为空
priority()SendPriority优先级提示
shouldQuery()boolean是否触发回复;false 时只补充上下文
customContext()Optional<Map<String, String>>本条输入的自定义上下文
clientComposed()Optional<Boolean>是否声明客户端已处理过 slash 命令
selectedSkills()List<SelectedSkill>本条输入显式选择的 skills,默认为空

UserInput.Builder

方法说明
addText(String)添加文本块
addImage(String mediaType, String base64Data)添加 base64 图片块,例如 image/png
addImageUrl(URI)添加 URL 图片块,必须是带主机名的绝对 HTTP(S) 地址;SDK 不下载图片
addToolResult(String toolUseId, String content)添加工具结果块,回应先前的工具调用
customContext(Map<String, String>)本条输入的完整上下文,覆盖会话默认值;空 map 表示清空
clientComposed(boolean)true 表示客户端已处理 slash 命令,CLI 不再解析开头的 /
selectedSkills(List<SelectedSkill>)本条输入显式选择的 skills
priority(SendPriority)优先级,默认 NOW
shouldQuery(boolean)是否触发回复,默认 true
build()构建;没有任何内容块时抛出 IllegalStateException
内容块类型 UserInput.Content 是封闭层次,与输出侧的 ContentBlock 相互独立:
类型访问器
UserInput.TextContentString text()
UserInput.ImageContentString mediaType()、String base64Data()
UserInput.UrlImageContentURI url()
UserInput.ToolResultContentString toolUseId()、String content()
customContext 未设置时继承 SessionOptions 的默认值;显式设置时完整替换默认值,不做合并。显式设置的 customContext(包括空 map)和非空的 selectedSkills 需要 CLI 支持对应能力,见能力要求。会话中被拒绝的输入由 send() 的 stage 报告;查询和 PreparedQuery.toQuery() 的输入被拒绝时,消息流以该错误结束。

SelectedSkill

本条输入显式选择的 skill。
public final class SelectedSkill {
    public static SelectedSkill of(String name);
    public static SelectedSkill of(String name, String arguments);
    public String name();
    public Optional<String> arguments();
}
名称不能为空或全是空白;arguments 原样传给 CLI。

ReadStateSeed

传给 seedReadState(),告诉 CLI 宿主应用已向用户展示过哪些文件。
public final class ReadStateSeed {
    public ReadStateSeed(List<String> readFiles);
    public static ReadStateSeed of(String path, long mtime);
    public List<String> readFiles();
}
列表构造器在应用种子时,按会话工作目录解析相对路径并读取文件当前的 mtime;of(path, mtime) 使用宿主已观测到的 mtime(epoch 毫秒)。只为实际展示过的文件设置;该调用不会把文件内容加入输入。

SendPriority

用户输入的优先级提示。SDK 不按优先级重排写入顺序,调度由 CLI 决定。
public enum SendPriority { NOW, NEXT, LATER }
值说明
NOW尽快处理(默认)
NEXT排在当前处理的消息之后
LATER排在所有已排队消息之后

FlagSettings

传给 applyFlagSettings() 的特性开关。
public final class FlagSettings {
    public FlagSettings(Map<String, Boolean> flags);
    public Map<String, Boolean> flags();
}
flags 是开关名到布尔值的映射,必填;flags() 返回不可变副本。

ProxySettings

传给 setProxy() 的代理设置。
public final class ProxySettings {
    public static ProxySettings of(SecretValue proxyUrl);
    public static ProxySettings clear();
    public SecretValue proxyUrlSecret();
    @Deprecated public ProxySettings(String proxyUrl);
    @Deprecated public String proxyUrl();
}
用 ProxySettings.of(SecretValue.of(url)) 设置代理,clear() 清除代理。代理 URL 及其中的用户名、密码按机密登记,SDK 在诊断信息中对其脱敏。已弃用的 String 构造器和 proxyUrl() 会暴露明文,不要用于日志。

ManagedRuntimeOptions

托管 CLI 的配置,通过 QoderClient.Builder.managedRuntime() 设置。作用于未指定 qoderCliPath 的启动和 installRuntime()。
public final class ManagedRuntimeOptions {
    public static final long DEFAULT_IO_TIMEOUT_MS = 30_000L;
    public static final long DEFAULT_TOTAL_TIMEOUT_MS = 600_000L;

    public enum DownloadPolicy { ALLOW, DISALLOW }

    public static Builder builder();
    public static ManagedRuntimeOptions defaults();
    public Builder toBuilder();
    public Optional<Path> cacheDirectory();
    public DownloadPolicy downloadPolicy();
    public Optional<URI> baseUrl();
    public Optional<SecretValue> authorization();
    public Optional<Path> localArchive();
    public long connectTimeoutMs();
    public long readTimeoutMs();
    public long totalTimeoutMs();
}
Builder 方法默认值说明
cacheDirectory(Path)~/.qoder/runtimes/managed缓存目录,应位于本地磁盘;null 恢复默认
downloadPolicy(DownloadPolicy)ALLOWDISALLOW 时从不联网,缓存和本地压缩包都没有时失败
baseUrl(URI)官方下载地址镜像地址,必须是不含用户信息的绝对 https 地址(回环地址可用 http)
authorization(SecretValue)—访问镜像时的 Authorization 头;只发给配置的主机,重定向到其他主机时不转发
localArchive(Path)—本地的锁定压缩包,优先于下载使用
connectTimeoutMs(long) / readTimeoutMs(long)30000连接和单次读取超时(毫秒),必须为正数
totalTimeoutMs(long)600000整个下载的超时(毫秒),必须为正数
build()—超时不为正数或 baseUrl 不合法时抛出 InvalidOptionsException
CLI 查找顺序。 启动时按以下顺序确定 CLI,找到即停止:
  1. SessionOptions.Builder.qoderCliPath() 指定的路径;路径无效直接失败。
  2. 环境变量 QODERCLI_PATH。
  3. 托管 CLI:复用缓存,否则依次从 localArchive、classpath 资源 META-INF/qoder-sdk/runtime/<文件名> 或下载地址安装。当前平台不在托管范围时跳过;安装失败直接报错,不会改用其他 CLI。
  4. ~/.qoder/runtimes/ 下旧版 SDK 留下的同版本 CLI。
  5. ~/.local/bin/qodercli,然后是 PATH。
每个 SDK 版本只使用它锁定的那一个 CLI 版本,不查询最新版本,也不自动升级。任何来源的压缩包都必须与内置的字节数和 SHA-256 一致,否则安装失败。
预热与离线。
场景做法
本机开发使用默认配置,首次启动自动下载
构建镜像时预热在镜像构建阶段运行 java -cp qoder-agent-sdk-VERSION.jar com.qoder.sdk.RuntimeInstaller [--cache-dir <dir>](VERSION 为应用依赖的 SDK 版本),运行时不再下载
随应用携带压缩包把锁定的压缩包放进 classpath 的 META-INF/qoder-sdk/runtime/<文件名>,或用 localArchive(Path) 指向本地文件;同时设置 downloadPolicy(DISALLOW) 确保不联网
企业镜像baseUrl(URI) 指向镜像,镜像按 <CLI 版本>/<文件名> 的布局提供相同字节的文件;需要认证时设置 authorization(SecretValue)
自管 CLI用 qoderCliPath(Path) 或 QODERCLI_PATH 指定
RuntimeInstaller 是命令行入口,支持 --cache-dir、--base-url、--local-archive、--no-download、--prune、--print-url;成功时打印可执行文件路径并以 0 退出,失败以 1 退出。镜像认证只能通过代码设置,命令行不接受凭据。 下载按 HTTPS_PROXY、ALL_PROXY、NO_PROXY 环境变量选择代理,只支持 http:// 代理。下载被禁止且没有可用来源、网络失败、超时或校验不符时,抛出 RuntimeInstallException。 installRuntime() 适合在部署阶段预热缓存或提前暴露安装问题;当前平台不在托管范围时以 RuntimeInstallException 失败。pruneRuntimes() 删除其他 CLI 版本以及本平台的其他摘要目录,跳过其他进程正在安装的目录;执行前应停止所有共用该缓存的 SDK 进程启动新会话。会话关闭不会删除缓存。

Authentication

Auth

认证策略,传给 QoderClient.Builder.auth(...)。包 com.qoder.sdk.auth。构造器不公开,只能通过静态工厂获得实例,实例不可变。
public abstract class Auth {
    public static Auth qoderCli();
    public static Auth accessToken(SecretValue token);
    public static Auth accessTokenEnv();
    public static Auth accessTokenEnv(String environmentVariable);
    public static Auth serviceAccountKey(SecretValue key);
    public static Auth serviceAccountTokenProvider(ServiceAccountTokenProvider provider);
    public static Auth jobTokenProvider(JobTokenProvider provider);
}
工厂方法凭据来源典型场景
qoderCli()复用本机 CLI 已保存的登录状态开发者本机
accessToken(SecretValue)调用方传入的 access tokenCI、脚本、代表用户运行的服务
accessTokenEnv()启动时读取环境变量 QODER_PERSONAL_ACCESS_TOKENCI、容器部署
accessTokenEnv(String)启动时读取调用方指定的环境变量自定义密钥注入
serviceAccountKey(SecretValue)Service Account Key,由 SDK 和 CLI 换取短期 token组织内无人值守服务
serviceAccountTokenProvider(ServiceAccountTokenProvider)宿主按需刷新的 Service Account token,长期密钥不进入 SDK自建 token 基础设施
jobTokenProvider(JobTokenProvider)宿主按作业提供的 token多租户作业运行器
启动时的认证配置通过仅当前账号可读的临时文件交给 CLI,不放进启动参数或环境变量。java.io.tmpdir 及其各级上级目录必须不允许其他账号替换其中的条目,否则启动以 UnsupportedSecureSecretTransportException 失败;需要时用启动参数 -Djava.io.tmpdir= 指定当前账号独占的目录。CLI 因认证失败退出时抛出 AuthenticationException,先修正凭据或账号权限,再重新创建 Query 或 Session,直接重试同一凭据没有意义。 各方式的完整用法见认证。

ServiceAccountTokenProvider

由宿主刷新短期 Service Account token 的回调。宿主自己保管长期密钥。
public interface ServiceAccountTokenProvider {
    CompletionStage<SecretValue> fetchServiceAccountToken();
}
返回以新 token 完成的 stage。CLI 需要凭据时可能多次调用。token 经 SDK 控制通道交给 CLI,不要写入日志。

JobTokenProvider

按作业提供 token 的回调。SDK 不向回调传入作业 ID,应用在闭包里绑定自己的作业身份。
public interface JobTokenProvider {
    CompletionStage<SecretValue> fetchJobToken();
}
返回以 token 完成的 stage,不要返回 null。CLI 需要时可能多次调用。

SecretValue

SDK 管控的密钥值。toString() 恒为 [REDACTED],equals 和 hashCode 的耗时只与长度有关。
public final class SecretValue {
    public static SecretValue of(char[] value);
    public static SecretValue of(String value);
    public char[] reveal();
    public int length();
}
方法返回类型说明
of(char[])SecretValue复制传入数组后创建
of(String)SecretValue由字符串创建
reveal()char[]返回明文副本,调用方用完后自行清零
length()int密钥长度
SDK 会对诊断事件和 stderr 中出现的已登记凭据做脱敏。这一机制不覆盖业务内容和第三方输出,应用仍需按自己的日志策略控制记录范围。

子进程环境

CLI 子进程默认只继承父进程的 PATH。Windows 上另外继承 SystemRoot、TEMP、TMP、USERPROFILE、APPDATA、LOCALAPPDATA 等系统目录变量。CLI 需要的 HOME、语言环境、配置目录等变量用 SessionOptions.Builder 的 putEnvironment(String, String) 或 environment(Map) 显式传入;代理地址用 putProxyEnvironment(String, SecretValue) 传入,SDK 会对其中的凭据脱敏。SDK 管理的凭据变量会被剥除,不能用环境变量代替 Auth。

Permissions

ToolPermissionHandler

工具权限回调,通过 SessionOptions.Builder.canUseTool(...) 注册。包 com.qoder.sdk.permission。CLI 需要询问某次工具调用时调用它;未注册时,CLI 按自身规则处理需要询问的调用。
@FunctionalInterface
public interface ToolPermissionHandler {
    CompletionStage<PermissionResult> canUseTool(ToolPermissionRequest request,
                                                 CancellationToken cancellation)
            throws Exception;
}
参数类型说明
requestToolPermissionRequest待裁决的工具调用
cancellationCancellationTokenCLI 放弃该请求、请求超时或会话关闭时取消
返回以 PermissionResult 完成的 stage,不得为 null。回调在 callbackExecutor 上执行,见线程模型与资源释放。回调抛出异常或 stage 异常完成时,只有这一次权限请求以 CALLBACK_EXECUTION_FAILED 失败,会话不受影响;超过 controlRequestTimeoutMs 未完成时以 CONTROL_REQUEST_TIMEOUT 失败,令牌同时被取消。用法见权限控制和审批与用户输入。

ToolPermissionRequest

一次工具权限请求。
public final class ToolPermissionRequest {
    public String toolName();
    public JsonValue toolInput();
    public List<JsonValue> suggestions();
    public Optional<String> toolUseId();
    public Optional<String> agentId();
    public Optional<String> title();
    public Optional<String> displayName();
    public Optional<String> description();
    public Optional<String> blockedPath();
    public Optional<String> decisionReason();
    public Optional<String> decisionReasonType();
    public Optional<String> permissionKind();
    public Optional<Boolean> classifierApprovable();
    public List<PermissionOption> options();
    public Optional<PermissionDetails> details();
    public JsonObject raw();
}
方法返回类型说明
toolName()String工具名
toolInput()JsonValue工具输入参数
suggestions()List<JsonValue>CLI 给出的权限建议,例如更新权限规则
toolUseId()Optional<String>本次工具调用的 ID
agentId()Optional<String>发起调用的 Agent
title() / displayName() / description()Optional<String>供审批界面展示的标题、名称和描述
blockedPath()Optional<String>触发询问的路径
decisionReason() / decisionReasonType()Optional<String>CLI 需要询问的原因及其类型
permissionKind()Optional<String>权限种类,开放字符串
classifierApprovable()Optional<Boolean>CLI 提示,不代表应用已批准
options()List<PermissionOption>CLI 提供的审批选项,不可变
details()Optional<PermissionDetails>附加详情
raw()JsonObject完整请求
可选字段缺失或类型不符时返回空 Optional。SDK 不把权限种类、选项结果和详情类型映射成封闭枚举,未知值从 raw() 读取。

PermissionOption 和 PermissionDetails

PermissionOption 是 CLI 提供的一个审批选项。
方法返回类型说明
outcome()Optional<String>选项结果,保留未知值,对应 PermissionScope.outcome()
name()Optional<String>展示名称
allowed()boolean原字段明确为 true 时才可选,缺失或格式错误按 false 处理
raw()JsonObject完整选项
PermissionDetails 是开放的详情模型,用于 repeated_tool_call 等提示。
方法返回类型说明
type() / title() / toolName() / inputPreview()Optional<String>详情类型、标题、工具名、输入预览
repeatCount() / threshold()Optional<Long>重复次数和阈值;非整数或越界时为空
raw()JsonObject完整对象
工具输入、预览和 raw() 可能包含业务数据,不要默认写入日志。

PermissionScope

显式审批范围,通过 PermissionResult.withScope(...) 使用。
public enum PermissionScope {
    ONCE, SESSION, PERSIST;
    public String wireValue();
    public String outcome();
}
常量含义outcome()
ONCE只批准本次调用proceed_once
SESSION本次 CLI 会话内始终批准proceed_always
PERSIST请求 CLI 持久保存批准proceed_always_and_save
PERSIST 由 CLI 负责保存,SDK 不自建权限存储,也不保证所有权限种类都支持。

PermissionResult

权限回调的裁决:允许(可改写工具输入)或拒绝(附原因)。
public final class PermissionResult {
    public static PermissionResult allow();
    public static PermissionResult allow(JsonValue updatedInput);
    public static PermissionResult deny(String reason);
    public PermissionResult withScope(PermissionScope scope);
    public boolean isAllowed();
    public Optional<JsonValue> updatedInput();
    public Optional<String> denyReason();
    public Optional<PermissionScope> scope();
}
方法返回类型说明
allow()PermissionResult允许,使用原始工具输入
allow(JsonValue)PermissionResult允许,CLI 改用给定的输入
deny(String)PermissionResult拒绝,原因会交给 CLI
withScope(PermissionScope)PermissionResult返回带显式范围的新 allow;对 deny 调用抛 IllegalStateException
isAllowed()boolean是否允许
updatedInput()Optional<JsonValue>改写后的输入
denyReason()Optional<String>拒绝原因
scope()Optional<PermissionScope>显式范围;未调用 withScope 时为空,CLI 按 ONCE 处理
请求带 options() 时,允许决定(包括未指定范围的 allow())必须对应一个 allowed() 为 true 的选项,否则本次权限请求失败,不会发送允许。CLI 取消请求后,迟到的裁决不再发送。

PermissionMode

权限模式。它是 final 类而不是 enum,未知值原样保留,以兼容新版 CLI 增加的模式。
public final class PermissionMode {
    public static final PermissionMode DEFAULT;
    public static final PermissionMode ACCEPT_EDITS;
    public static final PermissionMode BYPASS_PERMISSIONS;
    public static final PermissionMode PLAN;
    public static final PermissionMode DONT_ASK;
    public static final PermissionMode AUTO;
    public static PermissionMode of(String value);
    public String value();
}
常量value()说明
DEFAULTdefault按 CLI 规则请求权限
ACCEPT_EDITSacceptEdits自动接受文件编辑
BYPASS_PERMISSIONSbypassPermissions跳过全部权限检查;必须同时设置 allowDangerouslySkipPermissions(true)
PLANplan只规划不执行;需要和其他模式并存时改用独立 Plan
DONT_ASKdontAsk不询问,需要询问的操作直接拒绝
AUTOauto自动裁决
of(String) 对已知模式忽略大小写和字母数字以外的字符,"yolo" 等同 BYPASS_PERMISSIONS。启动时用 SessionOptions.Builder.permissionMode(...) 设置,运行中用 QoderSession.setPermissionMode(...) 切换;两处使用 BYPASS_PERMISSIONS 都要求该会话设置了 allowDangerouslySkipPermissions(true),否则以 InvalidOptionsException 失败。各模式的行为见权限控制。

CancellationToken

回调收到的取消令牌。CLI 取消请求、回调超时或会话关闭时被取消。SDK 负责协议层的取消响应,应用只需停止相关工作。
public interface CancellationToken {
    boolean isCancelled();
    void onCancel(Runnable action);
    default CancellationRegistration registerOnCancel(Runnable action);
}

public interface CancellationRegistration extends AutoCloseable {
    @Override void close();
}
方法返回类型说明
isCancelled()boolean是否已取消;一旦为 true 不再改变
onCancel(Runnable)void注册取消动作;令牌已取消时也会调用。动作异步执行,不在注册线程内联运行,也不使用 callbackExecutor
registerOnCancel(Runnable)CancellationRegistration同上,并返回可注销的句柄
CancellationRegistration.close()void注销动作,幂等、不阻塞;已开始执行的动作不会被打断
取消是合作式的,忽略令牌的代码会继续运行。取消动作应短小、不阻塞,抛出的异常会被忽略。onCancel 动作是尽力交付,会话关闭时排队中的动作可能被丢弃;需要确定停止的工作应在自己的副作用边界检查 isCancelled()。长期存在的工作建议用 registerOnCancel,结束时调用 close() 释放动作及其捕获的对象。

独立 Plan

Plan 状态可以独立于权限模式设置,开启 Plan 时保留当前权限模式。包 com.qoder.sdk.plan。
public final class PlanModeSnapshot {
    public boolean active();
}

// SessionOptions.Builder
public Builder planMode(boolean active);

// QoderSession
public CompletionStage<PlanModeSnapshot> setPlanMode(boolean active);
public CompletionStage<PlanModeSnapshot> getPlanMode();
public CompletionStage<Void> setPermissionModePreservingPlan(PermissionMode mode);
方法说明
SessionOptions.Builder.planMode(boolean)启动时的 Plan 状态。不调用时不改变 CLI 的 Plan 状态;显式传 false 会要求 CLI 退出 Plan
setPlanMode(boolean)运行中开启或退出 Plan,返回 CLI 确认后的状态
getPlanMode()查询 CLI 当前的 Plan 状态
setPermissionModePreservingPlan(PermissionMode)切换权限模式并保持 Plan 状态;传 PermissionMode.PLAN 时以 InvalidOptionsException 失败
PlanModeSnapshot.active()Plan 是否开启
规则:
  • 以上入口都需要 CLI 支持对应能力,见能力要求。不支持时以 UnsupportedCliCapabilityException 失败,不发送请求。
  • 启动时 planMode(...) 与 permissionMode(PermissionMode.PLAN) 互斥,即使传 planMode(false) 也一样,build() 抛 InvalidOptionsException。
  • QoderSession.setPermissionMode(...) 不保证保持 Plan,需要保持时用 setPermissionModePreservingPlan(...)。
  • 以 CLI 返回的 PlanModeSnapshot 或消息流中的 PlanModeChangedMessage 为准,不要按本地最近一次设置推断当前状态。

Hooks Reference

HookRegistration

一条 Hook 注册:事件名、可选 matcher 和处理器。包 com.qoder.sdk.hook。不可变,用 builder() 构建,通过 SessionOptions.Builder.hooks(List<HookRegistration>) 注册。
public final class HookRegistration {
    public static Builder builder();
    public String event();
    public Optional<String> matcher();
    public HookHandler handler();
    public long timeoutMs();
}
Builder 方法参数类型说明
event(String)String必填,事件名,如 PreToolUse、PostToolUse、Stop
matcher(String)String可选,交给 CLI 匹配,如工具名 Bash
handler(HookHandler)HookHandler必填,处理器
timeoutMs(long)long本条注册在 Java 侧的等待上限(毫秒);0(默认)沿用 controlRequestTimeoutMs,负数抛 IllegalArgumentException
build()—构建注册;缺少 event 或 handler 时抛 NullPointerException
事件名和 matcher 由 CLI 匹配,处理器在 Java 进程内执行。timeoutMs 只约束 Java 侧等待,不会传给 CLI。需要在消息流中看到 Hook 生命周期事件时,再设置 includeHookEvents(true),对应消息见 HookEventMessage。可用事件和用法见 Hooks。

HookHandler

Hook 处理器。在 SDK 独立的 Hook 线程上调用,不使用 callbackExecutor;耗时工作交给应用执行器,并返回表示完成的 stage。
@FunctionalInterface
public interface HookHandler {
    CompletionStage<JsonValue> onHookEvent(HookEvent event) throws Exception;
}
返回的 JSON 原样作为 Hook 输出交给 CLI,结构由 CLI 定义,见 Hooks 输出。空对象表示不干预;在 PreToolUse 中返回以下结构可以拒绝本次工具调用:
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "..."
  }
}
处理器抛出异常或 stage 异常完成时,只有这一次 Hook 调用以 CALLBACK_EXECUTION_FAILED 失败;超时以 CONTROL_REQUEST_TIMEOUT 失败。

HookEvent

一次 Hook 事件。
public final class HookEvent {
    public String eventName();
    public Optional<String> matcher();
    public JsonValue payload();
}
方法返回类型说明
eventName()String事件名,取自 CLI 输入中的 hook_event_name
matcher()Optional<String>CLI 回传的 matcher,通常为空
payload()JsonValueCLI 的 Hook 输入对象,原样 JSON
payload() 的常见字段:公共字段 session_id、cwd、hook_event_name;PreToolUse 和 PostToolUse 有 tool_name、tool_input、tool_use_id,PostToolUse 还有 tool_response;UserPromptSubmit 有 prompt;Stop 有 stop_hook_active。字段集合由 CLI 版本决定,SDK 不建模、不校验,读取时要容忍缺失。

MCP

McpServerConfig

MCP 服务器配置,通过 SessionOptions.Builder.mcpServers(List<McpServerConfig>) 注册。包 com.qoder.sdk.mcp。实例不可变。
public final class McpServerConfig {
    public static McpServerConfig stdio(String name, String command, List<String> args);
    public static McpServerConfig sse(String name, String url);
    public static McpServerConfig http(String name, String url);
    public static McpServerConfig inProcess(String name, InProcessMcpHandler handler);

    // 返回新实例
    public McpServerConfig withEnv(String envName, String value);
    public McpServerConfig withSecretEnv(String envName, SecretValue value);
    public McpServerConfig withHeader(String headerName, String value);
    public McpServerConfig withSecretHeader(String headerName, SecretValue value);

    public String name();
    public String type();
    public Optional<String> command();
    public List<String> args();
    public Map<String, String> env();
    public Map<String, SecretValue> secretEnv();
    public Optional<String> url();
    public Map<String, String> headers();
    public Map<String, SecretValue> secretHeaders();
    public Optional<InProcessMcpHandler> inProcessHandler();
    public boolean hasSecrets();
}
工厂type()说明
stdio(name, command, args)stdioCLI 启动的子进程服务器;args 为 null 时视为空列表
sse(name, url)sseSSE 远程服务器
http(name, url)httpStreamable HTTP 远程服务器
inProcess(name, handler)sdkJVM 进程内服务器,消息经 SDK 转发给 InProcessMcpHandler,没有子进程
链式方法适用形态说明
withEnv(String, String)stdio添加环境变量
withSecretEnv(String, SecretValue)stdio添加密钥环境变量,覆盖同名普通变量
withHeader(String, String)sse / http设置请求头,名称不区分大小写
withSecretHeader(String, SecretValue)sse / http设置密钥请求头,覆盖同名普通请求头
对不适用的形态调用链式方法抛 IllegalStateException。同一列表中服务器名必须唯一(区分大小写),否则 SessionOptions.Builder.build() 抛 InvalidOptionsException。MCP 配置连同密钥写入仅当前账号可读的临时文件交给 CLI,不进入启动参数或环境变量,会话运行期间不要手动删除这些文件。接入方式见 MCP。

InProcessMcpHandler

进程内 MCP 服务器的 JSON-RPC 端点,通过 McpServerConfig.inProcess(name, handler) 注册。
@FunctionalInterface
public interface InProcessMcpHandler {
    CompletionStage<JsonValue> handle(JsonValue message) throws Exception;
    default CompletionStage<JsonValue> handle(JsonValue message,
                                              CancellationToken cancellation) throws Exception;
    default InProcessMcpHandler newConnection();
}
方法说明
handle(JsonValue)处理一条 JSON-RPC 消息,返回以响应完成的 stage
handle(JsonValue, CancellationToken)SDK 实际调用的入口,默认委托单参数版本;需要感知取消时覆盖它,见 CancellationToken
newConnection()每个会话注册该服务器时调用一次,默认返回自身;有连接状态的处理器应返回独立实例,不能返回 null
SDK 把返回的 JSON 原样作为 MCP 响应转发给 CLI,响应需要自带 jsonrpc 和对应请求的 id。返回的 stage 不得为 null;以 JsonNull.INSTANCE 完成表示没有 JSON-RPC 响应(例如通知)。处理器在 callbackExecutor 上执行,抛出异常或 stage 异常完成只影响这一条消息。同一连接中,请求 ID 在 handle() 返回的 stage 结束前一直被占用,重复使用会得到 DUPLICATE_MCP_REQUEST_ID;未结束的请求达到 128 个时,新请求得到 MCP_REQUEST_CAPACITY_EXCEEDED。取消不会撤销处理器已经产生的副作用。

McpServerStatus

一个 MCP 服务器的状态,由 QoderSession.mcpServerStatus() 返回。
public final class McpServerStatus {
    public String name();
    public String status();
    public JsonValue raw();
}
方法返回类型说明
name()String服务器名
status()String状态字符串,取值以 CLI 上报为准
raw()JsonValue原始状态
CLI 可能在 raw() 中回显服务器的有效配置,包括 env 和 headers。通过 withSecretEnv、withSecretHeader 等登记的密钥会被替换为 [REDACTED],名称和状态文本同样处理。

McpAuthenticationResult 和 McpAuthenticationOptions

用于 MCP OAuth。流程:用 QoderSession.beginMcpAuthentication(serverName[, options]) 发起认证;结果需要用户操作时,宿主展示 authUrl(),拿到完整回调 URL 后调用 mcpSubmitOAuthCallbackUrl(serverName, callbackUrl)。SDK 不打开浏览器,state 校验、凭据交换和重连由 CLI 完成。建议在发送第一条业务输入前完成认证。
public final class McpAuthenticationResult {
    public boolean requiresUserAction();
    public Optional<String> authUrl();
}

public final class McpAuthenticationOptions {
    public static McpAuthenticationOptions defaults();
    public static McpAuthenticationOptions redirectUri(String redirectUri);
    public Optional<String> redirectUri();
}
方法说明
requiresUserAction()是否需要宿主完成用户授权;为 false 时认证已完成
authUrl()授权入口;需要用户操作时一定存在
McpAuthenticationOptions.defaults()使用 CLI 默认回调地址
McpAuthenticationOptions.redirectUri(String)指定宿主接收回调的 URI,不能为空白
授权 URL 和回调 URL 可能包含敏感的 OAuth state,不要写入日志;两个类的 toString() 都不包含 URL。完整流程见 MCP OAuth 认证。

ElicitationHandler

MCP elicitation 回调:MCP 服务器需要向用户补充收集信息时触发。通过 SessionOptions.Builder.onElicitation(...) 注册。
@FunctionalInterface
public interface ElicitationHandler {
    CompletionStage<ElicitationResult> onElicitation(ElicitationRequest request)
            throws Exception;
}
回调在 callbackExecutor 上执行,受 controlRequestTimeoutMs 约束。抛出异常或 stage 异常完成时,只有这一次 elicitation 以 CALLBACK_EXECUTION_FAILED 失败。

ElicitationRequest

public final class ElicitationRequest {
    public String serverName();
    public String message();
    public JsonValue requestedSchema();
    public JsonValue raw();
}
方法返回类型说明
serverName()String发起请求的 MCP 服务器名
message()String展示给用户的提示
requestedSchema()JsonValue期望回答的 JSON Schema;请求未携带时为空对象,不为 null
raw()JsonValue原始请求

ElicitationResult

elicitation 的结果。
public final class ElicitationResult {
    public static ElicitationResult accept(JsonValue content);
    public static ElicitationResult decline();
    public static ElicitationResult cancel();
    public String action();
    public Optional<JsonValue> content();
}
工厂action()说明
accept(JsonValue)accept用户接受,携带按 schema 填写的内容;内容为 null 时抛出 NullPointerException
decline()decline用户拒绝回答
cancel()cancel用户取消本次请求

Models

模型相关类型位于 com.qoder.sdk.model 包。选择方式与回调用法见 Model Policy。

ModelSelection

模型选择策略,传给 SessionOptions.Builder.modelSelection(...)。三种策略只能选一种。
public abstract class ModelSelection {
    public static StaticModelSelection staticModel(ModelId model);
    public static DynamicModelSelection dynamic(ModelPolicyProvider provider);
    public static DynamicModelSelection dynamic(ModelId initialModel, ModelPolicyProvider provider);
    public static PolicyModelSelection policy(ModelDecisionProvider provider);
    public static PolicyModelSelection policy(ModelId initialModel, ModelDecisionProvider provider);
}

public final class StaticModelSelection extends ModelSelection {
    public Optional<ModelId> model();
}

public final class DynamicModelSelection extends ModelSelection {
    public ModelPolicyProvider provider();
    public Optional<ModelId> initialModel();
}

public final class PolicyModelSelection extends ModelSelection {
    public ModelDecisionProvider provider();
    public Optional<ModelId> initialModel();
}
工厂方法返回类型说明
staticModel(ModelId)StaticModelSelection整个会话使用固定模型
dynamic(ModelPolicyProvider)DynamicModelSelection初始模型由 CLI 默认选择,之后每次模型请求由回调返回模型 ID
dynamic(ModelId, ModelPolicyProvider)DynamicModelSelection同上,并显式指定初始模型
policy(ModelDecisionProvider)PolicyModelSelection初始模型由 CLI 默认选择,回调返回结构化结果,可以保持当前模型或覆盖模型与参数
policy(ModelId, ModelDecisionProvider)PolicyModelSelection同上,并显式指定初始模型
初始化时 SDK 只向 CLI 声明回调,不会调用它。回调在 CLI 每次需要模型决策时才运行,不要依赖它在 openSession() 完成前执行。

ModelPolicyProvider

dynamic(...) 使用的回调,返回本次请求要使用的模型 ID。
@FunctionalInterface
public interface ModelPolicyProvider {
    CompletionStage<ModelId> resolveModel(ModelRequestContext context) throws Exception;
}
参数类型说明
contextModelRequestContext本次模型请求的上下文
超时由 SessionOptions.Builder.resolveModelTimeoutMs(long) 控制,默认 500 毫秒,0 表示不限时。回调抛出异常、返回失败的 stage 或超时,本次模型请求都会失败,不会回退到初始模型。这些失败不会以异常交给应用,消息游标和 SDK 的异步方法都不会抛出:SDK 只把错误码回给 CLI(超时为 MODEL_RESOLUTION_TIMEOUT,回调失败为 CALLBACK_EXECUTION_FAILED),并发出 inbound_control_request_failed 诊断事件,detail 含 error=<错误码>。需要感知时注册 DiagnosticsListener,本轮结果按 ResultMessage 的 outcome() 处理。

ModelDecisionProvider

policy(...) 使用的回调,返回 ModelPolicyResult。上下文、执行线程和超时预算与 ModelPolicyProvider 相同。
@FunctionalInterface
public interface ModelDecisionProvider {
    CompletionStage<ModelPolicyResult> resolve(ModelRequestContext context) throws Exception;
}
返回 null、stage 以 null 完成、抛出异常或超时,本次模型请求都会失败。要保持 CLI 当前选择的模型,必须显式返回 ModelPolicyResult.keepCurrent()。

ModelPolicyResult

ModelDecisionProvider 的不可变返回值。只发送显式设置的字段,未设置模型时保留 CLI 已选择的模型。
public final class ModelPolicyResult {
    public static ModelPolicyResult keepCurrent();
    public static ModelPolicyResult select(ModelId model);
    public static Builder builder();
    public Optional<ModelId> model();
    public Optional<JsonObject> parameters();
    public Optional<String> taskId();
    public Optional<String> subTask();
}
Builder 方法说明
model(ModelId)覆盖本次请求的模型
parameters(JsonObject)传给 CLI 的请求参数,SDK 不解释其含义
taskId(String)推理任务标识
subTask(String)业务子任务标识
build()创建结果;什么都不设置时等同于 keepCurrent()
Builder 方法不接受 null。parameters 不是传递密钥的通道,不要在其中放凭据。

ModelRequestContext

一次模型请求的上下文,由 SDK 传给模型回调。
public final class ModelRequestContext {
    public Optional<String> sessionId();
    public Optional<String> purpose();
    public Optional<Integer> turnIndex();
    public Optional<String> agentId();
    public Optional<String> agentType();
    public Optional<ModelId> resolvedModel();
    public List<ModelInfo> availableModels();
}
方法返回类型说明
sessionId()Optional<String>所属会话 ID
purpose()Optional<String>CLI 本次调用的用途
turnIndex()Optional<Integer>轮次索引
agentId() / agentType()Optional<String>发起请求的子 Agent ID 和类型
resolvedModel()Optional<ModelId>CLI 在回调之前预选的模型
availableModels()List<ModelInfo>本次请求附带的模型目录,不可变;未提供时为空列表
CLI 没有提供的字段为 Optional.empty()。availableModels() 只是本次请求的快照,不要缓存为整个会话的固定目录。

ModelInfo

模型目录中的一条记录,由 QoderSession.getAvailableModels() 返回,也出现在 ModelRequestContext 和 ModelCatalogSnapshot 中。
public final class ModelInfo {
    public ModelId modelId();
    public Optional<String> displayName();
    public Optional<String> description();
    public Optional<String> source();
    public Optional<String> outerProvider();
    public Optional<String> descriptionEn();
    public boolean isEnabled();
    public JsonValue raw();
}
方法返回类型说明
modelId()ModelId可用于模型选择的模型 ID
displayName() / description()Optional<String>展示名称和说明
source() / outerProvider() / descriptionEn()Optional<String>来源、外部提供方、英文说明
isEnabled()boolean当前账号能否选用;CLI 没有给出该字段时按 false 处理
raw()JsonValue完整的原始条目,包括上下文窗口、价格等未建模字段
目录里可能包含 isEnabled() 为 false 的模型,展示可选模型时应过滤掉。

ModelCatalogOptions

QoderSession.getAvailableModels(ModelCatalogOptions) 的读取选项。
public final class ModelCatalogOptions {
    public enum FetchStrategy { LIVE, CACHE }
    public static ModelCatalogOptions defaults();
    public static Builder builder();
    public Optional<FetchStrategy> fetchStrategy();
    public Optional<String> uid();
}
defaults() 和无参的 getAvailableModels() 都使用 CLI 默认的缓存策略。Builder 的 fetchStrategy(FetchStrategy) 显式选择实时读取(LIVE)或缓存(CACHE),uid(String) 指定目录 uid。

ModelCatalogSnapshot

QoderSession.modelCatalogSnapshot() 返回的本地模型目录快照,来自初始化结果或 CLI 推送的最新目录更新。
public final class ModelCatalogSnapshot {
    public List<ModelInfo> models();
    public Optional<ModelId> currentModel();
}
modelCatalogSnapshot() 返回 Optional.empty() 表示还没有收到目录;返回快照但 models() 为空,表示 CLI 明确给出了空目录。读取快照不会向 CLI 发请求,需要当前账号的实时结果时调用 getAvailableModels(...)。

ModelId

模型 ID 的值类型。
public final class ModelId {
    public static ModelId of(String value);
    public String value();
}

Memory

Memory 配置通过 SessionOptions.Builder.memory(MemoryOptions) 传入,相关类型位于 com.qoder.sdk.memory 包。记忆的生成、写入和加载由 CLI 执行,与保存会话记录的 SessionStore 是两项独立能力。启用方式、应用管理目录和逐轮生成控制见 Memory。

Memory 类型一览

类型作用
MemoryOptions查询级配置入口。disabled() 是默认值;nativeBuilder() 使用 CLI 原生记忆,customBuilder() 由应用指定生成与加载配置。可设置用户级/项目级作用域、结果回调和 drainTimeoutMs(默认 60000 毫秒)
MemoryGenerationOptionscustom 模式的生成配置:开关、记忆目录(roots)、生成提示词、轮次结束后的自动提取
MemoryTurnCompleteOptions轮次结束后自动提取的配置:开关、shouldGenerate 决策回调、超时(默认 10000 毫秒)、决策失败策略 OnGateError(SKIP / REPORT_FAILED)
MemoryShouldGenerate逐轮决策回调,返回 CompletionStage<MemoryGenerationGateResult>
MemoryGenerationGateInput / MemoryGenerationGateResult决策回调的输入(会话、轮次、prompt、回答等),以及 run() / skip(reason) 决策结果
MemoryConsumptionOptionscustom 模式的加载配置:开关、文件列表、maxTokens、超长处理 Overflow(TRUNCATE / FAIL_QUERY)、读取失败处理 FailureMode(BEST_EFFORT / FAIL_QUERY)
MemoryRoot / MemoryConsumptionFile用 builder(String id, Path path) 创建的记忆目录和加载文件;目录可设访问权限(READ / READ_WRITE)和索引文件,加载文件可设为必需
MemoryGenerationHandler / MemoryConsumptionHandler生成和加载结果的通知回调,返回 CompletionStage<Void>
EffectiveMemoryConfigCLI 在初始化时确认的实际配置,由 InitializationResult.memory() 返回;包含启用状态、模式、各开关和 roots 的 id 与权限,不包含解析后的文件路径
MemoryGenerationResult / MemoryConsumptionResult生成和加载结果:状态、文件明细、原始 JSON。生成状态为 SAVED、PARTIAL、NO_CHANGE、SKIPPED、FAILED,加载状态为 SUCCESS、PARTIAL、FAILED;无法识别的状态为 UNKNOWN
MemoryUsage记忆生成单独产生的用量,CLI 没有给出的值为 Optional.empty()
结果也会以 MemoryGenerationMessage 和 MemoryConsumptionMessage 出现在消息流中。memory(null) 抛出 NullPointerException,要关闭 Memory 请传 MemoryOptions.disabled()。

flushMemory() 与 refreshMemory()

QoderQuery 和 QoderSession 都提供这两个方法,返回 CompletionStage<Void>,会自行等待初始化完成。
  • flushMemory():等待 CLI 已经触发的记忆处理结束,不等待之后的轮次,也不保证每个文件都保存成功,结果仍需检查 MemoryGenerationResult。等待上限为 drainTimeoutMs。
  • refreshMemory():按现有加载配置重新读取记忆文件,适合在外部修改记忆文件后调用,不改变配置。超时使用 controlRequestTimeoutMs。
会话没有启用 Memory 时,两个方法都以 UnsupportedCliCapabilityException 失败,不会发送请求。一次性查询启用 Memory 时,ResultMessage 之后消息流还会继续传递记忆处理消息,应消费到流结束。

Session Storage

外部会话存储相关类型。SessionStorage、SessionRepository 位于 com.qoder.sdk 包,其余位于 com.qoder.sdk.session 包。使用方式见外置会话存储和会话控制。

SessionStorage

单次会话使用哪种历史存储,通过 SessionOptions.Builder.sessionStorage(...) 设置。
public final class SessionStorage {
    public enum Mode { INHERIT, CLI_LOCAL, EXTERNAL }
    public static SessionStorage inherit();
    public static SessionStorage cliLocal();
    public static SessionStorage external(SessionStore store);
    public Mode mode();
    public Optional<SessionStore> externalStore();
}
工厂方法说明
inherit()默认值,使用 QoderClient.Builder.sessionStore(...) 配置的客户端存储;客户端没有配置时使用 CLI 本地历史
cliLocal()本次会话不使用外部存储,只用 CLI 本地历史
external(SessionStore)本次会话使用指定的外部存储,参数不能为 null
最终生效的外部存储不能与 fileCheckpointing(true) 同时使用。

SessionStore

外部会话存储需要实现的接口。客户端通过 QoderClient.Builder.sessionStore(...) 设置默认存储,同时供 client.sessions() 使用。
public interface SessionStore {
    CompletionStage<AppendResult> append(SessionKey key, AppendBatch batch);
    CompletionStage<Optional<SessionSnapshot>> load(SessionKey key);
}
方法返回类型说明
append(SessionKey, AppendBatch)CompletionStage<AppendResult>原子地追加一个批次
load(SessionKey)CompletionStage<Optional<SessionSnapshot>>加载完整的 transcript 快照;键不存在或已删除时返回空
两个方法可以同步完成,但失败必须通过异常完成的 stage 报告,不能直接抛出。不要在调用线程上执行阻塞 I/O,应使用异步客户端或应用自己的线程池。存储应原样保存每条 transcript JSON(包括未知字段),不要再包一层信封。

实现契约

契约要求
顺序与隔离load 按追加顺序返回条目;不同 SessionKey 的数据互不可见
原子性同一键的并发追加必须串行化;一个批次要么全部写入,要么全部失败
幂等同一键下已写入过的 operationId 不能重复写入,应返回原操作的 newVersion 和 appendedCount,并标记为重放。该检查先于 CAS;已删除的键一律以 SessionStoreDeletedException 拒绝
CASexpectedVersion 不为 null 时必须等于当前版本,否则抛出携带当前版本的 SessionStoreConflictException;为 null 时无条件追加。空键的当前版本是 VersionToken.initial()
UUID 完整性uuid 和 rawJson 都相同的条目不重复写入;uuid 相同但 rawJson 不同必须报冲突。没有 uuid 的条目不按内容去重
镜像写入失败通过 MirrorErrorMessage 和诊断事件报告,主回答可能继续。回答成功不代表记录已经持久化。

ListableSessionStore

可枚举的存储。SessionRepository 的 list()、按最近会话继续以及子 Agent 恢复都依赖它。
public interface ListableSessionStore extends SessionStore {
    CompletionStage<List<SessionKey>> listSessions(String projectKey);
    CompletionStage<Void> deleteSession(SessionKey key);
}
listSessions 返回项目下的所有键(包括子路径键),顺序不保证。deleteSession 只删除指定的单个键,不会删除同一会话的其他子路径。

SessionDeletionStore

支持整个会话删除的存储,SessionRepository.delete(...) 要求存储实现这个接口。
public interface SessionDeletionStore extends ListableSessionStore {
    CompletionStage<Void> deleteSessionTree(String projectKey, String sessionId);
}
实现应在与追加相同的事务或临界区内删除根 transcript 和全部子路径,并永久标记该会话已删除;之后对该会话的写入都以 SessionStoreDeletedException 拒绝,旧版本也不能重建数据。删除不存在或已删除的会话也应成功。已删除的会话 ID 不能复用。

InMemorySessionStore

SDK 内置的内存实现,实现 SessionDeletionStore。数据随进程退出丢失,适合测试和短生命周期进程。
public final class InMemorySessionStore implements SessionDeletionStore {
    public InMemorySessionStore();
}

SessionMirrorOptions

外部存储写入(镜像)的资源上限,通过 SessionOptions.Builder.sessionMirrorOptions(...) 设置。用 builder() 创建,defaults() 返回默认值;所有值必须为正数。
配置项默认值说明
maxPendingBatches256待写入批次的数量上限
maxPendingBytes64 MiB待写入批次原始内容的字节上限
operationTimeoutMs60000每个批次从读取版本到追加完成(含重试)的总时长
closeDrainTimeoutMs5000关闭会话时等待剩余批次写入的时长
单个键写入失败或超时后,该键停止写入,其他键不受影响;超出会话总上限时停止整个会话的镜像。这些错误通过 MirrorErrorMessage 和诊断事件报告,不中断模型回合。

SessionRepository

读取、继续、分叉或删除已保存的会话。用 client.sessions() 获取客户端默认作用域的仓库,用 client.sessions(SessionOptions) 获取某次会话所用作用域的仓库。
public interface SessionRepository {
    CompletionStage<List<String>> list();
    CompletionStage<Optional<SessionSnapshot>> get(String sessionId);
    CompletionStage<Void> delete(String sessionId);
    CompletionStage<QoderSession> continueSession(String sessionId, SessionOptions options);
    CompletionStage<QoderSession> fork(String sessionId, SessionOptions options);
}
方法返回类型说明
list()CompletionStage<List<String>>当前作用域下已保存的会话 ID,需要 ListableSessionStore
get(String)CompletionStage<Optional<SessionSnapshot>>单个会话的 transcript 快照,需要外部存储
delete(String)CompletionStage<Void>删除会话及其全部子路径,需要 SessionDeletionStore
continueSession(String, SessionOptions)CompletionStage<QoderSession>在新的 CLI 进程中继续会话;sessionId 传 null 时继续最近的会话
fork(String, SessionOptions)CompletionStage<QoderSession>以指定会话为起点创建新会话
作用域规则:
  1. 仓库固定绑定一个存储和项目键。存储按 sessionStorage 解析;项目键取自 workingDirectory,未设置时用客户端的 projectDirectory,两者都没有时为 default。
  2. sessions(options) 只采用其中的存储和目录两项,不启动进程,也不保存 CLI 路径、环境变量等其他配置。
  3. continueSession 和 fork 的 options 没有指定目录或存储时沿用仓库的作用域;显式覆盖可以打开其他作用域的会话,但不会改变当前仓库的 list、get、delete。
缺少对应存储能力时,方法以 UnsupportedCliCapabilityException 失败,不会降级为逐键删除。外部存储上的 fork 还需要 CLI 支持对应能力,见能力要求。

存储值类型

SessionStore 实现中用到的值类型。
public final class SessionKey {
    public static final String DEFAULT_PROJECT; // "default"
    public static SessionKey of(String projectKey, String sessionId);
    public static SessionKey of(String projectKey, String sessionId, String subpath);
    public static String projectKeyFor(Path workingDirectory);
    public String projectKey();
    public String sessionId();
    public String subpath();
}

public final class AppendBatch {
    public static Builder builder();
    public OperationId operationId();
    public VersionToken expectedVersion();
    public long firstSequence();
    public List<SessionStoreEntry> entries();
}

public final class AppendResult {
    public static AppendResult applied(VersionToken newVersion, int appendedCount);
    public static AppendResult replayed(VersionToken newVersion, int appendedCount);
    public VersionToken newVersion();
    public int appendedCount();
    public boolean alreadyApplied();
}

public final class SessionSnapshot {
    public static SessionSnapshot of(List<SessionStoreEntry> entries, VersionToken version);
    public List<SessionStoreEntry> entries();
    public VersionToken version();
}

public final class SessionStoreEntry {
    public static SessionStoreEntry of(String rawJson);
    public String rawJson();
    public Optional<String> type();
    public Optional<String> uuid();
    public Optional<String> timestamp();
}

public final class OperationId {
    public static OperationId of(String value);
    public static OperationId random();
    public String value();
}

public final class VersionToken {
    public static VersionToken of(String value);
    public static VersionToken initial();
    public String value();
}

public class SessionStoreDeletedException extends QoderException {
    public static final String CODE = "SESSION_STORE_DELETED";
    public SessionStoreDeletedException(String message);
    public SessionStoreDeletedException(String message, Throwable cause);
}
类型成员说明
SessionKeyprojectKey / sessionId / subpath存储键。会话根的 subpath 为空字符串,子 Agent transcript 使用非空子路径;projectKeyFor(Path) 从项目目录生成项目键
AppendBatchoperationId幂等 ID,同一 ID 重试只应用一次
expectedVersionCAS 期望版本;null 表示无条件追加
firstSequence批次首条的顺序参考,不能代替 CAS 版本或幂等 ID
entries批次条目。Builder 提供 operationId、expectedVersion、firstSequence、addEntry、addEntries
AppendResultapplied(...) / replayed(...)实际写入的结果,或幂等重放的结果;重放时传入原操作的版本和条数
alreadyApplied()是否为重放
SessionSnapshotentries / version按追加顺序排列的条目和当前版本
SessionStoreEntryrawJson()原始 JSON 行;type、uuid、timestamp 从中提取。toString() 不输出正文
OperationIdof / random幂等操作 ID
VersionTokeninitial()空存储的起始版本;每次成功写入后版本必须前进
SessionStoreDeletedExceptionCODE会话已删除时拒绝写入,错误码 SESSION_STORE_DELETED,不可重试。它不是 CAS 冲突,不能按当前版本重试

Results and Identifiers

InitializationResult

初始化握手的结果,由 QoderQuery.initialization()、QoderSession.initialization() 等方法返回。它是握手完成时的只读快照,之后不会变化。
public final class InitializationResult {
    public Map<String, CapabilityState> capabilities();
    public CapabilityState capability(String capability);
    public Optional<String> cliVersion();
    public Optional<String> wireVersion();
    public Optional<String> sessionId();
    public Optional<EffectiveMemoryConfig> memory();
}
方法返回类型说明
capabilities()Map<String, CapabilityState>CLI 在握手时声明的能力,不可变
capability(String)CapabilityState单个能力的状态;未声明时返回 UNKNOWN
cliVersion()Optional<String>CLI 版本
wireVersion()Optional<String>协商的通信协议版本
sessionId()Optional<String>CLI 分配的会话 ID
memory()Optional<EffectiveMemoryConfig>CLI 确认的 Memory 配置;未启用时为空
运行中如果 CLI 以"不支持"拒绝某个受能力控制的请求,SDK 会把该能力降为 UNSUPPORTED,同一会话之后的调用直接在本地失败。InitializationResult 不反映这一变化;需要感知时监听 type 为 capability_downgraded 的诊断事件。

方法与所需能力

下列方法或配置需要 CLI 在握手时声明对应能力。能力状态不是 SUPPORTED(包括未声明的 UNKNOWN)时,SDK 不会发送请求,直接以 UnsupportedCliCapabilityException 失败。
方法或配置所需能力不支持时
QoderSession.backgroundTasks() / backgroundTasks(String)background_tasks_v1返回的 stage 异常完成
QoderSession.stopTask(TaskId)background_tasks_v1返回的 stage 异常完成
QoderSession.rewindHistory(MessageId)session_rewind_v1返回的 stage 异常完成
QoderSession.reloadSkills()reload_skills_v1返回的 stage 异常完成
getPlanMode() / setPlanMode(boolean) / setPermissionModePreservingPlan(PermissionMode)plan_mode_v1返回的 stage 异常完成
SessionOptions.Builder.planMode(boolean)plan_mode_v1初始化失败
SessionOptions.Builder.customContext(Map)custom_context_v1初始化失败
UserInput.Builder.customContext(Map)(包括空 map)custom_context_v1该输入的写入 stage 异常完成;作为首条输入时初始化失败
UserInput.Builder.selectedSkills(List)(非空)selected_skills_v1该输入的写入 stage 异常完成;作为首条输入时初始化失败
QoderSession.cancelQueuedMessage(MessageId) 的远端取消interrupt_cancel_queued_v1只做本地取消;输入已开始写入时返回 false,不抛异常
外部 SessionStore 加 forkSession(true),包括 SessionRepository.fork(...)session_store_fork_barrier_v1初始化失败
QoderSession 的其他控制方法(如 getAvailableModels、rewindFiles、setPermissionMode)不检查能力,直接发送请求。

CapabilityState

CLI 能力的三种状态。
public enum CapabilityState { SUPPORTED, UNSUPPORTED, UNKNOWN }
值说明
SUPPORTEDCLI 声明支持
UNSUPPORTEDCLI 明确声明不支持,或运行中拒绝了该能力的请求
UNKNOWNCLI 没有声明;依赖该能力的方法直接失败,SDK 不会试探

InterruptResult

interrupt() 的结果。
public final class InterruptResult {
    public boolean interrupted();
    public String turnState();
}
方法返回类型说明
interrupted()boolean是否向一个运行中的轮次发出了中断;没有运行中的轮次时为 false
turnState()String返回时的轮次状态:IDLE、RUNNING 或 INTERRUPTING
成功只表示 CLI 接受了中断请求,轮次何时结束以 ResultMessage 为准。中断请求失败(包括超时)后,轮次恢复为 RUNNING,可以重试。

MessageSubmission

QoderSession.submit(UserInput) 返回的提交句柄。
public final class MessageSubmission {
    public MessageId id();
    public CompletionStage<MessageId> written();
}
方法返回类型说明
id()MessageId立即可用的输入 ID,可传给 cancelQueuedMessage;不代表输入已被接受或写出
written()CompletionStage<MessageId>输入写入 CLI 后完成,不等待回答;输入被拒绝或写入失败时异常完成,本地取消成功时以 CancellationException 完成
MessageSubmission submission = session.submit(UserInput.text("待处理输入"));
CompletionStage<Boolean> cancelled = session.cancelQueuedMessage(submission.id());
只需要写入确认时用 send(input);需要在写入前取消时用 submit(input)。written() 每次返回独立的 stage,取消它不会取消输入本身。

标识类型

public final class MessageId {
    public static MessageId of(String value);
    public String value();
}

public final class TaskId {
    public static TaskId of(String value);
    public String value();
}

public final class SessionTitle {
    public SessionTitle(String title);
    public String title();
}
类型说明
MessageId用户输入的 ID,来自 MessageSubmission.id() 或 send(...) 的返回值;用于 cancelQueuedMessage、rewindHistory、rewindFiles
TaskId后台任务 ID,传给 stopTask(TaskId)
SessionTitlegenerateSessionTitle() 返回的会话标题

RewindFilesOptions / RewindFilesResult

QoderSession.rewindFiles(MessageId, RewindFilesOptions) 的选项和结果。会话需要以 SessionOptions.Builder.fileCheckpointing(true) 启动,见 Checkpoint。
public final class RewindFilesOptions {
    public static RewindFilesOptions defaults();
    public static RewindFilesOptions dryRun();
    public boolean isDryRun();
}

public final class RewindFilesResult {
    public boolean canRewind();
    public List<String> filesChanged();
    public Optional<String> error();
}
方法返回类型说明
RewindFilesOptions.defaults()RewindFilesOptions实际回退文件
RewindFilesOptions.dryRun()RewindFilesOptions只报告会改动哪些文件,不写盘
canRewind()boolean能否回退(试运行)或是否已回退
filesChanged()List<String>受影响的文件
error()Optional<String>无法回退的原因

Message Types

SdkMessage

消息流中所有消息的抽象基类,位于 com.qoder.sdk.message。用 instanceof 判断具体类型。
public abstract class SdkMessage {
    public final String wireType();
    public final Optional<String> uuid();
    public final Optional<String> sessionId();
    public final Optional<String> rawJson();
}
方法返回类型说明
wireType()String消息的顶层 type
uuid()Optional<String>消息 UUID,CLI 提供时有值
sessionId()Optional<String>所属会话 ID,CLI 提供时有值
rawJson()Optional<String>消息的原始 JSON。SDK 没有建模的字段,用 JsonValue.parse 解析后读取
具体类型:
类wireType()说明
AssistantMessageassistant助手回复
ResultMessageresult一轮结束的结果
UserReplayMessageuserCLI 回放的用户消息
PartialMessagepartial流式增量事件
SystemMessage 及子类system系统消息,按 subtype() 分为多个专用类,包括 Hook 通知和 Memory 结果
HookEventMessagehook_event旧格式 Hook 事件
MirrorErrorMessagemirror_error会话镜像写入失败
UnknownMessage原始值未知顶层类型
专用 system 消息都继承 SystemMessage。分支时先判断专用子类,再判断 SystemMessage,否则宽泛分支会遮住专用类型。

AssistantMessage 与内容块

助手产生一条回复时递送。内容块是输出侧类型,与输入侧的 UserInput 内容块相互独立。
public final class AssistantMessage extends SdkMessage {
    public List<ContentBlock> content();
    public String getText();
}

public abstract class ContentBlock {
    public abstract String type();
}

public final class TextBlock extends ContentBlock {
    public static final String TYPE = "text";
    public String text();
}

public final class UnknownContentBlock extends ContentBlock {
    public String rawJson();
}
方法返回类型说明
AssistantMessage.content()List<ContentBlock>内容块列表
AssistantMessage.getText()String拼接全部 TextBlock 的文本
ContentBlock.type()String内容块的 type,如 text
TextBlock.text()String文本内容
UnknownContentBlock.rawJson()String该内容块的原始 JSON
Java SDK 只把 text 块建模为 TextBlock。tool_use、thinking 等其他内容块都以 UnknownContentBlock 保留:type() 返回原始类型,需要时从 rawJson() 解析。TypeScript 和 Python SDK 为这些块提供了类型化的类。

ResultMessage 与用量

一轮对话结束时递送。Agent 业务失败(例如达到轮次上限)通过 outcome() 为 ERROR 的 ResultMessage 报告,不会抛出异常。
public final class ResultMessage extends SdkMessage implements TurnTerminalMessage {
    public static final String SUBTYPE_SUCCESS = "success";
    public String wireSubtype();
    public Optional<Boolean> wireIsError();
    public ResultOutcome outcome();
    public Optional<String> result();
    public List<String> errors();
    public Usage usage();
    public boolean terminatesTurn();   // 恒为 true
}

public enum ResultOutcome { SUCCESS, ERROR, UNKNOWN }

public final class Usage {
    public static Usage empty();
    public long inputTokens();
    public long outputTokens();
}

public interface TurnTerminalMessage {
    boolean terminatesTurn();
}
方法返回类型说明
wireSubtype()String原始 subtype,如 success、error_max_turns
wireIsError()Optional<Boolean>原始 is_error;CLI 未提供时为空
outcome()ResultOutcome归一化结果,规则见下
result()Optional<String>最终结果文本
errors()List<String>错误描述,可能为空,不为 null
usage()Usage本轮 token 用量
Usage.inputTokens() / outputTokens()long输入、输出 token 数
outcome() 的规则:is_error 为 true 或 subtype 以 error 开头时为 ERROR;subtype 为 success 时为 SUCCESS;其他情况为 UNKNOWN。 Usage 只有输入和输出两个计数,不包含费用、缓存 token 和上下文用量;需要这些字段时从 rawJson() 读取。计数缺失或无法精确表示为 long 时取 0,所以 0 不代表 CLI 确认用量为零。 启用 Memory 后,ResultMessage 只表示回答轮次结束,之后仍可能递送 Memory 结果消息,一次性 Query 应读到流结束。Memory 生成的用量单独报告,不计入本消息的 usage()。

SystemMessage 及子类

CLI 发出的系统消息。SystemMessage 本身用于没有专用类的 subtype;未知 subtype 也由它保留。
subtype()类关键访问器说明
其他或未知值SystemMessagesubtype()完整负载在 rawJson()
plan_mode_changedPlanModeChangedMessageplanMode() → PlanModeSnapshot独立 Plan 状态变化,见权限
commands_changedCommandsChangedMessagecommands() → List<CommandInfo>本次完整命令目录,空列表表示清空
available_models_updateAvailableModelsUpdateMessagecatalog() → ModelCatalogSnapshot本次完整模型列表与当前模型
agents_md_size_warningAgentsMdSizeWarningMessagetotalBytes()、files() 等AGENTS 文件大小告警
permission_deniedPermissionDeniedMessage无,读 rawJson()一次工具权限被拒绝
background_tasks_changedBackgroundTasksChangedMessage无,读 rawJson()后台任务集合变化
task_updatedTaskUpdatedMessage无,读 rawJson()某个任务状态更新
hook_started / hook_progress / hook_responseHookStartedMessage 等见 Hook 通知消息Hook 运行通知
memory_generation / memory_consumptionMemoryGenerationMessage / MemoryConsumptionMessageresult()记忆生成、加载结果
public class SystemMessage extends SdkMessage {
    public final Optional<String> subtype();
}

public final class AgentsMdSizeWarningMessage extends SystemMessage {
    public static final String SUBTYPE = "agents_md_size_warning";
    public long totalBytes();
    public long thresholdBytes();
    public Optional<String> thresholdSource();
    public Optional<String> loadStatus();
    public Optional<String> severity();
    public Optional<String> code();
    public boolean truncated();
    public List<FileSize> files();

    public static final class FileSize {
        public String path();
        public long bytes();
    }
}

// 其他子类的访问器见上表;Hook 通知和 Memory 结果见后续小节
AgentsMdSizeWarningMessage 的大小都是 UTF-8 字节数,由 CLI 报告,SDK 不重新计算。truncated() 表示 CLI 报告的加载状态,不是 SDK 做了截断。 PermissionDeniedMessage、BackgroundTasksChangedMessage、TaskUpdatedMessage 上的 WIRE_TYPE 常量已弃用,请按类型或 subtype() 判断。 命令或模型目录更新无效时,SDK 不构造对应的专用消息,保留上一份目录快照,原消息作为通用 SystemMessage 递送,同时发出 invalid_catalog_update 诊断。

PartialMessage 与 UserReplayMessage

public final class PartialMessage extends SdkMessage {}

public final class UserReplayMessage extends SdkMessage {
    public List<ContentBlock> content();
}
PartialMessage 是流式增量事件,只在 SessionOptions.Builder.includePartialMessages(true) 时递送。wireType() 为 partial,CLI 原始的 stream_event 帧在 rawJson() 中。Java SDK 没有类型化的流事件,文本增量和工具输入增量都要从 rawJson() 解析,见流式输出。 UserReplayMessage 是 CLI 回放到消息流的用户消息(type=user)。它与 UserInput 是不同类型,不能直接再次发送。

Hook 通知消息

Hook 运行时的通知,只在 SessionOptions.Builder.includeHookEvents(true) 时进入消息流。关闭该选项不影响 Hook 回调执行。这些消息只是通知,应用不需要回复;拦截或修改执行请用 HookHandler。
public final class HookStartedMessage extends SystemMessage {   // subtype=hook_started
    public static final String SUBTYPE = "hook_started";
    public Optional<String> hookId();
    public Optional<String> hookName();
    public Optional<String> hookEvent();
    public Optional<String> stdout();
    public Optional<String> stderr();
    public Optional<String> output();
    public Optional<String> outcome();
    public Optional<Integer> exitCode();
}
// HookProgressMessage(hook_progress)、HookResponseMessage(hook_response)的访问器相同

public final class HookEventMessage extends SdkMessage {        // type=hook_event
    public String eventName();
}
访问器对应同名的 snake_case 字段(如 hook_id、exit_code),outcome() 原样保留未知值。字段缺失或类型不符时返回空;exitCode() 在值不是 int 范围内的整数时返回空,不做截断。原始值仍可从 rawJson() 读取。 HookEventMessage 对应旧的 type=hook_event 消息。SDK 不会把上面三种 system 通知转换成它。

MemoryGenerationMessage 与 MemoryConsumptionMessage

记忆生成和记忆加载的结果,都不终结对话轮次。结果类型位于 com.qoder.sdk.memory,用法见 Memory。
public final class MemoryGenerationMessage extends SystemMessage {     // subtype=memory_generation
    public MemoryGenerationResult result();
}

public final class MemoryConsumptionMessage extends SystemMessage {    // subtype=memory_consumption
    public MemoryConsumptionResult result();
}
MemoryGenerationResult 包含生成状态、写入和失败的文件、耗时及可选的独立用量;flush 成功不代表文件都已保存,应检查 status() 和 failedFiles()。MemoryConsumptionResult 包含整体状态和每个文件的加载状态。未知状态映射为 UNKNOWN,原始值保留在 wireStatus()。注册结果回调不会把这两种消息从消息流中移除。

MirrorErrorMessage 与 UnknownMessage

public final class MirrorErrorMessage extends SdkMessage {}   // type=mirror_error

public final class UnknownMessage extends SdkMessage {
    public Optional<String> wireSubtype();
}
MirrorErrorMessage 在外置会话存储的镜像写入失败时递送。它没有专用访问器,错误文本在 rawJson() 的 error 字段中,已脱敏,格式不稳定,不要按字符串匹配;归类时结合 sessionId() 和诊断类型 mirror_append_failed。 UnknownMessage 承载 SDK 不认识的顶层消息类型:wireType() 返回原始值,wireSubtype() 返回原始 subtype,完整负载在 rawJson()。

JSON Types

JsonValue 及子类型

SDK 自带的不可变 JSON 类型,位于 com.qoder.sdk.json,没有第三方依赖。Hook、MCP 等回调的负载使用这些类型;消息的 rawJson() 返回字符串,需要时用 JsonValue.parse(...) 解析。
public abstract class JsonValue {
    public static JsonValue parse(String json);
    public final String toJson();

    public boolean isNull();
    public boolean isBoolean();
    public boolean isNumber();
    public boolean isString();
    public boolean isArray();
    public boolean isObject();

    public boolean asBoolean();
    public BigDecimal asNumber();
    public String asString();
    public JsonArray asArray();
    public JsonObject asObject();
}

public final class JsonNull extends JsonValue {
    public static final JsonNull INSTANCE;
}

public final class JsonBoolean extends JsonValue {
    public static final JsonBoolean TRUE;
    public static final JsonBoolean FALSE;
    public static JsonBoolean of(boolean value);
    public boolean value();
}

public final class JsonNumber extends JsonValue {
    public static JsonNumber of(BigDecimal value);
    public static JsonNumber of(long value);
    public static JsonNumber of(double value);
    public BigDecimal value();
}

public final class JsonString extends JsonValue {
    public static JsonString of(String value);
    public String value();
}

public final class JsonArray extends JsonValue {
    public static JsonArray empty();
    public static JsonArray of(List<? extends JsonValue> values);
    public static JsonArray of(JsonValue... values);
    public List<JsonValue> values();
    public int size();
    public JsonValue get(int index);
}

public final class JsonObject extends JsonValue {
    public static JsonObject empty();
    public static JsonObject of(Map<String, ? extends JsonValue> members);
    public static Builder builder();
    public Map<String, JsonValue> members();
    public JsonValue get(String name);
    public boolean contains(String name);
    public Set<String> names();
    public int size();

    public static final class Builder {
        public Builder put(String name, JsonValue value);
        public JsonObject build();
    }
}
方法返回类型说明
asXxx()对应类型转换类型;类型不匹配时抛出 IllegalStateException
asNumber() / JsonNumber.value()BigDecimal数值以 BigDecimal 精确保存
JsonObject.get(String)JsonValue成员不存在时返回 null;用 contains 判断是否存在
JsonObject.members()Map<String, JsonValue>只读视图,保持插入顺序
JsonObject.Builder.put(String, JsonValue)Builder添加成员,同名时后者覆盖;名称和值都不能为 null
  • parse 遇到嵌套深度超过 1000 层,或单个数字超过 1024 个字符时,抛出 IllegalArgumentException。
  • JsonNumber.of(double) 不接受 NaN 和无穷大;JsonNumber.of(BigDecimal) 在序列化结果超过 1024 个字符时抛出 IllegalArgumentException。
JsonObject payload = JsonObject.builder()
        .put("path", JsonString.of("src/Parser.java"))
        .put("dryRun", JsonBoolean.TRUE)
        .build();

Diagnostics

DiagnosticsListener

接收 SDK 的结构化诊断事件,例如会话初始化、关闭超时、能力降级和托管 CLI 安装。
public interface DiagnosticsListener {
    void onDiagnostic(DiagnosticEvent event);
}
  • 两级注册。 客户端级用 QoderClient.Builder.diagnosticsListener(...),会话级用 SessionOptions.Builder.diagnosticsListener(...)。两者不叠加:会话设置了监听器时只用会话级,否则回退到客户端级。托管 CLI 的 managed_runtime_* 事件只发给客户端级监听器。
  • 线程。 每个会话在 SDK 自己的诊断线程上串行调用监听器,不使用 callbackExecutor。同一个监听器注册到多个会话时可能被并发调用,需要线程安全。
  • 尽力交付。 排队事件超过上限时直接丢弃新事件;监听器抛出异常只丢失这一次通知;关闭时只留有限时间排空,超时会中断监听器并丢弃剩余事件。诊断不能当作无损审计日志,也不能作为业务状态的唯一依据。
  • 返回要快。 耗时操作交给应用自己的异步机制。监听器运行在 SDK 线程上,需要关闭发出事件的会话或所属客户端时,调用 closeAsync() 后直接返回;在监听器里阻塞调用它们的 close() 会抛出 IllegalBlockingCloseException。
QoderClient client = QoderClient.builder()
        .auth(Auth.accessTokenEnv())
        .diagnosticsListener(event -> System.err.println(
                "[qoder-diag] type=" + event.type()
                + " requestType=" + event.requestType().orElse("-")
                + " exitCode=" + event.exitCode().map(String::valueOf).orElse("-")))
        .build();

DiagnosticEvent

不可变的诊断事件。SDK 产生的 detail 已脱敏,会清除已登记的凭据并尽量识别凭据形态的文本,但不保证覆盖任意业务数据。
public final class DiagnosticEvent {
    public static Builder builder(String type);
    public String type();
    public Optional<String> sessionId();
    public Optional<String> requestType();
    public Optional<Long> durationMs();
    public Optional<Integer> queueDepth();
    public Optional<Integer> exitCode();
    public Optional<String> capability();
    public Optional<String> detail();
    // Builder 为下表每个可选字段提供同名 setter,最后调用 build()
}
字段类型说明
typeString事件类型,必填,常见值见下表
sessionIdOptional<String>所属会话 ID
requestTypeOptional<String>相关的控制请求类型
durationMsOptional<Long>操作耗时(毫秒)
queueDepthOptional<Integer>队列深度快照
exitCodeOptional<Integer>CLI 退出码
capabilityOptional<String>相关的能力名
detailOptional<String>自由文本细节
应用用 builder(type) 自建事件时,SDK 不会对 detail 脱敏。

常见事件类型

type主要字段含义
session_initializedsessionId、detail初始化握手完成,detail 含协议版本
protocol_handshake_warningdetail握手时协议协商给出的警告
session_failedsessionId、exitCode、detail会话因错误终止
control_request_completedrequestType、durationMs控制请求收到成功响应
inbound_control_request_failedsessionId、detailCLI 发起的回调请求(权限、Hook、MCP、模型策略、认证 token 等)失败或超时,SDK 已向 CLI 回复错误。detail 含 request_id 和 error=<错误码>,例如 MODEL_RESOLUTION_TIMEOUT
capability_downgradedcapability、requestType已通过能力检查的控制请求在运行时被 CLI 判为不支持。之后按降级状态检查,已返回的 InitializationResult 不变
invalid_catalog_updatesessionId命令或模型目录更新无效,已保留上一份快照
memory_callback_faileddetailMemory 结果回调失败或被执行器拒绝;消息保留,会话继续
memory_drain_failed / memory_drain_timeoutdetail自动收尾时等待 Memory 失败或超时;主回答保留,资源继续释放
mirror_append_failedsessionId、detail外置会话存储镜像写入失败,同时递送 MirrorErrorMessage
mirror_drain_timeoutsessionId、detail关闭时镜像写入未在时限内完成
process_tree_cleanup_degradedcapability、detail当前环境无法完整清理 CLI 进程树;不影响初始化,也不代表子进程已回收
stdin_writer_stop_timeoutsessionId关闭时写线程没有按时停止,正在写的消息可能被截断,SDK 改为终止 CLI 进程
cli_process_alive_after_closedetail关闭流程结束后 CLI 进程仍在运行;detail 列出已执行的终止步骤
lane_shutdown_timeoutdetailSDK 内部线程没有在关闭时限内停止;忽略中断的回调可能在关闭后继续运行
spool_cleanup_deferreddetail关闭时消息缓冲的临时文件仍被占用,读写返回后再删除
private_directory_cleanup_faileddetail认证或 MCP 配置用的私有临时目录没有清理完成。detail 含 credential files may remain 时,凭据文件可能仍在磁盘上
session_store_cleanup_faileddetail外置会话恢复用的临时目录清理失败
managed_runtime_reuseddetail使用已缓存的托管 CLI
managed_runtime_install_started / managed_runtime_installeddetail开始安装、安装完成托管 CLI
managed_runtime_stale_install_removeddetail删除了损坏或不完整的旧安装
managed_runtime_unsupported_platformdetail当前平台没有托管 CLI,改为查找已安装的 CLI
SDK 后续版本可能增加新的事件类型,按 type 分支时请保留默认分支。

Exceptions

所有 SDK 异常都继承非受检的 QoderException。同步方法直接抛出;异步方法通过返回的 CompletionStage 失败,用 join() 等待时会被包装成 CompletionException,用 getCause() 取出原异常。Agent 业务失败不抛异常,而是以 outcome() 为 ERROR 的 ResultMessage 结束本轮。
RuntimeException
└── QoderException
    ├── AuthenticationException
    ├── CliNotFoundException
    ├── RuntimeInstallException
    ├── CliProcessException
    ├── ProtocolException
    │   ├── FrameTooLargeException
    │   ├── MalformedUtf8Exception
    │   └── ProtocolVersionMismatchException
    ├── ControlRequestTimeoutException
    ├── UnsupportedCliCapabilityException
    ├── SessionClosedException
    ├── SessionStoreConflictException
    ├── SessionStoreDeletedException
    ├── SpoolIoException
    ├── SpoolCapacityExceededException
    ├── MessageStreamAlreadyConsumedException
    ├── ConsumerInterruptedException
    ├── CallbackExecutionException
    ├── CallbackRejectedException
    ├── IllegalBlockingCloseException
    ├── InvalidOptionsException
    └── UnsupportedSecureSecretTransportException

QoderException

public class QoderException extends RuntimeException {
    public QoderException(String code, String message, boolean retryable);
    public QoderException(String code, String message, boolean retryable, Throwable cause);
    public final String getCode();
    public final boolean isRetryable();
}
方法返回类型说明
getCode()String稳定的机器可读错误码,见异常一览
isRetryable()boolean是否值得重试
异常的消息和 cause 在构造时脱敏,printStackTrace() 输出脱敏后的快照。SDK 不会自动重试,当前内置异常的 isRetryable() 都返回 false;是否新建会话重试,由应用按错误码判断。

UnsupportedCliCapabilityException

public class UnsupportedCliCapabilityException extends QoderException {
    public static final String CODE = "UNSUPPORTED_CLI_CAPABILITY";
    public CapabilityState capabilityState();
}
以下情况抛出:
  • 调用的方法需要 CLI 能力,而 CLI 声明不支持(UNSUPPORTED)或从未声明(UNKNOWN)。SDK 在本地直接失败,不向 CLI 发送请求。所需能力见能力要求。
  • 基础控制请求在运行时被 CLI 拒绝。
  • SessionRepository 的本地操作需要可枚举的存储,而当前存储不支持。
capabilityState() 返回触发失败的 CapabilityState。

异常一览

除下表列出的访问器外,各异常类只有构造器,错误码常量为 CODE。
类错误码何时抛出
AuthenticationExceptionAUTHENTICATION_FAILED、CLI_AUTH_FATAL认证失败。CLI 以退出码 41 报告致命认证失败时错误码为 CLI_AUTH_FATAL(常量 CODE_CLI_AUTH_FATAL),不应按进程失败重试
CliNotFoundExceptionCLI_NOT_FOUND找不到 CLI:显式路径无效,或自动查找没有找到可执行文件
RuntimeInstallExceptionRUNTIME_INSTALL_FAILED托管 CLI 安装失败:禁止下载且没有本地压缩包、网络失败或超时、大小或 SHA-256 校验不符等。SDK 不会改用其他版本,见 ManagedRuntimeOptions
CliProcessExceptionCLI_PROCESS_FAILEDCLI 进程非正常退出(认证类退出码除外)。getExitCode() 返回退出码,getStderrTail() 返回有界且已脱敏的 stderr 尾部,不可用时为 null
ProtocolExceptionPROTOCOL_ERRORCLI 输出违反协议,下面三个子类以外的情况
FrameTooLargeExceptionFRAME_TOO_LARGE单条消息超过大小上限
MalformedUtf8ExceptionMALFORMED_UTF8CLI 输出不是合法的 UTF-8
ProtocolVersionMismatchExceptionPROTOCOL_VERSION_MISMATCHCLI 的协议版本与 SDK 不兼容
ControlRequestTimeoutExceptionCONTROL_REQUEST_TIMEOUT控制请求在 controlRequestTimeoutMs(默认 60000 毫秒)内没有收到响应
UnsupportedCliCapabilityExceptionUNSUPPORTED_CLI_CAPABILITY见上文
SessionClosedExceptionSESSION_CLOSED在已关闭的客户端、会话或查询上继续操作;完整关闭过程中 CLI 先退出时,消费者也会收到它
SessionStoreConflictExceptionSESSION_STORE_CONFLICT会话存储版本冲突(expectedVersion 已过期),或同一 UUID 对应不同内容。currentVersion() 返回存储的当前版本
SessionStoreDeletedExceptionSESSION_STORE_DELETED见 SessionStoreDeletedException
SpoolIoExceptionSPOOL_IO_ERROR消息缓冲的磁盘文件读写失败
SpoolCapacityExceededExceptionSPOOL_CAPACITY_EXCEEDED未消费的消息超过缓冲上限,消息流失败。及时消费消息流可以避免
MessageStreamAlreadyConsumedExceptionMESSAGE_STREAM_ALREADY_CONSUMED对同一个 MessageStream 第二次消费;openCursor() 和 consume() 合计只能调用一次
ConsumerInterruptedExceptionCONSUMER_INTERRUPTED阻塞在 MessageCursor.hasNext() 或 next() 上的线程被中断,线程的中断标志保留
CallbackExecutionExceptionCALLBACK_EXECUTION_FAILED应用回调(权限、Hook、MCP、消息处理器)抛出异常
CallbackRejectedExceptionCALLBACK_REJECTEDcallbackExecutor 拒绝执行回调任务,例如线程池已关闭或队列已满
IllegalBlockingCloseExceptionILLEGAL_BLOCKING_CLOSE在资源自己的 SDK 线程或推送处理器里阻塞调用 close()。抛出前 SDK 已按 closeAsync() 发起关闭,见线程模型与资源释放
InvalidOptionsExceptionINVALID_OPTIONS配置无效,例如 QoderClient.Builder 缺少 auth,或 SessionOptions 同时设置了互斥选项
UnsupportedSecureSecretTransportExceptionUNSUPPORTED_SECURE_SECRET_TRANSPORT无法安全创建或写入认证、MCP 配置用的私有临时文件,包括 java.io.tmpdir 或其上级目录允许其他账号替换其中条目的情况。SDK 拒绝启动,不会改用明文参数或环境变量传递
以下错误码直接以 QoderException 抛出:
错误码何时抛出
CLI_START_FAILEDCLI 进程启动失败,例如权限不足、工作目录无效、参数过长。Windows 上 CLI 是 .cmd/.bat 且启动参数含 cmd.exe 特殊字符(包括括号,如 Bash(git:*))时也会拒绝启动,请把 qoderCliPath 指向 .exe
MODEL_RESOLUTION_TIMEOUT 不会抛给应用。动态模型选择回调超时时,SDK 把这个错误码回给 CLI,并发出 inbound_control_request_failed 诊断事件,见 ModelPolicyProvider。

会话终止错误码

以下错误码通过 QoderException.getCode() 暴露,可能出现在消息流或相关异步方法的异常中,原始原因用 getCause() 获取。出现后原会话不能继续使用,处理方式见错误处理。
错误码含义
QUERY_WRITE_FAILED一次性查询的输入没有写入 CLI
CONTROL_WRITE_FAILED控制请求或响应没有写入 CLI
TURN_ACCOUNTING_FAILED会话的轮次状态无法继续处理
MESSAGE_DELIVERY_FAILED一条缓冲消息无法解码,已丢失。消息流在此终止,所属的 Query 或 Session 同时关闭