Java SDK 的 Maven 坐标为
SDK 主入口,用于创建一次性查询、多轮会话和预热启动。线程安全,可以复用;关闭时级联关闭它创建的全部查询和会话。
参数错误和本地状态错误同步抛出,例如
一次性查询句柄,由
CLI 在本轮
多轮会话,由
部分方法需要 CLI 支持对应能力,否则以
推送的目录总是整体替换旧值,空列表表示 CLI 明确清空了目录。
命令或 skill 的元数据,位于
已完成启动、认证和初始化的 CLI,由
已转换或已关闭后再转换会抛出
查询或会话的消息流,支持两种互斥的消费方式:用
每个流只能消费一次,
拉取游标。
多轮会话应跨轮复用同一个游标,每轮读到
推送模式的消息处理器。
处理器抛出的异常使
查询和会话的不可变选项,用于
两个默认值也以常量提供:
一次性查询的请求,包含一条
发给 CLI 的一条用户输入,包含内容块、优先级、是否触发回复,以及可选的自定义上下文和 skill 选择。不可变。
内容块类型
本条输入显式选择的 skill。
名称不能为空或全是空白;
传给
列表构造器在应用种子时,按会话工作目录解析相对路径并读取文件当前的 mtime;
用户输入的优先级提示。SDK 不按优先级重排写入顺序,调度由 CLI 决定。
传给
传给
用
托管 CLI 的配置,通过
CLI 查找顺序。 启动时按以下顺序确定 CLI,找到即停止:
认证策略,传给
启动时的认证配置通过仅当前账号可读的临时文件交给 CLI,不放进启动参数或环境变量。
由宿主刷新短期 Service Account token 的回调。宿主自己保管长期密钥。
返回以新 token 完成的 stage。CLI 需要凭据时可能多次调用。token 经 SDK 控制通道交给 CLI,不要写入日志。
按作业提供 token 的回调。SDK 不向回调传入作业 ID,应用在闭包里绑定自己的作业身份。
返回以 token 完成的 stage,不要返回
SDK 管控的密钥值。
SDK 会对诊断事件和 stderr 中出现的已登记凭据做脱敏。这一机制不覆盖业务内容和第三方输出,应用仍需按自己的日志策略控制记录范围。
CLI 子进程默认只继承父进程的
工具权限回调,通过
返回以
一次工具权限请求。
可选字段缺失或类型不符时返回空
工具输入、预览和
显式审批范围,通过
权限回调的裁决:允许(可改写工具输入)或拒绝(附原因)。
请求带
权限模式。它是
回调收到的取消令牌。CLI 取消请求、回调超时或会话关闭时被取消。SDK 负责协议层的取消响应,应用只需停止相关工作。
取消是合作式的,忽略令牌的代码会继续运行。取消动作应短小、不阻塞,抛出的异常会被忽略。
Plan 状态可以独立于权限模式设置,开启 Plan 时保留当前权限模式。包
规则:
一条 Hook 注册:事件名、可选 matcher 和处理器。包
事件名和 matcher 由 CLI 匹配,处理器在 Java 进程内执行。
Hook 处理器。在 SDK 独立的 Hook 线程上调用,不使用
返回的 JSON 原样作为 Hook 输出交给 CLI,结构由 CLI 定义,见 Hooks 输出。空对象表示不干预;在
处理器抛出异常或 stage 异常完成时,只有这一次 Hook 调用以
一次 Hook 事件。
MCP 服务器配置,通过
对不适用的形态调用链式方法抛
进程内 MCP 服务器的 JSON-RPC 端点,通过
SDK 把返回的 JSON 原样作为 MCP 响应转发给 CLI,响应需要自带
一个 MCP 服务器的状态,由
CLI 可能在
用于 MCP OAuth。流程:用
授权 URL 和回调 URL 可能包含敏感的 OAuth state,不要写入日志;两个类的
MCP elicitation 回调:MCP 服务器需要向用户补充收集信息时触发。通过
回调在
elicitation 的结果。
模型相关类型位于
模型选择策略,传给
初始化时 SDK 只向 CLI 声明回调,不会调用它。回调在 CLI 每次需要模型决策时才运行,不要依赖它在
超时由
返回
Builder 方法不接受
一次模型请求的上下文,由 SDK 传给模型回调。
CLI 没有提供的字段为
模型目录中的一条记录,由
模型 ID 的值类型。
Memory 配置通过
结果也会以
外部会话存储相关类型。
单次会话使用哪种历史存储,通过
最终生效的外部存储不能与
外部会话存储需要实现的接口。客户端通过
两个方法可以同步完成,但失败必须通过异常完成的 stage 报告,不能直接抛出。不要在调用线程上执行阻塞 I/O,应使用异步客户端或应用自己的线程池。存储应原样保存每条 transcript JSON(包括未知字段),不要再包一层信封。
镜像写入失败通过
可枚举的存储。
支持整个会话删除的存储,
实现应在与追加相同的事务或临界区内删除根 transcript 和全部子路径,并永久标记该会话已删除;之后对该会话的写入都以
SDK 内置的内存实现,实现
外部存储写入(镜像)的资源上限,通过
单个键写入失败或超时后,该键停止写入,其他键不受影响;超出会话总上限时停止整个会话的镜像。这些错误通过
读取、继续、分叉或删除已保存的会话。用
作用域规则:
初始化握手的结果,由
运行中如果 CLI 以"不支持"拒绝某个受能力控制的请求,SDK 会把该能力降为
下列方法或配置需要 CLI 在握手时声明对应能力。能力状态不是
CLI 能力的三种状态。
成功只表示 CLI 接受了中断请求,轮次何时结束以
只需要写入确认时用
消息流中所有消息的抽象基类,位于
具体类型:
专用 system 消息都继承
助手产生一条回复时递送。内容块是输出侧类型,与输入侧的
Java SDK 只把
一轮对话结束时递送。Agent 业务失败(例如达到轮次上限)通过
CLI 发出的系统消息。
Hook 运行时的通知,只在
访问器对应同名的 snake_case 字段(如
记忆生成和记忆加载的结果,都不终结对话轮次。结果类型位于
SDK 自带的不可变 JSON 类型,位于
接收 SDK 的结构化诊断事件,例如会话初始化、关闭超时、能力降级和托管 CLI 安装。
不可变的诊断事件。SDK 产生的
应用用
SDK 后续版本可能增加新的事件类型,按
所有 SDK 异常都继承非受检的
异常的消息和 cause 在构造时脱敏,
以下情况抛出:
除下表列出的访问器外,各异常类只有构造器,错误码常量为
以下错误码直接以
以下错误码通过
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 主入口,用于创建一次性查询、多轮会话和预热启动。线程安全,可以复用;关闭时级联关闭它创建的全部查询和会话。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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() 返回。
| 方法 | 返回类型 | 说明 |
|---|---|---|
messages() | MessageStream | 获取消息流 |
initialization() | CompletionStage<InitializationResult> | 初始化握手结果 |
interrupt() | CompletionStage<InterruptResult> | 中断正在运行的轮次 |
flushMemory() | CompletionStage<Void> | 等待已触发的 Memory 处理结束;完成不代表全部写入成功 |
refreshMemory() | CompletionStage<Void> | 重新加载 Memory 消费配置 |
closeAsync() / close() | CompletionStage<Void> / void | 关闭查询并释放资源 |
result 之后退出,但仍应把消息流消费到结束,然后关闭句柄或游标。启用 Memory 时,ResultMessage 之后还可能收到 Memory 消息。ResultOutcome.ERROR 表示本轮失败,没有抛异常不等于成功。
QoderSession
多轮会话,由 QoderClient.openSession() 或 PreparedQuery.toSession() 返回。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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 | 关闭会话并释放资源 |
UnsupportedCliCapabilityException 失败,不会发送请求,见能力要求。关闭会话时,还没写入的排队输入以 SessionClosedException 结束;需要确保已接受的输入写入 CLI,先等待 completeInput()。
运行时目录
| 方法 | 数据来源 | 是否发请求 |
|---|---|---|
commandCatalog() / supportedCommands() | 初始化时的命令目录;CLI 推送命令变更后整体替换 | 否 |
modelCatalogSnapshot() | 初始化或 CLI 推送的最新完整模型目录 | 否 |
getAvailableModels(...) | 本次查询结果,不更新上面的快照 | 是 |
reloadSkills() | 重新加载后的 skills 列表,不替换完整命令目录 | 是 |
modelCatalogSnapshot() 返回 Optional.empty() 表示还没收到目录。需要当前账号最新的模型列表时,调用 getAvailableModels(...)。
CommandInfo
命令或 skill 的元数据,位于 com.qoder.sdk.command。
name() 一定有值;raw() 保留 CLI 返回的全部字段,包括 SDK 未建模的字段。
PreparedQuery
已完成启动、认证和初始化的 CLI,由 QoderClient.prepare() 返回,用于消除首次请求的启动延迟。只能使用一次:转为查询、转为会话或直接关闭。
| 方法 | 返回类型 | 说明 |
|---|---|---|
initialization() | CompletionStage<InitializationResult> | 初始化握手结果 |
toQuery(UserInput) | QoderQuery | 转为一次性查询 |
toSession() | QoderSession | 转为多轮会话 |
closeAsync() / close() | CompletionStage<Void> / void | 丢弃未使用的预热启动 |
IllegalStateException。转换后,资源由新的 QoderQuery 或 QoderSession 负责释放,关闭 PreparedQuery 不再影响它们。toQuery() 的输入被拒绝时,返回的查询的消息流以该错误结束。
MessageStream
查询或会话的消息流,支持两种互斥的消费方式:用 openCursor() 拉取,或用 consume() 由 SDK 推送。
| 方法 | 返回类型 | 说明 |
|---|---|---|
openCursor() | MessageCursor | 打开拉取游标,按顺序逐条读取 |
consume(MessageHandler, Executor) | CompletionStage<Void> | 在给定执行器上按顺序逐条调用处理器;上一条处理完才投递下一条 |
consume() 返回的 stage 在流结束时正常完成。以下情况异常完成,并关闭所属的查询或会话:
| 情况 | 异常 |
|---|---|
| 处理器抛出异常 | CallbackExecutionException |
| 执行器拒绝任务 | CallbackRejectedException |
| 消费线程被中断 | ConsumerInterruptedException |
| 读取或解码消息失败 | 流的失败原因;非 SDK 异常包装为错误码 MESSAGE_DELIVERY_FAILED |
openCursor() 和 consume() 二选一;第二次订阅抛出 MessageStreamAlreadyConsumedException,第一次订阅即使失败也算已消费。CLI 停止输出后,已缓冲的消息仍可读取。
MessageCursor
拉取游标。
| 方法 | 返回类型 | 说明 |
|---|---|---|
hasNext() | boolean | 阻塞等待下一条消息;流结束返回 false,流已失败时抛出失败原因 |
next() | SdkMessage | 返回下一条消息;流已结束时抛出 NoSuchElementException |
close() | void | 关闭游标,同时异步关闭所属的查询或会话 |
ResultMessage 即可发送下一轮,不要每轮关闭再重开。某条消息读取失败后流就此终止,之后每次 hasNext() / next() 都抛出同一个异常;此时关闭游标,需要时用 resume 新建会话。消费线程被中断时抛出 ConsumerInterruptedException。
MessageHandler
推送模式的消息处理器。
consume() 返回的 stage 以 CallbackExecutionException 完成。使用直接执行器(如 Runnable::run)时,处理器运行在 SDK 的投递线程上,必须尽快返回。
线程模型与资源释放
- 异步方法返回
CompletionStage,每次调用得到独立的观察结果。取消或手工完成toCompletableFuture()只影响这一次等待,不会取消 SDK 操作。要停止操作,使用interrupt()、cancelQueuedMessage()或closeAsync()。 openSession()和prepare()返回的句柄一旦交付就归应用所有,即使从未调用get(),也要负责关闭。观察 future 在交付前已被取消时,SDK 会自动关闭启动好的资源。- 每个查询、会话和预热启动各占一个 CLI 子进程,应用应限制并发数量。
QoderClient本身开销很小,可以复用;关闭客户端会级联关闭它创建的全部资源。 - 权限、MCP、模型策略、认证 token 和 Memory 回调在
callbackExecutor上运行,Hook 使用 SDK 独立线程。未配置callbackExecutor时,同一会话的这些回调在一条 SDK 线程上串行执行,一个回调阻塞会推迟其他回调,可能导致超时。回调应尽快返回,耗时工作交给应用自己的执行器,通过返回的 stage 报告完成。 - 不要在 SDK 回调、推送处理器或 SDK 线程中阻塞等待;在这些位置关闭所属会话或客户端时,调用
closeAsync()后直接返回。在 SDK 自有线程或推送处理器中调用阻塞式close(),会先发起异步关闭,再抛出IllegalBlockingCloseException。SDK 自有线程包括未配置callbackExecutor时运行回调的线程,以及运行 Hook 和诊断监听器的线程;会话或查询的close()只检查它自己的这些线程,客户端的close()检查它创建的全部会话和查询。推送处理器即使运行在应用执行器上也会抛出。配置了自定义callbackExecutor时,回调运行在应用线程上,在其中调用close()不会抛出该异常,而是阻塞到关闭完成。 closeAsync()完成表示 SDK 已停止接收输入,排队输入和控制请求已经结算,CLI 进程已走完关闭流程(关闭 stdin,超时后依次请求终止和强制终止)。它不等待已在运行的应用回调或 continuation 返回,也不保证 CLI 的后代进程已退出、临时文件已删除。启用 Memory 时,空闲会话关闭前会先完成 Memory 收尾,最长等待drainTimeoutMs(默认 60 秒)。
Configuration
SessionOptions
查询和会话的不可变选项,用于 openSession()、prepare() 和 OneShotQueryRequest。
SessionOptions.Builder
| 分组 | 方法 | 默认值 | 说明 |
|---|---|---|---|
| Runtime | workingDirectory(Path) | 客户端 projectDirectory,其次进程当前目录 | CLI 工作目录 |
| Runtime | qoderCliPath(Path) | 托管 CLI | 显式指定 CLI 可执行文件;设置后不下载托管 CLI,路径无效直接失败 |
| Runtime | putEnvironment(String, String) | — | 添加一个传给 CLI 的环境变量;代理 URL 变量自动按机密处理 |
| Runtime | putProxyEnvironment(String, SecretValue) | — | 以 SecretValue 添加代理 URL 变量;变量名必须是可识别的代理变量(如 HTTPS_PROXY),否则抛 IllegalArgumentException |
| Runtime | environment(Map<String, String>) | — | 批量添加环境变量,追加而不是替换 |
| Prompt | systemPrompt(String) | 不替换 | 替换系统提示词 |
| Prompt | appendSystemPrompt(String) | 不追加 | 追加到系统提示词末尾 |
| Prompt | customContext(Map<String, String>) | 未设置 | 本会话输入的默认自定义上下文,见 UserInput |
| Prompt | maxTurns(int) | 不限制 | 最大轮次,必须为正数 |
| Prompt | includePartialMessages(boolean) | false | 投递增量的 PartialMessage,见流式输出 |
| Model | modelSelection(ModelSelection) | CLI 默认模型 | 固定模型、动态回调或结构化策略,见 ModelSelection |
| Tools | tools(List<String>) | CLI 默认 | 基础工具列表;空列表等同于未设置 |
| Tools | allowedTools(List<String>) | — | 预授权放行的工具 |
| Tools | disallowedTools(List<String>) | — | 禁止的工具,优先级高于 allowedTools |
| Permission | permissionMode(PermissionMode) | 由 CLI 决定 | 权限模式,见 PermissionMode |
| Permission | planMode(boolean) | 未设置 | 独立 Plan 的启动状态;与 permissionMode(PLAN) 互斥,见独立 Plan |
| Permission | allowDangerouslySkipPermissions(boolean) | false | BYPASS_PERMISSIONS 的第二道开关 |
| Permission | canUseTool(ToolPermissionHandler) | 无 | 工具权限回调,见 ToolPermissionHandler |
| MCP | mcpServers(List<McpServerConfig>) | — | MCP 服务器配置,名称不能重复,见 McpServerConfig |
| MCP | allowedMcpServerNames(List<String>) | — | 允许使用的 MCP 服务器名称 |
| MCP | onElicitation(ElicitationHandler) | 无 | MCP elicitation 回调,见 ElicitationHandler |
| Hooks | hooks(List<HookRegistration>) | — | Hook 注册,见 HookRegistration |
| Hooks | includeHookEvents(boolean) | false | 在消息流中投递 Hook 通知消息;不影响 Hook 回调本身 |
| Session | resume(String) | — | 按会话 ID 恢复;ID 只能包含 ASCII 字母、数字、_ 和 - |
| Session | continueSession(boolean) | false | 继续最近一次会话;配置了可列举的外部存储时,按存储中的时间戳选择 |
| Session | forkSession(boolean) | false | 与 resume 或 continueSession 配合,分叉为新会话 |
| Session | sessionStorage(SessionStorage) | SessionStorage.inherit() | 继承客户端设置、使用 CLI 本地存储或外部存储,见 SessionStorage |
| Session | sessionMirrorOptions(SessionMirrorOptions) | defaults() | 外部存储镜像的批量和等待上限,见 SessionMirrorOptions |
| Session | fileCheckpointing(boolean) | false | 启用文件检查点,见 Checkpoint |
| Memory | memory(MemoryOptions) | 不启用 | 原生或应用托管的 Memory,见 Memory |
| Control | controlRequestTimeoutMs(long) | 60000 | 控制请求超时(毫秒),0 表示不限制,负数非法 |
| Control | resolveModelTimeoutMs(long) | 500 | 动态模型回调的超时(毫秒),0 表示不限制,负数非法 |
| Control | debug(boolean) | false | 开启 CLI 调试输出 |
| Control | diagnosticsListener(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。
of(prompt) 使用单条文本输入和默认选项。Builder 的 prompt() 和 input() 只能设置其中一个,重复设置抛出 InvalidOptionsException;两者都没设置时 build() 抛出同一异常。未设置 options() 时使用默认选项。
UserInput
发给 CLI 的一条用户输入,包含内容块、优先级、是否触发回复,以及可选的自定义上下文和 skill 选择。不可变。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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.TextContent | String text() |
UserInput.ImageContent | String mediaType()、String base64Data() |
UserInput.UrlImageContent | URI url() |
UserInput.ToolResultContent | String toolUseId()、String content() |
customContext 未设置时继承 SessionOptions 的默认值;显式设置时完整替换默认值,不做合并。显式设置的 customContext(包括空 map)和非空的 selectedSkills 需要 CLI 支持对应能力,见能力要求。会话中被拒绝的输入由 send() 的 stage 报告;查询和 PreparedQuery.toQuery() 的输入被拒绝时,消息流以该错误结束。
SelectedSkill
本条输入显式选择的 skill。
arguments 原样传给 CLI。
ReadStateSeed
传给 seedReadState(),告诉 CLI 宿主应用已向用户展示过哪些文件。
of(path, mtime) 使用宿主已观测到的 mtime(epoch 毫秒)。只为实际展示过的文件设置;该调用不会把文件内容加入输入。
SendPriority
用户输入的优先级提示。SDK 不按优先级重排写入顺序,调度由 CLI 决定。
| 值 | 说明 |
|---|---|
NOW | 尽快处理(默认) |
NEXT | 排在当前处理的消息之后 |
LATER | 排在所有已排队消息之后 |
FlagSettings
传给 applyFlagSettings() 的特性开关。
flags 是开关名到布尔值的映射,必填;flags() 返回不可变副本。
ProxySettings
传给 setProxy() 的代理设置。
ProxySettings.of(SecretValue.of(url)) 设置代理,clear() 清除代理。代理 URL 及其中的用户名、密码按机密登记,SDK 在诊断信息中对其脱敏。已弃用的 String 构造器和 proxyUrl() 会暴露明文,不要用于日志。
ManagedRuntimeOptions
托管 CLI 的配置,通过 QoderClient.Builder.managedRuntime() 设置。作用于未指定 qoderCliPath 的启动和 installRuntime()。
| Builder 方法 | 默认值 | 说明 |
|---|---|---|
cacheDirectory(Path) | ~/.qoder/runtimes/managed | 缓存目录,应位于本地磁盘;null 恢复默认 |
downloadPolicy(DownloadPolicy) | ALLOW | DISALLOW 时从不联网,缓存和本地压缩包都没有时失败 |
baseUrl(URI) | 官方下载地址 | 镜像地址,必须是不含用户信息的绝对 https 地址(回环地址可用 http) |
authorization(SecretValue) | — | 访问镜像时的 Authorization 头;只发给配置的主机,重定向到其他主机时不转发 |
localArchive(Path) | — | 本地的锁定压缩包,优先于下载使用 |
connectTimeoutMs(long) / readTimeoutMs(long) | 30000 | 连接和单次读取超时(毫秒),必须为正数 |
totalTimeoutMs(long) | 600000 | 整个下载的超时(毫秒),必须为正数 |
build() | — | 超时不为正数或 baseUrl 不合法时抛出 InvalidOptionsException |
SessionOptions.Builder.qoderCliPath()指定的路径;路径无效直接失败。- 环境变量
QODERCLI_PATH。 - 托管 CLI:复用缓存,否则依次从
localArchive、classpath 资源META-INF/qoder-sdk/runtime/<文件名>或下载地址安装。当前平台不在托管范围时跳过;安装失败直接报错,不会改用其他 CLI。 ~/.qoder/runtimes/下旧版 SDK 留下的同版本 CLI。~/.local/bin/qodercli,然后是PATH。
| 场景 | 做法 |
|---|---|
| 本机开发 | 使用默认配置,首次启动自动下载 |
| 构建镜像时预热 | 在镜像构建阶段运行 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。构造器不公开,只能通过静态工厂获得实例,实例不可变。
| 工厂方法 | 凭据来源 | 典型场景 |
|---|---|---|
qoderCli() | 复用本机 CLI 已保存的登录状态 | 开发者本机 |
accessToken(SecretValue) | 调用方传入的 access token | CI、脚本、代表用户运行的服务 |
accessTokenEnv() | 启动时读取环境变量 QODER_PERSONAL_ACCESS_TOKEN | CI、容器部署 |
accessTokenEnv(String) | 启动时读取调用方指定的环境变量 | 自定义密钥注入 |
serviceAccountKey(SecretValue) | Service Account Key,由 SDK 和 CLI 换取短期 token | 组织内无人值守服务 |
serviceAccountTokenProvider(ServiceAccountTokenProvider) | 宿主按需刷新的 Service Account token,长期密钥不进入 SDK | 自建 token 基础设施 |
jobTokenProvider(JobTokenProvider) | 宿主按作业提供的 token | 多租户作业运行器 |
java.io.tmpdir 及其各级上级目录必须不允许其他账号替换其中的条目,否则启动以 UnsupportedSecureSecretTransportException 失败;需要时用启动参数 -Djava.io.tmpdir= 指定当前账号独占的目录。CLI 因认证失败退出时抛出 AuthenticationException,先修正凭据或账号权限,再重新创建 Query 或 Session,直接重试同一凭据没有意义。
各方式的完整用法见认证。
ServiceAccountTokenProvider
由宿主刷新短期 Service Account token 的回调。宿主自己保管长期密钥。
JobTokenProvider
按作业提供 token 的回调。SDK 不向回调传入作业 ID,应用在闭包里绑定自己的作业身份。
null。CLI 需要时可能多次调用。
SecretValue
SDK 管控的密钥值。toString() 恒为 [REDACTED],equals 和 hashCode 的耗时只与长度有关。
| 方法 | 返回类型 | 说明 |
|---|---|---|
of(char[]) | SecretValue | 复制传入数组后创建 |
of(String) | SecretValue | 由字符串创建 |
reveal() | char[] | 返回明文副本,调用方用完后自行清零 |
length() | int | 密钥长度 |
子进程环境
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 按自身规则处理需要询问的调用。
| 参数 | 类型 | 说明 |
|---|---|---|
request | ToolPermissionRequest | 待裁决的工具调用 |
cancellation | CancellationToken | CLI 放弃该请求、请求超时或会话关闭时取消 |
PermissionResult 完成的 stage,不得为 null。回调在 callbackExecutor 上执行,见线程模型与资源释放。回调抛出异常或 stage 异常完成时,只有这一次权限请求以 CALLBACK_EXECUTION_FAILED 失败,会话不受影响;超过 controlRequestTimeoutMs 未完成时以 CONTROL_REQUEST_TIMEOUT 失败,令牌同时被取消。用法见权限控制和审批与用户输入。
ToolPermissionRequest
一次工具权限请求。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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(...) 使用。
| 常量 | 含义 | outcome() |
|---|---|---|
ONCE | 只批准本次调用 | proceed_once |
SESSION | 本次 CLI 会话内始终批准 | proceed_always |
PERSIST | 请求 CLI 持久保存批准 | proceed_always_and_save |
PERSIST 由 CLI 负责保存,SDK 不自建权限存储,也不保证所有权限种类都支持。
PermissionResult
权限回调的裁决:允许(可改写工具输入)或拒绝(附原因)。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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 增加的模式。
| 常量 | value() | 说明 |
|---|---|---|
DEFAULT | default | 按 CLI 规则请求权限 |
ACCEPT_EDITS | acceptEdits | 自动接受文件编辑 |
BYPASS_PERMISSIONS | bypassPermissions | 跳过全部权限检查;必须同时设置 allowDangerouslySkipPermissions(true) |
PLAN | plan | 只规划不执行;需要和其他模式并存时改用独立 Plan |
DONT_ASK | dontAsk | 不询问,需要询问的操作直接拒绝 |
AUTO | auto | 自动裁决 |
of(String) 对已知模式忽略大小写和字母数字以外的字符,"yolo" 等同 BYPASS_PERMISSIONS。启动时用 SessionOptions.Builder.permissionMode(...) 设置,运行中用 QoderSession.setPermissionMode(...) 切换;两处使用 BYPASS_PERMISSIONS 都要求该会话设置了 allowDangerouslySkipPermissions(true),否则以 InvalidOptionsException 失败。各模式的行为见权限控制。
CancellationToken
回调收到的取消令牌。CLI 取消请求、回调超时或会话关闭时被取消。SDK 负责协议层的取消响应,应用只需停止相关工作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
isCancelled() | boolean | 是否已取消;一旦为 true 不再改变 |
onCancel(Runnable) | void | 注册取消动作;令牌已取消时也会调用。动作异步执行,不在注册线程内联运行,也不使用 callbackExecutor |
registerOnCancel(Runnable) | CancellationRegistration | 同上,并返回可注销的句柄 |
CancellationRegistration.close() | void | 注销动作,幂等、不阻塞;已开始执行的动作不会被打断 |
onCancel 动作是尽力交付,会话关闭时排队中的动作可能被丢弃;需要确定停止的工作应在自己的副作用边界检查 isCancelled()。长期存在的工作建议用 registerOnCancel,结束时调用 close() 释放动作及其捕获的对象。
独立 Plan
Plan 状态可以独立于权限模式设置,开启 Plan 时保留当前权限模式。包 com.qoder.sdk.plan。
| 方法 | 说明 |
|---|---|
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>) 注册。
| 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 |
timeoutMs 只约束 Java 侧等待,不会传给 CLI。需要在消息流中看到 Hook 生命周期事件时,再设置 includeHookEvents(true),对应消息见 HookEventMessage。可用事件和用法见 Hooks。
HookHandler
Hook 处理器。在 SDK 独立的 Hook 线程上调用,不使用 callbackExecutor;耗时工作交给应用执行器,并返回表示完成的 stage。
PreToolUse 中返回以下结构可以拒绝本次工具调用:
CALLBACK_EXECUTION_FAILED 失败;超时以 CONTROL_REQUEST_TIMEOUT 失败。
HookEvent
一次 Hook 事件。
| 方法 | 返回类型 | 说明 |
|---|---|---|
eventName() | String | 事件名,取自 CLI 输入中的 hook_event_name |
matcher() | Optional<String> | CLI 回传的 matcher,通常为空 |
payload() | JsonValue | CLI 的 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。实例不可变。
| 工厂 | type() | 说明 |
|---|---|---|
stdio(name, command, args) | stdio | CLI 启动的子进程服务器;args 为 null 时视为空列表 |
sse(name, url) | sse | SSE 远程服务器 |
http(name, url) | http | Streamable HTTP 远程服务器 |
inProcess(name, handler) | sdk | JVM 进程内服务器,消息经 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) 注册。
| 方法 | 说明 |
|---|---|
handle(JsonValue) | 处理一条 JSON-RPC 消息,返回以响应完成的 stage |
handle(JsonValue, CancellationToken) | SDK 实际调用的入口,默认委托单参数版本;需要感知取消时覆盖它,见 CancellationToken |
newConnection() | 每个会话注册该服务器时调用一次,默认返回自身;有连接状态的处理器应返回独立实例,不能返回 null |
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() 返回。
| 方法 | 返回类型 | 说明 |
|---|---|---|
name() | String | 服务器名 |
status() | String | 状态字符串,取值以 CLI 上报为准 |
raw() | JsonValue | 原始状态 |
raw() 中回显服务器的有效配置,包括 env 和 headers。通过 withSecretEnv、withSecretHeader 等登记的密钥会被替换为 [REDACTED],名称和状态文本同样处理。
McpAuthenticationResult 和 McpAuthenticationOptions
用于 MCP OAuth。流程:用 QoderSession.beginMcpAuthentication(serverName[, options]) 发起认证;结果需要用户操作时,宿主展示 authUrl(),拿到完整回调 URL 后调用 mcpSubmitOAuthCallbackUrl(serverName, callbackUrl)。SDK 不打开浏览器,state 校验、凭据交换和重连由 CLI 完成。建议在发送第一条业务输入前完成认证。
| 方法 | 说明 |
|---|---|
requiresUserAction() | 是否需要宿主完成用户授权;为 false 时认证已完成 |
authUrl() | 授权入口;需要用户操作时一定存在 |
McpAuthenticationOptions.defaults() | 使用 CLI 默认回调地址 |
McpAuthenticationOptions.redirectUri(String) | 指定宿主接收回调的 URI,不能为空白 |
toString() 都不包含 URL。完整流程见 MCP OAuth 认证。
ElicitationHandler
MCP elicitation 回调:MCP 服务器需要向用户补充收集信息时触发。通过 SessionOptions.Builder.onElicitation(...) 注册。
callbackExecutor 上执行,受 controlRequestTimeoutMs 约束。抛出异常或 stage 异常完成时,只有这一次 elicitation 以 CALLBACK_EXECUTION_FAILED 失败。
ElicitationRequest
ElicitationResult
elicitation 的结果。
| 工厂 | action() | 说明 |
|---|---|---|
accept(JsonValue) | accept | 用户接受,携带按 schema 填写的内容;内容为 null 时抛出 NullPointerException |
decline() | decline | 用户拒绝回答 |
cancel() | cancel | 用户取消本次请求 |
Models
模型相关类型位于 com.qoder.sdk.model 包。选择方式与回调用法见 Model Policy。
ModelSelection
模型选择策略,传给 SessionOptions.Builder.modelSelection(...)。三种策略只能选一种。
| 工厂方法 | 返回类型 | 说明 |
|---|---|---|
staticModel(ModelId) | StaticModelSelection | 整个会话使用固定模型 |
dynamic(ModelPolicyProvider) | DynamicModelSelection | 初始模型由 CLI 默认选择,之后每次模型请求由回调返回模型 ID |
dynamic(ModelId, ModelPolicyProvider) | DynamicModelSelection | 同上,并显式指定初始模型 |
policy(ModelDecisionProvider) | PolicyModelSelection | 初始模型由 CLI 默认选择,回调返回结构化结果,可以保持当前模型或覆盖模型与参数 |
policy(ModelId, ModelDecisionProvider) | PolicyModelSelection | 同上,并显式指定初始模型 |
openSession() 完成前执行。
ModelPolicyProvider
dynamic(...) 使用的回调,返回本次请求要使用的模型 ID。
| 参数 | 类型 | 说明 |
|---|---|---|
context | ModelRequestContext | 本次模型请求的上下文 |
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 相同。
null、stage 以 null 完成、抛出异常或超时,本次模型请求都会失败。要保持 CLI 当前选择的模型,必须显式返回 ModelPolicyResult.keepCurrent()。
ModelPolicyResult
ModelDecisionProvider 的不可变返回值。只发送显式设置的字段,未设置模型时保留 CLI 已选择的模型。
| Builder 方法 | 说明 |
|---|---|
model(ModelId) | 覆盖本次请求的模型 |
parameters(JsonObject) | 传给 CLI 的请求参数,SDK 不解释其含义 |
taskId(String) | 推理任务标识 |
subTask(String) | 业务子任务标识 |
build() | 创建结果;什么都不设置时等同于 keepCurrent() |
null。parameters 不是传递密钥的通道,不要在其中放凭据。
ModelRequestContext
一次模型请求的上下文,由 SDK 传给模型回调。
| 方法 | 返回类型 | 说明 |
|---|---|---|
sessionId() | Optional<String> | 所属会话 ID |
purpose() | Optional<String> | CLI 本次调用的用途 |
turnIndex() | Optional<Integer> | 轮次索引 |
agentId() / agentType() | Optional<String> | 发起请求的子 Agent ID 和类型 |
resolvedModel() | Optional<ModelId> | CLI 在回调之前预选的模型 |
availableModels() | List<ModelInfo> | 本次请求附带的模型目录,不可变;未提供时为空列表 |
Optional.empty()。availableModels() 只是本次请求的快照,不要缓存为整个会话的固定目录。
ModelInfo
模型目录中的一条记录,由 QoderSession.getAvailableModels() 返回,也出现在 ModelRequestContext 和 ModelCatalogSnapshot 中。
目录里可能包含
isEnabled() 为 false 的模型,展示可选模型时应过滤掉。
ModelCatalogOptions
QoderSession.getAvailableModels(ModelCatalogOptions) 的读取选项。
defaults() 和无参的 getAvailableModels() 都使用 CLI 默认的缓存策略。Builder 的 fetchStrategy(FetchStrategy) 显式选择实时读取(LIVE)或缓存(CACHE),uid(String) 指定目录 uid。
ModelCatalogSnapshot
QoderSession.modelCatalogSnapshot() 返回的本地模型目录快照,来自初始化结果或 CLI 推送的最新目录更新。
modelCatalogSnapshot() 返回 Optional.empty() 表示还没有收到目录;返回快照但 models() 为空,表示 CLI 明确给出了空目录。读取快照不会向 CLI 发请求,需要当前账号的实时结果时调用 getAvailableModels(...)。
ModelId
模型 ID 的值类型。
Memory
Memory 配置通过 SessionOptions.Builder.memory(MemoryOptions) 传入,相关类型位于 com.qoder.sdk.memory 包。记忆的生成、写入和加载由 CLI 执行,与保存会话记录的 SessionStore 是两项独立能力。启用方式、应用管理目录和逐轮生成控制见 Memory。
Memory 类型一览
| 类型 | 作用 |
|---|---|
MemoryOptions | 查询级配置入口。disabled() 是默认值;nativeBuilder() 使用 CLI 原生记忆,customBuilder() 由应用指定生成与加载配置。可设置用户级/项目级作用域、结果回调和 drainTimeoutMs(默认 60000 毫秒) |
MemoryGenerationOptions | custom 模式的生成配置:开关、记忆目录(roots)、生成提示词、轮次结束后的自动提取 |
MemoryTurnCompleteOptions | 轮次结束后自动提取的配置:开关、shouldGenerate 决策回调、超时(默认 10000 毫秒)、决策失败策略 OnGateError(SKIP / REPORT_FAILED) |
MemoryShouldGenerate | 逐轮决策回调,返回 CompletionStage<MemoryGenerationGateResult> |
MemoryGenerationGateInput / MemoryGenerationGateResult | 决策回调的输入(会话、轮次、prompt、回答等),以及 run() / skip(reason) 决策结果 |
MemoryConsumptionOptions | custom 模式的加载配置:开关、文件列表、maxTokens、超长处理 Overflow(TRUNCATE / FAIL_QUERY)、读取失败处理 FailureMode(BEST_EFFORT / FAIL_QUERY) |
MemoryRoot / MemoryConsumptionFile | 用 builder(String id, Path path) 创建的记忆目录和加载文件;目录可设访问权限(READ / READ_WRITE)和索引文件,加载文件可设为必需 |
MemoryGenerationHandler / MemoryConsumptionHandler | 生成和加载结果的通知回调,返回 CompletionStage<Void> |
EffectiveMemoryConfig | CLI 在初始化时确认的实际配置,由 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。
UnsupportedCliCapabilityException 失败,不会发送请求。一次性查询启用 Memory 时,ResultMessage 之后消息流还会继续传递记忆处理消息,应消费到流结束。
Session Storage
外部会话存储相关类型。SessionStorage、SessionRepository 位于 com.qoder.sdk 包,其余位于 com.qoder.sdk.session 包。使用方式见外置会话存储和会话控制。
SessionStorage
单次会话使用哪种历史存储,通过 SessionOptions.Builder.sessionStorage(...) 设置。
| 工厂方法 | 说明 |
|---|---|
inherit() | 默认值,使用 QoderClient.Builder.sessionStore(...) 配置的客户端存储;客户端没有配置时使用 CLI 本地历史 |
cliLocal() | 本次会话不使用外部存储,只用 CLI 本地历史 |
external(SessionStore) | 本次会话使用指定的外部存储,参数不能为 null |
fileCheckpointing(true) 同时使用。
SessionStore
外部会话存储需要实现的接口。客户端通过 QoderClient.Builder.sessionStore(...) 设置默认存储,同时供 client.sessions() 使用。
| 方法 | 返回类型 | 说明 |
|---|---|---|
append(SessionKey, AppendBatch) | CompletionStage<AppendResult> | 原子地追加一个批次 |
load(SessionKey) | CompletionStage<Optional<SessionSnapshot>> | 加载完整的 transcript 快照;键不存在或已删除时返回空 |
实现契约
| 契约 | 要求 |
|---|---|
| 顺序与隔离 | load 按追加顺序返回条目;不同 SessionKey 的数据互不可见 |
| 原子性 | 同一键的并发追加必须串行化;一个批次要么全部写入,要么全部失败 |
| 幂等 | 同一键下已写入过的 operationId 不能重复写入,应返回原操作的 newVersion 和 appendedCount,并标记为重放。该检查先于 CAS;已删除的键一律以 SessionStoreDeletedException 拒绝 |
| CAS | expectedVersion 不为 null 时必须等于当前版本,否则抛出携带当前版本的 SessionStoreConflictException;为 null 时无条件追加。空键的当前版本是 VersionToken.initial() |
| UUID 完整性 | uuid 和 rawJson 都相同的条目不重复写入;uuid 相同但 rawJson 不同必须报冲突。没有 uuid 的条目不按内容去重 |
MirrorErrorMessage 和诊断事件报告,主回答可能继续。回答成功不代表记录已经持久化。
ListableSessionStore
可枚举的存储。SessionRepository 的 list()、按最近会话继续以及子 Agent 恢复都依赖它。
listSessions 返回项目下的所有键(包括子路径键),顺序不保证。deleteSession 只删除指定的单个键,不会删除同一会话的其他子路径。
SessionDeletionStore
支持整个会话删除的存储,SessionRepository.delete(...) 要求存储实现这个接口。
SessionStoreDeletedException 拒绝,旧版本也不能重建数据。删除不存在或已删除的会话也应成功。已删除的会话 ID 不能复用。
InMemorySessionStore
SDK 内置的内存实现,实现 SessionDeletionStore。数据随进程退出丢失,适合测试和短生命周期进程。
SessionMirrorOptions
外部存储写入(镜像)的资源上限,通过 SessionOptions.Builder.sessionMirrorOptions(...) 设置。用 builder() 创建,defaults() 返回默认值;所有值必须为正数。
| 配置项 | 默认值 | 说明 |
|---|---|---|
maxPendingBatches | 256 | 待写入批次的数量上限 |
maxPendingBytes | 64 MiB | 待写入批次原始内容的字节上限 |
operationTimeoutMs | 60000 | 每个批次从读取版本到追加完成(含重试)的总时长 |
closeDrainTimeoutMs | 5000 | 关闭会话时等待剩余批次写入的时长 |
MirrorErrorMessage 和诊断事件报告,不中断模型回合。
SessionRepository
读取、继续、分叉或删除已保存的会话。用 client.sessions() 获取客户端默认作用域的仓库,用 client.sessions(SessionOptions) 获取某次会话所用作用域的仓库。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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> | 以指定会话为起点创建新会话 |
- 仓库固定绑定一个存储和项目键。存储按
sessionStorage解析;项目键取自workingDirectory,未设置时用客户端的projectDirectory,两者都没有时为default。 sessions(options)只采用其中的存储和目录两项,不启动进程,也不保存 CLI 路径、环境变量等其他配置。continueSession和fork的options没有指定目录或存储时沿用仓库的作用域;显式覆盖可以打开其他作用域的会话,但不会改变当前仓库的list、get、delete。
UnsupportedCliCapabilityException 失败,不会降级为逐键删除。外部存储上的 fork 还需要 CLI 支持对应能力,见能力要求。
存储值类型
SessionStore 实现中用到的值类型。
| 类型 | 成员 | 说明 |
|---|---|---|
SessionKey | projectKey / sessionId / subpath | 存储键。会话根的 subpath 为空字符串,子 Agent transcript 使用非空子路径;projectKeyFor(Path) 从项目目录生成项目键 |
AppendBatch | operationId | 幂等 ID,同一 ID 重试只应用一次 |
expectedVersion | CAS 期望版本;null 表示无条件追加 | |
firstSequence | 批次首条的顺序参考,不能代替 CAS 版本或幂等 ID | |
entries | 批次条目。Builder 提供 operationId、expectedVersion、firstSequence、addEntry、addEntries | |
AppendResult | applied(...) / replayed(...) | 实际写入的结果,或幂等重放的结果;重放时传入原操作的版本和条数 |
alreadyApplied() | 是否为重放 | |
SessionSnapshot | entries / version | 按追加顺序排列的条目和当前版本 |
SessionStoreEntry | rawJson() | 原始 JSON 行;type、uuid、timestamp 从中提取。toString() 不输出正文 |
OperationId | of / random | 幂等操作 ID |
VersionToken | initial() | 空存储的起始版本;每次成功写入后版本必须前进 |
SessionStoreDeletedException | CODE | 会话已删除时拒绝写入,错误码 SESSION_STORE_DELETED,不可重试。它不是 CAS 冲突,不能按当前版本重试 |
Results and Identifiers
InitializationResult
初始化握手的结果,由 QoderQuery.initialization()、QoderSession.initialization() 等方法返回。它是握手完成时的只读快照,之后不会变化。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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 配置;未启用时为空 |
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 能力的三种状态。
| 值 | 说明 |
|---|---|
SUPPORTED | CLI 声明支持 |
UNSUPPORTED | CLI 明确声明不支持,或运行中拒绝了该能力的请求 |
UNKNOWN | CLI 没有声明;依赖该能力的方法直接失败,SDK 不会试探 |
InterruptResult
interrupt() 的结果。
| 方法 | 返回类型 | 说明 |
|---|---|---|
interrupted() | boolean | 是否向一个运行中的轮次发出了中断;没有运行中的轮次时为 false |
turnState() | String | 返回时的轮次状态:IDLE、RUNNING 或 INTERRUPTING |
ResultMessage 为准。中断请求失败(包括超时)后,轮次恢复为 RUNNING,可以重试。
MessageSubmission
QoderSession.submit(UserInput) 返回的提交句柄。
| 方法 | 返回类型 | 说明 |
|---|---|---|
id() | MessageId | 立即可用的输入 ID,可传给 cancelQueuedMessage;不代表输入已被接受或写出 |
written() | CompletionStage<MessageId> | 输入写入 CLI 后完成,不等待回答;输入被拒绝或写入失败时异常完成,本地取消成功时以 CancellationException 完成 |
send(input);需要在写入前取消时用 submit(input)。written() 每次返回独立的 stage,取消它不会取消输入本身。
标识类型
| 类型 | 说明 |
|---|---|
MessageId | 用户输入的 ID,来自 MessageSubmission.id() 或 send(...) 的返回值;用于 cancelQueuedMessage、rewindHistory、rewindFiles |
TaskId | 后台任务 ID,传给 stopTask(TaskId) |
SessionTitle | generateSessionTitle() 返回的会话标题 |
RewindFilesOptions / RewindFilesResult
QoderSession.rewindFiles(MessageId, RewindFilesOptions) 的选项和结果。会话需要以 SessionOptions.Builder.fileCheckpointing(true) 启动,见 Checkpoint。
| 方法 | 返回类型 | 说明 |
|---|---|---|
RewindFilesOptions.defaults() | RewindFilesOptions | 实际回退文件 |
RewindFilesOptions.dryRun() | RewindFilesOptions | 只报告会改动哪些文件,不写盘 |
canRewind() | boolean | 能否回退(试运行)或是否已回退 |
filesChanged() | List<String> | 受影响的文件 |
error() | Optional<String> | 无法回退的原因 |
Message Types
SdkMessage
消息流中所有消息的抽象基类,位于 com.qoder.sdk.message。用 instanceof 判断具体类型。
| 方法 | 返回类型 | 说明 |
|---|---|---|
wireType() | String | 消息的顶层 type |
uuid() | Optional<String> | 消息 UUID,CLI 提供时有值 |
sessionId() | Optional<String> | 所属会话 ID,CLI 提供时有值 |
rawJson() | Optional<String> | 消息的原始 JSON。SDK 没有建模的字段,用 JsonValue.parse 解析后读取 |
| 类 | wireType() | 说明 |
|---|---|---|
AssistantMessage | assistant | 助手回复 |
ResultMessage | result | 一轮结束的结果 |
UserReplayMessage | user | CLI 回放的用户消息 |
PartialMessage | partial | 流式增量事件 |
SystemMessage 及子类 | system | 系统消息,按 subtype() 分为多个专用类,包括 Hook 通知和 Memory 结果 |
HookEventMessage | hook_event | 旧格式 Hook 事件 |
MirrorErrorMessage | mirror_error | 会话镜像写入失败 |
UnknownMessage | 原始值 | 未知顶层类型 |
SystemMessage。分支时先判断专用子类,再判断 SystemMessage,否则宽泛分支会遮住专用类型。
AssistantMessage 与内容块
助手产生一条回复时递送。内容块是输出侧类型,与输入侧的 UserInput 内容块相互独立。
| 方法 | 返回类型 | 说明 |
|---|---|---|
AssistantMessage.content() | List<ContentBlock> | 内容块列表 |
AssistantMessage.getText() | String | 拼接全部 TextBlock 的文本 |
ContentBlock.type() | String | 内容块的 type,如 text |
TextBlock.text() | String | 文本内容 |
UnknownContentBlock.rawJson() | String | 该内容块的原始 JSON |
text 块建模为 TextBlock。tool_use、thinking 等其他内容块都以 UnknownContentBlock 保留:type() 返回原始类型,需要时从 rawJson() 解析。TypeScript 和 Python SDK 为这些块提供了类型化的类。
ResultMessage 与用量
一轮对话结束时递送。Agent 业务失败(例如达到轮次上限)通过 outcome() 为 ERROR 的 ResultMessage 报告,不会抛出异常。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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() | 类 | 关键访问器 | 说明 |
|---|---|---|---|
| 其他或未知值 | SystemMessage | subtype() | 完整负载在 rawJson() |
plan_mode_changed | PlanModeChangedMessage | planMode() → PlanModeSnapshot | 独立 Plan 状态变化,见权限 |
commands_changed | CommandsChangedMessage | commands() → List<CommandInfo> | 本次完整命令目录,空列表表示清空 |
available_models_update | AvailableModelsUpdateMessage | catalog() → ModelCatalogSnapshot | 本次完整模型列表与当前模型 |
agents_md_size_warning | AgentsMdSizeWarningMessage | totalBytes()、files() 等 | AGENTS 文件大小告警 |
permission_denied | PermissionDeniedMessage | 无,读 rawJson() | 一次工具权限被拒绝 |
background_tasks_changed | BackgroundTasksChangedMessage | 无,读 rawJson() | 后台任务集合变化 |
task_updated | TaskUpdatedMessage | 无,读 rawJson() | 某个任务状态更新 |
hook_started / hook_progress / hook_response | HookStartedMessage 等 | 见 Hook 通知消息 | Hook 运行通知 |
memory_generation / memory_consumption | MemoryGenerationMessage / MemoryConsumptionMessage | result() | 记忆生成、加载结果 |
AgentsMdSizeWarningMessage 的大小都是 UTF-8 字节数,由 CLI 报告,SDK 不重新计算。truncated() 表示 CLI 报告的加载状态,不是 SDK 做了截断。
PermissionDeniedMessage、BackgroundTasksChangedMessage、TaskUpdatedMessage 上的 WIRE_TYPE 常量已弃用,请按类型或 subtype() 判断。
命令或模型目录更新无效时,SDK 不构造对应的专用消息,保留上一份目录快照,原消息作为通用 SystemMessage 递送,同时发出 invalid_catalog_update 诊断。
PartialMessage 与 UserReplayMessage
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。
hook_id、exit_code),outcome() 原样保留未知值。字段缺失或类型不符时返回空;exitCode() 在值不是 int 范围内的整数时返回空,不做截断。原始值仍可从 rawJson() 读取。
HookEventMessage 对应旧的 type=hook_event 消息。SDK 不会把上面三种 system 通知转换成它。
MemoryGenerationMessage 与 MemoryConsumptionMessage
记忆生成和记忆加载的结果,都不终结对话轮次。结果类型位于 com.qoder.sdk.memory,用法见 Memory。
MemoryGenerationResult 包含生成状态、写入和失败的文件、耗时及可选的独立用量;flush 成功不代表文件都已保存,应检查 status() 和 failedFiles()。MemoryConsumptionResult 包含整体状态和每个文件的加载状态。未知状态映射为 UNKNOWN,原始值保留在 wireStatus()。注册结果回调不会把这两种消息从消息流中移除。
MirrorErrorMessage 与 UnknownMessage
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(...) 解析。
| 方法 | 返回类型 | 说明 |
|---|---|---|
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。
Diagnostics
DiagnosticsListener
接收 SDK 的结构化诊断事件,例如会话初始化、关闭超时、能力降级和托管 CLI 安装。
- 两级注册。 客户端级用
QoderClient.Builder.diagnosticsListener(...),会话级用SessionOptions.Builder.diagnosticsListener(...)。两者不叠加:会话设置了监听器时只用会话级,否则回退到客户端级。托管 CLI 的managed_runtime_*事件只发给客户端级监听器。 - 线程。 每个会话在 SDK 自己的诊断线程上串行调用监听器,不使用
callbackExecutor。同一个监听器注册到多个会话时可能被并发调用,需要线程安全。 - 尽力交付。 排队事件超过上限时直接丢弃新事件;监听器抛出异常只丢失这一次通知;关闭时只留有限时间排空,超时会中断监听器并丢弃剩余事件。诊断不能当作无损审计日志,也不能作为业务状态的唯一依据。
- 返回要快。 耗时操作交给应用自己的异步机制。监听器运行在 SDK 线程上,需要关闭发出事件的会话或所属客户端时,调用
closeAsync()后直接返回;在监听器里阻塞调用它们的close()会抛出IllegalBlockingCloseException。
DiagnosticEvent
不可变的诊断事件。SDK 产生的 detail 已脱敏,会清除已登记的凭据并尽量识别凭据形态的文本,但不保证覆盖任意业务数据。
| 字段 | 类型 | 说明 |
|---|---|---|
type | String | 事件类型,必填,常见值见下表 |
sessionId | Optional<String> | 所属会话 ID |
requestType | Optional<String> | 相关的控制请求类型 |
durationMs | Optional<Long> | 操作耗时(毫秒) |
queueDepth | Optional<Integer> | 队列深度快照 |
exitCode | Optional<Integer> | CLI 退出码 |
capability | Optional<String> | 相关的能力名 |
detail | Optional<String> | 自由文本细节 |
builder(type) 自建事件时,SDK 不会对 detail 脱敏。
常见事件类型
type | 主要字段 | 含义 |
|---|---|---|
session_initialized | sessionId、detail | 初始化握手完成,detail 含协议版本 |
protocol_handshake_warning | detail | 握手时协议协商给出的警告 |
session_failed | sessionId、exitCode、detail | 会话因错误终止 |
control_request_completed | requestType、durationMs | 控制请求收到成功响应 |
inbound_control_request_failed | sessionId、detail | CLI 发起的回调请求(权限、Hook、MCP、模型策略、认证 token 等)失败或超时,SDK 已向 CLI 回复错误。detail 含 request_id 和 error=<错误码>,例如 MODEL_RESOLUTION_TIMEOUT |
capability_downgraded | capability、requestType | 已通过能力检查的控制请求在运行时被 CLI 判为不支持。之后按降级状态检查,已返回的 InitializationResult 不变 |
invalid_catalog_update | sessionId | 命令或模型目录更新无效,已保留上一份快照 |
memory_callback_failed | detail | Memory 结果回调失败或被执行器拒绝;消息保留,会话继续 |
memory_drain_failed / memory_drain_timeout | detail | 自动收尾时等待 Memory 失败或超时;主回答保留,资源继续释放 |
mirror_append_failed | sessionId、detail | 外置会话存储镜像写入失败,同时递送 MirrorErrorMessage |
mirror_drain_timeout | sessionId、detail | 关闭时镜像写入未在时限内完成 |
process_tree_cleanup_degraded | capability、detail | 当前环境无法完整清理 CLI 进程树;不影响初始化,也不代表子进程已回收 |
stdin_writer_stop_timeout | sessionId | 关闭时写线程没有按时停止,正在写的消息可能被截断,SDK 改为终止 CLI 进程 |
cli_process_alive_after_close | detail | 关闭流程结束后 CLI 进程仍在运行;detail 列出已执行的终止步骤 |
lane_shutdown_timeout | detail | SDK 内部线程没有在关闭时限内停止;忽略中断的回调可能在关闭后继续运行 |
spool_cleanup_deferred | detail | 关闭时消息缓冲的临时文件仍被占用,读写返回后再删除 |
private_directory_cleanup_failed | detail | 认证或 MCP 配置用的私有临时目录没有清理完成。detail 含 credential files may remain 时,凭据文件可能仍在磁盘上 |
session_store_cleanup_failed | detail | 外置会话恢复用的临时目录清理失败 |
managed_runtime_reused | detail | 使用已缓存的托管 CLI |
managed_runtime_install_started / managed_runtime_installed | detail | 开始安装、安装完成托管 CLI |
managed_runtime_stale_install_removed | detail | 删除了损坏或不完整的旧安装 |
managed_runtime_unsupported_platform | detail | 当前平台没有托管 CLI,改为查找已安装的 CLI |
type 分支时请保留默认分支。
Exceptions
所有 SDK 异常都继承非受检的 QoderException。同步方法直接抛出;异步方法通过返回的 CompletionStage 失败,用 join() 等待时会被包装成 CompletionException,用 getCause() 取出原异常。Agent 业务失败不抛异常,而是以 outcome() 为 ERROR 的 ResultMessage 结束本轮。
QoderException
| 方法 | 返回类型 | 说明 |
|---|---|---|
getCode() | String | 稳定的机器可读错误码,见异常一览 |
isRetryable() | boolean | 是否值得重试 |
printStackTrace() 输出脱敏后的快照。SDK 不会自动重试,当前内置异常的 isRetryable() 都返回 false;是否新建会话重试,由应用按错误码判断。
UnsupportedCliCapabilityException
- 调用的方法需要 CLI 能力,而 CLI 声明不支持(
UNSUPPORTED)或从未声明(UNKNOWN)。SDK 在本地直接失败,不向 CLI 发送请求。所需能力见能力要求。 - 基础控制请求在运行时被 CLI 拒绝。
SessionRepository的本地操作需要可枚举的存储,而当前存储不支持。
capabilityState() 返回触发失败的 CapabilityState。
异常一览
除下表列出的访问器外,各异常类只有构造器,错误码常量为 CODE。
| 类 | 错误码 | 何时抛出 |
|---|---|---|
AuthenticationException | AUTHENTICATION_FAILED、CLI_AUTH_FATAL | 认证失败。CLI 以退出码 41 报告致命认证失败时错误码为 CLI_AUTH_FATAL(常量 CODE_CLI_AUTH_FATAL),不应按进程失败重试 |
CliNotFoundException | CLI_NOT_FOUND | 找不到 CLI:显式路径无效,或自动查找没有找到可执行文件 |
RuntimeInstallException | RUNTIME_INSTALL_FAILED | 托管 CLI 安装失败:禁止下载且没有本地压缩包、网络失败或超时、大小或 SHA-256 校验不符等。SDK 不会改用其他版本,见 ManagedRuntimeOptions |
CliProcessException | CLI_PROCESS_FAILED | CLI 进程非正常退出(认证类退出码除外)。getExitCode() 返回退出码,getStderrTail() 返回有界且已脱敏的 stderr 尾部,不可用时为 null |
ProtocolException | PROTOCOL_ERROR | CLI 输出违反协议,下面三个子类以外的情况 |
FrameTooLargeException | FRAME_TOO_LARGE | 单条消息超过大小上限 |
MalformedUtf8Exception | MALFORMED_UTF8 | CLI 输出不是合法的 UTF-8 |
ProtocolVersionMismatchException | PROTOCOL_VERSION_MISMATCH | CLI 的协议版本与 SDK 不兼容 |
ControlRequestTimeoutException | CONTROL_REQUEST_TIMEOUT | 控制请求在 controlRequestTimeoutMs(默认 60000 毫秒)内没有收到响应 |
UnsupportedCliCapabilityException | UNSUPPORTED_CLI_CAPABILITY | 见上文 |
SessionClosedException | SESSION_CLOSED | 在已关闭的客户端、会话或查询上继续操作;完整关闭过程中 CLI 先退出时,消费者也会收到它 |
SessionStoreConflictException | SESSION_STORE_CONFLICT | 会话存储版本冲突(expectedVersion 已过期),或同一 UUID 对应不同内容。currentVersion() 返回存储的当前版本 |
SessionStoreDeletedException | SESSION_STORE_DELETED | 见 SessionStoreDeletedException |
SpoolIoException | SPOOL_IO_ERROR | 消息缓冲的磁盘文件读写失败 |
SpoolCapacityExceededException | SPOOL_CAPACITY_EXCEEDED | 未消费的消息超过缓冲上限,消息流失败。及时消费消息流可以避免 |
MessageStreamAlreadyConsumedException | MESSAGE_STREAM_ALREADY_CONSUMED | 对同一个 MessageStream 第二次消费;openCursor() 和 consume() 合计只能调用一次 |
ConsumerInterruptedException | CONSUMER_INTERRUPTED | 阻塞在 MessageCursor.hasNext() 或 next() 上的线程被中断,线程的中断标志保留 |
CallbackExecutionException | CALLBACK_EXECUTION_FAILED | 应用回调(权限、Hook、MCP、消息处理器)抛出异常 |
CallbackRejectedException | CALLBACK_REJECTED | callbackExecutor 拒绝执行回调任务,例如线程池已关闭或队列已满 |
IllegalBlockingCloseException | ILLEGAL_BLOCKING_CLOSE | 在资源自己的 SDK 线程或推送处理器里阻塞调用 close()。抛出前 SDK 已按 closeAsync() 发起关闭,见线程模型与资源释放 |
InvalidOptionsException | INVALID_OPTIONS | 配置无效,例如 QoderClient.Builder 缺少 auth,或 SessionOptions 同时设置了互斥选项 |
UnsupportedSecureSecretTransportException | UNSUPPORTED_SECURE_SECRET_TRANSPORT | 无法安全创建或写入认证、MCP 配置用的私有临时文件,包括 java.io.tmpdir 或其上级目录允许其他账号替换其中条目的情况。SDK 拒绝启动,不会改用明文参数或环境变量传递 |
QoderException 抛出:
| 错误码 | 何时抛出 |
|---|---|
CLI_START_FAILED | CLI 进程启动失败,例如权限不足、工作目录无效、参数过长。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 同时关闭 |