资讯详情

Hermes Studio(Ekko Studio)移动端日历/提醒「单条确认删除」契约详解:精确身份校验、deleted=true 回报与有界确认期限

📅 2026/9/23 20:36:22 | 华诺云谱 👁 阅读
Hermes Studio(Ekko Studio)移动端日历/提醒「单条确认删除」契约详解:精确身份校验、deleted=true 回报与有界确认期限
Hermes StudioEkko Studio移动端日历/提醒「单条确认删除」契约详解精确身份校验、deletedtrue 回报与有界确认期限【免费下载链接】ekko-studioEkko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.项目地址: https://gitcode.com/gh_mirrors/he/ekko-studio本文围绕 Hermes StudioEkko Studio在 2026-09-05 引入的Confirmed single-item mobile deletion单条目移动端确认删除变更记录展开讲解其直接聊天会话中删除日历事件与提醒Reminder时强制的三条核心契约删除请求必须携带精确的 id、title 与发生时间App 回报必须显式声明deletedtrue且 id 完全匹配并且全程受一次性 App 确认与有界截止时间的约束。读完本文你将掌握该删除能力在服务端请求规范化、响应净化、Socket 事件流转与超时边界上的完整实现以及如何在测试与 OpenAPI 文档中印证这套契约。背景移动日历能力从「只读写」到「确认删除」的演进该变更记录建立在 docs/chat-chain-changes/2026-09-04-mobile-calendar-reminders.md 所描述的移动日历/提醒能力之上。基线版本2026-09-04 及 PR #2892 集成规定MCP 工具hermes_studio_use_mobile_calendar与hermes_studio_use_mobile_reminders服务端端点POST /api/studio/mobile-calendar/request日历事件支持 list、create、update提醒支持 list、create、update、complete删除、后台访问、工作流/群聊/委派使用、跨会话持久化均不支持每个请求都绑定到已认证 profile 与精确的直接聊天会话App 返回结果在交给 MCP 调用方之前会经过允许清单allowlist净化。2026-09-05 的变更记录 docs/chat-chain-changes/2026-09-05-confirmed-mobile-delete.md 在此基线上只新增了「单条目确认删除」并用一句话概括其影响Delete results must match the requested id and explicitly reportdeletedtrue. No list/batch/series deletion, profile scope or background access expansion. Requires the matching App update; older Apps do not support delete.即只删除单条、只允许精确匹配、必须显式回报删除结果且不扩展任何删除范围列表、批量、系列、profile 范围、后台。这既是安全边界的定义也是本文后续所有源码细节的契约来源。核心契约一删除请求必须携带精确身份exact id title 发生时间删除动作的服务端入口是请求规范化函数normalizeMobileCalendarRequest其核心分支位于 mobile-calendar.ts。cleanItem在action delete时执行严格的字段校验if (action delete) { if (typeof value.id ! string || !value.id.trim() || value.id.length 512) return null if (typeof value.title ! string || !value.title.trim() || value.title.length 500) return null // Require the exact listed occurrence; never invent a time for deletion. const start capability calendar ? value.start_ms : value.due_ms if (capability calendar (typeof start ! number || !Number.isFinite(start) || start MIN_TIME_MS)) return null if (capability reminder start ! null (typeof start ! number || !Number.isFinite(start))) return null return { id: value.id.trim(), title: value.title.trim(), ...(start ! null ? { [capability calendar ? start_ms : due_ms]: start } : {}) } }具体规则可归纳为下表字段校验规则说明id必须是字符串、去空格后非空、长度 ≤ 512缺失、数字、数组均直接判无效title必须是字符串、去空格后非空、长度 ≤ 500与 id 共同构成「精确身份」start_ms日历必须是有限数字且 ≥MIN_TIME_MSDate.UTC(2001, 0, 1)必须携带服务端绝不替删除操作凭空推断发生时间due_ms提醒可选若提供则必须是有限数字提醒允许不携带时间提醒本身可能无截止时间deleteAll/ 批量标记会被剥离不进入透传字段杜绝删除整个系列MIN_TIME_MS定义在 mobile-calendar.ts服务端通过它挡住非法的时间戳输入。这些规则在 mobile-calendar-delete.test.ts 中有逐条测试佐证it.each([calendar, reminder])(requires exact identity for %s, capability { const base { capability, action: delete, purpose: Delete the selected test item } for (const item of [{}, { title: test }, { id: [1], title: test }, { id: 1 }]) { expect(() request({ ...base, item })).toThrow() } const value request({ ...base, item: { id: 1, title: test, start_ms: 1788624000000, due_ms: 1788624000000, deleteAll: true } }) expect(value.item).not.toHaveProperty(deleteAll) ... })测试表明空 item、缺 id、id 为数组、缺 title 的请求一律抛错而携带deleteAll: true的合法请求在规范化后会被剥离该字段——这正是「不支持批量/系列删除」在数据层的落点。核心契约二删除回报必须显式声明deletedtrue且 id 完全匹配请求侧校验只是第一步响应侧同样严格。normalizeMobileCalendarResponse的删除分支位于 mobile-calendar.tsif (expected.action delete) { if (!isRecord(value.result.item) || value.result.item.id ! expected.item?.id || value.result.item.deleted ! true) return null return { status: success, result: { capability: expected.capability, action: delete, item: { id: expected.item?.id, deleted: true } } } }三条硬性条件缺一不可result.item必须是一个对象result.item.id必须严格等于请求中的 id!比较不允许模糊匹配result.item.deleted必须严格等于true仅「存在」不够必须显式声明。任何一条不满足整个响应都会被判为无效返回null调用方随后以calendar_invalid_request错误码收尾。这一点在 mobile-calendar-delete.test.ts 中有完整的行为矩阵响应场景期望结果{ item: { id: 1, deleted: true } }id 匹配、deletedtrue多余字段被净化成功且仅保留{ id: 1, deleted: true }{ item: { id: other, deleted: true } }id 不匹配判无效返回null{}缺少 item判无效返回nullstatus: denied原样透传{ status: denied }此外即使响应通过校验结果字段也会经过cleanResultItem的允许清单净化mobile-calendar.ts只保留id、title、notes、location与startMs、endMs、dueMs、priority、reminderMinutes、allDay、completed等已知字段任何private、secret之类的附加字段都会被丢弃。这与基线文档「App responses are allowlisted and sanitized」一脉相承删除场景也不例外。错误码集合ERROR_CODES定义在 mobile-calendar.tscalendar_permission_denied、calendar_unavailable、calendar_item_not_found、calendar_invalid_request、calendar_failed。未知错误码会被统一映射为calendar_failed避免把 App 内部细节泄露给模型侧。核心契约三一次性 App 确认与有界截止时间删除不是「请求即执行」而是经由 Socket 事件把请求投递给目标移动设备等待用户当场确认并在有界期限内完成。完整链路位于 chat-run.ts前置守卫L574-L588会话必须存在、profile 必须匹配、session.source必须是直接聊天group_chat/workflow直接抛错Mobile calendar and reminders are available only in direct chats同一会话不允许存在未决的重复请求目标设备必须已注册mobileRunTargets且在线设备房间非空。发出请求事件L599-L621生成requestIdUUID向设备房间发出calendar.requested/reminder.requested负载包含calendar_request_id/reminder_request_id、完整请求体、target_device_id、target_user_id、target_profile、timeout_ms与expires_at_ms。等待 App 回报L1366-L1419App 通过calendar.respond/reminder.respond回传服务端校验会话访问权、请求仍处于 pending、sameMobileDevice目标设备一致再经normalizeMobileCalendarResponse规范化。收尾L2921-L2941finishMobileCalendarRequest移除 pending、清理定时器与事件状态向会话广播calendar.resolved/reminder.resolved并把结果附device_idresolve 给 REST 调用方。有界截止时间由boundedMobileCalendarTimeout实现chat-run.tsconst MOBILE_CALENDAR_MIN_TIMEOUT_MS 3_000 const MOBILE_CALENDAR_MAX_TIMEOUT_MS 300_000 const MOBILE_CALENDAR_DEFAULT_TIMEOUT_MS 300_000 function boundedMobileCalendarTimeout(value: unknown): number { const numeric Number(value) if (value null || !Number.isFinite(numeric) || numeric 0) return MOBILE_CALENDAR_DEFAULT_TIMEOUT_MS return Math.max(MOBILE_CALENDAR_MIN_TIMEOUT_MS, Math.min(MOBILE_CALENDAR_MAX_TIMEOUT_MS, numeric)) }即调用方可传timeout_ms但服务端会把其钳制在 3 秒到 300 秒5 分钟之间缺省即为 300 秒到期后未收到有效回报请求以calendar_failed结束。Socket 层同步把expires_at_ms: Date.now() timeoutMs下发给 App确保「确认卡片」与服务端 deadline 对齐。相关行为由 run-chat-mobile-calendar.test.ts 覆盖收到denied时立即以{ status: denied }结束vi.advanceTimersByTimeAsync(3001)推进 3001ms 后未决请求以status: error结束且 pending 表清空对undefined、null、0、600000、300000五种输入最终timeout_ms均为300000钳制与默认值生效过期后才到达的reminder.respond会被忽略pending 已清空finishMobileCalendarRequest返回false——这正是「fresh App confirmation」的服务端保证。调用入口REST 端点与请求参数删除能力通过既有的POST /api/studio/mobile-calendar/request端点暴露路由注册于 chat-run.ts控制器实现位于 chat-run.ts。请求必须携带 Bearer 认证控制器提取session_id、capability、action、purpose、start_ms、end_ms、include_completed、limit、item、timeout_ms后转交ChatRunSocket.requestMobileCalendarSession not found返回 404其余参数问题返回 400服务不可用时返回 503。openapi.json 给出了该端点的完整契约其中与删除直接相关的字段字段类型约束说明session_idstring必填精确的直接聊天会话 idcapabilitystringcalendar/reminder目标能力actionstringlist/create/update/complete/delete删除即deletepurposestring必填maxLength 240请求目的说明itemobject删除时须含精确id、title日历另须start_ms被删条目的精确身份timeout_msinteger3000 ~ 300000默认 300000确认期限有界一个典型的删除日历事件请求示例curl -X POST http://server/api/studio/mobile-calendar/request \ -H Authorization: Bearer token \ -H Content-Type: application/json \ -d { session_id: session-abc123, capability: calendar, action: delete, purpose: 删除用户选定的那条测试日历事件, item: { id: evt-42, title: Project sync, start_ms: 1788624000000 }, timeout_ms: 60000 }成功响应形如{ ok: true, session_id: session-abc123, device_id: iphone, status: success, result: { capability: calendar, action: delete, item: { id: evt-42, deleted: true } } }若用户在 App 上拒绝则返回{ status: denied }若回报的 id 不匹配或未声明deleted: true则会以{ status: error, error: { code: calendar_invalid_request } }结束。边界与不变量删除能力刻意不做的事该变更记录明确「No list/batch/series deletion, profile scope or background access expansion」这些限制在源码中有多处强制不做批量/系列删除请求侧剥离deleteAll响应侧要求单条id完全匹配注入到模型上下文的运行指令进一步约束chat-run.tsDelete only the exact listed item after fresh App confirmation; never delete a whole recurring series.不做后台访问指令明确禁止在委派子任务、工作流节点或后台跟踪中使用移动工具requestMobileCalendar的前置守卫也拒绝group_chat/workflow来源的会话。不做 profile 范围扩展请求绑定到ctx.state.profilegetSession校验会话 profile 必须与请求 profile 一致chat-run.ts。不做跨设备/跨会话漂移回报必须来自sameMobileDevice的目标设备chat-run.ts非目标设备的响应一律拒绝calendar.requested事件也只投递到目标设备房间。App 兼容性记录明确「Requires the matching App update; older Apps do not support delete」——即旧版本 App 即便收到delete请求也无法正确回报服务端不会为其提供降级路径。测试验证矩阵如何证明删除契约成立删除契约的自动化验证分布在两个测试文件中mobile-calendar-delete.test.ts纯函数级契约测试直接驱动normalizeMobileCalendarRequest/normalizeMobileCalendarResponse覆盖精确身份校验、deleteAll剥离、日历删除必须携带start_msundefined、null、NaN、tomorrow全部抛错、响应 id 匹配与deletedtrue回报。run-chat-mobile-calendar.test.tsSocket 级集成测试验证完整事件流——reminder.requested负载含expires_at_ms、Appdenied回报即时结束、超时3001ms后以error结束并清空 pending、五种timeout_ms输入统一钳制为 300000、非目标设备与过期回报均被拒绝、列表响应中的secret字段被净化丢弃。结语与延伸阅读「Confirmed single-item mobile deletion」是 Hermes Studio 移动能力中一个极具代表性的安全设计样本把破坏性操作压缩到最小范围单条、要求最大确定性精确 id title 发生时间 deletedtrue显式回报、并施加时间与设备双重约束一次性确认 有界期限 目标设备绑定。它既没有引入新的权限面也没有破坏既有 list/create/update/complete 流程而是以增量方式在请求规范化和响应净化两层同时收紧。想深入理解这条契约在更大图景中的位置可以继续阅读基线能力说明docs/chat-chain-changes/2026-09-04-mobile-calendar-reminders.md服务端核心实现mobile-calendar.tsSocket 事件流转与超时边界chat-run.tsREST 端点契约docs/openapi.json/api/studio/mobile-calendar/request契约测试mobile-calendar-delete.test.ts、run-chat-mobile-calendar.test.ts【免费下载链接】ekko-studioEkko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.项目地址: https://gitcode.com/gh_mirrors/he/ekko-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。