@clawos-dev/clawd 0.2.262 → 0.2.264

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -4149,346 +4149,6 @@ var init_zod = __esm({
4149
4149
  }
4150
4150
  });
4151
4151
 
4152
- // ../protocol/src/methods.ts
4153
- var METHOD_NAMES, RPC_METHOD_STATUSES, RpcMethodStatusSchema, RpcMethodInfoSchema, MetaMethodsResultSchema;
4154
- var init_methods = __esm({
4155
- "../protocol/src/methods.ts"() {
4156
- "use strict";
4157
- init_zod();
4158
- METHOD_NAMES = [
4159
- "session:create",
4160
- "session:list",
4161
- "session:get",
4162
- "session:update",
4163
- "session:delete",
4164
- "session:send",
4165
- // per-turn sender 2026-07-08 spec §4.6:MCP servers (ticket / inbox ...) 走 HTTP RPC
4166
- // /rpc/session:currentTurnSender(Bearer)拉当前 turn caller;WS handler 同名
4167
- "session:currentTurnSender",
4168
- "session:stop",
4169
- // 温柔中断当前轮次,不杀进程(走 CC IPC control_request.subtype=interrupt)
4170
- "session:interrupt",
4171
- // 回滚到指定 user message,并撤销其后 CC 的文件编辑(走 CC IPC control_request.subtype=rewind_files)
4172
- "session:rewind",
4173
- // Rewind 预览:daemon 读 ~/.claude/file-history 快照 + 磁盘现状算真实 diff,不依赖 CC 进程
4174
- "session:rewind-diff",
4175
- // 打开 session 时一次性拿到"有真实 diff"的 user message id 集合,UI heuristic 据此过滤按钮显示
4176
- "session:rewindable-message-ids",
4177
- // 从指定 message uuid 处 fork 出新 transcript(截断 + 重写 sessionId/parentUuid + forkedFrom)
4178
- // daemon 只生成新 jsonl 文件并返回新 toolSessionId;UI 自行决定是否走 session:create 拉起 resume
4179
- "session:fork",
4180
- "session:new",
4181
- "session:resume",
4182
- "session:observe",
4183
- "session:events",
4184
- "session:subscribe",
4185
- "session:unsubscribe",
4186
- "session:pin",
4187
- // 清共享未读标记(SessionFile.unreadAt → null)+ 广播 session:info;幂等
4188
- "session:markRead",
4189
- "session:reorderPins",
4190
- // mirror peer sessions (2026-05-26): owner-only RPC,写/删本机 mirror SessionFile —
4191
- // mirror 是「**我**在朋友 daemon 上自己创建的 session」的本机持久化副本(不是朋友的会话),
4192
- // 方便 sidebar 跨设备聚合。daemon dumb 写删,**不广播任何 push 帧** (不发 session:info /
4193
- // session:deleted);UI 是写入唯一源,双写完成后通过 cache.setData('sessions', ...) 同步
4194
- // 本地 sidebar state。native session:* handler 完全不感知 mirror (UI 操作 mirror 不复用
4195
- // native 路径)。
4196
- "peerSession:upsert",
4197
- "peerSession:remove",
4198
- "permission:respond",
4199
- // AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
4200
- // 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
4201
- "session:answerQuestion",
4202
- // AskUserQuestion 用户跳过:daemon 向 CC 回 allow + answers={} 让 tool 走空答案路径,
4203
- // 同时广播 cleared 帧(无 answers)让所有客户端 UI 卡片转 Ended
4204
- "session:cancelQuestion",
4205
- "history:projects",
4206
- "history:list",
4207
- "history:read",
4208
- "history:subagents",
4209
- "history:subagent-read",
4210
- "history:recentDirs",
4211
- "workspace:list",
4212
- "workspace:read",
4213
- "shift:list",
4214
- // ---- shift-internal:* (cc 内 shift MCP server 管理 owner 排班,spec 2026-06-24-clawd-shift) ----
4215
- // 统一 RPC 鉴权(spec 2026-07-28)后经 /rpc + Bearer owner token 调;personaId 归因
4216
- // 从 body 来(env CLAWD_PERSONA_ID),handler 内 createdByPersona 自检防跨 persona 误操作。
4217
- "shift-internal:add",
4218
- "shift-internal:list",
4219
- "shift-internal:cancel",
4220
- "shift-internal:update",
4221
- "shift-internal:history",
4222
- "skills:list",
4223
- "agents:list",
4224
- "git:root",
4225
- "git:branch",
4226
- "git:branches",
4227
- "capabilities:get",
4228
- // ---- persona:* (persona 管理) ----
4229
- // persona:list 响应 shape 不分流(统一返 PersonaFile[]),但 daemon handler 按
4230
- // ConnectionContext 投影不同子集:
4231
- // owner ctx → 返全量 personas(含 private)
4232
- // guest ctx → persona.public === true ∧ assertGrant(ctx.grants, {persona,p.id}, 'read')
4233
- // method-grants.ts: persona:list 是 CAPABILITY_SCOPED;其它 persona:* 仍 ADMIN_ANY。
4234
- "persona:create",
4235
- "persona:list",
4236
- "persona:get",
4237
- "persona:update",
4238
- "persona:delete",
4239
- // ---- personaDispatch:* (dispatch 全家,spec 2026-07-28-unify-rpc-auth 统一登记) ----
4240
- // run:本地 MCP server 经 /rpc(Bearer owner token);跨设备 A 的 peer daemon 经 /rpc
4241
- //(Bearer connect token)。capability-scoped(handler 自检 persona.public + assertGrant(send))。
4242
- // complete / list:B / A 本机 MCP server 调(owner token → ADMIN_ANY)。
4243
- // get:跨设备轮询——A 端 daemon 凭 connect token 查 B 台账终态;handler 校验行归属。
4244
- "personaDispatch:run",
4245
- "personaDispatch:complete",
4246
- "personaDispatch:list",
4247
- "personaDispatch:get",
4248
- // ---- peerExec:run (跨设备 exec,spec 2026-07-16-cross-device-exec-mcp) ----
4249
- // A 侧本机 MCP server 经 /rpc(Bearer owner token)带 peerDeviceId → daemon forward;
4250
- // B 侧对端经 /rpc(Bearer connect token)无 peerDeviceId → 本地 spawn。METHOD_GRANT_MAP
4251
- // 设 public,handler 内判 remoteAccessAllowed(跟 inbox:postMessage 同款)。
4252
- "peerExec:run",
4253
- // ---- session:pty 双向透传(仅 CLAWD_CC_MODE=tui 才生效,对应 session:pty push 帧的上行) ----
4254
- // session:pty:input — UI 把用户键盘字节(base64 UTF-8)发给 daemon,daemon 直写 pty stdin。
4255
- // 高频帧(每次按键 1 帧),不入 reducer / 不广播。response 是 fire-and-forget 风格 ack。
4256
- // session:pty:resize — UI 端 xterm 尺寸(cols/rows)变化时同步给 daemon 调 pty resize;
4257
- // 触发频率低(仅 window resize),response 也是 ack。
4258
- "session:pty:input",
4259
- "session:pty:resize",
4260
- // session:pty:snapshot — UI XtermPanel 重新挂载(切 session 回来 / 刷新)时主动拉一次当前
4261
- // 屏幕全量快照,再开始消费 incremental session:pty。daemon 调 manager.getPtyReplay(sessionId)
4262
- // 先 drain detector parse queue 再 serialize,保证 atSeq 与 buffer 内容严格对齐。
4263
- // response 字段:
4264
- // - data:当前屏幕 + scrollback 的 ANSI 字节串(base64-utf8),与 session:pty push 帧同格式
4265
- // - atSeq:此 snapshot 覆盖到的最后一帧 session:pty 的 seq(即 detector 已 parse 完成的最大 seq)
4266
- // UI 端 flush 时丢弃 pending 中 seq ≤ atSeq 的帧
4267
- // - popupKind:当前 detector 检测到的 popup 类型(null = 无 popup)
4268
- // session 不存在 / 非 tui / pty 已 exit 时 data=null, atSeq=0。
4269
- "session:pty:snapshot",
4270
- // ---- attachment.* file-sharing(详见 attachment-schemas.ts) ----
4271
- // 命名警告:这里的 `attachment.*` RPC 与 CC v2.x 上行 `type:"attachment"` 系统行
4272
- // (attachment-skills / attachment-deferred-tools / attachment_memories)是不同概念。
4273
- // 全部管理类 RPC dispatcher grant 限 owner(capability admin-only);
4274
- // 实际文件传输走 HTTP 路由(GET /files?p=&e=&s=,HMAC 签名自包含,不在白名单内)。
4275
- "attachment.signUrl",
4276
- "attachment.groupAdd",
4277
- "attachment.groupRemove",
4278
- "attachment.groupList",
4279
- // ---- inbox:* (capability platform Phase 3 跨用户通知 + Phase 4 DM) ----
4280
- // owner 接 guest 的 cross-principal 消息事件 + DM 双向私聊.
4281
- // inbox:list / markRead: admin-only (Phase 3); inbox:postMessage: DM 自带能力,
4282
- // 任何 principal (owner/guest) 可调 (Phase 4).
4283
- "inbox:list",
4284
- "inbox:markRead",
4285
- "inbox:postMessage",
4286
- // inbox:sendDm——persona 代发 DM 编排(inbox MCP server 经 /rpc + Bearer owner token 调)。
4287
- // sender/mode 由 handler 按 body.sessionId 反查 session scope 归因(业务逻辑非鉴权)。
4288
- "inbox:sendDm",
4289
- // ---- contact:* (联系人列表) ----
4290
- // 飞书统一身份 Phase 3 (决策 #14): received-capability:* 正名为 contact:*——Phase 2 后
4291
- // 联系人身份由中心 server introspect 背书,不再是"对方颁发的 capability"。
4292
- // 写入路径:device:connect 落 store + 自动反向(决策 #11);list / remove 作联系人列表读删。
4293
- "contact:list",
4294
- "contact:pin",
4295
- "contact:remove",
4296
- // ---- contact:setRemoteAccess (远程访问授权,spec 2026-07-16) ----
4297
- // owner UI 授权 contact 反向操作我的设备(跨设备 exec 开关 remoteAccessAllowed)。
4298
- // 详见 protocol/src/contact-remote-access.ts.
4299
- "contact:setRemoteAccess",
4300
- // ---- contact:handshake (双向确认建联握手, spec 2026-07-22) ----
4301
- // device:connect 临时连接调用;被调方同步落库+broadcast 后回 ok(携带身份载荷)。
4302
- // 替代旧 whoami+auto-reverse 组合;whoami 对 connect-token guest 已封(旧客户端拦截)。
4303
- "contact:handshake",
4304
- // ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
4305
- // owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
4306
- // visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
4307
- // 访客身份用 TTC unique_id,会话按 creatorPrincipalId='visitor-<id>' 进现有会话用户分组。
4308
- // 访客换 token 走 HTTP-only 公开路由 POST /share/exchange(非 WS method)。
4309
- "visitor:list",
4310
- // ---- whoami (v2 capability platform) ----
4311
- // 任何 authed connection 都能调;返回 owner 显示名 + 当前 capability wire 形态 +
4312
- // grants 对应的 persona 元数据 (id + displayName)。UI 端 AddRemotePersonaDialog
4313
- // 的 preview 一次性临时 client 用它判定身份和可用 persona。
4314
- "whoami",
4315
- "info",
4316
- "ping",
4317
- // ---- meta:methods (RPC 自省) ----
4318
- // 任何 authed connection 都能调(同 whoami):返回 METHOD_NAMES 全集 + **对本连接**
4319
- // 的可调用性三态。agent 侧 clawd-rpc 的 list tool 是唯一消费方——统一鉴权删掉白名单后
4320
- // agent 面前是 104 个 method,没有自省就只能靠试错。
4321
- "meta:methods",
4322
- // ---- extension runtime (v1: local mode only, no host-bridge —
4323
- // see clawd/superpowers/specs/2026-05-27-clawd-extensions-v1-design.md) ----
4324
- "extension.list",
4325
- "extension.open",
4326
- "extension.close",
4327
- "extension.uninstall",
4328
- // ---- extension sharing v1 (spec 2026-05-28-clawd-extension-sharing-design) ----
4329
- // owner-side: 公开 / 撤回 / 查 publish 状态(owner-only ADMIN_ANY)
4330
- "extension.publish",
4331
- "extension.unpublish",
4332
- "extension.publish:status",
4333
- // cross-tunnel: guest 从 owner 拉 published list + bundle(CAPABILITY_SCOPED)
4334
- "extension.list-published",
4335
- "extension.fetchBundle",
4336
- // guest-self: guest 在自己 daemon 上做安装与更新(ADMIN_ANY,guest 是 owner of self daemon).
4337
- // 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
4338
- "extension.install-from-channel",
4339
- "extension.update-from-channel",
4340
- // ---- auth:* 飞书统一身份 Phase 1 (spec 2026-06-01-feishu-identity-design) ----
4341
- // owner-only:daemon loopback 登录流程(auth:login:start 起 state → HTTP /auth/callback
4342
- // 回调落盘 → auth:login:done 事件)。中央字面量见 protocol/feishu-auth.ts FEISHU_AUTH_METHODS。
4343
- "auth:login:start",
4344
- "auth:getIdentity",
4345
- "auth:logout",
4346
- // ---- device:* 设备目录 + 连接(决策 #15:deviceId + 自包含 token) ----
4347
- // owner-only:公共设备目录 + audience 绑定设备的签名 token 连接(daemon 代理中心 server)。
4348
- // 中央字面量见 protocol/feishu-auth.ts DEVICE_METHODS。
4349
- "device:list",
4350
- "device:connect",
4351
- // ---- app-builder Project (spec 2026-06-01 picker → 2026-06-02 单栏默认 refactor) ----
4352
- // persona-app-builder Project 管理;schemas: ProjectMetadataSchema + 5 个 args/result 对。
4353
- // listProjects: readdir projects/,按目录读 .clawd-project.json
4354
- // createProject: 校验 personaId/未绑 + 校验名 + 从 6173-6182 段分配端口 + 写元数据 + 创建空目录
4355
- // + daemon 内部 mutate session.appBuilderProject + 起 dev server(单栏 refactor 后唯一绑定入口)
4356
- // deleteProject: 停 dev server + rm -rf 目录(绑定 session 由 session:delete 联动 / 不在这里删)
4357
- // updateProjectPort: 校验新端口段内 + 未被别的 project 占 + 重启 dev server
4358
- // setProdUrl: 发布上线后 assistant 调一次写入 .clawd-project.json.prodUrl(取代 prod marker)
4359
- "appBuilder:listProjects",
4360
- // getProject:按 sessionId 取单个绑定 project(含 status)。people/mirror 跨 daemon 场景:
4361
- // 本机 owner 在远端是 guest,listProjects 被 per-user 隔离拿不到别人的 project;getProject
4362
- // 按 session 可见性授权(ensureSessionAccess read)放行。本地也复用(省掉拉全量再 find)。
4363
- "appBuilder:getProject",
4364
- "appBuilder:createProject",
4365
- "appBuilder:deleteProject",
4366
- "appBuilder:updateProjectPort",
4367
- "appBuilder:setProdUrl",
4368
- // 单栏 refactor stage fix (spec 2026-06-02-app-builder-single-pane-default-design §5.6.2):
4369
- // createProject 时不再立即起 dev server(fresh 目录必崩)。assistant scaffold + pnpm install
4370
- // 完成后调 startDevServer 显式触发 supervisor spawn;中间用 reportStage 上报进度(scaffolding
4371
- // / installing 等),daemon broadcast appBuilder:project-updated 帧让 UI PreviewPane 切阶段。
4372
- "appBuilder:startDevServer",
4373
- "appBuilder:reportStage",
4374
- // 显式控制 fix (spec 2026-06-02-app-builder-explicit-dev-server-control):用户点"收起预览"按钮触发,
4375
- // 对称 startDevServer。砍掉了 setActiveSession(dev server 不再跟用户 focus 走),切 session 不动 dev server。
4376
- "appBuilder:stopDevServer",
4377
- // 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design):
4378
- // publish:PreviewPane「发布上线」按钮 onClick 调本 RPC,daemon 内串行化 + spawn publish.sh +
4379
- // 广播阶段进度帧 (appBuilder:publish-progress / publish-failed)。同 project 已有 in-flight job
4380
- // 返回 already-publishing + 既有 jobId(UI 不报错继续订阅)。失败时 daemon 调 manager.send()
4381
- // 注入 user_text 到 chat,由 assistant 接管完成部署。
4382
- // dismissPublishJob:取消 in-flight publish(SIGTERM 子进程 + 清字段)或清掉 daemon 崩溃留下的
4383
- // 中断残骸 publishJob 字段。UI 端 [取消] / [interrupted 取消] 都走这个 RPC。
4384
- "appBuilder:publish",
4385
- "appBuilder:dismissPublishJob"
4386
- ];
4387
- RPC_METHOD_STATUSES = ["allowed", "conditional", "denied"];
4388
- RpcMethodStatusSchema = external_exports.enum(RPC_METHOD_STATUSES);
4389
- RpcMethodInfoSchema = external_exports.object({
4390
- /** canonical method 名,直接可作为 clawd-rpc call 的 method 参数 */
4391
- name: external_exports.string().min(1),
4392
- status: RpcMethodStatusSchema,
4393
- /**
4394
- * `denied` 必带:daemon error code(`UNAUTHORIZED` / `FEISHU_LOGIN_REQUIRED` /
4395
- * `METHOD_NOT_IMPLEMENTED`)。`conditional` 必带:谁来做二次校验。
4396
- * `allowed` 恒无。
4397
- */
4398
- reason: external_exports.string().optional()
4399
- });
4400
- MetaMethodsResultSchema = external_exports.object({
4401
- type: external_exports.literal("meta:methods"),
4402
- /** METHOD_NAMES 全集,顺序与之一致 */
4403
- methods: external_exports.array(RpcMethodInfoSchema)
4404
- });
4405
- }
4406
- });
4407
-
4408
- // ../protocol/src/events.ts
4409
- var DAEMON_MODE_VALUES, SESSION_STATUS_VALUES, HISTORY_USER_META_VALUES, SKILL_SOURCE_VALUES, AGENT_SOURCE_VALUES, ASK_USER_QUESTION_TOOL_NAME;
4410
- var init_events = __esm({
4411
- "../protocol/src/events.ts"() {
4412
- "use strict";
4413
- DAEMON_MODE_VALUES = ["sdk", "tui"];
4414
- SESSION_STATUS_VALUES = [
4415
- "idle",
4416
- "running",
4417
- "running-idle",
4418
- "stopped",
4419
- "error",
4420
- "observing"
4421
- ];
4422
- HISTORY_USER_META_VALUES = [
4423
- "task-notification",
4424
- "slash-command",
4425
- "local-command",
4426
- "system-reminder",
4427
- "skill-hint",
4428
- "meta-text",
4429
- "attachment-skills",
4430
- "attachment-deferred-tools"
4431
- ];
4432
- SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
4433
- AGENT_SOURCE_VALUES = [
4434
- "builtin",
4435
- "global",
4436
- "project",
4437
- "policy",
4438
- "plugin"
4439
- ];
4440
- ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
4441
- }
4442
- });
4443
-
4444
- // ../protocol/src/errors.ts
4445
- var ERROR_CODES, ClawdError;
4446
- var init_errors2 = __esm({
4447
- "../protocol/src/errors.ts"() {
4448
- "use strict";
4449
- ERROR_CODES = {
4450
- SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
4451
- SESSION_BUSY: "SESSION_BUSY",
4452
- INVALID_CWD: "INVALID_CWD",
4453
- INVALID_PATH: "INVALID_PATH",
4454
- TOOL_NOT_SUPPORTED: "TOOL_NOT_SUPPORTED",
4455
- CLAUDE_NOT_FOUND: "CLAUDE_NOT_FOUND",
4456
- SPAWN_FAILED: "SPAWN_FAILED",
4457
- INVALID_PERMISSION_MODE: "INVALID_PERMISSION_MODE",
4458
- METHOD_NOT_IMPLEMENTED: "METHOD_NOT_IMPLEMENTED",
4459
- METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
4460
- VALIDATION_ERROR: "VALIDATION_ERROR",
4461
- PERMISSION_REQUEST_STALE: "PERMISSION_REQUEST_STALE",
4462
- FILE_TOO_LARGE: "FILE_TOO_LARGE",
4463
- INTERNAL: "INTERNAL",
4464
- UNAUTHORIZED: "UNAUTHORIZED",
4465
- FORBIDDEN: "FORBIDDEN",
4466
- INVALID_PARAM: "INVALID_PARAM",
4467
- // 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
4468
- // daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
4469
- // UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
4470
- FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED",
4471
- // app-builder 单栏默认 refactor (spec 2026-06-02-app-builder-single-pane-default-design):
4472
- // appBuilder:createProject 加 personaId + session 未绑校验后抛的两个错码。
4473
- WRONG_PERSONA: "WRONG_PERSONA",
4474
- SESSION_ALREADY_BOUND: "SESSION_ALREADY_BOUND",
4475
- // 2026-07-02 unified people sidebar: contact:pin 时 deviceId 不在 store(可能被别处删了)
4476
- CONTACT_NOT_FOUND: "CONTACT_NOT_FOUND",
4477
- // 统一 RPC 鉴权(spec 2026-07-28):personaDispatch:get 查不到台账行,或 guest ctx
4478
- // 查非自己发起的行(越权与未知 id 同码,不泄露存在性)。A 端轮询遇此码立即判失败。
4479
- DISPATCH_NOT_FOUND: "DISPATCH_NOT_FOUND"
4480
- };
4481
- ClawdError = class extends Error {
4482
- constructor(code, message) {
4483
- super(message);
4484
- this.code = code;
4485
- this.name = "ClawdError";
4486
- }
4487
- code;
4488
- };
4489
- }
4490
- });
4491
-
4492
4152
  // ../protocol/src/attachment-schemas.ts
4493
4153
  var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentUploadResponseSchema;
4494
4154
  var init_attachment_schemas = __esm({
@@ -4573,56 +4233,733 @@ var init_attachment_schemas = __esm({
4573
4233
  }
4574
4234
  });
4575
4235
 
4576
- // ../protocol/src/principal.ts
4577
- function makeOwnerPrincipal(id, displayName, feishuUnionId) {
4578
- return PrincipalSchema.parse({
4579
- id,
4580
- kind: "owner",
4581
- displayName,
4582
- ...feishuUnionId ? { feishuUnionId } : {}
4583
- });
4584
- }
4585
- function principalToSender(p2) {
4586
- return {
4587
- did: p2.id,
4588
- displayName: p2.displayName,
4589
- principal: p2.kind,
4590
- ...p2.feishuUnionId ? { feishuUnionId: p2.feishuUnionId } : {}
4591
- };
4592
- }
4593
- function makeGuestSender(did, displayName, feishuUnionId) {
4594
- return SenderInfoSchema.parse({
4595
- did,
4596
- displayName,
4597
- principal: "guest",
4598
- ...feishuUnionId ? { feishuUnionId } : {}
4599
- });
4600
- }
4601
- var PrincipalKindSchema, PrincipalSchema, SenderInfoSchema;
4602
- var init_principal = __esm({
4603
- "../protocol/src/principal.ts"() {
4236
+ // ../protocol/src/capability.ts
4237
+ var ResourceSchema, ActionSchema, GrantSchema, CapabilitySchema, CapabilityWireSchema, WhoamiResponseSchema, PERSONAL_CAP_GRANTS;
4238
+ var init_capability = __esm({
4239
+ "../protocol/src/capability.ts"() {
4604
4240
  "use strict";
4605
4241
  init_zod();
4606
- PrincipalKindSchema = external_exports.enum(["owner", "guest"]);
4607
- PrincipalSchema = external_exports.object({
4242
+ ResourceSchema = external_exports.discriminatedUnion("type", [
4243
+ external_exports.object({ type: external_exports.literal("persona"), id: external_exports.string().min(1) }).strict(),
4244
+ external_exports.object({ type: external_exports.literal("chat"), id: external_exports.string().min(1) }).strict(),
4245
+ external_exports.object({ type: external_exports.literal("*") }).strict()
4246
+ ]);
4247
+ ActionSchema = external_exports.enum(["read", "send", "admin"]);
4248
+ GrantSchema = external_exports.object({
4249
+ resource: ResourceSchema,
4250
+ actions: external_exports.array(ActionSchema).min(1)
4251
+ }).strict();
4252
+ CapabilitySchema = external_exports.object({
4608
4253
  id: external_exports.string().min(1),
4609
- kind: PrincipalKindSchema,
4254
+ // sha256(token) hex 64
4255
+ secretHash: external_exports.string().regex(/^[a-f0-9]{64}$/),
4610
4256
  displayName: external_exports.string(),
4257
+ // 空数组合法 = 纯访客(只能 DM)
4258
+ grants: external_exports.array(GrantSchema),
4259
+ issuedAt: external_exports.number().int().nonnegative(),
4260
+ expiresAt: external_exports.number().int().positive().optional(),
4261
+ maxUses: external_exports.number().int().positive().optional(),
4262
+ usedCount: external_exports.number().int().nonnegative(),
4611
4263
  /**
4612
- * 飞书 union_id。owner: 本机 daemon 已飞书登录时透传自 owner-identity.json;未登录 undefined。
4613
- * guest: v1 保持 undefined(connect token 当前不携带 unionId;未来若签名 payload 加字段可透传)。
4614
- * 消费方(如 ticket MCP / 未来别的飞书身份挂钩场景)按 undefined 决定是否降级;本层不做 fallback。
4264
+ * guest auth 帧捎带 selfPrincipalId 时由 AuthGate.authenticate 回写;
4265
+ * 未被消费的 cap 此字段缺失。
4615
4266
  */
4616
- feishuUnionId: external_exports.string().optional()
4267
+ peerOwnerId: external_exports.string().min(1).optional(),
4268
+ /** 首次 client hello 时间戳(与 peerOwnerId 同步写入) */
4269
+ firstUsedByPeerAt: external_exports.number().int().positive().optional(),
4270
+ /**
4271
+ * 对方 daemon owner 的 displayName,guest auth 帧 selfDisplayName 触发
4272
+ * recordPeerHello 时同步回写。视角 B (2026-05-23) UI 派生联系人列表用此字段
4273
+ * 显示对方名字(避免 fallback 到我颁 cap 时填的 displayName)。
4274
+ */
4275
+ peerDisplayName: external_exports.string().optional()
4617
4276
  }).strict();
4618
- SenderInfoSchema = external_exports.object({
4619
- /** = principal.id = deviceId(决策 #16)。稳定路由 id */
4620
- did: external_exports.string().min(1),
4277
+ CapabilityWireSchema = CapabilitySchema.omit({ secretHash: true });
4278
+ WhoamiResponseSchema = external_exports.object({
4279
+ type: external_exports.literal("whoami:ok"),
4280
+ owner: external_exports.object({
4281
+ id: external_exports.string(),
4282
+ kind: external_exports.enum(["owner", "guest"]),
4283
+ displayName: external_exports.string(),
4284
+ /** 归属人 id(决策 #16:TTC unique_id 等,落 contact.ownerId) */
4285
+ ownerId: external_exports.string(),
4286
+ /** 身份来源(决策 #16:'ttc'/'google',落 contact.provider) */
4287
+ provider: external_exports.string()
4288
+ }).strict(),
4289
+ capability: CapabilityWireSchema,
4290
+ grantedPersonas: external_exports.array(
4291
+ external_exports.object({
4292
+ id: external_exports.string().min(1),
4293
+ displayName: external_exports.string()
4294
+ }).strict()
4295
+ )
4296
+ }).strict();
4297
+ PERSONAL_CAP_GRANTS = Object.freeze([
4298
+ Object.freeze({
4299
+ resource: Object.freeze({ type: "persona", id: "*" }),
4300
+ actions: Object.freeze(["read", "send"])
4301
+ })
4302
+ ]);
4303
+ }
4304
+ });
4305
+
4306
+ // ../protocol/src/contact-handshake.ts
4307
+ var ContactHandshakeArgsSchema, ContactHandshakeOkSchema;
4308
+ var init_contact_handshake = __esm({
4309
+ "../protocol/src/contact-handshake.ts"() {
4310
+ "use strict";
4311
+ init_zod();
4312
+ init_capability();
4313
+ ContactHandshakeArgsSchema = external_exports.object({
4314
+ selfUrl: external_exports.string().min(1)
4315
+ });
4316
+ ContactHandshakeOkSchema = external_exports.object({
4317
+ type: external_exports.literal("contact:handshake:ok"),
4318
+ owner: external_exports.object({
4319
+ /** 被调方 deviceId(路由主键;调用方落 Contact 的主键用自己请求的 args.deviceId,此值供校验/展示) */
4320
+ id: external_exports.string(),
4321
+ /** 归属人(落 Contact.ownerId / provider,显示分组用) */
4322
+ ownerId: external_exports.string(),
4323
+ provider: external_exports.string(),
4324
+ displayName: external_exports.string()
4325
+ }),
4326
+ /** 被调方授调用方的 grants(落 Contact.grants) */
4327
+ grants: external_exports.array(GrantSchema)
4328
+ });
4329
+ }
4330
+ });
4331
+
4332
+ // ../protocol/src/contact-remote-access.ts
4333
+ var ContactSetRemoteAccessArgsSchema, ContactSetRemoteAccessOkSchema, ContactRemoteAccessUpdatedFrameSchema;
4334
+ var init_contact_remote_access = __esm({
4335
+ "../protocol/src/contact-remote-access.ts"() {
4336
+ "use strict";
4337
+ init_zod();
4338
+ ContactSetRemoteAccessArgsSchema = external_exports.object({
4339
+ deviceId: external_exports.string().min(1),
4340
+ remoteAccessAllowed: external_exports.boolean()
4341
+ }).strict();
4342
+ ContactSetRemoteAccessOkSchema = external_exports.object({
4343
+ type: external_exports.literal("contact:setRemoteAccess:ok"),
4344
+ deviceId: external_exports.string().min(1),
4345
+ remoteAccessAllowed: external_exports.boolean()
4346
+ }).strict();
4347
+ ContactRemoteAccessUpdatedFrameSchema = external_exports.object({
4348
+ type: external_exports.literal("contact:remote-access-updated"),
4349
+ deviceId: external_exports.string().min(1),
4350
+ remoteAccessAllowed: external_exports.boolean()
4351
+ }).strict();
4352
+ }
4353
+ });
4354
+
4355
+ // ../protocol/src/contact.ts
4356
+ var ContactSchema, ContactWireSchema, ContactRemoveArgsSchema, ContactRemoveOkSchema, ContactListOkSchema, ContactAddedFrameSchema, ContactRemovedFrameSchema, ContactPinArgsSchema, ContactPinOkSchema, ContactPinnedFrameSchema;
4357
+ var init_contact = __esm({
4358
+ "../protocol/src/contact.ts"() {
4359
+ "use strict";
4360
+ init_zod();
4361
+ init_capability();
4362
+ ContactSchema = external_exports.object({
4363
+ /**
4364
+ * 对方设备标识(主键):对方 daemon 自己生成并持久化的标识(auth.json deviceId,
4365
+ * 如 dev-<uuid>),provider 无关。同 deviceId 重复 upsert 覆盖。
4366
+ */
4367
+ deviceId: external_exports.string().min(1),
4368
+ /**
4369
+ * 归属人 id(决策 #16):TTC unique_id 等,由 connect token subOwner 签名背书。
4370
+ * 与 provider 一起唯一标识一个人。DM 不再按它路由(路由用 deviceId),仅显示/分组用。
4371
+ */
4372
+ ownerId: external_exports.string().min(1),
4373
+ /** 身份来源(决策 #16):'ttc'(飞书/手机/邮箱登入 TTC)| 'google' | ...,token 背书 */
4374
+ provider: external_exports.string().min(1),
4375
+ /** 联系人显示名 */
4621
4376
  displayName: external_exports.string(),
4622
- /** owner 未登录 / guest 无飞书身份时缺省。消费方各自决定是否降级 */
4623
- feishuUnionId: external_exports.string().optional(),
4624
- principal: PrincipalKindSchema
4377
+ /** 该设备可达的 ws/tunnel 地址 */
4378
+ remoteUrl: external_exports.string().min(1),
4379
+ // 自动反向建立联系人(spec 决策 #11):被连接方本地验票成功后自动落 A 为
4380
+ // 联系人,但它不持有 A 颁发的 token(A 从未给它换票)→ connectToken 为空串。
4381
+ // device:connect 出站路径仍存中心 server 签发的真实 connect token(断线重连复用)。
4382
+ // 不用 .min(1):自动反向路径合法地存空串,禁止填假 token 占位(不造假数据)。
4383
+ connectToken: external_exports.string(),
4384
+ grants: external_exports.array(GrantSchema),
4385
+ addedAt: external_exports.number().int().nonnegative(),
4386
+ /**
4387
+ * pin 时间戳(ms,Date.now() 语义);null = 未 pin。sidebar 顶部固定区渲染由此驱动。
4388
+ * 老 contacts.json 缺此字段 → zod default 补 null(无破坏性升级;不 orphan)。
4389
+ * 对齐 SessionFile.pinnedAt 语义(daemon 侧持久化,contact:pin RPC 更新 → contact:pinned push)。
4390
+ */
4391
+ pinnedAt: external_exports.number().int().nullable().default(null),
4392
+ /**
4393
+ * 远程访问授权(spec 2026-07-16)。owner 视角:允许该 contact 反向操作我的设备——
4394
+ * 跨设备 exec 的总开关(peerExec:run handler 运行时判它)。default=false,owner 显式开。
4395
+ * 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提。
4396
+ */
4397
+ remoteAccessAllowed: external_exports.boolean().default(false)
4398
+ }).strict();
4399
+ ContactWireSchema = ContactSchema;
4400
+ ContactRemoveArgsSchema = external_exports.object({
4401
+ deviceId: external_exports.string().min(1)
4402
+ }).strict();
4403
+ ContactRemoveOkSchema = external_exports.object({
4404
+ type: external_exports.literal("contact:remove:ok"),
4405
+ deviceId: external_exports.string().min(1)
4406
+ }).strict();
4407
+ ContactListOkSchema = external_exports.object({
4408
+ type: external_exports.literal("contact:list:ok"),
4409
+ contacts: external_exports.array(ContactWireSchema)
4410
+ }).strict();
4411
+ ContactAddedFrameSchema = external_exports.object({
4412
+ type: external_exports.literal("contact:added"),
4413
+ contact: ContactWireSchema
4414
+ }).strict();
4415
+ ContactRemovedFrameSchema = external_exports.object({
4416
+ type: external_exports.literal("contact:removed"),
4417
+ deviceId: external_exports.string().min(1)
4418
+ }).strict();
4419
+ ContactPinArgsSchema = external_exports.object({
4420
+ deviceId: external_exports.string().min(1),
4421
+ pinned: external_exports.boolean()
4422
+ }).strict();
4423
+ ContactPinOkSchema = external_exports.object({
4424
+ type: external_exports.literal("contact:pin:ok"),
4425
+ deviceId: external_exports.string().min(1),
4426
+ pinnedAt: external_exports.number().int().nullable()
4427
+ }).strict();
4428
+ ContactPinnedFrameSchema = external_exports.object({
4429
+ type: external_exports.literal("contact:pinned"),
4430
+ deviceId: external_exports.string().min(1),
4431
+ pinnedAt: external_exports.number().int().nullable()
4432
+ }).strict();
4433
+ }
4434
+ });
4435
+
4436
+ // ../protocol/src/feishu-auth.ts
4437
+ var FEISHU_GATED_METHODS, DeviceEntrySchema, DeviceListResponseSchema, DeviceConnectArgsSchema, DeviceConnectResponseSchema, FeishuIdentitySchema, AuthLoginStartResponseSchema, AuthGetIdentityResponseSchema, AuthLogoutResponseSchema, AuthLoginDoneEventSchema, AuthLoginFailedEventSchema;
4438
+ var init_feishu_auth = __esm({
4439
+ "../protocol/src/feishu-auth.ts"() {
4440
+ "use strict";
4441
+ init_zod();
4442
+ FEISHU_GATED_METHODS = [
4443
+ // 联系人列表(device:connect 落同一 store;list 读 / remove 删)
4444
+ "contact:list",
4445
+ "contact:remove",
4446
+ // DM / 跨用户通知
4447
+ "inbox:list",
4448
+ "inbox:markRead",
4449
+ "inbox:postMessage",
4450
+ // mirror peer sessions(远端会话镜像)
4451
+ "peerSession:upsert",
4452
+ "peerSession:remove",
4453
+ // 设备目录 + 连接(决策 #15:daemon 代理中心 server,需要 owner 的 ttcJwt)
4454
+ "device:list",
4455
+ "device:connect"
4456
+ ];
4457
+ DeviceEntrySchema = external_exports.object({
4458
+ /**
4459
+ * 设备标识(决策 #15:daemon 本地生成的 dev-<uuid>,与人的身份 unionId 无关)。
4460
+ * 中心 server clawd_devices 注册表仅作服务发现(url 下发 + 目录),不在安全路径上。
4461
+ */
4462
+ deviceId: external_exports.string().min(1),
4463
+ /** 显示名 */
4464
+ name: external_exports.string().min(1),
4465
+ /** ws/tunnel 地址 */
4466
+ url: external_exports.string().min(1)
4467
+ });
4468
+ DeviceListResponseSchema = external_exports.object({
4469
+ type: external_exports.literal("device:list:ok"),
4470
+ devices: external_exports.array(DeviceEntrySchema)
4471
+ });
4472
+ DeviceConnectArgsSchema = external_exports.object({
4473
+ /** 目标设备的 deviceId(对方 daemon 生成的 dev-<uuid>) */
4474
+ deviceId: external_exports.string().min(1),
4475
+ /**
4476
+ * 目标设备的 ws/tunnel 地址(可选):
4477
+ * - 公开目录点击:带 url(device:list 已下发,省一次 server 查询)
4478
+ * - 凭 deviceId 连接:缺省 url → daemon exchange 时由中心 server 下发该设备上报的 url
4479
+ * - 注册表不在安全路径上:知道对方 url 时显式传入即可连接(不依赖中心 server 服务发现)
4480
+ */
4481
+ url: external_exports.string().min(1).optional(),
4482
+ /** 显示名(公共设备来自目录;缺省时连上后用 whoami 回填) */
4483
+ name: external_exports.string().optional()
4484
+ });
4485
+ DeviceConnectResponseSchema = external_exports.object({
4486
+ type: external_exports.literal("device:connect:ok"),
4487
+ /** 连接的目标设备标识(= 请求 args.deviceId) */
4488
+ deviceId: external_exports.string(),
4489
+ name: external_exports.string(),
4490
+ url: external_exports.string()
4491
+ });
4492
+ FeishuIdentitySchema = external_exports.object({
4493
+ /** 归属人 id(决策 #16:TTC unique_id,取代飞书 union_id;跨登录方式稳定) */
4494
+ ownerId: external_exports.string().min(1),
4495
+ /** 身份来源:'ttc'(飞书/手机/邮箱登入 TTC)| 'google' | ...(决策 #16,拓展用) */
4496
+ provider: external_exports.string().min(1),
4497
+ /** 显示名。TTC user_info 接口 data.name */
4498
+ displayName: external_exports.string().min(1),
4499
+ /** TTC user_info 接口 data.avatar_url,可缺省 */
4500
+ avatarUrl: external_exports.string().optional(),
4501
+ /** TTC user_info 接口 data.union_id(飞书 union_id;dataclaw 可信入口 X-User-UnionId 用),可缺省 */
4502
+ unionId: external_exports.string().optional()
4503
+ });
4504
+ AuthLoginStartResponseSchema = external_exports.object({
4505
+ type: external_exports.literal("auth:login:start:ok"),
4506
+ /** 完整 TTC 授权页 URL(含 callback_url + state + auto=1),UI 直接 window.open */
4507
+ authUrl: external_exports.string().min(1),
4508
+ /** 防 CSRF 的一次性 nonce;回调时 daemon 校验 */
4509
+ state: external_exports.string().min(1)
4510
+ });
4511
+ AuthGetIdentityResponseSchema = external_exports.object({
4512
+ type: external_exports.literal("auth:getIdentity:ok"),
4513
+ /** null = 未登录 */
4514
+ identity: FeishuIdentitySchema.nullable(),
4515
+ /**
4516
+ * 本机设备标识(决策 #15:daemon 生成并持久化在 auth.json,如 dev-<uuid>)。
4517
+ * 设备属性,与登录态无关、登录登出不变。UI"我的设备标识"用它拼
4518
+ * clawos://device/<deviceId>(替代旧的 unionId——不再向外暴露人的身份标识)。
4519
+ */
4520
+ deviceId: external_exports.string().min(1)
4521
+ });
4522
+ AuthLogoutResponseSchema = external_exports.object({
4523
+ type: external_exports.literal("auth:logout:ok")
4524
+ });
4525
+ AuthLoginDoneEventSchema = external_exports.object({
4526
+ type: external_exports.literal("auth:login:done"),
4527
+ identity: FeishuIdentitySchema
4528
+ });
4529
+ AuthLoginFailedEventSchema = external_exports.object({
4530
+ type: external_exports.literal("auth:login:failed"),
4531
+ reason: external_exports.string()
4532
+ });
4533
+ }
4534
+ });
4535
+
4536
+ // ../protocol/src/dispatch.ts
4537
+ var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema, DISPATCH_STATUSES, DispatchStatusSchema, DispatchRecordSchema, DispatchListArgsSchema, DispatchListResultSchema, DispatchGetArgsSchema, DispatchGetOkSchema;
4538
+ var init_dispatch = __esm({
4539
+ "../protocol/src/dispatch.ts"() {
4540
+ "use strict";
4541
+ init_zod();
4542
+ DispatchOutcomeSchema = external_exports.discriminatedUnion("kind", [
4543
+ external_exports.object({
4544
+ kind: external_exports.literal("success"),
4545
+ text: external_exports.string(),
4546
+ filePaths: external_exports.array(external_exports.string()).optional()
4547
+ }),
4548
+ external_exports.object({
4549
+ kind: external_exports.literal("failure"),
4550
+ reason: external_exports.string()
4551
+ })
4552
+ ]);
4553
+ DispatchRunArgsSchema = external_exports.object({
4554
+ // 新开必填;续派(dispatchId 非空)时可省略——targetPersonaId 从台账行取。
4555
+ targetPersona: external_exports.string().min(1).optional(),
4556
+ // 续派(spec 2026-07-21,单 dispatchId 语义:一件事一行一 id):非空 = 把 prompt
4557
+ // 作为新 turn 打进该 dispatch 的原 worker session 接着干(台账行复位 running)。
4558
+ // 省略 = 新开一件事,daemon 铸新 id。
4559
+ dispatchId: external_exports.string().min(1).optional(),
4560
+ prompt: external_exports.string(),
4561
+ // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
4562
+ // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
4563
+ targetDeviceId: external_exports.string().min(1).optional(),
4564
+ // 可选:override B session 使用的 claude 模型。缺省 = 用 persona 默认。
4565
+ // 合法值集与 claude adapter capabilities.models[].id 一致(见 daemon
4566
+ // tools/claude.ts CLAUDE_MODEL_IDS);handler 层做二次校验,非法值 fail-fast。
4567
+ // protocol 层保持 string 便于未来跨 tool 扩展(届时 union 收合法字面量)。
4568
+ model: external_exports.string().min(1).optional(),
4569
+ // 可选:给新 worker session 起 label(写入 SessionFile.label,sidebar 显示)。
4570
+ // 仅在**新开 dispatch**(dispatchId 缺席)时生效;续派(dispatchId 非空)
4571
+ // 时 daemon 层静默丢弃(避免半途改 label 造成 sidebar 抖动)。
4572
+ // 上限 60 字符——sidebar 显示 tolerance,卡在 dispatch 入口即可。
4573
+ // 协议层不硬拒 sessionLabel + dispatchId 同传,保留 forward-compat。
4574
+ sessionLabel: external_exports.string().min(1).max(60).optional()
4575
+ }).refine((v2) => v2.targetPersona !== void 0 || v2.dispatchId !== void 0, {
4576
+ message: "either targetPersona (new dispatch) or dispatchId (follow-up) is required"
4577
+ }).refine((v2) => !(v2.dispatchId !== void 0 && v2.targetDeviceId !== void 0), {
4578
+ message: "cross-device dispatch cannot be continued (dispatchId + targetDeviceId are mutually exclusive)"
4579
+ });
4580
+ DispatchCompleteArgsSchema = external_exports.object({
4581
+ dispatchId: external_exports.string().min(1),
4582
+ outcome: DispatchOutcomeSchema
4583
+ });
4584
+ DISPATCH_STATUSES = ["running", "completed", "failed"];
4585
+ DispatchStatusSchema = external_exports.enum(DISPATCH_STATUSES);
4586
+ DispatchRecordSchema = external_exports.object({
4587
+ dispatchId: external_exports.string().min(1),
4588
+ sourceSessionId: external_exports.string().min(1),
4589
+ sourcePersonaId: external_exports.string().min(1).optional(),
4590
+ targetPersonaId: external_exports.string().min(1),
4591
+ /** B 的 sessionId;registerBSession 后回填 */
4592
+ workerSessionId: external_exports.string().min(1).optional(),
4593
+ /** 任务摘要;截断(≤500 字符)在 daemon store.add 处保证 */
4594
+ taskText: external_exports.string(),
4595
+ status: DispatchStatusSchema,
4596
+ outcome: DispatchOutcomeSchema.optional(),
4597
+ /** 上层消费者语义(如司礼监的口谕/验收状态),daemon 不解释 */
4598
+ meta: external_exports.record(external_exports.unknown()).optional(),
4599
+ createdAt: external_exports.string().min(1),
4600
+ completedAt: external_exports.string().min(1).optional(),
4601
+ /** 结果注入 source session 成功时戳,兼当投递 dedup 标记 */
4602
+ deliveredAt: external_exports.string().min(1).optional()
4603
+ });
4604
+ DispatchListArgsSchema = external_exports.object({
4605
+ sourceSessionId: external_exports.string().min(1).optional()
4606
+ });
4607
+ DispatchListResultSchema = external_exports.object({
4608
+ records: external_exports.array(DispatchRecordSchema)
4609
+ });
4610
+ DispatchGetArgsSchema = external_exports.object({
4611
+ dispatchId: external_exports.string().min(1)
4612
+ });
4613
+ DispatchGetOkSchema = external_exports.object({
4614
+ type: external_exports.literal("personaDispatch:get:ok"),
4615
+ status: DispatchStatusSchema,
4616
+ outcome: DispatchOutcomeSchema.optional()
4617
+ });
4618
+ }
4619
+ });
4620
+
4621
+ // ../protocol/src/extension.ts
4622
+ function isAllowedHostedUrl(raw) {
4623
+ let u;
4624
+ try {
4625
+ u = new URL(raw);
4626
+ } catch {
4627
+ return false;
4628
+ }
4629
+ if (u.protocol === "https:") return true;
4630
+ if (u.protocol === "http:" && LOCALHOST_HOSTS.has(u.hostname)) return true;
4631
+ return false;
4632
+ }
4633
+ function manifestMode(m2) {
4634
+ return m2.entry?.url ? "hosted" : "local";
4635
+ }
4636
+ function ownerNamespace(ownerPrincipalId) {
4637
+ let h = 2166136261;
4638
+ for (let i = 0; i < ownerPrincipalId.length; i++) {
4639
+ h ^= ownerPrincipalId.charCodeAt(i);
4640
+ h = Math.imul(h, 16777619) >>> 0;
4641
+ }
4642
+ return (h >>> 0).toString(16).padStart(8, "0");
4643
+ }
4644
+ function namespacedExtId(slug, ownerPrincipalId) {
4645
+ return `${slug}-${ownerNamespace(ownerPrincipalId)}`;
4646
+ }
4647
+ function validateSimpleSemver(v2) {
4648
+ if (typeof v2 !== "string") {
4649
+ return { ok: false, reason: "version must be a string" };
4650
+ }
4651
+ const parts = v2.split(".");
4652
+ if (parts.length !== 3) {
4653
+ return { ok: false, reason: `"${v2}" \u2014 only MAJOR.MINOR.PATCH allowed` };
4654
+ }
4655
+ for (const p2 of parts) {
4656
+ if (!/^\d+$/.test(p2)) {
4657
+ return { ok: false, reason: `"${v2}" \u2014 only numeric segments allowed` };
4658
+ }
4659
+ }
4660
+ return { ok: true };
4661
+ }
4662
+ function compareSimpleSemver(a, b2) {
4663
+ const pa = parseOrThrow(a);
4664
+ const pb = parseOrThrow(b2);
4665
+ for (let i = 0; i < 3; i++) {
4666
+ if (pa[i] < pb[i]) return -1;
4667
+ if (pa[i] > pb[i]) return 1;
4668
+ }
4669
+ return 0;
4670
+ }
4671
+ function parseOrThrow(v2) {
4672
+ const r = validateSimpleSemver(v2);
4673
+ if (!r.ok) throw new Error(r.reason);
4674
+ const [a, b2, c] = v2.split(".").map((p2) => parseInt(p2, 10));
4675
+ return [a, b2, c];
4676
+ }
4677
+ var EXT_ID_REGEX, ExtensionStateSchema, LOCALHOST_HOSTS, ExtensionManifestSchema, ExtensionTargetSchema, ExtensionRecordSchema, PublishedSnapshotSchema, ChannelRefSchema, SourceRefSchema, PublishedExtensionRecordSchema, PublishCheckSchema, PublishStatusResultSchema, ExtensionPublishArgs, ExtensionIdArgs, ExtensionFetchBundleArgs, ExtensionFromChannelArgs;
4678
+ var init_extension = __esm({
4679
+ "../protocol/src/extension.ts"() {
4680
+ "use strict";
4681
+ init_zod();
4682
+ EXT_ID_REGEX = /^[a-z0-9][a-z0-9-]*$/;
4683
+ ExtensionStateSchema = external_exports.enum([
4684
+ "stopped",
4685
+ "starting",
4686
+ "running",
4687
+ "crashed",
4688
+ "invalid"
4689
+ ]);
4690
+ LOCALHOST_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
4691
+ ExtensionManifestSchema = external_exports.object({
4692
+ id: external_exports.string().regex(EXT_ID_REGEX),
4693
+ name: external_exports.string().min(1),
4694
+ version: external_exports.string().min(1),
4695
+ apiVersion: external_exports.literal("1"),
4696
+ runtime: external_exports.object({ startCommand: external_exports.string().min(1) }).passthrough().optional(),
4697
+ entry: external_exports.object({
4698
+ url: external_exports.string().url().refine(isAllowedHostedUrl, {
4699
+ message: "INVALID_URL_SCHEME: only https or http://localhost is allowed"
4700
+ })
4701
+ }).strict().optional()
4702
+ }).passthrough().superRefine((m2, ctx) => {
4703
+ const hasRuntime = !!m2.runtime;
4704
+ const hasUrl = !!m2.entry?.url;
4705
+ if (hasRuntime && hasUrl) {
4706
+ ctx.addIssue({
4707
+ code: external_exports.ZodIssueCode.custom,
4708
+ message: "INVALID_MANIFEST: entry.url and runtime cannot coexist"
4709
+ });
4710
+ }
4711
+ if (!hasRuntime && !hasUrl) {
4712
+ ctx.addIssue({
4713
+ code: external_exports.ZodIssueCode.custom,
4714
+ message: "INVALID_MANIFEST: must have either entry.url or runtime"
4715
+ });
4716
+ }
4717
+ });
4718
+ ExtensionTargetSchema = external_exports.discriminatedUnion("kind", [
4719
+ external_exports.object({ kind: external_exports.literal("local"), port: external_exports.number().int().min(1).max(65535) }),
4720
+ external_exports.object({ kind: external_exports.literal("hosted"), url: external_exports.string().url() })
4721
+ ]);
4722
+ ExtensionRecordSchema = external_exports.discriminatedUnion("state", [
4723
+ external_exports.object({
4724
+ extId: external_exports.string(),
4725
+ manifest: ExtensionManifestSchema,
4726
+ state: external_exports.literal("stopped")
4727
+ }),
4728
+ external_exports.object({
4729
+ extId: external_exports.string(),
4730
+ manifest: ExtensionManifestSchema,
4731
+ state: external_exports.literal("starting")
4732
+ }),
4733
+ external_exports.object({
4734
+ extId: external_exports.string(),
4735
+ manifest: ExtensionManifestSchema,
4736
+ state: external_exports.literal("running"),
4737
+ target: ExtensionTargetSchema
4738
+ }),
4739
+ external_exports.object({
4740
+ extId: external_exports.string(),
4741
+ manifest: ExtensionManifestSchema,
4742
+ state: external_exports.literal("crashed")
4743
+ }),
4744
+ external_exports.object({
4745
+ extId: external_exports.string(),
4746
+ manifest: external_exports.unknown().nullable(),
4747
+ state: external_exports.literal("invalid"),
4748
+ invalidReason: external_exports.string().min(1)
4749
+ })
4750
+ ]);
4751
+ PublishedSnapshotSchema = external_exports.object({
4752
+ snapshotHash: external_exports.string().min(1),
4753
+ version: external_exports.string().min(1),
4754
+ publishedAt: external_exports.number()
4755
+ });
4756
+ ChannelRefSchema = external_exports.discriminatedUnion("kind", [
4757
+ external_exports.object({
4758
+ kind: external_exports.literal("p2p-tunnel"),
4759
+ ownerPrincipalId: external_exports.string().min(1),
4760
+ extId: external_exports.string().min(1)
4761
+ })
4762
+ ]);
4763
+ SourceRefSchema = external_exports.discriminatedUnion("kind", [
4764
+ external_exports.object({
4765
+ kind: external_exports.literal("p2p-tunnel"),
4766
+ ownerPrincipalId: external_exports.string().min(1),
4767
+ extId: external_exports.string().min(1),
4768
+ snapshotHash: external_exports.string().min(1)
4769
+ })
4770
+ ]);
4771
+ PublishedExtensionRecordSchema = external_exports.object({
4772
+ extId: external_exports.string().min(1),
4773
+ name: external_exports.string().min(1),
4774
+ version: external_exports.string().min(1),
4775
+ contentHash: external_exports.string().min(1),
4776
+ publishedAt: external_exports.number()
4777
+ });
4778
+ PublishCheckSchema = external_exports.discriminatedUnion("kind", [
4779
+ external_exports.object({ kind: external_exports.literal("clean") }),
4780
+ external_exports.object({
4781
+ kind: external_exports.literal("ready-new-version"),
4782
+ fromVersion: external_exports.string().min(1),
4783
+ toVersion: external_exports.string().min(1)
4784
+ }),
4785
+ external_exports.object({
4786
+ kind: external_exports.literal("error-same-hash"),
4787
+ version: external_exports.string().min(1)
4788
+ }),
4789
+ external_exports.object({
4790
+ kind: external_exports.literal("error-version-not-bumped"),
4791
+ localVersion: external_exports.string().min(1),
4792
+ publishedVersion: external_exports.string().min(1)
4793
+ }),
4794
+ external_exports.object({
4795
+ kind: external_exports.literal("error-version-regression"),
4796
+ localVersion: external_exports.string().min(1),
4797
+ publishedVersion: external_exports.string().min(1)
4798
+ })
4799
+ ]);
4800
+ PublishStatusResultSchema = external_exports.object({
4801
+ localManifest: external_exports.object({
4802
+ name: external_exports.string().min(1),
4803
+ version: external_exports.string().min(1),
4804
+ contentHash: external_exports.string().min(1)
4805
+ }),
4806
+ publishState: external_exports.discriminatedUnion("kind", [
4807
+ external_exports.object({ kind: external_exports.literal("unpublished") }),
4808
+ external_exports.object({
4809
+ kind: external_exports.literal("published"),
4810
+ version: external_exports.string().min(1),
4811
+ contentHash: external_exports.string().min(1),
4812
+ publishedAt: external_exports.number()
4813
+ })
4814
+ ]),
4815
+ check: PublishCheckSchema
4816
+ });
4817
+ ExtensionPublishArgs = external_exports.object({
4818
+ extId: external_exports.string().min(1),
4819
+ /**
4820
+ * 可选版本号:给了就在 publish-check 之前重写 manifest.json 的 version。
4821
+ * nullish 而非 optional —— 改造前的 pickOptionalString 显式把 null 当「没给」处理,
4822
+ * 换成纯 optional 会让 `newVersion: null` 从「不 bump」变成 INVALID_PARAM。
4823
+ */
4824
+ newVersion: external_exports.string().min(1).nullish()
4825
+ });
4826
+ ExtensionIdArgs = external_exports.object({
4827
+ extId: external_exports.string().min(1)
4828
+ });
4829
+ ExtensionFetchBundleArgs = external_exports.object({
4830
+ extId: external_exports.string().min(1),
4831
+ snapshotHash: external_exports.string().min(1)
4832
+ });
4833
+ ExtensionFromChannelArgs = external_exports.object({
4834
+ channelRef: ChannelRefSchema,
4835
+ snapshotHash: external_exports.string().min(1),
4836
+ zipBase64: external_exports.string().min(1)
4837
+ });
4838
+ }
4839
+ });
4840
+
4841
+ // ../protocol/src/inbox.ts
4842
+ var InboxMessageSchema, InboxPostMessageArgsSchema, InboxListArgsSchema, InboxMarkReadArgsSchema, InboxSendDmArgsSchema;
4843
+ var init_inbox = __esm({
4844
+ "../protocol/src/inbox.ts"() {
4845
+ "use strict";
4846
+ init_zod();
4847
+ InboxMessageSchema = external_exports.object({
4848
+ id: external_exports.string().min(1),
4849
+ peerDeviceId: external_exports.string().min(1),
4850
+ senderDeviceId: external_exports.string().min(1),
4851
+ text: external_exports.string().min(1),
4852
+ createdAt: external_exports.number().int().nonnegative(),
4853
+ readBy: external_exports.record(external_exports.string().min(1), external_exports.number().int().positive()).default({}),
4854
+ // persona 代发署名(B 版设计):非空 = 该消息由某 persona 代发,非真人手打。
4855
+ // 缺省 = 真人。routing 身份仍看 senderDeviceId;origin 只用于末端 UI 渲染来源。
4856
+ origin: external_exports.object({ kind: external_exports.literal("persona"), personaId: external_exports.string().min(1) }).optional()
4625
4857
  }).strict();
4858
+ InboxPostMessageArgsSchema = external_exports.object({
4859
+ peerDeviceId: external_exports.string().min(1).optional(),
4860
+ id: external_exports.string().min(1),
4861
+ text: external_exports.string().min(1),
4862
+ createdAt: external_exports.number().int().nonnegative(),
4863
+ // persona 代发署名(跨 daemon forward 时随远程腿透传;真人发缺省)。
4864
+ origin: external_exports.object({ kind: external_exports.literal("persona"), personaId: external_exports.string().min(1) }).optional()
4865
+ }).strict();
4866
+ InboxListArgsSchema = external_exports.object({
4867
+ peerDeviceId: external_exports.string().min(1),
4868
+ sinceCreatedAt: external_exports.number().int().nonnegative().optional()
4869
+ }).strict();
4870
+ InboxMarkReadArgsSchema = external_exports.object({
4871
+ peerDeviceId: external_exports.string().min(1),
4872
+ upToCreatedAt: external_exports.number().int().nonnegative()
4873
+ }).strict();
4874
+ InboxSendDmArgsSchema = external_exports.object({
4875
+ peerDeviceId: external_exports.string().min(1).optional(),
4876
+ text: external_exports.string().min(1)
4877
+ });
4878
+ }
4879
+ });
4880
+
4881
+ // ../protocol/src/peer-exec.ts
4882
+ var PeerExecRunArgsSchema, PeerExecRunOkSchema;
4883
+ var init_peer_exec = __esm({
4884
+ "../protocol/src/peer-exec.ts"() {
4885
+ "use strict";
4886
+ init_zod();
4887
+ PeerExecRunArgsSchema = external_exports.object({
4888
+ /** A 侧发起时带:目标设备 deviceId。peer-forward 转发前 strip 掉,B 侧本地执行时无此字段。 */
4889
+ peerDeviceId: external_exports.string().min(1).optional(),
4890
+ /** 交给对端 `bash -c` 的命令;cc 自拼完整路径(一发一收,无跨命令会话状态)。 */
4891
+ command: external_exports.string().min(1),
4892
+ /** 超时上限(ms);缺省用 handler 默认 30_000,clamp 300_000。 */
4893
+ timeoutMs: external_exports.number().int().positive().optional()
4894
+ }).strict();
4895
+ PeerExecRunOkSchema = external_exports.object({
4896
+ type: external_exports.literal("peerExec:run:ok"),
4897
+ stdout: external_exports.string(),
4898
+ stderr: external_exports.string(),
4899
+ /** 进程退出码;被超时 kill 时 null。 */
4900
+ exitCode: external_exports.number().int().nullable(),
4901
+ timedOut: external_exports.boolean(),
4902
+ stdoutTruncated: external_exports.boolean(),
4903
+ stderrTruncated: external_exports.boolean()
4904
+ }).strict();
4905
+ }
4906
+ });
4907
+
4908
+ // ../protocol/src/shift-internal.ts
4909
+ var ShiftScheduleSchema, personaId, ShiftInternalAddArgs, ShiftInternalListArgs, ShiftInternalCancelArgs, ShiftPatchSchema, ShiftInternalUpdateArgs, ShiftInternalHistoryArgs;
4910
+ var init_shift_internal = __esm({
4911
+ "../protocol/src/shift-internal.ts"() {
4912
+ "use strict";
4913
+ init_zod();
4914
+ ShiftScheduleSchema = external_exports.discriminatedUnion("kind", [
4915
+ /** ISO-8601 时间点;时区随字符串自带 */
4916
+ external_exports.object({ kind: external_exports.literal("at"), at: external_exports.string().min(1) }),
4917
+ /** 固定间隔;anchorMs 缺省 = 从登记时刻起算 */
4918
+ external_exports.object({
4919
+ kind: external_exports.literal("every"),
4920
+ everyMs: external_exports.number().int().positive(),
4921
+ anchorMs: external_exports.number().int().nonnegative().optional()
4922
+ }),
4923
+ /** 5 字段 cron 表达式;tz 缺省 = 系统本地时区 */
4924
+ external_exports.object({ kind: external_exports.literal("cron"), expr: external_exports.string().min(1), tz: external_exports.string().min(1).optional() })
4925
+ ]);
4926
+ personaId = external_exports.string().trim().min(1);
4927
+ ShiftInternalAddArgs = external_exports.object({
4928
+ personaId,
4929
+ name: external_exports.string().min(1),
4930
+ schedule: ShiftScheduleSchema,
4931
+ prompt: external_exports.string().min(1),
4932
+ /** 缺省 = 派给调用方自己 */
4933
+ targetPersona: external_exports.string().min(1).optional(),
4934
+ timeoutMs: external_exports.number().int().positive().optional()
4935
+ });
4936
+ ShiftInternalListArgs = external_exports.object({
4937
+ personaId,
4938
+ /** 缺省 true —— 只列本 persona 建的 */
4939
+ onlyMine: external_exports.boolean().optional()
4940
+ });
4941
+ ShiftInternalCancelArgs = external_exports.object({
4942
+ personaId,
4943
+ shiftId: external_exports.string().min(1)
4944
+ });
4945
+ ShiftPatchSchema = external_exports.object({
4946
+ name: external_exports.string().min(1).optional(),
4947
+ schedule: ShiftScheduleSchema.optional(),
4948
+ prompt: external_exports.string().min(1).optional(),
4949
+ targetPersona: external_exports.string().min(1).optional(),
4950
+ timeoutMs: external_exports.number().int().positive().optional(),
4951
+ enabled: external_exports.boolean().optional()
4952
+ });
4953
+ ShiftInternalUpdateArgs = external_exports.object({
4954
+ personaId,
4955
+ shiftId: external_exports.string().min(1),
4956
+ patch: ShiftPatchSchema
4957
+ });
4958
+ ShiftInternalHistoryArgs = external_exports.object({
4959
+ personaId,
4960
+ shiftId: external_exports.string().min(1),
4961
+ limit: external_exports.number().int().positive().optional()
4962
+ });
4626
4963
  }
4627
4964
  });
4628
4965
 
@@ -4726,8 +5063,97 @@ var init_persona_schemas = __esm({
4726
5063
  }
4727
5064
  });
4728
5065
 
5066
+ // ../protocol/src/events.ts
5067
+ var DAEMON_MODE_VALUES, SESSION_STATUS_VALUES, HISTORY_USER_META_VALUES, SKILL_SOURCE_VALUES, AGENT_SOURCE_VALUES, ASK_USER_QUESTION_TOOL_NAME;
5068
+ var init_events = __esm({
5069
+ "../protocol/src/events.ts"() {
5070
+ "use strict";
5071
+ DAEMON_MODE_VALUES = ["sdk", "tui"];
5072
+ SESSION_STATUS_VALUES = [
5073
+ "idle",
5074
+ "running",
5075
+ "running-idle",
5076
+ "stopped",
5077
+ "error",
5078
+ "observing"
5079
+ ];
5080
+ HISTORY_USER_META_VALUES = [
5081
+ "task-notification",
5082
+ "slash-command",
5083
+ "local-command",
5084
+ "system-reminder",
5085
+ "skill-hint",
5086
+ "meta-text",
5087
+ "attachment-skills",
5088
+ "attachment-deferred-tools"
5089
+ ];
5090
+ SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
5091
+ AGENT_SOURCE_VALUES = [
5092
+ "builtin",
5093
+ "global",
5094
+ "project",
5095
+ "policy",
5096
+ "plugin"
5097
+ ];
5098
+ ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
5099
+ }
5100
+ });
5101
+
5102
+ // ../protocol/src/principal.ts
5103
+ function makeOwnerPrincipal(id, displayName, feishuUnionId) {
5104
+ return PrincipalSchema.parse({
5105
+ id,
5106
+ kind: "owner",
5107
+ displayName,
5108
+ ...feishuUnionId ? { feishuUnionId } : {}
5109
+ });
5110
+ }
5111
+ function principalToSender(p2) {
5112
+ return {
5113
+ did: p2.id,
5114
+ displayName: p2.displayName,
5115
+ principal: p2.kind,
5116
+ ...p2.feishuUnionId ? { feishuUnionId: p2.feishuUnionId } : {}
5117
+ };
5118
+ }
5119
+ function makeGuestSender(did, displayName, feishuUnionId) {
5120
+ return SenderInfoSchema.parse({
5121
+ did,
5122
+ displayName,
5123
+ principal: "guest",
5124
+ ...feishuUnionId ? { feishuUnionId } : {}
5125
+ });
5126
+ }
5127
+ var PrincipalKindSchema, PrincipalSchema, SenderInfoSchema;
5128
+ var init_principal = __esm({
5129
+ "../protocol/src/principal.ts"() {
5130
+ "use strict";
5131
+ init_zod();
5132
+ PrincipalKindSchema = external_exports.enum(["owner", "guest"]);
5133
+ PrincipalSchema = external_exports.object({
5134
+ id: external_exports.string().min(1),
5135
+ kind: PrincipalKindSchema,
5136
+ displayName: external_exports.string(),
5137
+ /**
5138
+ * 飞书 union_id。owner: 本机 daemon 已飞书登录时透传自 owner-identity.json;未登录 undefined。
5139
+ * guest: v1 保持 undefined(connect token 当前不携带 unionId;未来若签名 payload 加字段可透传)。
5140
+ * 消费方(如 ticket MCP / 未来别的飞书身份挂钩场景)按 undefined 决定是否降级;本层不做 fallback。
5141
+ */
5142
+ feishuUnionId: external_exports.string().optional()
5143
+ }).strict();
5144
+ SenderInfoSchema = external_exports.object({
5145
+ /** = principal.id = deviceId(决策 #16)。稳定路由 id */
5146
+ did: external_exports.string().min(1),
5147
+ displayName: external_exports.string(),
5148
+ /** owner 未登录 / guest 无飞书身份时缺省。消费方各自决定是否降级 */
5149
+ feishuUnionId: external_exports.string().optional(),
5150
+ principal: PrincipalKindSchema
5151
+ }).strict();
5152
+ }
5153
+ });
5154
+
4729
5155
  // ../protocol/src/schemas.ts
4730
- var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, ToolFeaturesSchema, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionListArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, GetProjectArgsSchema, GetProjectResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema, PublishArgsSchema, PublishResultSchema, DismissPublishJobArgsSchema, DismissPublishJobResultSchema, AppBuilderPublishProgressEventSchema, AppBuilderPublishFailedEventSchema;
5156
+ var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, ToolFeaturesSchema, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionListArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionInterruptArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionCurrentTurnSenderArgs, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, GetProjectArgsSchema, GetProjectResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema, PublishArgsSchema, PublishResultSchema, DismissPublishJobArgsSchema, DismissPublishJobResultSchema, AppBuilderPublishProgressEventSchema, AppBuilderPublishFailedEventSchema;
4731
5157
  var init_schemas = __esm({
4732
5158
  "../protocol/src/schemas.ts"() {
4733
5159
  "use strict";
@@ -5214,6 +5640,7 @@ var init_schemas = __esm({
5214
5640
  sessionId: external_exports.string().min(1),
5215
5641
  text: external_exports.string()
5216
5642
  });
5643
+ SessionInterruptArgs = SessionIdArgs;
5217
5644
  SessionRewindArgs = external_exports.object({
5218
5645
  sessionId: external_exports.string().min(1),
5219
5646
  userMessageId: external_exports.string().min(1),
@@ -5344,7 +5771,12 @@ var init_schemas = __esm({
5344
5771
  AgentsListResponseSchema = external_exports.object({
5345
5772
  agents: external_exports.array(AgentEntrySchema)
5346
5773
  });
5347
- SessionSubscribeArgs = external_exports.object({ sessionId: external_exports.string().min(1) });
5774
+ SessionCurrentTurnSenderArgs = external_exports.object({
5775
+ // 刻意不加 .min(1):空串是被依赖的软路径(handler 见空即返 sender:null,
5776
+ // session-current-turn-sender.spec 有用例守着)。加了 min 会把它变成硬报错。
5777
+ sessionId: external_exports.string().optional()
5778
+ });
5779
+ SessionSubscribeArgs = external_exports.object({ sessionId: external_exports.string().min(1).optional() });
5348
5780
  SessionPinArgs = external_exports.object({
5349
5781
  sessionId: external_exports.string().min(1),
5350
5782
  pinned: external_exports.boolean()
@@ -5583,7 +6015,9 @@ var init_schemas = __esm({
5583
6015
  CreateProjectArgsSchema = external_exports.object({
5584
6016
  /** 必填:caller session,daemon 自动绑过去(spec 2026-06-02 §3 invariant 1 Session ↔ 1 Project) */
5585
6017
  sessionId: external_exports.string().min(1),
5586
- name: external_exports.string()
6018
+ // .min(1):改造前手写校验是 `!f.name` 明确拒空串;空 name 会让 projectDir('')
6019
+ // 落到 projects 根目录,scaffold 往根上铺模板。
6020
+ name: external_exports.string().min(1)
5587
6021
  }).strict();
5588
6022
  CreateProjectResultSchema = external_exports.object({
5589
6023
  project: ProjectMetadataSchema
@@ -5650,690 +6084,564 @@ var init_schemas = __esm({
5650
6084
  name: external_exports.string().regex(projectNameRegex)
5651
6085
  });
5652
6086
  DismissPublishJobResultSchema = external_exports.object({
5653
- ok: external_exports.literal(true)
5654
- }).strict();
5655
- AppBuilderPublishProgressEventSchema = external_exports.object({
5656
- type: external_exports.literal("appBuilder:publish-progress"),
5657
- name: external_exports.string().regex(projectNameRegex),
5658
- jobId: external_exports.string().min(1),
5659
- stage: external_exports.enum(["build", "deploy", "verify"]),
5660
- status: external_exports.enum(["started", "completed"])
5661
- }).strict();
5662
- AppBuilderPublishFailedEventSchema = external_exports.object({
5663
- type: external_exports.literal("appBuilder:publish-failed"),
5664
- name: external_exports.string().regex(projectNameRegex),
5665
- jobId: external_exports.string().min(1),
5666
- stage: external_exports.enum(["build", "deploy", "verify", "unknown"]),
5667
- errorSummary: external_exports.string()
5668
- }).strict();
5669
- }
5670
- });
5671
-
5672
- // ../protocol/src/frames.ts
5673
- var PROTOCOL_VERSION, DISPATCH_MCP_ID;
5674
- var init_frames = __esm({
5675
- "../protocol/src/frames.ts"() {
5676
- "use strict";
5677
- PROTOCOL_VERSION = 1;
5678
- DISPATCH_MCP_ID = "dispatch";
5679
- }
5680
- });
5681
-
5682
- // ../protocol/src/persona-mode.ts
5683
- var init_persona_mode = __esm({
5684
- "../protocol/src/persona-mode.ts"() {
5685
- "use strict";
5686
- }
5687
- });
5688
-
5689
- // ../protocol/src/capability.ts
5690
- var ResourceSchema, ActionSchema, GrantSchema, CapabilitySchema, CapabilityWireSchema, WhoamiResponseSchema, PERSONAL_CAP_GRANTS;
5691
- var init_capability = __esm({
5692
- "../protocol/src/capability.ts"() {
5693
- "use strict";
5694
- init_zod();
5695
- ResourceSchema = external_exports.discriminatedUnion("type", [
5696
- external_exports.object({ type: external_exports.literal("persona"), id: external_exports.string().min(1) }).strict(),
5697
- external_exports.object({ type: external_exports.literal("chat"), id: external_exports.string().min(1) }).strict(),
5698
- external_exports.object({ type: external_exports.literal("*") }).strict()
5699
- ]);
5700
- ActionSchema = external_exports.enum(["read", "send", "admin"]);
5701
- GrantSchema = external_exports.object({
5702
- resource: ResourceSchema,
5703
- actions: external_exports.array(ActionSchema).min(1)
5704
- }).strict();
5705
- CapabilitySchema = external_exports.object({
5706
- id: external_exports.string().min(1),
5707
- // sha256(token) hex 64
5708
- secretHash: external_exports.string().regex(/^[a-f0-9]{64}$/),
5709
- displayName: external_exports.string(),
5710
- // 空数组合法 = 纯访客(只能 DM)
5711
- grants: external_exports.array(GrantSchema),
5712
- issuedAt: external_exports.number().int().nonnegative(),
5713
- expiresAt: external_exports.number().int().positive().optional(),
5714
- maxUses: external_exports.number().int().positive().optional(),
5715
- usedCount: external_exports.number().int().nonnegative(),
5716
- /**
5717
- * guest 在 auth 帧捎带 selfPrincipalId 时由 AuthGate.authenticate 回写;
5718
- * 未被消费的 cap 此字段缺失。
5719
- */
5720
- peerOwnerId: external_exports.string().min(1).optional(),
5721
- /** 首次 client hello 时间戳(与 peerOwnerId 同步写入) */
5722
- firstUsedByPeerAt: external_exports.number().int().positive().optional(),
5723
- /**
5724
- * 对方 daemon owner 的 displayName,guest auth 帧 selfDisplayName 触发
5725
- * recordPeerHello 时同步回写。视角 B (2026-05-23) UI 派生联系人列表用此字段
5726
- * 显示对方名字(避免 fallback 到我颁 cap 时填的 displayName)。
5727
- */
5728
- peerDisplayName: external_exports.string().optional()
5729
- }).strict();
5730
- CapabilityWireSchema = CapabilitySchema.omit({ secretHash: true });
5731
- WhoamiResponseSchema = external_exports.object({
5732
- type: external_exports.literal("whoami:ok"),
5733
- owner: external_exports.object({
5734
- id: external_exports.string(),
5735
- kind: external_exports.enum(["owner", "guest"]),
5736
- displayName: external_exports.string(),
5737
- /** 归属人 id(决策 #16:TTC unique_id 等,落 contact.ownerId) */
5738
- ownerId: external_exports.string(),
5739
- /** 身份来源(决策 #16:'ttc'/'google',落 contact.provider) */
5740
- provider: external_exports.string()
5741
- }).strict(),
5742
- capability: CapabilityWireSchema,
5743
- grantedPersonas: external_exports.array(
5744
- external_exports.object({
5745
- id: external_exports.string().min(1),
5746
- displayName: external_exports.string()
5747
- }).strict()
5748
- )
5749
- }).strict();
5750
- PERSONAL_CAP_GRANTS = Object.freeze([
5751
- Object.freeze({
5752
- resource: Object.freeze({ type: "persona", id: "*" }),
5753
- actions: Object.freeze(["read", "send"])
5754
- })
5755
- ]);
5756
- }
5757
- });
5758
-
5759
- // ../protocol/src/inbox.ts
5760
- var InboxMessageSchema, InboxPostMessageArgsSchema, InboxListArgsSchema, InboxMarkReadArgsSchema, InboxSendDmArgsSchema;
5761
- var init_inbox = __esm({
5762
- "../protocol/src/inbox.ts"() {
5763
- "use strict";
5764
- init_zod();
5765
- InboxMessageSchema = external_exports.object({
5766
- id: external_exports.string().min(1),
5767
- peerDeviceId: external_exports.string().min(1),
5768
- senderDeviceId: external_exports.string().min(1),
5769
- text: external_exports.string().min(1),
5770
- createdAt: external_exports.number().int().nonnegative(),
5771
- readBy: external_exports.record(external_exports.string().min(1), external_exports.number().int().positive()).default({}),
5772
- // persona 代发署名(B 版设计):非空 = 该消息由某 persona 代发,非真人手打。
5773
- // 缺省 = 真人。routing 身份仍看 senderDeviceId;origin 只用于末端 UI 渲染来源。
5774
- origin: external_exports.object({ kind: external_exports.literal("persona"), personaId: external_exports.string().min(1) }).optional()
5775
- }).strict();
5776
- InboxPostMessageArgsSchema = external_exports.object({
5777
- peerDeviceId: external_exports.string().min(1).optional(),
5778
- id: external_exports.string().min(1),
5779
- text: external_exports.string().min(1),
5780
- createdAt: external_exports.number().int().nonnegative(),
5781
- // persona 代发署名(跨 daemon forward 时随远程腿透传;真人发缺省)。
5782
- origin: external_exports.object({ kind: external_exports.literal("persona"), personaId: external_exports.string().min(1) }).optional()
5783
- }).strict();
5784
- InboxListArgsSchema = external_exports.object({
5785
- peerDeviceId: external_exports.string().min(1),
5786
- sinceCreatedAt: external_exports.number().int().nonnegative().optional()
5787
- }).strict();
5788
- InboxMarkReadArgsSchema = external_exports.object({
5789
- peerDeviceId: external_exports.string().min(1),
5790
- upToCreatedAt: external_exports.number().int().nonnegative()
5791
- }).strict();
5792
- InboxSendDmArgsSchema = external_exports.object({
5793
- peerDeviceId: external_exports.string().min(1).optional(),
5794
- text: external_exports.string().min(1)
5795
- });
5796
- }
5797
- });
5798
-
5799
- // ../protocol/src/contact.ts
5800
- var ContactSchema, ContactWireSchema, ContactRemoveArgsSchema, ContactRemoveOkSchema, ContactListOkSchema, ContactAddedFrameSchema, ContactRemovedFrameSchema, ContactPinArgsSchema, ContactPinOkSchema, ContactPinnedFrameSchema;
5801
- var init_contact = __esm({
5802
- "../protocol/src/contact.ts"() {
5803
- "use strict";
5804
- init_zod();
5805
- init_capability();
5806
- ContactSchema = external_exports.object({
5807
- /**
5808
- * 对方设备标识(主键):对方 daemon 自己生成并持久化的标识(auth.json deviceId,
5809
- * 如 dev-<uuid>),provider 无关。同 deviceId 重复 upsert 覆盖。
5810
- */
5811
- deviceId: external_exports.string().min(1),
5812
- /**
5813
- * 归属人 id(决策 #16):TTC unique_id 等,由 connect token subOwner 签名背书。
5814
- * 与 provider 一起唯一标识一个人。DM 不再按它路由(路由用 deviceId),仅显示/分组用。
5815
- */
5816
- ownerId: external_exports.string().min(1),
5817
- /** 身份来源(决策 #16):'ttc'(飞书/手机/邮箱登入 TTC)| 'google' | ...,token 背书 */
5818
- provider: external_exports.string().min(1),
5819
- /** 联系人显示名 */
5820
- displayName: external_exports.string(),
5821
- /** 该设备可达的 ws/tunnel 地址 */
5822
- remoteUrl: external_exports.string().min(1),
5823
- // 自动反向建立联系人(spec 决策 #11):被连接方本地验票成功后自动落 A 为
5824
- // 联系人,但它不持有 A 颁发的 token(A 从未给它换票)→ connectToken 为空串。
5825
- // device:connect 出站路径仍存中心 server 签发的真实 connect token(断线重连复用)。
5826
- // 不用 .min(1):自动反向路径合法地存空串,禁止填假 token 占位(不造假数据)。
5827
- connectToken: external_exports.string(),
5828
- grants: external_exports.array(GrantSchema),
5829
- addedAt: external_exports.number().int().nonnegative(),
5830
- /**
5831
- * pin 时间戳(ms,Date.now() 语义);null = 未 pin。sidebar 顶部固定区渲染由此驱动。
5832
- * 老 contacts.json 缺此字段 → zod default 补 null(无破坏性升级;不 orphan)。
5833
- * 对齐 SessionFile.pinnedAt 语义(daemon 侧持久化,contact:pin RPC 更新 → contact:pinned push)。
5834
- */
5835
- pinnedAt: external_exports.number().int().nullable().default(null),
5836
- /**
5837
- * 远程访问授权(spec 2026-07-16)。owner 视角:允许该 contact 反向操作我的设备——
5838
- * 跨设备 exec 的总开关(peerExec:run handler 运行时判它)。default=false,owner 显式开。
5839
- * 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提。
5840
- */
5841
- remoteAccessAllowed: external_exports.boolean().default(false)
5842
- }).strict();
5843
- ContactWireSchema = ContactSchema;
5844
- ContactRemoveArgsSchema = external_exports.object({
5845
- deviceId: external_exports.string().min(1)
5846
- }).strict();
5847
- ContactRemoveOkSchema = external_exports.object({
5848
- type: external_exports.literal("contact:remove:ok"),
5849
- deviceId: external_exports.string().min(1)
5850
- }).strict();
5851
- ContactListOkSchema = external_exports.object({
5852
- type: external_exports.literal("contact:list:ok"),
5853
- contacts: external_exports.array(ContactWireSchema)
5854
- }).strict();
5855
- ContactAddedFrameSchema = external_exports.object({
5856
- type: external_exports.literal("contact:added"),
5857
- contact: ContactWireSchema
5858
- }).strict();
5859
- ContactRemovedFrameSchema = external_exports.object({
5860
- type: external_exports.literal("contact:removed"),
5861
- deviceId: external_exports.string().min(1)
5862
- }).strict();
5863
- ContactPinArgsSchema = external_exports.object({
5864
- deviceId: external_exports.string().min(1),
5865
- pinned: external_exports.boolean()
5866
- }).strict();
5867
- ContactPinOkSchema = external_exports.object({
5868
- type: external_exports.literal("contact:pin:ok"),
5869
- deviceId: external_exports.string().min(1),
5870
- pinnedAt: external_exports.number().int().nullable()
5871
- }).strict();
5872
- ContactPinnedFrameSchema = external_exports.object({
5873
- type: external_exports.literal("contact:pinned"),
5874
- deviceId: external_exports.string().min(1),
5875
- pinnedAt: external_exports.number().int().nullable()
5876
- }).strict();
5877
- }
5878
- });
5879
-
5880
- // ../protocol/src/contact-handshake.ts
5881
- var ContactHandshakeArgsSchema, ContactHandshakeOkSchema;
5882
- var init_contact_handshake = __esm({
5883
- "../protocol/src/contact-handshake.ts"() {
5884
- "use strict";
5885
- init_zod();
5886
- init_capability();
5887
- ContactHandshakeArgsSchema = external_exports.object({
5888
- selfUrl: external_exports.string().min(1)
5889
- });
5890
- ContactHandshakeOkSchema = external_exports.object({
5891
- type: external_exports.literal("contact:handshake:ok"),
5892
- owner: external_exports.object({
5893
- /** 被调方 deviceId(路由主键;调用方落 Contact 的主键用自己请求的 args.deviceId,此值供校验/展示) */
5894
- id: external_exports.string(),
5895
- /** 归属人(落 Contact.ownerId / provider,显示分组用) */
5896
- ownerId: external_exports.string(),
5897
- provider: external_exports.string(),
5898
- displayName: external_exports.string()
5899
- }),
5900
- /** 被调方授调用方的 grants(落 Contact.grants) */
5901
- grants: external_exports.array(GrantSchema)
5902
- });
5903
- }
5904
- });
5905
-
5906
- // ../protocol/src/contact-remote-access.ts
5907
- var ContactSetRemoteAccessArgsSchema, ContactSetRemoteAccessOkSchema, ContactRemoteAccessUpdatedFrameSchema;
5908
- var init_contact_remote_access = __esm({
5909
- "../protocol/src/contact-remote-access.ts"() {
5910
- "use strict";
5911
- init_zod();
5912
- ContactSetRemoteAccessArgsSchema = external_exports.object({
5913
- deviceId: external_exports.string().min(1),
5914
- remoteAccessAllowed: external_exports.boolean()
5915
- }).strict();
5916
- ContactSetRemoteAccessOkSchema = external_exports.object({
5917
- type: external_exports.literal("contact:setRemoteAccess:ok"),
5918
- deviceId: external_exports.string().min(1),
5919
- remoteAccessAllowed: external_exports.boolean()
5920
- }).strict();
5921
- ContactRemoteAccessUpdatedFrameSchema = external_exports.object({
5922
- type: external_exports.literal("contact:remote-access-updated"),
5923
- deviceId: external_exports.string().min(1),
5924
- remoteAccessAllowed: external_exports.boolean()
5925
- }).strict();
5926
- }
5927
- });
5928
-
5929
- // ../protocol/src/peer-exec.ts
5930
- var PeerExecRunArgsSchema, PeerExecRunOkSchema;
5931
- var init_peer_exec = __esm({
5932
- "../protocol/src/peer-exec.ts"() {
5933
- "use strict";
5934
- init_zod();
5935
- PeerExecRunArgsSchema = external_exports.object({
5936
- /** A 侧发起时带:目标设备 deviceId。peer-forward 转发前 strip 掉,B 侧本地执行时无此字段。 */
5937
- peerDeviceId: external_exports.string().min(1).optional(),
5938
- /** 交给对端 `bash -c` 的命令;cc 自拼完整路径(一发一收,无跨命令会话状态)。 */
5939
- command: external_exports.string().min(1),
5940
- /** 超时上限(ms);缺省用 handler 默认 30_000,clamp 300_000。 */
5941
- timeoutMs: external_exports.number().int().positive().optional()
5942
- }).strict();
5943
- PeerExecRunOkSchema = external_exports.object({
5944
- type: external_exports.literal("peerExec:run:ok"),
5945
- stdout: external_exports.string(),
5946
- stderr: external_exports.string(),
5947
- /** 进程退出码;被超时 kill 时 null。 */
5948
- exitCode: external_exports.number().int().nullable(),
5949
- timedOut: external_exports.boolean(),
5950
- stdoutTruncated: external_exports.boolean(),
5951
- stderrTruncated: external_exports.boolean()
5952
- }).strict();
5953
- }
5954
- });
5955
-
5956
- // ../protocol/src/extension.ts
5957
- function isAllowedHostedUrl(raw) {
5958
- let u;
5959
- try {
5960
- u = new URL(raw);
5961
- } catch {
5962
- return false;
5963
- }
5964
- if (u.protocol === "https:") return true;
5965
- if (u.protocol === "http:" && LOCALHOST_HOSTS.has(u.hostname)) return true;
5966
- return false;
5967
- }
5968
- function manifestMode(m2) {
5969
- return m2.entry?.url ? "hosted" : "local";
5970
- }
5971
- function ownerNamespace(ownerPrincipalId) {
5972
- let h = 2166136261;
5973
- for (let i = 0; i < ownerPrincipalId.length; i++) {
5974
- h ^= ownerPrincipalId.charCodeAt(i);
5975
- h = Math.imul(h, 16777619) >>> 0;
5976
- }
5977
- return (h >>> 0).toString(16).padStart(8, "0");
5978
- }
5979
- function namespacedExtId(slug, ownerPrincipalId) {
5980
- return `${slug}-${ownerNamespace(ownerPrincipalId)}`;
5981
- }
5982
- function validateSimpleSemver(v2) {
5983
- if (typeof v2 !== "string") {
5984
- return { ok: false, reason: "version must be a string" };
5985
- }
5986
- const parts = v2.split(".");
5987
- if (parts.length !== 3) {
5988
- return { ok: false, reason: `"${v2}" \u2014 only MAJOR.MINOR.PATCH allowed` };
5989
- }
5990
- for (const p2 of parts) {
5991
- if (!/^\d+$/.test(p2)) {
5992
- return { ok: false, reason: `"${v2}" \u2014 only numeric segments allowed` };
5993
- }
5994
- }
5995
- return { ok: true };
5996
- }
5997
- function compareSimpleSemver(a, b2) {
5998
- const pa = parseOrThrow(a);
5999
- const pb = parseOrThrow(b2);
6000
- for (let i = 0; i < 3; i++) {
6001
- if (pa[i] < pb[i]) return -1;
6002
- if (pa[i] > pb[i]) return 1;
6003
- }
6004
- return 0;
6005
- }
6006
- function parseOrThrow(v2) {
6007
- const r = validateSimpleSemver(v2);
6008
- if (!r.ok) throw new Error(r.reason);
6009
- const [a, b2, c] = v2.split(".").map((p2) => parseInt(p2, 10));
6010
- return [a, b2, c];
6011
- }
6012
- var EXT_ID_REGEX, ExtensionStateSchema, LOCALHOST_HOSTS, ExtensionManifestSchema, ExtensionTargetSchema, ExtensionRecordSchema, PublishedSnapshotSchema, ChannelRefSchema, SourceRefSchema, PublishedExtensionRecordSchema, PublishCheckSchema, PublishStatusResultSchema;
6013
- var init_extension = __esm({
6014
- "../protocol/src/extension.ts"() {
6015
- "use strict";
6016
- init_zod();
6017
- EXT_ID_REGEX = /^[a-z0-9][a-z0-9-]*$/;
6018
- ExtensionStateSchema = external_exports.enum([
6019
- "stopped",
6020
- "starting",
6021
- "running",
6022
- "crashed",
6023
- "invalid"
6024
- ]);
6025
- LOCALHOST_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
6026
- ExtensionManifestSchema = external_exports.object({
6027
- id: external_exports.string().regex(EXT_ID_REGEX),
6028
- name: external_exports.string().min(1),
6029
- version: external_exports.string().min(1),
6030
- apiVersion: external_exports.literal("1"),
6031
- runtime: external_exports.object({ startCommand: external_exports.string().min(1) }).passthrough().optional(),
6032
- entry: external_exports.object({
6033
- url: external_exports.string().url().refine(isAllowedHostedUrl, {
6034
- message: "INVALID_URL_SCHEME: only https or http://localhost is allowed"
6035
- })
6036
- }).strict().optional()
6037
- }).passthrough().superRefine((m2, ctx) => {
6038
- const hasRuntime = !!m2.runtime;
6039
- const hasUrl = !!m2.entry?.url;
6040
- if (hasRuntime && hasUrl) {
6041
- ctx.addIssue({
6042
- code: external_exports.ZodIssueCode.custom,
6043
- message: "INVALID_MANIFEST: entry.url and runtime cannot coexist"
6044
- });
6045
- }
6046
- if (!hasRuntime && !hasUrl) {
6047
- ctx.addIssue({
6048
- code: external_exports.ZodIssueCode.custom,
6049
- message: "INVALID_MANIFEST: must have either entry.url or runtime"
6050
- });
6051
- }
6052
- });
6053
- ExtensionTargetSchema = external_exports.discriminatedUnion("kind", [
6054
- external_exports.object({ kind: external_exports.literal("local"), port: external_exports.number().int().min(1).max(65535) }),
6055
- external_exports.object({ kind: external_exports.literal("hosted"), url: external_exports.string().url() })
6056
- ]);
6057
- ExtensionRecordSchema = external_exports.discriminatedUnion("state", [
6058
- external_exports.object({
6059
- extId: external_exports.string(),
6060
- manifest: ExtensionManifestSchema,
6061
- state: external_exports.literal("stopped")
6062
- }),
6063
- external_exports.object({
6064
- extId: external_exports.string(),
6065
- manifest: ExtensionManifestSchema,
6066
- state: external_exports.literal("starting")
6067
- }),
6068
- external_exports.object({
6069
- extId: external_exports.string(),
6070
- manifest: ExtensionManifestSchema,
6071
- state: external_exports.literal("running"),
6072
- target: ExtensionTargetSchema
6073
- }),
6074
- external_exports.object({
6075
- extId: external_exports.string(),
6076
- manifest: ExtensionManifestSchema,
6077
- state: external_exports.literal("crashed")
6078
- }),
6079
- external_exports.object({
6080
- extId: external_exports.string(),
6081
- manifest: external_exports.unknown().nullable(),
6082
- state: external_exports.literal("invalid"),
6083
- invalidReason: external_exports.string().min(1)
6084
- })
6085
- ]);
6086
- PublishedSnapshotSchema = external_exports.object({
6087
- snapshotHash: external_exports.string().min(1),
6088
- version: external_exports.string().min(1),
6089
- publishedAt: external_exports.number()
6090
- });
6091
- ChannelRefSchema = external_exports.discriminatedUnion("kind", [
6092
- external_exports.object({
6093
- kind: external_exports.literal("p2p-tunnel"),
6094
- ownerPrincipalId: external_exports.string().min(1),
6095
- extId: external_exports.string().min(1)
6096
- })
6097
- ]);
6098
- SourceRefSchema = external_exports.discriminatedUnion("kind", [
6099
- external_exports.object({
6100
- kind: external_exports.literal("p2p-tunnel"),
6101
- ownerPrincipalId: external_exports.string().min(1),
6102
- extId: external_exports.string().min(1),
6103
- snapshotHash: external_exports.string().min(1)
6104
- })
6105
- ]);
6106
- PublishedExtensionRecordSchema = external_exports.object({
6107
- extId: external_exports.string().min(1),
6108
- name: external_exports.string().min(1),
6109
- version: external_exports.string().min(1),
6110
- contentHash: external_exports.string().min(1),
6111
- publishedAt: external_exports.number()
6112
- });
6113
- PublishCheckSchema = external_exports.discriminatedUnion("kind", [
6114
- external_exports.object({ kind: external_exports.literal("clean") }),
6115
- external_exports.object({
6116
- kind: external_exports.literal("ready-new-version"),
6117
- fromVersion: external_exports.string().min(1),
6118
- toVersion: external_exports.string().min(1)
6119
- }),
6120
- external_exports.object({
6121
- kind: external_exports.literal("error-same-hash"),
6122
- version: external_exports.string().min(1)
6123
- }),
6124
- external_exports.object({
6125
- kind: external_exports.literal("error-version-not-bumped"),
6126
- localVersion: external_exports.string().min(1),
6127
- publishedVersion: external_exports.string().min(1)
6128
- }),
6129
- external_exports.object({
6130
- kind: external_exports.literal("error-version-regression"),
6131
- localVersion: external_exports.string().min(1),
6132
- publishedVersion: external_exports.string().min(1)
6133
- })
6134
- ]);
6135
- PublishStatusResultSchema = external_exports.object({
6136
- localManifest: external_exports.object({
6137
- name: external_exports.string().min(1),
6138
- version: external_exports.string().min(1),
6139
- contentHash: external_exports.string().min(1)
6140
- }),
6141
- publishState: external_exports.discriminatedUnion("kind", [
6142
- external_exports.object({ kind: external_exports.literal("unpublished") }),
6143
- external_exports.object({
6144
- kind: external_exports.literal("published"),
6145
- version: external_exports.string().min(1),
6146
- contentHash: external_exports.string().min(1),
6147
- publishedAt: external_exports.number()
6148
- })
6149
- ]),
6150
- check: PublishCheckSchema
6151
- });
6087
+ ok: external_exports.literal(true)
6088
+ }).strict();
6089
+ AppBuilderPublishProgressEventSchema = external_exports.object({
6090
+ type: external_exports.literal("appBuilder:publish-progress"),
6091
+ name: external_exports.string().regex(projectNameRegex),
6092
+ jobId: external_exports.string().min(1),
6093
+ stage: external_exports.enum(["build", "deploy", "verify"]),
6094
+ status: external_exports.enum(["started", "completed"])
6095
+ }).strict();
6096
+ AppBuilderPublishFailedEventSchema = external_exports.object({
6097
+ type: external_exports.literal("appBuilder:publish-failed"),
6098
+ name: external_exports.string().regex(projectNameRegex),
6099
+ jobId: external_exports.string().min(1),
6100
+ stage: external_exports.enum(["build", "deploy", "verify", "unknown"]),
6101
+ errorSummary: external_exports.string()
6102
+ }).strict();
6152
6103
  }
6153
6104
  });
6154
6105
 
6155
- // ../protocol/src/feishu-auth.ts
6156
- var FEISHU_GATED_METHODS, DeviceEntrySchema, DeviceListResponseSchema, DeviceConnectArgsSchema, DeviceConnectResponseSchema, FeishuIdentitySchema, AuthLoginStartResponseSchema, AuthGetIdentityResponseSchema, AuthLogoutResponseSchema, AuthLoginDoneEventSchema, AuthLoginFailedEventSchema;
6157
- var init_feishu_auth = __esm({
6158
- "../protocol/src/feishu-auth.ts"() {
6106
+ // ../protocol/src/methods.ts
6107
+ function methodArgFields(method) {
6108
+ let schema = METHOD_DOCS[method].args;
6109
+ if (!schema) return null;
6110
+ for (let i = 0; i < 5 && schema && !schema.shape; i += 1) {
6111
+ schema = schema._def?.schema;
6112
+ }
6113
+ const shape = schema?.shape;
6114
+ if (!shape) return null;
6115
+ return Object.entries(shape).map(([name, def]) => ({ name, required: !def.isOptional() }));
6116
+ }
6117
+ var NO_ARGS, METHOD_DOCS, METHOD_NAMES, RPC_METHOD_STATUSES, RpcMethodStatusSchema, RpcMethodInfoSchema, MetaMethodsResultSchema;
6118
+ var init_methods = __esm({
6119
+ "../protocol/src/methods.ts"() {
6159
6120
  "use strict";
6160
6121
  init_zod();
6161
- FEISHU_GATED_METHODS = [
6162
- // 联系人列表(device:connect 落同一 store;list 读 / remove 删)
6163
- "contact:list",
6164
- "contact:remove",
6165
- // DM / 跨用户通知
6166
- "inbox:list",
6167
- "inbox:markRead",
6168
- "inbox:postMessage",
6169
- // mirror peer sessions(远端会话镜像)
6170
- "peerSession:upsert",
6171
- "peerSession:remove",
6172
- // 设备目录 + 连接(决策 #15:daemon 代理中心 server,需要 owner 的 ttcJwt)
6173
- "device:list",
6174
- "device:connect"
6175
- ];
6176
- DeviceEntrySchema = external_exports.object({
6177
- /**
6178
- * 设备标识(决策 #15:daemon 本地生成的 dev-<uuid>,与人的身份 unionId 无关)。
6179
- * 中心 server clawd_devices 注册表仅作服务发现(url 下发 + 目录),不在安全路径上。
6180
- */
6181
- deviceId: external_exports.string().min(1),
6182
- /** 显示名 */
6122
+ init_attachment_schemas();
6123
+ init_contact_handshake();
6124
+ init_contact_remote_access();
6125
+ init_contact();
6126
+ init_feishu_auth();
6127
+ init_dispatch();
6128
+ init_extension();
6129
+ init_inbox();
6130
+ init_peer_exec();
6131
+ init_shift_internal();
6132
+ init_persona_schemas();
6133
+ init_schemas();
6134
+ NO_ARGS = external_exports.object({});
6135
+ METHOD_DOCS = {
6136
+ "session:create": {
6137
+ summary: "\u65B0\u5EFA\u4F1A\u8BDD\u5E76\u62C9\u8D77 tool \u8FDB\u7A0B\uFF08claude / codex\uFF09\u3002**cwd \u4E0E ownerPersonaId \u81F3\u5C11\u4F20\u4E00\u4E2A**\uFF08\u5E26 ownerPersonaId \u65F6 cwd \u7531 daemon \u6D3E\u751F\uFF09\u2014\u2014\u8FD9\u6761\u4E92\u65A5\u7EA6\u675F\u662F schema \u7684 refine\uFF0C\u53CD\u5C04\u4E0D\u51FA\u6765",
6138
+ args: SessionCreateArgs
6139
+ },
6140
+ "session:list": {
6141
+ summary: "\u5217\u51FA\u5F53\u524D\u8EAB\u4EFD\u53EF\u89C1\u7684\u4F1A\u8BDD\uFF08owner \u5168\u91CF\uFF1Bguest \u9650\u88AB\u6388\u6743 persona\uFF09",
6142
+ args: SessionListArgs
6143
+ },
6144
+ "session:get": {
6145
+ summary: "\u53D6\u5355\u4E2A\u4F1A\u8BDD\u7684\u5B8C\u6574 SessionFile\uFF08\u5143\u6570\u636E + \u72B6\u6001\uFF09",
6146
+ args: SessionIdArgs
6147
+ },
6148
+ "session:update": {
6149
+ summary: "\u6539\u4F1A\u8BDD\u5143\u6570\u636E\uFF0C\u4E0D\u5F71\u54CD\u8FDB\u7A0B\u3002patch \u5141\u8BB8\u7684\u952E\uFF1Alabel / model / permissionMode / effort / cwd / iconKey\u2014\u2014**\u4F20\u522B\u7684\u952E\u4F1A\u88AB\u9759\u9ED8\u4E22\u5F03**\uFF08zod strip\uFF09\uFF0Chandler \u7167\u6837\u8FD4 ok",
6150
+ args: SessionUpdateArgs
6151
+ },
6152
+ "session:delete": {
6153
+ summary: "\u5220\u4F1A\u8BDD\uFF1A\u505C\u8FDB\u7A0B + \u5220 SessionFile\uFF0C\u5E76\u8054\u52A8\u6E05\u7406\u7ED1\u5B9A\u7684 app-builder project",
6154
+ args: SessionIdArgs
6155
+ },
6156
+ "session:send": {
6157
+ summary: "\u5411\u4F1A\u8BDD\u53D1\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF08\u89E6\u53D1\u65B0\u4E00\u8F6E turn\uFF09",
6158
+ args: SessionSendArgs
6159
+ },
6160
+ "session:currentTurnSender": {
6161
+ summary: "\u67E5\u5F53\u524D turn \u7684\u9A71\u52A8\u8005\uFF08owner / guest \u7684 SenderInfo\uFF09\u3002\u7701\u7565 sessionId \u6216\u4F20\u7A7A\u4E32 \u2192 \u76F4\u63A5\u8FD4 sender:null\uFF0C\u4E0D\u62A5\u9519",
6162
+ args: SessionCurrentTurnSenderArgs
6163
+ },
6164
+ "session:stop": {
6165
+ summary: "\u505C\u6389\u4F1A\u8BDD\u7684 tool \u8FDB\u7A0B\uFF08\u6740\u8FDB\u7A0B\uFF0C\u4F1A\u8BDD\u672C\u8EAB\u4FDD\u7559\uFF09",
6166
+ args: SessionIdArgs
6167
+ },
6168
+ "session:interrupt": {
6169
+ summary: "\u6E29\u67D4\u4E2D\u65AD\u5F53\u524D\u8F6E\u6B21\uFF0C\u4E0D\u6740\u8FDB\u7A0B\uFF08\u8D70 CC IPC control_request.subtype=interrupt\uFF09",
6170
+ args: SessionInterruptArgs
6171
+ },
6172
+ "session:rewind": {
6173
+ summary: "\u56DE\u6EDA\u5230\u6307\u5B9A user message\uFF0C\u5E76\u64A4\u9500\u5176\u540E CC \u505A\u7684\u6587\u4EF6\u7F16\u8F91",
6174
+ args: SessionRewindArgs
6175
+ },
6176
+ "session:rewind-diff": {
6177
+ summary: "\u56DE\u6EDA\u9884\u89C8\uFF1A\u8BFB ~/.claude/file-history \u5FEB\u7167 + \u78C1\u76D8\u73B0\u72B6\u7B97\u771F\u5B9E diff\uFF0C\u4E0D\u4F9D\u8D56 CC \u8FDB\u7A0B",
6178
+ args: SessionRewindDiffArgs
6179
+ },
6180
+ "session:rewindable-message-ids": {
6181
+ summary: "\u4E00\u6B21\u6027\u62FF\u5230\u300C\u6709\u771F\u5B9E diff\u300D\u7684 user message id \u96C6\u5408\uFF0CUI \u636E\u6B64\u51B3\u5B9A\u54EA\u4E9B\u6D88\u606F\u663E\u793A\u56DE\u6EDA\u6309\u94AE",
6182
+ args: SessionRewindableMessageIdsArgs
6183
+ },
6184
+ "session:fork": {
6185
+ summary: "\u4ECE\u6307\u5B9A message \u5904 fork \u51FA\u65B0 transcript\uFF08\u622A\u65AD + \u91CD\u5199 sessionId/parentUuid\uFF09\uFF0C\u8FD4\u56DE\u65B0 toolSessionId\uFF1B\u4E0D\u81EA\u52A8\u62C9\u8D77\u8FDB\u7A0B",
6186
+ args: SessionForkArgs
6187
+ },
6188
+ "session:new": {
6189
+ summary: "\u5728**\u5DF2\u6709**\u4F1A\u8BDD\u4E0A\u6E05\u7A7A\u4E0A\u4E0B\u6587\u5F00\u65B0\u4E00\u8F6E\uFF0C\u76F8\u5F53\u4E8E CC \u7684 /clear\uFF08\u4E0D\u662F\u5EFA\u65B0\u4F1A\u8BDD\u2014\u2014\u5EFA\u65B0\u4F1A\u8BDD\u7528 session:create\uFF09",
6190
+ args: SessionIdArgs
6191
+ },
6192
+ "session:resume": {
6193
+ summary: "\u628A\u4F1A\u8BDD\u6062\u590D\u5230\u6307\u5B9A\u7684 tool \u4F1A\u8BDD\uFF08CC transcript\uFF09\u4E0A\u7EE7\u7EED",
6194
+ args: SessionResumeArgs
6195
+ },
6196
+ "session:observe": {
6197
+ summary: "\u6302 observer \u76D1\u542C\u8BE5\u4F1A\u8BDD\u7684 CC jsonl\uFF0C\u628A daemon \u4E4B\u5916\u542F\u52A8\u7684 CC \u6D3B\u52A8\u56DE\u704C\u8FDB\u6765\uFF08\u591A\u7AEF\u5171\u4EAB / \u63A5\u7BA1\u5916\u90E8\u4F1A\u8BDD\uFF09\u3002\u4EC5 claude \u652F\u6301",
6198
+ args: SessionObserveArgs
6199
+ },
6200
+ "session:events": {
6201
+ summary: "\u62C9\u53D6\u4F1A\u8BDD\u7684\u5386\u53F2\u4E8B\u4EF6\uFF08\u6309 seq \u589E\u91CF\uFF09\uFF0C\u7528\u4E8E\u65AD\u7EBF\u91CD\u8FDE\u8865\u5E27",
6202
+ args: SessionEventsArgs
6203
+ },
6204
+ "session:subscribe": {
6205
+ summary: "\u8BA2\u9605\u8BE5\u4F1A\u8BDD\u7684 push \u5E27\uFF08\u672C\u8FDE\u63A5\u751F\u6548\uFF0CrefCount \u8BA1\u6570\uFF09\u3002**\u7701\u7565 sessionId = \u4E0D\u5EFA\u7ACB\u4EFB\u4F55\u8BA2\u9605**\uFF0C\u4F46\u7167\u6837\u8FD4 subscribed \u2014\u2014 \u4E0D\u662F\u300C\u8BA2\u9605\u5168\u90E8\u300D",
6206
+ args: SessionSubscribeArgs
6207
+ },
6208
+ "session:unsubscribe": {
6209
+ summary: "\u53D6\u6D88\u8BA2\u9605\u8BE5\u4F1A\u8BDD\u7684 push \u5E27\u3002\u7701\u7565 sessionId \u65F6\u4E0D\u53D6\u6D88\u4EFB\u4F55\u8BA2\u9605\uFF0C\u7167\u6837\u8FD4 ok",
6210
+ args: SessionSubscribeArgs
6211
+ },
6212
+ "session:pin": {
6213
+ summary: "\u7F6E\u9876 / \u53D6\u6D88\u7F6E\u9876\u4F1A\u8BDD",
6214
+ args: SessionPinArgs
6215
+ },
6216
+ "session:markRead": {
6217
+ summary: "\u6E05\u6389\u4F1A\u8BDD\u7684\u5171\u4EAB\u672A\u8BFB\u6807\u8BB0\u5E76\u5E7F\u64AD session:info\uFF1B\u5E42\u7B49",
6218
+ args: SessionIdArgs
6219
+ },
6220
+ "session:reorderPins": {
6221
+ summary: "\u6574\u4F53\u91CD\u5199\u6240\u6709\u7F6E\u9876\u4F1A\u8BDD\u7684\u6392\u5E8F\uFF08sidebar \u62D6\u62FD\u5B8C\u6210\u65F6\u8C03\uFF09",
6222
+ args: SessionReorderPinsArgs
6223
+ },
6224
+ "peerSession:upsert": {
6225
+ summary: "\u5199\u5165 / \u66F4\u65B0\u4E00\u6761 mirror SessionFile \u2014\u2014 \u300C\u6211\u5728\u670B\u53CB daemon \u4E0A\u521B\u5EFA\u7684\u4F1A\u8BDD\u300D\u5728\u672C\u673A\u7684\u526F\u672C\uFF0C\u4EC5\u4F9B sidebar \u8DE8\u8BBE\u5907\u805A\u5408\uFF1Bdaemon \u53EA dumb \u5199\uFF0C\u4E0D\u5E7F\u64AD\u4EFB\u4F55 push \u5E27",
6226
+ args: PeerSessionUpsertArgs
6227
+ },
6228
+ "peerSession:remove": {
6229
+ summary: "\u5220\u9664\u4E00\u6761 mirror SessionFile\uFF08\u540C\u6837\u4E0D\u5E7F\u64AD\uFF09",
6230
+ args: PeerSessionRemoveArgs
6231
+ },
6232
+ "permission:respond": {
6233
+ summary: "\u56DE\u5E94 CC \u7684\u6743\u9650\u8BF7\u6C42\uFF08\u5141\u8BB8 / \u62D2\u7EDD / \u8BB0\u4F4F\u89C4\u5219\uFF09",
6234
+ args: PermissionRespondArgs
6235
+ },
6236
+ "session:answerQuestion": {
6237
+ summary: "\u56DE\u586B AskUserQuestion \u8868\u5355\u7B54\u6848\uFF0Cdaemon \u5408\u5E76\u8FDB updated_input \u5199 control_response \u7ED9 CC",
6238
+ args: AnswerQuestionArgs
6239
+ },
6240
+ "session:cancelQuestion": {
6241
+ summary: "\u7528\u6237\u8DF3\u8FC7 AskUserQuestion\uFF1Adaemon \u56DE allow + \u7A7A\u7B54\u6848\u8BA9 tool \u8D70\u7A7A\u8DEF\u5F84\uFF0C\u5E76\u5E7F\u64AD cleared \u5E27",
6242
+ args: CancelQuestionArgs
6243
+ },
6244
+ "history:projects": {
6245
+ summary: "\u5217\u51FA CC \u5386\u53F2\u91CC\u7684\u5168\u90E8\u9879\u76EE\uFF08\u6309 projectPath \u805A\u5408\uFF09\uFF1Bguest \u53EA\u770B\u5230\u81EA\u5DF1\u88AB\u6388\u6743 persona \u5B50\u6811\u4E0B\u7684",
6246
+ args: NO_ARGS
6247
+ },
6248
+ "history:list": {
6249
+ summary: "\u5217\u51FA\u67D0\u4E2A\u9879\u76EE\u4E0B\u7684\u5386\u53F2\u4F1A\u8BDD",
6250
+ args: HistoryListArgs
6251
+ },
6252
+ "history:read": {
6253
+ summary: "\u8BFB\u4E00\u6761\u5386\u53F2\u4F1A\u8BDD\u7684\u5B8C\u6574\u6D88\u606F\uFF08\u89E3\u6790 CC jsonl\uFF09",
6254
+ args: HistoryReadArgs
6255
+ },
6256
+ "history:subagents": {
6257
+ summary: "\u5217\u51FA\u67D0\u6B21\u4F1A\u8BDD\u91CC\u7684 subagent \u8FD0\u884C\u8BB0\u5F55",
6258
+ args: HistorySubagentsArgs
6259
+ },
6260
+ "history:subagent-read": {
6261
+ summary: "\u8BFB\u4E00\u6761 subagent \u7684\u5B8C\u6574\u8BB0\u5F55",
6262
+ args: HistorySubagentReadArgs
6263
+ },
6264
+ "history:recentDirs": {
6265
+ summary: "\u6700\u8FD1\u7528\u8FC7\u7684\u5DE5\u4F5C\u76EE\u5F55\u5217\u8868\uFF08\u6309 cwd \u53BB\u91CD\u3001\u53D6\u6700\u65B0 updatedAt\uFF09\u3002\u65E0\u53C2\u6570",
6266
+ args: NO_ARGS
6267
+ },
6268
+ "workspace:list": {
6269
+ summary: "\u5217\u76EE\u5F55\u5185\u5BB9\uFF08\u6587\u4EF6\u6D4F\u89C8\uFF09\uFF1Bguest \u88AB\u9650\u5236\u5728\u88AB\u6388\u6743 persona \u5B50\u6811\u5185",
6270
+ args: WorkspaceListArgs
6271
+ },
6272
+ "workspace:read": {
6273
+ summary: "\u8BFB\u6587\u4EF6\u5185\u5BB9\uFF1Bguest \u8DEF\u5F84\u8FB9\u754C\u4E0E workspace:list \u540C",
6274
+ args: WorkspaceReadArgs
6275
+ },
6276
+ "shift:list": {
6277
+ summary: "\u5217\u51FA owner \u7684\u5168\u90E8 shift\uFF08\u6392\u73ED\uFF09\uFF0C\u7ED9 UI \u65E5\u5386\u6E32\u67D3\u7528\uFF1Bowner-only",
6278
+ args: NO_ARGS
6279
+ },
6280
+ "shift-internal:add": {
6281
+ summary: "\u6392\u4E00\u4E2A shift\uFF08\u5230\u70B9\u81EA\u52A8\u62C9\u8D77 persona \u5E72\u6D3B\uFF09\u3002\u7701\u7565 targetPersona = \u6D3E\u7ED9\u8C03\u7528\u65B9\u81EA\u5DF1",
6282
+ args: ShiftInternalAddArgs
6283
+ },
6284
+ "shift-internal:list": {
6285
+ summary: "\u5217 shift\u3002\u7701\u7565 onlyMine \u9ED8\u8BA4 true \u2014\u2014 \u53EA\u5217\u672C persona \u5EFA\u7684",
6286
+ args: ShiftInternalListArgs
6287
+ },
6288
+ "shift-internal:cancel": {
6289
+ summary: "\u53D6\u6D88 shift\uFF08\u4EC5\u521B\u5EFA\u5B83\u7684 persona \u53EF\u64CD\u4F5C\uFF09",
6290
+ args: ShiftInternalCancelArgs
6291
+ },
6292
+ "shift-internal:update": {
6293
+ summary: "\u4FEE\u6539 shift \u5B57\u6BB5\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09\u3002patch \u5141\u8BB8\u7684\u952E\uFF1Aname / schedule / prompt / targetPersona / timeoutMs / enabled\uFF1Bschedule \u53D8\u4E86 store \u81EA\u52A8\u91CD\u7B97 nextRunAtMs",
6294
+ args: ShiftInternalUpdateArgs
6295
+ },
6296
+ "shift-internal:history": {
6297
+ summary: "\u67E5 shift \u7684\u6267\u884C\u5386\u53F2\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09",
6298
+ args: ShiftInternalHistoryArgs
6299
+ },
6300
+ "skills:list": {
6301
+ summary: "\u626B\u63CF\u5E76\u5217\u51FA\u8BE5\u76EE\u5F55\u53EF\u7528\u7684 skill",
6302
+ args: SkillsListArgs
6303
+ },
6304
+ "agents:list": {
6305
+ summary: "\u626B\u63CF\u5E76\u5217\u51FA\u8BE5\u76EE\u5F55\u53EF\u7528\u7684 subagent \u5B9A\u4E49",
6306
+ args: AgentsListArgs
6307
+ },
6308
+ "git:root": {
6309
+ summary: "\u53D6\u8BE5\u8DEF\u5F84\u6240\u5C5E git \u4ED3\u5E93\u7684\u6839\u76EE\u5F55\uFF08\u4E0D\u5728\u4ED3\u5E93\u91CC\u8FD4 null\uFF09",
6310
+ args: GitRootArgs
6311
+ },
6312
+ "git:branch": {
6313
+ summary: "\u53D6\u5F53\u524D\u5206\u652F\u540D",
6314
+ args: GitBranchArgs
6315
+ },
6316
+ "git:branches": {
6317
+ summary: "\u5217\u51FA\u672C\u5730\u5206\u652F",
6318
+ args: GitBranchesArgs
6319
+ },
6320
+ "capabilities:get": {
6321
+ summary: "\u53D6\u67D0\u4E2A tool \u7684\u80FD\u529B\u96C6\uFF1A\u53EF\u7528\u6A21\u578B\u5217\u8868 / permissionModes / configSchema",
6322
+ args: CapabilitiesGetArgs
6323
+ },
6324
+ "persona:create": {
6325
+ summary: "\u65B0\u5EFA persona\uFF08\u5199 PersonaFile + CLAUDE.md \u9AA8\u67B6\uFF09\u3002owner-only",
6326
+ args: PersonaCreateArgsSchema
6327
+ },
6328
+ "persona:list": {
6329
+ summary: "\u5217\u51FA persona \u5143\u6570\u636E\uFF08\u4E0D\u542B CLAUDE.md \u6B63\u6587\uFF09\u3002owner \u8FD4\u5168\u91CF\u542B private\uFF1Bguest \u53EA\u8FD4 public \u4E14\u88AB\u6388\u6743\u7684",
6330
+ args: NO_ARGS
6331
+ },
6332
+ "persona:get": {
6333
+ summary: "\u53D6\u5355\u4E2A persona \u8BE6\u60C5\uFF0C**\u542B personality\uFF08CLAUDE.md \u6B63\u6587\uFF09**\u2014\u2014\u552F\u4E00\u643A\u5E26\u6B63\u6587\u7684 method",
6334
+ args: PersonaIdArgsSchema
6335
+ },
6336
+ "persona:update": {
6337
+ summary: "\u6539 persona\uFF08label / model / public / CLAUDE.md \u7B49\uFF09\u3002owner-only",
6338
+ args: PersonaUpdateArgsSchema
6339
+ },
6340
+ "persona:delete": {
6341
+ summary: "\u5220 persona\u3002owner-only",
6342
+ args: PersonaIdArgsSchema
6343
+ },
6344
+ "personaDispatch:run": {
6345
+ summary: "\u628A\u4EFB\u52A1\u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08\u53EF\u8DE8\u8BBE\u5907\uFF09\uFF0C\u5F02\u6B65\u79D2\u56DE { dispatchId }\u3002targetPersona \u4E0E dispatchId **\u81F3\u5C11\u7ED9\u4E00\u4E2A**\uFF08\u524D\u8005\u65B0\u5F00\u3001\u540E\u8005\u7EED\u6D3E\uFF09\uFF1BdispatchId \u4E0E targetDeviceId **\u4E92\u65A5**",
6346
+ args: DispatchRunArgsSchema
6347
+ },
6348
+ "personaDispatch:complete": {
6349
+ summary: '\u88AB\u59D4\u6D3E\u65B9\u56DE\u62A5\u7ED3\u679C\uFF1A\u5199\u53F0\u8D26\u5E76\u628A\u7ED3\u679C\u6295\u9012\u56DE\u53D1\u8D77\u65B9\u4F1A\u8BDD\u3002outcome \u662F { kind:"success", text, filePaths? } \u6216 { kind:"failure", reason } \u4E8C\u9009\u4E00',
6350
+ args: DispatchCompleteArgsSchema
6351
+ },
6352
+ "personaDispatch:list": {
6353
+ summary: "\u67E5 dispatch \u53F0\u8D26\uFF08\u72B6\u6001 / \u7ED3\u679C / \u65F6\u95F4\u6233\uFF09\u3002\u7701\u7565 sourceSessionId = \u8FD4\u5168\u91CF",
6354
+ args: DispatchListArgsSchema
6355
+ },
6356
+ "personaDispatch:get": {
6357
+ summary: "\u67E5\u5355\u6761 dispatch \u53F0\u8D26\u7EC8\u6001\uFF1B\u8DE8\u8BBE\u5907\u65F6 A \u7AEF\u51ED connect token \u8F6E\u8BE2 B \u7AEF\u7528",
6358
+ args: DispatchGetArgsSchema
6359
+ },
6360
+ "peerExec:run": {
6361
+ summary: "\u5728\u8054\u7CFB\u4EBA\u8BBE\u5907\u4E0A\u8DD1\u4E00\u6761 bash \u547D\u4EE4\uFF08\u4E00\u53D1\u4E00\u6536\uFF0C\u65E0\u8DE8\u547D\u4EE4\u4F1A\u8BDD\u72B6\u6001\uFF09\u3002**\u672C\u673A\u53D1\u8D77\u5FC5\u586B peerDeviceId**\u2014\u2014\u7701\u7565\u4EC5\u7528\u4E8E\u5BF9\u7AEF guest \u53CD\u5411\u5165\u53E3\uFF0Cowner ctx \u7701\u7565\u4F1A\u88AB\u62D2\uFF1B\u9700\u5BF9\u65B9\u5F00\u4E86 remoteAccess",
6362
+ args: PeerExecRunArgsSchema
6363
+ },
6364
+ "session:pty:input": {
6365
+ summary: "\u628A\u7528\u6237\u952E\u76D8\u5B57\u8282\u5199\u8FDB pty stdin\uFF08\u4EC5 CLAWD_CC_MODE=tui\uFF09\u3002\u9AD8\u9891\u5E27\uFF0C\u4E0D\u5165 reducer\u3002body: { sessionId, data\uFF08base64-utf8\uFF09 }"
6366
+ },
6367
+ "session:pty:resize": {
6368
+ summary: "\u540C\u6B65\u7EC8\u7AEF\u5C3A\u5BF8\u7ED9 pty\uFF08\u4EC5 tui \u6A21\u5F0F\uFF09\u3002body: { sessionId, cols, rows }"
6369
+ },
6370
+ "session:pty:snapshot": {
6371
+ summary: "\u62C9\u5F53\u524D pty \u5C4F\u5E55\u5168\u91CF\u5FEB\u7167\uFF08\u5207\u56DE\u4F1A\u8BDD / \u5237\u65B0\u65F6\u7528\uFF09\uFF0C\u8FD4\u56DE { data, atSeq, popupKind }\uFF1B\u975E tui \u6216\u5DF2\u9000\u51FA\u65F6 data=null\u3002body: { sessionId }"
6372
+ },
6373
+ "attachment.signUrl": {
6374
+ summary: "\u7ED9\u5DF2\u5165\u7FA4\u7684\u6587\u4EF6\u7B7E\u53D1 HMAC \u4E0B\u8F7D URL\uFF08\u8DE8\u8BBE\u5907\u53EF\u76F4\u63A5\u6253\u5F00\uFF09",
6375
+ args: AttachmentSignUrlArgs
6376
+ },
6377
+ "attachment.groupAdd": {
6378
+ summary: "\u628A\u6587\u4EF6\u767B\u8BB0\u8FDB\u672C\u4F1A\u8BDD\u7684\u5171\u4EAB\u6E05\u5355\uFF08\u7B7E URL \u524D\u5FC5\u987B\u5148\u5165\u7FA4\uFF09",
6379
+ args: AttachmentGroupAddArgs
6380
+ },
6381
+ "attachment.groupRemove": {
6382
+ summary: "\u628A\u6587\u4EF6\u79FB\u51FA\u5171\u4EAB\u6E05\u5355",
6383
+ args: AttachmentGroupRemoveArgs
6384
+ },
6385
+ "attachment.groupList": {
6386
+ summary: "\u5217\u51FA\u672C\u4F1A\u8BDD\u5171\u4EAB\u6E05\u5355\u91CC\u7684\u6587\u4EF6",
6387
+ args: AttachmentGroupListArgs
6388
+ },
6389
+ "inbox:list": {
6390
+ summary: "\u62C9\u67D0\u4E2A peer \u7684 DM / \u901A\u77E5\u961F\u5217",
6391
+ args: InboxListArgsSchema
6392
+ },
6393
+ "inbox:markRead": {
6394
+ summary: "\u628A\u67D0 peer \u961F\u5217\u6807\u8BB0\u4E3A\u5DF2\u8BFB\u5230\u67D0\u65F6\u95F4\u70B9",
6395
+ args: InboxMarkReadArgsSchema
6396
+ },
6397
+ "inbox:postMessage": {
6398
+ summary: "\u6295\u9012\u4E00\u6761 P2P IM \u6D88\u606F\uFF08\u53CC\u6295\uFF1A\u672C\u673A\u843D\u5E93 + \u8F6C\u53D1\u5BF9\u7AEF\uFF09\u3002**owner \u8C03\u7528\u5FC5\u586B peerDeviceId**\uFF0C\u4EC5 guest \u53EF\u7701\uFF08\u4ECE ctx \u63A8\uFF09",
6399
+ args: InboxPostMessageArgsSchema
6400
+ },
6401
+ "inbox:sendDm": {
6402
+ summary: "\u4EE5\u5F53\u524D\u4F1A\u8BDD\u7684\u8EAB\u4EFD\u53D1\u4E00\u6761 DM\uFF08\u53D1\u4FE1\u4EBA\u7531 session scope \u5F52\u56E0\uFF0C\u4E0D\u53EF\u4F2A\u9020\uFF09\u3002**owner \u9A71\u52A8\u7684\u4F1A\u8BDD\u5FC5\u586B peerDeviceId**\uFF0Cguest \u9A71\u52A8\u7684\u53EF\u7701",
6403
+ args: InboxSendDmArgsSchema
6404
+ },
6405
+ "contact:list": {
6406
+ summary: "\u5217\u51FA\u8054\u7CFB\u4EBA\uFF08\u5DF2\u5EFA\u8054\u7684\u8BBE\u5907\uFF09\u3002owner-only",
6407
+ args: NO_ARGS
6408
+ },
6409
+ "contact:pin": {
6410
+ summary: "\u7F6E\u9876 / \u53D6\u6D88\u7F6E\u9876\u8054\u7CFB\u4EBA",
6411
+ args: ContactPinArgsSchema
6412
+ },
6413
+ "contact:remove": {
6414
+ summary: "\u5220\u9664\u8054\u7CFB\u4EBA\u5E76\u6E05\u6389\u5BF9\u5E94\u7684 inbox \u961F\u5217",
6415
+ args: ContactRemoveArgsSchema
6416
+ },
6417
+ "contact:setRemoteAccess": {
6418
+ summary: "\u5F00\u5173\u67D0\u8054\u7CFB\u4EBA\u7684\u8FDC\u7A0B\u8BBF\u95EE\u6388\u6743\uFF08\u63A7\u5236\u5B83\u80FD\u5426\u53CD\u5411 peerExec \u6211\u7684\u8BBE\u5907\uFF09",
6419
+ args: ContactSetRemoteAccessArgsSchema
6420
+ },
6421
+ "contact:handshake": {
6422
+ summary: "\u53CC\u5411\u786E\u8BA4\u5EFA\u8054\u63E1\u624B\uFF1A\u8FDE\u63A5\u65B9\u81EA\u62A5 selfUrl\uFF0C\u88AB\u8FDE\u65B9\u843D\u5E93 + \u5E7F\u64AD\u540E\u56DE\u8EAB\u4EFD\u8F7D\u8377\u3002\u7531 device:connect \u7684\u4E34\u65F6\u8FDE\u63A5\u8C03\u7528",
6423
+ args: ContactHandshakeArgsSchema
6424
+ },
6425
+ "visitor:list": {
6426
+ summary: "\u5217\u51FA\u767B\u5F55\u8BBF\u95EE\u8FC7\u672C\u673A public persona \u7684 web \u8BBF\u5BA2\u3002owner-only",
6427
+ args: NO_ARGS
6428
+ },
6429
+ "whoami": {
6430
+ summary: "\u8FD4\u56DE\u672C\u8FDE\u63A5\u7684\u8EAB\u4EFD\uFF1Aowner \u663E\u793A\u540D + capability wire \u5F62\u6001 + \u88AB\u6388\u6743\u7684 persona \u5217\u8868\u3002\u4EFB\u4F55 authed \u8FDE\u63A5\u53EF\u8C03",
6431
+ args: NO_ARGS
6432
+ },
6433
+ "info": {
6434
+ summary: "daemon \u8FD0\u884C\u65F6\u4FE1\u606F\uFF1A\u7248\u672C / \u534F\u8BAE\u7248\u672C / \u4E3B\u673A / \u53EF\u7528 tool / \u5728\u8DD1\u7684\u4F1A\u8BDD / tunnel URL \u7B49",
6435
+ args: NO_ARGS
6436
+ },
6437
+ "ping": {
6438
+ summary: "\u5B58\u6D3B\u63A2\u6D4B\uFF0C\u56DE pong",
6439
+ args: NO_ARGS
6440
+ },
6441
+ "meta:methods": {
6442
+ summary: "RPC \u81EA\u7701\uFF1A\u8FD4\u56DE METHOD_NAMES \u5168\u96C6 + **\u5BF9\u672C\u8FDE\u63A5**\u7684\u53EF\u8C03\u7528\u6027\u4E09\u6001\uFF08allowed / conditional / denied\uFF09+ \u6BCF\u6761\u7684\u8BF4\u660E\u3002agent \u4FA7 clawd-rpc \u7684 list tool \u662F\u552F\u4E00\u6D88\u8D39\u65B9",
6443
+ args: NO_ARGS
6444
+ },
6445
+ "extension.list": {
6446
+ summary: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 extension",
6447
+ args: NO_ARGS
6448
+ },
6449
+ "extension.open": {
6450
+ summary: "\u62C9\u8D77\u67D0\u4E2A extension\uFF08local-spawn \u8D77\u8FDB\u7A0B\u5E76\u7B49 healthz\uFF09",
6451
+ args: ExtensionIdArgs
6452
+ },
6453
+ "extension.close": {
6454
+ summary: "\u5173\u6389\u67D0\u4E2A\u6B63\u5728\u8DD1\u7684 extension",
6455
+ args: ExtensionIdArgs
6456
+ },
6457
+ "extension.uninstall": {
6458
+ summary: "\u5378\u8F7D extension\uFF08\u5220\u5B89\u88C5\u76EE\u5F55\uFF09",
6459
+ args: ExtensionIdArgs
6460
+ },
6461
+ "extension.publish": {
6462
+ summary: "\u628A\u672C\u673A extension \u516C\u5F00\u5230\u5206\u4EAB\u9891\u9053\uFF0C\u4F9B\u8054\u7CFB\u4EBA\u62C9\u53D6\u3002owner-only",
6463
+ args: ExtensionPublishArgs
6464
+ },
6465
+ "extension.unpublish": {
6466
+ summary: "\u64A4\u56DE\u5DF2\u516C\u5F00\u7684 extension\u3002owner-only",
6467
+ args: ExtensionIdArgs
6468
+ },
6469
+ "extension.publish:status": {
6470
+ summary: "\u67E5\u67D0\u4E2A extension \u7684\u516C\u5F00\u72B6\u6001\u3002owner-only",
6471
+ args: ExtensionIdArgs
6472
+ },
6473
+ "extension.list-published": {
6474
+ summary: "\uFF08\u8DE8\u8BBE\u5907\uFF09\u4ECE\u5BF9\u7AEF owner \u62C9\u5B83\u516C\u5F00\u7684 extension \u6E05\u5355",
6475
+ args: NO_ARGS
6476
+ },
6477
+ "extension.fetchBundle": {
6478
+ summary: "\uFF08\u8DE8\u8BBE\u5907\uFF09\u4ECE\u5BF9\u7AEF owner \u4E0B\u8F7D extension bundle \u5B57\u8282\u3002extId \u5FC5\u4F20\u4F46\u4E0D\u53C2\u4E0E\u7D22\u5F15\u2014\u2014\u771F\u6B63\u7684\u952E\u662F snapshotHash\uFF08publish \u90A3\u523B\u51BB\u7ED3\u7684\u90A3\u4EFD\uFF0C\u4E0E owner \u5DE5\u4F5C\u76EE\u5F55\u540E\u7EED\u53D8\u5316\u89E3\u8026\uFF09",
6479
+ args: ExtensionFetchBundleArgs
6480
+ },
6481
+ "extension.install-from-channel": {
6482
+ summary: "\u5728\u81EA\u5DF1 daemon \u4E0A\u4ECE\u5206\u4EAB\u9891\u9053\u5B89\u88C5 extension",
6483
+ args: ExtensionFromChannelArgs
6484
+ },
6485
+ "extension.update-from-channel": {
6486
+ summary: "\u5728\u81EA\u5DF1 daemon \u4E0A\u628A\u5DF2\u88C5 extension \u66F4\u65B0\u5230\u9891\u9053\u6700\u65B0\u7248",
6487
+ args: ExtensionFromChannelArgs
6488
+ },
6489
+ "auth:login:start": {
6490
+ summary: "\u8D77\u98DE\u4E66\u767B\u5F55\u6D41\u7A0B\uFF1A\u751F\u6210 state \u5E76\u8FD4\u56DE\u6388\u6743 URL\uFF0C\u56DE\u8C03\u8D70 HTTP /auth/callback \u843D\u76D8\u3002owner-only",
6491
+ args: NO_ARGS
6492
+ },
6493
+ "auth:getIdentity": {
6494
+ summary: "\u53D6\u672C\u673A owner \u7684\u98DE\u4E66\u8EAB\u4EFD\uFF08ownerId / provider / displayName / unionId\uFF09+ **\u672C\u673A deviceId**\uFF08\u8BBE\u5907\u5C5E\u6027\uFF0C\u4E0E\u767B\u5F55\u6001\u65E0\u5173\uFF1Bdevice:connect / contact:* / inbox:* \u7684\u4E3B\u952E\u90FD\u662F\u5B83\uFF09\uFF1B\u7EDD\u4E0D\u8FD4\u56DE ttcToken",
6495
+ args: NO_ARGS
6496
+ },
6497
+ "auth:logout": {
6498
+ summary: "\u767B\u51FA\u98DE\u4E66\u8EAB\u4EFD\uFF08\u6E05 owner-identity \u843D\u76D8\uFF09\u3002owner-only",
6499
+ args: NO_ARGS
6500
+ },
6501
+ "device:list": {
6502
+ summary: "\u5217\u51FA\u516C\u5171\u8BBE\u5907\u76EE\u5F55\u91CC\u53EF\u8FDE\u63A5\u7684\u8BBE\u5907\uFF08daemon \u4EE3\u7406\u4E2D\u5FC3 server\uFF09\u3002owner-only",
6503
+ args: NO_ARGS
6504
+ },
6505
+ "device:connect": {
6506
+ summary: "\u8FDE\u63A5\u76EE\u6807\u8BBE\u5907\uFF1A\u6362 aud \u7ED1\u5B9A\u7684\u7B7E\u540D token \u5E76\u8D70 contact:handshake \u5EFA\u8054\u3002owner-only",
6507
+ args: DeviceConnectArgsSchema
6508
+ },
6509
+ "appBuilder:listProjects": {
6510
+ summary: "\u5217\u51FA app-builder project\uFF08\u542B dev server \u72B6\u6001\uFF09",
6511
+ args: ListProjectsArgsSchema
6512
+ },
6513
+ "appBuilder:getProject": {
6514
+ summary: "\u6309 sessionId \u53D6\u5355\u4E2A\u7ED1\u5B9A\u7684 project\uFF08\u542B status\uFF09",
6515
+ args: GetProjectArgsSchema
6516
+ },
6517
+ "appBuilder:createProject": {
6518
+ summary: "\u7ED9\u4F1A\u8BDD\u7ED1\u5B9A\u65B0 project\uFF1A\u6821\u9A8C\u540D + \u4ECE 6173-6182 \u6BB5\u5206\u914D\u7AEF\u53E3 + \u5EFA\u76EE\u5F55\u5199\u5143\u6570\u636E",
6519
+ args: CreateProjectArgsSchema
6520
+ },
6521
+ "appBuilder:deleteProject": {
6522
+ summary: "\u505C dev server + \u5220 project \u76EE\u5F55\uFF08\u7ED1\u5B9A\u7684\u4F1A\u8BDD\u4E0D\u5728\u8FD9\u91CC\u5220\uFF09",
6523
+ args: DeleteProjectArgsSchema
6524
+ },
6525
+ "appBuilder:updateProjectPort": {
6526
+ summary: "\u6539 dev server \u7AEF\u53E3\uFF08\u6821\u9A8C\u6BB5\u5185 + \u4E0D\u649E\u8F66\uFF09\u5E76\u91CD\u542F",
6527
+ args: UpdateProjectPortArgsSchema
6528
+ },
6529
+ "appBuilder:setProdUrl": {
6530
+ summary: "\u53D1\u5E03\u4E0A\u7EBF\u540E\u56DE\u586B\u7EBF\u4E0A\u5730\u5740\u5230 .clawd-project.json",
6531
+ args: SetProdUrlArgsSchema
6532
+ },
6533
+ "appBuilder:startDevServer": {
6534
+ summary: "scaffold + \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210\u540E\u663E\u5F0F\u62C9\u8D77 dev server\uFF08createProject \u4E0D\u518D\u81EA\u52A8\u8D77\uFF09",
6535
+ args: StartDevServerArgsSchema
6536
+ },
6537
+ "appBuilder:reportStage": {
6538
+ summary: "\u4E0A\u62A5\u811A\u624B\u67B6\u9636\u6BB5\uFF08scaffolding / installing / failed \u7B49\uFF09\uFF0Cdaemon \u5E7F\u64AD\u5E27\u8BA9 UI \u5207 PreviewPane",
6539
+ args: ReportStageArgsSchema
6540
+ },
6541
+ "appBuilder:stopDevServer": {
6542
+ summary: "\u6536\u8D77\u9884\u89C8\uFF1A\u505C\u6389 dev server\uFF08\u5BF9\u79F0 startDevServer\uFF09",
6543
+ args: StopDevServerArgsSchema
6544
+ },
6545
+ "appBuilder:publish": {
6546
+ summary: "\u53D1\u5E03\u4E0A\u7EBF\uFF1A\u4E32\u884C\u5316 spawn publish.sh \u5E76\u5E7F\u64AD\u9636\u6BB5\u8FDB\u5EA6\u5E27\uFF1B\u540C project \u5DF2\u6709 in-flight job \u65F6\u8FD4\u56DE already-publishing",
6547
+ args: PublishArgsSchema
6548
+ },
6549
+ "appBuilder:dismissPublishJob": {
6550
+ summary: "\u53D6\u6D88 in-flight publish\uFF08SIGTERM \u5B50\u8FDB\u7A0B\uFF09\u6216\u6E05\u6389 daemon \u5D29\u6E83\u7559\u4E0B\u7684\u6B8B\u9AB8 publishJob \u5B57\u6BB5",
6551
+ args: DismissPublishJobArgsSchema
6552
+ }
6553
+ };
6554
+ METHOD_NAMES = Object.keys(METHOD_DOCS);
6555
+ RPC_METHOD_STATUSES = ["allowed", "conditional", "denied"];
6556
+ RpcMethodStatusSchema = external_exports.enum(RPC_METHOD_STATUSES);
6557
+ RpcMethodInfoSchema = external_exports.object({
6558
+ /** canonical method 名,直接可作为 clawd-rpc call 的 method 参数 */
6183
6559
  name: external_exports.string().min(1),
6184
- /** ws/tunnel 地址 */
6185
- url: external_exports.string().min(1)
6186
- });
6187
- DeviceListResponseSchema = external_exports.object({
6188
- type: external_exports.literal("device:list:ok"),
6189
- devices: external_exports.array(DeviceEntrySchema)
6190
- });
6191
- DeviceConnectArgsSchema = external_exports.object({
6192
- /** 目标设备的 deviceId(对方 daemon 生成的 dev-<uuid>) */
6193
- deviceId: external_exports.string().min(1),
6560
+ status: RpcMethodStatusSchema,
6561
+ /** 该 method 干什么,取自 {@link METHOD_DOCS} 的 summary */
6562
+ description: external_exports.string().min(1),
6194
6563
  /**
6195
- * 目标设备的 ws/tunnel 地址(可选):
6196
- * - 公开目录点击:带 url(device:list 已下发,省一次 server 查询)
6197
- * - 凭 deviceId 连接:缺省 url → daemon exchange 时由中心 server 下发该设备上报的 url
6198
- * - 注册表不在安全路径上:知道对方 url 时显式传入即可连接(不依赖中心 server 服务发现)
6564
+ * 参数字段,从 {@link METHOD_DOCS} 的 args schema **反射**得来(不手写)。
6565
+ * `null` = method 还没有中央 zod schema(欠账,见 RpcMethodDoc),调用方需自行试探。
6199
6566
  */
6200
- url: external_exports.string().min(1).optional(),
6201
- /** 显示名(公共设备来自目录;缺省时连上后用 whoami 回填) */
6202
- name: external_exports.string().optional()
6203
- });
6204
- DeviceConnectResponseSchema = external_exports.object({
6205
- type: external_exports.literal("device:connect:ok"),
6206
- /** 连接的目标设备标识(= 请求 args.deviceId) */
6207
- deviceId: external_exports.string(),
6208
- name: external_exports.string(),
6209
- url: external_exports.string()
6210
- });
6211
- FeishuIdentitySchema = external_exports.object({
6212
- /** 归属人 id(决策 #16:TTC unique_id,取代飞书 union_id;跨登录方式稳定) */
6213
- ownerId: external_exports.string().min(1),
6214
- /** 身份来源:'ttc'(飞书/手机/邮箱登入 TTC)| 'google' | ...(决策 #16,拓展用) */
6215
- provider: external_exports.string().min(1),
6216
- /** 显示名。TTC user_info 接口 data.name */
6217
- displayName: external_exports.string().min(1),
6218
- /** TTC user_info 接口 data.avatar_url,可缺省 */
6219
- avatarUrl: external_exports.string().optional(),
6220
- /** TTC user_info 接口 data.union_id(飞书 union_id;dataclaw 可信入口 X-User-UnionId 用),可缺省 */
6221
- unionId: external_exports.string().optional()
6222
- });
6223
- AuthLoginStartResponseSchema = external_exports.object({
6224
- type: external_exports.literal("auth:login:start:ok"),
6225
- /** 完整 TTC 授权页 URL(含 callback_url + state + auto=1),UI 直接 window.open */
6226
- authUrl: external_exports.string().min(1),
6227
- /** 防 CSRF 的一次性 nonce;回调时 daemon 校验 */
6228
- state: external_exports.string().min(1)
6229
- });
6230
- AuthGetIdentityResponseSchema = external_exports.object({
6231
- type: external_exports.literal("auth:getIdentity:ok"),
6232
- /** null = 未登录 */
6233
- identity: FeishuIdentitySchema.nullable(),
6567
+ args: external_exports.array(external_exports.object({ name: external_exports.string(), required: external_exports.boolean() })).nullable(),
6234
6568
  /**
6235
- * 本机设备标识(决策 #15:daemon 生成并持久化在 auth.json,如 dev-<uuid>)。
6236
- * 设备属性,与登录态无关、登录登出不变。UI"我的设备标识"用它拼
6237
- * clawos://device/<deviceId>(替代旧的 unionId——不再向外暴露人的身份标识)。
6569
+ * `denied` 必带:daemon error code(`UNAUTHORIZED` / `FEISHU_LOGIN_REQUIRED` /
6570
+ * `METHOD_NOT_IMPLEMENTED`)。`conditional` 必带:谁来做二次校验。
6571
+ * `allowed` 恒无。
6238
6572
  */
6239
- deviceId: external_exports.string().min(1)
6240
- });
6241
- AuthLogoutResponseSchema = external_exports.object({
6242
- type: external_exports.literal("auth:logout:ok")
6243
- });
6244
- AuthLoginDoneEventSchema = external_exports.object({
6245
- type: external_exports.literal("auth:login:done"),
6246
- identity: FeishuIdentitySchema
6573
+ reason: external_exports.string().optional()
6247
6574
  });
6248
- AuthLoginFailedEventSchema = external_exports.object({
6249
- type: external_exports.literal("auth:login:failed"),
6250
- reason: external_exports.string()
6575
+ MetaMethodsResultSchema = external_exports.object({
6576
+ type: external_exports.literal("meta:methods"),
6577
+ /** METHOD_NAMES 全集,顺序与之一致 */
6578
+ methods: external_exports.array(RpcMethodInfoSchema)
6251
6579
  });
6252
6580
  }
6253
6581
  });
6254
6582
 
6255
- // ../protocol/src/dispatch.ts
6256
- var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema, DISPATCH_STATUSES, DispatchStatusSchema, DispatchRecordSchema, DispatchListArgsSchema, DispatchListResultSchema, DispatchGetArgsSchema, DispatchGetOkSchema;
6257
- var init_dispatch = __esm({
6258
- "../protocol/src/dispatch.ts"() {
6583
+ // ../protocol/src/errors.ts
6584
+ var ERROR_CODES, ClawdError;
6585
+ var init_errors2 = __esm({
6586
+ "../protocol/src/errors.ts"() {
6587
+ "use strict";
6588
+ ERROR_CODES = {
6589
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
6590
+ SESSION_BUSY: "SESSION_BUSY",
6591
+ INVALID_CWD: "INVALID_CWD",
6592
+ INVALID_PATH: "INVALID_PATH",
6593
+ TOOL_NOT_SUPPORTED: "TOOL_NOT_SUPPORTED",
6594
+ CLAUDE_NOT_FOUND: "CLAUDE_NOT_FOUND",
6595
+ SPAWN_FAILED: "SPAWN_FAILED",
6596
+ INVALID_PERMISSION_MODE: "INVALID_PERMISSION_MODE",
6597
+ METHOD_NOT_IMPLEMENTED: "METHOD_NOT_IMPLEMENTED",
6598
+ METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
6599
+ VALIDATION_ERROR: "VALIDATION_ERROR",
6600
+ PERMISSION_REQUEST_STALE: "PERMISSION_REQUEST_STALE",
6601
+ FILE_TOO_LARGE: "FILE_TOO_LARGE",
6602
+ INTERNAL: "INTERNAL",
6603
+ UNAUTHORIZED: "UNAUTHORIZED",
6604
+ FORBIDDEN: "FORBIDDEN",
6605
+ INVALID_PARAM: "INVALID_PARAM",
6606
+ // 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
6607
+ // daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
6608
+ // UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
6609
+ FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED",
6610
+ // app-builder 单栏默认 refactor (spec 2026-06-02-app-builder-single-pane-default-design):
6611
+ // appBuilder:createProject 加 personaId + session 未绑校验后抛的两个错码。
6612
+ WRONG_PERSONA: "WRONG_PERSONA",
6613
+ SESSION_ALREADY_BOUND: "SESSION_ALREADY_BOUND",
6614
+ // 2026-07-02 unified people sidebar: contact:pin 时 deviceId 不在 store(可能被别处删了)
6615
+ CONTACT_NOT_FOUND: "CONTACT_NOT_FOUND",
6616
+ // 统一 RPC 鉴权(spec 2026-07-28):personaDispatch:get 查不到台账行,或 guest ctx
6617
+ // 查非自己发起的行(越权与未知 id 同码,不泄露存在性)。A 端轮询遇此码立即判失败。
6618
+ DISPATCH_NOT_FOUND: "DISPATCH_NOT_FOUND"
6619
+ };
6620
+ ClawdError = class extends Error {
6621
+ constructor(code, message) {
6622
+ super(message);
6623
+ this.code = code;
6624
+ this.name = "ClawdError";
6625
+ }
6626
+ code;
6627
+ };
6628
+ }
6629
+ });
6630
+
6631
+ // ../protocol/src/frames.ts
6632
+ var PROTOCOL_VERSION, DISPATCH_MCP_ID;
6633
+ var init_frames = __esm({
6634
+ "../protocol/src/frames.ts"() {
6635
+ "use strict";
6636
+ PROTOCOL_VERSION = 1;
6637
+ DISPATCH_MCP_ID = "dispatch";
6638
+ }
6639
+ });
6640
+
6641
+ // ../protocol/src/persona-mode.ts
6642
+ var init_persona_mode = __esm({
6643
+ "../protocol/src/persona-mode.ts"() {
6259
6644
  "use strict";
6260
- init_zod();
6261
- DispatchOutcomeSchema = external_exports.discriminatedUnion("kind", [
6262
- external_exports.object({
6263
- kind: external_exports.literal("success"),
6264
- text: external_exports.string(),
6265
- filePaths: external_exports.array(external_exports.string()).optional()
6266
- }),
6267
- external_exports.object({
6268
- kind: external_exports.literal("failure"),
6269
- reason: external_exports.string()
6270
- })
6271
- ]);
6272
- DispatchRunArgsSchema = external_exports.object({
6273
- // 新开必填;续派(dispatchId 非空)时可省略——targetPersonaId 从台账行取。
6274
- targetPersona: external_exports.string().min(1).optional(),
6275
- // 续派(spec 2026-07-21,单 dispatchId 语义:一件事一行一 id):非空 = 把 prompt
6276
- // 作为新 turn 打进该 dispatch 的原 worker session 接着干(台账行复位 running)。
6277
- // 省略 = 新开一件事,daemon 铸新 id。
6278
- dispatchId: external_exports.string().min(1).optional(),
6279
- prompt: external_exports.string(),
6280
- // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
6281
- // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
6282
- targetDeviceId: external_exports.string().min(1).optional(),
6283
- // 可选:override B session 使用的 claude 模型。缺省 = 用 persona 默认。
6284
- // 合法值集与 claude adapter capabilities.models[].id 一致(见 daemon
6285
- // tools/claude.ts CLAUDE_MODEL_IDS);handler 层做二次校验,非法值 fail-fast。
6286
- // protocol 层保持 string 便于未来跨 tool 扩展(届时 union 收合法字面量)。
6287
- model: external_exports.string().min(1).optional(),
6288
- // 可选:给新 worker session 起 label(写入 SessionFile.label,sidebar 显示)。
6289
- // 仅在**新开 dispatch**(dispatchId 缺席)时生效;续派(dispatchId 非空)
6290
- // 时 daemon 层静默丢弃(避免半途改 label 造成 sidebar 抖动)。
6291
- // 上限 60 字符——sidebar 显示 tolerance,卡在 dispatch 入口即可。
6292
- // 协议层不硬拒 sessionLabel + dispatchId 同传,保留 forward-compat。
6293
- sessionLabel: external_exports.string().min(1).max(60).optional()
6294
- }).refine((v2) => v2.targetPersona !== void 0 || v2.dispatchId !== void 0, {
6295
- message: "either targetPersona (new dispatch) or dispatchId (follow-up) is required"
6296
- }).refine((v2) => !(v2.dispatchId !== void 0 && v2.targetDeviceId !== void 0), {
6297
- message: "cross-device dispatch cannot be continued (dispatchId + targetDeviceId are mutually exclusive)"
6298
- });
6299
- DispatchCompleteArgsSchema = external_exports.object({
6300
- dispatchId: external_exports.string().min(1),
6301
- outcome: DispatchOutcomeSchema
6302
- });
6303
- DISPATCH_STATUSES = ["running", "completed", "failed"];
6304
- DispatchStatusSchema = external_exports.enum(DISPATCH_STATUSES);
6305
- DispatchRecordSchema = external_exports.object({
6306
- dispatchId: external_exports.string().min(1),
6307
- sourceSessionId: external_exports.string().min(1),
6308
- sourcePersonaId: external_exports.string().min(1).optional(),
6309
- targetPersonaId: external_exports.string().min(1),
6310
- /** B 的 sessionId;registerBSession 后回填 */
6311
- workerSessionId: external_exports.string().min(1).optional(),
6312
- /** 任务摘要;截断(≤500 字符)在 daemon store.add 处保证 */
6313
- taskText: external_exports.string(),
6314
- status: DispatchStatusSchema,
6315
- outcome: DispatchOutcomeSchema.optional(),
6316
- /** 上层消费者语义(如司礼监的口谕/验收状态),daemon 不解释 */
6317
- meta: external_exports.record(external_exports.unknown()).optional(),
6318
- createdAt: external_exports.string().min(1),
6319
- completedAt: external_exports.string().min(1).optional(),
6320
- /** 结果注入 source session 成功时戳,兼当投递 dedup 标记 */
6321
- deliveredAt: external_exports.string().min(1).optional()
6322
- });
6323
- DispatchListArgsSchema = external_exports.object({
6324
- sourceSessionId: external_exports.string().min(1).optional()
6325
- });
6326
- DispatchListResultSchema = external_exports.object({
6327
- records: external_exports.array(DispatchRecordSchema)
6328
- });
6329
- DispatchGetArgsSchema = external_exports.object({
6330
- dispatchId: external_exports.string().min(1)
6331
- });
6332
- DispatchGetOkSchema = external_exports.object({
6333
- type: external_exports.literal("personaDispatch:get:ok"),
6334
- status: DispatchStatusSchema,
6335
- outcome: DispatchOutcomeSchema.optional()
6336
- });
6337
6645
  }
6338
6646
  });
6339
6647
 
@@ -6413,6 +6721,7 @@ var init_runtime = __esm({
6413
6721
  init_dispatch();
6414
6722
  init_log();
6415
6723
  init_visitor_schemas();
6724
+ init_shift_internal();
6416
6725
  }
6417
6726
  });
6418
6727
 
@@ -41624,21 +41933,21 @@ var SessionStoreFactory = class {
41624
41933
  bareRoot() {
41625
41934
  return path5.join(this.dataDir, "sessions");
41626
41935
  }
41627
- vmOwnerRoot(personaId) {
41936
+ vmOwnerRoot(personaId2) {
41628
41937
  return path5.join(
41629
41938
  this.dataDir,
41630
41939
  "personas",
41631
- safeFileName(personaId),
41940
+ safeFileName(personaId2),
41632
41941
  ".clawd",
41633
41942
  "sessions",
41634
41943
  "owner"
41635
41944
  );
41636
41945
  }
41637
- vmGuestRoot(personaId, capId) {
41946
+ vmGuestRoot(personaId2, capId) {
41638
41947
  return path5.join(
41639
41948
  this.dataDir,
41640
41949
  "personas",
41641
- safeFileName(personaId),
41950
+ safeFileName(personaId2),
41642
41951
  ".clawd",
41643
41952
  "sessions",
41644
41953
  "guests",
@@ -41652,19 +41961,19 @@ var SessionStoreFactory = class {
41652
41961
  }
41653
41962
  return this.bareStore;
41654
41963
  }
41655
- forVmOwner(personaId) {
41656
- const key = personaId;
41964
+ forVmOwner(personaId2) {
41965
+ const key = personaId2;
41657
41966
  const cached = this.vmOwnerStores.get(key);
41658
41967
  if (cached) return cached;
41659
- const st = new SessionStore({ root: this.vmOwnerRoot(personaId) });
41968
+ const st = new SessionStore({ root: this.vmOwnerRoot(personaId2) });
41660
41969
  this.vmOwnerStores.set(key, st);
41661
41970
  return st;
41662
41971
  }
41663
- forVmGuest(personaId, capId) {
41664
- const key = `${personaId}::${capId}`;
41972
+ forVmGuest(personaId2, capId) {
41973
+ const key = `${personaId2}::${capId}`;
41665
41974
  const cached = this.vmGuestStores.get(key);
41666
41975
  if (cached) return cached;
41667
- const st = new SessionStore({ root: this.vmGuestRoot(personaId, capId) });
41976
+ const st = new SessionStore({ root: this.vmGuestRoot(personaId2, capId) });
41668
41977
  this.vmGuestStores.set(key, st);
41669
41978
  return st;
41670
41979
  }
@@ -42146,8 +42455,8 @@ function buildSpawnContext(state, deps) {
42146
42455
  if (clawosApi) env.CLAWOS_API = clawosApi;
42147
42456
  const dispatchId = deps.lookupDispatchByBSessionId?.(file.sessionId);
42148
42457
  if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
42149
- const personaId = file.ownerPersonaId;
42150
- if (personaId) env.CLAWD_PERSONA_ID = personaId;
42458
+ const personaId2 = file.ownerPersonaId;
42459
+ if (personaId2) env.CLAWD_PERSONA_ID = personaId2;
42151
42460
  const mcpConfigs = deps.getMcpConfigs?.() ?? [];
42152
42461
  const ctx = {
42153
42462
  cwd: file.cwd,
@@ -42162,7 +42471,7 @@ function buildSpawnContext(state, deps) {
42162
42471
  ...mcpConfigs.length > 0 ? { mcpConfigs } : {},
42163
42472
  // shift v1:把 SessionFile.ownerPersonaId 透传到 SpawnContext;buildSpawnArgs 等下游
42164
42473
  // 不直接读这个字段(只读 env),但保留显式字段方便 daemon 内部诊断/日志。
42165
- personaId
42474
+ personaId: personaId2
42166
42475
  };
42167
42476
  const meta = state.subSessionMeta;
42168
42477
  if (meta?.personaMode) {
@@ -43456,14 +43765,14 @@ When done, call the \`personaDispatchComplete\` tool from the \`clawd-dispatch\`
43456
43765
  dispatchId for this task: ${args.dispatchId}`;
43457
43766
  }
43458
43767
  function derivePersonaSpawnCwd(file, personaRoot) {
43459
- const personaId = file.ownerPersonaId;
43460
- if (!personaId) return file.cwd;
43768
+ const personaId2 = file.ownerPersonaId;
43769
+ if (!personaId2) return file.cwd;
43461
43770
  if (!personaRoot) {
43462
43771
  throw new Error(
43463
- `derivePersonaSpawnCwd: personaRoot missing for owner session ${file.sessionId} (ownerPersonaId=${personaId})`
43772
+ `derivePersonaSpawnCwd: personaRoot missing for owner session ${file.sessionId} (ownerPersonaId=${personaId2})`
43464
43773
  );
43465
43774
  }
43466
- return import_node_path9.default.join(personaRoot, safeFileName(personaId));
43775
+ return import_node_path9.default.join(personaRoot, safeFileName(personaId2));
43467
43776
  }
43468
43777
  function makeInitialState(file, subSessionMeta) {
43469
43778
  return {
@@ -43613,12 +43922,12 @@ var SessionManager = class {
43613
43922
  // 扫某 persona 下 guests/ 子目录, 拿所有 capId.
43614
43923
  // 路径: <dataDir>/personas/<pid>/.clawd/sessions/guests/<capId>/
43615
43924
  // 只在 storeFactory 注入 (新布局) 下生效, 老布局无 guest 目录.
43616
- listGuestCapIdsForPersona(personaId) {
43925
+ listGuestCapIdsForPersona(personaId2) {
43617
43926
  if (!this.deps.dataDir || !this.deps.storeFactory) return [];
43618
43927
  const root = import_node_path9.default.join(
43619
43928
  this.deps.dataDir,
43620
43929
  "personas",
43621
- personaId,
43930
+ personaId2,
43622
43931
  ".clawd",
43623
43932
  "sessions",
43624
43933
  "guests"
@@ -43650,12 +43959,12 @@ var SessionManager = class {
43650
43959
  if (runner) return runner.getState().file;
43651
43960
  const dflt = this.deps.store.read(sessionId);
43652
43961
  if (dflt) return dflt;
43653
- for (const personaId of this.listPersonaIdsOnDisk()) {
43654
- const ownerStore = this.storeFor({ kind: "persona", personaId, mode: "owner" });
43962
+ for (const personaId2 of this.listPersonaIdsOnDisk()) {
43963
+ const ownerStore = this.storeFor({ kind: "persona", personaId: personaId2, mode: "owner" });
43655
43964
  const found = ownerStore.read(sessionId);
43656
43965
  if (found) return found;
43657
- for (const capId of this.listGuestCapIdsForPersona(personaId)) {
43658
- const guestStore = this.storeFor({ kind: "persona", personaId, mode: "guest", capId });
43966
+ for (const capId of this.listGuestCapIdsForPersona(personaId2)) {
43967
+ const guestStore = this.storeFor({ kind: "persona", personaId: personaId2, mode: "guest", capId });
43659
43968
  const g2 = guestStore.read(sessionId);
43660
43969
  if (g2) return g2;
43661
43970
  }
@@ -43684,11 +43993,11 @@ var SessionManager = class {
43684
43993
  listAllOwned() {
43685
43994
  const out = [];
43686
43995
  out.push(...this.deps.store.list());
43687
- for (const personaId of this.listPersonaIdsOnDisk()) {
43688
- const ownerStore = this.storeFor({ kind: "persona", personaId, mode: "owner" });
43996
+ for (const personaId2 of this.listPersonaIdsOnDisk()) {
43997
+ const ownerStore = this.storeFor({ kind: "persona", personaId: personaId2, mode: "owner" });
43689
43998
  out.push(...ownerStore.list());
43690
- for (const capId of this.listGuestCapIdsForPersona(personaId)) {
43691
- const guestStore = this.storeFor({ kind: "persona", personaId, mode: "guest", capId });
43999
+ for (const capId of this.listGuestCapIdsForPersona(personaId2)) {
44000
+ const guestStore = this.storeFor({ kind: "persona", personaId: personaId2, mode: "guest", capId });
43692
44001
  out.push(...guestStore.list());
43693
44002
  }
43694
44003
  }
@@ -43716,11 +44025,11 @@ var SessionManager = class {
43716
44025
  // 删除 owned SessionFile(default / persona owner / persona guest),与 findOwnedSession 对称扫盘.
43717
44026
  deleteOwned(sessionId) {
43718
44027
  if (this.deps.store.delete(sessionId)) return true;
43719
- for (const personaId of this.listPersonaIdsOnDisk()) {
43720
- const ownerStore = this.storeFor({ kind: "persona", personaId, mode: "owner" });
44028
+ for (const personaId2 of this.listPersonaIdsOnDisk()) {
44029
+ const ownerStore = this.storeFor({ kind: "persona", personaId: personaId2, mode: "owner" });
43721
44030
  if (ownerStore.delete(sessionId)) return true;
43722
- for (const capId of this.listGuestCapIdsForPersona(personaId)) {
43723
- const guestStore = this.storeFor({ kind: "persona", personaId, mode: "guest", capId });
44031
+ for (const capId of this.listGuestCapIdsForPersona(personaId2)) {
44032
+ const guestStore = this.storeFor({ kind: "persona", personaId: personaId2, mode: "guest", capId });
43724
44033
  if (guestStore.delete(sessionId)) return true;
43725
44034
  }
43726
44035
  }
@@ -45418,19 +45727,19 @@ var PersonaStore = class {
45418
45727
  fs10.mkdirSync(root, { recursive: true });
45419
45728
  }
45420
45729
  root;
45421
- personaDir(personaId) {
45422
- return path12.join(this.root, safeFileName(personaId));
45730
+ personaDir(personaId2) {
45731
+ return path12.join(this.root, safeFileName(personaId2));
45423
45732
  }
45424
- metaPath(personaId) {
45425
- return path12.join(this.personaDir(personaId), ".clawd", "persona.json");
45733
+ metaPath(personaId2) {
45734
+ return path12.join(this.personaDir(personaId2), ".clawd", "persona.json");
45426
45735
  }
45427
- claudeMdPath(personaId) {
45428
- return path12.join(this.personaDir(personaId), "CLAUDE.md");
45736
+ claudeMdPath(personaId2) {
45737
+ return path12.join(this.personaDir(personaId2), "CLAUDE.md");
45429
45738
  }
45430
45739
  // codex 原生读 cwd 的 AGENTS.md。人格双写镜像:claude 读 CLAUDE.md、codex 读 AGENTS.md,
45431
45740
  // 两份内容恒一致,persona 切 tool 零迁移。
45432
- agentsMdPath(personaId) {
45433
- return path12.join(this.personaDir(personaId), "AGENTS.md");
45741
+ agentsMdPath(personaId2) {
45742
+ return path12.join(this.personaDir(personaId2), "AGENTS.md");
45434
45743
  }
45435
45744
  /**
45436
45745
  * persona 级 sandbox base 落盘路径 —— 故意放 `.clawd/` 而非 `.claude/`,让 CC 的 project
@@ -45438,8 +45747,8 @@ var PersonaStore = class {
45438
45747
  * 这份是 guest 沙箱的 **base 真源**(manager compose 读它当 base);guest 实际加载的是 manager
45439
45748
  * spawn 前 per-guest 动态拼到各自 session 目录的那份(base + 强制底座 + 本 guest userWorkDir carve)。
45440
45749
  */
45441
- sandboxSettingsPath(personaId) {
45442
- return path12.join(this.personaDir(personaId), ".clawd", "sandbox-settings.json");
45750
+ sandboxSettingsPath(personaId2) {
45751
+ return path12.join(this.personaDir(personaId2), ".clawd", "sandbox-settings.json");
45443
45752
  }
45444
45753
  write(persona, personality) {
45445
45754
  const dir = this.personaDir(persona.personaId);
@@ -45449,9 +45758,9 @@ var PersonaStore = class {
45449
45758
  this.writeSandboxSettings(persona.personaId, buildGuestSettingsV1());
45450
45759
  this.atomicWrite(this.metaPath(persona.personaId), JSON.stringify(persona, null, 2));
45451
45760
  }
45452
- writePersonality(personaId, personality) {
45453
- this.atomicWrite(this.claudeMdPath(personaId), personality);
45454
- this.atomicWrite(this.agentsMdPath(personaId), personality);
45761
+ writePersonality(personaId2, personality) {
45762
+ this.atomicWrite(this.claudeMdPath(personaId2), personality);
45763
+ this.atomicWrite(this.agentsMdPath(personaId2), personality);
45455
45764
  }
45456
45765
  /**
45457
45766
  * 历史 persona 补 AGENTS.md:人格双写镜像是 codex 适配后才加的,更早落盘的 persona 只有
@@ -45459,9 +45768,9 @@ var PersonaStore = class {
45459
45768
  * migrate(见 seed.migrateAgentsMirror)。幂等:已有 AGENTS.md(含用户手改的)不覆盖;
45460
45769
  * 无 CLAUDE.md 不凭空造。返回是否实际补写。
45461
45770
  */
45462
- ensureAgentsMirror(personaId) {
45463
- const claudeMd = this.claudeMdPath(personaId);
45464
- const agentsMd = this.agentsMdPath(personaId);
45771
+ ensureAgentsMirror(personaId2) {
45772
+ const claudeMd = this.claudeMdPath(personaId2);
45773
+ const agentsMd = this.agentsMdPath(personaId2);
45465
45774
  if (!fs10.existsSync(claudeMd)) return false;
45466
45775
  if (fs10.existsSync(agentsMd)) return false;
45467
45776
  this.atomicWrite(agentsMd, fs10.readFileSync(claudeMd, "utf8"));
@@ -45474,25 +45783,25 @@ var PersonaStore = class {
45474
45783
  * 写入时强制注入本 persona 的 Edit-deny('~/<persona>/**' 按实际 persona dir 算)+ 剥掉历史相对 'Edit(./**)',
45475
45784
  * 让落盘 base 就是 source of truth(compose 不再运行时替换 permission deny)。
45476
45785
  */
45477
- writeSandboxSettings(personaId, settings) {
45786
+ writeSandboxSettings(personaId2, settings) {
45478
45787
  this.atomicWrite(
45479
- this.sandboxSettingsPath(personaId),
45480
- JSON.stringify(this.injectPersonaEditDeny(personaId, settings), null, 2)
45788
+ this.sandboxSettingsPath(personaId2),
45789
+ JSON.stringify(this.injectPersonaEditDeny(personaId2, settings), null, 2)
45481
45790
  );
45482
45791
  }
45483
45792
  /** 把 persona Edit-deny(绝对 '~/<persona>/**')并进 settings.permissions.deny,剥掉历史相对 'Edit(./**)'。 */
45484
- injectPersonaEditDeny(personaId, settings) {
45793
+ injectPersonaEditDeny(personaId2, settings) {
45485
45794
  const s = settings;
45486
- const rule = personaEditDeny(this.personaDir(personaId));
45795
+ const rule = personaEditDeny(this.personaDir(personaId2));
45487
45796
  const prev = (s.permissions?.deny ?? []).filter((r) => r !== "Edit(./**)" && r !== rule);
45488
45797
  return { ...s, permissions: { ...s.permissions ?? {}, deny: [...prev, rule] } };
45489
45798
  }
45490
- codexSandboxSettingsPath(personaId) {
45491
- return path12.join(this.personaDir(personaId), ".clawd", "codex-sandbox.json");
45799
+ codexSandboxSettingsPath(personaId2) {
45800
+ return path12.join(this.personaDir(personaId2), ".clawd", "codex-sandbox.json");
45492
45801
  }
45493
45802
  /** 读 codex-sandbox.json;不存在/损坏 → null。 */
45494
- readCodexSandboxSettings(personaId) {
45495
- const p2 = this.codexSandboxSettingsPath(personaId);
45803
+ readCodexSandboxSettings(personaId2) {
45804
+ const p2 = this.codexSandboxSettingsPath(personaId2);
45496
45805
  if (!fs10.existsSync(p2)) return null;
45497
45806
  try {
45498
45807
  return CodexSandboxSettingsSchema.parse(JSON.parse(fs10.readFileSync(p2, "utf8")));
@@ -45501,16 +45810,16 @@ var PersonaStore = class {
45501
45810
  }
45502
45811
  }
45503
45812
  /** 覆盖写 codex-sandbox.json(seed/migrate 用)。 */
45504
- writeCodexSandboxSettings(personaId, settings) {
45505
- const dir = path12.join(this.personaDir(personaId), ".clawd");
45813
+ writeCodexSandboxSettings(personaId2, settings) {
45814
+ const dir = path12.join(this.personaDir(personaId2), ".clawd");
45506
45815
  fs10.mkdirSync(dir, { recursive: true });
45507
- this.atomicWrite(this.codexSandboxSettingsPath(personaId), JSON.stringify(settings, null, 2));
45816
+ this.atomicWrite(this.codexSandboxSettingsPath(personaId2), JSON.stringify(settings, null, 2));
45508
45817
  }
45509
45818
  writeMeta(persona) {
45510
45819
  this.atomicWrite(this.metaPath(persona.personaId), JSON.stringify(persona, null, 2));
45511
45820
  }
45512
- read(personaId) {
45513
- const p2 = this.metaPath(personaId);
45821
+ read(personaId2) {
45822
+ const p2 = this.metaPath(personaId2);
45514
45823
  if (!fs10.existsSync(p2)) return null;
45515
45824
  const raw = JSON.parse(fs10.readFileSync(p2, "utf8"));
45516
45825
  if (raw && typeof raw === "object" && "tokenMap" in raw) {
@@ -45519,13 +45828,13 @@ var PersonaStore = class {
45519
45828
  }
45520
45829
  return PersonaFileSchema.parse(raw);
45521
45830
  }
45522
- has(personaId) {
45523
- return fs10.existsSync(this.metaPath(personaId));
45831
+ has(personaId2) {
45832
+ return fs10.existsSync(this.metaPath(personaId2));
45524
45833
  }
45525
- readPersonality(personaId) {
45526
- const claudeMd = this.claudeMdPath(personaId);
45834
+ readPersonality(personaId2) {
45835
+ const claudeMd = this.claudeMdPath(personaId2);
45527
45836
  if (fs10.existsSync(claudeMd)) return fs10.readFileSync(claudeMd, "utf8");
45528
- const agentsMd = this.agentsMdPath(personaId);
45837
+ const agentsMd = this.agentsMdPath(personaId2);
45529
45838
  if (fs10.existsSync(agentsMd)) return fs10.readFileSync(agentsMd, "utf8");
45530
45839
  return null;
45531
45840
  }
@@ -45533,8 +45842,8 @@ var PersonaStore = class {
45533
45842
  * 读 sandbox-settings.json 当前内容;文件不存在 / JSON 解析失败均返回 null。
45534
45843
  * owner 手改文件可能写出非法形状,daemon 不强校验,UI 拿到 null → 显示空状态。
45535
45844
  */
45536
- readSandboxSettings(personaId) {
45537
- const p2 = this.sandboxSettingsPath(personaId);
45845
+ readSandboxSettings(personaId2) {
45846
+ const p2 = this.sandboxSettingsPath(personaId2);
45538
45847
  if (!fs10.existsSync(p2)) return null;
45539
45848
  try {
45540
45849
  return JSON.parse(fs10.readFileSync(p2, "utf8"));
@@ -45543,15 +45852,15 @@ var PersonaStore = class {
45543
45852
  }
45544
45853
  }
45545
45854
  /** Persona 私有 skills 目录路径:<personaDir>/.claude/skills */
45546
- skillsDir(personaId) {
45547
- return path12.join(this.personaDir(personaId), ".claude", "skills");
45855
+ skillsDir(personaId2) {
45856
+ return path12.join(this.personaDir(personaId2), ".claude", "skills");
45548
45857
  }
45549
45858
  /**
45550
45859
  * Claude Code 项目级 settings 路径:`<personaDir>/.claude/settings.json`。
45551
45860
  * 这里只读 `enabledPlugins` 字段,由 owner 通过 CC `/plugin` 之类命令维护,daemon 不写。
45552
45861
  */
45553
- claudeSettingsPath(personaId) {
45554
- return path12.join(this.personaDir(personaId), ".claude", "settings.json");
45862
+ claudeSettingsPath(personaId2) {
45863
+ return path12.join(this.personaDir(personaId2), ".claude", "settings.json");
45555
45864
  }
45556
45865
  /**
45557
45866
  * 读取 persona 的 `.claude/settings.json` 中 `enabledPlugins` map,把 value === true
@@ -45564,8 +45873,8 @@ var PersonaStore = class {
45564
45873
  * - 单 value 非 boolean → 跳过该 entry
45565
45874
  * 其他错误(fs 权限等)原样抛出,由上层 handler 转 ws error 帧。
45566
45875
  */
45567
- readEnabledPlugins(personaId) {
45568
- const p2 = this.claudeSettingsPath(personaId);
45876
+ readEnabledPlugins(personaId2) {
45877
+ const p2 = this.claudeSettingsPath(personaId2);
45569
45878
  if (!fs10.existsSync(p2)) return [];
45570
45879
  let raw;
45571
45880
  try {
@@ -45588,12 +45897,12 @@ var PersonaStore = class {
45588
45897
  return fs10.existsSync(path12.join(this.root, name, ".clawd", "persona.json"));
45589
45898
  });
45590
45899
  }
45591
- remove(personaId) {
45592
- const dir = this.personaDir(personaId);
45900
+ remove(personaId2) {
45901
+ const dir = this.personaDir(personaId2);
45593
45902
  if (fs10.existsSync(dir)) fs10.rmSync(dir, { recursive: true, force: true });
45594
45903
  }
45595
- personaDirPath(personaId) {
45596
- return this.personaDir(personaId);
45904
+ personaDirPath(personaId2) {
45905
+ return this.personaDir(personaId2);
45597
45906
  }
45598
45907
  atomicWrite(file, content) {
45599
45908
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
@@ -45625,15 +45934,15 @@ var PersonaRegistry = class {
45625
45934
  }
45626
45935
  }
45627
45936
  }
45628
- get(personaId) {
45629
- return this.cache.get(personaId);
45937
+ get(personaId2) {
45938
+ return this.cache.get(personaId2);
45630
45939
  }
45631
45940
  /** PersonaManager 写盘后同步 cache;不主动写盘 */
45632
45941
  set(file) {
45633
45942
  this.cache.set(file.personaId, file);
45634
45943
  }
45635
- remove(personaId) {
45636
- this.cache.delete(personaId);
45944
+ remove(personaId2) {
45945
+ this.cache.delete(personaId2);
45637
45946
  }
45638
45947
  list() {
45639
45948
  return Array.from(this.cache.values());
@@ -45923,10 +46232,10 @@ var PersonaManager = class {
45923
46232
  }
45924
46233
  deps;
45925
46234
  create(args) {
45926
- const personaId = this.generatePersonaId(args.label);
46235
+ const personaId2 = this.generatePersonaId(args.label);
45927
46236
  const now = Date.now();
45928
46237
  const persona = {
45929
- personaId,
46238
+ personaId: personaId2,
45930
46239
  label: args.label,
45931
46240
  model: args.model,
45932
46241
  tool: args.tool,
@@ -45939,9 +46248,9 @@ var PersonaManager = class {
45939
46248
  this.deps.registry.set(persona);
45940
46249
  return persona;
45941
46250
  }
45942
- update(personaId, patch, personality) {
45943
- const existing = this.deps.registry.get(personaId);
45944
- if (!existing) throw new Error(`persona not found: ${personaId}`);
46251
+ update(personaId2, patch, personality) {
46252
+ const existing = this.deps.registry.get(personaId2);
46253
+ if (!existing) throw new Error(`persona not found: ${personaId2}`);
45945
46254
  const { iconKey: iconKeyPatch, ...restPatch } = patch;
45946
46255
  const updated = {
45947
46256
  ...existing,
@@ -45954,30 +46263,30 @@ var PersonaManager = class {
45954
46263
  updated.iconKey = iconKeyPatch;
45955
46264
  }
45956
46265
  if (personality !== void 0) {
45957
- this.deps.store.writePersonality(personaId, personality);
46266
+ this.deps.store.writePersonality(personaId2, personality);
45958
46267
  }
45959
46268
  this.deps.store.writeMeta(updated);
45960
46269
  this.deps.registry.set(updated);
45961
46270
  return updated;
45962
46271
  }
45963
46272
  /** 读取 CLAUDE.md 内容;persona 目录或文件不存在时返回 null。透传给 handler 拼响应。 */
45964
- readPersonality(personaId) {
45965
- return this.deps.store.readPersonality(personaId);
46273
+ readPersonality(personaId2) {
46274
+ return this.deps.store.readPersonality(personaId2);
45966
46275
  }
45967
46276
  /** 扫 persona 私有 skills 目录;目录不存在返回空数组。供 persona:get handler 拼响应。 */
45968
- listSkills(personaId) {
45969
- return listSkillsForDir(this.deps.store.skillsDir(personaId), "project");
46277
+ listSkills(personaId2) {
46278
+ return listSkillsForDir(this.deps.store.skillsDir(personaId2), "project");
45970
46279
  }
45971
46280
  /**
45972
46281
  * 读取 persona `.claude/settings.json` 中已启用的插件列表(value === true 的 key)。
45973
46282
  * 文件不存在 / 解析失败 / 字段缺失 → 空数组。供 persona:get handler 拼响应。
45974
46283
  */
45975
- listEnabledPlugins(personaId) {
45976
- return this.deps.store.readEnabledPlugins(personaId);
46284
+ listEnabledPlugins(personaId2) {
46285
+ return this.deps.store.readEnabledPlugins(personaId2);
45977
46286
  }
45978
46287
  /** 读 sandbox-settings.json;不存在 / 损坏返回 null。供 persona:get handler 拼响应。 */
45979
- readSandboxSettings(personaId) {
45980
- return this.deps.store.readSandboxSettings(personaId);
46288
+ readSandboxSettings(personaId2) {
46289
+ return this.deps.store.readSandboxSettings(personaId2);
45981
46290
  }
45982
46291
  /**
45983
46292
  * 删除 persona。
@@ -45985,9 +46294,9 @@ var PersonaManager = class {
45985
46294
  * (含 .clawd/sessions/owner/ 和 .clawd/sessions/guests/<capId>/),rmSync
45986
46295
  * recursive force 一锅清。
45987
46296
  */
45988
- delete(personaId) {
45989
- this.deps.store.remove(personaId);
45990
- this.deps.registry.remove(personaId);
46297
+ delete(personaId2) {
46298
+ this.deps.store.remove(personaId2);
46299
+ this.deps.registry.remove(personaId2);
45991
46300
  }
45992
46301
  /**
45993
46302
  * label 转 4-16 char slug。优先用 `persona-<slug>`;若 slug 已被占用,追加 4 char
@@ -46317,15 +46626,15 @@ function refreshSandboxSettings(args) {
46317
46626
  }
46318
46627
  function migrateAgentsMirror(args) {
46319
46628
  let mirrored = 0;
46320
- for (const personaId of args.store.list()) {
46629
+ for (const personaId2 of args.store.list()) {
46321
46630
  try {
46322
- if (args.store.ensureAgentsMirror(personaId)) {
46631
+ if (args.store.ensureAgentsMirror(personaId2)) {
46323
46632
  mirrored++;
46324
- args.logger.info("persona.agents-mirror.synced", { personaId });
46633
+ args.logger.info("persona.agents-mirror.synced", { personaId: personaId2 });
46325
46634
  }
46326
46635
  } catch (err) {
46327
46636
  args.logger.warn("persona.agents-mirror.failed", {
46328
- personaId,
46637
+ personaId: personaId2,
46329
46638
  error: err instanceof Error ? err.message : String(err)
46330
46639
  });
46331
46640
  }
@@ -46335,15 +46644,15 @@ function migrateAgentsMirror(args) {
46335
46644
  function migrateCodexSandbox(args) {
46336
46645
  const entries = args.entries ?? DEFAULT_PERSONAS;
46337
46646
  let n = 0;
46338
- for (const personaId of args.store.list()) {
46339
- if (args.store.readCodexSandboxSettings(personaId)) continue;
46647
+ for (const personaId2 of args.store.list()) {
46648
+ if (args.store.readCodexSandboxSettings(personaId2)) continue;
46340
46649
  try {
46341
- const def = entries.find((e) => e.personaId === personaId)?.codexSandbox ?? { writableRoots: [], denyRead: [], network: false };
46342
- args.store.writeCodexSandboxSettings(personaId, def);
46650
+ const def = entries.find((e) => e.personaId === personaId2)?.codexSandbox ?? { writableRoots: [], denyRead: [], network: false };
46651
+ args.store.writeCodexSandboxSettings(personaId2, def);
46343
46652
  n++;
46344
- args.logger.info("persona.codex-sandbox.synced", { personaId });
46653
+ args.logger.info("persona.codex-sandbox.synced", { personaId: personaId2 });
46345
46654
  } catch (err) {
46346
- args.logger.warn("persona.codex-sandbox.failed", { personaId, error: err instanceof Error ? err.message : String(err) });
46655
+ args.logger.warn("persona.codex-sandbox.failed", { personaId: personaId2, error: err instanceof Error ? err.message : String(err) });
46347
46656
  }
46348
46657
  }
46349
46658
  if (n > 0) args.logger.info("persona.codex-sandbox.done", { migrated: n });
@@ -49193,16 +49502,17 @@ function createShiftSpawnFn(deps) {
49193
49502
  };
49194
49503
  }
49195
49504
 
49196
- // src/handlers/shift-internal.ts
49197
- function readPersonaId(frame) {
49198
- const raw = frame.personaId;
49199
- if (typeof raw !== "string" || raw.trim() === "") {
49200
- throw new Error(
49201
- "shift-internal: missing personaId (mcp-server should set CLAWD_PERSONA_ID env)"
49202
- );
49203
- }
49204
- return raw;
49505
+ // src/handlers/types.ts
49506
+ init_protocol();
49507
+ function parseArgs2(schema, frame, what, code = ERROR_CODES.VALIDATION_ERROR) {
49508
+ const effective = typeof schema.strip === "function" ? schema.strip() : schema;
49509
+ const r = effective.safeParse(frame);
49510
+ if (!r.success) throw new ClawdError(code, `${what}: ${r.error.message}`);
49511
+ return r.data;
49205
49512
  }
49513
+
49514
+ // src/handlers/shift-internal.ts
49515
+ init_protocol();
49206
49516
  function validateSchedule(schedule) {
49207
49517
  const next = computeNextRunAtMs(schedule, Date.now());
49208
49518
  if (next === null) {
@@ -49213,10 +49523,9 @@ function validateSchedule(schedule) {
49213
49523
  }
49214
49524
  function buildShiftInternalHandlers(deps) {
49215
49525
  const add = async (frame) => {
49216
- const persona = readPersonaId(frame);
49217
49526
  const callerSessionId = typeof frame.sessionId === "string" ? frame.sessionId : void 0;
49218
- const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
49219
- const args = rest;
49527
+ const args = parseArgs2(ShiftInternalAddArgs, frame, "shift-internal:add");
49528
+ const persona = args.personaId;
49220
49529
  validateSchedule(args.schedule);
49221
49530
  const shift = deps.store.add({
49222
49531
  ownerId: deps.ownerIdProvider(),
@@ -49234,17 +49543,15 @@ function buildShiftInternalHandlers(deps) {
49234
49543
  };
49235
49544
  };
49236
49545
  const list = async (frame) => {
49237
- const persona = readPersonaId(frame);
49238
- const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
49239
- const args = rest;
49546
+ const args = parseArgs2(ShiftInternalListArgs, frame, "shift-internal:list");
49547
+ const persona = args.personaId;
49240
49548
  const onlyMine = args.onlyMine !== false;
49241
49549
  const filter = onlyMine ? { ownerId: deps.ownerIdProvider(), createdByPersona: persona } : { ownerId: deps.ownerIdProvider() };
49242
49550
  return { response: { shifts: deps.store.list(filter) } };
49243
49551
  };
49244
49552
  const cancel = async (frame) => {
49245
- const persona = readPersonaId(frame);
49246
- const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
49247
- const args = rest;
49553
+ const args = parseArgs2(ShiftInternalCancelArgs, frame, "shift-internal:cancel");
49554
+ const persona = args.personaId;
49248
49555
  const shift = deps.store.get(args.shiftId);
49249
49556
  if (!shift) {
49250
49557
  throw new Error(`shift-internal:cancel: shift not found: ${args.shiftId}`);
@@ -49258,9 +49565,8 @@ function buildShiftInternalHandlers(deps) {
49258
49565
  return { response: { ok: true } };
49259
49566
  };
49260
49567
  const update = async (frame) => {
49261
- const persona = readPersonaId(frame);
49262
- const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
49263
- const args = rest;
49568
+ const args = parseArgs2(ShiftInternalUpdateArgs, frame, "shift-internal:update");
49569
+ const persona = args.personaId;
49264
49570
  const shift = deps.store.get(args.shiftId);
49265
49571
  if (!shift) {
49266
49572
  throw new Error(`shift-internal:update: shift not found: ${args.shiftId}`);
@@ -49280,9 +49586,8 @@ function buildShiftInternalHandlers(deps) {
49280
49586
  };
49281
49587
  };
49282
49588
  const history = async (frame) => {
49283
- const persona = readPersonaId(frame);
49284
- const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
49285
- const args = rest;
49589
+ const args = parseArgs2(ShiftInternalHistoryArgs, frame, "shift-internal:history");
49590
+ const persona = args.personaId;
49286
49591
  const shift = deps.store.get(args.shiftId);
49287
49592
  if (!shift) {
49288
49593
  throw new Error(`shift-internal:history: shift not found: ${args.shiftId}`);
@@ -49338,9 +49643,9 @@ function canAccessSession(ctx, sessionId, action, deps) {
49338
49643
  if (!file) return true;
49339
49644
  return canAccessPersona(ctx.grants, file.ownerPersonaId, action);
49340
49645
  }
49341
- function canAccessPersona(grants, personaId, action) {
49342
- if (!personaId) return false;
49343
- const resource = { type: "persona", id: personaId };
49646
+ function canAccessPersona(grants, personaId2, action) {
49647
+ if (!personaId2) return false;
49648
+ const resource = { type: "persona", id: personaId2 };
49344
49649
  return assertGrant(grants, resource, action);
49345
49650
  }
49346
49651
 
@@ -50685,7 +50990,7 @@ function listRegistered() {
50685
50990
 
50686
50991
  // ../node_modules/.pnpm/ws@8.20.0/node_modules/ws/wrapper.mjs
50687
50992
  var import_stream = __toESM(require_stream(), 1);
50688
- var import_extension = __toESM(require_extension(), 1);
50993
+ var import_extension2 = __toESM(require_extension(), 1);
50689
50994
  var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
50690
50995
  var import_receiver = __toESM(require_receiver(), 1);
50691
50996
  var import_sender = __toESM(require_sender(), 1);
@@ -54287,14 +54592,14 @@ function buildSessionHandlers(deps) {
54287
54592
  );
54288
54593
  }
54289
54594
  };
54290
- const ensurePersonaAccess = (ctx, personaId, action) => {
54595
+ const ensurePersonaAccess = (ctx, personaId2, action) => {
54291
54596
  if (!ctx) return;
54292
54597
  if (ctx.principal.kind === "owner") return;
54293
- const ok = canAccessPersona(ctx.grants, personaId, action);
54598
+ const ok = canAccessPersona(ctx.grants, personaId2, action);
54294
54599
  if (!ok) {
54295
54600
  throw new ClawdError(
54296
54601
  ERROR_CODES.UNAUTHORIZED,
54297
- `principal ${ctx.principal.kind}:${ctx.principal.id} cannot ${action} on persona:${personaId ?? "<none>"}`
54602
+ `principal ${ctx.principal.kind}:${ctx.principal.id} cannot ${action} on persona:${personaId2 ?? "<none>"}`
54298
54603
  );
54299
54604
  }
54300
54605
  };
@@ -54399,13 +54704,17 @@ function buildSessionHandlers(deps) {
54399
54704
  const args = SessionSendArgs.parse(frame);
54400
54705
  ensureSessionAccess(ctx, args.sessionId, "send");
54401
54706
  const sender = ctx ? principalToSender(ctx.principal) : principalToSender(
54402
- makeOwnerPrincipal(ownerPrincipalId, deps.ownerDisplayName, deps.ownerFeishuUnionId)
54707
+ makeOwnerPrincipal(
54708
+ ownerPrincipalId,
54709
+ deps.getOwnerIdentity().displayName,
54710
+ deps.getOwnerIdentity().feishuUnionId
54711
+ )
54403
54712
  );
54404
54713
  const { broadcast } = manager.send(args, sender);
54405
54714
  return { response: { type: "session:send", ok: true }, broadcast };
54406
54715
  };
54407
54716
  const currentTurnSender = async (frame, _client, ctx) => {
54408
- const sid = typeof frame === "object" && frame != null && "sessionId" in frame ? String(frame.sessionId) : "";
54717
+ const sid = SessionCurrentTurnSenderArgs.parse(frame).sessionId ?? "";
54409
54718
  if (!sid) {
54410
54719
  return { response: { type: "session:currentTurnSender", sender: null } };
54411
54720
  }
@@ -54510,19 +54819,20 @@ function buildSessionHandlers(deps) {
54510
54819
  return { response: { type: "session:events", ...response } };
54511
54820
  };
54512
54821
  const subscribe = async (frame, client, ctx) => {
54513
- if (typeof frame.sessionId === "string") {
54514
- ensureSessionAccess(ctx, frame.sessionId, "read");
54515
- addSubscription(client, frame.sessionId);
54822
+ const args = SessionSubscribeArgs.parse(frame);
54823
+ if (args.sessionId !== void 0) {
54824
+ ensureSessionAccess(ctx, args.sessionId, "read");
54825
+ addSubscription(client, args.sessionId);
54516
54826
  }
54517
54827
  return {
54518
- response: { type: "subscribed", sessionId: frame.sessionId }
54828
+ response: { type: "subscribed", sessionId: args.sessionId }
54519
54829
  };
54520
54830
  };
54521
54831
  const unsubscribe = async (frame, client) => {
54522
- if (typeof frame.sessionId === "string")
54523
- removeSubscription(client, frame.sessionId);
54832
+ const args = SessionSubscribeArgs.parse(frame);
54833
+ if (args.sessionId !== void 0) removeSubscription(client, args.sessionId);
54524
54834
  return {
54525
- response: { type: "unsubscribed", sessionId: frame.sessionId }
54835
+ response: { type: "unsubscribed", sessionId: args.sessionId }
54526
54836
  };
54527
54837
  };
54528
54838
  const pin = async (frame, _client, ctx) => {
@@ -54721,11 +55031,11 @@ function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userW
54721
55031
  if (!target.startsWith(root + sep3)) return false;
54722
55032
  const rel = path56.relative(root, target);
54723
55033
  if (!rel || rel.startsWith("..")) return false;
54724
- const personaId = rel.split(path56.sep)[0];
54725
- if (!personaId) return false;
55034
+ const personaId2 = rel.split(path56.sep)[0];
55035
+ if (!personaId2) return false;
54726
55036
  const allowed = getAllowedPersonaIds(grants, action);
54727
55037
  if (allowed === "*") return true;
54728
- return allowed.has(personaId);
55038
+ return allowed.has(personaId2);
54729
55039
  }
54730
55040
  function personaIdFromPath(absPath, personaRoot) {
54731
55041
  const root = path56.resolve(personaRoot);
@@ -54743,9 +55053,9 @@ function isPathWithin(dir, absPath) {
54743
55053
  const sep3 = d.endsWith(path56.sep) ? "" : path56.sep;
54744
55054
  return t === d || t.startsWith(d + sep3);
54745
55055
  }
54746
- function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
55056
+ function isPathInGuestBoundary(personaRoot, personaId2, userWorkDir, absPath) {
54747
55057
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
54748
- return personaIdFromPath(path56.resolve(absPath), personaRoot) === personaId;
55058
+ return personaIdFromPath(path56.resolve(absPath), personaRoot) === personaId2;
54749
55059
  }
54750
55060
 
54751
55061
  // src/handlers/history.ts
@@ -54878,9 +55188,9 @@ var path58 = __toESM(require("path"), 1);
54878
55188
  var os16 = __toESM(require("os"), 1);
54879
55189
  init_protocol();
54880
55190
  init_protocol();
54881
- function buildEnabledPluginNames(personaManager, personaId) {
55191
+ function buildEnabledPluginNames(personaManager, personaId2) {
54882
55192
  const out = /* @__PURE__ */ new Set();
54883
- for (const p2 of personaManager.listEnabledPlugins(personaId)) {
55193
+ for (const p2 of personaManager.listEnabledPlugins(personaId2)) {
54884
55194
  const name = p2.id.includes("@") ? p2.id.slice(0, p2.id.indexOf("@")) : p2.id;
54885
55195
  if (name) out.add(name);
54886
55196
  }
@@ -54935,8 +55245,8 @@ function buildWorkspaceHandlers(deps) {
54935
55245
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
54936
55246
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
54937
55247
  if (ctx?.principal.kind === "guest" && personaRoot) {
54938
- const personaId = personaIdFromPath(cwdAbs, personaRoot);
54939
- const enabled = personaId ? buildEnabledPluginNames(personaManager, personaId) : /* @__PURE__ */ new Set();
55248
+ const personaId2 = personaIdFromPath(cwdAbs, personaRoot);
55249
+ const enabled = personaId2 ? buildEnabledPluginNames(personaManager, personaId2) : /* @__PURE__ */ new Set();
54940
55250
  return { response: { type: "skills:list", skills: filterGuestSkills(list2, enabled) } };
54941
55251
  }
54942
55252
  return { response: { type: "skills:list", skills: list2 } };
@@ -54950,8 +55260,8 @@ function buildWorkspaceHandlers(deps) {
54950
55260
  }
54951
55261
  const list2 = agents.list(args);
54952
55262
  if (ctx?.principal.kind === "guest" && personaRoot) {
54953
- const personaId = personaIdFromPath(cwdAbs, personaRoot);
54954
- const enabled = personaId ? buildEnabledPluginNames(personaManager, personaId) : /* @__PURE__ */ new Set();
55263
+ const personaId2 = personaIdFromPath(cwdAbs, personaRoot);
55264
+ const enabled = personaId2 ? buildEnabledPluginNames(personaManager, personaId2) : /* @__PURE__ */ new Set();
54955
55265
  return { response: { type: "agents:list", agents: filterGuestAgents(list2, enabled) } };
54956
55266
  }
54957
55267
  return { response: { type: "agents:list", agents: list2 } };
@@ -55124,8 +55434,7 @@ function resolvePeerDeviceId(ctx, argsPeerDeviceId) {
55124
55434
  function buildInboxHandlers(deps) {
55125
55435
  const { manager } = deps;
55126
55436
  const postMessage = async (frame, _client, ctx) => {
55127
- const { type: _t, requestId: _r, ...rest } = frame;
55128
- const args = InboxPostMessageArgsSchema.parse(rest);
55437
+ const args = parseArgs2(InboxPostMessageArgsSchema, frame, "inbox:postMessage");
55129
55438
  if (!ctx) {
55130
55439
  throw new ClawdError(ERROR_CODES.INTERNAL, "inbox:postMessage: missing ConnectionContext");
55131
55440
  }
@@ -55143,8 +55452,7 @@ function buildInboxHandlers(deps) {
55143
55452
  };
55144
55453
  };
55145
55454
  const list = async (frame, _client, ctx) => {
55146
- const { type: _t, requestId: _r, ...rest } = frame;
55147
- const args = InboxListArgsSchema.parse(rest);
55455
+ const args = parseArgs2(InboxListArgsSchema, frame, "inbox:list");
55148
55456
  if (!ctx) {
55149
55457
  throw new ClawdError(ERROR_CODES.INTERNAL, "inbox:list: missing ConnectionContext");
55150
55458
  }
@@ -55155,8 +55463,7 @@ function buildInboxHandlers(deps) {
55155
55463
  };
55156
55464
  };
55157
55465
  const markRead = async (frame, _client, ctx) => {
55158
- const { type: _t, requestId: _r, ...rest } = frame;
55159
- const args = InboxMarkReadArgsSchema.parse(rest);
55466
+ const args = parseArgs2(InboxMarkReadArgsSchema, frame, "inbox:markRead");
55160
55467
  if (!ctx) {
55161
55468
  throw new ClawdError(ERROR_CODES.INTERNAL, "inbox:markRead: missing ConnectionContext");
55162
55469
  }
@@ -55178,7 +55485,7 @@ function buildInboxHandlers(deps) {
55178
55485
  const sendDm = async (frame) => {
55179
55486
  const { type: _t, requestId: _r, ...rest } = frame;
55180
55487
  const sessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
55181
- const args = InboxSendDmArgsSchema.parse(rest);
55488
+ const args = parseArgs2(InboxSendDmArgsSchema, frame, "inbox:sendDm");
55182
55489
  const d = deps.sendDmDeps;
55183
55490
  if (!d) throw new ClawdError(ERROR_CODES.INTERNAL, "inbox:sendDm not wired");
55184
55491
  if (!sessionId) {
@@ -55339,8 +55646,7 @@ function buildContactHandlers(deps) {
55339
55646
  };
55340
55647
  const pin = async (frame, _client, ctx) => {
55341
55648
  ensureOwner(ctx);
55342
- const { type: _t, requestId: _r, ...rest } = frame;
55343
- const args = ContactPinArgsSchema.parse(rest);
55649
+ const args = parseArgs2(ContactPinArgsSchema, frame, "contact:pin");
55344
55650
  const pinnedAt = args.pinned ? deps.now() : null;
55345
55651
  const hit = deps.store.setPin(args.deviceId, pinnedAt);
55346
55652
  if (!hit) {
@@ -55364,8 +55670,7 @@ function buildContactHandlers(deps) {
55364
55670
  };
55365
55671
  const remove = async (frame, _client, ctx) => {
55366
55672
  ensureOwner(ctx);
55367
- const { type: _t, requestId: _r, ...rest } = frame;
55368
- const args = ContactRemoveArgsSchema.parse(rest);
55673
+ const args = parseArgs2(ContactRemoveArgsSchema, frame, "contact:remove");
55369
55674
  deps.store.removeByDeviceId(args.deviceId);
55370
55675
  deps.broadcast({
55371
55676
  type: "contact:removed",
@@ -55385,8 +55690,7 @@ function buildContactHandlers(deps) {
55385
55690
  "contact:handshake \u4EC5\u63A5\u53D7 connect-token \u8FDE\u63A5\uFF08\u8BBE\u5907\u4E92\u8FDE\u63E1\u624B\u4E13\u7528\uFF09"
55386
55691
  );
55387
55692
  }
55388
- const { type: _t, requestId: _r, ...rest } = frame;
55389
- const args = ContactHandshakeArgsSchema.parse(rest);
55693
+ const args = parseArgs2(ContactHandshakeArgsSchema, frame, "contact:handshake");
55390
55694
  const contact = upsertPeerContact({
55391
55695
  store: deps.store,
55392
55696
  broadcast: deps.broadcast,
@@ -55403,14 +55707,15 @@ function buildContactHandlers(deps) {
55403
55707
  contact,
55404
55708
  exchangeToken: deps.exchangeToken
55405
55709
  });
55710
+ const identity = deps.getOwnerIdentity();
55406
55711
  return {
55407
55712
  response: {
55408
55713
  type: "contact:handshake:ok",
55409
55714
  owner: {
55410
55715
  id: deps.ownerPrincipalId,
55411
- ownerId: deps.ownerId,
55412
- provider: deps.ownerProvider,
55413
- displayName: deps.ownerDisplayName
55716
+ ownerId: identity.ownerId,
55717
+ provider: identity.provider,
55718
+ displayName: identity.displayName
55414
55719
  },
55415
55720
  grants: ctx.grants
55416
55721
  }
@@ -55437,8 +55742,7 @@ function ensureOwner2(ctx) {
55437
55742
  function buildContactRemoteAccessHandlers(deps) {
55438
55743
  const setRemoteAccess = async (frame, _client, ctx) => {
55439
55744
  ensureOwner2(ctx);
55440
- const { type: _t, requestId: _r, ...rest } = frame;
55441
- const args = ContactSetRemoteAccessArgsSchema.parse(rest);
55745
+ const args = parseArgs2(ContactSetRemoteAccessArgsSchema, frame, "contact:setRemoteAccess");
55442
55746
  const hit = deps.store.setRemoteAccess(args.deviceId, {
55443
55747
  remoteAccessAllowed: args.remoteAccessAllowed
55444
55748
  });
@@ -55473,6 +55777,7 @@ function buildWhoamiHandler(deps) {
55473
55777
  if (!ctx) {
55474
55778
  throw new ClawdError(ERROR_CODES.INTERNAL, "whoami: missing ConnectionContext");
55475
55779
  }
55780
+ const identity = deps.getOwnerIdentity();
55476
55781
  if (ctx.tokenKind === "connect") {
55477
55782
  return {
55478
55783
  response: {
@@ -55482,15 +55787,15 @@ function buildWhoamiHandler(deps) {
55482
55787
  };
55483
55788
  }
55484
55789
  const owner = {
55485
- ...makeOwnerPrincipal(deps.ownerPrincipalId, deps.ownerDisplayName),
55486
- ownerId: deps.ownerId,
55487
- provider: deps.ownerProvider
55790
+ ...makeOwnerPrincipal(deps.ownerPrincipalId, identity.displayName),
55791
+ ownerId: identity.ownerId,
55792
+ provider: identity.provider
55488
55793
  };
55489
55794
  let capability;
55490
55795
  if (ctx.principal.kind === "owner") {
55491
55796
  capability = {
55492
55797
  id: deps.ownerPrincipalId,
55493
- displayName: deps.ownerDisplayName,
55798
+ displayName: identity.displayName,
55494
55799
  grants: ctx.grants,
55495
55800
  issuedAt: 0,
55496
55801
  usedCount: 0
@@ -55880,24 +56185,28 @@ function computeGrant(method) {
55880
56185
  }
55881
56186
  function computeMethodAccess(args) {
55882
56187
  const { method, grants, feishuActive, isImplemented } = args;
56188
+ const description = METHOD_DOCS[method].summary;
56189
+ const argFields = methodArgFields(method);
55883
56190
  if (!isImplemented(method)) {
55884
- return { name: method, status: "denied", reason: "METHOD_NOT_IMPLEMENTED" };
56191
+ return { name: method, description, args: argFields, status: "denied", reason: "METHOD_NOT_IMPLEMENTED" };
55885
56192
  }
55886
56193
  if (!feishuActive && FEISHU_GATED_METHODS.includes(method)) {
55887
- return { name: method, status: "denied", reason: "FEISHU_LOGIN_REQUIRED" };
56194
+ return { name: method, description, args: argFields, status: "denied", reason: "FEISHU_LOGIN_REQUIRED" };
55888
56195
  }
55889
56196
  if (METHOD_GRANT_MAP[method]?.kind === "capability-scoped") {
55890
56197
  return {
55891
56198
  name: method,
56199
+ description,
56200
+ args: argFields,
55892
56201
  status: "conditional",
55893
56202
  reason: "handler checks your grants for the target resource"
55894
56203
  };
55895
56204
  }
55896
56205
  const verdict = computeGrant(method);
55897
56206
  if (verdict.kind === "check" && !assertGrant(grants, verdict.resource, verdict.action)) {
55898
- return { name: method, status: "denied", reason: "UNAUTHORIZED" };
56207
+ return { name: method, description, args: argFields, status: "denied", reason: "UNAUTHORIZED" };
55899
56208
  }
55900
- return { name: method, status: "allowed" };
56209
+ return { name: method, description, args: argFields, status: "allowed" };
55901
56210
  }
55902
56211
 
55903
56212
  // src/version.ts
@@ -55973,7 +56282,7 @@ function buildPersonaHandlers(deps) {
55973
56282
  const { personaManager, personaRegistry } = deps;
55974
56283
  const create = async (frame) => {
55975
56284
  const { type: _type, requestId: _requestId, ...rest } = frame;
55976
- const args = PersonaCreateArgsSchema.parse(rest);
56285
+ const args = parseArgs2(PersonaCreateArgsSchema, frame, "persona:create");
55977
56286
  const persona = personaManager.create(args);
55978
56287
  return { response: { type: "persona:info", ...persona } };
55979
56288
  };
@@ -55989,7 +56298,7 @@ function buildPersonaHandlers(deps) {
55989
56298
  };
55990
56299
  };
55991
56300
  const get = async (frame, _client, ctx) => {
55992
- const args = PersonaIdArgsSchema.parse(frame);
56301
+ const args = parseArgs2(PersonaIdArgsSchema, frame, "persona:get");
55993
56302
  const persona = personaRegistry.get(args.personaId) ?? null;
55994
56303
  if (!persona) {
55995
56304
  return { response: { type: "persona:info", persona: null } };
@@ -56018,13 +56327,13 @@ function buildPersonaHandlers(deps) {
56018
56327
  };
56019
56328
  const update = async (frame) => {
56020
56329
  const { type: _type, requestId: _requestId, ...rest } = frame;
56021
- const args = PersonaUpdateArgsSchema.parse(rest);
56330
+ const args = parseArgs2(PersonaUpdateArgsSchema, frame, "persona:update");
56022
56331
  const { personality, ...metaPatch } = args.patch;
56023
56332
  const persona = personaManager.update(args.personaId, metaPatch, personality);
56024
56333
  return { response: { type: "persona:info", ...persona } };
56025
56334
  };
56026
56335
  const del = async (frame) => {
56027
- const args = PersonaIdArgsSchema.parse(frame);
56336
+ const args = parseArgs2(PersonaIdArgsSchema, frame, "persona:get");
56028
56337
  personaManager.delete(args.personaId);
56029
56338
  return {
56030
56339
  response: { type: "persona:deleted", personaId: args.personaId }
@@ -56582,32 +56891,6 @@ async function rollback(deps, localExtId, liveDir, prevDir) {
56582
56891
  }
56583
56892
 
56584
56893
  // src/handlers/extension.ts
56585
- function pickChannelRef(frame) {
56586
- const ref = frame.channelRef;
56587
- const parsed = ChannelRefSchema.safeParse(ref);
56588
- if (!parsed.success) {
56589
- throw new ClawdError(
56590
- ERROR_CODES.INVALID_PARAM,
56591
- `channelRef invalid: ${parsed.error.issues.map((i) => i.message).join("; ")}`
56592
- );
56593
- }
56594
- return parsed.data;
56595
- }
56596
- function pickStringField(frame, name) {
56597
- const v2 = frame[name];
56598
- if (typeof v2 !== "string" || v2.length === 0) {
56599
- throw new ClawdError(ERROR_CODES.INVALID_PARAM, `${name} required`);
56600
- }
56601
- return v2;
56602
- }
56603
- function pickOptionalString(frame, name) {
56604
- const v2 = frame[name];
56605
- if (v2 === void 0 || v2 === null) return null;
56606
- if (typeof v2 !== "string") {
56607
- throw new ClawdError(ERROR_CODES.INVALID_PARAM, `${name} must be string`);
56608
- }
56609
- return v2;
56610
- }
56611
56894
  async function rewriteManifestVersion(root, extId, newVersion, previousPublishedVersion) {
56612
56895
  const v2 = validateSimpleSemver(newVersion);
56613
56896
  if (!v2.ok) {
@@ -56665,13 +56948,6 @@ function assertOwner(ctx) {
56665
56948
  throw new ClawdError(ERROR_CODES.FORBIDDEN, "owner only");
56666
56949
  }
56667
56950
  }
56668
- function pickExtId(frame) {
56669
- const extId = frame.extId;
56670
- if (typeof extId !== "string") {
56671
- throw new ClawdError(ERROR_CODES.INVALID_PARAM, "extId required");
56672
- }
56673
- return extId;
56674
- }
56675
56951
  function composeState(rec3, running, crashed, getPort) {
56676
56952
  if (rec3.state === "invalid") return rec3;
56677
56953
  if (manifestMode(rec3.manifest) === "hosted") {
@@ -56705,19 +56981,19 @@ function buildExtensionHandlers(deps) {
56705
56981
  };
56706
56982
  const open = async (frame, _client, ctx) => {
56707
56983
  assertOwner(ctx);
56708
- const extId = pickExtId(frame);
56984
+ const { extId } = parseArgs2(ExtensionIdArgs, frame, "extension", ERROR_CODES.INVALID_PARAM);
56709
56985
  const target = await deps.runtime.open(extId);
56710
56986
  return { response: target };
56711
56987
  };
56712
56988
  const close = async (frame, _client, ctx) => {
56713
56989
  assertOwner(ctx);
56714
- const extId = pickExtId(frame);
56990
+ const { extId } = parseArgs2(ExtensionIdArgs, frame, "extension", ERROR_CODES.INVALID_PARAM);
56715
56991
  await deps.runtime.close(extId);
56716
56992
  return { response: { ok: true } };
56717
56993
  };
56718
56994
  const uninstall2 = async (frame, _client, ctx) => {
56719
56995
  assertOwner(ctx);
56720
- const extId = pickExtId(frame);
56996
+ const { extId } = parseArgs2(ExtensionIdArgs, frame, "extension", ERROR_CODES.INVALID_PARAM);
56721
56997
  await deps.uninstall({ root: deps.root, extId, runtime: deps.runtime });
56722
56998
  return { response: { ok: true } };
56723
56999
  };
@@ -56728,9 +57004,8 @@ function buildExtensionHandlers(deps) {
56728
57004
  }
56729
57005
  const publish = async (frame, _client, ctx) => {
56730
57006
  assertOwner(ctx);
56731
- const extId = pickExtId(frame);
56732
- const newVersion = pickOptionalString(frame, "newVersion");
56733
- if (newVersion !== null) {
57007
+ const { extId, newVersion } = parseArgs2(ExtensionPublishArgs, frame, "publish", ERROR_CODES.INVALID_PARAM);
57008
+ if (newVersion != null) {
56734
57009
  await rewriteManifestVersion(deps.root, extId, newVersion, deps.publishedChannelStore.get(extId)?.version);
56735
57010
  }
56736
57011
  const { manifest, contentHash, buffer } = await buildSnapshotMeta(extId);
@@ -56774,7 +57049,7 @@ function buildExtensionHandlers(deps) {
56774
57049
  };
56775
57050
  const unpublish = async (frame, _client, ctx) => {
56776
57051
  assertOwner(ctx);
56777
- const extId = pickExtId(frame);
57052
+ const { extId } = parseArgs2(ExtensionIdArgs, frame, "extension", ERROR_CODES.INVALID_PARAM);
56778
57053
  const head = deps.publishedChannelStore.get(extId);
56779
57054
  await deps.publishedChannelStore.delete(extId);
56780
57055
  if (head) {
@@ -56784,7 +57059,7 @@ function buildExtensionHandlers(deps) {
56784
57059
  };
56785
57060
  const publishStatus = async (frame, _client, ctx) => {
56786
57061
  assertOwner(ctx);
56787
- const extId = pickExtId(frame);
57062
+ const { extId } = parseArgs2(ExtensionIdArgs, frame, "extension", ERROR_CODES.INVALID_PARAM);
56788
57063
  const { manifest, contentHash } = await buildSnapshotMeta(extId);
56789
57064
  const head = deps.publishedChannelStore.get(extId);
56790
57065
  const check = computePublishCheck({
@@ -56833,8 +57108,7 @@ function buildExtensionHandlers(deps) {
56833
57108
  if (!ctx || ctx.principal.kind !== "guest") {
56834
57109
  throw new ClawdError(ERROR_CODES.FORBIDDEN, "guest only");
56835
57110
  }
56836
- pickExtId(frame);
56837
- const expectedHash = pickStringField(frame, "snapshotHash");
57111
+ const { snapshotHash: expectedHash } = parseArgs2(ExtensionFetchBundleArgs, frame, "fetchBundle", ERROR_CODES.INVALID_PARAM);
56838
57112
  const cached = await deps.bundleCache.read(expectedHash);
56839
57113
  if (cached === null) {
56840
57114
  throw new ClawdError(
@@ -56846,9 +57120,7 @@ function buildExtensionHandlers(deps) {
56846
57120
  };
56847
57121
  const installFromChannelHandler = async (frame, _client, ctx) => {
56848
57122
  assertOwner(ctx);
56849
- const channelRef = pickChannelRef(frame);
56850
- const snapshotHash = pickStringField(frame, "snapshotHash");
56851
- const zipBase64 = pickStringField(frame, "zipBase64");
57123
+ const { channelRef, snapshotHash, zipBase64 } = parseArgs2(ExtensionFromChannelArgs, frame, "fromChannel", ERROR_CODES.INVALID_PARAM);
56852
57124
  const bundleZip = Buffer.from(zipBase64, "base64");
56853
57125
  try {
56854
57126
  const { extId, installedVersion } = await installFromChannel(
@@ -56868,9 +57140,7 @@ function buildExtensionHandlers(deps) {
56868
57140
  };
56869
57141
  const updateFromChannelHandler = async (frame, _client, ctx) => {
56870
57142
  assertOwner(ctx);
56871
- const channelRef = pickChannelRef(frame);
56872
- const snapshotHash = pickStringField(frame, "snapshotHash");
56873
- const zipBase64 = pickStringField(frame, "zipBase64");
57143
+ const { channelRef, snapshotHash, zipBase64 } = parseArgs2(ExtensionFromChannelArgs, frame, "fromChannel", ERROR_CODES.INVALID_PARAM);
56874
57144
  const bundleZip = Buffer.from(zipBase64, "base64");
56875
57145
  try {
56876
57146
  const result = await updateFromChannel(
@@ -57635,10 +57905,7 @@ function buildAppBuilderHandlers(deps) {
57635
57905
  return { response: { projects: metas.map(toProjectWithStatus) } };
57636
57906
  };
57637
57907
  const getProject = async (frame, _client, ctx) => {
57638
- const f = frame;
57639
- if (typeof f.sessionId !== "string" || !f.sessionId) {
57640
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "getProject: sessionId required");
57641
- }
57908
+ const f = parseArgs2(GetProjectArgsSchema, frame, "getProject");
57642
57909
  if (ctx && !canAccessSession(ctx, f.sessionId, "read", {
57643
57910
  readSession: (sid) => sessionManager.findOwnedSession(sid) ?? null
57644
57911
  })) {
@@ -57653,13 +57920,7 @@ function buildAppBuilderHandlers(deps) {
57653
57920
  return { response: { project: meta ? toProjectWithStatus(meta) : null } };
57654
57921
  };
57655
57922
  const createProject = async (frame, _client, ctx) => {
57656
- const f = frame;
57657
- if (typeof f.sessionId !== "string" || !f.sessionId) {
57658
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "createProject: sessionId required");
57659
- }
57660
- if (typeof f.name !== "string" || !f.name) {
57661
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "createProject: name required");
57662
- }
57923
+ const f = parseArgs2(CreateProjectArgsSchema, frame, "createProject");
57663
57924
  const session = sessionManager.findOwnedSession(f.sessionId);
57664
57925
  if (!session) {
57665
57926
  throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, `session ${f.sessionId} not found`);
@@ -57713,8 +57974,7 @@ function buildAppBuilderHandlers(deps) {
57713
57974
  return { response: { project, projectDir: userStore.projectDir(project.name) }, broadcast };
57714
57975
  };
57715
57976
  const deleteProject = async (frame, _client, ctx) => {
57716
- const name = frame.name;
57717
- if (typeof name !== "string") throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "deleteProject: name required");
57977
+ const { name } = parseArgs2(DeleteProjectArgsSchema, frame, "deleteProject");
57718
57978
  const userStore = resolveStore({ ctx, projectName: name });
57719
57979
  await devServerLifecycle.stopForProject(name);
57720
57980
  await userStore.delete(name);
@@ -57722,9 +57982,7 @@ function buildAppBuilderHandlers(deps) {
57722
57982
  return { response: {} };
57723
57983
  };
57724
57984
  const updateProjectPort = async (frame, _client, ctx) => {
57725
- const f = frame;
57726
- if (typeof f.name !== "string") throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "updateProjectPort: name required");
57727
- if (typeof f.newPort !== "number") throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "updateProjectPort: newPort required");
57985
+ const f = parseArgs2(UpdateProjectPortArgsSchema, frame, "updateProjectPort");
57728
57986
  const userStore = resolveStore({ ctx, projectName: f.name });
57729
57987
  await devServerLifecycle.stopForProject(f.name);
57730
57988
  const project = await userStore.updatePort(f.name, f.newPort);
@@ -57732,9 +57990,7 @@ function buildAppBuilderHandlers(deps) {
57732
57990
  return { response: { project } };
57733
57991
  };
57734
57992
  const setProdUrl = async (frame, _client, ctx) => {
57735
- const f = frame;
57736
- if (typeof f.name !== "string") throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "setProdUrl: name required");
57737
- if (typeof f.url !== "string") throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "setProdUrl: url required");
57993
+ const f = parseArgs2(SetProdUrlArgsSchema, frame, "setProdUrl");
57738
57994
  ensureProjectWriteAccess(ctx, { projectName: f.name });
57739
57995
  const userStore = resolveStore({ ctx, projectName: f.name });
57740
57996
  const project = await userStore.setProdUrl(f.name, f.url);
@@ -57743,10 +57999,7 @@ function buildAppBuilderHandlers(deps) {
57743
57999
  return { response: { project } };
57744
58000
  };
57745
58001
  const startDevServer = async (frame, _client, ctx) => {
57746
- const f = frame;
57747
- if (typeof f.sessionId !== "string" || !f.sessionId) {
57748
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "startDevServer: sessionId required");
57749
- }
58002
+ const f = parseArgs2(StartDevServerArgsSchema, frame, "startDevServer");
57750
58003
  const force = f.force === true;
57751
58004
  ensureProjectWriteAccess(ctx, { sessionId: f.sessionId });
57752
58005
  const session = sessionManager.findOwnedSession(f.sessionId);
@@ -57805,13 +58058,7 @@ function buildAppBuilderHandlers(deps) {
57805
58058
  return { response: { project: meta } };
57806
58059
  };
57807
58060
  const reportStage = async (frame, _client, ctx) => {
57808
- const f = frame;
57809
- if (typeof f.sessionId !== "string" || !f.sessionId) {
57810
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "reportStage: sessionId required");
57811
- }
57812
- if (typeof f.stage !== "string") {
57813
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "reportStage: stage required");
57814
- }
58061
+ const f = parseArgs2(ReportStageArgsSchema, frame, "reportStage");
57815
58062
  if (!ASSISTANT_REPORTABLE_STAGES.includes(f.stage)) {
57816
58063
  throw new ClawdError(
57817
58064
  ERROR_CODES.VALIDATION_ERROR,
@@ -57839,10 +58086,7 @@ function buildAppBuilderHandlers(deps) {
57839
58086
  return { response: { ok: true } };
57840
58087
  };
57841
58088
  const stopDevServer = async (frame, _client, ctx) => {
57842
- const f = frame;
57843
- if (typeof f.sessionId !== "string" || !f.sessionId) {
57844
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, "stopDevServer: sessionId required");
57845
- }
58089
+ const f = parseArgs2(StopDevServerArgsSchema, frame, "stopDevServer");
57846
58090
  ensureProjectWriteAccess(ctx, { sessionId: f.sessionId });
57847
58091
  const session = sessionManager.findOwnedSession(f.sessionId);
57848
58092
  if (!session) {
@@ -57936,8 +58180,8 @@ function buildAppBuilderHandlers(deps) {
57936
58180
  const ownerSender = principalToSender(
57937
58181
  makeOwnerPrincipal(
57938
58182
  deps.ownerPrincipalId,
57939
- deps.ownerDisplayName ?? "Owner",
57940
- deps.ownerFeishuUnionId
58183
+ deps.getOwnerIdentity().displayName || "Owner",
58184
+ deps.getOwnerIdentity().feishuUnionId
57941
58185
  )
57942
58186
  );
57943
58187
  const { broadcast } = sessionManager.send({ sessionId, text }, ownerSender);
@@ -58330,9 +58574,7 @@ function buildMethodHandlers(deps) {
58330
58574
  broadcast: deps.broadcastToOwners,
58331
58575
  now: () => Date.now(),
58332
58576
  ownerPrincipalId: deps.ownerPrincipalId,
58333
- ownerDisplayName: deps.ownerDisplayName,
58334
- ownerId: deps.ownerId,
58335
- ownerProvider: deps.ownerProvider,
58577
+ getOwnerIdentity: deps.getOwnerIdentity,
58336
58578
  exchangeToken: deps.exchangeToken
58337
58579
  }),
58338
58580
  ...buildContactRemoteAccessHandlers({
@@ -58346,10 +58588,8 @@ function buildMethodHandlers(deps) {
58346
58588
  forwardToPeer: (a) => forwardPeerExecToPeer(a)
58347
58589
  }),
58348
58590
  whoami: buildWhoamiHandler({
58349
- ownerDisplayName: deps.ownerDisplayName,
58350
58591
  ownerPrincipalId: deps.ownerPrincipalId,
58351
- ownerId: deps.ownerId,
58352
- ownerProvider: deps.ownerProvider,
58592
+ getOwnerIdentity: deps.getOwnerIdentity,
58353
58593
  personaStore: deps.personaStore
58354
58594
  }),
58355
58595
  ...buildFeishuAuthHandlers(deps.feishuAuth),
@@ -58370,8 +58610,7 @@ function buildMethodHandlers(deps) {
58370
58610
  devServerLifecycle: deps.devServerLifecycle,
58371
58611
  sessionManager: deps.manager,
58372
58612
  ownerPrincipalId: deps.ownerPrincipalId,
58373
- ownerDisplayName: deps.ownerDisplayName,
58374
- ownerFeishuUnionId: deps.ownerFeishuUnionId,
58613
+ getOwnerIdentity: deps.getOwnerIdentity,
58375
58614
  getTunnelUrl: deps.getTunnelUrl,
58376
58615
  broadcastProjectUpdated: deps.broadcastProjectUpdated,
58377
58616
  logger: deps.logger,
@@ -58652,6 +58891,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
58652
58891
  };
58653
58892
 
58654
58893
  // src/handlers/dispatch-rpc.ts
58894
+ init_zod();
58655
58895
  init_protocol();
58656
58896
  async function dispatchRpc(method, frame, client, ctx, deps) {
58657
58897
  if (!METHOD_NAMES.includes(method)) {
@@ -58675,7 +58915,14 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
58675
58915
  );
58676
58916
  }
58677
58917
  }
58678
- return handler(frame, client, ctx);
58918
+ try {
58919
+ return await handler(frame, client, ctx);
58920
+ } catch (err) {
58921
+ if (err instanceof ZodError) {
58922
+ throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, err.message);
58923
+ }
58924
+ throw err;
58925
+ }
58679
58926
  }
58680
58927
 
58681
58928
  // src/extension/runtime.ts
@@ -59111,7 +59358,6 @@ async function startDaemon(config) {
59111
59358
  isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
59112
59359
  ownerPrincipalId,
59113
59360
  ownerDisplayName,
59114
- ownerFeishuUnionId,
59115
59361
  // persona web 分享:访客 visitor token = daemon 自签 HMAC(无 aud),独立于飞书 connect token
59116
59362
  // 基建——secret 恒存在(auth-store backfill),故无条件注入。验签 ok → guest ctx(persona wildcard)。
59117
59363
  verifyVisitorToken: (t) => {
@@ -59383,7 +59629,7 @@ async function startDaemon(config) {
59383
59629
  personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59384
59630
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
59385
59631
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
59386
- getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool,
59632
+ getPersonaTool: (personaId2) => personaRegistry.get(personaId2)?.tool,
59387
59633
  usersRoot,
59388
59634
  personaStore,
59389
59635
  ownerDisplayName,
@@ -59622,217 +59868,219 @@ async function startDaemon(config) {
59622
59868
  const effectivePreviewPorts = Array.from(
59623
59869
  /* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
59624
59870
  );
59625
- const makeHandlers = () => buildMethodHandlers({
59626
- // meta:methods 自省:飞书 gate getter + 最终 handler 表(下面还会 spread 进
59627
- // dispatch / shift-internal 两组,只看 buildMethodHandlers 内部表会误报死方法)
59628
- feishuActive: () => feishuActive,
59629
- getAllHandlers: () => handlers,
59630
- manager,
59631
- workspace,
59632
- skills,
59633
- agents,
59634
- // skills:list tool 路由:codex → app-server skills/list;缺省 claude → SkillsScanner(同步包成 Promise)。
59635
- getSkillsForTool: (tool, cwd) => tool === "codex" ? listCodexSkills(cwd) : Promise.resolve(skills.list({ cwd })),
59636
- history,
59637
- // history:read 按 session.tool 路由:codex → adapter.historyReader(CodexHistoryReader);缺省 claude。
59638
- getHistoryReader: (tool) => getAdapter(tool).historyReader ?? history,
59639
- observer,
59640
- getAdapter,
59641
- store,
59642
- personaManager,
59643
- personaRegistry,
59644
- getTunnelUrl: () => currentTunnelUrl,
59645
- // ready / info 帧的 mode = daemon CC spawn 模式('sdk' | 'tui')。UI 据此挂 XtermPanel +
59646
- // 订阅 session:pty / session:control;业务帧名两种 mode 完全一致,UI 业务订阅代码不变
59647
- mode: config.mode,
59648
- // file-sharing (spec §8):ready / info 帧把 httpBaseUrl + httpToken 下发给 UI。
59649
- // PR 2 阶段 httpToken 复用 owner WS token;noAuth 模式下为 null(UI 看到无 httpToken
59650
- // 时禁用文件 GET/POST,保持 1.0 行为兼容)。
59651
- getHttpBaseUrl,
59652
- httpToken: resolvedAuthToken,
59653
- // file-sharing attachment.* RPC。signUrl owner token 做 HMAC secret;group RPC
59654
- // 根据 sessionId 反查 scope 写盘。
59655
- //
59656
- // sessionStore / getSessionScope 都走 manager 的跨 scope 公开 APIfindOwnedSession /
59657
- // findOwnedSessionScope)—— 否则 default-only SessionStore 找不到 persona owner-mode
59658
- // session,attachment 整套 RPC 对 persona session 都会报 "session not found"。
59659
- // 详见 fix(daemon) #703:file-sharing v1 预存 wiring bug,#701 把 desktop 默认 --tunnel
59660
- // 后首次让 desktop 用户用上 file-sharing 才被暴露。
59661
- attachment: {
59662
- groupFileStore,
59663
- sessionStore: { read: (sid) => manager.findOwnedSession(sid) },
59871
+ const getOwnerIdentity = () => ({
59872
+ displayName: ownerDisplayName,
59873
+ ownerId,
59874
+ provider: ownerProvider,
59875
+ feishuUnionId: ownerFeishuUnionId
59876
+ });
59877
+ const handlers = {
59878
+ ...buildMethodHandlers({
59879
+ // meta:methods 自省:飞书 gate getter + 最终 handler 表(下面还会 spread 进
59880
+ // dispatch / shift-internal 两组,只看 buildMethodHandlers 内部表会误报死方法)
59881
+ feishuActive: () => feishuActive,
59882
+ getAllHandlers: () => handlers,
59883
+ manager,
59884
+ workspace,
59885
+ skills,
59886
+ agents,
59887
+ // skills:list 按 tool 路由:codex → app-server skills/list;缺省 claude → SkillsScanner(同步包成 Promise)。
59888
+ getSkillsForTool: (tool, cwd) => tool === "codex" ? listCodexSkills(cwd) : Promise.resolve(skills.list({ cwd })),
59889
+ history,
59890
+ // history:read 按 session.tool 路由:codex → adapter.historyReader(CodexHistoryReader);缺省 claude。
59891
+ getHistoryReader: (tool) => getAdapter(tool).historyReader ?? history,
59892
+ observer,
59893
+ getAdapter,
59894
+ store,
59895
+ personaManager,
59896
+ personaRegistry,
59897
+ getTunnelUrl: () => currentTunnelUrl,
59898
+ // ready / info 帧的 mode = daemon CC spawn 模式('sdk' | 'tui')。UI 据此挂 XtermPanel +
59899
+ // 订阅 session:pty / session:control;业务帧名两种 mode 完全一致,UI 业务订阅代码不变
59900
+ mode: config.mode,
59901
+ // file-sharing (spec §8):ready / info 帧把 httpBaseUrl + httpToken 下发给 UI。
59902
+ // PR 2 阶段 httpToken 复用 owner WS token;noAuth 模式下为 nullUI 看到无 httpToken
59903
+ // 时禁用文件 GET/POST,保持 1.0 行为兼容)。
59664
59904
  getHttpBaseUrl,
59665
- // HMAC sign secret = auth.json 唯一 token(authFile 恒非空,startDaemon 开头加载)。
59666
- getSignSecret: () => authFile.token,
59667
- // group RPC + sign 都用:根据 sessionId 反查 scope。owner-mode persona session 走
59668
- // 'persona/<pid>/owner',default 走 'default'。
59669
- getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59670
- // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59905
+ httpToken: resolvedAuthToken,
59906
+ // file-sharing attachment.* RPC。signUrl 用 owner token 做 HMAC secret;group RPC
59907
+ // 根据 sessionId 反查 scope 写盘。
59908
+ //
59909
+ // sessionStore / getSessionScope 都走 manager 的跨 scope 公开 API(findOwnedSession /
59910
+ // findOwnedSessionScope)—— 否则 default-only SessionStore 找不到 persona owner-mode
59911
+ // session,attachment 整套 RPC 对 persona session 都会报 "session not found"。
59912
+ // 详见 fix(daemon) #703:file-sharing v1 预存 wiring bug,#701 把 desktop 默认 --tunnel
59913
+ // 后首次让 desktop 用户用上 file-sharing 才被暴露。
59914
+ attachment: {
59915
+ groupFileStore,
59916
+ sessionStore: { read: (sid) => manager.findOwnedSession(sid) },
59917
+ getHttpBaseUrl,
59918
+ // HMAC sign secret = auth.json 唯一 token(authFile 恒非空,startDaemon 开头加载)。
59919
+ getSignSecret: () => authFile.token,
59920
+ // group RPC + sign 都用:根据 sessionId 反查 scope。owner-mode persona session 走
59921
+ // 'persona/<pid>/owner',default 走 'default'。
59922
+ getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59923
+ // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59924
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59925
+ usersRoot
59926
+ },
59927
+ // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59671
59928
  personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59672
- usersRoot
59673
- },
59674
- // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59675
- personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59676
- // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
59677
- usersRoot,
59678
- // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
59679
- ownerDisplayName,
59680
- // 决策 #16: whoami owner.id = 本机 deviceId(路由主键)+ ownerId/provider 归属人
59681
- ownerPrincipalId,
59682
- // per-turn sender §4.6:send handler 无 ctx 兜底 + AppBuilder publish injectUserText 需要
59683
- ownerFeishuUnionId,
59684
- ownerId,
59685
- ownerProvider,
59686
- personaStore,
59687
- // capability platform v3 inbox: handler manager (post/list/markRead),
59688
- // cascade 接 store (list 统计 deletedInboxEvents).
59689
- inboxManager,
59690
- inboxStore,
59691
- // 联系人列表 store(device:connect 出站 / contact:handshake 入站落同一 store)
59692
- contactStore,
59693
- // contact:handshake 换票回填(spec 2026-07-22):用 owner 当前 jwt 换 aud=对方设备 的票。
59694
- // 现读 ownerIdentityStore(热切换后用新身份);失败 / 未登录 → null → 保持空票。
59695
- exchangeToken: async (targetDeviceId) => {
59696
- const record = ownerIdentityStore.read();
59697
- if (!record) return null;
59698
- try {
59699
- const r = await centralExchange({
59700
- api: config.clawosApi ?? DEFAULT_CLAWOS_API,
59701
- jwt: record.ttcToken,
59702
- provider: record.identity.provider,
59703
- deviceId: targetDeviceId,
59704
- selfDeviceId: authFile.deviceId
59705
- });
59706
- return r.token;
59707
- } catch (e) {
59708
- logger.warn("contact:handshake exchange token failed", {
59709
- targetDeviceId,
59710
- err: e.message
59711
- });
59712
- return null;
59713
- }
59714
- },
59715
- // inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
59716
- getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59717
- // contact:removed broadcast;复用 capability:tokenIssued 同款通路
59718
- broadcastToOwners: (frame) => wsServer?.broadcastToOwners(frame),
59719
- // extension runtime (v1: local-mode only). Instance owned by daemon top
59720
- // level so stopAll() runs in the shutdown hook below.
59721
- extensionRuntime,
59722
- // extension sharing v1 — owner-side published-channels store.
59723
- publishedChannelStore,
59724
- bundleCache,
59725
- // 飞书统一身份 Phase 1:登录 RPC handlers(auth:login:start / getIdentity / logout)
59726
- // deviceId(决策 #15):getIdentity 返回给 UI 拼"我的设备标识"
59727
- feishuAuth: {
59728
- loginFlow,
59729
- ownerIdentityStore,
59730
- deviceId: authFile.deviceId,
59731
- serverKeyStore,
59732
- // 退出登录:登录(feishuLogin.handleLoginCallback)在进程内设置的身份状态的对称逆操作。
59733
- // 核心是 feishuActive 翻 false——否则 dispatcher 飞书 gate(下方 feishuActive 判定,每帧
59734
- // 现读 let binding)退登后仍放行 gated 方法。一并把归属人重置回未登录初值 + 重建 handlers
59735
- // (deps 持 ownerId/displayName 值快照,whoami/出站自报身份用),与登录置位三件套对称。
59736
- // 不踢连:踢连是登录热切换换身份 ctx 的需要;退登只是降级,gate 每帧现读即时生效,
59737
- // 既有连接继续用,避免改动连接生命周期 / wire 契约。
59738
- onLogout: () => {
59739
- feishuActive = false;
59740
- ownerId = "";
59741
- ownerProvider = "";
59742
- ownerDisplayName = loadOwnerDisplayName(config.dataDir);
59743
- handlers = makeHandlers();
59744
- }
59745
- },
59746
- // 设备目录 + 连接(决策 #15:daemon 代理中心 server)。
59747
- // exchange/listDevices 包掉 owner ttcJwt(每次现读 store——热切换后立即用新身份);
59748
- // dispatcher 的 FEISHU_GATED_METHODS gate 已保证未登录时这些 RPC 到不了 handler,
59749
- // 这里的 FEISHU_LOGIN_REQUIRED 抛错只是防御纵深。
59750
- feishuDevice: {
59751
- store: contactStore,
59752
- exchange: async (deviceId) => {
59929
+ // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
59930
+ usersRoot,
59931
+ // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
59932
+ getOwnerIdentity,
59933
+ // 决策 #16: whoami owner.id = 本机 deviceId(路由主键,const,热切换不变)
59934
+ ownerPrincipalId,
59935
+ personaStore,
59936
+ // capability platform v3 inbox: handler 接 manager (post/list/markRead),
59937
+ // cascade store (list 统计 deletedInboxEvents).
59938
+ inboxManager,
59939
+ inboxStore,
59940
+ // 联系人列表 store(device:connect 出站 / contact:handshake 入站落同一 store)
59941
+ contactStore,
59942
+ // contact:handshake 换票回填(spec 2026-07-22):用 owner 当前 jwt 换 aud=对方设备 的票。
59943
+ // 现读 ownerIdentityStore(热切换后用新身份);失败 / 未登录 → null → 保持空票。
59944
+ exchangeToken: async (targetDeviceId) => {
59753
59945
  const record = ownerIdentityStore.read();
59754
- if (!record) {
59755
- throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "device:connect requires feishu login");
59756
- }
59757
- return centralExchange({
59758
- api: config.clawosApi ?? DEFAULT_CLAWOS_API,
59759
- jwt: record.ttcToken,
59760
- provider: record.identity.provider,
59761
- deviceId,
59762
- selfDeviceId: authFile.deviceId
59763
- });
59946
+ if (!record) return null;
59947
+ try {
59948
+ const r = await centralExchange({
59949
+ api: config.clawosApi ?? DEFAULT_CLAWOS_API,
59950
+ jwt: record.ttcToken,
59951
+ provider: record.identity.provider,
59952
+ deviceId: targetDeviceId,
59953
+ selfDeviceId: authFile.deviceId
59954
+ });
59955
+ return r.token;
59956
+ } catch (e) {
59957
+ logger.warn("contact:handshake exchange token failed", {
59958
+ targetDeviceId,
59959
+ err: e.message
59960
+ });
59961
+ return null;
59962
+ }
59764
59963
  },
59765
- listDevices: async () => {
59766
- const record = ownerIdentityStore.read();
59767
- if (!record) {
59768
- throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "device:list requires feishu login");
59964
+ // inbox:sendDm 用:sessionId session 出身(复用 attachment 同款 findOwnedSessionScope)
59965
+ getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59966
+ // contact:removed broadcast;复用 capability:tokenIssued 同款通路
59967
+ broadcastToOwners: (frame) => wsServer?.broadcastToOwners(frame),
59968
+ // extension runtime (v1: local-mode only). Instance owned by daemon top
59969
+ // level so stopAll() runs in the shutdown hook below.
59970
+ extensionRuntime,
59971
+ // extension sharing v1 — owner-side published-channels store.
59972
+ publishedChannelStore,
59973
+ bundleCache,
59974
+ // 飞书统一身份 Phase 1:登录 RPC handlers(auth:login:start / getIdentity / logout)
59975
+ // deviceId(决策 #15):getIdentity 返回给 UI 拼"我的设备标识"
59976
+ feishuAuth: {
59977
+ loginFlow,
59978
+ ownerIdentityStore,
59979
+ deviceId: authFile.deviceId,
59980
+ serverKeyStore,
59981
+ // 退出登录:登录(feishuLogin.handleLoginCallback)在进程内设置的身份状态的对称逆操作。
59982
+ // 核心是 feishuActive 翻 false——否则 dispatcher 飞书 gate(下方 feishuActive 判定,每帧
59983
+ // 现读 let binding)退登后仍放行 gated 方法。一并把归属人重置回未登录初值 + 重建 handlers
59984
+ // (deps 持 ownerId/displayName 值快照,whoami/出站自报身份用),与登录置位三件套对称。
59985
+ // 不踢连:踢连是登录热切换换身份 ctx 的需要;退登只是降级,gate 每帧现读即时生效,
59986
+ // 既有连接继续用,避免改动连接生命周期 / wire 契约。
59987
+ onLogout: () => {
59988
+ feishuActive = false;
59989
+ ownerId = "";
59990
+ ownerProvider = "";
59991
+ ownerDisplayName = loadOwnerDisplayName(config.dataDir);
59769
59992
  }
59770
- return centralListDevices({
59771
- api: config.clawosApi ?? DEFAULT_CLAWOS_API,
59772
- ttcJwt: record.ttcToken,
59773
- provider: record.identity.provider
59774
- });
59775
59993
  },
59776
- connectRemote,
59777
- broadcast: (frame) => wsServer?.broadcastToOwners(frame),
59778
- // 自动反向(spec 决策 #11):本机可达地址 = tunnel URL(公网),出站连接时
59779
- // 透传让对方反向加我为联系人。无 tunnel(仅本机 ws)→ null,对方不自动反向(不造假数据)。
59780
- // 决策 #16:身份不自报——本机 deviceId/ownerId/provider 由 exchange 签进 token 背书。
59781
- selfUrl: () => currentTunnelUrl
59782
- },
59783
- // app-builder
59784
- appBuilderStore,
59785
- getProjectStore,
59786
- devServerLifecycle: devServerSupervisor,
59787
- // 单栏 refactor §5.6.2 stage fix:appBuilder:project-updated 帧通过 wsServer broadcast。
59788
- // 推给所有 owner 连接(picker 可能没订阅特定 session,全局 broadcast 最稳)。
59789
- // people/mirror(spec 2026-06-04-people-mode-app-builder-write-and-push):额外推给订阅该 project
59790
- // 绑定 session 的 client(mirror guest 经 session:observe 订阅),让远端协同者也看到 stage / 发布进度。
59791
- // 同一帧 owner 也走 project-updated 复用给 publish-progress/failed(app-builder.ts 内 broadcast 闭包):
59792
- // name 在 event.project.name(project-updated)或 event.name(publish 帧)。owner 双栏会同时命中
59793
- // owner-broadcast + session-broadcast,但 UI patch 幂等(同 project 覆盖)无害。
59794
- broadcastProjectUpdated: (event) => {
59795
- const ev = event;
59796
- wsServer?.broadcastToOwners(ev);
59797
- const projectName = event.project?.name ?? event.name;
59798
- const boundSid = projectName ? manager.findOwnedSessionByAppBuilderProject(projectName)?.sessionId : void 0;
59799
- if (boundSid) wsServer?.broadcastToSession(boundSid, ev);
59800
- },
59801
- // 发布上线脚手架化 (spec 2026-06-03 §5.2):
59802
- // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
59803
- // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
59804
- appBuilderPersonaRoot: import_node_path62.default.join(config.dataDir, "personas", "persona-app-builder"),
59805
- // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
59806
- deployKitRoot: import_node_path62.default.join(config.dataDir, "deploy-kit"),
59807
- // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
59808
- resolvePersonaRoot: (personaId) => import_node_path62.default.join(config.dataDir, "personas", personaId),
59809
- // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
59810
- // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
59811
- // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
59812
- broadcastSessionFrame: (frame, target) => {
59813
- if (target === "all") {
59814
- transport?.broadcastAll(frame);
59815
- return;
59816
- }
59817
- const sid = frame.sessionId;
59818
- if (!sid) return;
59819
- if (target === "first-subscriber") {
59820
- const handle = transport?.firstSubscriber(sid);
59821
- if (handle) handle.send(frame);
59822
- return;
59823
- }
59824
- transport?.broadcastToSession(sid, frame);
59825
- },
59826
- logger,
59827
- // shift v1:shift:list 桥接 handler 透传 ShiftStore 单例(同实例也喂给
59828
- // shift-internal:* MCP loopback handler)。owner 过滤用 ownerPrincipalId(跨
59829
- // daemon 稳定身份),跟我们 ShiftStore boot 时用的 provider 一致。
59830
- shiftStore,
59831
- shiftOwnerIdProvider: () => ownerPrincipalId,
59832
- // persona web 分享:visitor:list owner RPC 读访客名单(同 exchange upsert 的实例)
59833
- visitorStore
59834
- });
59835
- let handlers = makeHandlers();
59994
+ // 设备目录 + 连接(决策 #15:daemon 代理中心 server)。
59995
+ // exchange/listDevices 包掉 owner ttcJwt(每次现读 store——热切换后立即用新身份);
59996
+ // dispatcher FEISHU_GATED_METHODS gate 已保证未登录时这些 RPC 到不了 handler,
59997
+ // 这里的 FEISHU_LOGIN_REQUIRED 抛错只是防御纵深。
59998
+ feishuDevice: {
59999
+ store: contactStore,
60000
+ exchange: async (deviceId) => {
60001
+ const record = ownerIdentityStore.read();
60002
+ if (!record) {
60003
+ throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "device:connect requires feishu login");
60004
+ }
60005
+ return centralExchange({
60006
+ api: config.clawosApi ?? DEFAULT_CLAWOS_API,
60007
+ jwt: record.ttcToken,
60008
+ provider: record.identity.provider,
60009
+ deviceId,
60010
+ selfDeviceId: authFile.deviceId
60011
+ });
60012
+ },
60013
+ listDevices: async () => {
60014
+ const record = ownerIdentityStore.read();
60015
+ if (!record) {
60016
+ throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "device:list requires feishu login");
60017
+ }
60018
+ return centralListDevices({
60019
+ api: config.clawosApi ?? DEFAULT_CLAWOS_API,
60020
+ ttcJwt: record.ttcToken,
60021
+ provider: record.identity.provider
60022
+ });
60023
+ },
60024
+ connectRemote,
60025
+ broadcast: (frame) => wsServer?.broadcastToOwners(frame),
60026
+ // 自动反向(spec 决策 #11):本机可达地址 = tunnel URL(公网),出站连接时
60027
+ // 透传让对方反向加我为联系人。无 tunnel(仅本机 ws)→ null,对方不自动反向(不造假数据)。
60028
+ // 决策 #16:身份不自报——本机 deviceId/ownerId/provider exchange 签进 token 背书。
60029
+ selfUrl: () => currentTunnelUrl
60030
+ },
60031
+ // app-builder
60032
+ appBuilderStore,
60033
+ getProjectStore,
60034
+ devServerLifecycle: devServerSupervisor,
60035
+ // 单栏 refactor §5.6.2 stage fix:appBuilder:project-updated 帧通过 wsServer broadcast。
60036
+ // 推给所有 owner 连接(picker 可能没订阅特定 session,全局 broadcast 最稳)。
60037
+ // people/mirror(spec 2026-06-04-people-mode-app-builder-write-and-push):额外推给订阅该 project
60038
+ // 绑定 session 的 client(mirror guest 经 session:observe 订阅),让远端协同者也看到 stage / 发布进度。
60039
+ // 同一帧 owner 也走 project-updated 复用给 publish-progress/failed(app-builder.ts 内 broadcast 闭包):
60040
+ // name 在 event.project.name(project-updated)或 event.name(publish 帧)。owner 双栏会同时命中
60041
+ // owner-broadcast + session-broadcast,但 UI patch 幂等(同 project 覆盖)无害。
60042
+ broadcastProjectUpdated: (event) => {
60043
+ const ev = event;
60044
+ wsServer?.broadcastToOwners(ev);
60045
+ const projectName = event.project?.name ?? event.name;
60046
+ const boundSid = projectName ? manager.findOwnedSessionByAppBuilderProject(projectName)?.sessionId : void 0;
60047
+ if (boundSid) wsServer?.broadcastToSession(boundSid, ev);
60048
+ },
60049
+ // 发布上线脚手架化 (spec 2026-06-03 §5.2):
60050
+ // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
60051
+ // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
60052
+ appBuilderPersonaRoot: import_node_path62.default.join(config.dataDir, "personas", "persona-app-builder"),
60053
+ // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
60054
+ deployKitRoot: import_node_path62.default.join(config.dataDir, "deploy-kit"),
60055
+ // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
60056
+ resolvePersonaRoot: (personaId2) => import_node_path62.default.join(config.dataDir, "personas", personaId2),
60057
+ // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
60058
+ // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
60059
+ // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
60060
+ broadcastSessionFrame: (frame, target) => {
60061
+ if (target === "all") {
60062
+ transport?.broadcastAll(frame);
60063
+ return;
60064
+ }
60065
+ const sid = frame.sessionId;
60066
+ if (!sid) return;
60067
+ if (target === "first-subscriber") {
60068
+ const handle = transport?.firstSubscriber(sid);
60069
+ if (handle) handle.send(frame);
60070
+ return;
60071
+ }
60072
+ transport?.broadcastToSession(sid, frame);
60073
+ },
60074
+ logger,
60075
+ // shift v1:shift:list 桥接 handler 透传 ShiftStore 单例(同实例也喂给
60076
+ // shift-internal:* MCP loopback handler)。owner 过滤用 ownerPrincipalId(跨
60077
+ // daemon 稳定身份),跟我们 ShiftStore boot 时用的 provider 一致。
60078
+ shiftStore,
60079
+ shiftOwnerIdProvider: () => ownerPrincipalId,
60080
+ // persona web 分享:visitor:list owner RPC 读访客名单(同 exchange upsert 的实例)
60081
+ visitorStore
60082
+ })
60083
+ };
59836
60084
  const dispatchHandlers = buildPersonaDispatchHandlers({
59837
60085
  personaDispatchManager,
59838
60086
  store: dispatchStore,
@@ -59899,16 +60147,16 @@ async function startDaemon(config) {
59899
60147
  });
59900
60148
  },
59901
60149
  // B 角色:判断 targetPersona 是否 public(跨设备授权边界,private 拒)。
59902
- getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false,
60150
+ getPersonaPublic: (personaId2) => personaRegistry.get(personaId2)?.public ?? false,
59903
60151
  // model 校验按 target persona 绑定 tool 选表(claude / codex 模型集不同)
59904
- getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool
60152
+ getPersonaTool: (personaId2) => personaRegistry.get(personaId2)?.tool
59905
60153
  });
59906
- handlers = { ...handlers, ...dispatchHandlers };
60154
+ Object.assign(handlers, dispatchHandlers);
59907
60155
  const shiftHandlers = buildShiftInternalHandlers({
59908
60156
  store: shiftStore,
59909
60157
  ownerIdProvider: () => ownerPrincipalId
59910
60158
  });
59911
- handlers = { ...handlers, ...shiftHandlers };
60159
+ Object.assign(handlers, shiftHandlers);
59912
60160
  const shiftSpawnFn = createShiftSpawnFn({ sessionManager: manager });
59913
60161
  const shiftScheduler = createShiftScheduler({
59914
60162
  store: shiftStore,
@@ -59987,7 +60235,6 @@ async function startDaemon(config) {
59987
60235
  ownerProvider = result.identity.provider;
59988
60236
  ownerDisplayName = result.identity.displayName;
59989
60237
  ownerFeishuUnionId = result.identity.unionId;
59990
- handlers = makeHandlers();
59991
60238
  wsServer?.broadcastToOwners({ type: "auth:login:done", identity: result.identity });
59992
60239
  setImmediate(() => wsServer?.closeAllClients());
59993
60240
  void reportDevice();