Macro Agent Harness SDK 错误契约详解:GetAgentSessionChangesError 与变更接口的 401/403/500 处理 后端前端协同办公即时通讯企业应用CRMAI Agent人工智能【免费下载链接】macroMacro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — -linked together with shared AI memory.项目地址https://gitcode.com/GitHub_Trending/macro3/macro点击查看免费下载在 Macro 的 Agent Harness TypeScript SDK 中GetAgentSessionChangesError描述了“读取某个 agent session 最新捕获改动”这一接口可能返回的全部错误分支。它是把 HTTP 错误码映射为调用方可处理类型的最后一环。本文将从这个三行类型别名出发结合 OpenAPI 规范、Rust 服务端实现与 SDK 测试说明这些错误在什么条件下产生、错误体是什么形态以及如何在代码中正确地捕获与降级处理。一、类型定义本身一个“按状态码索引”的联合类型关联文档给出的定义非常精简type GetAgentSessionChangesError GetAgentSessionChangesErrors[keyof GetAgentSessionChangesErrors]它引用的是同一目录下的 GetAgentSessionChangesErrors其结构为type GetAgentSessionChangesErrors { 401: string; 403: string; 500: string; }这里的写法是 TypeScript 的索引访问类型indexed access typekeyof GetAgentSessionChangesErrors的取值是401 | 403 | 500因此最终得到的GetAgentSessionChangesError等价于type GetAgentSessionChangesError string; // 401 | 403 | 500 三个分支的值同为 string需要注意三点这些细节直接影响错误处理代码的写法键是数字字面量值是string不是字面量联合。生成器没有把错误体建模成{ message: string }之类的结构化对象说明该接口的错误响应体在 OpenAPI 中被声明为text/plain的纯字符串。在 packages/sdk/specs/agent-harness.json 的/agent-sessions/{session_id}/changes定义里401、403、500 三个响应的content均为text/plainschema: { type: string }。只覆盖 401/403/500没有 404。这一点与相邻的 patch 接口不同后者的错误类型额外包含404后文会解释原因。该类型描述的是“错误响应的数据部分”不包含 HTTP 状态码本身。状态码由调用层的response.status携带SDK 会把它放进抛出异常对象里。对应的原始生成代码位于 packages/sdk/generated/agent-harness/types.gen.tsexport type GetAgentSessionChangesErrors { 401: string; 403: string; 500: string; }; export type GetAgentSessionChangesError GetAgentSessionChangesErrors[keyof GetAgentSessionChangesErrors];二、这个错误类型属于哪个请求GetAgentSessionChangesError与GetAgentSessionChangesData、GetAgentSessionChangesResponses、GetAgentSessionChangesResponse是同一组生成产物共同描述一次 HTTP 调用export type GetAgentSessionChangesData { body?: never; path: { /** ID of the agent session */ session_id: string; }; query?: never; url: /agent-sessions/{session_id}/changes; }; export type GetAgentSessionChangesResponses { 200: AgentSessionChangesResponse; };也就是说它对应GET /agent-sessions/{session_id}/changes路径参数只有session_idUUID 字符串没有请求体也没有查询参数成功时返回200与 AgentSessionChangesResponse其字段为changeset?: ChangesetDto | null、attempt?: CaptureAttemptDto | null、capturing: boolean失败时返回 401/403/500 三者之一响应体是纯文本字符串这正是GetAgentSessionChangesErrors中三个string的由来。在生成的客户端类中该方法只做一层薄封装见 packages/sdk/generated/agent-harness/sdk.gen.ts/** * The latest captured changes of an agent session: the changed files with * statuses and line counts, and how the latest capture attempt went. */ public getAgentSessionChangesThrowOnError extends boolean false( options: OptionsGetAgentSessionChangesData, ThrowOnError ): RequestResultGetAgentSessionChangesResponses, GetAgentSessionChangesErrors, ThrowOnError { return (options.client ?? this.client).get GetAgentSessionChangesResponses, GetAgentSessionChangesErrors, ThrowOnError ({ url: /agent-sessions/{session_id}/changes, ...options }); }泛型参数ThrowOnError决定了失败时的行为为false默认时返回{ data, error, response }结果对象为true时直接抛出异常。而GetAgentSessionChangesError正是结果对象里error字段的类型来源。三、三个状态码分别代表什么要判断错误该不该重试、该提示用户做什么必须知道每个码在服务端的产生条件。这一层信息不在生成的 Markdown 里但可以从 Rust 实现中确认。3.1 路由与处理函数/changes相关路由由agent_changescrate 的 Axum router 提供被挂载进 agent harness 服务见 services/agent_harness_service/src/api.rs 中对agent_changes_router的合并。处理函数签名位于 crates/agent_changes/src/inbound/axum_router.rs#[utoipa::path( get, path /agent-sessions/{session_id}/changes, tag agent-sessions, operation_id get_agent_session_changes, params((session_id Uuid, Path, description ID of the agent session)), responses( (status 200, body AgentSessionChangesResponse), (status 401, body String), (status 403, body String), (status 500, body String), ) )] pub async fn get_agent_session_changes_handler...( access: AgentSessionAccessLevelExtractorViewAccessLevel, Access, Auth, State(state): StateAgentChangesRouterStateChanges, Access, Auth, ) - ResultJsonAgentSessionChangesResponse, AgentChangesApiError { let changes state.service.changes(access.entity_access_receipt).await?; Ok(Json(changes.into())) }可以看到两个关键信息参数提取器是AgentSessionAccessLevelExtractorViewAccessLevel, ...即该接口要求调用方对目标 session 具备查看view级别的访问权限utoipa注解声明的响应集合恰好是200/401/403/500与 TypeScript 侧的GetAgentSessionChangesErrors完全一致——这就是“类型别名只有三个键”的根因。3.2 错误枚举到 HTTP 状态的映射AgentChangesApiError的IntoResponse实现把领域错误翻译为线上状态见 crates/agent_changes/src/inbound/axum_router.rsmatch self { Self::Domain(ChangesError::Forbidden) (StatusCode::FORBIDDEN, forbidden).into_response(), Self::Domain(error (ChangesError::NoChangeset | ChangesError::PatchMissing)) (StatusCode::NOT_FOUND, error.to_string()).into_response(), Self::Domain(ChangesError::Session(AgentSessionError::Forbidden)) (StatusCode::FORBIDDEN, forbidden).into_response(), Self::Domain(error) { tracing::error!(error ?error, agent session changes request failed); (StatusCode::INTERNAL_SERVER_ERROR, internal error).into_response() } }结合领域错误定义 crates/agent_changes/src/domain/error.rs可以整理出下表状态码触发条件响应体401请求未通过认证缺少或无效的凭据纯文本字符串403调用方对 session 没有 view 权限ChangesError::Forbidden或会话读取本身返回AgentSessionError::Forbidden字面量forbidden500存储层失败ChangesError::Storage、读取会话出错或其它未归类领域错误字面量internal error服务端同时记录 error 日志补充说明两点为什么/changes没有 404ChangesError::NoChangeset尚未捕获过任何改动与ChangesError::PatchMissing元数据存在但 blob 丢失只在读取 diff 内容时才会触发它们被映射为 404只出现在/changes/patch的响应集合中。因此 prompt 的GetAgentSessionChangesPatchErrors比本类型多一个404键而/changes在“还没捕获成功”时依然返回200只是changeset为null。500 不泄露内部细节响应体固定是internal error真正的错误只进tracing::error!日志。因此客户端无法从500的 body 判断根因只能依赖重试或联系服务端排查。四、在 SDK 中如何捕获这个错误生成的sdk.gen.ts是底层客户端日常使用更推荐上层的实体封装它会把错误统一成可判定的异常。相关链路如下packages/sdk/src/entities/agent-sessions/agent-session.ts 中的changes()调用getAgentSessionChanges并用unwrap解包/** Read the latest captured GitHub pull request changes and capture status. */ async changes(): PromiseAgentSessionChangesResponse { return unwrap( await this.client.agentHarness.getAgentSessionChanges({ path: { session_id: this.id }, }), ); }unwrap在 packages/sdk/src/utils/unwrap.ts 中把{ data, error, response }转成“成功返回值或抛异常”export function unwrapTData, TError(res: { data?: TData; error?: TError; response?: Response; }): TData { if (res.error ! undefined) throw new MacroApiError(res.response?.status ?? 0, res.error); return res.data as TData; }MacroApiError同时携带status与data即错误响应体本接口下就是那段纯文本见 packages/sdk/src/utils/unwrap.ts。因此GetAgentSessionChangesError在运行时表现为MacroApiError.data类型上就是string。下面是一个完整的调用与分类处理示例Macro客户端的构造方式与仓库测试保持一致import { Macro } from macro-inc/sdk; import { MacroApiError } from macro-inc/sdk/utils/unwrap; const macro new Macro({ token: process.env.MACRO_TOKEN!, // 用户访问令牌 hosts: { agent-harness: https://agent.example.test }, }); const session macro.agentSessions.byId(sessionId); try { const changes await session.changes(); if (changes.capturing) { // 捕获正在进行changeset 可能还是上一轮的旧值 } if (!changes.changeset) { // 200 但从未成功捕获过属于“暂无数据”不是错误 } } catch (err) { if (err instanceof MacroApiError) { switch (err.status) { case 401: // 令牌缺失/过期需要重新认证重试无意义 break; case 403: // 对 session 没有 view 权限或会话已不可读 // err.data 通常是 forbidden break; case 500: // 存储层等内部故障可退避重试 // err.data 为 internal error break; default: // 其它状态码例如网络层错误可能为 0 break; } } throw err; }如果你直接使用生成的底层客户端并希望以异常风格调用可以把throwOnError设为true此时GetAgentSessionChangesErrors同样会作为错误信息类型参与推断const client macro.agentHarness; // 或自行构造 Sdk 实例 try { const res await client.getAgentSessionChanges({ path: { session_id: sessionId }, throwOnError: true, }); console.log(res.data.changeset?.files); } catch (err) { // 这里的错误来自 GetAgentSessionChangesError 分支 }五、测试如何验证这套错误契约仓库中的 SDK 测试对“403 必须原样保留”有明确断言见 packages/sdk/tests/agent-sessions.test.tstest(preserves access errors from session result endpoints, async () { globalThis.fetch (async (_input) Response.json({ message: Forbidden }, { status: 403 })) as typeof fetch; const macro new Macro({ token: user-token, hosts: { agent-harness: https://agent.example.test }, }); const agent macro.agentSessions.byId(sessionId); for (const read of [ () agent.changes(), () agent.changesPatch(), () agent.refreshChanges(), () agent.codeExecutions .byId(0198a4cc-e138-7670-a308-a6b766602702) .status(), ]) { await expect(read()).rejects.toMatchObject({ name: MacroApiError, status: 403, data: { message: Forbidden }, }); } });这条用例说明了两点工程约束状态码必须保留上层封装不能把 401/403/500 吞掉或统一改写否则调用方无法做区分处理错误体必须透传data字段原样保留服务端返回的内容。测试里用 JSON 体模拟响应以便断言结构而真实服务端对该接口返回的是纯文本forbidden/internal error这也解释了为什么生成类型把错误值标为string而不是结构化对象。同文件的另一条用例agent-sessions.test.ts则验证了成功路径changes()走GET /changes、changesPatch()走GET /changes/patch、refreshChanges()走POST /changes/refresh且三者都带Authorization: Bearer token头与401的语义形成对照。六、与相邻错误类型的边界理解了GetAgentSessionChangesError就能理解它周围几个类型的差异避免在错误处理里写错分支类型对应操作状态码键典型差异GetAgentSessionChangesErrorGET /changes401 / 403 / 500无 404未捕获成功时返回 200 changeset: nullGetAgentSessionChangesPatchErrorGET /changes/patch401 / 403 /404/ 500404 表示NoChangeset或PatchMissingRefreshAgentSessionChangesErrorPOST /changes/refresh401 / 403 / 500返回202与当前状态捕获在后台继续这种“只看状态码不够、还要看是哪个端点”的设计是排查变更类问题时的常见坑同一个500在/changes上意味着读取失败而在/changes/refresh上还可能是触发捕获失败同一个403在/changes上是 view 权限问题在 patch 上同样是 view 权限问题但排错方向应优先确认 session 的共享配置而非 diff 存储。七、实战排查清单当GetAgentSessionChangesError真的出现时可以按下面的顺序定位先区分 401 与 403。两者都表现为“无法访问”但 401 是凭据层面的问题令牌缺失、过期、host 配错导致打到错误服务403 是授权层面的问题当前用户对该 session 无 view 权限。仓库测试要求两者都被保留为MacroApiError.status因此你可以直接根据status分流而不必解析文案。403 时检查 session 的共享设置。服务端在AgentSessionAccessLevelExtractorViewAccessLevel, ...处就完成了判定权限不足根本不会进入业务逻辑因此修改重试策略没有意义。可以先用session.sharePermissions()查看当前授权见 agent-session.ts。500 时优先退避重试同时保留上下文。由于响应体固定为internal error真正信息在服务端日志agent session changes request failed这条 tracing 事件里客户端应记录session_id与时间点以便对齐。不要把“没有改动”当成错误。如果任务刚启动、尚未完成任何捕获/changes会返回200且changeset为nullattempt可能处于进行中capturing: true。此时正确做法是等待或调用refreshChanges()而不是把响应当作失败处理。需要 diff 文本时再请求 patch。changes()只返回文件清单与统计ChangedFileDto的additions/deletions/kind/binary/patchOmitted等真正的统一 diff 通过changesPatch()获取只有在确认changeset存在后才调用它才能避开 patch 端点独有的 404。小结GetAgentSessionChangesError看起来只是一个三行的生成类型但它精确地固化了GET /agent-sessions/{session_id}/changes的错误契约错误体是纯文本字符串状态码只有 401、403、500且 404 被有意排除。Rust 侧的AgentChangesApiError把这个契约落实到领域错误的每一次映射TypeScript 侧的unwrap与MacroApiError则保证状态码与错误体在调用链中原样传递。掌握这条链路之后变更读取相关的失败就不再是“一个报错”而是可以按认证、授权、服务端故障三类分别处理的确定性分支。赞分享后端前端协同办公即时通讯企业应用CRMAI Agent人工智能【免费下载链接】macroMacro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — -linked together with shared AI memory.项目地址https://gitcode.com/GitHub_Trending/macro3/macro点击查看免费下载相关推荐Macro Agent Harness SDK 错误类型详解GetAgentSessionChangesPatchError 与变更 Patch 接口的错误契约Macro Agent Harness SDK 错误类型详解GetAgentSessionChangesPatchError 与变更 Patch 接口的错误契后端前端协同办公即时通讯企业应用CRMAI Agent人工智能Macro Agent Harness 沙箱规格错误类型 GetAgentSandboxSizeError 全解从 SDK 类型别名到 401/500 错误契约Macro Agent Harness 沙箱规格错误类型 GetAgentSandboxSizeError 全解从 SDK 类型别名到 401/500 错误契后端前端协同办公即时通讯企业应用CRMAI Agent人工智能Macro Agent Harness SDK 中 GetCodeExecutionErrors 错误契约解析Macro Agent Harness SDK 中 GetCodeExecutionErrors 错误契约解析 本文聚焦 Macro 仓库中由 OpenAPI后端前端协同办公即时通讯企业应用CRMAI Agent人工智能上一篇从请求到响应gh_mirrors/ht/http-kernel的高性能消息处理指南下一篇Hello-Agents 部署3 条命令在本地跑起多智能体科研助手创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考