
happy-wire 统一线缆协议包深度解析Happy 跨端消息与会话协议的共享契约层【免费下载链接】happyMobile and Web client for Codex and Claude Code, with realtime voice, encryption and fully featured项目地址: https://gitcode.com/gh_mirrors/happy20/happy导读本文围绕仓库中docs/happy-wire.md与 packages/happy-wire/README.md 展开系统讲解 Happy 项目为解决 CLI、App、Server、Agent 四端 wire 级协议重复定义、schema 漂移问题而建立的共享包slopus/happy-wire。读者将掌握该包承载的加密消息/更新契约、现代 Session 协议信封与事件流、仓库内迁移现状、构建发布流程与 schema 演进策略并看到每个规范背后的 Zod 源码与测试佐证可直接用于理解或接入 Happy 的线缆协议层。1. 为什么需要 happy-wirewire 契约漂移问题与包的定位在slopus/happy-wire出现之前Happy 仓库中的 wire 级消息与 Session 协议 schema 被分散复制到 CLI、App、Server、Agent 等多个包中参见 docs/happy-wire.md。这种复制带来两个典型问题漂移风险同一字段例如消息role、更新类型t在不同包中被独立修改导致端与端之间的线缆契约在不知不觉中分叉协议演进困难一次协议升级需要在多个包中同步改动漏改一处就会在运行时暴露为解析失败。slopus/happy-wire正是为此而生它把共享 schema 与类型集中到一个可发布的库中让所有客户端与服务端引用同一份 wire 契约。从源码看该包刻意保持小而专注——src/index.ts只做纯导出见 packages/happy-wire/src/index.ts不包含任何领域业务逻辑完全符合文档只保留 wire contracts类型 Zod schema 小工具的定位。需要说明的是仓库内packages/happy-wire/src/sessionProtocol.ts文件头部有一段注释UNDER REVIEW - NEEDS MORE CAREFUL DESIGN指出当前 Session 协议格式仍在演进应被视为当前生产者/消费者的兼容性契约而非最终的跨 Agent 标准。从源码结构看该包是演进中的协议层读者在基于它二次开发时应留意后续变更。2. 包身份与依赖关系slopus/happy-wire的关键身份信息如下来自 packages/happy-wire/package.json项目值npm 名称slopus/happy-wireworkspace 路径packages/happy-wire当前版本0.1.0包类型可发布库publishConfig.access public非 private运行时依赖zod^4.0.0、paralleldrive/cuid2^2.2.2包管理器pnpm10.11.0packageManager 字段声明入口src/index.ts许可证MIT仓库内其余 workspace 包如 CLI、App、Agent均以版本化依赖^0.1.0的方式引用它而不是直接引用 workspace 内的源文件。这一设计有意模拟发布后消费的形态减少对 workspace 本地文件的隐性耦合见 docs/happy-wire.md 的 Versioning model 一节。3. 模块结构与公共导出全景src/index.ts统一导出六个子模块的内容见 packages/happy-wire/src/index.tsmessages.ts加密消息/更新契约与顶层负载legacyProtocol.ts传统legacy解密负载sessionProtocol.ts现代 Session 协议的信封与事件流controlMessages.ts后台任务通知包装的剥离工具voice.ts实时语音会话的授权/用量响应rigMetadata.tsRig 客户端元数据bot、provider、model、operating modes、capabilities、activity 等。其中前三者是docs/happy-wire.md与 README 描述的核心后三者是源码中实际存在、文档未展开的补充模块本文第 8 节会单独介绍。3.1 messages.ts 导出清单Schema 推断类型SessionMessageContentSchema、SessionMessageSchema、MessageMetaSchema、SessionProtocolMessageSchema、MessageContentSchema、VersionedEncryptedValueSchema、VersionedNullableEncryptedValueSchema、UpdateNewMessageBodySchema、UpdateSessionBodySchema、VersionedMachineEncryptedValueSchema、UpdateMachineBodySchema、CoreUpdateBodySchema、CoreUpdateContainerSchema及对应 TypeScript 类型。迁移期兼容别名供存量消费者在迁移过程中使用见 packages/happy-wire/src/messages.ts兼容别名指向ApiMessageSchema/ApiMessageSessionMessageSchema/SessionMessageApiUpdateNewMessageSchema/ApiUpdateNewMessageUpdateNewMessageBodySchema/UpdateNewMessageBodyApiUpdateSessionStateSchema/ApiUpdateSessionStateUpdateSessionBodySchema/UpdateSessionBodyApiUpdateMachineStateSchema/ApiUpdateMachineStateUpdateMachineBodySchema/UpdateMachineBodyUpdateBodySchema/UpdateBodyUpdateNewMessageBodySchema/UpdateNewMessageBodyUpdateSchema/UpdateCoreUpdateContainerSchema/CoreUpdateContainer3.2 legacyProtocol.ts 导出清单UserMessageSchema、AgentMessageSchema、LegacyMessageContentSchema及其类型见 packages/happy-wire/src/legacyProtocol.ts。3.3 sessionProtocol.ts 导出清单角色、9 种事件、信封与工具函数sessionRoleSchema、sessionTextEventSchema、sessionServiceMessageEventSchema、sessionToolCallStartEventSchema、sessionToolCallEndEventSchema、sessionFileEventSchema、sessionTurnStartEventSchema、sessionStartEventSchema、sessionTurnEndStatusSchema、sessionTurnEndEventSchema、sessionStopEventSchema、sessionEventSchema、sessionEnvelopeSchema、CreateEnvelopeOptions、createEnvelope(...)及SessionEvent、SessionEnvelope等类型。此外源码还导出了文档未列入清单的sessionUsageSchematoken 用量与SessionUsage类型见 packages/happy-wire/src/sessionProtocol.ts。4. 加密消息与更新契约messages.ts4.1 公共原语规则README 明确这些是 schema 级要求而非建议id、sid、machineId、call、name、title、description、ref等一律为stringseq、createdAt、updatedAt、size、width、height、version、activeAt等一律为number所有可空字段显式使用.nullable()所有可选字段显式使用.optional().nullish()表示undefined | null | 类型三态。4.2 SessionMessageContent加密容器内容{ t: encrypted; c: string; }t是严格判别字段字面量encryptedc是加密负载字节的字符串编码当前用法下通常为 base64。这一层保证真实消息在线上始终是密文明文负载只存在于解密之后的校验环节见 packages/happy-wire/src/messages.ts。4.3 SessionMessage一条会话消息的行格式{ id: string; seq: number; localId?: string | null; // .nullish()兼容不同生产者 content: SessionMessageContent; createdAt: number; // 必填 updatedAt: number; // 必填 }localId使用.nullish()以兼容不同生产者createdAt/updatedAt在共享 schema 中是必填字段见 packages/happy-wire/src/messages.ts。4.4 三层 Update 契约new-message / update-session / update-machineUpdateNewMessageBodySchema新增一条消息{ t: new-message; sid: string; // 会话 id message: SessionMessage; // 完整消息行 }UpdateSessionBodySchema更新会话级状态{ t: update-session; id: string; metadata?: VersionedEncryptedValue | null; // value 为 string agentState?: VersionedNullableEncryptedValue | null; // value 可为 string | null }这里有一个关键区分README 专门强调metadata.value在块存在时一定是string而agentState.value在块存在时可以是string或null——后者用于显式把 Agent 状态清空但保留版本号的场景见 packages/happy-wire/src/messages.ts。UpdateMachineBodySchema更新机器/守护进程状态{ t: update-machine; machineId: string; metadata?: VersionedMachineEncryptedValue | null; daemonState?: VersionedMachineEncryptedValue | null; active?: boolean; activeAt?: number; }三个版本化加密值 schema 的对比Schemavalue类型用途VersionedEncryptedValueSchemastring存在时不可为 null 的加密版本化负载VersionedNullableEncryptedValueSchemastring \| null允许显式重置为 null 的版本化负载VersionedMachineEncryptedValueSchemastring机器更新的版本化变体形状同VersionedEncryptedValueSchemaCoreUpdateBodySchema是判别联合discriminatedUnion(t, ...)严格限定恰好三个变体new-message|update-session|update-machine。CoreUpdateContainerSchema给 update body 加上信封{ id: string; seq: number; body: CoreUpdateBody; createdAt: number; }这构成了 Happy 端间同步的核心单位任何对消息、会话或机器的变更都被封装成带id/seq的更新容器便于去重、排序与增量同步见 packages/happy-wire/src/messages.ts。4.5 MessageMeta消息元数据与 permissionMode 的演进MessageMetaSchema承载消息的发送方与运行参数见 packages/happy-wire/src/messageMeta.ts{ sentFrom?: string; // 例如 mobile / cli permissionMode?: string; // 源码为开放的 string model?: string | null; modelProviderId?: string; // 源码补充字段 fallbackModel?: string | null; customSystemPrompt?: string | null; appendSystemPrompt?: string | null; allowedTools?: string[] | null; disallowedTools?: string[] | null; effort?: string | null; // 源码补充字段 displayText?: string; }值得注意docs/happy-wire.md把permissionMode描述为枚举default | acceptEdits | bypassPermissions | plan | read-only | safe-yolo | yolo但源码实现为z.string().optional()。从源码注释看这是有意为之——原生客户端如 Rig可能发布自己的模式编码例如auto/workspace_write/read_only/full_access因此该字段保持开放字符串文档中的枚举是推荐语义集合而非运行时硬约束。这正是文档描述意图、源码体现宽容性的典型例子接入方应以 schema 实际行为为准。5. 顶层解密负载legacy 与 modern 的统一判别5.1 传统负载legacyProtocol.tsUserMessageSchema用户消息role: user{ role: user; content: { type: text; text: string; }; localKey?: string; meta?: MessageMeta; }AgentMessageSchemaAgent 输出role: agent{ role: agent; content: { type: string; [key: string]: unknown; // passthrough允许任意附加字段 }; meta?: MessageMeta; }AgentMessageSchema的 content 使用.passthrough()见 packages/happy-wire/src/legacyProtocol.ts因为传统 Agent 输出有message、data、tool等多种内部形态schema 只锁定type字段其余结构保持开放。LegacyMessageContentSchema按role判别的联合仅包含user与agent两个分支。5.2 现代负载messages.tsSessionProtocolMessageSchemarole字面量固定为sessioncontent直接是SessionEnvelope对象不是包在content.data之下meta可选。这是现代 Session 协议记录的最外层形态。MessageContentSchema顶层role判别联合三路合一user→UserMessageSchemalegacyagent→AgentMessageSchemalegacysession→SessionProtocolMessageSchemamodern从 README 的协议不变量可总结为外层role session标记现代 Session 协议负载内层信封content.role只允许user或agent。两个时代、三种负载形态共存于同一 schema 体系靠role判别字段完成路由。6. Session 协议事件变体与信封6.1 角色sessionRoleSchema为user | agentuser表示用户发起的信封agent表示 Agent/系统输出的信封。6.2 九种事件变体sessionEventSchema是基于t的判别联合恰好 9 个变体源码见 packages/happy-wire/src/sessionProtocol.ts#t字段说明1texttext: string; thinking?: boolean文本输出thinking标记思考过程2servicetext: string服务级消息如 MCP bridge 重启通知3tool-call-startcall, name, title, description: string; args: Recordstring, unknown工具调用开始4tool-call-endcall: string工具调用结束按call配对5fileref, name: string; size: number; mimeType?: string; image?: { width, height, thumbhash }文件/图片引用thumbhash用于缩略图6turn-start无一轮交互开始7starttitle?: string会话/子任务开始8turn-endstatus: completed \| failed \| cancelled一轮交互结束9stop无会话停止相比 README 中列出的file事件字段源码还额外提供了可选的mimeType字段测试中也覆盖了带mimeType与image的 file 事件见 packages/happy-wire/src/sessionProtocol.test.ts。turn-end的status由sessionTurnEndStatusSchemaz.enum([completed, failed, cancelled])约束测试明确验证了拼写canceled美式拼写会被拒绝见 packages/happy-wire/src/sessionProtocol.test.ts接入方务必使用cancelled。6.3 信封 sessionEnvelopeSchema{ id: string; time: number; // Unix 毫秒必填 role: user | agent; turn?: string; subagent?: string; // 出现时必须是合法 cuid2 ev: SessionEvent; }除 README 记载的字段外源码还实现了三个可选扩展字段见 packages/happy-wire/src/sessionProtocol.tsclaudeUuid?: string底层 Agent 协议消息 id如 Claude session JSONL 中的uuid用于让 App 提供精确的 fork/duplicate 回滚点codexItemId?: stringCodex app-server 的 item id作为 Codex 线程 duplicate/fork-from-message 的精确回滚点usage?: SessionUsage携带的 token 用量供消费端更新上下文计量器而无需额外渲染一行。SessionUsage的字段包括必填的input_tokens、output_tokens非负整数可选的cache_creation_input_tokens、cache_read_input_tokens、context_window正整数、service_tier整体.passthrough()以容忍扩展见 packages/happy-wire/src/sessionProtocol.ts。superRefine 交叉约束schema 级强制不仅是约定若ev.t service则role必须为agent若ev.t start或ev.t stop则role必须为agent若subagent存在则必须通过isCuid(...)校验。这些约束在测试中有系统验证见 packages/happy-wire/src/sessionProtocol.test.tsrole: user携带 service 事件、role: user携带 start 事件、非 cuid2 的 subagent 都会被拒绝同时role: session出现在信封层也会被拒绝信封层不允许session角色。6.4 createEnvelope信封工厂函数createEnvelope(role: SessionRole, ev: SessionEvent, opts?: CreateEnvelopeOptions): SessionEnvelope行为契约源码见 packages/happy-wire/src/sessionProtocol.tsopts.id缺省时用createId()cuid2生成opts.time缺省时用Date.now()turn、subagent、claudeUuid、codexItemId、usage仅在提供时才写入返回值经过sessionEnvelopeSchema.parse(...)因此非法组合如role: userev.t: service会直接抛错测试见 packages/happy-wire/src/sessionProtocol.test.ts。7. 规范性 JSON 示例可直接作为对接基准7.1 加密更新容器含 new-message{ id: upd-1, seq: 100, createdAt: 1739347200000, body: { t: new-message, sid: session-1, message: { id: msg-1, seq: 55, localId: null, content: { t: encrypted, c: Zm9v }, createdAt: 1739347199000, updatedAt: 1739347199000 } } }message.content.c密文解密后对于 Session 协议消息得到外层role: sessioncontent直接是信封time为必填 Unix 毫秒{ role: session, content: { id: env_01, time: 1739347232000, role: agent, turn: turn_01, ev: { t: text, text: I found 3 TODOs. } }, meta: { sentFrom: cli } }7.2 加密更新容器update-session注意metadata.value是字符串而agentState.value为null——两种版本化负载语义的直观对比{ id: upd-2, seq: 101, createdAt: 1739347210000, body: { t: update-session, id: session-1, metadata: { version: 8, value: BASE64... }, agentState: { version: 13, value: null } } }7.3 加密更新容器update-machine{ id: upd-3, seq: 102, createdAt: 1739347220000, body: { t: update-machine, machineId: machine-1, metadata: { version: 2, value: BASE64... }, daemonState: { version: 3, value: BASE64... }, active: true, activeAt: 1739347220000 } }7.4 Session 协议信封{ id: x8s1k2..., role: agent, turn: turn-42, ev: { t: turn-start } }7.5 legacy 与 modern 的对照示例同一段对话在两种协议下的差异README 快速示例legacy 用户消息role: user、content 为{ type: text, text }modern 用户信封外层role: session、内层content.role: user、ev.t: text。协议不变量始终成立外层session标记现代负载内层信封角色只取user/agent。8. 源码中存在的补充模块文档之外的 wire 契约虽然docs/happy-wire.md聚焦核心三模块但src/index.ts同时导出了三个补充模块它们同样属于wire 契约范畴controlMessages.ts提供stripLeadingTaskNotificationWrappers(text)用于剥离文本前导的完整task-notification.../task-notification后台任务控制包装支持嵌套计数同时保留其后真实消息文本不完整或用户手写的内联标签不受影响见 packages/happy-wire/src/controlMessages.ts。voice.ts实时语音的授权与用量响应。VoiceConversationResponseSchema是按allowed判别的联合允许时返回conversationToken、conversationId、agentId、elevenUserId、usedSeconds、limitSeconds拒绝时返回reasonvoice_hard_limit_reached|subscription_required|voice_conversation_limit_reached。另有VoiceUsageResponseSchema描述用量汇总见 packages/happy-wire/src/voice.ts。rigMetadata.tsRig 客户端元数据的完整 schema 族包括RigBotSchemabot 身份、RigProviderSchema、RigModelSchema、RigOperatingModeSchema、RigCapabilitiesSchemaabort/attachments/files/modelSelection/resume/rpcMethods/shell/steering 等能力位、RigActivitySchemasubagents/workflows/processes/tasks 计数以及RigMetadataV1Schema带rigMetadataVersion: 1的版本化元数据容器见 packages/happy-wire/src/rigMetadata.ts。这些模块的存在印证了wire contracts类型 Zod schema 小工具的包定位凡是跨端传输的结构化数据都优先收敛到这里统一维护。9. 解析/验证用法在任何一端CLI、App、Server、Agent使用共享 schema 做运行时校验的标准姿势README 示例导入路径以当前仓库 workspace 为准import { CoreUpdateContainerSchema, sessionEnvelopeSchema, } from slopus/happy-wire; const maybeUpdate CoreUpdateContainerSchema.safeParse(input); if (!maybeUpdate.success) { // invalid update payload } const maybeEnvelope sessionEnvelopeSchema.safeParse(envelopeInput); if (!maybeEnvelope.success) { // invalid envelope/event payload }使用safeParse而非parse是因为线上不可信输入应当走非抛出路径由调用方根据success分支决定降级策略而createEnvelope(...)内部使用parse因为它是生产信封的工厂非法组合应当立即暴露为开发期错误。10. 仓库内迁移现状四个消费端的接入方式10.1 CLIpackages/happy-clisrc/sessionProtocol/types.ts现在直接export * from slopus/happy-wire作为兼容 shim见 packages/happy-cli/src/sessionProtocol/types.tssrc/api/types.ts中的 API wire schema 改为引用slopus/happy-wire的共享消息/更新 schema。从仓库搜索结果看CLI 内部对slopus/happy-wire的引用面很广包括src/api/apiSession.ts、src/claude/utils/sessionProtocolMapper.ts、src/codex/utils/sessionProtocolMapper.ts、src/codex/utils/threadImageBackfill.ts、src/agent/acp/AcpSessionManager.ts、src/agent/acp/runAcp.ts、src/agy/runAgy.ts、src/openclaw/runOpenClaw.ts等——多个 Agent 运行时Claude、Codex、ACP、Agy、OpenClaw都通过该包产出现代 Session 信封由 App 归一化消费。10.2 Apppackages/happy-appsources/sync/apiTypes.ts从slopus/happy-wire导入共享的 API 消息/更新 schemaApiMessageSchema、ApiUpdateNewMessageSchema、ApiUpdateSessionStateSchema、ApiUpdateMachineStateSchema。此外sources/sync/apiVoice.ts、sources/sync/storageTypes.ts、sources/sync/typesRaw.ts也引用了该包对应 voice 与加密存储相关的 wire 类型。10.3 Serverpackages/happy-serverPrisma JSON 消息内容类型引用slopus/happy-wire的SessionMessageContent事件路由器event router使用共享的SessionMessageContent类型标注new-message负载。10.4 Agentpackages/happy-agentRawMessage现在别名为slopus/happy-wire的SessionMessage说明 Agent 侧的原始消息模型已与共享线缆格式对齐。11. 构建、分发与 Monorepo 构建顺序11.1 package.json 契约slopus/happy-wire的构建配置与仓库中其他可发布库一致见 packages/happy-wire/package.jsonmain:./dist/index.cjsmodule:./dist/index.mjstypes:./dist/index.d.ctsexports[.]同时提供 CJS 与 ESM 入口及其类型路径构建脚本shx rm -rf dist tsc --noEmit pkgroll类型检查 pkgroll 打包出 ESM/CJS/类型三件套测试build vitest run针对src/*.test.ts发布门禁prepublishOnly依次执行 build test发布文件仅包含dist、package.json、README.md发布源publishConfig.registry指向 npm registryaccess: public。11.2 Monorepo 构建依赖行为仓库内消费者通过 package exports 引用指向dist/*的产物因此在干净检出时构建顺序很重要先构建 wireyarn workspace slopus/happy-wire build生成dist供依赖方解析再构建/类型检查依赖方。发布到 npm 之后依赖方则直接从发布 tarball 消费预构建产物不再依赖 workspace 本地dist。需要说明的是docs/happy-wire.md与 README 以yarn workspace ...描述命令而package.json的packageManager字段声明为pnpm10.11.0脚本内部使用$npm_execpath转发当前包管理器在 pnpm 工作区环境中等价命令为pnpm --filter slopus/happy-wire build请按仓库实际环境选择。12. 发布流程与维护清单12.1 发布命令与仓库其他可发布包共用同一个 release 入口yarn release # choose happy-wire或直接指定 workspaceyarn workspace slopus/happy-wire releaserelease脚本为npx --no-install release-it走release-it流程。12.2 维护者发布检查清单docs/happy-wire.md 原文要点确保所有 workspace 构建/测试通过确认 wire schema 变更向后兼容或已记录文档升级并发布slopus/happy-wire视需要更新下游包版本仅在新版 happy-wire 可用后才发布依赖它的下游包更新。12.3 变更策略README Change Policy修改 wire schema 时应遵守优先加法式变更additive让旧消费者保持兼容将判别值t视为协议级 API避免破坏性重命名语义变更需在 README 中记录在下游发布依赖新 schema 行为的版本之前先升级包版本。13. 演进方向与边界Notesdocs/happy-wire.md对包的边界做了三条明确约束happy-wire应保持聚焦只承载 wire contracts类型 Zod schema 小工具领域/业务逻辑应留在各消费者包中CLI 的 Agent 适配、App 的 UI 状态、Server 的持久化等尽量保持 schema 加法式演进以最小化客户端破坏。结合src/sessionProtocol.ts头部的UNDER REVIEW注释可以推断现代 Session 协议当前仍由多个 CLI 运行时Claude、Codex、ACP、Agy、OpenClaw产出、由 App 归一化格式仍在演进对协议的未来走向维护者倾向参考业内标准化做法而非自行固化信封格式。因此接入方应当把sessionProtocol视为当前兼容性契约把messages.ts/legacyProtocol.ts视为更稳定的线缆基础并紧跟包版本的加法式升级。14. 速查核心文件索引主题文件包说明文档docs/happy-wire.md包 README含全部 schema 规范packages/happy-wire/README.md公共导出入口packages/happy-wire/src/index.ts消息/更新 schema 与兼容别名packages/happy-wire/src/messages.ts传统负载 schemapackages/happy-wire/src/legacyProtocol.ts现代 Session 协议事件/信封/工厂packages/happy-wire/src/sessionProtocol.ts消息元数据permissionMode 等packages/happy-wire/src/messageMeta.tsSession 协议测试用例packages/happy-wire/src/sessionProtocol.test.ts包构建/发布配置packages/happy-wire/package.jsonCLI 兼容 shimpackages/happy-cli/src/sessionProtocol/types.tsApp 侧共享 schema 引用packages/happy-app/sources/sync/apiTypes.ts至此从为什么存在到每个 schema 的字段语义再到四端迁移与发布流程slopus/happy-wire作为 Happy 全链路线缆契约层的全貌已经完整呈现。读者无论是接续开发新 Agent 运行时、实现新的同步端点还是审计协议兼容性都可以以此包为唯一的契约基准避免再次陷入 schema 漂移的泥潭。【免费下载链接】happyMobile and Web client for Codex and Claude Code, with realtime voice, encryption and fully featured项目地址: https://gitcode.com/gh_mirrors/happy20/happy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考