dsh-deeppilot 0.9.11 → 0.9.14

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/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["DEFAULT_CAPACITY","reject","reject","reject","reject","reject","reject","reject","fsConstants","cryptoSign","alert","http2Connect","createHttpServer","createHttpsServer","record","httpsRequest"],"sources":["../src/wire-errors.ts","../src/connection-policy.ts","../src/auth-rate-limit.ts","../src/diagnostics.ts","../src/device-auth.ts","../src/wire-interaction.ts","../src/wire-device.ts","../src/token.ts","../src/wire-push.ts","../src/wire-schedule.ts","../src/document-payload.ts","../src/host-event-projection.ts","../src/wire-session.ts","../src/wire-command.ts","../src/wire-registry.ts","../src/connection-gate.ts","../src/connection.ts","../src/funnel-policy.ts","../src/remote-supervisor.ts","../src/local-policy.ts","../src/config.ts","../src/apns.ts","../src/relay-url.ts","../src/relay-client.ts","../src/widget-push.ts","../src/live-activity.ts","../src/push-policy.ts","../src/relay-test.ts","../src/push-gateway.ts","../src/phone-server.ts","../src/transport-reconciler.ts","../src/dispatch-journal.ts","../src/notification-text.ts","../src/host-api.ts","../src/host-capabilities.ts","../src/host-bridge.ts","../src/dsh-remote-interactions.ts","../src/command-api.ts","../src/dsh-api-proxy.ts","../src/report-service.ts","../src/report-wire.ts","../src/report-remote.ts","../src/local-address.ts","../src/update-check.ts","../src/phone-http.ts","../src/lan-tls.ts","../src/index.ts"],"sourcesContent":["/**\n * 错误词表(error vocabulary)的唯一属主。\n *\n * 一条失败信息在系统里要过三种词汇:Host 控制器返回的错误码(`session-not-found`、\n * `schedule_conflict`……)、Bridge 结果里的状态、以及 wire 上的 `E_*` 码。\n * 此前这三层各有映射函数(host-bridge 里 5 个、connection-policy 里 2 个、\n * connection.ts 的 switch 里 5 处内联三元链),一处漂移就会让客户端对\n * 「该不该重试」的判断失真。\n *\n * 本 module 只做一次翻译:每个域一张「Host code -> wire code」表,\n * Bridge 的结果直接携带 wire 码。`E_BUSY` 与 `E_PROTOCOL` 决定客户端是否\n * 重试,因此这张表是 wire 行为的一部分,逐码都有测试把守。\n *\n * 规范见 PROTOCOL.md 错误码表(`E_PROTOCOL` = 未知类型或非法 payload,\n * `E_FORBIDDEN` = 权限不足,`E_UNSUPPORTED` = 能力缺失)。\n */\n\n/** wire 错误码及其规范描述;PROTOCOL.md 错误码表的 TS 镜像。 */\nexport const ERROR_CODES = {\n E_AUTH: 'device proof missing or invalid',\n E_FORBIDDEN: 'device scope does not allow this operation',\n E_PROTOCOL: 'unknown type or malformed payload',\n E_NOT_FOUND: 'session or request not found',\n E_BUSY: 'session is busy',\n E_UNSUPPORTED: 'protocol version or capability unsupported',\n E_INTERNAL: 'internal error',\n} as const\n\nexport type WireErrorCode = keyof typeof ERROR_CODES\n\n/** Bridge 未携带 code 的结果(内部守卫失败等)落到这一档。 */\nexport const WIRE_ERROR_FALLBACK: WireErrorCode = 'E_INTERNAL'\n\n/**\n * 每个域一张「Host code -> wire code」表。查不到的 Host code 归\n * `E_INTERNAL`:未知的 Host 失败对手机没有可执行含义,重试只会放大故障。\n *\n * 表的取值与迁移前的行为逐码一致(见 tests/wire-errors.test.ts 的对等部分),\n * 即此前各映射链的并集——包括 models 域把 `model-unavailable` 映到\n * `E_NOT_FOUND` 的现行取值。\n *\n * 键写的是 DSH 实际抛出的**带命名空间**的码(旧 host 的扁平写法作为兼容别名\n * 逐条保留,它们同时充当「命名空间换了但尾部没换」时的兜底——查表会先原样命中,\n * 再剥前缀命中,见 lookupCode)。\n *\n * schedule 域例外:定时任务服务(`@deepseek-ai/dsh-schedule`)至今仍抛扁平\n * snake_case(`schedule_not_found`),不要给它加命名空间。\n */\nexport type ErrorDomain = 'session' | 'model' | 'schedule' | 'command'\n\nconst DOMAIN_TABLES: Record<ErrorDomain, Readonly<Record<string, WireErrorCode>>> = {\n // 会话/工作区/目录管理域的 Host 错误码。\n session: {\n 'session/not-found': 'E_NOT_FOUND',\n 'workspace/not-found': 'E_NOT_FOUND',\n 'session/agent-busy': 'E_BUSY',\n // writer-held 与 agent-busy 同类:会话正被别人占着写,稍后重试有意义。\n 'session/writer-held': 'E_BUSY',\n 'session/conflict': 'E_BUSY',\n 'workspace/session-active': 'E_BUSY',\n 'gateway/bad-request': 'E_PROTOCOL',\n 'session/title-invalid': 'E_PROTOCOL',\n 'session/attachment-invalid': 'E_PROTOCOL',\n 'session/invalid-time-zone': 'E_PROTOCOL',\n 'workspace/invalid-path': 'E_PROTOCOL',\n 'workspace/name-conflict': 'E_PROTOCOL',\n 'workspace/move-invalid': 'E_PROTOCOL',\n 'workspace-file/not-found': 'E_NOT_FOUND',\n 'directory-picker/unavailable': 'E_UNSUPPORTED',\n // 旧 host 的扁平写法:保留是为了兼容仍在跑的老 DSH。\n 'session-not-found': 'E_NOT_FOUND',\n 'workspace-not-found': 'E_NOT_FOUND',\n 'agent-busy': 'E_BUSY',\n 'session-conflict': 'E_BUSY',\n 'title-invalid': 'E_PROTOCOL',\n 'workspace-invalid-path': 'E_PROTOCOL',\n 'workspace-name-conflict': 'E_PROTOCOL',\n 'directory-unreadable': 'E_PROTOCOL',\n 'directory-exists': 'E_PROTOCOL',\n 'directory-create-failed': 'E_PROTOCOL',\n 'directory-picker-unavailable': 'E_UNSUPPORTED',\n },\n // 模型目录与切换域的 Host 错误码。\n model: {\n 'session/not-found': 'E_NOT_FOUND',\n 'session/agent-busy': 'E_BUSY',\n 'session/writer-held': 'E_BUSY',\n 'session/conflict': 'E_BUSY',\n 'session/model-unavailable': 'E_NOT_FOUND',\n // 旧 host 的扁平写法。\n 'session-not-found': 'E_NOT_FOUND',\n 'agent-busy': 'E_BUSY',\n 'session-conflict': 'E_BUSY',\n 'model-unavailable': 'E_NOT_FOUND',\n },\n // 定时任务域的 Host 错误码(snake_case 是 Host Schedule 服务的词表,至今未变)。\n schedule: {\n 'schedule_not_found': 'E_NOT_FOUND',\n 'delivery_cursor_not_found': 'E_NOT_FOUND',\n 'schedule_conflict': 'E_BUSY',\n 'invalid_prompt': 'E_PROTOCOL',\n 'invalid_selector': 'E_PROTOCOL',\n 'invalid_rule': 'E_PROTOCOL',\n 'invalid_time_zone': 'E_PROTOCOL',\n 'not_future': 'E_PROTOCOL',\n 'time_out_of_range': 'E_PROTOCOL',\n 'frequency_too_high': 'E_PROTOCOL',\n 'schedule_ended': 'E_PROTOCOL',\n // DSH 0.2.1-alpha.1 新增:目标会话属于子代理路由,投递永远到不了它,\n // Host 对 create/update 拒绝(delete 仍放行以便清理旧数据)。\n // 归 E_PROTOCOL 而非 E_UNSUPPORTED:能力本身在宿主上存在,是这次请求\n // 的目标不可满足,重试无意义。与其余「参数/时序不合法」类拒绝同档。\n 'subagent_session': 'E_PROTOCOL',\n },\n // 手机指令域的 Host 错误码。这一域一半是插件在 command-api.ts 自己造的码\n // (`command/not-found` 等),一半是上游 Gateway 的原样拒绝(`gateway/*`、\n // `session/*`)。收进同一张表,理由与别的域相同:HostBridge.commandOperation\n // 此前把整域无条件压成 E_INTERNAL,手机端于是分不清「这个指令不存在」和\n // 「主机内部错误」,只能显示一句通用失败,客户端也没法据此分支。\n command: {\n 'command/not-found': 'E_NOT_FOUND',\n // 目录过期:用户选中的定义已被替换或删除。归 E_NOT_FOUND 与既有\n // `model-unavailable` 同惯例——目标不可用,重试无意义;客户端应重拉目录后\n // 让用户重选,而不是按 E_BUSY 去自动重试。\n 'command/definition-changed': 'E_NOT_FOUND',\n // 该指令不接受附件:请求参数与指令自身的声明不符,与其余 payload 类拒绝同档。\n 'command/attachments-unsupported': 'E_PROTOCOL',\n // 主机没有生成归档,或归档内容类型不符:能力在,是这一次失败,重试有意义。\n 'command/export-unavailable': 'E_INTERNAL',\n // 导出被取消或超时:没有上游码可依,保持 E_INTERNAL 的语义。\n 'command/export-cancelled': 'E_INTERNAL',\n // 主机连命令 API 都没挂载:能力缺失,客户端不该按「稍后重试」处理。\n 'command/export-unsupported': 'E_UNSUPPORTED',\n // 主机返回的目录/执行/反馈结果不合约定的形状:主机侧违约,客户端修不了。\n 'command/invalid-response': 'E_INTERNAL',\n // 主机没挂载权限预设服务:能力缺失,与 directory-picker/unavailable 同档。\n 'command/permissions-unsupported': 'E_UNSUPPORTED',\n // 上游 Gateway 在指令路径上的原样拒绝。\n 'gateway/bad-request': 'E_PROTOCOL',\n 'session/not-found': 'E_NOT_FOUND',\n 'session/agent-busy': 'E_BUSY',\n 'session/writer-held': 'E_BUSY',\n 'session/conflict': 'E_BUSY',\n // 旧 host 的扁平写法。\n 'session-not-found': 'E_NOT_FOUND',\n 'agent-busy': 'E_BUSY',\n 'session-conflict': 'E_BUSY',\n },\n}\n\n/**\n * 一个 Host 失败在 Bridge 侧的形状。\n *\n * `details` 是 DSH 的 `RemoteError` 附带的结构化载荷(dsh-typert-protocol 的\n * `remote-error.d.ts`),键由错误码决定;prompt 准入那一路的 `reason` 装的就是\n * 被包装掉的真实原因。窄化成只读 `reason` 是因为**只有**这一路会用到它。\n */\nexport interface HostFailure {\n code: string\n message?: string\n readonly details?: { readonly reason?: unknown } | undefined\n}\n\n/**\n * 剥掉 `namespace/` 前缀:`session/agent-busy` -> `agent-busy`。\n *\n * DSH 的 API 层现在抛的是带命名空间的码(`session/agent-busy`、\n * `workspace/not-found`、`gateway/internal`……),而下面的表一开始只有扁平写法。\n * 两边对不上时整表失配、一律落进 `E_INTERNAL`,于是 `E_BUSY` 永远出不来——\n * 而 `E_BUSY` 正是客户端决定「该不该重试」的那个码(issue #26)。\n */\nexport function canonicalHostCode(code: string): string {\n const slash = code.lastIndexOf('/')\n return slash === -1 ? code : code.slice(slash + 1)\n}\n\n/** 查表顺序:原样命中优先,其次剥掉命名空间后命中,最后才是兜底。 */\nfunction lookupCode(domain: ErrorDomain, code: string): WireErrorCode {\n const table = DOMAIN_TABLES[domain]\n return table[code] ?? table[canonicalHostCode(code)] ?? WIRE_ERROR_FALLBACK\n}\n\n/** 一个 Host 失败翻译成 Bridge 的失败结果:直接携带 wire 错误码。 */\nexport function wireErrorOf(\n domain: ErrorDomain,\n error: HostFailure,\n): { ok: false; code: WireErrorCode; message: string } {\n const code = lookupCode(domain, error.code)\n return {\n ok: false,\n code,\n message: describeFailure(error, code),\n }\n}\n\n/**\n * 把任意抛出物收敛成 `HostFailure`,供 `catch` 分支复用同一张域表。\n *\n * 指令链路里有两种失败来源:`DshApiProxy.call` 把插件的失败码放在\n * `{result:{ok:false,error}}` 里正常返回,而导出的归档流是直接抛出的\n * (`AbortError`、主机侧 `Error` 都算)。把「取形状」和「查表」分开,是为了让\n * 两条路都只经一次翻译;没有 code 的抛出物用调用方给的 fallback,避免把\n * `undefined` 当码送进 lookupCode 而落进无法解释的兜底。\n */\nexport function hostFailureOf(error: unknown, fallbackCode: string): HostFailure {\n const value = error as { code?: unknown; message?: unknown } | null | undefined\n const code = typeof value?.code === 'string' && value.code.length > 0 ? value.code : fallbackCode\n const message = typeof value?.message === 'string' && value.message.length > 0 ? value.message : undefined\n return message === undefined ? { code } : { code, message }\n}\n\n/**\n * 组装给手机看的那句话。\n *\n * DSH 把 prompt 准入的**任意**内部失败包成 `session/agent-busy`,真实原因只留在\n * `details.reason`(`@deepseek-ai/dsh-api-session-controller` 的 `types/commands.js`\n * 里那一句 `throw new RemoteError('session/agent-busy', 'prompt rejected', { reason: … })`)。\n * 插件原先只取 `message`,于是手机端永远只显示笼统的「发送未确认」。\n *\n * 只在落到 `E_INTERNAL` 时追加 reason:这类码按定义对客户端没有可执行含义,\n * 多这一句不改变「该不该重试」的判断,却能让用户和排查者看到真正的原因。已映射\n * 的码本身已经可执行,保持原样不动。\n */\nfunction describeFailure(error: HostFailure, wireCode: WireErrorCode): string {\n const base = error.message ?? error.code\n if (wireCode !== WIRE_ERROR_FALLBACK) return base\n const reason = error.details?.reason\n if (typeof reason !== 'string' || reason.length === 0) return base\n return `${base} (${error.code}): ${reason}`\n}\n\n/** 只有一个 Host code(没有附带 message)时取它的 wire 码。 */\nexport function wireCodeFor(domain: ErrorDomain, hostCode: string | undefined): WireErrorCode {\n if (hostCode === undefined) return WIRE_ERROR_FALLBACK\n return lookupCode(domain, hostCode)\n}\n\n/**\n * 回答待决 approval/question 的三种结局。保留三分而不是压成布尔:\n * 「没有这条待决」与「Host 拒收了答案」必须让客户端分得清,否则手机上\n * 任何拒绝都显示成 question not pending。\n */\nexport type PendingResponseReason = 'not-pending' | 'bad-response' | 'transport'\n\nexport function pendingResponseErrorCode(reason: PendingResponseReason): WireErrorCode {\n switch (reason) {\n case 'not-pending': return 'E_NOT_FOUND'\n // Host 拒收了答案批次(形状/标签不匹配)——客户端载荷问题,不是待决消失。\n case 'bad-response': return 'E_PROTOCOL'\n case 'transport': return 'E_INTERNAL'\n }\n}\n\n/** 与 pendingResponseErrorCode 配套的可读信息,同一处维护。 */\nexport function pendingResponseMessage(\n kind: 'approval' | 'question',\n reason: PendingResponseReason,\n): string {\n switch (reason) {\n case 'not-pending': return kind + ' not pending'\n case 'bad-response': return kind + ' answer rejected by host: answer does not match the asked questions'\n case 'transport': return 'host connection failed while answering ' + kind\n }\n}\n","/**\n * 连接层常量与载荷清洗。\n *\n * 本 module 的职责已经收窄:scope 表随 wire 注册表(每帧一行)迁到\n * wire-registry,错误码映射随错误词表迁到 wire-errors,请求形状校验随行内\n * validate 迁到各特性模块。剩下的都是与帧类型无关的东西——资源上限、\n * 清洗函数、envelope 形状守卫、下行广播权限。\n */\n\nimport type { DeviceScope } from './device-auth.ts'\nimport type { Envelope } from './protocol.ts'\n\nexport const AUTH_TIMEOUT_MS = 35_000\nexport const MAX_OUTBOUND_BUFFER_BYTES = 4 * 1024 * 1024\n/**\n * Pre-auth frame cap. The 64 MiB cap on a fully authenticated socket exists\n * to support multi-MB image attachments; before hello the only legal frames\n * are c2s.ping and c2s.auth.prove, neither of which can legitimately exceed\n * a few KB. Capping unauthenticated frames at 64 KiB keeps an anonymous TCP\n * peer from forcing expensive JSON.parse work on a 64 MiB payload inside\n * the 5-second auth window.\n */\nexport const PRE_AUTH_FRAME_BYTES = 64 * 1024\nexport const IMAGE_MEDIA_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif'])\nexport const MAX_PROMPT_IMAGES = 4\nexport const MAX_BASE64_CHARS_PER_IMAGE = 8 * 1024 * 1024\nexport const MAX_PROMPT_DOCUMENTS = 4\nexport const MAX_DOCUMENT_TEXT_CHARS = 256 * 1024\nexport const MAX_DOCUMENT_NAME_CHARS = 180\nexport const MAX_DOCUMENT_MEDIA_TYPE_CHARS = 120\n/** Bounds a single prompt's text; the frame itself is capped by ws maxPayload. */\nexport const MAX_PROMPT_TEXT_CHARS = 256 * 1024\n// Client-supplied identity fields land in logs and devices-v2.json — keep them\n// short and free of control characters so they can neither flood the registry\n// nor forge log lines.\nexport const MAX_DEVICE_ID_CHARS = 128\nexport const MAX_DEVICE_NAME_CHARS = 64\nexport const MAX_APP_VERSION_CHARS = 32\n\n/**\n * clientSendId / clientRequestId 的形态:13 位纪元前缀 + UUID。\n * wire 层据此校验载荷,journal 侧据此守卫(见 dispatch-journal.ts)。\n */\nexport function validSendId(id: unknown): id is string {\n return typeof id === 'string' && /^\\d{13}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)\n}\n\nexport function sanitizeDeviceField(value: unknown, maxChars: number): string {\n const raw = typeof value === 'string' ? value : String(value ?? '')\n return raw.replace(/[\\u0000-\\u001f\\u007f]/g, ' ').trim().slice(0, maxChars)\n}\n\n/**\n * Runtime shape guard for a frame after JSON.parse. The old `as Envelope`\n * cast alone let JSON `null` reach `env.v` and let a missing or mistyped\n * `type` reach the registry lookup's string operations — one anonymous frame\n * could crash the host process. Reject anything that is not a plain object\n * with a numeric version and a non-empty string type, so field access below\n * is always safe. The payload is intentionally opaque here; each row\n * validates its own payload shape (see the per-row `validate` in\n * wire-registry.ts).\n */\nexport function isEnvelope(value: unknown): value is Envelope {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const frame = value as Record<string, unknown>\n if (typeof frame.v !== 'number') return false\n if (typeof frame.type !== 'string' || frame.type.length === 0) return false\n if (frame.id !== undefined && typeof frame.id !== 'string') return false\n if (frame.ts !== undefined && typeof frame.ts !== 'number') return false\n if (frame.seq !== undefined && typeof frame.seq !== 'number') return false\n return true\n}\n\n/**\n * 下行广播与重放授权。未知帧类型 fail-closed:不下发。\n *\n * `s2c.session.tail` 与 `s2c.history.page` 是 PROTOCOL.md 下行权限表里的类型,\n * 但由连接点对点直推(不经 record()/重放环)。条目保留:将来任何把它们送进\n * ring 的路径都会自动受到这里的 gate 约束,而不是静默全量广播。\n */\nconst PUSH_SCOPE_BY_TYPE: Partial<Record<string, DeviceScope>> = {\n 's2c.session.event': 'sessions.read',\n 's2c.skills.changed': 'sessions.read',\n 's2c.commands.changed': 'sessions.read',\n 's2c.models.changed': 'sessions.read',\n 's2c.sessions.delta': 'sessions.read',\n 's2c.session.tail': 'sessions.read',\n 's2c.history.page': 'sessions.read',\n 's2c.pending.approval': 'interactions.respond',\n 's2c.pending.question': 'interactions.respond',\n 's2c.pending.cleared': 'interactions.respond',\n 's2c.schedule.changed': 'schedule.manage',\n}\n\nexport function pushScopeFor(type: string, payload?: unknown): DeviceScope | undefined {\n const fixed = PUSH_SCOPE_BY_TYPE[type]\n if (fixed !== undefined) return fixed\n if (type === 's2c.notify') {\n const category = (payload as { category?: unknown } | undefined)?.category\n if (category === 'approval.required' || category === 'question.asked') return 'interactions.respond'\n if (category === 'turn.completed' || category === 'session.error') return 'sessions.read'\n }\n return undefined\n}\n\nexport function sanitizeImageName(value: string): string {\n return value.replace(/[\\u0000-\\u001F\\u007F]/g, '').trim().slice(0, 120)\n}\n\nexport function sanitizeDocumentField(value: string, maxChars: number): string {\n return value.replace(/[\\u0000-\\u001F\\u007F]/g, ' ').trim().slice(0, maxChars)\n}\n","export interface AuthRatePolicy {\n windowMs: number\n attemptsPerSource: number\n globalAttempts: number\n maxUnauthenticatedPerSource: number\n failureWindowMs: number\n failuresBeforeBlock: number\n blockMs: number\n maxSources: number\n}\n\nexport const DEFAULT_AUTH_RATE_POLICY: AuthRatePolicy = {\n windowMs: 60_000,\n attemptsPerSource: 12,\n globalAttempts: 120,\n maxUnauthenticatedPerSource: 2,\n failureWindowMs: 10 * 60_000,\n failuresBeforeBlock: 5,\n blockMs: 15 * 60_000,\n maxSources: 4_096,\n}\n\ninterface SourceState {\n attempts: number[]\n failures: number[]\n blockedUntil: number\n active: number\n lastSeen: number\n}\n\nexport interface AuthAdmission {\n ok: boolean\n retryAfterMs: number\n release: () => void\n}\n\nexport interface AuthFailureResult {\n blocked: boolean\n newlyBlocked: boolean\n retryAfterMs: number\n}\n\nconst noop = (): void => {}\n\n/** Bounded in-memory protection for anonymous authentication attempts. */\nexport class AuthRateLimiter {\n private readonly sources = new Map<string, SourceState>()\n private globalAttempts: number[] = []\n\n constructor(private readonly policy: AuthRatePolicy = DEFAULT_AUTH_RATE_POLICY) {}\n\n admit(source: string, now = Date.now()): AuthAdmission {\n const state = this.source(source, now)\n if (state === null) return { ok: false, retryAfterMs: this.policy.windowMs, release: noop }\n this.prune(state, now)\n state.lastSeen = now\n\n if (state.blockedUntil > now) {\n return { ok: false, retryAfterMs: state.blockedUntil - now, release: noop }\n }\n if (state.active >= this.policy.maxUnauthenticatedPerSource) {\n return { ok: false, retryAfterMs: this.policy.windowMs, release: noop }\n }\n if (state.attempts.length >= this.policy.attemptsPerSource) {\n return { ok: false, retryAfterMs: state.attempts[0]! + this.policy.windowMs - now, release: noop }\n }\n this.globalAttempts = this.globalAttempts.filter((ts) => ts > now - this.policy.windowMs)\n if (this.globalAttempts.length >= this.policy.globalAttempts) {\n return { ok: false, retryAfterMs: this.globalAttempts[0]! + this.policy.windowMs - now, release: noop }\n }\n\n state.attempts.push(now)\n this.globalAttempts.push(now)\n state.active += 1\n let released = false\n return {\n ok: true,\n retryAfterMs: 0,\n release: () => {\n if (released) return\n released = true\n state.active = Math.max(0, state.active - 1)\n },\n }\n }\n\n recordFailure(source: string, now = Date.now()): AuthFailureResult {\n const state = this.source(source, now)\n if (state === null) return { blocked: true, newlyBlocked: false, retryAfterMs: this.policy.blockMs }\n this.prune(state, now)\n state.lastSeen = now\n const wasBlocked = state.blockedUntil > now\n state.failures.push(now)\n if (state.failures.length >= this.policy.failuresBeforeBlock) {\n state.blockedUntil = Math.max(state.blockedUntil, now + this.policy.blockMs)\n }\n return {\n blocked: state.blockedUntil > now,\n newlyBlocked: !wasBlocked && state.blockedUntil > now,\n retryAfterMs: Math.max(0, state.blockedUntil - now),\n }\n }\n\n recordSuccess(source: string, now = Date.now()): void {\n const state = this.sources.get(source)\n if (state === undefined) return\n state.failures = []\n state.blockedUntil = 0\n state.lastSeen = now\n }\n\n private source(source: string, now: number): SourceState | null {\n const existing = this.sources.get(source)\n if (existing !== undefined) return existing\n if (this.sources.size >= this.policy.maxSources) this.pruneSources(now)\n if (this.sources.size >= this.policy.maxSources) return null\n const state: SourceState = { attempts: [], failures: [], blockedUntil: 0, active: 0, lastSeen: now }\n this.sources.set(source, state)\n return state\n }\n\n private prune(state: SourceState, now: number): void {\n state.attempts = state.attempts.filter((ts) => ts > now - this.policy.windowMs)\n state.failures = state.failures.filter((ts) => ts > now - this.policy.failureWindowMs)\n if (state.blockedUntil <= now) state.blockedUntil = 0\n }\n\n private pruneSources(now: number): void {\n const staleBefore = now - Math.max(this.policy.failureWindowMs, this.policy.blockMs)\n for (const [source, state] of this.sources) {\n this.prune(state, now)\n if (state.active === 0 && state.blockedUntil === 0 && state.lastSeen < staleBefore) {\n this.sources.delete(source)\n }\n }\n }\n}\n","/**\n * 连接诊断事件模型(Diagnostics)——插件唯一的「失败可观测」面。\n *\n * 为什么需要独立模块:本插件的日志出口原先是 `apply()` 里的一个闭包\n * (`console.log('[deeppilot] ' + message)`),无时间戳、无级别、无来源;\n * 连接被拒、认证失败、TLS 握手失败都各自静默结束,用户在设置页只看到\n * 「连不上」三个字。把事件模型、节流、环形缓冲与落盘收在这里,目的有三:\n *\n * 1. 一次失败 = 一条结构化事件(时间戳 + kind + reason + 来源哈希 +\n * 可选 attemptId / 关闭码 / 耗时),微信、tailscale、扫描器刷屏时仍\n * 只留可读的十几行;\n * 2. index.ts / connection-gate.ts / phone-server.ts / connection.ts 四个\n * 入口共用同一个 `record(kind, reason, extra)`,失败路径不再各自发明\n * 日志文案;\n * 3. 环形 256 条 + 按天落盘(保留 7 天、目录上限 5MB)让「刚刚那次连不上」\n * 在重启前后都能查;`recent()` / `lastFailure()` 同时供 `/phone/health`\n * 与设置页诊断区块读取。\n *\n * 硬约束(与 AGENTS.md 一致):\n * - 事件里只出现 auditLabel 产生的来源哈希,绝不出现原始 IP;\n * - attemptId 只记录、不校验、不参与任何准入判断;长度 ≤16,字符集\n * `[A-Za-z0-9_-]`,来源不可信(WS query `at` / HTTP header\n * `X-DeepPilot-Attempt`);\n * - 不打印配对码、公钥原文、APNs token、relay 凭据与消息正文。detail\n * 字段只接受代码生成的短文本(如 socket 错误码),最长 160 字符。\n *\n * 与 `diagnostics.debug` 的关系:本模块的事件**无条件记录**。那个开关只\n * 控制现有的 per-frame / 推送日志,失败事件若也绑到它上面,用户不开调试\n * 开关就永远看不到为什么连不上——这正是本轮要修的 P0。\n */\n\nimport { appendFile, mkdir, readdir, stat, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n// ---------- 日志行 ----------\n\n/** 日志级别:失败一律 warn,成功与状态变更 info。 */\nexport type LogLevel = 'info' | 'warn'\n\n/**\n * 统一日志行格式:`[deeppilot] <ISO 时间戳> <LEVEL> <message>`。\n *\n * 时间戳是 P0 的可观测性基线:没有它,用户贴回来的日志无法与手机端时间\n * 对齐,也无法判断「刚才那次」是哪一次。ISO-8601(UTC)避免时区误读。\n */\nexport function formatLogLine(level: LogLevel, message: string, at: number = Date.now()): string {\n return `[deeppilot] ${new Date(at).toISOString()} ${level.toUpperCase()} ${message}`\n}\n\n/**\n * 插件日志函数。单参调用即 info;失败路径显式传 'warn'。\n * 返回值的双参签名可以安全地传给只接受 `(message: string) => void` 的\n * 既有依赖(PushGateway / HostBridge / 两个 transport 行)。\n */\nexport function createLogger(): (message: string, level?: LogLevel) => void {\n return (message: string, level: LogLevel = 'info'): void => {\n console.log(formatLogLine(level, message))\n }\n}\n\n// ---------- 事件模型 ----------\n\n/** 事件类型:覆盖一次握手从传输层到认证层的全部可失败点。 */\nexport type DiagnosticKind =\n /** WS upgrade 在建立 socket 之前被拒(禁用、名额、降级、限流)。 */\n | 'upgrade-rejected'\n /** HTTP 配对请求被拒(码无效、注册表满、限流)。 */\n | 'pair-rejected'\n /** c2s.auth.prove 失败(4401 分因)或 hello 超时(4402)。 */\n | 'auth-failed'\n /** 一条 WS 连接建立成功。 */\n | 'connection-opened'\n /** 非正常关闭(关闭码不是 1000/1001)。 */\n | 'connection-closed'\n /** socket 层 error(ws.on('error') / TLS client error / HTTP clientError)。 */\n | 'connection-error'\n /** Host 身份 / LAN TLS 身份的新建、重建或读取异常。 */\n | 'identity'\n /** 设备注册表落盘失败。 */\n | 'storage'\n\n/** 一条诊断事件。字段刻意保持扁平、可 JSON 序列化。 */\nexport interface DiagnosticEvent {\n /** 事件发生时刻(Unix ms)。 */\n ts: number\n kind: DiagnosticKind\n /** 分因枚举,形如 `nonce-mismatch` / `too-many-connections`;代码内常量。 */\n reason: string\n /** 客户端自带的尝试 id(仅用于串联一次握手的多条日志)。 */\n attemptId?: string\n /** 来源的审计哈希(auditLabel),不是原始 IP。 */\n source?: string\n /** WS 关闭码;只在有 socket 的事件上出现。 */\n closeCode?: number\n /** 从建连到关闭的存活时长(毫秒)。 */\n durationMs?: number\n /** 关闭时这条连接是否已通过认证。 */\n authenticated?: boolean\n /** 代码生成的短说明(socket 错误码等),已去控制字符并截断。 */\n detail?: string\n /** 日志级别;不进入 report wire,只用于本地筛选「最近失败」。 */\n level: LogLevel\n}\n\n/** record 的可选附加信息。 */\nexport interface DiagnosticExtra {\n attemptId?: string\n source?: string\n closeCode?: number\n durationMs?: number\n authenticated?: boolean\n detail?: string\n /** 覆盖默认级别:成功事件(连接建立、host-id 新建)用 'info'。 */\n level?: LogLevel\n}\n\n/**\n * 四个入口(index / connection-gate / phone-server / connection)共用的\n * 最小记录面。依赖方只拿这个接口,单测可以用一个收数组的假实现替换整个\n * 落盘与节流机制。\n */\nexport type DiagnosticsRecorder = Pick<DiagnosticsStore, 'record'>\n\nconst MAX_DETAIL_CHARS = 160\nconst MAX_REASON_CHARS = 64\nconst DEFAULT_CAPACITY = 256\n/** 同 kind + reason + source 在 60 秒内只输出一行。 */\nconst DEFAULT_THROTTLE_MS = 60_000\n/** 日志目录总量上限:超过后停止落盘,但内存环形与终端日志继续工作。 */\nexport const MAX_DIAGNOSTICS_BYTES = 5 * 1024 * 1024\n/** 按天滚动文件保留的日历天数(含今天)。 */\nexport const DIAGNOSTICS_RETENTION_DAYS = 7\n\nconst INFO_KINDS: ReadonlySet<DiagnosticKind> = new Set(['connection-opened'])\n\nfunction defaultLevel(kind: DiagnosticKind): LogLevel {\n return INFO_KINDS.has(kind) ? 'info' : 'warn'\n}\n\n/** 事件文本一律单行:换成空格并去掉控制字符,避免日志被注入换行。 */\nfunction sanitizeText(value: string, maxChars: number): string {\n return value\n .replace(/[\\u0000-\\u001f\\u007f-\\u009f]+/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, maxChars)\n}\n\nfunction normalizeReason(reason: string): string {\n // reason 是代码内常量;仍然收敛一次,防止未来有人把上游错误消息塞进来。\n return sanitizeText(reason, MAX_REASON_CHARS).replace(/\\s+/g, '-')\n}\n\n/**\n * 来源只允许短标识(auditLabel 的 12 位十六进制)。原始 IP 含 `.` / `:`,\n * 会在这一步被丢弃——即便某个调用方忘了哈希,也不会有 IP 落进日志。\n */\nfunction normalizeSource(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n return /^[A-Za-z0-9_-]{1,64}$/.test(value) ? value : undefined\n}\n\n// ---------- attemptId(不可信输入) ----------\n\nexport const ATTEMPT_ID_HEADER = 'x-deeppilot-attempt'\n/** 只允许 URL 安全字符,最长 16:记录用,永不回显或参与判定。 */\nconst ATTEMPT_ID_PATTERN = /^[A-Za-z0-9_-]{1,16}$/\n\n/** 归一化一个客户端提供的 attemptId;不合法即丢弃(不报错、不拒绝请求)。 */\nexport function normalizeAttemptId(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n return ATTEMPT_ID_PATTERN.test(value) ? value : undefined\n}\n\n/**\n * 从一条 HTTP / WS 升级请求里取 attemptId。\n *\n * 优先 HTTP header(真实 HTTP 客户端可自由设置),其次 WS 升级 URL 的\n * `?at=`(浏览器 / URLSession 的 WS 握手无法自定义 header)。两个来源都\n * 经过同一份长度与字符集校验;任何异常(畸形 URL、数组头)都按「没有\n * attemptId」处理——这是纯观测字段,绝不能因为它让握手失败。\n */\nexport function attemptIdFromRequest(request: {\n url?: string | undefined\n headers?: Readonly<Record<string, string | string[] | undefined>> | undefined\n}): string | undefined {\n const header = request.headers?.[ATTEMPT_ID_HEADER]\n const fromHeader = normalizeAttemptId(Array.isArray(header) ? header[0] : header)\n if (fromHeader !== undefined) return fromHeader\n if (typeof request.url !== 'string' || request.url.length === 0) return undefined\n try {\n return normalizeAttemptId(new URL(request.url, 'http://phone.local').searchParams.get('at'))\n } catch {\n return undefined\n }\n}\n\n// ---------- 事件存储 ----------\n\nexport interface DiagnosticsOptions {\n /** 落盘目录(插件数据目录下的 diagnostics/);缺省时只保留内存环形。 */\n dir?: string\n /** 环形缓冲上限,默认 256。 */\n capacity?: number\n /** 同 kind+reason+source 的日志节流窗口,默认 60s。 */\n throttleMs?: number\n /** 时钟注入(测试用)。 */\n now?: () => number\n /** 日志输出注入(测试用);缺省 console.log。 */\n write?: (line: string) => void\n}\n\ninterface ThrottleEntry {\n lastTs: number\n /** 上次输出之后被抑制的事件数;恢复输出时带在行尾。 */\n suppressed: number\n}\n\n/**\n * 内存环形 + 节流日志 + 按天落盘。\n *\n * 职责边界:它不做任何策略判断(不决定是否拒绝连接),只负责把调用方\n * 已经判定的失败事实记下来。因此它可以在四个入口之间安全共用。\n */\nexport class DiagnosticsStore {\n private readonly events: DiagnosticEvent[] = []\n private readonly throttles = new Map<string, ThrottleEntry>()\n private readonly capacity: number\n private readonly throttleMs: number\n private readonly now: () => number\n private readonly write: (line: string) => void\n private readonly dir: string | undefined\n private tail: Promise<void> = Promise.resolve()\n /** 已统计过的文件大小,避免每次 append 前都 stat。 */\n private readonly fileSizes = new Map<string, number>()\n private directoryBytes = 0\n /** 最近一次执行过保留期清理的 UTC 日期(YYYY-MM-DD)。 */\n private prunedDay: string | undefined\n /** 落盘自身失败只报一次:否则磁盘满会把日志刷成新的刷屏源。 */\n private writeFailureReported = false\n\n constructor(options: DiagnosticsOptions = {}) {\n this.capacity = options.capacity ?? DEFAULT_CAPACITY\n this.throttleMs = options.throttleMs ?? DEFAULT_THROTTLE_MS\n this.now = options.now ?? Date.now\n this.write = options.write ?? ((line: string) => { console.log(line) })\n this.dir = options.dir\n }\n\n /**\n * 记录一条事件。事件总是进入内存环形;终端日志与落盘按\n * kind + reason + source 节流(60s 一行,恢复时标注 suppressed 次数)。\n * 返回完整事件,方便调用方测试与断言。\n */\n record(kind: DiagnosticKind, reason: string, extra: DiagnosticExtra = {}): DiagnosticEvent {\n const ts = this.now()\n // attemptId 来自客户端:在这里再收敛一次,任何入口传进来的非法值都丢弃。\n const attemptId = normalizeAttemptId(extra.attemptId)\n const source = normalizeSource(extra.source)\n const event: DiagnosticEvent = {\n ts,\n kind,\n reason: normalizeReason(reason),\n level: extra.level ?? defaultLevel(kind),\n ...(attemptId !== undefined ? { attemptId } : {}),\n ...(source !== undefined ? { source } : {}),\n ...(extra.closeCode !== undefined ? { closeCode: extra.closeCode } : {}),\n ...(extra.durationMs !== undefined ? { durationMs: Math.max(0, Math.round(extra.durationMs)) } : {}),\n ...(extra.authenticated !== undefined ? { authenticated: extra.authenticated } : {}),\n ...(extra.detail !== undefined ? { detail: sanitizeText(extra.detail, MAX_DETAIL_CHARS) } : {}),\n }\n\n this.events.push(event)\n while (this.events.length > this.capacity) this.events.shift()\n\n const throttleKey = `${kind}\\u0000${event.reason}\\u0000${event.source ?? '-'}`\n const previous = this.throttles.get(throttleKey)\n let suppressed = 0\n if (previous !== undefined && ts - previous.lastTs < this.throttleMs) {\n previous.suppressed += 1\n return event\n }\n if (previous !== undefined) suppressed = previous.suppressed\n this.throttles.set(throttleKey, { lastTs: ts, suppressed: 0 })\n\n this.write(formatEventLine(event, suppressed))\n this.persist(event, suppressed)\n return event\n }\n\n /** 最近 `limit` 条事件(时间升序),供 health / report / 测试读取。 */\n recent(limit = 20): DiagnosticEvent[] {\n const size = Math.max(0, Math.trunc(limit))\n return this.events.slice(-size)\n }\n\n /** 最近 `limit` 条失败事件(warn 级),新→旧排序前的升序切片。 */\n recentFailures(limit = 10): DiagnosticEvent[] {\n const failures = this.events.filter((event) => event.level === 'warn')\n const size = Math.max(0, Math.trunc(limit))\n return failures.slice(-size)\n }\n\n /** 最近一条失败事件;没有失败时 undefined。 */\n lastFailure(): DiagnosticEvent | undefined {\n for (let index = this.events.length - 1; index >= 0; index -= 1) {\n const event = this.events[index]\n if (event !== undefined && event.level === 'warn') return event\n }\n return undefined\n }\n\n /** 等价于「诊断面板复制」/health 需要的最近失败摘要。 */\n lastFailureSummary(): Pick<DiagnosticEvent, 'kind' | 'reason' | 'ts'> | undefined {\n const failure = this.lastFailure()\n return failure === undefined ? undefined : { kind: failure.kind, reason: failure.reason, ts: failure.ts }\n }\n\n /** 等待所有排队中的落盘完成(测试与进程收尾用)。 */\n async flush(): Promise<void> {\n await this.tail\n }\n\n /** 进程收尾:不再有新的写入队列,等待既有写入落盘。 */\n async dispose(): Promise<void> {\n await this.flush()\n }\n\n // ---------- 落盘 ----------\n\n /**\n * 追加一行 JSONL。写入串行、失败静默(只报一次),绝不阻塞调用方——\n * 诊断日志不能反过来拖慢或搞崩握手路径。\n */\n private persist(event: DiagnosticEvent, suppressed: number): void {\n const dir = this.dir\n if (dir === undefined) return\n const day = utcDay(event.ts)\n const path = join(dir, `diagnostics-${day}.jsonl`)\n const payload = suppressed > 0 ? { ...event, suppressed } : event\n const line = JSON.stringify(payload) + '\\n'\n const bytes = Buffer.byteLength(line)\n\n this.tail = this.tail.then(async () => {\n try {\n await this.prepareDirectory(dir, day)\n const knownSize = this.fileSizes.get(path)\n const currentSize = knownSize ?? await stat(path).then((info) => info.size).catch(() => 0)\n this.fileSizes.set(path, currentSize)\n if (this.directoryBytes + bytes > MAX_DIAGNOSTICS_BYTES) return\n await mkdir(dir, { recursive: true, mode: 0o700 })\n await appendFile(path, line, { mode: 0o600 })\n this.fileSizes.set(path, currentSize + bytes)\n this.directoryBytes += bytes\n } catch (error) {\n this.reportWriteFailure(error)\n }\n })\n this.tail = this.tail.catch(() => {})\n }\n\n /**\n * 每天第一次落盘时清理保留期之外的文件并统计目录大小;同时限制目录\n * 总量(5MB)。清理失败不影响当次写入。\n */\n private async prepareDirectory(dir: string, day: string): Promise<void> {\n if (this.prunedDay === day) return\n const names = await readdir(dir).catch(() => [] as string[])\n const cutoffDay = utcDayMinus(day, DIAGNOSTICS_RETENTION_DAYS - 1)\n let total = 0\n for (const name of names) {\n const match = /^diagnostics-(\\d{4}-\\d{2}-\\d{2})\\.jsonl$/.exec(name)\n if (match === null) continue\n const full = join(dir, name)\n if (match[1] !== undefined && match[1] < cutoffDay) {\n await unlink(full).catch(() => {})\n this.fileSizes.delete(full)\n continue\n }\n const size = await stat(full).then((info) => info.size).catch(() => 0)\n this.fileSizes.set(full, size)\n total += size\n }\n this.directoryBytes = total\n this.prunedDay = day\n }\n\n private reportWriteFailure(error: unknown): void {\n if (this.writeFailureReported) return\n this.writeFailureReported = true\n this.write(formatLogLine('warn', 'diagnostics write failed: ' + sanitizeText(String(error), MAX_DETAIL_CHARS)))\n }\n}\n\n/** 一条事件的终端日志文本(不含前缀时间戳,由 formatLogLine 统一加)。 */\nfunction formatEventLine(event: DiagnosticEvent, suppressed: number): string {\n const parts: string[] = [event.kind, event.reason]\n if (event.source !== undefined) parts.push('src=' + event.source)\n if (event.attemptId !== undefined) parts.push('attempt=' + event.attemptId)\n if (event.closeCode !== undefined) parts.push('code=' + String(event.closeCode))\n if (event.durationMs !== undefined) parts.push('durationMs=' + String(event.durationMs))\n if (event.authenticated !== undefined) parts.push('auth=' + (event.authenticated ? 'yes' : 'no'))\n if (suppressed > 0) parts.push('suppressed=' + String(suppressed))\n if (event.detail !== undefined) parts.push('detail=' + event.detail)\n return formatLogLine(event.level, parts.join(' '), event.ts)\n}\n\nfunction utcDay(ts: number): string {\n return new Date(ts).toISOString().slice(0, 10)\n}\n\n/** 日期字符串(YYYY-MM-DD)往前推 `days` 天;保留期计算用。 */\nfunction utcDayMinus(day: string, days: number): string {\n const date = new Date(day + 'T00:00:00.000Z')\n date.setUTCDate(date.getUTCDate() - days)\n return date.toISOString().slice(0, 10)\n}\n\n// ---------- /phone/health 载荷 ----------\n\nexport interface HealthPayloadInput {\n /** 插件 apply() 的启动时刻。 */\n startedAt: number\n enabled: boolean\n protocolVersion: number\n serverVersion: string\n /** 数据面(HostBridge)是否已经挂上。 */\n dataPlaneActive: boolean\n /** LAN 传输相位与端口;来自 TransportReconciler.status()。 */\n localPhase: string\n localPort: number\n /** 本次进程是否重建过 LAN TLS 身份。 */\n tlsIdentityRegenerated: boolean\n /** 最近一次失败;没有则为 undefined。 */\n lastFailure: Pick<DiagnosticEvent, 'kind' | 'reason' | 'ts'> | undefined\n}\n\n/**\n * `/phone/health` 的响应体:**只加不删**。\n *\n * 原有 `ok/enabled/protocolVersion/serverVersion/dataPlane` 保持原义;\n * 新增字段只描述「这台电脑此刻能不能被连上」,不含 debug 开关状态,\n * 也不返回局域网地址候选(那是配对页的职责,health 不该成为扫描器\n * 的地址字典):\n * - `local`:LAN 传输此刻的相位与端口;\n * - `dataPlanePhase`:数据面 active / inactive;\n * - `tlsRebuilt`:本次运行是否重建过 TLS 身份(手机据此判断是否需要重新配对);\n * - `startedAt`:插件启动时刻;\n * - `lastFailure`:最近一次失败的类型、分因与时间。\n */\nexport function buildHealthPayload(input: HealthPayloadInput): Record<string, unknown> {\n return {\n ok: true,\n enabled: input.enabled,\n protocolVersion: input.protocolVersion,\n serverVersion: input.serverVersion,\n dataPlane: input.dataPlaneActive,\n dataPlanePhase: input.dataPlaneActive ? 'active' : 'inactive',\n local: { phase: input.localPhase, port: input.localPort },\n tlsRebuilt: input.tlsIdentityRegenerated,\n startedAt: input.startedAt,\n lastFailure: input.lastFailure === undefined\n ? null\n : { kind: input.lastFailure.kind, reason: input.lastFailure.reason, ts: input.lastFailure.ts },\n }\n}\n","import {\n createHash,\n createPublicKey,\n randomBytes,\n timingSafeEqual,\n verify,\n type KeyObject,\n} from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { formatLogLine } from './diagnostics.ts'\n\nexport function getPairingCodeTtlMs(): number {\n return Number(process.env.DEEPPILOT_PAIRING_TTL_MS) || 5 * 60_000\n}\nexport const AUTH_CHALLENGE_TTL_MS = 30_000\n\nexport const DEVICE_SCOPES = [\n 'sessions.read',\n 'prompt.send',\n 'sessions.manage',\n 'interactions.respond',\n 'notifications.register',\n 'schedule.manage',\n] as const\n\nexport type DeviceScope = (typeof DEVICE_SCOPES)[number]\n\nexport const DEFAULT_DEVICE_SCOPES: readonly DeviceScope[] = DEVICE_SCOPES\n\nexport interface PairingGrant {\n code: string\n expiresAt: number\n audience?: string\n}\n\nexport async function loadOrCreateHostAudience(path: string): Promise<string> {\n try {\n const existing = (await readFile(path, 'utf8')).trim()\n if (/^deeppilot:[A-Za-z0-9_-]{22}$/.test(existing)) return existing\n throw new Error(`host audience is malformed at ${path}`)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const audience = 'deeppilot:' + randomBytes(16).toString('base64url')\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, audience + '\\n', { mode: 0o600 })\n // 新建 Host 身份意味着 audience 变了:所有此前配对的设备都会在下一步\n // 认证失败(audience-mismatch)。这是「手机突然全部要重新配对」最直接的\n // 证据,必须留一行;只记事实与路径,不记 audience 原文(可被冒充)。\n console.log(formatLogLine('info', 'host identity created: ' + path + ' (previously paired devices must pair again)'))\n return audience\n}\n\nexport interface AuthChallenge {\n nonce: string\n audience: string\n issuedAt: number\n expiresAt: number\n}\n\nexport interface AuthProofFields extends AuthChallenge {\n deviceId: string\n deviceName: string\n appVersion: string\n resumeCursor?: number\n}\n\nfunction b64urlText(value: string): string {\n return Buffer.from(value, 'utf8').toString('base64url')\n}\n\n/**\n * Cross-language signature input. Text fields are base64url encoded before\n * joining so names cannot create ambiguous separators. Decimal timestamps\n * and cursor values are finite integers, or `-` when the cursor is absent.\n */\nexport function canonicalAuthChallenge(fields: AuthProofFields): Buffer {\n const cursor = fields.resumeCursor === undefined ? '-' : String(fields.resumeCursor)\n return Buffer.from([\n 'deeppilot-auth-v2',\n `device-id:${b64urlText(fields.deviceId)}`,\n `nonce:${fields.nonce}`,\n `audience:${b64urlText(fields.audience)}`,\n `issued-at:${fields.issuedAt}`,\n `expires-at:${fields.expiresAt}`,\n `device-name:${b64urlText(fields.deviceName)}`,\n `app-version:${b64urlText(fields.appVersion)}`,\n `resume-cursor:${cursor}`,\n ].join('\\n'), 'utf8')\n}\n\n/** Accept only an uncompressed ANSI X9.63 P-256 public key (65 bytes). */\nexport function parseP256PublicKey(encoded: string): { key: KeyObject; raw: Buffer } {\n const raw = Buffer.from(encoded, 'base64url')\n if (raw.length !== 65 || raw[0] !== 0x04) {\n throw new TypeError('publicKey must be an uncompressed P-256 X9.63 key')\n }\n // SubjectPublicKeyInfo prefix for id-ecPublicKey / prime256v1 followed by\n // the 65-byte uncompressed point.\n const spkiPrefix = Buffer.from('3059301306072a8648ce3d020106082a8648ce3d030107034200', 'hex')\n const key = createPublicKey({ key: Buffer.concat([spkiPrefix, raw]), format: 'der', type: 'spki' })\n if (key.asymmetricKeyType !== 'ec' || key.asymmetricKeyDetails?.namedCurve !== 'prime256v1') {\n throw new TypeError('publicKey must use P-256')\n }\n return { key, raw }\n}\n\nexport function deviceIdForPublicKey(publicKey: string): string {\n const { raw } = parseP256PublicKey(publicKey)\n return createHash('sha256').update(raw).digest('base64url')\n}\n\nexport function fingerprintForPublicKey(publicKey: string): string {\n const { raw } = parseP256PublicKey(publicKey)\n return createHash('sha256').update(raw).digest('hex')\n}\n\nexport function verifyAuthProof(publicKey: string, fields: AuthProofFields, signature: string): boolean {\n try {\n const { key } = parseP256PublicKey(publicKey)\n const der = Buffer.from(signature, 'base64url')\n if (der.length < 64 || der.length > 80) return false\n return verify('sha256', canonicalAuthChallenge(fields), key, der)\n } catch {\n return false\n }\n}\n\nexport function normalizeDeviceScopes(value: unknown): DeviceScope[] {\n if (!Array.isArray(value)) return [...DEFAULT_DEVICE_SCOPES]\n const allowed = new Set<string>(DEVICE_SCOPES)\n return [...new Set(value.filter((scope): scope is DeviceScope => typeof scope === 'string' && allowed.has(scope)))]\n}\n\n/** One active, single-use pairing grant per plugin runtime. */\nexport class PairingCodeManager {\n private active: PairingGrant | null = null\n\n issue(now = Date.now()): PairingGrant {\n const grant = {\n code: randomBytes(24).toString('base64url'),\n expiresAt: now + getPairingCodeTtlMs(),\n }\n this.active = grant\n return { ...grant }\n }\n\n consume(presented: string, now = Date.now()): boolean {\n const active = this.active\n if (active === null || now > active.expiresAt) {\n this.active = null\n return false\n }\n const expected = Buffer.from(active.code)\n const actual = Buffer.from(presented)\n const matches = expected.length === actual.length && timingSafeEqual(expected, actual)\n if (matches) this.active = null\n return matches\n }\n\n invalidate(): void {\n this.active = null\n }\n}\n\nexport function createAuthChallenge(audience: string, now = Date.now()): AuthChallenge {\n return {\n nonce: randomBytes(24).toString('base64url'),\n audience,\n issuedAt: now,\n expiresAt: now + AUTH_CHALLENGE_TTL_MS,\n }\n}\n","/**\n * 交互特性的 wire 行:回答待决的 approval 与 question。\n *\n * 两帧共用 `interactions.respond`。失败的三种结局(没有这条待决 / Host 拒收\n * 答案 / 传输失败)由 wire-errors 的词表翻译成 wire 错误码与可读信息——此前\n * 这条映射住在 connection-policy,和 scope 表、常量混在一起。\n */\n\nimport {\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\nimport { pendingResponseErrorCode, pendingResponseMessage } from './wire-errors.ts'\n\nconst RESPOND: WireFrameRow['scopes'] = ['interactions.respond']\n\nexport const interactionRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.approval.respond',\n stage: 'authenticated',\n scopes: RESPOND,\n doc: 'PROTOCOL.md c2s.approval.respond',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (!isText(p.requestId)) return reject('E_PROTOCOL', 'invalid requestId')\n if (\n !['allow', 'deny'].includes(p.decision as string) ||\n !isOptionalField(p, 'reason', (v) => isText(v, 65536, false))\n ) {\n return reject('E_PROTOCOL', 'invalid approval response')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const decision = payload.decision as 'allow' | 'deny'\n if (decision !== 'allow' && decision !== 'deny') {\n return ctx.fail('E_PROTOCOL', 'requestId and decision required')\n }\n // 可选的拒绝理由必须抵达 Host,模型据此理解工具调用为何被拒。\n const outcome = await ctx.bridge.respondApproval(\n payload.requestId as string,\n decision,\n typeof payload.reason === 'string' ? (payload.reason as string) : undefined,\n )\n if (!outcome.ok) {\n const reason = outcome.reason\n return ctx.fail(pendingResponseErrorCode(reason), pendingResponseMessage('approval', reason))\n }\n ctx.send('s2c.ack', {})\n },\n },\n {\n type: 'c2s.question.respond',\n stage: 'authenticated',\n scopes: RESPOND,\n doc: 'PROTOCOL.md c2s.question.respond',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (!isText(p.requestId)) return reject('E_PROTOCOL', 'invalid requestId')\n if (!Array.isArray(p.answers) || p.answers.length > 100) return reject('E_PROTOCOL', 'invalid answers')\n const ids = new Set<unknown>()\n for (const answer of p.answers as Array<Record<string, unknown>>) {\n if (\n !answer ||\n typeof answer !== 'object' ||\n Array.isArray(answer) ||\n !isText(answer.id) ||\n ids.has(answer.id) ||\n !Array.isArray(answer.selected) ||\n answer.selected.length > 100 ||\n !answer.selected.every((v: unknown) => isText(v, 4096)) ||\n (answer.custom !== undefined && !isText(answer.custom, 65536))\n ) {\n return reject('E_PROTOCOL', 'invalid answer')\n }\n ids.add(answer.id)\n }\n return checked\n },\n handle: async (ctx, payload) => {\n if (!Array.isArray(payload.answers)) return ctx.fail('E_PROTOCOL', 'requestId and answers required')\n const outcome = await ctx.bridge.respondQuestion(payload.requestId as string, payload.answers)\n if (!outcome.ok) {\n const reason = outcome.reason\n return ctx.fail(pendingResponseErrorCode(reason), pendingResponseMessage('question', reason))\n }\n ctx.send('s2c.ack', {})\n },\n },\n]\n","/**\n * 设备生命周期特性的 wire 行:自撤销(解绑)。\n *\n * 该帧不要求任何 scope:设备生命周期操作不能因为 scope 被收窄而无法解绑,\n * 否则它只会永远留在离线推送目标集合里(PROTOCOL.md c2s.device.revoke)。\n * widget 策略取 `handler`:小组件的拒必须是「小组件不得解绑设备」这个具体\n * 理由,通用只读门禁只会说 read-only。\n */\n\nimport {\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\n\nexport const deviceRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.device.revoke',\n stage: 'authenticated',\n scopes: [],\n widgetPolicy: 'handler',\n doc: 'PROTOCOL.md c2s.device.revoke',\n validate: (payload) => {\n // 载荷可整体省略:省略与 `{}` 等价,deviceId 可选。\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n // 校验可选的 deviceId:类型/非空(旧 request-validation 的判定),通过后\n // 才进入 handler 的等值判断。\n if (!isOptionalField(checked.value, 'deviceId', (v) => isText(v))) {\n return reject('E_PROTOCOL', 'invalid deviceId')\n }\n if (checked.value.deviceId !== undefined && typeof checked.value.deviceId !== 'string') {\n return reject('E_PROTOCOL', 'deviceId must be a string')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n if (typeof payload.deviceId === 'string' && payload.deviceId !== deviceId) {\n return ctx.fail('E_FORBIDDEN', 'deviceId does not match the authenticated device')\n }\n if (ctx.widgetClient) {\n return ctx.fail('E_FORBIDDEN', 'widget connection cannot revoke its device')\n }\n // 幂等:已撤销的记录仍然 ack {revoked:true}——设备无法区分首次撤销与\n // 重复撤销,两种情况都不能表现为错误。\n ctx.devices.revoke(deviceId, Date.now())\n ctx.markRevoked()\n // 摘除同设备的其它活跃连接是尽力而为:注册表墓碑已落盘,hook 失败\n // 不能吞掉这次 ack。\n try {\n await ctx.revokeSiblings(deviceId)\n } catch (error) {\n if (ctx.debug) ctx.log('device revoke hook failed: ' + String(error))\n }\n // ack 严格先于关闭,慢客户端才能在 4401 前看到解绑成功。\n ctx.send('s2c.ack', { revoked: true })\n ctx.close(4401, 'device revoked')\n },\n },\n]\n","import type { LiveActivityState } from './protocol.ts'\nimport { MAX_DEVICE_NAME_CHARS, sanitizeDeviceField } from './connection-policy.ts'\nimport { formatLogLine } from './diagnostics.ts'\nimport { randomBytes } from 'node:crypto'\nimport { access, chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, resolve } from 'node:path'\nimport {\n deviceIdForPublicKey,\n fingerprintForPublicKey,\n normalizeDeviceScopes,\n type DeviceScope,\n} from './device-auth.ts'\n\n/** Expand a leading ~ using the process home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir()\n if (p.startsWith('~/')) return resolve(homedir(), p.slice(2))\n return p\n}\n\nfunction dshDataRoot(): string {\n const dshHome = process.env.DSH_HOME\n if (dshHome && dshHome.trim().length > 0) return resolve(dshHome.trim())\n return resolve(homedir(), '.dsh')\n}\n\n/** DeepPilot data directory: under $DSH_HOME when set, else ~/.dsh. */\nexport function bridgeDataDir(): string {\n return resolve(dshDataRoot(), 'deeppilot')\n}\n\n/** Create or repair the canonical secret-bearing directory as owner-only. */\nexport async function ensurePrivateBridgeDataDir(): Promise<string> {\n const target = bridgeDataDir()\n await mkdir(target, { recursive: true, mode: 0o700 })\n await chmod(target, 0o700)\n return target\n}\n\n/**\n * Move the pre-DeepPilot data directory as one atomic directory rename.\n * Existing canonical data always wins; secrets are never merged or replaced.\n */\nexport async function migrateLegacyBridgeDataDir(): Promise<string | null> {\n const target = bridgeDataDir()\n try {\n await access(target)\n return null\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n\n const legacy = resolve(dshDataRoot(), 'pocket-bridge')\n try {\n await rename(legacy, target)\n return legacy\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\nexport type ApnsEnvironment = 'development' | 'production'\n\n/** Per-device APNs registration (F-9 离线推送). The raw token never leaves the registry file. */\nexport interface DeviceApnsInfo {\n /** Hex device token, lowercased, 32–512 chars. */\n token: string\n environment: ApnsEnvironment\n updatedAt: number\n /** Per-category opt-out mirror; absent keys mean enabled. */\n categories?: Record<string, boolean>\n}\n\nexport interface LiveActivityRegistration extends DeviceApnsInfo {\n activityId: string\n sessionId: string\n expiresAt: number\n endedState?: LiveActivityState\n}\n\nexport interface DeviceRecord {\n deviceId: string\n /** Name reported by the phone. Refreshed on every authenticated hello. */\n deviceName: string\n /** User-assigned name from the DSH settings page; survives app reconnects. */\n customName?: string\n appVersion: string\n /** Uncompressed P-256 X9.63 public key, base64url encoded. */\n publicKey?: string\n /** SHA-256 fingerprint of publicKey, hex encoded for local display. */\n fingerprint?: string\n scopes?: DeviceScope[]\n revokedAt?: number\n firstSeenTs: number\n lastSeenTs: number\n apns?: DeviceApnsInfo\n widgetApns?: DeviceApnsInfo\n liveActivity?: LiveActivityRegistration\n}\n\n/** Name shown to the operator: a user label when present, otherwise the app-reported model name. */\nexport function deviceDisplayName(record: Pick<DeviceRecord, 'deviceName' | 'customName'>): string {\n return record.customName?.trim() ? record.customName : record.deviceName\n}\n\n/** Hex shape of an APNs device token as delivered by iOS (usually 64 chars). */\nconst APNS_TOKEN_PATTERN = /^[0-9a-f]{32,512}$/\n\nexport function isValidApnsToken(token: unknown): token is string {\n return typeof token === 'string' && APNS_TOKEN_PATTERN.test(token)\n}\n\n/** Registry row cap: hello spam with fresh ids must not grow the file forever. */\nexport const MAX_DEVICES = 64\n\n/**\n * Paired-device registry persisted as one JSON document. Whole-document\n * writes (serialized, never interleaved); a corrupt file falls back to an\n * empty registry rather than failing the plugin.\n */\nexport class DeviceStore {\n private devices = new Map<string, DeviceRecord>()\n private flushTail: Promise<void> = Promise.resolve()\n /**\n * 注册表写失败的日志节流:磁盘只读 / 空间不足时每次 flush 都会失败,\n * 不节流会把「写不进去」变成新的刷屏源。同一条错误 60s 只报一次。\n */\n private lastWriteFailureLogAt = 0\n\n private constructor(private readonly filePath: string) {}\n\n static async load(filePath: string): Promise<DeviceStore> {\n const store = new DeviceStore(filePath)\n try {\n const raw = JSON.parse(await readFile(expandHome(filePath), 'utf8')) as {\n devices?: DeviceRecord[]\n }\n for (const rec of raw.devices ?? []) {\n if (typeof rec.deviceId === 'string') store.devices.set(rec.deviceId, rec)\n }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (code === 'ENOENT') {\n // First run on this home: nothing registered yet — normal.\n } else {\n // A truncated/corrupt registry silently wiping pairings and push\n // registrations would be indistinguishable from \"never registered\".\n // Make data loss loud so the cause (crash mid-write, disk, …) shows.\n console.log(formatLogLine('warn', 'device registry unreadable, starting empty: ' + String(error)))\n }\n }\n return store\n }\n\n /** Register one public key after a valid, single-use pairing grant. */\n register(\n record: { publicKey: string; deviceName: string; appVersion: string; scopes?: unknown },\n now: number,\n ): DeviceRecord {\n const deviceId = deviceIdForPublicKey(record.publicKey)\n const existing = this.devices.get(deviceId)\n if (!existing && this.devices.size >= MAX_DEVICES) {\n throw new Error('device registry is full')\n }\n const next: DeviceRecord = {\n deviceId,\n deviceName: record.deviceName,\n ...(existing?.customName ? { customName: existing.customName } : {}),\n appVersion: record.appVersion,\n publicKey: record.publicKey,\n fingerprint: fingerprintForPublicKey(record.publicKey),\n scopes: normalizeDeviceScopes(record.scopes),\n firstSeenTs: existing?.firstSeenTs ?? now,\n lastSeenTs: now,\n ...(existing?.apns ? { apns: existing.apns } : {}),\n ...(existing?.widgetApns ? { widgetApns: existing.widgetApns } : {}),\n ...(existing?.liveActivity ? { liveActivity: existing.liveActivity } : {}),\n }\n this.devices.set(deviceId, next)\n void this.flush()\n return structuredClone(next)\n }\n\n /** Return an active cryptographic identity. */\n authorized(deviceId: string): DeviceRecord | undefined {\n const record = this.devices.get(deviceId)\n if (!record?.publicKey || record.revokedAt !== undefined) return undefined\n return record\n }\n\n /**\n * 认证分因用:把「为什么 authorized() 返回 undefined」拆成三种情形。\n * 只给日志使用,准入判定仍然只走 authorized()——wire 响应不会因为\n * 这个方法泄露设备是否注册过(4401 仍只有一个码)。\n */\n statusOf(deviceId: string): 'active' | 'revoked' | 'unknown' {\n const record = this.devices.get(deviceId)\n if (record === undefined) return 'unknown'\n if (record.revokedAt !== undefined) return 'revoked'\n if (record.publicKey === undefined) return 'unknown'\n return 'active'\n }\n\n markAuthenticated(deviceId: string, deviceName: string, appVersion: string, now: number): void {\n const record = this.authorized(deviceId)\n if (!record) return\n record.deviceName = deviceName || record.deviceName\n record.appVersion = appVersion || record.appVersion\n record.lastSeenTs = now\n void this.flush()\n }\n\n /**\n * Set or clear the operator-facing device label. The phone-reported name is\n * intentionally left untouched so a reconnect cannot erase this value.\n * Returns the effective display name, or null when the device is not active.\n */\n async setCustomName(deviceId: string, customName: string | null): Promise<string | null> {\n const record = this.authorized(deviceId)\n if (!record) return null\n const normalized = customName === null ? '' : sanitizeDeviceField(customName, MAX_DEVICE_NAME_CHARS)\n if (normalized === (record.customName ?? '')) return deviceDisplayName(record)\n const previous = record.customName\n if (normalized === '') delete record.customName\n else record.customName = normalized\n try {\n await this.flush()\n } catch (error) {\n if (previous === undefined) delete record.customName\n else record.customName = previous\n throw error\n }\n return deviceDisplayName(record)\n }\n\n revoke(deviceId: string, now: number): boolean {\n const record = this.devices.get(deviceId)\n if (!record || record.revokedAt !== undefined) return false\n record.revokedAt = now\n delete record.apns\n delete record.widgetApns\n delete record.liveActivity\n void this.flush()\n return true\n }\n\n setScopes(deviceId: string, scopes: unknown): DeviceScope[] | null {\n const record = this.authorized(deviceId)\n if (!record) return null\n record.scopes = normalizeDeviceScopes(scopes)\n void this.flush()\n return [...record.scopes]\n }\n\n list(): DeviceRecord[] {\n return [...this.devices.values()].map((record) => structuredClone(record))\n }\n\n /**\n * Store (or refresh) the APNs registration of a paired device. Idempotent:\n * an unchanged registration does not rewrite the registry file, so the\n * app's re-register-on-every-handshake policy stays write-quiet.\n */\n setPushToken(\n deviceId: string,\n token: string,\n environment: ApnsEnvironment,\n categories: Record<string, boolean> | undefined,\n now: number,\n ): void {\n const normalized = token.toLowerCase()\n if (!isValidApnsToken(normalized)) return\n const record = this.authorized(deviceId)\n if (!record) return\n const next: DeviceApnsInfo = { token: normalized, environment, updatedAt: now }\n if (categories && typeof categories === 'object') {\n const clean: Record<string, boolean> = {}\n for (const [key, value] of Object.entries(categories)) {\n if (/^[a-z.]{1,64}$/.test(key) && typeof value === 'boolean') clean[key] = value\n }\n if (Object.keys(clean).length > 0) next.categories = clean\n }\n const current = record.apns\n if (\n current &&\n current.token === next.token &&\n current.environment === next.environment &&\n JSON.stringify(current.categories ?? {}) === JSON.stringify(next.categories ?? {})\n ) {\n return\n }\n this.detachDuplicateToken('apns', normalized, deviceId)\n record.apns = next\n void this.flush()\n }\n\n /** Drop a device's APNs registration (APNs reported the token unregistered). */\n clearPushToken(deviceId: string): void {\n const record = this.devices.get(deviceId)\n if (!record?.apns) return\n delete record.apns\n void this.flush()\n }\n\n setLiveActivity(deviceId: string, registration: LiveActivityRegistration): void {\n const record = this.authorized(deviceId)\n if (!record || !isValidApnsToken(registration.token)) return\n const old = record.liveActivity\n // Rotation/reconnect cannot extend a round or erase its terminal state.\n record.liveActivity = old?.activityId === registration.activityId\n ? { ...registration, expiresAt: old.expiresAt, endedState: old.endedState }\n : registration\n void this.flush()\n }\n\n endLiveActivity(deviceId: string, token: string, state: LiveActivityState): void {\n const registration = this.devices.get(deviceId)?.liveActivity\n if (!registration || registration.token !== token || registration.endedState) return\n registration.endedState = state\n void this.flush()\n }\n\n clearLiveActivity(deviceId: string, activityId: string, token?: string): void {\n const record = this.devices.get(deviceId)\n if (record?.liveActivity?.activityId !== activityId ||\n (token !== undefined && record.liveActivity.token !== token)) return\n delete record.liveActivity\n void this.flush()\n }\n\n setWidgetPushToken(deviceId: string, token: string, environment: ApnsEnvironment, now: number): void {\n const record = this.authorized(deviceId)\n if (!record || !isValidApnsToken(token)) return\n // Match c2s.push.register: hex tokens are stored lowercased, so an\n // uppercase re-send is the same registration, not a rotation.\n const normalized = token.toLowerCase()\n const previous = record.widgetApns\n if (previous?.token === normalized && previous.environment === environment &&\n now - previous.updatedAt < 60 * 60 * 1000) return\n this.detachDuplicateToken('widgetApns', normalized, deviceId)\n record.widgetApns = { token: normalized, environment, updatedAt: now }\n void this.flush()\n }\n\n /** Compare-and-clear protects a rotated token from a delayed APNs rejection. */\n clearWidgetPushToken(deviceId: string, token: string): void {\n const record = this.devices.get(deviceId)\n if (record?.widgetApns?.token !== token) return\n delete record.widgetApns\n void this.flush()\n }\n\n /**\n * One physical device owns one APNs token, but a re-pair or a botched unbind\n * can leave the same token attached to a second deviceId. Registering it\n * under `keepDeviceId` detaches it from every other record — only that one\n * token field, never the other record's pairing key or scopes. No flush of\n * its own: the caller batches it with the write that follows, so a single\n * registry write covers both changes.\n */\n private detachDuplicateToken(\n field: 'apns' | 'widgetApns',\n token: string,\n keepDeviceId: string,\n ): void {\n for (const record of this.devices.values()) {\n if (record.deviceId === keepDeviceId) continue\n if (field === 'apns') {\n if (record.apns?.token === token) delete record.apns\n } else if (record.widgetApns?.token === token) {\n delete record.widgetApns\n }\n }\n }\n\n /** Serialized so concurrent touches can never interleave half-written JSON. */\n private flush(): Promise<void> {\n const next = this.flushTail.then(() => this.writeFile())\n this.flushTail = next.catch(() => {\n // 真正的写失败已由 writeFile 自己的 catch 记录(含节流)。\n })\n return next\n }\n\n /** Resolves once every queued registry write has landed (test support). */\n async drain(): Promise<void> {\n await this.flushTail\n }\n\n private async writeFile(): Promise<void> {\n const full = expandHome(this.filePath)\n const body = JSON.stringify(\n { version: 2, devices: this.list() },\n null,\n 2,\n )\n try {\n await mkdir(dirname(full), { recursive: true })\n // Atomic replace: a restart mid-write previously left a truncated JSON\n // behind, which load() treats as corrupt and drops ENTIRELY — losing\n // pairings and push registrations on every unlucky shutdown.\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, body + '\\n', { mode: 0o600 })\n await rename(temp, full)\n } catch (error) {\n // best-effort persistence; the bridge still works without it.\n // 但失败本身必须留一行:注册表静默不落盘时,重启后配对消失会被\n // 误认为「设备自己掉了」,真正原因(磁盘 / 权限)只有这里可见。\n this.logWriteFailure(error)\n }\n }\n\n /** 写失败日志按 60s 节流:磁盘满时 flush 会被反复触发,不能让它刷屏。 */\n private logWriteFailure(error: unknown): void {\n const now = Date.now()\n if (now - this.lastWriteFailureLogAt < 60_000) return\n this.lastWriteFailureLogAt = now\n const code = (error as NodeJS.ErrnoException | undefined)?.code\n const message = error instanceof Error ? error.message : String(error)\n console.log(formatLogLine('warn', 'device registry write failed'\n + (typeof code === 'string' ? ' code=' + code : '')\n + ': ' + message.slice(0, 160)))\n }\n}\n","/**\n * 离线推送特性的 wire 行:APNs token 注册(手机与小组件两份)与 Live Activity\n * 注册。\n *\n * 三处需要特别注意的次序,迁移前一模一样保留:\n * 1. 零配置注册(enrollKey)必须早于 push 能力门——首次注册恰恰发生在推送尚未\n * 就绪时,而钥匙本身会把中继模式打开;\n * 2. token 写入必须早于就绪门——推送还在引导中或中继临时不可用时,token 已落盘,\n * 恢复后无需重新注册;\n * 3. 任何 await 之后都要重新读取设备记录:权限可能在等待期间被改窄。\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport { isValidApnsToken } from './token.ts'\nimport {\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\n\n/** Live Activity 需要的三个 scope:通知注册 + 会话读取 + 交互应答。 */\nconst LIVE_ACTIVITY_SCOPES: WireFrameRow['scopes'] = [\n 'notifications.register',\n 'sessions.read',\n 'interactions.respond',\n]\n\n/** 小组件总览需要的三个 scope:与 Live Activity 同组(PROTOCOL.md 下行权限一节)。 */\nconst WIDGET_PUSH_SCOPES: WireFrameRow['scopes'] = [\n 'notifications.register',\n 'sessions.read',\n 'interactions.respond',\n]\n\n/** enrollKey 的清洗:只保留可打印 ASCII,截到 128(与迁移前逐字一致)。 */\nfunction cleanEnrollKey(value: unknown): string {\n return typeof value === 'string' ? value.trim().replace(/[^\\x20-\\x7e]/g, '').slice(0, 128) : ''\n}\n\nexport const pushRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.push.register',\n stage: 'authenticated',\n scopes: ['notifications.register'],\n // 注意:不在此声明 capability 门。零配置注册(enrollKey)必须先于 push\n // 能力门执行——首次注册恰恰发生在推送尚未就绪时。门由 handler 在注册之后\n // 就地检查,与迁移前的次序一致。\n doc: 'PROTOCOL.md c2s.push.register',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n // token 的形状检查原在 handler 内(G3);此处并入校验,接受集不变。\n const token = typeof p.deviceToken === 'string' ? (p.deviceToken as string).trim() : ''\n if (!isValidApnsToken(token)) return reject('E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n if (p.environment !== undefined && p.environment !== 'production' && p.environment !== 'development') {\n return reject('E_PROTOCOL', 'invalid APNs environment')\n }\n if (!isOptionalField(p, 'enrollKey', (v) => isText(v, 128))) return reject('E_PROTOCOL', 'invalid enrollKey')\n if (\n p.categories !== undefined &&\n (typeof p.categories !== 'object' || p.categories === null || Array.isArray(p.categories) ||\n Object.values(p.categories as Record<string, unknown>).some((v) => typeof v !== 'boolean'))\n ) {\n return reject('E_PROTOCOL', 'invalid categories')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n const token = String(payload.deviceToken).trim()\n const environment: ApnsEnvironment = payload.environment === 'production' ? 'production' : 'development'\n const categories = typeof payload.categories === 'object' && payload.categories !== null\n ? (payload.categories as Record<string, boolean>)\n : undefined\n // 零配置注册先于能力门(见文件头说明 1)。\n if (typeof payload.enrollKey === 'string') {\n const enrollKey = cleanEnrollKey(payload.enrollKey)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) await ctx.enrollPushKey(enrollKey)\n }\n // token 先落盘,再评估就绪(见文件头说明 2);await 之后重新读取权限。\n const record = ctx.devices.authorized(deviceId)\n if (!record?.scopes?.includes('notifications.register')) {\n return ctx.fail('E_FORBIDDEN', 'device authorization changed')\n }\n ctx.devices.setPushToken(deviceId, token, environment, categories, Date.now())\n if (ctx.bridge.capabilities.push !== true) {\n if (ctx.debug) ctx.log('push register held: bridge not ready')\n return ctx.fail('E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n if (ctx.debug) ctx.log('push token registered env=' + environment)\n // enabled 让 App 立即翻转本地能力位,不必等下一次握手。\n ctx.send('s2c.ack', { enabled: true })\n },\n },\n {\n type: 'c2s.widget.push.register',\n stage: 'authenticated',\n scopes: WIDGET_PUSH_SCOPES,\n widgetPolicy: 'allowed',\n // 同 c2s.push.register:能力门在 handler 内、注册之后检查。\n doc: 'PROTOCOL.md c2s.widget.push.register',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n const token = typeof p.deviceToken === 'string' ? (p.deviceToken as string).trim() : ''\n if (!isValidApnsToken(token)) return reject('E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n if (p.environment !== undefined && p.environment !== 'production' && p.environment !== 'development') {\n return reject('E_PROTOCOL', 'invalid APNs environment')\n }\n if (!isOptionalField(p, 'enrollKey', (v) => isText(v, 128))) return reject('E_PROTOCOL', 'invalid enrollKey')\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n const token = String(payload.deviceToken).trim()\n const environment: ApnsEnvironment = payload.environment === 'production' ? 'production' : 'development'\n if (typeof payload.enrollKey === 'string') {\n const enrollKey = cleanEnrollKey(payload.enrollKey)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) await ctx.enrollPushKey(enrollKey)\n }\n const record = ctx.devices.authorized(deviceId)\n if (\n !record?.scopes?.includes('notifications.register') ||\n !record.scopes.includes('sessions.read') ||\n !record.scopes.includes('interactions.respond')\n ) {\n return ctx.fail('E_FORBIDDEN', 'device authorization changed')\n }\n ctx.devices.setWidgetPushToken(deviceId, token, environment, Date.now())\n if (ctx.bridge.capabilities.push !== true) {\n if (ctx.debug) ctx.log('push register held: bridge not ready')\n return ctx.fail('E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n ctx.send('s2c.ack', { enabled: true })\n },\n },\n {\n type: 'c2s.liveActivity.unregister',\n stage: 'authenticated',\n scopes: ['notifications.register'],\n doc: 'PROTOCOL.md c2s.liveActivity.unregister',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.activityId, 128)) return reject('E_PROTOCOL', 'invalid activityId')\n return checked\n },\n handle: (ctx, payload) => {\n const activityId = payload.activityId as string\n // 注销正在注册中的活动:抬高端次使在途的注册失效。\n if (ctx.pendingLiveActivityId === activityId) {\n ctx.nextLiveActivityGeneration()\n ctx.setPendingLiveActivityId(undefined)\n }\n ctx.devices.clearLiveActivity(ctx.deviceId!, activityId)\n ctx.send('s2c.ack', {})\n },\n },\n {\n type: 'c2s.liveActivity.register',\n stage: 'authenticated',\n scopes: LIVE_ACTIVITY_SCOPES,\n doc: 'PROTOCOL.md c2s.liveActivity.register',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (!isText(p.activityId, 128)) return reject('E_PROTOCOL', 'invalid activityId')\n if (\n !isText(p.sessionId) ||\n typeof p.deviceToken !== 'string' ||\n !/^[0-9a-fA-F]{32,512}$/.test(p.deviceToken) ||\n !['development', 'production'].includes(p.environment as string) ||\n !isOptionalField(p, 'enrollKey', (v) => isText(v, 128))\n ) {\n return reject('E_PROTOCOL', 'invalid live activity registration')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n const activityId = payload.activityId as string\n const generation = ctx.nextLiveActivityGeneration()\n ctx.setPendingLiveActivityId(activityId)\n // 注意:此处 enrollKey 原样透传,未做 push.register 那样的可打印 ASCII\n // 清洗。两条路径的清洗规则不一致属于已知不对称,本次按原行为保留,\n // 是否统一留待专门决策(不在本轮 wire 对等清单内)。\n if (typeof payload.enrollKey === 'string' && payload.enrollKey) {\n await ctx.enrollPushKey(payload.enrollKey as string)\n }\n if (generation !== ctx.liveActivityGeneration) {\n return ctx.fail('E_BUSY', 'live activity registration superseded')\n }\n // await 之后重新校验:权限可能已被改窄,连接可能已关闭。\n const record = ctx.devices.authorized(deviceId)\n if (\n !record ||\n !LIVE_ACTIVITY_SCOPES.every((scope) => record.scopes?.includes(scope))\n ) {\n return ctx.fail('E_FORBIDDEN', 'live activity permissions required')\n }\n if (!ctx.bridge.listSessions().some((row) => row.id === payload.sessionId)) {\n return ctx.fail('E_NOT_FOUND', 'session not found')\n }\n const previous = record.liveActivity\n if (previous?.activityId === activityId && previous.sessionId !== payload.sessionId) {\n return ctx.fail('E_PROTOCOL', 'activity is bound to another session')\n }\n ctx.devices.setLiveActivity(record.deviceId, {\n activityId,\n sessionId: payload.sessionId as string,\n token: String(payload.deviceToken).toLowerCase(),\n environment: payload.environment as ApnsEnvironment,\n updatedAt: Date.now(),\n expiresAt: Date.now() + 8 * 60 * 60 * 1000,\n })\n ctx.bridge.refreshLiveActivities()\n ctx.send('s2c.ack', { enabled: ctx.bridge.capabilities.push })\n },\n },\n]\n","/**\n * 定时任务特性的 wire 行:列表、历史、创建、修改、删除。\n *\n * scope 取 `schedule.manage` + `sessions.read`:PROTOCOL.md scope 映射表写明\n * 「`schedule.manage` 定时任务/提醒的列表、历史、创建、修改和删除;同时需要\n * `sessions.read` 才能读取任务内容」。迁移前这条组合一半住在 requiredScope、\n * 一半住在 connection.ts 调用点的一行补充规则里(334-336),现在是一行声明。\n *\n * 五个帧共享一个能力位 `schedules`;该位现在只要求 Schedule 服务存在\n * (见 host-capabilities.ts 的 G4 修正)。\n */\n\nimport { validSendId } from './connection-policy.ts'\nimport {\n accept,\n isInteger,\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\n\n/** PROTOCOL.md scope 映射表:schedule.manage 需搭配 sessions.read。 */\nconst SCHEDULE_SCOPES: WireFrameRow['scopes'] = ['schedule.manage', 'sessions.read']\n\nexport const scheduleRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.schedule.list',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.list',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.sessionId)) return reject('E_PROTOCOL', 'invalid sessionId')\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.listSchedules(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.snapshot', { sessionId: payload.sessionId, tasks: result.value })\n },\n },\n {\n type: 'c2s.schedule.history',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.history',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (\n !isText(checked.value.sessionId) ||\n !isText(checked.value.id) ||\n !isInteger(checked.value.limit, 1, 100) ||\n !isOptionalField(checked.value, 'before', (v) => isText(v))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule history')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.scheduleHistory(\n payload.sessionId as string,\n payload.id as string,\n payload.limit as number,\n payload.before as string | undefined,\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.history', { sessionId: payload.sessionId, history: result.value })\n },\n },\n {\n type: 'c2s.schedule.create',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !validSendId(p.clientRequestId) ||\n !isText(p.sessionId) ||\n !isText(p.title, 120) ||\n !isText(p.prompt, 256 * 1024)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule create fields')\n }\n // 恰好一个时机选择器:after_seconds / at / every_seconds / daily / weekly / cron。\n const selectors = ['after_seconds', 'at', 'every_seconds', 'daily', 'weekly', 'cron']\n .filter((key) => p[key] !== undefined)\n if (selectors.length !== 1) return reject('E_PROTOCOL', 'schedule requires exactly one selector')\n if (p.after_seconds !== undefined && !isInteger(p.after_seconds, 1, Number.MAX_SAFE_INTEGER)) {\n return reject('E_PROTOCOL', 'invalid after_seconds')\n }\n if (p.every_seconds !== undefined && !isInteger(p.every_seconds, 60, Number.MAX_SAFE_INTEGER)) {\n return reject('E_PROTOCOL', 'invalid every_seconds')\n }\n if (\n p.at !== undefined &&\n typeof p.at !== 'string' &&\n (typeof p.at !== 'object' || p.at === null || Array.isArray(p.at))\n ) {\n return reject('E_PROTOCOL', 'invalid at selector')\n }\n return accept(p)\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.createSchedule(\n ctx.deviceId!,\n payload as Record<string, unknown> & { sessionId: string; clientRequestId: string; title: string; prompt: string },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n sessionId: payload.sessionId,\n task: result.value,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.update',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.update',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !validSendId(p.clientRequestId) ||\n !isText(p.sessionId) ||\n !isText(p.id) ||\n p.expected === undefined ||\n typeof p.expected !== 'object' ||\n Array.isArray(p.expected)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule update fields')\n }\n if (\n !isOptionalField(p, 'title', (v) => isText(v, 120)) ||\n !isOptionalField(p, 'prompt', (v) => isText(v, 256 * 1024))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule update content')\n }\n if (\n p.change !== undefined &&\n (typeof p.change !== 'object' || p.change === null || Array.isArray(p.change))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule timing change')\n }\n return accept(p)\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.updateSchedule(\n ctx.deviceId!,\n payload as Record<string, unknown> & { sessionId: string; id: string; clientRequestId: string; expected: unknown },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n sessionId: payload.sessionId,\n task: result.value,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.delete',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.delete',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (\n !validSendId(checked.value.clientRequestId) ||\n !isText(checked.value.sessionId) ||\n !isText(checked.value.id)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule delete fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.deleteSchedule(ctx.deviceId!, {\n sessionId: payload.sessionId as string,\n id: payload.id as string,\n clientRequestId: payload.clientRequestId as string,\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n sessionId: payload.sessionId,\n deleted: true,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n]\n","export interface PromptDocument {\n name: string\n mediaType: string\n text: string\n truncated?: boolean\n}\n\ninterface DocumentMarker {\n v: 1\n name: string\n mediaType: string\n truncated?: boolean\n}\n\nconst PREFIX = '[DeepPilot document:'\nconst SUFFIX = ']'\n\n/**\n * DSH 0.1.2 has durable image attachments but no generic file content block.\n * Documents therefore travel as a bounded, explicitly-labelled text block.\n * The marker lets the canonical phone row recover an attachment chip without\n * exposing the complete document body as ordinary user-authored bubble text.\n */\nexport function documentPromptBlock(document: PromptDocument): string {\n const marker: DocumentMarker = {\n v: 1,\n name: document.name,\n mediaType: document.mediaType,\n ...(document.truncated ? { truncated: true } : {}),\n }\n const encoded = Buffer.from(JSON.stringify(marker), 'utf8').toString('base64url')\n return `${PREFIX}${encoded}${SUFFIX}\\nAttached document: ${document.name}\\n\\n${document.text}`\n}\n\nexport function projectedDocument(text: string): PromptDocument | undefined {\n if (!text.startsWith(PREFIX)) return undefined\n const end = text.indexOf(SUFFIX, PREFIX.length)\n if (end < 0) return undefined\n try {\n const decoded = JSON.parse(Buffer.from(text.slice(PREFIX.length, end), 'base64url').toString('utf8')) as Partial<DocumentMarker>\n if (decoded.v !== 1 || typeof decoded.name !== 'string' || typeof decoded.mediaType !== 'string') return undefined\n if (!decoded.name.trim() || !decoded.mediaType.trim()) return undefined\n return {\n name: decoded.name,\n mediaType: decoded.mediaType,\n text: text.slice(end + SUFFIX.length).replace(/^\\nAttached document:[^\\n]*\\n\\n/, ''),\n ...(decoded.truncated === true ? { truncated: true } : {}),\n }\n } catch {\n return undefined\n }\n}\n","import type { MessageProjection, SessionEventKind } from './protocol.ts'\nimport type { SessionEventLike } from './host-api.ts'\nimport { projectedDocument } from './document-payload.ts'\n\nexport const MAX_MESSAGE_PROJECTION_BYTES = 256 * 1024\n/// Aggregate message-array budget for one tail/history frame. The complete\n/// envelope stays below the legacy iOS 1 MB decoder cap with ample metadata\n/// headroom, while each individual projection keeps its 256 KB allowance.\nexport const MAX_SESSION_PAGE_MESSAGES_BYTES = 900 * 1024\n\n/** One durable host event sequence becomes exactly one phone message row.\n * Keep the last projection when a host history response repeats an event. */\nexport function canonicalSessionMessages(messages: MessageProjection[]): MessageProjection[] {\n const bySequence = new Map<number, MessageProjection>()\n for (const message of messages) bySequence.set(message.seq, message)\n return [...bySequence.values()].sort((a, b) => a.seq - b.seq)\n}\n\nexport function limitSessionPageMessages(messages: MessageProjection[]): {\n messages: MessageProjection[]\n dropped: number\n} {\n const canonical = canonicalSessionMessages(messages)\n let bytes = 2 // JSON array brackets\n const kept: MessageProjection[] = []\n for (let index = canonical.length - 1; index >= 0; index -= 1) {\n const message = canonical[index]!\n const candidateBytes = jsonBytes(message) + (kept.length > 0 ? 1 : 0)\n if (bytes + candidateBytes > MAX_SESSION_PAGE_MESSAGES_BYTES) break\n kept.unshift(message)\n bytes += candidateBytes\n }\n return { messages: kept, dropped: canonical.length - kept.length }\n}\n\nexport function projectEvent(sessionId: string, event: SessionEventLike): { kind: SessionEventKind; data: Record<string, unknown> } | null {\n switch (event.type) {\n case 'turn/start':\n return { kind: 'turn.start', data: {} };\n case 'turn/end': {\n const data = event.data as { reason?: { kind?: string } } | undefined;\n return { kind: 'turn.end', data: { ok: data?.reason?.kind === 'completed' } };\n }\n case 'system/message': {\n // DSH 0.1.5 (session format V3) records the system prompt in message\n // history as a native surface event: {turn, step, message: SystemMessage}.\n // The SystemMessage content is model-facing prompt prose, not\n // conversation content — projecting it onto the phone's message list\n // would duplicate it on every page and leak prompt internals the user\n // never typed. Skip it explicitly (rather than via `default`) so a\n // future shape change forces a deliberate decision here.\n return null;\n }\n case 'user/message':\n return {\n kind: 'message.final',\n data: { ...limitMessageProjection({\n seq: event.seq,\n role: userRoleOf(event.data),\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ...(contextProjectionOf(event.data)),\n ts: tsOf(event),\n }) },\n };\n case 'assistant/chunk': {\n if (chunkTypeOf(event.data) === 'reasoning-delta') {\n return { kind: 'thinking.delta', data: limitRealtimeText({ text: chunkText(event.data), ts: tsOf(event) }) };\n }\n return { kind: 'message.delta', data: limitRealtimeText({ text: chunkText(event.data), ts: tsOf(event) }) };\n }\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n // Pure-reasoning or tool-only steps carry no visible answer; emitting\n // them produced empty bubbles on the phone.\n if (!text.trim() && !thinking.trim()) return null;\n return {\n kind: 'message.final',\n data: { ...limitMessageProjection({ seq: event.seq, role: 'assistant', text, ...(thinking ? { thinking } : {}), ts: tsOf(event) }) },\n };\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n return {\n kind: 'tool.start',\n data: {\n seq: event.seq,\n role: 'tool',\n tool: {\n name: String(data?.name ?? 'tool'),\n state: 'running',\n summary: summarizeArgs(data?.arguments),\n // Echo the invocation id so clients can pair the later result with\n // this exact row instead of guessing.\n ...(data?.callId ? { callId: String(data.callId) } : {}),\n },\n ts: tsOf(event)\n }\n };\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; error?: unknown; message?: { content?: unknown } } | undefined;\n const ok = data?.error === undefined;\n const result = limitMessageProjection({\n seq: event.seq, role: 'tool', ts: tsOf(event),\n tool: { name: 'result', state: ok ? 'ok' : 'error', summary: ok ? summarizeResult(data?.message?.content) : '失败' },\n ...(ok ? attachmentProjection(data?.message) : {}),\n });\n return {\n kind: 'tool.end',\n data: {\n seq: result.seq, role: result.role, ts: result.ts, ok,\n summary: result.tool?.summary,\n ...(result.attachments ? { attachments: result.attachments } : {}),\n ...(data?.callId ? { callId: String(data.callId) } : {}),\n }\n };\n }\n default:\n return null;\n }\n}\n\nfunction tsOf(event: SessionEventLike): number {\n return typeof event.time === 'number' ? event.time : Date.now();\n}\n\n// ---------- user-role source classification ----------\n//\n// The DSH host logs every model-visible user-role message as `user/message`,\n// but only some of them are human prompts: synthetic `agent.inject()` context\n// (runtime-context snapshots, background-job notices, workspace instructions,\n// skill content, …) rides the same event type. The durable message carries a\n// `source` whose `kind` tells them apart, and the host's own trajectory view\n// renders only `source.kind === 'user'` as a human prompt — everything else is\n// injected context. The bridge mirrors that classification so the phone can\n// keep the two apart instead of guessing from text shapes.\n\n/** Read the durable message source off one user/message payload. Handles both\n * bare-message payloads and older `{message: {...}}` wrappers; undefined when\n * the shape carries no readable source (legacy hosts). */\nfunction userMessageSource(data: unknown): Record<string, unknown> | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const obj = data as { source?: unknown; message?: { source?: unknown } };\n if (obj.source && typeof obj.source === 'object') {\n return obj.source as Record<string, unknown>;\n }\n if (obj.message && typeof obj.message === 'object' &&\n obj.message.source && typeof obj.message.source === 'object') {\n return obj.message.source as Record<string, unknown>;\n }\n return undefined;\n}\n\n/** Wire role for one user/message payload. A payload without any readable\n * source degrades to 'user' so history written by older hosts stays visible;\n * a present source follows the host's own trajectory rule — anything whose\n * `kind` is not 'user' is injected context and projects as 'system'. */\nfunction userRoleOf(data: unknown): 'user' | 'system' {\n const source = userMessageSource(data);\n if (!source) return 'user';\n return source.kind === 'user' ? 'user' : 'system';\n}\n\n/** Producer name of one injected-context source, mirroring how the DSH client\n * runtime derives its trajectory label: plugin name, skill name, instruction\n * paths, session-reference labels, or the raw kind as fallback. */\nfunction contextLabelOf(source: Record<string, unknown>): string | undefined {\n const kind = typeof source.kind === 'string' ? source.kind : '';\n const joined = (member: string): string | undefined => {\n const list = source[member];\n if (!Array.isArray(list)) return undefined;\n const names = list.flatMap((entry) => {\n if (!entry || typeof entry !== 'object') return [];\n const record = entry as Record<string, unknown>;\n return [typeof record.label === 'string' ? record.label :\n (typeof record.path === 'string' ? record.path : '')];\n }).filter((name) => name.length > 0);\n return names.length > 0 ? names.join(', ') : undefined;\n };\n switch (kind) {\n case 'session-reference':\n return joined('references') ?? (kind || undefined);\n case 'agent-instructions':\n return joined('changes') ?? (kind || undefined);\n case 'plugin':\n return typeof source.plugin === 'string' && source.plugin.length > 0 ? source.plugin : kind || undefined;\n case 'skill-invocation':\n return typeof source.name === 'string' && source.name.length > 0 ? source.name : kind || undefined;\n default:\n return kind || undefined;\n }\n}\n\n/** Semantic ContextForm declared by the producer ('snapshot', 'notice', …);\n * anything unrecognized stays undefined so clients render it opaque. */\nfunction contextFormOf(source: Record<string, unknown>): string | undefined {\n if (typeof source.form !== 'string' || source.form.length === 0) return undefined;\n // Same known-vocabulary guard as the host's trajectory UI.\n const known = ['instructions', 'catalog', 'snapshot', 'notice', 'relay', 'recall'];\n return known.includes(source.form) ? source.form : undefined;\n}\n\n/** Optional `context` metadata for one system row; {} on user rows. */\nfunction contextProjectionOf(data: unknown): { context?: { label?: string; form?: string } } {\n if (userRoleOf(data) !== 'system') return {};\n const source = userMessageSource(data);\n if (!source) return {};\n const label = contextLabelOf(source);\n const form = contextFormOf(source);\n if (!label && !form) return {};\n return { context: { ...(label ? { label } : {}), ...(form ? { form } : {}) } };\n}\n\n/** Extract plain text from user/assistant message payloads across shapes. */\nexport function messageText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; text?: unknown; message?: unknown };\n if (typeof obj.text === 'string') return obj.text;\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageText(obj.message);\n return contentText(obj.content);\n}\n\nfunction contentText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map(part => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'text' && typeof piece.text === 'string') {\n return projectedDocument(piece.text) ? '' : piece.text\n }\n }\n return '';\n }).join('');\n }\n return '';\n}\n\nfunction messageAttachments(data: unknown): Array<{\n kind: 'image' | 'document'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n truncated?: boolean\n}> {\n if (!data || typeof data !== 'object') return [];\n const obj = data as { content?: unknown; message?: unknown };\n if (obj.message && typeof obj.message === 'object') return messageAttachments(obj.message);\n if (!Array.isArray(obj.content)) return [];\n const attachments: Array<{\n kind: 'image' | 'document'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n truncated?: boolean\n }> = []\n for (const part of obj.content) {\n if (!part || typeof part !== 'object') continue\n // The host normalizes uploaded bytes into an ImageAttachmentRef\n // (attachmentId/mediaType/bytes/width/height...); the id is what clients\n // need to read the image back through c2s.session.attachment.\n const block = part as {\n type?: string\n text?: unknown\n attachment?: {\n name?: unknown\n mediaType?: unknown\n attachmentId?: unknown\n width?: unknown\n height?: unknown\n }\n };\n if (block.type === 'text' && typeof block.text === 'string') {\n const document = projectedDocument(block.text)\n if (document) {\n attachments.push({\n kind: 'document',\n name: document.name,\n mediaType: document.mediaType,\n ...(document.truncated ? { truncated: true } : {}),\n })\n }\n continue\n }\n if (block.type !== 'image' || !block.attachment) continue\n const attachmentId = typeof block.attachment.attachmentId === 'string' && block.attachment.attachmentId.length > 0\n ? block.attachment.attachmentId\n : undefined;\n const width = typeof block.attachment.width === 'number' && Number.isFinite(block.attachment.width)\n ? block.attachment.width\n : undefined;\n const height = typeof block.attachment.height === 'number' && Number.isFinite(block.attachment.height)\n ? block.attachment.height\n : undefined;\n attachments.push({\n kind: 'image',\n ...(typeof block.attachment.name === 'string' ? { name: block.attachment.name } : {}),\n ...(typeof block.attachment.mediaType === 'string' ? { mediaType: block.attachment.mediaType } : {}),\n ...(attachmentId ? { attachmentId } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(height !== undefined ? { height } : {}),\n })\n }\n return attachments\n}\n\nfunction attachmentProjection(data: unknown): { attachments?: ReturnType<typeof messageAttachments> } {\n const attachments = messageAttachments(data);\n return attachments.length > 0 ? { attachments } : {};\n}\n\n/** Extract reasoning (\"thinking\") text from assistant message payloads. */\nfunction messageThinking(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; message?: unknown };\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageThinking(obj.message);\n return reasoningContent(obj.content);\n}\n\nfunction reasoningContent(content: unknown): string {\n if (!Array.isArray(content)) return '';\n return content.map(part => {\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'reasoning' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n}\n\n/** Stream chunk type of an assistant/chunk payload ('' when unwrapped). */\nfunction chunkTypeOf(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n return String((obj.chunk as { type?: unknown }).type ?? '');\n }\n // Unwrapped payloads carry plain text deltas.\n return 'text-delta';\n}\n\nfunction chunkText(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n // StreamChunk shape: {turn, step, chunk: {type: 'text-delta'|'reasoning-delta', text}}\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n const inner = obj.chunk as { type?: string; text?: string };\n if ((inner.type === 'text-delta' || inner.type === 'reasoning-delta') && typeof inner.text === 'string') {\n return inner.text;\n }\n return '';\n }\n const direct = data as { text?: unknown };\n return typeof direct.text === 'string' ? direct.text : '';\n}\n\nfunction summarizeArgs(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '';\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const parts: string[] = [];\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') parts.push(key + '=' + truncate(value.replace(/\\s+/g, ' '), 60));\n }\n return truncate(parts.join(' '), 90);\n } catch {\n return truncate(raw, 90);\n }\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : text.slice(0, max - 1) + '…';\n}\n\nexport function projectHistory(events: Array<{ event: SessionEventLike; view?: unknown }>): MessageProjection[] {\n const messages: MessageProjection[] = [];\n const toolByCall = new Map<string, MessageProjection>();\n for (const entry of events) {\n const event = entry.event;\n const base = { seq: event.seq, ts: tsOf(event) };\n switch (event.type) {\n // DSH 0.1.5 (session format V3) records the system prompt in message\n // history as a native `system/message` surface event. It is\n // model-facing prompt prose, not conversation content — see\n // projectEvent for the rationale. Skip explicitly.\n case 'system/message':\n break;\n case 'user/message':\n messages.push({\n ...base,\n role: userRoleOf(event.data),\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ...(contextProjectionOf(event.data)),\n });\n break;\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n if (!text.trim() && !thinking.trim()) break;\n messages.push({ ...base, role: 'assistant', text, ...(thinking ? { thinking } : {}) });\n break;\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n const row: MessageProjection = {\n ...base,\n role: 'tool',\n tool: { name: String(data?.name ?? 'tool'), state: 'running', summary: summarizeArgs(data?.arguments) },\n };\n messages.push(row);\n if (data?.callId) toolByCall.set(String(data.callId), row);\n break;\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; message?: { content?: unknown }; error?: unknown } | undefined;\n const callId = data?.callId ? String(data.callId) : undefined;\n const target = callId ? toolByCall.get(callId) : undefined;\n const failed = data?.error !== undefined;\n const summary = failed ? '失败' : summarizeResult(data?.message?.content);\n // A tool result may carry images as well as text — browser and\n // computer-use backends return screenshots this way. `summarizeResult`\n // covers only the textual part, so project the binary blocks as\n // attachments too; otherwise the phone renders a text-only summary and\n // the image is silently dropped. The durable attachmentId is what lets\n // the client read the bytes back through c2s.session.attachment.\n const attachments = failed ? {} : attachmentProjection(data?.message);\n if (target?.tool) {\n target.tool = { ...target.tool, state: failed ? 'error' : 'ok', summary };\n if (attachments.attachments) Object.assign(target, attachments);\n } else {\n messages.push({\n ...base,\n role: 'tool',\n tool: { name: 'result', state: failed ? 'error' : 'ok', summary },\n ...attachments,\n });\n }\n break;\n }\n default:\n break;\n }\n }\n return canonicalSessionMessages(messages.map(limitMessageProjection));\n}\n\n/**\n * Enforce PROTOCOL.md's per-message 256 KB ceiling by UTF-8 JSON byte size.\n * Keep structural identity and attachment references intact; progressively\n * shorten human-readable fields until the serialized projection fits.\n */\nexport function limitMessageProjection(message: MessageProjection): MessageProjection {\n if (jsonBytes(message) <= MAX_MESSAGE_PROJECTION_BYTES) return message\n const next: MessageProjection = {\n ...message,\n ...(message.tool ? {\n tool: {\n ...message.tool,\n name: truncateUtf8(message.tool.name, 4 * 1024),\n summary: truncateUtf8(message.tool.summary, 64 * 1024),\n },\n } : {}),\n ...(message.attachments ? {\n attachments: message.attachments.slice(0, 16).map((attachment) => ({\n ...attachment,\n ...(attachment.name ? { name: truncateUtf8(attachment.name, 4 * 1024) } : {}),\n ...(attachment.mediaType ? { mediaType: truncateUtf8(attachment.mediaType, 256) } : {}),\n ...(attachment.attachmentId ? { attachmentId: truncateUtf8(attachment.attachmentId, 4 * 1024) } : {}),\n })),\n } : {}),\n ...(message.context ? {\n context: {\n ...(message.context.label ? { label: truncateUtf8(message.context.label, 8 * 1024) } : {}),\n ...(message.context.form ? { form: truncateUtf8(message.context.form, 256) } : {}),\n },\n } : {}),\n truncated: true,\n }\n\n const textFields: Array<{\n get: () => string\n set: (value: string) => void\n }> = []\n if (typeof next.text === 'string') textFields.push({\n get: () => next.text ?? '',\n set: (value) => { next.text = value },\n })\n if (typeof next.thinking === 'string') textFields.push({\n get: () => next.thinking ?? '',\n set: (value) => { next.thinking = value },\n })\n if (next.tool) textFields.push({\n get: () => next.tool?.summary ?? '',\n set: (value) => { if (next.tool) next.tool.summary = value },\n })\n if (next.context?.label) textFields.push({\n get: () => next.context?.label ?? '',\n set: (value) => { if (next.context) next.context.label = value },\n })\n\n while (jsonBytes(next) > MAX_MESSAGE_PROJECTION_BYTES) {\n const largest = textFields\n .map((field) => ({ field, bytes: Buffer.byteLength(field.get(), 'utf8') }))\n .sort((a, b) => b.bytes - a.bytes)[0]\n if (largest && largest.bytes > 0) {\n largest.field.set(truncateUtf8(largest.field.get(), Math.floor(largest.bytes / 2)))\n continue\n }\n if (next.attachments && next.attachments.length > 0) {\n next.attachments = next.attachments.slice(0, -1)\n continue\n }\n break\n }\n return next\n}\n\nfunction limitRealtimeText(data: { text: string; ts: number }): Record<string, unknown> {\n if (jsonBytes(data) <= MAX_MESSAGE_PROJECTION_BYTES) return data\n let text = data.text\n const next: Record<string, unknown> = { ...data, truncated: true }\n while (jsonBytes(next) > MAX_MESSAGE_PROJECTION_BYTES && text.length > 0) {\n text = truncateUtf8(text, Math.floor(Buffer.byteLength(text, 'utf8') / 2))\n next.text = text\n }\n return next\n}\n\nfunction jsonBytes(value: unknown): number {\n return Buffer.byteLength(JSON.stringify(value), 'utf8')\n}\n\nfunction truncateUtf8(value: string, maxBytes: number): string {\n if (maxBytes <= 0) return ''\n if (Buffer.byteLength(value, 'utf8') <= maxBytes) return value\n let low = 0\n let high = value.length\n while (low < high) {\n const mid = Math.ceil((low + high) / 2)\n const candidate = value.slice(0, mid)\n if (Buffer.byteLength(candidate, 'utf8') <= maxBytes) low = mid\n else high = mid - 1\n }\n let end = low\n if (end > 0 && /[\\uD800-\\uDBFF]/.test(value[end - 1]!)) end -= 1\n return value.slice(0, end)\n}\n\nfunction summarizeResult(content: unknown): string {\n const text = contentText(content).replace(/\\s+/g, ' ').trim();\n return truncate(text, 90);\n}\n\n// ---------- 历史分页窗口 ----------\n\n/** 参与窗口计算的原始事件:可投影的最小形状。 */\nexport interface HistoryWindowEvent {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface HistoryWindowOptions {\n /** 只取 seq 严格小于该值的事件(翻页边界)。 */\n beforeSeq?: number\n /** 目标消息条数;小于 1 一律按 1 处理。 */\n limit: number\n}\n\nexport interface HistoryWindow {\n /** 窗口内的原始事件,旧→新,含投影为空者。 */\n events: Array<{ event: HistoryWindowEvent }>\n /** 起点之前是否还有更多事件(客户端可继续翻页)。 */\n hasMore: boolean\n}\n\n/** 一页能要多少条消息:wire 校验与 bridge clamp 共用这一条规则。 */\nexport const MIN_HISTORY_PAGE_MESSAGES = 1\nexport const MAX_HISTORY_PAGE_MESSAGES = 500\n/** 打开会话时的默认尾部条数。 */\nexport const DEFAULT_HISTORY_PAGE_MESSAGES = 100\n/** 打开会话时至少要拉多少条:少于这个数客户端一屏都铺不满。 */\nexport const MIN_TAIL_MESSAGES = 10\n\n/** 把 wire 声明的页大小收敛到合法区间。 */\nexport function normalizePageLimit(value: unknown): number {\n return typeof value === 'number' && Number.isInteger(value)\n ? Math.max(MIN_HISTORY_PAGE_MESSAGES, Math.min(MAX_HISTORY_PAGE_MESSAGES, value))\n : DEFAULT_HISTORY_PAGE_MESSAGES\n}\n\n/**\n * 会话历史的分页窗口:从候选事件里取出「投影后末尾 `limit` 条消息」所在的那\n * 一段原始事件。\n *\n * 迁移前这段循环住在 host adapter(dsh-api-proxy.ts 的 sessions.history)里,\n * 按块累积、每轮对累计窗口重跑一次投影(稀疏投影时最坏 O(N²/L))。它与自己\n * 依赖的投影规则分居两地,而 `projectHistory` 是顺序依赖的(tool/call 与\n * tool/result 靠 toolByCall 配对),所以这里只做一次前向投影,再按消息归属\n * 反推窗口起点。\n *\n * 注意:窗口**不是**最小的那个后缀。现算法按 `limit` 的整数倍从末端取块,\n * 因此窗口可能含若干投影为空的前导事件;本实现按消息归属取起点,同样保留\n * 这些前导事件之前的所有内容——两者的差异由 tests/history-window-parity.test.ts\n * 逐条钉住(75 例确定性语料,含切断 call/result 配对与重复 seq 的形状)。\n */\nexport function historyWindow(\n source: readonly unknown[],\n options: HistoryWindowOptions,\n): HistoryWindow {\n // 页大小走同一条规则:非有限值回落默认,其余收敛到 [1, 500]。\n const limit = normalizePageLimit(options.limit)\n const beforeSeq = options.beforeSeq\n const candidates: HistoryWindowEvent[] = []\n for (const raw of source) {\n if (typeof raw !== 'object' || raw === null) continue\n const event = raw as HistoryWindowEvent\n if (typeof event.type !== 'string' || typeof event.seq !== 'number') continue\n if (beforeSeq !== undefined && !(event.seq < beforeSeq)) continue\n candidates.push(event)\n }\n const entries = candidates.map((event) => ({ event }))\n if (entries.length === 0) return { events: [], hasMore: false }\n\n const projected = projectHistory(entries)\n if (projected.length <= limit) return { events: entries, hasMore: false }\n\n // 每条消息对应的原始下标:投影按 seq 去重(后者胜出),因此取该 seq 最后一次\n // 出现的下标。窗口必须包含这些下标,否则消息会被裁掉。\n const lastIndexBySeq = new Map<number, number>()\n for (let index = 0; index < candidates.length; index += 1) {\n lastIndexBySeq.set(candidates[index]!.seq, index)\n }\n const keep = projected.slice(projected.length - limit)\n let front = candidates.length\n for (const message of keep) {\n const index = lastIndexBySeq.get(message.seq)\n if (index !== undefined && index < front) front = index\n }\n // hasMore 只表示「窗口之前还有能产出消息的事件」:旧实现按 limit 整数倍取块,\n // 窗口之前只剩投影为空的前导事件时也报 true,客户端会多发一次翻页才拿到空页。\n // 这里对前缀做一次投影来定性,代价 O(前 `front` 个事件)。\n const older = front > 0 ? projectHistory(entries.slice(0, front)) : []\n return { events: entries.slice(front), hasMore: older.length > 0 }\n}\n","/**\n * 会话特性的 wire 行:列表/归档、打开与关闭、创建/分支/重命名/取消、历史与\n * 附件、模型目录与切换、prompt 投递。\n *\n * handler 体从 connection.ts 的 switch 原样迁来,`this.` 换成 `ctx.`;\n * 该校验的判定与顺序和迁移前一致——先是原 request-validation 的形状检查,\n * 后是原 case 体内的形状检查——因此同一载荷在新旧路径上得到同一个错误码与\n * 同一条信息(tests/wire-parity.test.ts 在迁移期逐条对等,之后由\n * tests/wire-rows.test.ts 固定为显式期望)。\n */\n\nimport { validSendId } from './connection-policy.ts'\nimport { normalizePageLimit } from './host-event-projection.ts'\nimport {\n IMAGE_MEDIA_TYPES,\n MAX_BASE64_CHARS_PER_IMAGE,\n MAX_DOCUMENT_MEDIA_TYPE_CHARS,\n MAX_DOCUMENT_NAME_CHARS,\n MAX_DOCUMENT_TEXT_CHARS,\n MAX_PROMPT_DOCUMENTS,\n MAX_PROMPT_IMAGES,\n MAX_PROMPT_TEXT_CHARS,\n sanitizeDocumentField,\n sanitizeImageName,\n} from './connection-policy.ts'\nimport {\n accept,\n isInteger,\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type CheckedPayload,\n type WireFrameRow,\n} from './wire-registry.ts'\n\n/** 会话读取域的帧共同要求 sessions.read(PROTOCOL.md scope 映射表)。 */\nconst READ: WireFrameRow['scopes'] = ['sessions.read']\nconst MANAGE: WireFrameRow['scopes'] = ['sessions.manage']\n\n/** 原 request-validation 的 sessionId 检查:所有 c2s.session.* 共用的第一道。 */\nfunction requireSessionId(payload: Record<string, unknown>): CheckedPayload | undefined {\n if (!isText(payload.sessionId)) return reject('E_PROTOCOL', 'invalid sessionId')\n return undefined\n}\n\nexport const sessionRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.ping',\n stage: 'pre-auth',\n scopes: [],\n widgetPolicy: 'allowed',\n doc: 'PROTOCOL.md 连接与鉴权',\n handle: (ctx) => {\n ctx.send('s2c.pong', { serverTime: Date.now() })\n },\n },\n {\n // 握手帧。机制(验签、scope 载入、welcome、续传)仍在连接里:这是认证\n // 状态机的地盘,行只登记「它存在、无需 scope、认证前可到」。\n type: 'c2s.auth.prove',\n stage: 'pre-auth',\n scopes: [],\n widgetPolicy: 'allowed',\n doc: 'PROTOCOL.md 连接与鉴权',\n handle: (ctx) => ctx.prove(),\n },\n {\n type: 'c2s.sessions.list',\n stage: 'authenticated',\n scopes: READ,\n widgetPolicy: 'allowed',\n doc: 'PROTOCOL.md c2s.sessions.list',\n validate: (payload) => payloadObject(payload),\n handle: (ctx) => {\n ctx.send('s2c.sessions.snapshot', { full: true, sessions: ctx.bridge.listSessions() })\n },\n },\n {\n // 与 list 同语义的无参帧:载荷可省略或为 null(G2)。\n type: 'c2s.sessions.archived',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.sessions.archived',\n validate: (payload) => payloadObject(payload),\n handle: (ctx) => {\n ctx.send('s2c.sessions.archived.snapshot', { sessions: ctx.bridge.listArchivedSessions() })\n },\n },\n {\n type: 'c2s.pending.list',\n stage: 'authenticated',\n scopes: ['interactions.respond'],\n widgetPolicy: 'allowed',\n capability: 'pendingSnapshot',\n doc: 'PROTOCOL.md c2s.pending.list',\n validate: (payload) => payloadObject(payload),\n handle: (ctx) => {\n ctx.send('s2c.pending.snapshot', ctx.bridge.pendingSnapshot())\n },\n },\n {\n type: 'c2s.workspaces.list',\n stage: 'authenticated',\n scopes: READ,\n capability: 'projectSelection',\n doc: 'PROTOCOL.md c2s.workspaces.list',\n validate: (payload) => payloadObject(payload),\n handle: async (ctx) => {\n const result = await ctx.bridge.listWorkspaces()\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.workspaces.snapshot', { workspaces: result.value })\n },\n },\n {\n type: 'c2s.workspace.create',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'projectSelection',\n doc: 'PROTOCOL.md c2s.workspace.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.path, 32768)) return reject('E_PROTOCOL', 'invalid path')\n return checked\n },\n handle: async (ctx, payload) => {\n const path = String(payload.path).trim()\n // 空路径是载荷问题,先于能力检查(与迁移前次序一致)。\n if (!path) return ctx.fail('E_PROTOCOL', 'non-empty path required')\n const result = await ctx.bridge.createWorkspace(path)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.workspace.created', result.value)\n },\n },\n {\n type: 'c2s.directory.list',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.directory.list',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isOptionalField(checked.value, 'path', (v) => isText(v, 32768, false))) {\n return reject('E_PROTOCOL', 'invalid path')\n }\n if (checked.value.path !== undefined && typeof checked.value.path !== 'string') {\n return reject('E_PROTOCOL', 'path must be a string')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.listDirectory(payload.path as string | undefined)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.directory.listing', result.value)\n },\n },\n {\n type: 'c2s.directory.pick',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.directory.pick',\n validate: (payload) => payloadObject(payload),\n handle: async (ctx) => {\n const result = await ctx.bridge.pickDirectory()\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.directory.picked', { path: result.value })\n },\n },\n {\n type: 'c2s.session.open',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.open',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isOptionalField(checked.value, 'tailCount', (v) => isInteger(v, 1, 10000))) {\n return reject('E_PROTOCOL', 'invalid tailCount')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const sessionId = payload.sessionId as string\n const buffered = ctx.bufferOpenEvents(sessionId)\n const ok = await ctx.bridge.openSession(ctx, sessionId, (payload.tailCount as number | undefined) ?? 100)\n if (!ok) {\n // 打开失败:仅当缓冲仍属于本次尝试时才移除,避免清掉并发打开的新缓冲。\n ctx.discardOpenBuffer(sessionId, buffered)\n return ctx.fail('E_NOT_FOUND', 'session history unavailable')\n }\n // 并发的 close 或替换打开会使本次尝试失效:它的 tail 已经发出,但不应\n // 在用户离开页面后重新激活实时投递。\n if (!ctx.flushOpenBuffer(sessionId, buffered)) return\n },\n },\n {\n type: 'c2s.session.close',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.close',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: (ctx, payload) => {\n ctx.closeOpenSession(payload.sessionId as string)\n ctx.send('s2c.ack', {})\n },\n },\n {\n type: 'c2s.session.create',\n stage: 'authenticated',\n scopes: MANAGE,\n doc: 'PROTOCOL.md c2s.session.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (\n !isOptionalField(checked.value, 'workspaceId', (v) => isText(v)) ||\n !isOptionalField(checked.value, 'cwd', (v) => isText(v, 32768)) ||\n (checked.value.workspaceId !== undefined && checked.value.cwd !== undefined)\n ) {\n return reject('E_PROTOCOL', 'invalid workspace selection')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const workspaceId = typeof payload.workspaceId === 'string' ? String(payload.workspaceId).trim() : ''\n const cwd = typeof payload.cwd === 'string' ? String(payload.cwd).trim() : ''\n if (workspaceId && cwd) return ctx.fail('E_PROTOCOL', 'workspaceId and cwd are mutually exclusive')\n const newId = await ctx.bridge.createSession({\n ...(workspaceId ? { workspaceId } : {}),\n ...(cwd ? { cwd } : {}),\n })\n if (!newId) return ctx.fail('E_INTERNAL', 'session create failed')\n ctx.send('s2c.ack', { sessionId: newId })\n },\n },\n {\n // PROTOCOL.md:分支会话要求 sessions.read 与 sessions.manage 两个 scope\n // (迁移前只校验了 sessions.manage,调用点的补充规则漏掉了本帧,见 G1)。\n type: 'c2s.session.fork',\n stage: 'authenticated',\n scopes: [...MANAGE, ...READ],\n capability: 'sessionFork',\n doc: 'PROTOCOL.md Session 分支扩展',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!validSendId(checked.value.clientRequestId) || !isOptionalField(checked.value, 'atSeq', (v) => isInteger(v, 0, Number.MAX_SAFE_INTEGER))) {\n return reject('E_PROTOCOL', 'invalid session fork fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId!\n const result = await ctx.bridge.forkSession(deviceId, {\n sessionId: payload.sessionId as string,\n clientRequestId: payload.clientRequestId as string,\n ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq as number } : {}),\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.forked', {\n clientRequestId: payload.clientRequestId,\n sourceSessionId: payload.sessionId,\n sessionId: result.value.sessionId,\n ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq } : {}),\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.session.rename',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'sessionManagement',\n doc: 'PROTOCOL.md c2s.session.rename',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isText(checked.value.title, 4096)) return reject('E_PROTOCOL', 'invalid title')\n return checked\n },\n handle: async (ctx, payload) => {\n const title = String(payload.title).trim()\n if (title.length === 0) return ctx.fail('E_PROTOCOL', 'sessionId and non-empty title required')\n const result = await ctx.bridge.renameSession(payload.sessionId as string, title)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.renamed', { sessionId: payload.sessionId, title: result.value })\n },\n },\n {\n type: 'c2s.session.archive',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'sessionManagement',\n doc: 'PROTOCOL.md c2s.session.archive',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.archiveSession(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.archived', { sessionId: payload.sessionId })\n },\n },\n {\n type: 'c2s.session.unarchive',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'sessionRestore',\n doc: 'PROTOCOL.md c2s.session.unarchive',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.unarchiveSession(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.unarchived', { sessionId: payload.sessionId })\n },\n },\n {\n type: 'c2s.session.cancel',\n stage: 'authenticated',\n scopes: MANAGE,\n doc: 'PROTOCOL.md c2s.session.cancel',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.cancelSession(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.ack', { sessionId: payload.sessionId })\n },\n },\n {\n type: 'c2s.session.history',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.history',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (\n !isInteger(checked.value.beforeSeq, 0, Number.MAX_SAFE_INTEGER) ||\n !isOptionalField(checked.value, 'limit', (v) => isInteger(v, 1, 500))\n ) {\n return reject('E_PROTOCOL', 'invalid history range')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n if (typeof payload.beforeSeq !== 'number') {\n return ctx.fail('E_PROTOCOL', 'sessionId and beforeSeq required')\n }\n const page = await ctx.bridge.historyPage(\n payload.sessionId as string,\n payload.beforeSeq,\n normalizePageLimit(payload.limit),\n )\n if (!page) return ctx.fail('E_NOT_FOUND', 'history unavailable')\n ctx.send('s2c.history.page', page)\n },\n },\n {\n type: 'c2s.session.attachment',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.attachment',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isText(checked.value.attachmentId)) return reject('E_PROTOCOL', 'invalid attachmentId')\n return checked\n },\n handle: async (ctx, payload) => {\n const attachmentId = payload.attachmentId as string\n if (attachmentId.length === 0) return ctx.fail('E_PROTOCOL', 'sessionId and attachmentId required')\n const image = await ctx.bridge.attachmentData(payload.sessionId as string, attachmentId)\n if (!image) return ctx.fail('E_NOT_FOUND', 'attachment unavailable')\n ctx.send('s2c.ack', image)\n },\n },\n {\n type: 'c2s.session.skills',\n stage: 'authenticated',\n scopes: READ,\n capability: 'skills',\n doc: 'PROTOCOL.md c2s.session.skills',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.sessionSkills(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.skills', { sessionId: payload.sessionId, ...result.value })\n },\n },\n {\n type: 'c2s.session.models',\n stage: 'authenticated',\n scopes: READ,\n capability: 'models',\n doc: 'PROTOCOL.md c2s.session.models',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.sessionModels(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.models', { sessionId: payload.sessionId, ...result.value })\n },\n },\n {\n type: 'c2s.session.selectModel',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'models',\n doc: 'PROTOCOL.md c2s.session.selectModel',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (\n !isText(checked.value.provider, 256) ||\n !isText(checked.value.model, 1024) ||\n !isOptionalField(checked.value, 'reasoningEffort', (v) => isText(v, 128, false))\n ) {\n return reject('E_PROTOCOL', 'invalid model selection')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n if (!String(payload.provider).trim() || !String(payload.model).trim()) {\n return ctx.fail('E_PROTOCOL', 'sessionId, provider and model required')\n }\n const result = await ctx.bridge.selectSessionModel(payload.sessionId as string, {\n provider: String(payload.provider).trim(),\n model: String(payload.model).trim(),\n ...(String(payload.reasoningEffort ?? '').trim()\n ? { reasoningEffort: String(payload.reasoningEffort).trim() }\n : {}),\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.modelSelected', { sessionId: payload.sessionId, selected: result.value })\n },\n },\n {\n // 投递回执查询:journal 语义,能力位恒真但仍绑定,缺位即说明 Bridge 被改动。\n type: 'c2s.session.delivery',\n stage: 'authenticated',\n scopes: ['prompt.send'],\n capability: 'promptDelivery',\n doc: 'PROTOCOL.md c2s.session.delivery',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!validSendId(checked.value.clientSendId)) return reject('E_PROTOCOL', 'invalid clientSendId')\n return checked\n },\n handle: (ctx, payload) => {\n ctx.send('s2c.ack', ctx.bridge.promptDeliveries.lookup(\n ctx.deviceId!,\n payload.clientSendId as string,\n payload.sessionId as string,\n ))\n },\n },\n {\n type: 'c2s.session.sendPrompt',\n stage: 'authenticated',\n scopes: ['prompt.send'],\n doc: 'PROTOCOL.md c2s.session.sendPrompt',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n const p = checked.value\n if (p.clientSendId !== undefined && !validSendId(p.clientSendId)) {\n return reject('E_PROTOCOL', 'invalid clientSendId')\n }\n if (\n !isOptionalField(p, 'text', (v) => isText(v, MAX_PROMPT_TEXT_CHARS, false)) ||\n !isOptionalField(p, 'images', Array.isArray) ||\n !isOptionalField(p, 'documents', Array.isArray)\n ) {\n return reject('E_PROTOCOL', 'invalid prompt fields')\n }\n // 附件条目形状:原 request-validation 的检查整体先于 handler 的资源上限,\n // 次序保持不变,同一条载荷在两条路径上得到同一条信息。\n for (const key of ['images', 'documents']) {\n const items = p[key] as unknown[] | undefined\n if (items && items.some((v) => v === null || typeof v !== 'object' || Array.isArray(v))) {\n return reject('E_PROTOCOL', 'invalid attachment')\n }\n for (const item of (items ?? []) as Array<Record<string, unknown>>) {\n if (item.name !== undefined && !isText(item.name, 4096, false)) {\n return reject('E_PROTOCOL', 'invalid attachment name')\n }\n if (item.truncated !== undefined && typeof item.truncated !== 'boolean') {\n return reject('E_PROTOCOL', 'invalid truncated flag')\n }\n }\n }\n const text = typeof p.text === 'string' ? p.text : ''\n const rawImages = Array.isArray(p.images) ? p.images : []\n const rawDocuments = Array.isArray(p.documents) ? p.documents : []\n if (text.trim().length === 0 && rawImages.length === 0 && rawDocuments.length === 0) {\n return reject('E_PROTOCOL', 'sessionId and prompt content required')\n }\n if (rawImages.length > MAX_PROMPT_IMAGES) return reject('E_PROTOCOL', 'too many images')\n if (rawDocuments.length > MAX_PROMPT_DOCUMENTS || rawImages.length + rawDocuments.length > MAX_PROMPT_IMAGES) {\n return reject('E_PROTOCOL', 'too many prompt attachments')\n }\n const images: Array<{ mediaType: string; data: string; name?: string }> = []\n for (const image of rawImages as Array<Record<string, unknown>>) {\n const mediaType = String(image?.mediaType)\n const data = image?.data\n if (\n !IMAGE_MEDIA_TYPES.has(mediaType) ||\n typeof data !== 'string' ||\n data.length === 0 ||\n data.length > MAX_BASE64_CHARS_PER_IMAGE\n ) {\n return reject('E_PROTOCOL', 'invalid image attachment')\n }\n images.push({\n mediaType,\n data,\n ...(typeof image.name === 'string' && sanitizeImageName(image.name).length > 0\n ? { name: sanitizeImageName(image.name) }\n : {}),\n })\n }\n const documents: Array<{ mediaType: string; name: string; text: string; truncated?: boolean }> = []\n for (const document of rawDocuments as Array<Record<string, unknown>>) {\n const name = document?.name\n const mediaType = document?.mediaType\n const documentText = document?.text\n if (\n typeof name !== 'string' ||\n typeof mediaType !== 'string' ||\n typeof documentText !== 'string' ||\n documentText.length === 0 ||\n documentText.length > MAX_DOCUMENT_TEXT_CHARS\n ) {\n return reject('E_PROTOCOL', 'invalid document attachment')\n }\n const cleanName = sanitizeDocumentField(name, MAX_DOCUMENT_NAME_CHARS)\n const cleanMediaType = sanitizeDocumentField(mediaType, MAX_DOCUMENT_MEDIA_TYPE_CHARS).toLowerCase()\n if (!cleanName || !cleanMediaType || cleanMediaType.startsWith('image/')) {\n return reject('E_PROTOCOL', 'invalid document attachment')\n }\n documents.push({\n name: cleanName,\n mediaType: cleanMediaType,\n text: documentText,\n ...(document.truncated === true ? { truncated: true } : {}),\n })\n }\n return accept({\n sessionId: p.sessionId,\n text,\n images,\n documents,\n ...(p.clientSendId !== undefined ? { clientSendId: p.clientSendId } : {}),\n })\n },\n handle: async (ctx, payload) => {\n const sessionId = payload.sessionId as string\n const text = payload.text as string\n const images = payload.images as Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }>\n const documents = payload.documents as Array<{ mediaType: string; name: string; text: string; truncated?: boolean }>\n const clientSendId = payload.clientSendId as string | undefined\n if (clientSendId !== undefined) {\n // 幂等投递:同一 clientSendId 重复到达时复用回执,不重复入队。\n const receipt = await ctx.bridge.promptDeliveries.dispatch(\n ctx.deviceId!,\n clientSendId,\n { sessionId, content: { text, images, documents } },\n () => ctx.bridge.sendPrompt(sessionId, text, images, documents),\n )\n ctx.send('s2c.ack', receipt)\n return\n }\n const userSeq = await ctx.bridge.sendPrompt(sessionId, text, images, documents)\n if (!userSeq.ok) return ctx.fail(userSeq.code, userSeq.message)\n ctx.send('s2c.ack', { userSeq: userSeq.value })\n },\n },\n]\n","import { isText, payloadObject, reject, type CheckedPayload, type WireFrameRow } from './wire-registry.ts'\nimport { hostFailureOf, wireErrorOf } from './wire-errors.ts'\nimport type { CommandAttachment } from './command-api.ts'\n\nfunction sessionPayload(value: unknown): CheckedPayload {\n const result = payloadObject(value)\n return result.ok && !isText(result.value.sessionId) ? reject('E_PROTOCOL', '会话标识无效') : result\n}\nconst categories = ['task-result', 'instruction-following', 'product-interaction', 'service-stability', 'resource-cost', 'security-privacy-permission', 'other']\nexport const commandRows: readonly WireFrameRow[] = [\n ...(['commands', 'permissions', 'feedback', 'command'] as const).map((name): WireFrameRow => ({\n type: `c2s.session.${name}`, stage: 'authenticated', capability: 'commands',\n scopes: name === 'commands' || name === 'permissions' ? ['sessions.read'] : ['prompt.send'],\n doc: `PROTOCOL.md c2s.session.${name}`,\n validate: value => {\n const result = sessionPayload(value)\n if (!result.ok) return result\n const p = result.value\n if (name === 'command') {\n if (typeof p.line !== 'string' || p.line.length > 64 * 1024 || !/^\\/[a-z][a-z0-9_-]*(?:\\s|$)/.test(p.line)) return reject('E_PROTOCOL', '指令格式无效')\n if (p.expectedDefinitionId !== undefined && (typeof p.expectedDefinitionId !== 'string' || !p.expectedDefinitionId || p.expectedDefinitionId.length > 512)) return reject('E_PROTOCOL', '指令定义标识无效')\n const attachments = p.attachments ?? []\n if (!Array.isArray(attachments) || attachments.length > 4) return reject('E_PROTOCOL', '附件数量无效')\n for (const item of attachments as CommandAttachment[]) {\n if (!item || !['image', 'document'].includes(item.type) || typeof item.name !== 'string' || item.name.length > 512 ||\n typeof item.mediaType !== 'string' || typeof item.data !== 'string' || item.data.length > 16 * 1024 * 1024 ||\n !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(item.data) ||\n (item.type === 'image' && !['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(item.mediaType))) return reject('E_PROTOCOL', '附件格式无效')\n }\n return { ok: true, value: { ...p, attachments } }\n }\n if (name === 'feedback' && (typeof p.text !== 'string' || p.text.length > 64 * 1024 || (p.category !== undefined && !categories.includes(String(p.category))))) return reject('E_PROTOCOL', '反馈内容无效')\n return result\n },\n handle: async (ctx, payload) => {\n const operation = name === 'commands' ? 'list' : name === 'command' ? 'execute' : name\n const result = await ctx.bridge.commandOperation(operation, payload)\n if (!result.ok) return ctx.fail(result.code, result.message)\n const type = name === 'commands' ? 's2c.session.commands' : name === 'permissions' ? 's2c.session.permissions' : name === 'command' ? 's2c.session.commandResult' : 's2c.ack'\n ctx.send(type, { sessionId: payload.sessionId, ...(result.value as object) })\n },\n })),\n {\n type: 'c2s.session.export', stage: 'authenticated', scopes: ['sessions.read'], capability: 'commands',\n doc: 'PROTOCOL.md c2s.session.export', validate: sessionPayload,\n handle: async (ctx, payload) => {\n const signal = AbortSignal.any([ctx.signal ?? new AbortController().signal, AbortSignal.timeout(10 * 60_000)])\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined\n const cancel = () => { void reader?.cancel().catch(() => {}) }\n try {\n const response = await ctx.bridge.exportSession(payload.sessionId as string, signal)\n reader = response.body!.getReader()\n signal.addEventListener('abort', cancel, { once: true })\n let index = 0, bytes = 0\n while (true) {\n signal.throwIfAborted()\n const next = await reader.read()\n if (next.done) break\n for (let offset = 0; offset < next.value.length; offset += 48 * 1024) {\n const chunk = next.value.subarray(offset, offset + 48 * 1024)\n bytes += chunk.length\n await ctx.waitForWritable?.()\n ctx.send('s2c.session.export.chunk', { index: index++, data: Buffer.from(chunk).toString('base64') })\n }\n }\n signal.throwIfAborted()\n ctx.send('s2c.ack', { sessionId: payload.sessionId, fileName: 'dsh-session-log.zip', chunks: index, bytes })\n } catch (error) {\n // 归档失败不止一种:主机没生成归档、内容类型不符、十分钟超时,或者本次\n // 连接断开。此前这里统一成一句「请重试」,把主机给的真实原因也一并吞掉;\n // 现在按域表翻译,主机的中文原因原样带给手机,只有取消/超时这类没有上游\n // 码的抛出物才落回兜底文案。断线同样走这里——它必须留在「不报成功」一侧。\n const failure = hostFailureOf(error, 'command/export-cancelled')\n const message = failure.code === 'command/export-cancelled' ? '会话日志导出失败,请重试' : failure.message\n const mapped = wireErrorOf('command', { ...failure, message })\n ctx.fail(mapped.code, mapped.message)\n }\n finally { signal.removeEventListener('abort', cancel); await reader?.cancel().catch(() => {}); reader?.releaseLock() }\n },\n },\n]\n","/**\n * wire 注册表(wire registry)——phone wire 缝的 host 侧属主。\n *\n * 一帧一行:`stage`(认证前/后)、`scopes`(全部满足)、`widgetPolicy`\n * (widget 短连接可读性)、`capability`(welcome 位门)、`validate`\n * (形状与资源上限)、`handle`(帧逻辑)。此前这些事实分散在四个模块里,\n * 各用一种风格枚举帧类型:connection.ts 的 switch、connection-policy 的\n * requiredScope if 链、request-validation 的 if 链、host-bridge 的\n * capabilities getter 与方法内 typeof 探测。\n *\n * 本 module 只提供表的形状、检查与 dispatch;handler 体按特性住在\n * wire-session / wire-schedule / wire-push / wire-device / wire-interaction,\n * 各自向这里登记自己的行(静态导入,无 import 副作用)。\n *\n * 帧的不变量由 tests/wire-registry.test.ts 逐行断言:每位被闸的帧都绑定一个\n * 已存在的 welcome 位、每个 scope 都是合法 DeviceScope、每个类型都有行、\n * 每行都指向 PROTOCOL.md 的一个小节。\n */\n\nimport type { Envelope } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { WelcomeCapabilities } from './protocol.ts'\nimport type { DeviceStore } from './token.ts'\nimport type { DeviceScope } from './device-auth.ts'\nimport type { CapabilityGate } from './host-capabilities.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\n\n/** 出站帧字面量:与入站共用一份词汇,record() 随后只接受这个子集。 */\nexport type S2CType =\n | 's2c.ack'\n | 's2c.auth.challenge'\n | 's2c.directory.listing'\n | 's2c.directory.picked'\n | 's2c.error'\n | 's2c.history.page'\n | 's2c.notify'\n | 's2c.pending.approval'\n | 's2c.pending.cleared'\n | 's2c.pending.question'\n | 's2c.pending.snapshot'\n | 's2c.pong'\n | 's2c.resume.done'\n | 's2c.resync'\n | 's2c.schedule.changed'\n | 's2c.schedule.history'\n | 's2c.schedule.snapshot'\n | 's2c.schedule.updated'\n | 's2c.session.archived'\n | 's2c.session.event'\n | 's2c.session.forked'\n | 's2c.session.models'\n | 's2c.session.skills'\n | 's2c.skills.changed'\n | 's2c.commands.changed'\n | 's2c.models.changed'\n | 's2c.session.commands'\n | 's2c.session.commandResult'\n | 's2c.session.permissions'\n | 's2c.session.export.chunk'\n | 's2c.session.modelSelected'\n | 's2c.session.renamed'\n | 's2c.session.tail'\n | 's2c.session.unarchived'\n | 's2c.sessions.archived.snapshot'\n | 's2c.sessions.delta'\n | 's2c.sessions.snapshot'\n | 's2c.welcome'\n | 's2c.workspace.created'\n | 's2c.workspaces.snapshot'\n\n/**\n * 入站帧字面量 = 本 Bridge 实现的全部客户端帧。\n *\n * 注意 `c2s.resume` 不在其中:PROTOCOL.md 把它与 c2s.ping 并列为控制帧,但\n * 续传由 `c2s.auth.prove` 的 `resumeCursor` 字段承载,没有独立的 resume 帧\n * (历史上 requiredScope 为它留过一个无人使用的条目,见 G7)。\n */\nexport type C2SType =\n | 'c2s.ping'\n | 'c2s.auth.prove'\n | 'c2s.sessions.list'\n | 'c2s.sessions.archived'\n | 'c2s.pending.list'\n | 'c2s.workspaces.list'\n | 'c2s.workspace.create'\n | 'c2s.directory.list'\n | 'c2s.directory.pick'\n | 'c2s.session.open'\n | 'c2s.session.close'\n | 'c2s.session.create'\n | 'c2s.session.fork'\n | 'c2s.session.rename'\n | 'c2s.session.archive'\n | 'c2s.session.unarchive'\n | 'c2s.session.cancel'\n | 'c2s.session.history'\n | 'c2s.session.attachment'\n | 'c2s.session.models'\n | 'c2s.session.skills'\n | 'c2s.session.commands'\n | 'c2s.session.command'\n | 'c2s.session.permissions'\n | 'c2s.session.feedback'\n | 'c2s.session.export'\n | 'c2s.session.selectModel'\n | 'c2s.session.delivery'\n | 'c2s.session.sendPrompt'\n | 'c2s.schedule.list'\n | 'c2s.schedule.history'\n | 'c2s.schedule.create'\n | 'c2s.schedule.update'\n | 'c2s.schedule.delete'\n | 'c2s.approval.respond'\n | 'c2s.question.respond'\n | 'c2s.device.revoke'\n | 'c2s.liveActivity.register'\n | 'c2s.liveActivity.unregister'\n | 'c2s.push.register'\n | 'c2s.widget.push.register'\n\n/** 帧在连接生命周期里允许出现的阶段。 */\nexport type FrameStage = 'pre-auth' | 'authenticated'\n\n/**\n * widget 短连接(clientRole: \"widget\")的帧策略。\n *\n * - `allowed`:只读面板需要的少量帧;\n * - `deny`:通用只读拒绝;\n * - `handler`:交给 handler 用自己的理由拒绝——用于 c2s.device.revoke,\n * 通用门禁只会说 \"read-only\",而规范要求给出「小组件不得解绑设备」的\n * 具体原因(PROTOCOL.md device.revoke 一节)。\n */\nexport type WidgetPolicy = 'allowed' | 'deny' | 'handler'\n\nexport const DEFAULT_WIDGET_POLICY: WidgetPolicy = 'deny'\n\n/** 拒绝:wire 错误码 + 可读信息。undefined 表示通过。 */\nexport interface WireRejection {\n code: WireErrorCode\n message: string\n}\n\n/**\n * 校验结果:通过时带上「归一化后的载荷」——形状检查与归一化同处一行,\n * handler 拿到的就是可直接使用的值(比如 sendPrompt 的 images/documents\n * 已在此时完成媒体类型与长度清洗)。拒绝时带 wire 错误码与可读信息。\n */\nexport type CheckedPayload =\n | { ok: true; value: Record<string, unknown> }\n | ({ ok: false } & WireRejection)\n\n/** 一帧在打开历史期间缓存的实时事件;连接持有所有权。 */\nexport interface OpenEventBuffer {\n readonly frames: Array<{ type: string; payload: unknown; seq?: number }>\n}\n\n/**\n * handler 看到的每帧 facade:把当前帧的 id 绑进 send/fail,handler 不必传 id;\n * 连接自身状态(打开中的会话缓冲、live activity 代次、撤销标志)以少量操作\n * 暴露,所有权仍在 connection —— 注册表只拥有帧的事实。\n */\nexport interface FrameContext extends BridgeSink {\n readonly signal?: AbortSignal\n waitForWritable?(): Promise<void>\n readonly frame: Envelope\n readonly deviceId: string | undefined\n readonly widgetClient: boolean\n readonly bridge: HostBridge\n readonly devices: DeviceStore\n readonly debug: boolean\n log(message: string): void\n send(type: S2CType, payload: unknown): void\n fail(code: WireErrorCode, message: string): void\n close(code: number, reason: string): void\n bufferOpenEvents(sessionId: string): OpenEventBuffer\n /** 打开失败:仅当缓冲仍属于本次尝试时才移除。 */\n discardOpenBuffer(sessionId: string, buffer: OpenEventBuffer): void\n /** 打开成功:认领缓冲并按其到达顺序补发;被并发操作取代时返回 false。 */\n flushOpenBuffer(sessionId: string, buffer: OpenEventBuffer): boolean\n closeOpenSession(sessionId: string): void\n /** 抬升 Live Activity 注册代次并返回新值;在途注册用旧值比对即知自己已被取代。 */\n nextLiveActivityGeneration(): number\n /** 当前代次:await 之后重新比对用。 */\n readonly liveActivityGeneration: number\n readonly pendingLiveActivityId: string | undefined\n setPendingLiveActivityId(activityId: string | undefined): void\n markRevoked(): void\n /** c2s.auth.prove 的机制由连接门实现(认证是安全判定,不属于特性模块)。 */\n prove(): Promise<void>\n enrollPushKey(enrollKey: string): Promise<void> | void\n revokeSiblings(deviceId: string): Promise<unknown> | unknown\n}\n\nexport interface WireFrameRow {\n readonly type: C2SType\n readonly stage: FrameStage\n /** 全部满足才放行;空数组表示控制帧,不要求业务 scope。 */\n readonly scopes: readonly DeviceScope[]\n readonly widgetPolicy?: WidgetPolicy\n readonly capability?: CapabilityGate\n /** PROTOCOL.md 小节名;tests/wire-registry.test.ts 断言其存在。 */\n readonly doc: string\n readonly validate?: (payload: unknown) => CheckedPayload\n readonly handle: (ctx: FrameContext, payload: Record<string, unknown>) => Promise<void> | void\n}\n\n// ---------- 检查(connection 在 dispatch 前依次调用) ----------\n\n/** 第一个缺失的 scope;消息与迁移前一致(`scope <name> required`)。 */\nexport function scopeRejection(\n row: WireFrameRow,\n scopes: ReadonlySet<DeviceScope>,\n): WireRejection | undefined {\n for (const scope of row.scopes) {\n if (!scopes.has(scope)) return { code: 'E_FORBIDDEN', message: `scope ${scope} required` }\n }\n return undefined\n}\n\n/** 能力位关闭时拒绝。校验先于本检查,与迁移前的次序一致。 */\nexport function capabilityRejection(\n row: WireFrameRow,\n capabilities: WelcomeCapabilities,\n): WireRejection | undefined {\n if (row.capability === undefined) return undefined\n if (capabilities[row.capability] === true) return undefined\n return { code: 'E_UNSUPPORTED', message: 'capability unavailable on this host version' }\n}\n\nexport function widgetPolicyOf(row: WireFrameRow): WidgetPolicy {\n return row.widgetPolicy ?? DEFAULT_WIDGET_POLICY\n}\n\n/** 校验一帧载荷;未登记 validate 的行原样通过。 */\nexport function validatePayload(row: WireFrameRow, payload: unknown): CheckedPayload {\n if (row.validate === undefined) {\n return { ok: true, value: (payload ?? {}) as Record<string, unknown> }\n }\n return row.validate(payload)\n}\n\n/** 执行一帧。检查(阶段/scope/能力/校验)由 connection 按序先行完成。 */\nexport async function dispatchFrame(\n row: WireFrameRow,\n ctx: FrameContext,\n payload: Record<string, unknown>,\n): Promise<void> {\n await row.handle(ctx, payload)\n}\n\n// ---------- 行内校验共用工具 ----------\n\n/**\n * 载荷必须是普通对象。此前这条判定住在 request-validation 里,对\n * `c2s.sessions.list` 等无参帧豁免、对 `c2s.sessions.archived` 却不通融\n * (同语义两种待遇,见 G2);现在豁免由各行的 validate 自己声明。\n */\nexport function payloadObject(value: unknown): CheckedPayload {\n if (value === undefined || value === null) return { ok: true, value: {} }\n if (typeof value !== 'object' || Array.isArray(value)) {\n return { ok: false, code: 'E_PROTOCOL', message: 'payload must be an object' }\n }\n return { ok: true, value: value as Record<string, unknown> }\n}\n\n/** 字符串字段:类型正确、不超长、非空(可放宽)。 */\nexport function isText(value: unknown, max = 4096, nonempty = true): boolean {\n return typeof value === 'string' && value.length <= max && (!nonempty || value.trim().length > 0)\n}\n\n/** 可选字段:缺省合法,给出时必须满足 check。 */\nexport function isOptionalField(\n payload: Record<string, unknown>,\n key: string,\n check: (value: unknown) => boolean,\n): boolean {\n return payload[key] === undefined || check(payload[key])\n}\n\nexport function isInteger(value: unknown, min: number, max: number): boolean {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= min && value <= max\n}\n\nexport function reject(code: WireErrorCode, message: string): CheckedPayload {\n return { ok: false, code, message }\n}\n\nexport function accept(value: Record<string, unknown>): CheckedPayload {\n return { ok: true, value }\n}\n\n// ---------- 表 ----------\n\nimport { interactionRows } from './wire-interaction.ts'\nimport { deviceRows } from './wire-device.ts'\nimport { pushRows } from './wire-push.ts'\nimport { scheduleRows } from './wire-schedule.ts'\nimport { sessionRows } from './wire-session.ts'\nimport { commandRows } from './wire-command.ts'\n\n/** 全部入站行。装配是静态的:新增特性 module 必须在这里显式出现。 */\nexport const WIRE_FRAME_ROWS: readonly WireFrameRow[] = [\n ...sessionRows,\n ...commandRows,\n ...scheduleRows,\n ...interactionRows,\n ...pushRows,\n ...deviceRows,\n]\n\nconst ROWS_BY_TYPE = new Map<C2SType, WireFrameRow>(WIRE_FRAME_ROWS.map((row) => [row.type, row]))\n\nexport function registryRowFor(type: string): WireFrameRow | undefined {\n return ROWS_BY_TYPE.get(type as C2SType)\n}\n\n/** 供 build 期不变量检查与测试使用:每行可索引。 */\nexport function wireFrameRows(): readonly WireFrameRow[] {\n return WIRE_FRAME_ROWS\n}\n","/**\n * 连接门(ConnectionGate)——「一个匿名 socket 被允许做什么」的唯一属主。\n *\n * 迁移前,这些规则散在五个地方:connection.ts 的 onMessage(长度上限、信封\n * 卫生、pre-auth 白名单、scope/widget 门)、connection.ts 的 prove()(认证)、\n * index.ts 的 handleUpgrade(连接数、名额、失败记账、6 个手工 release 点)、\n * auth-rate-limit.ts(策略)、token.ts(注册表语义)。理解一次 4401 关闭要跨\n * 400 行来回跳。\n *\n * 现在:门拥有 ①认证状态机的全部迁移 ②认证名额的获取与释放(单一释放点)\n * ③「此刻这一帧是否被允许进入」的判定。连接只剩传输、welcome 发射、sink 台账\n * 与连接自有缓冲;注册表行仍住在 wire-registry 及其特性 module 里,门只负责\n * 在正确的时机调用它们。\n *\n * 名额的生命周期分两段,各有保证:\n * - 接入段:门在 handleUpgrade 里构造(admit 发生于此)。若 upgrade 回调根本\n * 不执行(upgrade 期间 socket 被销毁),5 秒兜底会释放名额并判门死刑;\n * - 认证段:attach 之后由 35 秒 hello 计时器兜底,任何一条落定路径\n * (成功/证明无效/超时/关闭)都经 settleAuthentication 的 exactly-once\n * 守卫释放一次。\n */\n\nimport { PROTOCOL_VERSION } from './protocol.ts'\nimport type { AuthProofPayload, Envelope } from './protocol.ts'\nimport type { HostBridge } from './host-bridge.ts'\nimport type { DeviceStore } from './token.ts'\nimport type { DiagnosticsRecorder } from './diagnostics.ts'\nimport {\n createAuthChallenge,\n verifyAuthProof,\n type AuthChallenge,\n type DeviceScope,\n} from './device-auth.ts'\nimport {\n AUTH_TIMEOUT_MS,\n MAX_APP_VERSION_CHARS,\n MAX_DEVICE_ID_CHARS,\n MAX_DEVICE_NAME_CHARS,\n PRE_AUTH_FRAME_BYTES,\n sanitizeDeviceField,\n isEnvelope,\n} from './connection-policy.ts'\nimport { AuthRateLimiter } from './auth-rate-limit.ts'\nimport {\n capabilityRejection,\n dispatchFrame,\n registryRowFor,\n scopeRejection,\n validatePayload,\n widgetPolicyOf,\n type FrameContext,\n type OpenEventBuffer,\n type S2CType,\n} from './wire-registry.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\n\n/** 认证落定的四种结局;与迁移前 index.ts 收到的 reason 一致。 */\nexport type SettleReason = 'success' | 'invalid-proof' | 'timeout' | 'closed'\n\n/**\n * 4401 的七种分因。**只进日志与诊断事件**:wire 响应仍然只有一个 4401\n * 关闭码,绝不透露设备是否注册过、签名哪一步失败。\n */\nexport type AuthFailureReason =\n | 'unknown-device'\n | 'revoked'\n | 'bad-signature'\n | 'nonce-mismatch'\n | 'audience-mismatch'\n | 'challenge-expired'\n | 'clock-skew'\n\n/**\n * 把一次认证失败归一到一个原因。判定顺序就是语义优先级:\n *\n * 1. 注册表状态先行——设备不存在 / 已撤销时,后面任何字段都不可信;\n * 2. nonce / audience 不匹配:挑战对象不是这一次(重放或串线);\n * 3. 服务器时钟已过挑战有效期:挑战过期;\n * 4. 客户端回显的 issuedAt / expiresAt 与挑战不同:客户端时钟或状态偏差;\n * 5. 其余(签名缺失 / 验签失败):bad-signature。\n *\n * 纯函数,连接门与单测共用同一份判定。\n */\nexport function classifyAuthFailure(input: {\n device: 'active' | 'revoked' | 'unknown'\n proof: Partial<AuthProofPayload>\n challenge: AuthChallenge\n now: number\n}): AuthFailureReason {\n if (input.device === 'revoked') return 'revoked'\n if (input.device === 'unknown') return 'unknown-device'\n const { proof, challenge, now } = input\n if (proof.nonce !== challenge.nonce) return 'nonce-mismatch'\n if (proof.audience !== challenge.audience) return 'audience-mismatch'\n if (now > challenge.expiresAt) return 'challenge-expired'\n if (proof.issuedAt !== challenge.issuedAt || proof.expiresAt !== challenge.expiresAt) return 'clock-skew'\n return 'bad-signature'\n}\n\n/** 认证成功的产物:连接据它发 welcome、注册 sink、决定重放。 */\nexport interface AuthenticatedIdentity {\n deviceId: string\n scopes: ReadonlySet<DeviceScope>\n widgetClient: boolean\n resumeCursor: number | undefined\n deviceName: string\n appVersion: string\n}\n\n/**\n * 门看到的宿主,由连接实现:传输、sink 转发、连接自有缓冲,以及认证成功与\n * 审计两个通知。行(wire-registry)看到的 FrameContext 是它的一个按帧投影。\n */\nexport interface GateHost {\n readonly signal?: AbortSignal\n waitForWritable?(): Promise<void>\n readonly bridge: HostBridge\n readonly devices: DeviceStore\n readonly debug: boolean\n log(message: string): void\n send(type: S2CType, payload: unknown, id?: string): void\n fail(id: string | undefined, code: WireErrorCode, message: string): void\n close(code: number, reason: string): void\n // ---------- BridgeSink 转发(openSession 等行需要把上下文当作 sink) ----------\n canReceive(scope: DeviceScope): boolean\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n // ---------- 连接自有状态 ----------\n bufferOpenEvents(sessionId: string): OpenEventBuffer\n discardOpenBuffer(sessionId: string, buffer: OpenEventBuffer): void\n flushOpenBuffer(sessionId: string, buffer: OpenEventBuffer): boolean\n closeOpenSession(sessionId: string): void\n nextLiveActivityGeneration(): number\n readonly liveActivityGeneration: number\n readonly pendingLiveActivityId: string | undefined\n setPendingLiveActivityId(activityId: string | undefined): void\n markRevoked(): void\n enrollPushKey(enrollKey: string): Promise<void> | void\n revokeSiblings(deviceId: string): Promise<unknown> | unknown\n // ---------- 通知 ----------\n /** 认证成功:由连接发 welcome、注册 sink、决定重放/重同步。 */\n onAuthenticated(identity: AuthenticatedIdentity): void\n /** 隐私 preserving 审计事件(有效 hello 之后)。 */\n deviceAuthenticated(deviceId: string): void\n /** 认证落定:纯观测通知(名额释放与限流记账已在门内完成)。 */\n settled(ok: boolean, reason: SettleReason): void\n}\n\nexport interface ConnectionGateOptions {\n /** 客户端身份,用于限流(IP 或回环标识)。 */\n source: string\n devices: DeviceStore\n /** 稳定 Host 身份,写入每次挑战。 */\n audience: string\n limiter: AuthRateLimiter\n log: (message: string) => void\n /** 审计标签函数:把敏感标识变成短哈希,供日志使用。 */\n auditLabel?: (value: string) => string\n /** 客户端带来的 attemptId;只用于串联日志,不参与任何判定。 */\n attemptId?: string\n /** 诊断事件出口;缺省时失败路径不额外记录(行为不变)。 */\n diagnostics?: DiagnosticsRecorder\n /** 未 attach 的兜底时限:upgrade 回调根本不执行时释放名额。 */\n attachTimeoutMs?: number\n}\n\n/** upgrade 正常是毫秒级;5 秒已极宽,只为「回调根本不执行」兜底。 */\nconst DEFAULT_ATTACH_TIMEOUT_MS = 5_000\n\nexport class ConnectionGate {\n private authenticated = false\n private settled = false\n private revoked = false\n private deviceId: string | undefined\n private scopes = new Set<DeviceScope>()\n private widgetClient = false\n private readonly challenge: AuthChallenge\n private helloTimer: NodeJS.Timeout | undefined\n private attachTimer: NodeJS.Timeout | undefined\n private attached = false\n private host: GateHost | undefined\n private admission: { release: () => void } | undefined\n private dead = false\n\n constructor(private readonly options: ConnectionGateOptions) {\n this.challenge = createAuthChallenge(options.audience)\n // 名额在此获取:早于 upgrade 回调,因此「回调不执行」也有释放保证。\n const admission = options.limiter.admit(options.source)\n this.admission = admission.ok ? admission : undefined\n if (!admission.ok) {\n this.dead = true\n return\n }\n this.attachTimer = setTimeout(() => {\n this.attachTimer = undefined\n // 从未 attach:回调没跑,socket 不会再来。释放名额,门作废。\n if (!this.attached && !this.settled) {\n this.dead = true\n this.releaseAdmission()\n }\n }, options.attachTimeoutMs ?? DEFAULT_ATTACH_TIMEOUT_MS)\n this.attachTimer.unref()\n }\n\n /** 名额是否拿到;false 时 handleUpgrade 应直接拒掉这个源。 */\n get admitted(): boolean {\n return !this.dead\n }\n\n /** 客户端身份:限流与审计日志共用同一个值。 */\n get source(): string {\n return this.options.source\n }\n\n /**\n * ws 就绪:接线宿主、下发挑战并启动 hello 计时器。只应被调用一次——\n * 由连接在构造时调用(那一刻 ws 才真正存在)。\n */\n attach(host: GateHost): void {\n if (this.dead || this.attached) return\n this.attached = true\n this.host = host\n if (this.attachTimer !== undefined) {\n clearTimeout(this.attachTimer)\n this.attachTimer = undefined\n }\n host.send('s2c.auth.challenge', this.challenge)\n this.helloTimer = setTimeout(() => {\n this.helloTimer = undefined\n if (this.authenticated) return\n this.settle(false, 'timeout')\n // 4402 与 4401 对称:超时同样是「手机握手失败」,没有理由只让认证\n // 失败有记录、超时无声无息。\n this.reportAuthFailure('timeout', 4402)\n this.host?.close(4402, 'auth timeout')\n }, AUTH_TIMEOUT_MS)\n this.helloTimer.unref()\n }\n\n /**\n * 接入窗口结束:若始终没有 socket 接上(upgrade 回调没执行),立刻归还名额,\n * 不必等 5 秒兜底。已 attach 的门不在此释放——它的名额由落定路径或 hello\n * 计时器负责。\n */\n releaseIfNeverAttached(): void {\n if (!this.attached && !this.settled) {\n this.dead = true\n this.releaseAdmission()\n }\n }\n\n /** 拒绝一个已创建但最终不会接线的门(bridge 变更、构造失败)。 */\n markDead(): void {\n this.dead = true\n this.releaseAdmission()\n }\n\n /** socket 关闭:未落定则按 closed 收尾(并释放名额)。 */\n onClose(): void {\n if (!this.settled) this.settle(false, 'closed')\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n if (this.attachTimer !== undefined) clearTimeout(this.attachTimer)\n }\n\n /**\n * 处理一条原始帧。内部完成全部 pre-auth 规则(长度上限、信封卫生、版本、\n * pre-auth 白名单、撤销吞帧),再进入认证、授权与分发。\n */\n async handleFrame(raw: string): Promise<void> {\n const host = this.host\n if (this.dead || host === undefined) return\n // 撤销后的设备正在关闭:吞掉一切,不再回应一个已不存在的设备。\n if (this.revoked) return\n // 长度上限先行:预认证帧很小,超限即拒且不解析——不让匿名端用 64MiB\n // 载荷在认证窗口内消耗 CPU。\n if (!this.authenticated && raw.length > PRE_AUTH_FRAME_BYTES) {\n host.close(1009, 'pre-auth frame too large')\n return\n }\n let env: Envelope\n try {\n const parsed: unknown = JSON.parse(raw)\n if (!isEnvelope(parsed)) {\n host.fail(undefined, 'E_PROTOCOL', 'malformed frame')\n return\n }\n env = parsed\n } catch {\n host.fail(undefined, 'E_PROTOCOL', 'frame is not valid JSON')\n return\n }\n if (env.v !== PROTOCOL_VERSION) {\n host.fail(env.id, 'E_UNSUPPORTED', 'unsupported protocol version')\n host.close(4500, 'protocol version mismatch')\n return\n }\n\n if (!this.authenticated) {\n // 匿名端对每个非控制帧只得到一个答复:透露哪些名字已注册等于泄露帧清单。\n const control = registryRowFor(env.type)\n if (control === undefined || control.stage !== 'pre-auth') {\n host.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n return\n }\n await dispatchFrame(control, this.context(env), {})\n return\n }\n\n const row = registryRowFor(env.type)\n // widget 只读门在前,且覆盖未知类型:短连接只准发白名册行,其余一律\n // 「只读」,不泄露帧清单。\n if (this.widgetClient && (row === undefined || widgetPolicyOf(row) === 'deny')) {\n return host.fail(env.id, 'E_FORBIDDEN', 'widget connection is read-only')\n }\n // 未知类型直接 E_PROTOCOL(G6)。先查 scope 会让没有 sessions.read 的\n // 设备听到关于一个不存在帧的「权限不足」。\n if (row === undefined) {\n host.fail(env.id, 'E_PROTOCOL', 'unknown type: ' + env.type)\n return\n }\n const scope = scopeRejection(row, this.scopes)\n if (scope !== undefined) return host.fail(env.id, scope.code, scope.message)\n // 校验先于能力门,与迁移前「载荷形状先于 handler 看能力」的次序一致。\n const validated = validatePayload(row, env.payload)\n if (!validated.ok) return host.fail(env.id, validated.code, validated.message)\n const capability = capabilityRejection(row, host.bridge.capabilities)\n if (capability !== undefined) return host.fail(env.id, capability.code, capability.message)\n\n await dispatchFrame(row, this.context(env), validated.value)\n }\n\n /** 门持有的撤销标记由 device.revoke 行置位。 */\n markRevoked(): void {\n this.revoked = true\n }\n\n // ---------- 认证 ----------\n\n /** c2s.auth.prove 的行 handler 调用这里:验签、载入 scope、记账、落定。 */\n async authenticate(env: Envelope): Promise<void> {\n const host = this.host\n // 刚自撤销的 socket 正在关闭,不能再认证(注册表墓碑本来也会拒)。\n if (this.revoked || host === undefined) return\n const p = (env.payload ?? {}) as Partial<AuthProofPayload>\n if (!p.deviceId) {\n host.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.reportAuthFailure('device-id-missing', 4403)\n host.close(4403, 'deviceId required')\n return\n }\n const deviceId = sanitizeDeviceField(p.deviceId, MAX_DEVICE_ID_CHARS)\n if (!deviceId) {\n host.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.reportAuthFailure('device-id-missing', 4403)\n host.close(4403, 'deviceId required')\n return\n }\n const deviceName = sanitizeDeviceField(p.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(p.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n // 注册表状态先行:authorized() 仍是唯一准入判定,statusOf() 只把\n // 「未知 / 已撤销 / 可认证」拆开,让 4401 在日志里能分因。\n const deviceState = this.options.devices.statusOf(deviceId)\n const record = deviceState === 'active' ? this.options.devices.authorized(deviceId) : undefined\n const resumeCursor = typeof p.resumeCursor === 'number' && Number.isInteger(p.resumeCursor) && p.resumeCursor >= 0\n ? p.resumeCursor\n : undefined\n const challengeMatches = p.nonce === this.challenge.nonce &&\n p.audience === this.challenge.audience &&\n p.issuedAt === this.challenge.issuedAt &&\n p.expiresAt === this.challenge.expiresAt &&\n Date.now() <= this.challenge.expiresAt\n const proofValid = record?.publicKey !== undefined && typeof p.signature === 'string' && challengeMatches &&\n verifyAuthProof(record.publicKey, {\n deviceId,\n deviceName,\n appVersion,\n resumeCursor,\n ...this.challenge,\n }, p.signature)\n if (!proofValid || record === undefined) {\n const reason = classifyAuthFailure({\n device: deviceState,\n proof: p,\n challenge: this.challenge,\n now: Date.now(),\n })\n host.fail(env.id, 'E_AUTH', 'device proof missing or invalid')\n this.reportAuthFailure(reason, 4401)\n this.settle(false, 'invalid-proof')\n host.close(4401, 'invalid device proof')\n return\n }\n this.settle(true, 'success')\n this.authenticated = true\n this.deviceId = deviceId\n this.widgetClient = p.clientRole === 'widget'\n this.scopes = new Set(record.scopes ?? [])\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n if (!this.widgetClient) {\n this.options.devices.markAuthenticated(deviceId, deviceName, appVersion, Date.now())\n }\n host.deviceAuthenticated(deviceId)\n // 欢迎帧与重放由连接负责:认证是安全判定,欢迎是数据面展示。\n host.onAuthenticated({\n deviceId,\n scopes: this.scopes,\n widgetClient: this.widgetClient,\n resumeCursor: this.widgetClient ? undefined : resumeCursor,\n deviceName,\n appVersion,\n })\n }\n\n /**\n * 认证失败事件。来源只写 auditLabel 哈希(没有哈希函数时宁可不写,\n * 也不把原始 IP 落盘);attemptId 原样带出,便于把同一次握手的\n * upgrade / auth 两条日志串起来。\n */\n private reportAuthFailure(reason: string, closeCode: number): void {\n const source = this.options.auditLabel?.(this.options.source)\n this.options.diagnostics?.record('auth-failed', reason, {\n ...(source !== undefined ? { source } : {}),\n ...(this.options.attemptId !== undefined ? { attemptId: this.options.attemptId } : {}),\n closeCode,\n })\n }\n\n /** 限流记账与名额释放只此一处;exactly-once 由 settled 守卫保证。 */\n private settle(ok: boolean, reason: SettleReason): void {\n if (this.settled) return\n this.settled = true\n this.releaseAdmission()\n this.host?.settled(ok, reason)\n if (ok) {\n this.options.limiter.recordSuccess(this.options.source)\n return\n }\n const failure = this.options.limiter.recordFailure(this.options.source)\n if (failure.newlyBlocked) {\n const label = this.options.auditLabel?.(this.options.source) ?? this.options.source\n this.options.log(`authentication source blocked source=${label}`)\n }\n }\n\n private releaseAdmission(): void {\n this.admission?.release()\n this.admission = undefined\n }\n\n /** 把宿主投影成行看到的每帧上下文:id 绑进 send/fail,身份来自门。 */\n private context(env: Envelope): FrameContext {\n const host = this.host!\n return {\n frame: env,\n signal: host.signal,\n waitForWritable: host.waitForWritable?.bind(host),\n deviceId: this.deviceId,\n widgetClient: this.widgetClient,\n bridge: host.bridge,\n devices: host.devices,\n debug: host.debug,\n log: (message) => host.log(message),\n send: (type, payload) => host.send(type, payload, env.id),\n fail: (code, message) => host.fail(env.id, code, message),\n close: (code, reason) => host.close(code, reason),\n canReceive: (scope) => host.canReceive(scope),\n push: (type, payload, seq) => host.push(type, payload, seq),\n lastCursor: () => host.lastCursor(),\n replay: (entries) => host.replay(entries),\n replayDone: () => host.replayDone(),\n resync: () => host.resync(),\n bufferOpenEvents: (sessionId) => host.bufferOpenEvents(sessionId),\n discardOpenBuffer: (sessionId, buffer) => host.discardOpenBuffer(sessionId, buffer),\n flushOpenBuffer: (sessionId, buffer) => host.flushOpenBuffer(sessionId, buffer),\n closeOpenSession: (sessionId) => host.closeOpenSession(sessionId),\n nextLiveActivityGeneration: () => host.nextLiveActivityGeneration(),\n get liveActivityGeneration() {\n return host.liveActivityGeneration\n },\n get pendingLiveActivityId() {\n return host.pendingLiveActivityId\n },\n setPendingLiveActivityId: (activityId) => host.setPendingLiveActivityId(activityId),\n markRevoked: () => this.markRevoked(),\n prove: () => this.authenticate(env),\n enrollPushKey: (enrollKey) => host.enrollPushKey(enrollKey),\n revokeSiblings: (deviceId) => host.revokeSiblings(deviceId),\n }\n }\n}\n","import type { WebSocket } from 'ws'\nimport { PROTOCOL_VERSION } from './protocol.ts'\nimport type { Envelope } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { DeviceStore } from './token.ts'\nimport type { DeviceScope } from './device-auth.ts'\nimport { MAX_OUTBOUND_BUFFER_BYTES } from './connection-policy.ts'\nimport { AuthRateLimiter } from './auth-rate-limit.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\nimport type { DiagnosticsRecorder } from './diagnostics.ts'\nimport { ConnectionGate, type AuthenticatedIdentity, type GateHost } from './connection-gate.ts'\nimport type { OpenEventBuffer, S2CType } from './wire-registry.ts'\n\nexport {\n AUTH_TIMEOUT_MS,\n MAX_OUTBOUND_BUFFER_BYTES,\n PRE_AUTH_FRAME_BYTES,\n} from './connection-policy.ts'\n\nexport interface ConnectionStateDeps {\n bridge: HostBridge\n devices: DeviceStore\n serverVersion: string\n /** Stable Host identity included in every signed challenge. */\n audience: string\n debug?: boolean\n log: (message: string) => void\n /** 客户端身份,供连接门做限流;缺省时按本地回环源处理。 */\n source?: string\n /** 认证名额与失败记账由连接门持有(见 connection-gate.ts)。 */\n rateLimiter?: AuthRateLimiter\n /** 审计标签函数:把客户端身份变成短哈希供日志使用;缺省时直接用原值。 */\n auditLabel?: (value: string) => string\n /** 诊断事件出口;连接建立 / 非正常关闭 / socket error 各记一条。 */\n diagnostics?: DiagnosticsRecorder\n /** 客户端带来的 attemptId(WS 升级 query `at`),只记录不校验。 */\n attemptId?: string\n /** Called once when the socket closes (cleanly or not). */\n onClosed?: (connection: BridgeConnection) => void\n /**\n * 认证落定通知:纯观测用。名额释放与限流记账已由连接门内部完成,\n * 接入方不再需要据此做任何事;测试用它断言「只落定一次」。\n */\n onAuthenticationSettled?: (ok: boolean, reason: 'success' | 'invalid-proof' | 'timeout' | 'closed') => void\n /** Emits a privacy-preserving audit event after a valid hello. */\n onDeviceAuthenticated?: (deviceId: string) => void\n /**\n * Zero-touch push enrollment: fired when a distributed app presents the\n * distributor's shared enrollKey during c2s.push.register. May perform the\n * relay round-trip; the register handler awaits it before evaluating push\n * readiness, so the very first registration can switch the feature on.\n */\n onPushEnrollKey?: (enrollKey: string) => Promise<void> | void\n /**\n * c2s.device.revoke (device unbind): the registered device revoked itself\n * and every OTHER active connection of that device must be dropped.\n * `except` is the connection that sent the frame — its close is already\n * owned by the handler (ack first, then 4401). The hook must not flip the\n * registry itself; the handler calls DeviceStore.revoke before this.\n */\n onDeviceRevoke?: (deviceId: string, except: BridgeConnection) => Promise<unknown> | unknown\n}\n\n/**\n * One connected phone. Implements BridgeSink so the HostBridge can push\n * projected frames and replays.\n *\n * 本模块现在只剩传输与连接自有状态:socket、send/fail/close、背压、idle 计时、\n * welcome 发射、sink 台账、打开中的会话缓冲、Live Activity 代次。帧的规则\n * (pre-auth、认证、授权、分发)全部属于 ConnectionGate;帧的事实属于\n * wire-registry。连接通过实现 GateHost 把这两者接起来。\n */\nexport class BridgeConnection implements BridgeSink {\n private closed = false\n /**\n * Realtime events that arrive while a session's history snapshot is in\n * flight. The wire contract requires tail first; sending these immediately\n * lets the later tail roll the client back over messages it just rendered.\n */\n private readonly openingSessionEvents = new Map<string, Array<{\n type: string\n payload: unknown\n seq?: number\n }>>()\n private readonly openSessions = new Set<string>()\n private liveActivityRegistrationGeneration = 0\n private pendingLiveActivityId?: string\n private lastActivity = Date.now()\n private readonly streamAbort = new AbortController()\n /** 建连时刻:关闭事件用它算存活时长。 */\n private readonly openedAt = Date.now()\n /** 来源的审计哈希;没有 auditLabel 时留空,绝不记录原始 IP。 */\n private readonly sourceLabel: string | undefined\n\n /** True when no inbound frame arrived within maxIdleMs. */\n isStale(now: number, maxIdleMs: number): boolean {\n return now - this.lastActivity > maxIdleMs\n }\n\n /** 认证成功后由门回填,供 welcome 与 sink 注册使用。 */\n private identity: AuthenticatedIdentity | undefined\n\n /** 帧规则(pre-auth、认证、授权、分发)的属主;由接入方创建并交进来。 */\n readonly gate: ConnectionGate\n\n constructor(\n private readonly ws: WebSocket,\n private readonly deps: ConnectionStateDeps,\n /** 由接入方预先创建的门(提前占名额以在握手前限流);缺省时连接自建。 */\n gate: ConnectionGate = new ConnectionGate({\n source: deps.source ?? 'local',\n devices: deps.devices,\n audience: deps.audience,\n limiter: deps.rateLimiter ?? new AuthRateLimiter(),\n log: (message) => deps.log(message),\n ...(deps.auditLabel ? { auditLabel: deps.auditLabel } : {}),\n ...(deps.diagnostics ? { diagnostics: deps.diagnostics } : {}),\n ...(deps.attemptId !== undefined ? { attemptId: deps.attemptId } : {}),\n }),\n ) {\n const connection = this\n const sourceLabel = deps.auditLabel?.(deps.source ?? 'local')\n this.sourceLabel = sourceLabel\n const host: GateHost = {\n signal: this.streamAbort.signal,\n waitForWritable: async () => {\n while (this.ws.bufferedAmount > 256 * 1024) {\n this.streamAbort.signal.throwIfAborted()\n await new Promise(resolve => setTimeout(resolve, 10))\n }\n this.streamAbort.signal.throwIfAborted()\n },\n get bridge() { return connection.deps.bridge },\n get devices() { return connection.deps.devices },\n get debug() { return deps.debug === true },\n log: (message) => deps.log(message),\n send: (type: S2CType, payload, id) => connection.send(type, payload, id),\n fail: (id, code, message) => connection.fail(id, code, message),\n close: (code, reason) => connection.close(code, reason),\n canReceive: (scope) => connection.canReceive(scope),\n push: (type, payload, seq) => connection.push(type, payload, seq),\n lastCursor: () => connection.lastCursor(),\n replay: (entries) => connection.replay(entries),\n replayDone: () => connection.replayDone(),\n resync: () => connection.resync(),\n bufferOpenEvents: (sessionId) => connection.bufferOpenEvents(sessionId),\n discardOpenBuffer: (sessionId, buffer) => connection.discardOpenBuffer(sessionId, buffer),\n flushOpenBuffer: (sessionId, buffer) => connection.flushOpenBuffer(sessionId, buffer),\n closeOpenSession: (sessionId) => connection.closeOpenSession(sessionId),\n nextLiveActivityGeneration: () => {\n connection.liveActivityRegistrationGeneration += 1\n return connection.liveActivityRegistrationGeneration\n },\n get liveActivityGeneration() { return connection.liveActivityRegistrationGeneration },\n get pendingLiveActivityId() { return connection.pendingLiveActivityId },\n setPendingLiveActivityId: (activityId) => { connection.pendingLiveActivityId = activityId },\n markRevoked: () => connection.gate.markRevoked(),\n enrollPushKey: (enrollKey) => deps.onPushEnrollKey?.(enrollKey),\n revokeSiblings: (deviceId) => deps.onDeviceRevoke?.(deviceId, connection),\n onAuthenticated: (identity) => connection.onAuthenticated(identity),\n deviceAuthenticated: (deviceId) => deps.onDeviceAuthenticated?.(deviceId),\n settled: (ok, reason) => deps.onAuthenticationSettled?.(ok, reason),\n }\n this.gate = gate\n\n ws.on('message', (data) => {\n this.lastActivity = Date.now()\n void gate.handleFrame(String(data)).catch((error) => {\n // A frame must never take down the host process: fail only this\n // socket. Protocol-shape mistakes are handled inside the gate; this\n // is the last line of defense for unexpected handler errors.\n if (this.deps.debug === true) this.deps.log('frame handler failed: ' + String(error))\n if (this.closed) return\n this.terminate()\n })\n })\n ws.on('close', (code?: number) => {\n this.onClose(code)\n deps.onClosed?.(this)\n })\n // socket 层错误此前是空处理:手机只看到「连接断开」,电脑端毫无线索。\n // 这里只记录事实(错误消息、是否已认证);close 事件仍会跟上,原有\n // 「error 之后由 close 收尾」的语义不变。\n ws.on('error', (error?: unknown) => {\n this.deps.diagnostics?.record('connection-error', 'socket-error', {\n ...(this.sourceLabel !== undefined ? { source: this.sourceLabel } : {}),\n ...(this.deps.attemptId !== undefined ? { attemptId: this.deps.attemptId } : {}),\n authenticated: this.identity !== undefined,\n detail: error instanceof Error ? error.message : String(error ?? 'unknown'),\n })\n })\n // The challenge goes out the moment the socket is live; the hello deadline\n // is the gate's, so an upgrade that never produced a socket still has the\n // attach timeout to release its admission slot.\n gate.attach(host)\n // 建连成功一行:来源哈希 + attemptId。放在 attach 之后,保证日志顺序\n // 与线上事件顺序一致(先说挑战,再说连接已建立)。\n this.deps.diagnostics?.record('connection-opened', 'socket-open', {\n ...(sourceLabel !== undefined ? { source: sourceLabel } : {}),\n ...(deps.attemptId !== undefined ? { attemptId: deps.attemptId } : {}),\n })\n }\n\n /** Hard-drop the socket (server-side stale sweep). */\n terminate(): void {\n this.ws.terminate()\n }\n\n /** Protocol-compliant idle timeout: let the peer observe a normal 1001 close. */\n closeIdle(): void {\n this.close(1001, 'idle timeout')\n }\n\n /** Announce an orderly plugin/data-plane shutdown before closing the socket. */\n closeForServerStop(): void {\n this.fail(undefined, 'E_INTERNAL', 'server stopping')\n this.close(1001, 'server stopping')\n }\n\n /** Used by dependency-lifecycle cleanup to avoid closing a replacement bridge. */\n isAttachedTo(bridge: HostBridge): boolean {\n return this.deps.bridge === bridge\n }\n\n /** Device identity once hello succeeded; undefined before that. */\n get connectedDeviceId(): string | undefined {\n return this.identity?.deviceId\n }\n\n get suppressesAlertPush(): boolean {\n return this.identity?.widgetClient !== true\n }\n\n // ---------- BridgeSink ----------\n\n /** S→C permission gate consulted by the bridge for every broadcast/replay\n * frame: a device only receives what its scopes grant (R1/P2). */\n canReceive(scope: DeviceScope): boolean {\n return this.identity?.scopes.has(scope) ?? false\n }\n\n push(type: string, payload: unknown, seq?: number): void {\n if (type === 's2c.notify') payload = { ...(payload as object), hostAudience: this.deps.audience }\n if (type === 's2c.session.event') {\n const sessionId = (payload as { sessionId?: unknown } | undefined)?.sessionId\n if (typeof sessionId === 'string') {\n const buffered = this.openingSessionEvents.get(sessionId)\n if (buffered) {\n buffered.push({ type, payload, ...(seq !== undefined ? { seq } : {}) })\n return\n }\n }\n }\n if (this.deps.debug === true) this.deps.log('push ' + type + ' seq=' + String(seq))\n this.send(type, payload, undefined, seq)\n }\n\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void {\n for (const entry of entries) this.push(entry.type, entry.payload, entry.seq)\n }\n\n replayDone(): void {\n this.push('s2c.resume.done', {})\n }\n\n resync(): void {\n this.push('s2c.resync', { reason: 'gap' })\n }\n\n lastCursor(): number {\n return this.deps.bridge.currentCursor()\n }\n\n // ---------- lifecycle ----------\n\n /** 认证成功:门只做安全判定,欢迎与重放归连接(数据面)。 */\n private onAuthenticated(identity: AuthenticatedIdentity): void {\n this.identity = identity\n const cursor = identity.resumeCursor\n // Each replayed business frame is authorized separately by HostBridge.\n const canResume = cursor !== undefined && this.deps.bridge.canResumeFrom(cursor)\n // Welcome strictly precedes any replayed pushes.\n this.send('s2c.welcome', {\n protocolVersion: PROTOCOL_VERSION,\n serverVersion: this.deps.serverVersion,\n deviceId: identity.deviceId,\n scopes: [...identity.scopes],\n capabilities: this.deps.bridge.capabilities,\n cursor: this.deps.bridge.currentCursor(),\n resumed: canResume,\n })\n if (!identity.widgetClient) this.deps.bridge.addSink(this)\n if (cursor !== undefined) {\n if (canResume) {\n this.deps.bridge.resumeFrom(cursor, this)\n } else {\n this.resync()\n }\n }\n }\n\n private onClose(code?: number): void {\n if (this.closed) return\n this.closed = true\n // 非正常关闭(不是 1000 normal / 1001 going-away)各一行:关闭码、\n // 是否已认证、存活时长与来源哈希。正常关闭不记,避免每次退出都刷屏。\n // 缺省(没有 close 帧,如底层 socket 直接消失)按 1005 处理。\n const closeCode = code ?? 1005\n if (closeCode !== 1000 && closeCode !== 1001) {\n this.deps.diagnostics?.record('connection-closed', 'abnormal-close', {\n ...(this.sourceLabel !== undefined ? { source: this.sourceLabel } : {}),\n ...(this.deps.attemptId !== undefined ? { attemptId: this.deps.attemptId } : {}),\n closeCode,\n durationMs: Date.now() - this.openedAt,\n authenticated: this.identity !== undefined,\n })\n }\n this.streamAbort.abort()\n // Unsettled sockets (never proven) settle as 'closed' and give back their\n // admission slot; already-settled ones are a no-op inside the gate.\n this.gate.onClose()\n for (const id of this.openSessions) {\n this.deps.bridge.markSinkClosed(this, id)\n }\n this.openSessions.clear()\n this.openingSessionEvents.clear()\n this.deps.bridge.dropSinkSessions(this)\n if (this.identity !== undefined) this.deps.bridge.removeSink(this)\n }\n\n private close(code: number, reason: string): void {\n if (this.closed) return\n try {\n this.ws.close(code, reason)\n } catch {\n this.ws.terminate()\n }\n }\n\n private send(type: string, payload: unknown, id?: string, seq?: number): void {\n const envelope: Envelope = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n ...(id !== undefined ? { id } : {}),\n ...(seq !== undefined ? { seq } : {}),\n payload,\n }\n if (this.ws.readyState !== this.ws.OPEN) return\n if (this.ws.bufferedAmount > MAX_OUTBOUND_BUFFER_BYTES) {\n this.close(1013, 'client too slow')\n return\n }\n this.ws.send(JSON.stringify(envelope))\n }\n\n private fail(id: string | undefined, code: WireErrorCode, message: string): void {\n this.send('s2c.error', { code, message }, id)\n }\n\n // ---------- connection-owned session bookkeeping ----------\n\n private bufferOpenEvents(sessionId: string): OpenEventBuffer {\n const frames: OpenEventBuffer['frames'] = []\n const buffer: OpenEventBuffer = { frames }\n this.openingSessionEvents.set(sessionId, frames)\n return buffer\n }\n\n private discardOpenBuffer(sessionId: string, buffer: OpenEventBuffer): void {\n if (this.openingSessionEvents.get(sessionId) === buffer.frames) {\n this.openingSessionEvents.delete(sessionId)\n }\n }\n\n private flushOpenBuffer(sessionId: string, buffer: OpenEventBuffer): boolean {\n if (this.openingSessionEvents.get(sessionId) !== buffer.frames) return false\n this.openSessions.add(sessionId)\n this.deps.bridge.markSinkOpen(this, sessionId)\n this.openingSessionEvents.delete(sessionId)\n for (const frame of buffer.frames) this.push(frame.type, frame.payload, frame.seq)\n return true\n }\n\n private closeOpenSession(sessionId: string): void {\n this.openingSessionEvents.delete(sessionId)\n this.openSessions.delete(sessionId)\n this.deps.bridge.markSinkClosed(this, sessionId)\n }\n}\n\n/** 供接入方引用:帧上下文的类型仍由 wire-registry 属主。 */\nexport type { S2CType }\n","export const DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE = 8\nexport const MAX_FUNNEL_CONNECTIONS_PER_SOURCE = 16\n\nexport function normalizeFunnelConnectionLimit(value: unknown): number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= MAX_FUNNEL_CONNECTIONS_PER_SOURCE\n ? value\n : DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { access, mkdir, readdir, rm, writeFile } from 'node:fs/promises'\nimport { constants as fsConstants } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { expandHome } from './token.ts'\nimport { normalizeFunnelConnectionLimit } from './funnel-policy.ts'\n\nexport type RemotePhase =\n | 'disabled'\n | 'starting'\n | 'login_required'\n | 'online'\n | 'error'\n | 'unavailable'\n | 'stopped'\n\nexport interface RemoteStatus {\n provider: 'tailscale-funnel'\n phase: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n}\n\nexport interface RemoteSupervisorOptions {\n enabled: boolean\n hostname: string\n statePath: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n maxConnectionsPerSource?: number\n log: (message: string) => void\n}\n\ninterface HelperEvent {\n phase?: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n}\n\nconst RESTART_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000]\n/**\n * When the helper dies with a Go panic, the supervisor keeps only the last\n * 2KB of stderr for the status message — enough for a summary but not for the\n * full goroutine dump that would pinpoint the crash (and be needed to report\n * upstream). Each crash therefore also writes the full stderr into the state\n * directory as crash-<millis>.log (0600), and the log line points at it.\n * Only the newest dumps are retained to bound disk usage.\n */\nexport const MAX_CRASH_DUMPS = 6\n/**\n * Throttle for configuration-level failures (helper binary missing, state dir\n * unwritable). The environment will not self-heal between attempts, so the\n * previous \"1s..30s exponential\" backoff was a CPU/for-loop on a misconfigured\n * Host. 60s matches the APNs sender-failure throttle on the host plugin\n * (index.ts SENDER_FAILURE_RETRY_MS) and the relay enrollment throttle.\n */\nconst UNAVAILABLE_RETRY_MS = 60_000\n\n/**\n * Exposed for tests: the backoff schedule depends on the failure category\n * (unavailable = config, won't self-heal; crash = transient). Tying this to\n * the constants above keeps the public schedule easy to assert against.\n */\nexport function restartDelayMs(kind: 'unavailable' | 'crash', attempt: number): number {\n if (kind === 'unavailable') return UNAVAILABLE_RETRY_MS\n const index = Math.min(attempt, RESTART_DELAYS_MS.length - 1)\n // RESTART_DELAYS_MS is a non-empty literal array, so the indexed value is\n // always defined; the explicit fallback is just to satisfy the strict\n // noUncheckedIndexedAccess setting without a non-null assertion.\n return RESTART_DELAYS_MS[index] ?? RESTART_DELAYS_MS[RESTART_DELAYS_MS.length - 1]!\n}\n\nexport const DEFAULT_REMOTE_HOSTNAME = 'dsh-deeppilot'\n\n/** Preserve custom node names while migrating every pre-DeepPilot default. */\nexport function normalizeRemoteHostname(value: string | undefined): string {\n const hostname = value?.trim() ?? ''\n if (hostname === '' || ['dsh-phone', 'dsh-pocket', 'harnesspocket'].includes(hostname.toLowerCase())) {\n return DEFAULT_REMOTE_HOSTNAME\n }\n return hostname\n}\n\nexport function tunnelHelperArguments(\n originURL: string,\n statePath: string,\n options: Pick<RemoteSupervisorOptions, 'hostname' | 'funnelPort' | 'maxConnectionsPerSource'>,\n): string[] {\n return [\n '--origin', originURL,\n '--hostname', normalizeRemoteHostname(options.hostname),\n '--state-dir', statePath,\n '--port', String(options.funnelPort ?? 443),\n '--max-connections-per-source', String(normalizeFunnelConnectionLimit(options.maxConnectionsPerSource)),\n ]\n}\n\n/**\n * Summarize a fatal helper crash from its stderr tail. Go panics dump the\n * actual reason on the `panic:` line while the trailing stack frames are\n * `file.go:NN +0xOFF` noise — the old behavior of reporting only the final\n * line surfaced exactly that noise (e.g. `.../singleflight.go:194 +0x45c`)\n * and hid the real panic message. Keep the crash header line (a raw\n * `panic:`, a runtime `fatal error:`, or net/http's `http: panic serving`),\n * plus an optional `[signal ...]` continuation and the first non-runtime\n * stack frame's symbol + `file.go:NN` as a location hint. Pointer offsets\n * are not architecture stable, so they are deliberately dropped.\n *\n * The frame symbol sits on the line just above the indented source line in a\n * Go dump, e.g.:\n * tailscale.com/net/dnscache.(*Resolver).lookupIP(0x1400012c000, {...})\n * tailscale.com@v1.102.3/net/dnscache/dnscache.go:604 +0x25c\n * The first symbol (shortened) is used because a bare `file.go:NN` is not\n * unique across tailscale (singleflight.go is one example).\n */\nexport function helperCrashSummary(stderr: string): string {\n const lines = stderr.split(/\\r?\\n/)\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim()\n if (line === '') continue\n // `panic:` line, a runtime `fatal error:`, or net/http's\n // `http: panic serving` (optionally prefixed by Go's log timestamp).\n const logTimestamp = /^(\\d{4}\\/\\d{2}\\/\\d{2} \\d{2}:\\d{2}:\\d{2} )/.exec(line)?.[1] ?? ''\n if (!/^(panic:|fatal error:|http: panic serving)/.test(logTimestamp.length > 0 ? line.slice(logTimestamp.length) : line)) {\n continue\n }\n let summary = logTimestamp.length > 0 ? line.slice(logTimestamp.length) : line\n const next = lines[i + 1]?.trim()\n if (next?.startsWith('[signal')) summary += ' ' + next\n // Walk the dump for the first non-runtime frame: symbol line followed by\n // an indented `path/file.go:NN` source line. Track the last symbol seen\n // before each source line.\n let lastSymbol: string | undefined\n for (let j = i + 1; j < lines.length; j++) {\n const frame = lines[j]!\n const trimmed = frame.trim()\n if (trimmed === '') continue\n // A symbol/argument line: `package.(*Type).method(args)` — not a source\n // line (which is indented and ends with `file.go:NN +0x…`). Take the\n // last symbol preceding a source line; the first source line after the\n // crash header is the crash point.\n const sourceMatch = /^\\s+(\\S+\\.go:\\d+)/.exec(frame)\n if (sourceMatch !== null) {\n const source = sourceMatch[1]!\n if (source.startsWith('runtime/') || source.startsWith('runtime\\\\')) {\n // runtime/debug.Stack() noise — skip to the next frame.\n lastSymbol = undefined\n continue\n }\n const slash = Math.max(source.lastIndexOf('/'), source.lastIndexOf('\\\\'))\n const file = slash >= 0 ? source.slice(slash + 1) : source\n const symbol = lastSymbol !== undefined ? shortenSymbol(lastSymbol) : undefined\n summary += ' @ ' + (symbol !== undefined ? `${symbol} (${file})` : file)\n break\n }\n // Otherwise this is a frame symbol (or a goroutine heading / created-by\n // note); remember the last one so the following source line can pair.\n if (trimmed.includes('(') || trimmed.endsWith(')')) {\n lastSymbol = trimmed\n }\n }\n return summary.replace(/\\s+/g, ' ').trim()\n }\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim()\n if (line !== '') return line\n }\n return ''\n}\n\n/** Shrink a Go frame symbol to `pkg.Receiver.method` form (drop module-root\n * path, generic type arguments and the trailing call arguments), so the\n * summary hint stays readable. Keeps a `.funcN` closure suffix when present\n * (e.g. `doCall.func2`), since it identifies the exact closure.\n *\n * A symbol looks like:\n * tailscale.com/util/singleflight.(*Group[...]).doCall.func2(0x…, …)\n * The trailing call arguments form a balanced paren group that starts at\n * the function name — that group is stripped first; generics appear only\n * inside `(*Group[...])` receiver brackets before the final method dot, so\n * they are removed by trimming from the first `[` to the matching `]`.\n */\nexport function shortenSymbol(symbol: string): string {\n const trimmed = symbol.trim()\n let end = trimmed.length\n if (trimmed.endsWith(')')) {\n let depth = 0\n for (let i = trimmed.length - 1; i >= 0; i--) {\n const ch = trimmed[i]!\n if (ch === ')') depth++\n else if (ch === '(') {\n depth--\n if (depth === 0) {\n end = i\n break\n }\n }\n }\n }\n let name = trimmed.slice(0, end).trim()\n // Remove generic instantiations `[...]` inside receiver/type brackets.\n const bracketOpen = name.indexOf('[')\n if (bracketOpen >= 0) {\n const bracketClose = name.indexOf(']', bracketOpen)\n if (bracketClose > bracketOpen) {\n name = name.slice(0, bracketOpen) + name.slice(bracketClose + 1)\n }\n }\n // Keep only the last path segment (after the final /).\n const slash = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\\\'))\n return slash >= 0 ? name.slice(slash + 1) : name\n}\n\n/** A normalized fatal-crash analysis used by the exit handler to produce a\n * bounded status message and, when a full dump is available, persist it. */\nexport interface CrashAnalysis {\n summary: string\n dumpPath?: string\n}\n\n/** Shared by crash summary and dump: whether the stderr looks like a Go\n * panic/fatal-error crash (vs. an ordinary exit). */\nfunction looksLikeCrash(stderr: string): boolean {\n return /(?:^|\\n)\\s*(?:\\d{4}\\/\\d{2}\\/\\d{2} \\d{2}:\\d{2}:\\d{2} )?(?:panic:|fatal error:|http: panic serving)/.test(stderr)\n}\n\n/** Turn a crash stderr tail into a status message and optionally persist the\n * full dump under the state dir. Keeps at most MAX_CRASH_DUMPS dump files. */\nexport async function analyzeCrashExit(stderr: string, statePath: string): Promise<CrashAnalysis> {\n const summary = helperCrashSummary(stderr) || 'helper exited'\n if (!looksLikeCrash(stderr) || stderr.trim() === '') return { summary }\n const target = statePath.trim()\n if (target === '') return { summary }\n try {\n const file = join(target, `crash-${Date.now()}.log`)\n await writeFile(file, stderr, { mode: 0o600 })\n await trimCrashDumps(target)\n return { summary, dumpPath: file }\n } catch {\n return { summary }\n }\n}\n\n/** Remove oldest crash-*.log files beyond MAX_CRASH_DUMPS. */\nasync function trimCrashDumps(dir: string): Promise<void> {\n let names: string[]\n try {\n names = (await readdir(dir)).filter((name) => name.startsWith('crash-') && name.endsWith('.log'))\n } catch {\n return\n }\n if (names.length <= MAX_CRASH_DUMPS) return\n names.sort()\n const excess = names.length - MAX_CRASH_DUMPS\n await Promise.all(names.slice(0, excess).map((name) => rm(join(dir, name), { force: true }).catch(() => {})))\n}\n\n/** Parse one helper IPC line without ever evaluating or interpolating it. */\nexport function parseHelperEvent(line: string): HelperEvent | null {\n try {\n const value = JSON.parse(line) as Record<string, unknown>\n const phases: RemotePhase[] = ['starting', 'login_required', 'online', 'error', 'stopped']\n if (typeof value.phase !== 'string' || !phases.includes(value.phase as RemotePhase)) return null\n return {\n phase: value.phase as RemotePhase,\n ...(typeof value.publicURL === 'string' ? { publicURL: value.publicURL } : {}),\n ...(typeof value.authURL === 'string' ? { authURL: value.authURL } : {}),\n ...(typeof value.message === 'string' ? { message: value.message.slice(0, 500) } : {}),\n }\n } catch {\n return null\n }\n}\n\nfunction isTailscaleAuthURL(value: string | undefined): boolean {\n if (!value) return false\n try {\n const url = new URL(value)\n return url.protocol === 'https:' &&\n (url.hostname === 'login.tailscale.com' || url.hostname.endsWith('.login.tailscale.com'))\n } catch {\n return false\n }\n}\n\n/** Translate Node's platform/architecture names to the GOOS/GOARCH directory\n * names used by the committed helper matrix. */\nexport function bundledHelperPlatformDir(\n platform: NodeJS.Platform = process.platform,\n arch: string = process.arch,\n): string {\n const goos = platform === 'win32' ? 'windows' : platform\n const goarch = arch === 'x64' ? 'amd64' : arch\n return `${goos}-${goarch}`\n}\n\n/** Build the list of candidate locations for the embedded tunnel helper, in\n * priority order. The first existing executable wins at start() time. The\n * order matters: explicit config (handled by the caller) > npm install\n * layout > DSH-bundled layout > user data dir. */\nexport function bundledHelperCandidates(\n platform: NodeJS.Platform = process.platform,\n arch: string = process.arch,\n): string[] {\n const here = dirname(fileURLToPath(import.meta.url))\n const pkgRoot = resolve(here, '..')\n const fileName = platform === 'win32' ? 'dsh-deeppilot-tunnel.exe' : 'dsh-deeppilot-tunnel'\n const platformDir = bundledHelperPlatformDir(platform, arch)\n\n const candidates: string[] = []\n // 1. Standard npm layout: <pkgRoot>/bin/<os>-<arch>/<file>\n candidates.push(resolve(pkgRoot, 'bin', platformDir, fileName))\n // 2. DSH sometimes relocates lib/ into apps/web or a build cache while\n // leaving bin/ beside the original package root. Try a few hops up.\n candidates.push(resolve(pkgRoot, '..', '..', '..', 'node_modules', 'dsh-deeppilot', 'bin', platformDir, fileName))\n candidates.push(resolve(pkgRoot, '..', '..', 'dsh-deeppilot', 'bin', platformDir, fileName))\n candidates.push(resolve(pkgRoot, '..', '..', '..', '..', 'node_modules', 'dsh-deeppilot', 'bin', platformDir, fileName))\n\n // 3. As a last resort, ask the loader to resolve the binary via Node's\n // package resolution. Works when bin/ is shipped but import.meta.url\n // points somewhere unexpected.\n try {\n const require = createRequire(import.meta.url)\n const resolved = require.resolve(`dsh-deeppilot/bin/${platformDir}/${fileName}`)\n if (!candidates.includes(resolved)) candidates.push(resolved)\n } catch {\n /* package.json exports field may not list bin/ — ignore */\n }\n\n // 4. User data dir fallback so power users can drop a custom build in.\n const home = process.env.DSH_HOME?.trim()\n || process.env.HOME\n || process.env.USERPROFILE\n if (home && home.length > 0) {\n const dataDir = resolve(home, '.dsh')\n candidates.push(join(dataDir, 'deeppilot', 'bin', platformDir, fileName))\n }\n\n return candidates\n}\n\n/** Owns exactly one embedded tunnel helper and restarts it after failures. */\nexport class RemoteSupervisor {\n private child: ChildProcess | undefined\n private restartTimer: NodeJS.Timeout | undefined\n private restartAttempt = 0\n private stopping = false\n private statusValue: RemoteStatus\n\n constructor(private readonly options: RemoteSupervisorOptions) {\n this.statusValue = {\n provider: 'tailscale-funnel',\n phase: options.enabled ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n }\n\n status(): RemoteStatus {\n return { ...this.statusValue }\n }\n\n async start(originURL: string): Promise<void> {\n if (!this.options.enabled || this.child !== undefined || this.stopping) return\n const statePath = expandHome(this.options.statePath)\n // Resolve a usable helper path. An explicit remote.helperPath wins; if it\n // is missing, fall back to a small set of candidate locations so that\n // DSH-bundled layouts and manually-dropped builds still work.\n const configured = this.options.helperPath?.trim() ?? ''\n const candidates = configured\n ? [expandHome(configured)]\n : bundledHelperCandidates()\n let helper: string | undefined\n let lastError: unknown\n for (const candidate of candidates) {\n try {\n await access(candidate, fsConstants.X_OK)\n helper = candidate\n break\n } catch (error) {\n lastError = error\n }\n }\n if (helper === undefined) {\n // A dispose() that raced these checks must not be overwritten by a\n // late failure report.\n if (this.stopping) return\n const platform = `${process.platform}-${process.arch}`\n const message = configured\n ? `embedded tunnel helper unavailable: ${configured}: ${String(lastError ?? 'not found')}`\n : `embedded tunnel helper not found for ${platform} (tried: ${candidates.join(', ')}); set remote.helperPath to override`\n this.setStatus({ phase: 'unavailable', message })\n this.scheduleRestart(originURL, 'unavailable')\n return\n }\n try {\n await mkdir(statePath, { recursive: true, mode: 0o700 })\n } catch (error) {\n if (this.stopping) return\n this.setStatus({ phase: 'unavailable', message: `cannot create remote state dir: ${String(error)}` })\n this.scheduleRestart(originURL, 'unavailable')\n return\n }\n\n // dispose() may have run while the filesystem checks above were in\n // flight; spawning afterwards would orphan a helper process that nothing\n // will ever reap (dispose already returned seeing no child).\n if (this.stopping) return\n\n this.setStatus({ phase: 'starting', message: undefined })\n const child = spawn(helper, tunnelHelperArguments(originURL, statePath, this.options), {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n PATH: process.env.PATH ?? '/usr/bin:/bin',\n TMPDIR: process.env.TMPDIR ?? '/tmp',\n },\n })\n this.child = child\n if (child.stdout === null || child.stderr === null) {\n this.setStatus({ phase: 'error', message: 'helper stdio unavailable' })\n child.kill('SIGTERM')\n return\n }\n\n let stdoutBuffer = ''\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', (chunk: string) => {\n stdoutBuffer += chunk\n const lines = stdoutBuffer.split('\\n')\n stdoutBuffer = lines.pop() ?? ''\n for (const line of lines) this.acceptLine(line)\n })\n\n // Keep enough stderr to write a full crash dump on exit; the summary the\n // user sees is derived from this buffer.\n let stderrBuffer = ''\n child.stderr.setEncoding('utf8')\n child.stderr.on('data', (chunk: string) => {\n stderrBuffer = (stderrBuffer + chunk).slice(-16_000)\n })\n\n child.once('error', (error) => {\n this.setStatus({ phase: 'error', message: `helper launch failed: ${String(error)}` })\n })\n child.once('exit', () => {\n if (this.child === child) this.child = undefined\n if (this.stopping) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n void analyzeCrashExit(stderrBuffer, this.options.statePath).then((analysis) => {\n if (this.stopping) return\n const message = analysis.summary + (analysis.dumpPath !== undefined ? ` (full dump: ${analysis.dumpPath})` : '')\n this.setStatus({\n phase: 'error',\n message,\n })\n this.scheduleRestart(originURL, 'crash')\n })\n })\n }\n\n async dispose(): Promise<void> {\n this.stopping = true\n if (this.restartTimer !== undefined) clearTimeout(this.restartTimer)\n this.restartTimer = undefined\n const child = this.child\n this.child = undefined\n if (child === undefined) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n await new Promise<void>((resolveDone) => {\n const force = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n }, 3_000)\n child.once('exit', () => {\n clearTimeout(force)\n resolveDone()\n })\n child.kill('SIGTERM')\n })\n this.setStatus({ phase: 'stopped', message: undefined })\n }\n\n private acceptLine(line: string): void {\n const event = parseHelperEvent(line)\n if (event === null || event.phase === undefined) return\n if (event.phase === 'login_required') {\n // UserLogf is diagnostic output, not a typed auth callback. Accept only\n // the known login origin, and never let an incidental runtime URL knock\n // an already-online Funnel back into the setup state.\n if (this.statusValue.phase === 'online' || !isTailscaleAuthURL(event.authURL)) return\n }\n if (event.phase === 'online') this.restartAttempt = 0\n this.setStatus({ ...event, phase: event.phase })\n }\n\n private scheduleRestart(originURL: string, kind: 'unavailable' | 'crash' = 'crash'): void {\n if (this.stopping || this.restartTimer !== undefined) return\n // Configuration-level failures (helper missing, state dir unwritable)\n // cannot self-heal in a tight loop; throttle to one probe per minute so\n // a misconfigured Host stays observable without becoming a CPU sink.\n // Crashes still get the original exponential backoff so a transient\n // helper failure recovers quickly.\n const delay = kind === 'unavailable'\n ? UNAVAILABLE_RETRY_MS\n : RESTART_DELAYS_MS[Math.min(this.restartAttempt, RESTART_DELAYS_MS.length - 1)]\n if (kind === 'crash') this.restartAttempt += 1\n this.restartTimer = setTimeout(() => {\n this.restartTimer = undefined\n void this.start(originURL)\n }, delay)\n this.restartTimer.unref?.()\n }\n\n private setStatus(next: Partial<RemoteStatus> & Pick<RemoteStatus, 'phase'>): void {\n const cleared: Partial<RemoteStatus> = next.phase === 'online'\n ? { authURL: undefined, message: undefined }\n : next.phase === 'login_required'\n ? { publicURL: undefined, message: undefined }\n : next.phase === 'starting' || next.phase === 'stopped' || next.phase === 'disabled'\n ? { publicURL: undefined, authURL: undefined, message: undefined }\n : {}\n this.statusValue = {\n ...this.statusValue,\n ...cleared,\n ...next,\n updatedAt: Date.now(),\n }\n if (next.phase === 'online') {\n this.options.log('remote Funnel online')\n } else if (next.phase === 'login_required') {\n this.options.log('remote Funnel requires browser authorization')\n } else if (next.phase === 'error' || next.phase === 'unavailable') {\n this.options.log(`remote Funnel ${next.phase}: ${next.message ?? 'unknown error'}`)\n }\n }\n}\n","export const DEFAULT_LOCAL_PORT = 3098\nexport const MIN_LOCAL_PORT = 1024\nexport const MAX_LOCAL_PORT = 65_535\n\nexport function normalizeLocalPort(value: unknown): number {\n return typeof value === 'number'\n && Number.isInteger(value)\n && value >= MIN_LOCAL_PORT\n && value <= MAX_LOCAL_PORT\n ? value\n : DEFAULT_LOCAL_PORT\n}\n\n/** The LAN listener is TLS-only, so every advertised endpoint is `https://`. */\nexport function localEndpointURLs(addresses: readonly string[], port: number): string[] {\n const normalizedPort = normalizeLocalPort(port)\n return [...new Set(addresses)].map((address) => `https://${address}:${normalizedPort}`)\n}\n\nexport function localListenError(error: unknown, port: number): string {\n const code = (error as NodeJS.ErrnoException | undefined)?.code\n if (code === 'EADDRINUSE') return `local port ${port} is already in use`\n if (code === 'EACCES') return `permission denied while opening local port ${port}`\n return error instanceof Error ? error.message : String(error)\n}\n","import { join } from 'node:path'\nimport z from '@deepseek-ai/schemastery'\nimport {\n DEFAULT_REMOTE_HOSTNAME,\n} from './remote-supervisor.ts'\nimport { DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE, MAX_FUNNEL_CONNECTIONS_PER_SOURCE } from './funnel-policy.ts'\nimport { bridgeDataDir } from './token.ts'\nimport { DEFAULT_LOCAL_PORT, MAX_LOCAL_PORT, MIN_LOCAL_PORT } from './local-policy.ts'\n\nexport interface Config {\n /** Master switch; when false the plugin activates and does nothing. */\n enabled?: boolean\n /** Protocol-v2 device registry JSON path. */\n devicesPath?: string\n /** Replay ring buffer bound (frames) per deployment. */\n historyBufferMax?: number\n /** Testing & troubleshooting, rendered as its own settings section. */\n diagnostics?: {\n /** Verbose per-frame diagnostics (never prints token or message bodies). */\n debug?: boolean\n }\n /** Independent LAN transport. Never exposes the wider DSH web server. */\n local?: {\n enabled?: boolean\n port?: number\n }\n /** Optional embedded remote transport. Reconciled when settings change. */\n remote?: {\n enabled?: boolean\n provider?: 'tailscale-funnel'\n hostname?: string\n statePath?: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n /** Concurrent Funnel WebSockets allowed from one public source address. */\n maxConnectionsPerSource?: number\n }\n /**\n * Offline push (F-9). `apns` sends directly from the computer with the user's\n * Apple credentials. `relay` sends notify projections to an operator-run\n * relay for distributed builds. The device reports its own APNs environment,\n * so development and TestFlight/App Store devices may coexist.\n */\n push?: {\n /** `none` (default), `apns`, or `relay`. */\n provider?: 'none' | 'apns' | 'relay'\n /** Generic mode removes conversation-derived titles and bodies before outbound push. */\n contentMode?: 'preview' | 'generic'\n /** Apple Developer team id (JWT iss claim). */\n teamId?: string\n /** APNs auth key id (JWT kid header). */\n keyId?: string\n /** `.p8` private key path. */\n keyPath?: string\n /** App bundle id — the apns-topic header. */\n bundleId?: string\n /** Relay base URL. */\n relayUrl?: string\n /** Per-user bearer token issued by the relay operator. */\n relayToken?: string\n }\n}\n\n/** Operator-run relay used by distributed builds; overridable via config. */\nexport const DEFAULT_RELAY_URL = 'https://pilot.hailab.dev'\n\nexport const Config = z.object({\n // rc.2 accepts live settings writes only for fields declared volatile.\n enabled: z.boolean().default(true).volatile(),\n devicesPath: z.string().default(join(bridgeDataDir(), 'devices-v2.json')),\n historyBufferMax: z.natural().min(100).default(2000),\n diagnostics: z.object({\n debug: z.boolean().default(false)\n .description('打印握手、推送等诊断日志;不会打印配对令牌、APNs token 或消息内容'),\n }).default({\n debug: false,\n }).volatile(),\n local: z.object({\n enabled: z.boolean().default(true),\n port: z.natural()\n .min(MIN_LOCAL_PORT)\n .max(MAX_LOCAL_PORT)\n .default(DEFAULT_LOCAL_PORT)\n .description('DeepPilot 局域网独立端口(仅 TLS;默认 3098,修改后本地连接会短暂重连)'),\n }).default({\n enabled: true,\n port: DEFAULT_LOCAL_PORT,\n }).volatile(),\n remote: z.object({\n enabled: z.boolean().default(false),\n provider: z.union(['tailscale-funnel'] as const).default('tailscale-funnel'),\n hostname: z.string().default(DEFAULT_REMOTE_HOSTNAME),\n statePath: z.string().default(join(bridgeDataDir(), 'tailscale')),\n helperPath: z.string().default(''),\n funnelPort: z.union([443, 8443, 10000] as const).default(443),\n maxConnectionsPerSource: z.natural()\n .min(1)\n .max(MAX_FUNNEL_CONNECTIONS_PER_SOURCE)\n .default(DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE)\n .description('Funnel 每个来源允许的并发连接数(1–16,修改后远程连接会短暂重连)'),\n }).default({\n enabled: false,\n provider: 'tailscale-funnel',\n hostname: DEFAULT_REMOTE_HOSTNAME,\n statePath: join(bridgeDataDir(), 'tailscale'),\n helperPath: '',\n funnelPort: 443,\n maxConnectionsPerSource: DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE,\n }).volatile(),\n push: z.object({\n provider: z.union(['none', 'apns', 'relay'] as const).default('none'),\n contentMode: z.union(['preview', 'generic'] as const).default('preview'),\n teamId: z.string().default(''),\n keyId: z.string().default(''),\n keyPath: z.string().default(join(bridgeDataDir(), 'apns', 'AuthKey.p8')),\n bundleId: z.string().default('dev.hailab.deeppilot'),\n relayUrl: z.string().default(DEFAULT_RELAY_URL),\n relayToken: z.string().default(''),\n }).default({\n provider: 'none',\n contentMode: 'preview',\n teamId: '',\n keyId: '',\n keyPath: join(bridgeDataDir(), 'apns', 'AuthKey.p8'),\n bundleId: 'dev.hailab.deeppilot',\n relayUrl: DEFAULT_RELAY_URL,\n relayToken: '',\n }),\n})\n\n/**\n * Deep-unwrap live-update (volatile) references to plain values.\n *\n * On a 0.1.7 host, `apply()`'s options — and even a direct `Config(raw)` call\n * — carry `{ get() }` reference objects for volatile fields. Every consumer of\n * `currentConfig()` compares plain values (`=== true`, numeric bounds, object\n * fields), and schemastery refuses to re-parse a reference, so normalization\n * unwraps on the way in and on the way out. A JSON-shaped config value cannot\n * carry a function, so the `get` check cannot false-positive.\n */\nexport function plainConfig(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (typeof (value as { get?: unknown }).get === 'function') {\n // A reference's snapshot is recursively plain data (references cannot\n // nest inside references), so one unwrap layer suffices; recursion keeps\n // plain containers working around it.\n return plainConfig((value as { get: () => unknown }).get())\n }\n if (Array.isArray(value)) return value.map(plainConfig)\n const plain: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) plain[key] = plainConfig(item)\n return plain\n}\n\n/**\n * Cordis hands the second argument in different shapes depending on host\n * composition: a reactive options getter, the resolved config value, or\n * nothing when the patch row omits `config`. Normalize all of them — and\n * unwrap volatile references so the returned Config is plain data.\n */\nexport function normalizeOptions(options: unknown): Config {\n if (typeof options === 'function') return plainConfig((options as () => unknown)()) as Config\n if (options && typeof options === 'object') return plainConfig(options) as Config\n const validated = (Config as unknown as (data: unknown) => Config)(undefined)\n return plainConfig(validated ?? {}) as Config\n}\n","/**\n * Minimal APNs provider client (HTTP/2) with zero npm dependencies.\n *\n * Implements exactly what the bridge needs:\n * - ES256 provider token (JWT) signed with an Apple .p8 key, refreshed under\n * the 1-hour freshness window Apple enforces;\n * - one long-lived HTTP/2 session per environment, recreated transparently\n * after GOAWAY/errors;\n * - alert pushes carrying the notify projection (category/thread/collapse),\n * with `interruption-level: time-sensitive` for approval/question events;\n * - outcome classification so callers can prune dead device tokens.\n *\n * Privacy: logs carry outcomes and masked token prefixes only — never message\n * bodies or full tokens.\n */\n\nimport { connect as http2Connect, type ClientHttp2Session } from 'node:http2'\nimport { createHash, createPrivateKey, sign as cryptoSign, type KeyObject } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushDelivery, PushNotification } from './protocol.ts'\n\nexport interface ApnsClientOptions {\n teamId: string\n keyId: string\n /** Absolute path to the .p8 private key file. */\n keyPath: string\n bundleId: string\n debug?: boolean\n log: (message: string) => void\n}\n\nexport type ApnsOutcome = 'sent' | 'invalid-token' | 'failed'\n\n/** Outcome plus Apple's rejection reason (when present) for diagnosis. */\nexport interface ApnsSendResult {\n outcome: ApnsOutcome\n reason?: string\n}\n\n/** Classify Apple's reason without losing recoverable configuration errors. */\nexport function classifyApnsReason(reason: string): ApnsOutcome {\n return reason === 'Unregistered' || reason === 'ExpiredToken' ? 'invalid-token' : 'failed'\n}\n\nconst PROVIDER_TOKEN_TTL_MS = 50 * 60 * 1000 // Apple allows 1h; refresh early.\nconst REQUEST_TIMEOUT_MS = 10_000\n\n/** base64url without padding. */\nfunction b64url(input: Buffer | string): string {\n return Buffer.from(input).toString('base64url')\n}\n\n/** Sign one ES256 JWT for the given signing input with a P-256 private key. */\nexport function es256Jwt(signingInput: string, key: KeyObject): string {\n const signature = cryptoSign('sha256', Buffer.from(signingInput, 'utf8'), {\n key,\n // JOSE/ES256 wants raw r||s; node emits DER by default.\n dsaEncoding: 'ieee-p1363',\n })\n return signingInput + '.' + b64url(signature)\n}\n\n/** Strip PEM armor from a .p8 file and decode to PKCS#8 DER. */\nexport function p8ToDer(pem: string): Buffer {\n const body = pem\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\n .replace(/-----END PRIVATE KEY-----/, '')\n .replace(/\\s+/g, '')\n return Buffer.from(body, 'base64')\n}\n\nexport interface LoadedKey {\n key: KeyObject\n}\n\n/** Pure payload builder so tests can assert the wire format without sockets. */\nexport function apnsPayload(notification: PushDelivery): Record<string, unknown> {\n if ('kind' in notification && notification.kind === 'liveactivity') return { aps: {\n timestamp: notification.timestamp, event: notification.event,\n 'content-state': notification.contentState,\n ...(notification.event === 'end' ? { 'dismissal-date': notification.timestamp + 300 } : { 'stale-date': notification.timestamp + 180 }),\n } }\n if ('kind' in notification && notification.kind === 'widget') return { aps: { 'content-changed': true } }\n const alert = notification as PushNotification\n return alertPayload(alert)\n}\n\nfunction alertPayload(notification: PushNotification): Record<string, unknown> {\n // 需要用户动手或任务出事的通知穿透专注模式;任务完成保持普通级别。\n const timeSensitive = notification.category === 'approval.required' || notification.category === 'question.asked' ||\n notification.category === 'session.error'\n return {\n aps: {\n alert: {\n title: notification.title.slice(0, 120),\n body: notification.body.slice(0, 200),\n },\n sound: 'default',\n category: notification.category,\n // 让 Notification Service Extension 有机会按类别给横幅附上阶段缩略图。\n 'mutable-content': 1,\n 'thread-id': notification.hostAudience ? createHash('sha256').update(`${notification.hostAudience}:${notification.sessionId}`).digest('hex') : notification.sessionId.slice(0, 64),\n ...(timeSensitive ? { 'interruption-level': 'time-sensitive' } : {}),\n },\n ...(notification.hostAudience ? { hostAudience: notification.hostAudience } : {}),\n sessionId: notification.sessionId,\n notificationId: notification.notificationId,\n kind: notification.category,\n // 长按展开页使用;仅审批通知携带,generic 模式下已在 pushContent 中剔除。\n ...(notification.category === 'approval.required' && notification.toolName\n ? { toolName: notification.toolName.slice(0, 80), ...(notification.detail ? { detail: notification.detail.slice(0, 600) } : {}) }\n : {}),\n }\n}\n\n/** One delivery request: the notify projection plus its target device token.\n * The environment comes from the device's own registration (its build kind),\n * so one client serves both sandbox and production devices. */\nexport type ApnsSendRequest = PushDelivery & {\n deviceToken: string\n environment: ApnsEnvironment\n}\n\nexport function pushHeaders(bundleId: string, notification: PushDelivery): Record<string, string> {\n if ('kind' in notification && notification.kind === 'liveactivity') return {\n 'apns-topic': bundleId + '.push-type.liveactivity',\n 'apns-push-type': 'liveactivity', 'apns-priority': notification.event === 'end' ? '10' : '5',\n 'apns-expiration': String(notification.timestamp + 180),\n }\n if ('kind' in notification && notification.kind === 'widget') {\n return {\n 'apns-topic': bundleId + '.push-type.widgets',\n 'apns-push-type': 'widgets',\n 'apns-priority': '5',\n 'apns-collapse-id': 'widget-overview',\n }\n }\n return {\n 'apns-topic': bundleId, 'apns-push-type': 'alert', 'apns-priority': '10',\n 'apns-collapse-id': collapseIdFor(notification as PushNotification),\n }\n}\n\n/** collapse-id accepts ≤64 bytes of ASCII; keep it stable per session+event. */\nexport function collapseIdFor(notification: PushNotification): string {\n const raw = `${notification.hostAudience ? notification.hostAudience + ':' : ''}${notification.category}:${notification.sessionId}`\n const readable = raw.replace(/[^a-zA-Z0-9.:-]/g, '')\n const digest = createHash('sha256').update(raw, 'utf8').digest('hex').slice(0, 12)\n return `${readable.slice(0, 51)}:${digest}`\n}\n\nfunction authorityFor(environment: ApnsEnvironment): string {\n return environment === 'production' ? 'api.push.apple.com' : 'api.sandbox.push.apple.com'\n}\n\nexport class ApnsClient {\n private readonly log: (message: string) => void\n private readonly debug: boolean\n private readonly opts: ApnsClientOptions\n\n private providerToken = ''\n private providerTokenIssuedAt = 0\n private key: KeyObject | undefined\n\n constructor(opts: ApnsClientOptions) {\n this.opts = opts\n this.log = opts.log\n this.debug = opts.debug === true\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.sessions.values()]\n this.sessions.clear()\n await Promise.all(\n sessions\n .filter((session) => !session.destroyed)\n .map((session) => new Promise<void>((resolve) => session.close(() => resolve()))),\n )\n }\n\n // ---------- credentials ----------\n\n private async ensureProviderToken(): Promise<string> {\n if (this.providerToken && Date.now() - this.providerTokenIssuedAt < PROVIDER_TOKEN_TTL_MS) {\n return this.providerToken\n }\n if (!this.key) {\n const pem = await readFile(this.opts.keyPath, 'utf8')\n this.key = createPrivateKey({ key: p8ToDer(pem), format: 'der', type: 'pkcs8' })\n }\n const issuedAt = Math.floor(Date.now() / 1000)\n const header = b64url(JSON.stringify({ alg: 'ES256', kid: this.opts.keyId }))\n const claims = b64url(JSON.stringify({ iss: this.opts.teamId, iat: issuedAt }))\n this.providerToken = es256Jwt(`${header}.${claims}`, this.key)\n this.providerTokenIssuedAt = Date.now()\n return this.providerToken\n }\n\n // ---------- transport ----------\n\n /** One long-lived HTTP/2 session per Apple host (sandbox + production). */\n private readonly sessions = new Map<string, ClientHttp2Session>()\n\n private ensureSession(authority: string): ClientHttp2Session {\n const existing = this.sessions.get(authority)\n if (existing && !existing.destroyed && !existing.closed) return existing\n const session = http2Connect(`https://${authority}`)\n // Keep a handler attached so background errors never become uncaught\n // exceptions; the next send() recreates the session when dead.\n session.on('error', (error) => {\n if (this.debug) this.log('apns session error (' + authority + '): ' + String(error))\n this.sessions.delete(authority)\n })\n this.sessions.set(authority, session)\n return session\n }\n\n /**\n * Deliver one alert. Never throws — every failure path resolves to an\n * outcome so fan-out loops cannot crash the host on a flaky network.\n */\n async send(request: ApnsSendRequest): Promise<ApnsSendResult> {\n const { deviceToken, environment, ...notification } = request\n let stream: ReturnType<ClientHttp2Session['request']> | undefined\n try {\n const token = await this.ensureProviderToken()\n const body = JSON.stringify(apnsPayload(notification))\n const session = this.ensureSession(authorityFor(environment))\n const done = new Promise<ApnsSendResult>((resolve) => {\n const req = session.request({\n [':method']: 'POST',\n [':path']: '/3/device/' + deviceToken,\n authorization: 'bearer ' + token,\n ...pushHeaders(this.opts.bundleId, notification),\n // Stale approvals arriving hours later are worse than none.\n 'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600),\n 'content-type': 'application/json',\n 'content-length': String(Buffer.byteLength(body)),\n })\n stream = req\n let status = 0\n let responseBody = ''\n const settle = (outcome: ApnsOutcome, reason?: string): void => {\n if (this.debug) {\n this.log(`apns ${outcome}${reason ? ' (' + reason + ')' : ''} (${this.maskToken(deviceToken)})`)\n }\n resolve(reason !== undefined && reason !== '' ? { outcome, reason } : { outcome })\n }\n const timer = setTimeout(() => {\n req.close()\n settle('failed')\n }, REQUEST_TIMEOUT_MS)\n timer.unref?.()\n req.on('response', (headers) => {\n status = Number(headers[':status'] ?? 0)\n })\n req.on('data', (chunk: Buffer) => {\n responseBody += chunk.toString('utf8')\n })\n req.on('error', () => {\n clearTimeout(timer)\n settle('failed')\n })\n req.on('end', () => {\n clearTimeout(timer)\n if (status === 200) return settle('sent')\n let reason = ''\n try {\n reason = String((JSON.parse(responseBody) as { reason?: string }).reason ?? '')\n } catch {\n // non-JSON body; fall through with empty reason\n }\n if (status !== 200 && !reason) reason = 'HTTP ' + String(status)\n // Unregistered/ExpiredToken are authoritative lifecycle verdicts.\n // BadDeviceToken can also mean the provider selected the wrong APNs\n // environment; pruning then destroys a valid registration exactly\n // when the operator needs its stored environment to diagnose config.\n const outcome = classifyApnsReason(reason)\n if (outcome === 'invalid-token') return settle(outcome, reason)\n if (this.debug) this.log(`apns rejected status=${status} reason=${reason}`)\n settle('failed', reason)\n })\n req.end(body)\n })\n return await done\n } catch (error) {\n // Credential load failures and dead sessions land here; force both to be\n // rebuilt on the next attempt.\n this.key = undefined\n this.providerToken = ''\n this.sessions.clear()\n if (this.debug) this.log('apns send failed: ' + String(error))\n return { outcome: 'failed', reason: String(error).slice(0, 120) }\n } finally {\n try { stream?.close() } catch { /* already closed */ }\n }\n }\n\n maskToken(token: string): string {\n return token.length <= 10 ? '…' : token.slice(0, 6) + '…' + token.slice(-4)\n }\n}\n","/**\n * Normalize an operator-configured relay URL without a backtracking regular\n * expression. Scanning from the end keeps even pathological inputs linear.\n */\nexport function normalizeRelayBaseUrl(value: string): string {\n const trimmed = value.trim()\n let end = trimmed.length\n while (end > 0 && trimmed.charCodeAt(end - 1) === 0x2f) end -= 1\n return end === trimmed.length ? trimmed : trimmed.slice(0, end)\n}\n","/**\n * Client for the operator-run push relay (relay/server.js). Used when the App\n * is distributed under the operator's team: bridges cannot hold the\n * distributor's .p8, so they forward notify projections over HTTPS and let\n * the relay do APNs.\n *\n * Outcome contract mirrors src/apns.ts exactly so the dispatcher prunes dead\n * tokens identically on either path:\n * sent | invalid-token (terminal Unregistered/ExpiredToken) | failed\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushDelivery } from './protocol.ts'\nimport { normalizeRelayBaseUrl } from './relay-url.ts'\n\nexport interface RelayClientOptions {\n /** Base URL of the relay, e.g. https://relay.example.com */\n url: string\n /** Per-user bearer token issued by the relay operator (or via enroll). */\n token?: string\n timeoutMs?: number\n debug?: boolean\n log: (message: string) => void\n}\n\nexport interface RelaySendRequest {\n deviceToken: string\n environment: ApnsEnvironment\n notification: PushDelivery\n}\n\nexport class RelayClient {\n private readonly base: string\n private readonly token: string\n private readonly timeoutMs: number\n private readonly debug: boolean\n private readonly log: (message: string) => void\n\n constructor(opts: RelayClientOptions) {\n this.base = normalizeRelayBaseUrl(opts.url)\n this.token = (opts.token ?? '').trim()\n this.timeoutMs = opts.timeoutMs ?? 10_000\n this.debug = opts.debug === true\n this.log = opts.log\n }\n\n /**\n * Zero-touch enrollment: exchange the distributor's shared key (baked into\n * the distributed app) for a stable per-bridge bearer token. Idempotent —\n * relays derive the same token for the same clientId. Returns null on any\n * failure; callers treat that as \"not enrolled yet\", not as an error.\n */\n async enroll(clientId: string, enrollKey: string): Promise<string | null> {\n try {\n const response = await fetch(this.base + '/v1/enroll', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (!response.ok) {\n if (this.debug) this.log(`enroll http ${response.status}`)\n return null\n }\n const body = (await response.json()) as { token?: unknown }\n if (typeof body.token === 'string' && body.token.startsWith('rl_')) return body.token\n if (this.debug) this.log('enroll returned no usable token')\n return null\n } catch (error) {\n if (this.debug) this.log('enroll failed: ' + String(error))\n return null\n }\n }\n\n async send(request: RelaySendRequest): Promise<{ outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }> {\n try {\n const response = await fetch(this.base + '/v1/push', {\n method: 'POST',\n headers: {\n authorization: 'Bearer ' + this.token,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n deviceToken: request.deviceToken,\n environment: request.environment,\n notification: request.notification,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (response.status === 401 || response.status === 429) {\n // Credential/quota problems are configuration errors, not token death;\n // surface as failed so the user fixes the config instead of silently\n // losing registrations.\n if (this.debug) this.log(`relay rejected status=${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n if (!response.ok) {\n if (this.debug) this.log(`relay http ${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n const body = (await response.json()) as { outcome?: string; reason?: string }\n if (body.outcome === 'sent') return { outcome: 'sent' }\n if (body.outcome === 'invalid-token') return { outcome: 'invalid-token', reason: body.reason }\n if (this.debug) this.log('relay outcome=' + String(body.outcome) + ' reason=' + String(body.reason ?? ''))\n return { outcome: 'failed', reason: body.reason }\n } catch (error) {\n if (this.debug) this.log('relay send failed: ' + String(error))\n const message = error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)\n return { outcome: 'failed', reason: message }\n }\n }\n}\n","/** Coalesce bursts and bound continuous updates, without starving the trailing\n * state. One scheduler per host, not per token or per streaming text frame. */\nexport class WidgetPushScheduler {\n private timer?: ReturnType<typeof setTimeout>\n private lastSent = 0\n private disposed = false\n private sending = false\n private dirty = false\n\n constructor(private readonly send: () => Promise<void>, private readonly intervalMs = 30_000) {}\n\n changed(): void {\n if (this.disposed) return\n this.dirty = true\n if (this.timer || this.sending) return\n this.timer = setTimeout(() => {\n this.timer = undefined\n this.dirty = false\n this.sending = true\n this.lastSent = Date.now()\n void this.send().catch(() => {}).finally(() => {\n this.sending = false\n if (this.dirty) this.changed()\n })\n }, Math.max(Math.min(1000, this.intervalMs), this.intervalMs - (Date.now() - this.lastSent)))\n this.timer.unref()\n }\n\n dispose(): void {\n this.disposed = true\n if (this.timer) clearTimeout(this.timer)\n this.timer = undefined\n }\n}\n","import type { DeviceStore } from './token.ts'\nimport type { LiveActivityState, LiveActivityPushNotification, SessionSummary } from './protocol.ts'\nimport type { ApnsSendResult } from './apns.ts'\nimport { WidgetPushScheduler } from './widget-push.ts'\n\nexport function liveActivityState(session?: SessionSummary): LiveActivityState {\n const total = Math.max(0, session?.todos?.total ?? session?.todoItems?.length ?? 0)\n const done = Math.min(total, Math.max(0, session?.todos?.done ?? 0))\n const task = session?.todoItems?.find(item => item.status === 'in_progress') ??\n session?.todoItems?.find(item => item.status === 'pending')\n return {\n title: Array.from(session?.title ?? '').slice(0, 100).join(''),\n task: Array.from(session?.activity?.trim() || task?.content || '').slice(0, 160).join(''), done, total,\n phase: !session || total === 0 ? 'unavailable' : session.pendingApproval ? 'approval' :\n session.pendingQuestion ? 'question' : session.status === 'running' ? 'running' :\n session.status === 'idle' ? 'ended' : session.status === 'error' ? 'failed' : 'unavailable',\n }\n}\n\n/** Persist terminal state before coalescing so a rapid next round cannot revive an activity. */\nexport class LiveActivityPushManager {\n private latest = new Map<string, SessionSummary>()\n private sent = new Map<string, string>()\n private readonly scheduler: WidgetPushScheduler\n constructor(\n private readonly devices: () => DeviceStore | undefined,\n private readonly send: (token: string, environment: 'development' | 'production', notification: LiveActivityPushNotification) => Promise<ApnsSendResult>,\n intervalMs = 15_000,\n ) { this.scheduler = new WidgetPushScheduler(() => this.flush(), intervalMs) }\n\n changed(sessions: SessionSummary[]): void {\n this.latest = new Map(sessions.map(session => [session.id, session]))\n for (const device of this.devices()?.list() ?? []) {\n const r = device.liveActivity\n if (!r) continue\n const state = liveActivityState(this.latest.get(r.sessionId))\n if (state.phase === 'ended' || state.phase === 'failed' || state.phase === 'unavailable') {\n this.devices()?.endLiveActivity(device.deviceId, r.token, state)\n }\n }\n this.scheduler.changed()\n }\n\n async flush(): Promise<void> {\n const devices = this.devices()\n if (!devices) return\n const valid = new Set<string>()\n for (const device of devices.list()) {\n const r = device.liveActivity\n if (!r) continue\n const key = device.deviceId + ':' + r.token\n valid.add(key)\n if (device.revokedAt !== undefined || !['notifications.register', 'sessions.read', 'interactions.respond'].every(scope => device.scopes?.some(value => value === scope)) || r.expiresAt <= Date.now()) {\n devices.clearLiveActivity(device.deviceId, r.activityId, r.token)\n continue\n }\n const state = r.endedState ?? liveActivityState(this.latest.get(r.sessionId))\n const fingerprint = JSON.stringify(state)\n if (this.sent.get(key) === fingerprint) continue\n // Never send a queued registration after it has rotated or been removed.\n if (devices.authorized(device.deviceId)?.liveActivity?.token !== r.token) continue\n const result = await this.send(r.token, r.environment, {\n kind: 'liveactivity', event: r.endedState ? 'end' : 'update',\n timestamp: Math.floor(Date.now() / 1000), contentState: state,\n }).catch((): ApnsSendResult => ({ outcome: 'failed' }))\n if (result.outcome === 'sent') {\n this.sent.set(key, fingerprint)\n // Retain the terminal tombstone until expiry/unregister: reconnect and token rotation cannot restart this round.\n } else if (result.outcome === 'invalid-token') {\n devices.clearLiveActivity(device.deviceId, r.activityId, r.token)\n } else {\n this.scheduler.changed()\n }\n }\n for (const key of this.sent.keys()) if (!valid.has(key)) this.sent.delete(key)\n }\n dispose(): void { this.scheduler.dispose() }\n}\n","import type { PushNotification } from './protocol.ts'\nimport { pushScopeFor } from './connection-policy.ts'\n\n/** Prune only when the provider supplies an authoritative token-lifecycle verdict. */\nexport function shouldPrunePushToken(\n outcome: 'sent' | 'invalid-token' | 'failed',\n reason?: string,\n): boolean {\n return outcome === 'invalid-token' && (reason === 'Unregistered' || reason === 'ExpiredToken')\n}\n\n/** APNs requires both registration and the same content permission as WS. */\nexport function mayReceivePush(device: { scopes?: readonly string[] }, notification: unknown): boolean {\n const scope = pushScopeFor('s2c.notify', notification)\n return scope !== undefined && device.scopes?.includes('notifications.register') === true &&\n device.scopes.includes(scope)\n}\n\n/**\n * Zero-touch relay self-heal: HTTP 401 means the relay no longer honors the\n * cached credential. Only auto-enrolled cells with a still-current token may\n * re-derive it; an explicitly configured relay token remains user-owned\n * configuration and is never silently rewritten.\n */\nexport function shouldReEnrollRelayToken(\n transport: 'apns' | 'relay',\n outcome: 'sent' | 'invalid-token' | 'failed',\n reason: string | undefined,\n opts: { usedCellToken: boolean; hasEnrollKey: boolean; tokenStillCurrent: boolean },\n): boolean {\n return (\n transport === 'relay' &&\n outcome === 'failed' &&\n reason === 'HTTP 401' &&\n opts.hasEnrollKey &&\n opts.usedCellToken &&\n opts.tokenStillCurrent\n )\n}\n\n/** Apply before either transport receives the payload, including the Relay. */\nexport function pushContent(notification: PushNotification, mode?: 'preview' | 'generic'): PushNotification {\n if (mode !== 'generic') return notification\n // generic 模式下工具名与详情同样不得出站。\n const { toolName: _toolName, detail: _detail, ...rest } = notification\n notification = rest\n const bodies: Record<PushNotification['category'], string> = {\n 'turn.completed': 'A task has completed.',\n 'approval.required': 'An approval needs your attention.',\n 'question.asked': 'A question needs your answer.',\n 'session.error': 'A task needs your attention.',\n }\n return { ...notification, title: 'DeepPilot', body: bodies[notification.category] ?? 'Open DeepPilot for an update.' }\n}\n","/**\n * Relay connectivity self-test used by the settings page \"测试中继\" button.\n *\n * Two steps mirror what zero-touch enrollment actually does:\n * 1. health — GET {url}/healthz → is the relay reachable?\n * 2. enroll — POST {url}/v1/enroll → does the shared key grant a token?\n *\n * The enroll step doubles as a repair path: a token it issues is handed back\n * via `onEnrolled` so the bridge caches it and flips push readiness without\n * waiting for the next app registration.\n *\n * Transport is injectable (`fetchImpl`) so tests never touch the network.\n */\n\nimport { normalizeRelayBaseUrl } from './relay-url.ts'\n\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n /** Human-readable, user-facing (zh) outcome; safe to render verbatim. */\n message: string\n latencyMs?: number\n}\n\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n /** True when the probe produced/cached a usable relay token. */\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\nexport interface RelayProbeOptions {\n url: string\n /** Stable per-bridge identity for enrollment; generated upstream if absent. */\n clientId?: string\n /** Distributor's shared key; absent → enroll step reports why it cannot run. */\n enrollKey?: string\n /** True when the user configured relayToken manually (enroll unnecessary). */\n manualToken?: boolean\n timeoutMs?: number\n fetchImpl?: typeof fetch\n /** Called with an issued token so the caller can persist it. */\n onEnrolled?: (token: string) => void\n}\n\nconst DEFAULT_TIMEOUT_MS = 6_000\n\nasync function requestJson(\n fetchImpl: typeof fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<{ status: number; body: unknown }> {\n const response = await fetchImpl(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs),\n })\n let body: unknown = null\n try {\n body = await response.json()\n } catch {\n // non-JSON body keeps body null; status still drives the verdict\n }\n return { status: response.status, body }\n}\n\nexport async function runRelayProbe(options: RelayProbeOptions): Promise<RelayTestResult> {\n const base = normalizeRelayBaseUrl(options.url)\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const fetchImpl = options.fetchImpl ?? fetch\n const steps: RelayTestStep[] = []\n let tokenIssued = false\n\n // ---- step 1: reachability ----\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/healthz`,\n { method: 'GET' },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n if (status === 200 && (body as { ok?: unknown })?.ok === true) {\n steps.push({ id: 'health', ok: true, message: '中继服务可达', latencyMs })\n } else {\n steps.push({ id: 'health', ok: false, message: `中继响应异常(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'health', ok: false, message: '无法连接中继:' + reason })\n }\n\n // ---- step 2: enrollment ----\n if ((options.manualToken ?? false)) {\n steps.push({ id: 'enroll', ok: true, message: '已手动配置 relayToken,跳过注册验证' })\n } else if (!options.enrollKey) {\n steps.push({ id: 'enroll', ok: false, message: '尚无注册密钥:等待分发版 App 首次注册后才能验证注册' })\n } else {\n const clientId = options.clientId ?? 'u_' + Math.random().toString(36).slice(2)\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/v1/enroll`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey: options.enrollKey }),\n },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n const token = (body as { token?: unknown })?.token\n if (status === 200 && typeof token === 'string' && token.startsWith('rl_')) {\n tokenIssued = true\n options.onEnrolled?.(token)\n steps.push({ id: 'enroll', ok: true, message: '注册成功,已取得推送凭证', latencyMs })\n } else if (status === 403) {\n steps.push({ id: 'enroll', ok: false, message: '注册被拒:注册密钥不匹配(检查 App 内 DSPushEnrollKey 与服务器 RELAY_ENROLL_KEY)', latencyMs })\n } else if (status === 429) {\n steps.push({ id: 'enroll', ok: false, message: '尝试过于频繁,稍后再试', latencyMs })\n } else {\n steps.push({ id: 'enroll', ok: false, message: `注册失败(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'enroll', ok: false, message: '注册请求失败:' + reason })\n }\n }\n\n const overall: RelayTestResult['overall'] =\n steps.length > 0 && steps.some((step) => step.id === 'health' && step.ok) &&\n steps.every((step) => step.ok)\n ? 'ok'\n : 'failed'\n return { url: base, overall, tokenIssued, steps }\n}\n","/**\n * 推送网关(PushGateway)——离线推送的单一属主。\n *\n * 迁移前,这约 450 行住在 `apply()` 的闭包里:enrollment cell 与持久化、\n * `handlePushEnrollKey`、`resolvePushConfig`、`ensureRelayEnrolled`、\n * `senderFor` + cachedSender + 失败退避、两个 scheduler、`makePushOutlet`、\n * 两个自测。它今天的「接口」其实是「被 WidgetPushScheduler、\n * LiveActivityPushManager、PushOutlet 和两个 report 回调各自捕获的那几段代码」\n * ——没有任何一处能回答「推送为什么没到」。\n *\n * 现在:一个 module,实现 `PushOutlet`(HostBridge 消费的那四个方法)并额外\n * 暴露自测与零配置注册入口。enrollment、持久化、sender 缓存、退避、401 自愈\n * 全部入内;`apply()` 只做依赖注入。\n *\n * 两处本次收敛的重复:\n * - effective provider 推导(原先在 resolvePushConfig 与 relay 自测里各一份);\n * - 401 自愈三元组(原先在 widget / liveActivity / fanOut 三处各一份,\n * 见 healRelayCredential)。\n */\n\nimport { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport {\n DEFAULT_RELAY_URL,\n type Config,\n} from './config.ts'\nimport { ApnsClient } from './apns.ts'\nimport { RelayClient } from './relay-client.ts'\nimport { WidgetPushScheduler } from './widget-push.ts'\nimport { LiveActivityPushManager } from './live-activity.ts'\nimport {\n mayReceivePush,\n pushContent,\n shouldPrunePushToken,\n shouldReEnrollRelayToken,\n} from './push-policy.ts'\nimport { deviceDisplayName, expandHome, type DeviceStore } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushDelivery, PushNotification, SessionSummary } from './protocol.ts'\nimport type { PushOutlet } from './host-api.ts'\nimport type { PushTestResult, RelayTestResult } from './report-wire.ts'\nimport { runRelayProbe } from './relay-test.ts'\n\n/** 零配置注册的持久化单元(deeppilot/push-relay.json)。 */\ninterface RelayEnrollmentCell {\n clientId?: string\n autoRelay?: boolean\n enrollKey?: string\n token?: string\n}\n\ntype PushConfigSnapshot =\n | { kind: 'apns'; teamId: string; keyId: string; keyPath: string; bundleId: string }\n | { kind: 'relay'; url: string; token: string }\n\ninterface SendOutcome { outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }\ntype PushSender = (\n request: { deviceToken: string; environment: ApnsEnvironment; notification: PushDelivery },\n) => Promise<SendOutcome>\n\ninterface CachedSender {\n fingerprint: string\n send: PushSender\n dispose?: () => Promise<void>\n}\n\n/** 274 秒(约 4.5 分钟)内的失败只记一次;其间同一指纹不再重试。 */\nconst SENDER_FAILURE_RETRY_MS = 60_000\n/** 中继注册失败的节流:一次/分钟,避免中继抖动把每次通知变成一次外呼。 */\nconst ENROLL_RETRY_MS = 60_000\n\n/** 网关需要的一切都从外部注入;它自己不创建配置、设备表或连接集合。 */\nexport interface PushGatewayDeps {\n config: () => Config\n /** 推送数据目录(push-relay.json 与 APNs 密钥默认路径的宿主)。 */\n dataDir: string\n devices: () => DeviceStore | null\n audience: () => string | null\n /** 当前活跃连接:用于跳过已有 WebSocket 的设备。 */\n connections: () => Iterable<{\n connectedDeviceId: string | undefined\n suppressesAlertPush: boolean\n }>\n enabledNow: () => boolean\n log: (message: string) => void\n /** 中继探测的传输实现:默认真实 fetch,测试注入假件以免碰网络。 */\n fetchImpl?: typeof fetch\n}\n\nexport class PushGateway implements PushOutlet {\n private readonly pushRelayPath: string\n private readonly enrollmentCell: RelayEnrollmentCell = {}\n private enrollmentWriteTail: Promise<void> = Promise.resolve()\n private enrollAttemptFor: string | undefined\n private enrollLastAttemptAt = 0\n private cachedSender: CachedSender | undefined\n private senderFailedFor: { fingerprint: string; at: number } | undefined\n\n private readonly widgetPush: WidgetPushScheduler\n private readonly liveActivityPush: LiveActivityPushManager\n\n constructor(private readonly deps: PushGatewayDeps) {\n this.pushRelayPath = join(deps.dataDir, 'push-relay.json')\n this.widgetPush = new WidgetPushScheduler(async () => { await this.flushWidgetPushes() })\n this.liveActivityPush = new LiveActivityPushManager(\n () => deps.devices() ?? undefined,\n async (deviceToken, environment, notification) => this.sendLiveActivity(deviceToken, environment, notification),\n )\n }\n\n // ---------- 生命周期 ----------\n\n /** 首启恢复零配置注册状态(best effort)。 */\n async restore(): Promise<void> {\n try {\n const raw = JSON.parse(await readFile(this.pushRelayPath, 'utf8')) as RelayEnrollmentCell\n if (typeof raw.clientId === 'string') this.enrollmentCell.clientId = raw.clientId\n if (typeof raw.enrollKey === 'string') this.enrollmentCell.enrollKey = raw.enrollKey\n if (typeof raw.token === 'string') this.enrollmentCell.token = raw.token\n if (raw.autoRelay === true) this.enrollmentCell.autoRelay = true\n } catch {\n // first boot: no enrollment yet\n }\n }\n\n /** 写盘串行化:并发注册不会交错写坏同一个文件。 */\n private persistEnrollment(): void {\n const snapshot = JSON.stringify({ version: 1, ...this.enrollmentCell }, null, 2) + '\\n'\n this.enrollmentWriteTail = this.enrollmentWriteTail.then(async () => {\n const tempPath = this.pushRelayPath + '.' + randomBytes(6).toString('hex') + '.tmp'\n try {\n await mkdir(this.deps.dataDir, { recursive: true })\n await writeFile(tempPath, snapshot, { mode: 0o600 })\n await rename(tempPath, this.pushRelayPath)\n } catch {\n await unlink(tempPath).catch(() => {})\n // best-effort persistence; enrollment retries on next trigger\n }\n })\n }\n\n /** 等注册状态的写盘尾部排空;测试与收尾都用它取得确定性。 */\n async persisted(): Promise<void> {\n await this.enrollmentWriteTail\n }\n\n /** 进程收尾:等写盘尾部落盘,释放 sender。 */\n async dispose(): Promise<void> {\n this.widgetPush.dispose()\n this.liveActivityPush.dispose()\n const sender = this.cachedSender\n this.cachedSender = undefined\n await Promise.allSettled([\n this.enrollmentWriteTail,\n sender?.dispose?.() ?? Promise.resolve(),\n ])\n }\n\n // ---------- PushOutlet(HostBridge 消费) ----------\n\n widgetChanged(): void {\n this.widgetPush.changed()\n }\n\n liveActivityChanged(sessions: SessionSummary[]): void {\n this.liveActivityPush.changed(sessions)\n }\n\n /** 能力位必须说真话:只在 provider 完全就绪时才广告 push。 */\n isAvailable(): boolean {\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) return false\n // 中继模式要等注册产出 token 才算就绪;提前广告会让客户端压掉本地通知,\n // 而那条通知永远不会到。\n if (resolved.value.kind === 'relay' && !resolved.value.token) return false\n return true\n }\n\n /**\n * 把一个值得通知的事件扇到持有 APNs token 的已配对设备。规则:\n * - 有活跃 WebSocket 的设备跳过(它们已收到 WS 帧,会自己弹本地通知);\n * - 只考虑被授予 `notifications.register` 的设备——scope 被收窄的设备不得\n * 再收到离线推送(R1/P2 S→C 权限策略);\n * - 每设备按它自己注册的环境投递,沙盒与生产设备可共存;\n * - 设备按类别的静音开关抑制对应类别;\n * - 只有 APNs 的终态 Unregistered/ExpiredToken 才清理存储;\n * BadDeviceToken 可能是环境不匹配,保留可诊断性。\n */\n fanOut(sourceNotification: PushNotification): void {\n const notification = pushContent(\n { ...sourceNotification, hostAudience: this.deps.audience() ?? undefined },\n this.deps.config().push?.contentMode,\n )\n void (async () => {\n let resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok && resolved.reason === 'relay token not enrolled yet') {\n // 注册时带着 enrollKey 但注册还没跑完——立刻试一次再重新解析。\n await this.ensureRelayEnrolled(this.relayUrl())\n resolved = this.resolvePushConfig(this.deps.config())\n }\n if (!resolved.ok) return\n const devices = this.deps.devices()\n if (!devices) return\n const send = await this.senderFor(resolved.value)\n if (!send) return\n const transport = resolved.value.kind\n const connectedIds = new Set<string>()\n for (const connection of this.deps.connections()) {\n const id = connection.connectedDeviceId\n if (id && connection.suppressesAlertPush) connectedIds.add(id)\n }\n // 可观测性优先:推送失败曾经完全静默(受 debug 门控),现场无法诊断。\n // 现在每次派送与每次跳过都留一行平铺日志——只有类别与结果,绝不带消息体。\n const candidates = devices.list().filter((device) => {\n const registration = device.apns\n if (!registration) return false\n if (connectedIds.has(device.deviceId)) return false\n if (!mayReceivePush(device, notification)) {\n if (this.deps.config().diagnostics?.debug === true) {\n this.deps.log(`push skip \"${deviceDisplayName(device)}\": notification permission not granted`)\n }\n return false\n }\n if (registration.categories?.[notification.category] === false) {\n if (this.deps.config().diagnostics?.debug === true) {\n this.deps.log(`push skip \"${deviceDisplayName(device)}\": category ${notification.category} muted`)\n }\n return false\n }\n return true\n })\n if (candidates.length === 0) {\n const tokenized = devices.list().filter((device) => device.apns !== undefined).length\n this.deps.log(`push(${transport}) ${notification.category}: no offline targets (connected=${connectedIds.size}, tokenized=${tokenized})`)\n return\n }\n // 自愈输入每次派送解析一次:实际发送所用的 URL,以及凭据是否来自零配置\n // 单元(显式配置的 relayToken 永不被改写)。\n const relayUrl = resolved.value.kind === 'relay' ? resolved.value.url : undefined\n const relayTokenUsed = resolved.value.kind === 'relay' ? resolved.value.token : undefined\n const usedCellToken = relayTokenUsed !== undefined && relayTokenUsed === this.enrollmentCell.token\n const hasEnrollKey = Boolean(this.enrollmentCell.enrollKey)\n for (const device of candidates) {\n const registration = device.apns!\n void send({ deviceToken: registration.token, environment: registration.environment, notification })\n .then(({ outcome, reason }) => {\n this.deps.log(`push(${transport}) ${notification.category} → \"${deviceDisplayName(device)}\" [${registration.environment}] = ${outcome}${reason ? ' (' + reason + ')' : ''}`)\n if (shouldPrunePushToken(outcome, reason)) {\n devices.clearPushToken(device.deviceId)\n this.deps.log(`push: pruned stale token of \"${deviceDisplayName(device)}\" (${reason ?? 'unknown'}) — app re-registers on next launch`)\n return\n }\n if (\n relayUrl !== undefined &&\n shouldReEnrollRelayToken(transport, outcome, reason, {\n usedCellToken,\n hasEnrollKey,\n // Compare-and-clear:另一请求携带旧凭据的迟到 401 不得擦掉\n // 已被更早回调刷新过的 token。\n tokenStillCurrent: this.enrollmentCell.token === relayTokenUsed,\n })\n ) {\n // 中继不再认这个凭据。丢掉并从 enroll key 重新派生;\n // ensureRelayEnrolled 自己的节流避免并行 401 打爆端点,\n // senderFor 的配置指纹会在下次派送时用新 token 重建客户端。\n void this.healRelayCredential(relayUrl, relayTokenUsed, 'push relay credential rejected (HTTP 401); re-enrolling')\n }\n })\n .catch(() => {})\n }\n })()\n }\n\n // ---------- 零配置注册与自测(report remote 与 wire 帧消费) ----------\n\n /** 分布式 App 在 c2s.push.register 里呈上分发方的共享钥匙时触发。 */\n async enrollKey(enrollKey: string): Promise<void> {\n if (this.enrollmentCell.enrollKey !== enrollKey) {\n this.enrollmentCell.enrollKey = enrollKey\n }\n const configuredProvider = this.deps.config().push?.provider\n // 只在用户没有显式选择时才翻转。\n if (!configuredProvider || configuredProvider === 'none') {\n if (!this.enrollmentCell.autoRelay) {\n this.enrollmentCell.autoRelay = true\n this.deps.log('push relay mode auto-enabled by enrolled app')\n }\n }\n this.persistEnrollment()\n // 内联完成注册,让 register handler 看到最终就绪状态:第一条离线通知\n // 不应依赖一次重连。\n await this.ensureRelayEnrolled(this.relayUrl())\n }\n\n /** 设置页推送自测:强制一条合成通知走完整链路到每个已注册设备。 */\n async selfTest(): Promise<PushTestResult> {\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) {\n return {\n transport: 'none',\n overall: 'not-configured',\n message: '推送未启用(' + resolved.reason + ')。可先用「测试访问与注册」完成中继注册,或在配置中设置 push.provider',\n results: [],\n }\n }\n const devices = this.deps.devices()\n const tokenized = (devices?.list() ?? []).filter((device) => device.apns !== undefined)\n if (!devices || tokenized.length === 0) {\n return {\n transport: resolved.value.kind,\n overall: 'no-targets',\n message: '还没有设备注册离线推送——在手机上打开 DeepPilot 并允许系统通知,等状态变为「已就绪」后再试',\n results: [],\n }\n }\n const send = await this.senderFor(resolved.value)\n if (!send) {\n return { transport: resolved.value.kind, overall: 'failed', message: '发送通道不可用(检查 .p8 密钥文件或中继配置)', results: [] }\n }\n const notification: PushNotification = {\n notificationId: 'test-' + Date.now(),\n category: 'turn.completed',\n sessionId: 'push-test',\n title: 'DeepPilot 测试推送',\n body: '收到这条通知说明离线推送链路正常',\n }\n const results = await Promise.all(tokenized.map(async (device) => {\n const registration = device.apns!\n const { outcome, reason } = await send({\n deviceToken: registration.token,\n environment: registration.environment,\n notification,\n })\n return {\n name: deviceDisplayName(device),\n environment: registration.environment,\n outcome,\n // 前 10 位十六进制让运维能核对存储的 token 与设备当前持有的一致\n // (重装会轮换 token)。\n tokenFingerprint: registration.token.slice(0, 10),\n ...(reason !== undefined ? { reason } : {}),\n }\n }))\n const overall: PushTestResult['overall'] = results.some((r) => r.outcome === 'sent') ? 'sent' : 'failed'\n this.deps.log('push self-test: ' + overall + ' (' + results.map((r) => `\"${r.name}\"=${r.outcome}${r.reason ? '/' + r.reason : ''}`).join(', ') + ')')\n return { transport: resolved.value.kind, overall, results }\n }\n\n /** 设置页中继自测:健康检查 + 注册往返,成功即完成一次注册。 */\n async relayTest(): Promise<RelayTestResult> {\n const push = this.deps.config().push ?? {}\n const configured = push.provider ?? 'none'\n const effective = configured === 'none' && this.enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effective !== 'relay') {\n return {\n url: '',\n overall: 'failed',\n tokenIssued: false,\n steps: [{ id: 'health', ok: false, message: `当前推送模式不是中继(provider=${configured})。启用方式二选一:① 零配置——在 ios/project.yml 填写 DSPushEnrollKey(与服务器 RELAY_ENROLL_KEY 一致)并重新安装 App,打开 App 即自动启用;② 手动——将 push.provider 设为 relay 并填入 relayToken` }],\n }\n }\n const url = this.relayUrl()\n if (!/^https:\\/\\//i.test(url)) {\n return {\n url,\n overall: 'failed',\n tokenIssued: false,\n steps: [{ id: 'health', ok: false, message: 'relayUrl 必须是 https 地址:注册请求携带共享密钥,明文 HTTP 会把它暴露给链路上的任何节点' }],\n }\n }\n // 注册步骤需要一个身份:现在铸造,于是一次成功的自测同时就是一次完成的注册。\n if (!this.enrollmentCell.clientId && this.enrollmentCell.enrollKey) {\n this.enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n this.persistEnrollment()\n }\n return await runRelayProbe({\n url,\n clientId: this.enrollmentCell.clientId,\n enrollKey: this.enrollmentCell.enrollKey,\n manualToken: Boolean((push.relayToken ?? '').trim()),\n ...(this.deps.fetchImpl !== undefined ? { fetchImpl: this.deps.fetchImpl } : {}),\n onEnrolled: (token) => {\n this.enrollmentCell.token = token\n this.persistEnrollment()\n this.deps.log('push relay enrollment succeeded (via settings self-test)')\n },\n })\n }\n\n // ---------- 内部:配置解析 / 注册 / sender ----------\n\n private relayUrl(): string {\n return (this.deps.config().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n }\n\n private resolvePushConfig(config: Config):\n | { ok: true; value: PushConfigSnapshot }\n | { ok: false; reason: string } {\n const push = config.push ?? {}\n // 已注册的分布式 App 在用户没有显式选择时自动启用中继模式。\n const configured = push.provider ?? 'none'\n const effectiveProvider = configured === 'none' && this.enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effectiveProvider === 'relay') {\n const url = this.relayUrl()\n const token = (push.relayToken ?? '').trim() || this.enrollmentCell.token || ''\n if (!/^https:\\/\\//i.test(url)) return { ok: false, reason: 'relayUrl must be an https URL' }\n if (!token) return { ok: false, reason: 'relay token not enrolled yet' }\n return { ok: true, value: { kind: 'relay', url, token } }\n }\n if (effectiveProvider === 'apns') {\n const teamId = (push.teamId ?? '').trim()\n const keyId = (push.keyId ?? '').trim()\n const keyPath = expandHome((push.keyPath ?? '').trim() || join(this.deps.dataDir, 'apns', 'AuthKey.p8'))\n const bundleId = (push.bundleId ?? '').trim()\n if (!teamId || !keyId || !bundleId) return { ok: false, reason: 'teamId/keyId/bundleId missing' }\n return { ok: true, value: { kind: 'apns', teamId, keyId, keyPath, bundleId } }\n }\n return { ok: false, reason: 'provider disabled' }\n }\n\n /**\n * 对运营方中继做零配置注册。幂等且结果缓存在持久化单元里;一次失败只留\n * 一行日志,直到配置指纹变化。\n */\n private async ensureRelayEnrolled(url: string): Promise<string | undefined> {\n // 注册体携带分发方的共享钥匙;配置错的 http:// relayUrl 绝不能让它明文\n // 泄露。(发送路径的请求已由 resolvePushConfig 把门,注册调用点没有。)\n if (!/^https:\\/\\//i.test(url.trim())) {\n this.deps.log('push relay enrollment refused: relayUrl must be an https URL')\n return undefined\n }\n if (this.enrollmentCell.token) return this.enrollmentCell.token\n const fingerprint = url + ':' + String(this.enrollmentCell.enrollKey ?? '')\n if (fingerprint !== this.enrollAttemptFor) {\n this.enrollAttemptFor = fingerprint\n this.enrollLastAttemptAt = 0\n }\n // 同一指纹反复失败:节流到一分钟一次,宕机的中继不会把每次通知变成一次\n // 外呼风暴,同时瞬时故障仍能快速恢复。\n if (Date.now() - this.enrollLastAttemptAt < ENROLL_RETRY_MS) return undefined\n this.enrollLastAttemptAt = Date.now()\n try {\n if (!this.enrollmentCell.clientId) {\n this.enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n this.persistEnrollment()\n }\n const client = new RelayClient({ url, debug: this.deps.config().diagnostics?.debug === true, log: this.deps.log })\n const token = await client.enroll(this.enrollmentCell.clientId, this.enrollmentCell.enrollKey ?? '')\n if (!token) {\n this.deps.log('push relay enrollment failed (' + url + '); will retry on next trigger')\n return undefined\n }\n this.enrollmentCell.token = token\n this.persistEnrollment()\n this.deps.log('push relay enrollment succeeded')\n return token\n } catch (error) {\n this.deps.log('push relay enrollment error: ' + String(error))\n return undefined\n }\n }\n\n /**\n * 按当前配置惰性构建 sender。坏配置(读不到的 .p8)只让该指纹失效并留\n * 一行日志,而不是每个事件都失败。\n */\n private async senderFor(resolved: PushConfigSnapshot): Promise<PushSender | undefined> {\n const fingerprint = JSON.stringify(resolved)\n if (this.cachedSender?.fingerprint === fingerprint) return this.cachedSender.send\n // 最近的失败只在短窗口内阻止重试:长期坏配置不能把每个事件都变成日志风暴,\n // 但同一配置在密钥文件补放之后 MUST 再获得一次机会。\n if (\n this.senderFailedFor?.fingerprint === fingerprint &&\n Date.now() - this.senderFailedFor.at < SENDER_FAILURE_RETRY_MS\n ) {\n return undefined\n }\n if (this.cachedSender) {\n await this.cachedSender.dispose?.().catch(() => {})\n this.cachedSender = undefined\n }\n if (resolved.kind === 'relay') {\n // resolvePushConfig 已保证单元里存在 token(配置 token 或已完成的注册)。\n const client = new RelayClient({\n url: resolved.url,\n token: resolved.token,\n debug: this.deps.config().diagnostics?.debug === true,\n log: this.deps.log,\n })\n this.cachedSender = {\n fingerprint,\n send: (request) => client.send(request),\n }\n this.deps.log('push relay enabled')\n } else {\n try {\n await readFile(expandHome(resolved.keyPath), 'utf8')\n } catch (error) {\n this.senderFailedFor = { fingerprint, at: Date.now() }\n this.deps.log('apns push unavailable (key unreadable at ' + resolved.keyPath + '): ' + String(error))\n return undefined\n }\n const client = new ApnsClient({\n teamId: resolved.teamId,\n keyId: resolved.keyId,\n keyPath: resolved.keyPath,\n bundleId: resolved.bundleId,\n debug: this.deps.config().diagnostics?.debug === true,\n log: this.deps.log,\n })\n this.cachedSender = {\n fingerprint,\n send: (request) => client.send({\n ...request.notification,\n deviceToken: request.deviceToken,\n environment: request.environment,\n }),\n dispose: () => client.dispose(),\n }\n this.deps.log('apns push enabled')\n }\n this.senderFailedFor = undefined\n return this.cachedSender.send\n }\n\n /**\n * 小组件总览推送:令牌新鲜、三项 scope 齐全、7 天内的设备,按环境去重后\n * 各投一次;中继拒绝凭据时自愈。\n */\n private async flushWidgetPushes(): Promise<void> {\n if (!this.deps.enabledNow()) return\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) return\n const devices = this.deps.devices()\n const send = await this.senderFor(resolved.value)\n if (!devices || !send) return\n const sent = new Set<string>()\n for (const device of devices.list()) {\n const registration = device.widgetApns\n if (!registration || device.revokedAt !== undefined ||\n !(['notifications.register', 'sessions.read', 'interactions.respond'] as const).every((scope) =>\n device.scopes?.includes(scope)) ||\n Date.now() - registration.updatedAt > 7 * 24 * 60 * 60 * 1000) continue\n const key = registration.environment + ':' + registration.token\n if (sent.has(key)) continue\n sent.add(key)\n const { outcome, reason } = await send({\n deviceToken: registration.token, environment: registration.environment,\n notification: { kind: 'widget' },\n })\n if (shouldPrunePushToken(outcome, reason)) {\n devices.clearWidgetPushToken(device.deviceId, registration.token)\n }\n if (resolved.value.kind === 'relay' && reason === 'HTTP 401') {\n await this.healRelayCredential(resolved.value.url, resolved.value.token)\n }\n }\n }\n\n /** Live Activity 推送:与 alert 推送共用 sender 与自愈。 */\n private async sendLiveActivity(\n deviceToken: string,\n environment: ApnsEnvironment,\n notification: PushDelivery,\n ): Promise<SendOutcome> {\n if (!this.deps.enabledNow()) return { outcome: 'failed' }\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) return { outcome: 'failed' }\n const send = await this.senderFor(resolved.value)\n if (!send) return { outcome: 'failed' }\n const result = await send({ deviceToken, environment, notification })\n if (resolved.value.kind === 'relay' && result.reason === 'HTTP 401') {\n await this.healRelayCredential(resolved.value.url, resolved.value.token)\n }\n return result\n }\n\n /**\n * 401 自愈(原先是三份拷贝:widget / liveActivity / fanOut)。\n * 中继不再认这个凭据:丢掉它,从 enroll key 重新派生。节流由\n * ensureRelayEnrolled 自己负责,并行 401 不会打爆端点。\n */\n private async healRelayCredential(\n url: string,\n token: string | undefined,\n logLine?: string,\n ): Promise<void> {\n if (token === undefined || this.enrollmentCell.token !== token) return\n if (!this.enrollmentCell.enrollKey) return\n this.enrollmentCell.token = undefined\n this.persistEnrollment()\n if (logLine !== undefined) this.deps.log(logLine)\n await this.ensureRelayEnrolled(url)\n }\n}\n","import { createServer as createHttpServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http'\nimport { createServer as createHttpsServer } from 'node:https'\nimport type { Duplex } from 'node:stream'\n\nexport interface PhoneServerHandlers {\n health(req: IncomingMessage, res: ServerResponse): void | Promise<void>\n pair(req: IncomingMessage, res: ServerResponse): void | Promise<void>\n upgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void\n}\n\nexport interface PhoneServerTls {\n key: string\n cert: string\n}\n\n/** 传输层错误:TLS 握手失败,或 HTTP 报文解析失败。 */\nexport interface PhoneServerTransportError {\n kind: 'tls-client-error' | 'client-error'\n /** 原始错误对象;调用方负责脱敏,不直接落日志。 */\n error: unknown\n /** 对端地址(原始值);调用方必须哈希后再记录。 */\n remoteAddress: string | undefined\n}\n\nexport interface PhoneServerOptions {\n /**\n * 传输层错误出口。缺省时不挂监听,保持 Node 默认行为。\n * 这两类错误此前完全静默:手机证书不对时浏览器侧只看到 «连接已重置»,\n * 电脑端没有任何一行日志,无法区分「证书钉住失败」与「端口没开」。\n */\n onTransportError?: (event: PhoneServerTransportError) => void\n}\n\ninterface ErrorSocket {\n remoteAddress?: string | undefined\n destroy(): void\n}\n\n/**\n * A deliberately narrow transport listener. Both the LAN endpoint and the\n * loopback-only Funnel origin use this factory, so neither can accidentally\n * inherit DSH's wider web/API route surface.\n *\n * With `tls` the listener speaks HTTPS/WSS only (the LAN posture); without\n * it the listener is plain HTTP, which is reserved for the loopback Funnel\n * origin where tailscaled terminates TLS.\n */\nexport function createPhoneServer(\n handlers: PhoneServerHandlers,\n tls?: PhoneServerTls,\n options?: PhoneServerOptions,\n): Server {\n const onRequest = (req: IncomingMessage, res: ServerResponse): void => {\n const path = requestPath(req)\n if (path === '/phone/health') {\n void handlers.health(req, res)\n } else if (path === '/phone/pair') {\n void handlers.pair(req, res)\n } else {\n res.statusCode = 404\n res.end('not found')\n }\n }\n const server: Server = tls === undefined\n ? createHttpServer(onRequest)\n : createHttpsServer({ key: tls.key, cert: tls.cert, minVersion: 'TLSv1.2' }, onRequest)\n server.on('upgrade', (req, socket, head) => {\n if (requestPath(req) !== '/phone') {\n socket.end('HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n')\n return\n }\n handlers.upgrade(req, socket, head)\n })\n const onTransportError = options?.onTransportError\n if (onTransportError !== undefined) {\n // TLS 握手失败(证书钉住不符、客户端版本过旧……)。\n if (tls !== undefined) {\n const tlsServer = server as unknown as {\n on(event: 'tlsClientError', listener: (error: Error, socket: ErrorSocket) => void): unknown\n }\n tlsServer.on('tlsClientError', (error, socket) => {\n onTransportError({ kind: 'tls-client-error', error, remoteAddress: socket.remoteAddress })\n socket.destroy()\n })\n }\n // HTTP 报文层错误(畸形请求行 / 头)。挂上监听即接管 Node 的默认\n // 400 响应,因此必须自己收尾;直接销毁,不向未认证对端多写一个字节。\n server.on('clientError', (error: Error, socket: ErrorSocket) => {\n onTransportError({ kind: 'client-error', error, remoteAddress: socket.remoteAddress })\n socket.destroy()\n })\n }\n return server\n}\n\nfunction requestPath(req: IncomingMessage): string {\n try {\n return new URL(req.url ?? '/', 'http://phone.local').pathname\n } catch {\n return '/'\n }\n}\n\nexport function listen(server: Server, port: number, host: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const onError = (error: Error): void => {\n server.off('listening', onListening)\n reject(error)\n }\n const onListening = (): void => {\n server.off('error', onError)\n resolve()\n }\n server.once('error', onError)\n server.once('listening', onListening)\n server.listen(port, host)\n })\n}\n\nexport function closeServer(server: Server | undefined): Promise<void> {\n if (server === undefined || !server.listening) return Promise.resolve()\n return new Promise((resolve) => server.close(() => resolve()))\n}\n","/**\n * 传输协调器(TransportReconciler)——独立传输接入的公共协议,只写一次。\n *\n * 迁移前 LAN 与 Funnel 各有一份 40 行的 reconcile 闭包,做同一件事:算目标值 →\n * 与 appliedKey 比对 → 拆除前一个 → 起新的 → 更新状态,外加一条串行 tail 防\n * 并发。两份拷贝之间还飘着三处**刻意保留的差异**(见 TransportSpec)——本轮\n * 的目标是给 `apply()` 造出接口,不是统一传输的并发语义,因此差异原样保留并\n * 在此声明,而不是悄悄抹平。\n *\n * 每个传输一行声明(见 index.ts 的 createLocalTransport / createRemoteTransport),\n * 协调器只拥有差分、串行、拆除与状态发布。\n */\n\nimport type { Config } from './config.ts'\nimport type { DeepPilotReport } from './report-wire.ts'\n\n/** 状态的最小面:phase 必填,其余字段由各传输自己定义。 */\nexport interface TransportStatus {\n phase: string\n updatedAt: number\n}\n\n/** 目标值:参与差分的稳定对象。 */\nexport type TransportTarget = Record<string, unknown>\n\nexport interface TransportSpec<TInstance, TStatus extends TransportStatus> {\n /** 从当前配置算出的目标值;其 JSON 即差分键。 */\n keyOf(config: Config): TransportTarget\n /**\n * `true`:目标未启用时提前返回并直接置 disabled 状态(LAN 式)。\n * `false`:仍构造实例,由实例内部的 no-op 路径把状态置为 disabled(Funnel 式)。\n */\n skipWhenDisabled: boolean\n /**\n * appliedKey 的写入时机。LAN 在拆除旧 listener 之前写,Funnel 在 supervisor\n * 构造之后、异步 start 之前写——两者在「reconcile 等待期间又来一次\n * volatile-update」时行为不同。差异保留,见文件头说明。\n */\n applyKey: 'before-teardown' | 'after-construct'\n /** 构造实例:同步、即刻取得权威性(Funnel 的 appliedKey 在此之后写入)。 */\n construct(target: TransportTarget): TInstance\n /**\n * 启动实例:异步部分(TLS 加载、监听、拉起 helper)。可返回一个新实例来\n * 替换——LAN 的 listener 要等 TLS 身份就绪后才存在。抛错即走错误分支。\n */\n begin(instance: TInstance, guard: BeginGuard<TInstance>): Promise<TInstance | void>\n /** 拆掉一个实例。 */\n /** 拆除实例;`undefined` 表示没有可拆的(首轮或已拆)。 */\n stop(instance: TInstance | undefined): Promise<void>\n /** 目标未启用且 skipWhenDisabled 时的状态。 */\n disabledStatus(target: TransportTarget): TStatus\n /** 启动过程中的状态。 */\n startingStatus(target: TransportTarget): TStatus\n /** 启动失败时的状态;缺省表示该行没有错误分支,错误原样上抛。 */\n errorStatus?(error: unknown, target: TransportTarget): TStatus\n /** 状态查询:实例在场时问它;返回 undefined 表示「仍是我刚设的过渡态」。 */\n statusOf(instance: TInstance | undefined): TStatus | undefined\n /** 目标未启用时记的日志;缺省不记。 */\n disabledMessage?: string\n /**\n * 启动失败时记的日志;缺省由协调器给一句通用的。\n * 必须把 target 传进来:LAN 的端口来自配置,硬编码 0 会让所有\n * EADDRINUSE / EACCES 日志都写「port 0 is already in use」,\n * 用户无法知道是哪一个端口冲突(P0 端口 bug)。\n */\n failureMessage?: (error: unknown, target: TransportTarget) => string\n}\n\n/**\n * begin 期间的守卫:让行在不复制协调器内部状态的前提下表达「我已被取代」。\n * LAN 原先在 TLS 加载后与 listen 后各查一次(appliedLocalKey / localServer),\n * 这两个检查对应 isCurrent() 与 owns()。\n */\nexport interface BeginGuard<TInstance> {\n /** 这次触发是否仍是当前配置(差分键未被后续触发改写)。 */\n isCurrent(): boolean\n /** 传入的实例是否仍是协调器持有的那个(未被后续触发替换)。 */\n owns(instance: TInstance): boolean\n /** 进程是否正在销毁。 */\n isDisposed(): boolean\n}\n\nexport class TransportReconciler<TInstance, TStatus extends TransportStatus> {\n private instance: TInstance | undefined\n private appliedKey: string | undefined\n private disposed = false\n private tail: Promise<void> = Promise.resolve()\n private currentStatus: TStatus\n\n constructor(\n private readonly spec: TransportSpec<TInstance, TStatus>,\n private readonly config: () => Config,\n private readonly log: (message: string) => void,\n initialStatus: TStatus,\n ) {\n this.currentStatus = initialStatus\n }\n\n /**\n * 当前状态;report 快照直接读它。实例不在场时返回协调器自己维护的状态\n * (disabled / error / 初始),不被 `statusOf(undefined)` 的缺省值覆盖——\n * 否则一次启动失败会把状态翻转回 idle。\n */\n status(): TStatus {\n if (this.instance === undefined) return this.currentStatus\n return this.spec.statusOf(this.instance) ?? this.currentStatus\n }\n\n /** 由 loader/volatile-update 触发:串行执行,前一个没完就不抢。 */\n scheduleReconcile(): void {\n this.tail = this.tail\n .then(() => this.reconcile())\n .catch((error) => this.log('reconcile failed: ' + String(error)))\n }\n\n /** 等当前在途的那一次完成;供 teardown 使用。 */\n async settled(): Promise<void> {\n await this.tail\n }\n\n /** 一次协调:差分 → 拆除 → 启动 → 状态。 */\n async reconcile(): Promise<void> {\n if (this.disposed) return\n const target = this.spec.keyOf(this.config())\n const nextKey = JSON.stringify(target)\n if (nextKey === this.appliedKey) return\n\n if (this.spec.applyKey === 'before-teardown') {\n this.appliedKey = nextKey\n }\n\n const previous = this.instance\n this.instance = undefined\n // 没有可拆的就不调用 stop:LAN 的 closeServer(undefined) 虽是安全路径,\n // 但不该让每个 spec 都为「空实例」写防御分支。\n if (previous !== undefined) await this.spec.stop(previous).catch(() => {})\n if (this.disposed) return\n\n if (target.enabled !== true && this.spec.skipWhenDisabled) {\n this.currentStatus = this.spec.disabledStatus(target)\n if (this.spec.disabledMessage !== undefined) this.log(this.spec.disabledMessage)\n return\n }\n\n this.currentStatus = this.spec.startingStatus(target)\n try {\n const constructed = this.spec.construct(target)\n this.instance = constructed\n if (this.spec.applyKey === 'after-construct') {\n this.appliedKey = nextKey\n }\n const guard: BeginGuard<TInstance> = {\n isCurrent: () => this.appliedKey === nextKey,\n owns: (instance) => this.instance === instance,\n isDisposed: () => this.disposed,\n }\n const replacement = await this.spec.begin(constructed, guard)\n if (replacement !== undefined) this.instance = replacement\n if (this.disposed) {\n await this.spec.stop(this.instance).catch(() => {})\n return\n }\n this.currentStatus = this.spec.statusOf(this.instance) ?? this.currentStatus\n } catch (error) {\n if (this.spec.errorStatus === undefined) throw error\n // 有错误分支的行(LAN 式):半成品实例要拆掉并清空,否则 status() 会\n // 继续问它要状态,把 error 覆盖成 online。Funnel 式没有错误分支,\n // 实例的自身状态机会把 phase 落到 error,因此保留。\n const partial = this.instance\n this.instance = undefined\n if (partial !== undefined) await this.spec.stop(partial).catch(() => {})\n this.currentStatus = this.spec.errorStatus(error, target)\n this.log(this.spec.failureMessage?.(error, target) ?? 'transport failed: ' + String(error))\n }\n }\n\n /** 进程收尾:标记销毁、拆掉实例、等在途的那一次完成。 */\n async dispose(): Promise<void> {\n this.disposed = true\n const active = this.instance\n this.instance = undefined\n await this.spec.stop(active).catch(() => {})\n await this.tail\n }\n}\n\n// ---------- 两个传输行 ----------\n\nimport type { Server } from 'node:http'\nimport { join } from 'node:path'\nimport {\n closeServer,\n createPhoneServer,\n listen,\n type PhoneServerHandlers,\n type PhoneServerTransportError,\n} from './phone-server.ts'\nimport {\n localListenError,\n normalizeLocalPort,\n} from './local-policy.ts'\nimport {\n RemoteSupervisor,\n normalizeRemoteHostname,\n type RemoteStatus,\n} from './remote-supervisor.ts'\nimport { normalizeFunnelConnectionLimit } from './funnel-policy.ts'\n\n/** LAN 行的状态即 report 的 `local` 段。 */\nexport type LocalStatus = DeepPilotReport['local']\n\n/** LAN 实例:一个(可能还在起步的)TLS listener。 */\ninterface LocalInstance {\n /** TLS 就绪后才填上;在此之前协调器仍持有这个占位实例。 */\n server: Server | undefined\n tlsFingerprint: string | undefined\n port: number\n /** 上线时刻:状态查询复用同一时间戳,不随查询漂移(与迁移前一致)。 */\n onlineAt: number\n}\n\n/** LAN 需要的外部能力:窄 handler 面、TLS 身份加载器、当前配置。 */\nexport interface LocalTransportDeps {\n handlers: PhoneServerHandlers\n config: () => Config\n /** 加载(必要时创建)LAN TLS 身份;由 apply() 持有,reconciler 不懂 TLS。 */\n tls: () => Promise<{ key: string; cert: string; fingerprint: string }>\n log: (message: string) => void\n /** TLS 握手 / HTTP 报文层错误出口;index 负责把来源哈希后记录。 */\n onTransportError?: (event: PhoneServerTransportError) => void\n}\n\n/**\n * LAN 传输:TLS-only,绑定 0.0.0.0 的稳定端口。\n *\n * 三处刻意保留的差异:`skipWhenDisabled: true`(未启用即提前返回并置\n * disabled)、`applyKey: 'before-teardown'`(拆除旧 listener 之前写键)、\n * 有错误分支(监听失败落到 `phase: 'error'`)。另有两处 supersede 检查原样\n * 保留:TLS 加载后查 `isCurrent()`,listen 后查 `owns()`。\n */\nexport function createLocalTransport(deps: LocalTransportDeps): TransportReconciler<LocalInstance, LocalStatus> {\n const spec: TransportSpec<LocalInstance, LocalStatus> = {\n keyOf: (config) => ({\n enabled: config.enabled === true && config.local?.enabled !== false,\n port: normalizeLocalPort(config.local?.port),\n }),\n skipWhenDisabled: true,\n applyKey: 'before-teardown',\n // 只占位:listener 要等 TLS 身份就绪后才存在。\n construct: (target) => ({ server: undefined, tlsFingerprint: undefined, port: normalizeLocalPort(target.port), onlineAt: Date.now() }),\n begin: async (instance, guard) => {\n const tls = await deps.tls()\n // 被更新的触发取代:不建 listener,状态停在 starting(与迁移前一致)。\n if (!guard.isCurrent() || guard.isDisposed()) return\n const port = instance.port\n const server = createPhoneServer(\n deps.handlers,\n { key: tls.key, cert: tls.cert },\n deps.onTransportError !== undefined ? { onTransportError: deps.onTransportError } : undefined,\n )\n instance.server = server\n instance.tlsFingerprint = tls.fingerprint\n await listen(server, port, '0.0.0.0')\n // listen 期间被取代:拆掉这个半成品,不要让它在端口上继续听着。\n if (!guard.owns(instance) || guard.isDisposed()) {\n instance.server = undefined\n await closeServer(server)\n return\n }\n instance.onlineAt = Date.now()\n deps.log(`local transport listening on https://0.0.0.0:${port} (tls fingerprint ${tls.fingerprint})`)\n },\n stop: async (instance) => {\n if (instance?.server === undefined) return\n const server = instance.server\n instance.server = undefined\n await closeServer(server)\n },\n disabledStatus: (target) => ({\n phase: 'disabled', port: normalizeLocalPort(target.port), endpoints: [], updatedAt: Date.now(),\n }),\n startingStatus: (target) => ({\n phase: 'starting', port: normalizeLocalPort(target.port), endpoints: [], updatedAt: Date.now(),\n }),\n errorStatus: (error, target) => ({\n phase: 'error',\n port: normalizeLocalPort(target.port),\n endpoints: [],\n message: localListenError(error, normalizeLocalPort(target.port)),\n updatedAt: Date.now(),\n }),\n disabledMessage: 'local transport disabled',\n // 端口从 target 取,不再硬编码 0:EADDRINUSE 的日志必须能指向用户\n // 实际配置的那个端口。\n failureMessage: (error, target) => 'local transport failed: ' + localListenError(error, normalizeLocalPort(target.port)),\n statusOf: (instance) => {\n if (instance === undefined || instance.server === undefined) return undefined\n return {\n phase: 'online',\n port: instance.port,\n endpoints: [],\n tlsFingerprint: instance.tlsFingerprint,\n updatedAt: instance.onlineAt,\n }\n },\n }\n return new TransportReconciler<LocalInstance, LocalStatus>(spec, deps.config, deps.log, {\n phase: deps.config().enabled === true && deps.config().local?.enabled !== false ? 'starting' : 'disabled',\n port: normalizeLocalPort(deps.config().local?.port),\n endpoints: [],\n updatedAt: Date.now(),\n })\n}\n\n/** Funnel 需要的外部能力:窄 handler 面、loopback origin 地址、数据目录。 */\nexport interface RemoteTransportDeps {\n handlers: PhoneServerHandlers\n config: () => Config\n /** loopback origin 的地址;未就绪时该行不协调。 */\n originURL: () => string | undefined\n dataDir: string\n log: (message: string) => void\n}\n\n/**\n * Funnel 传输:loopback origin + Tailscale Funnel helper。\n *\n * 三处刻意保留的差异:`skipWhenDisabled: false`(未启用仍构造 supervisor,\n * 由其内部 no-op 把状态置 disabled)、`applyKey: 'after-construct'`\n * (supervisor 构造后、异步 start 之前写键)、没有错误分支(supervisor 自己\n * 的状态机负责 phase)。\n */\nexport function createRemoteTransport(deps: RemoteTransportDeps): TransportReconciler<RemoteSupervisor, RemoteStatus> {\n const fallbackStatus = (): RemoteStatus => ({\n provider: 'tailscale-funnel',\n phase: deps.config().remote?.enabled === true ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n })\n const spec: TransportSpec<RemoteSupervisor, RemoteStatus> = {\n keyOf: (config) => {\n const remoteConfig = config.remote ?? {}\n const remotePort: 443 | 8443 | 10000 = remoteConfig.funnelPort === 8443 || remoteConfig.funnelPort === 10000\n ? remoteConfig.funnelPort\n : 443\n return {\n enabled: config.enabled === true && remoteConfig.enabled === true && remoteConfig.provider === 'tailscale-funnel',\n hostname: normalizeRemoteHostname(remoteConfig.hostname),\n statePath: remoteConfig.statePath?.trim() || join(deps.dataDir, 'tailscale'),\n helperPath: remoteConfig.helperPath?.trim() || undefined,\n funnelPort: remotePort,\n maxConnectionsPerSource: normalizeFunnelConnectionLimit(remoteConfig.maxConnectionsPerSource),\n }\n },\n skipWhenDisabled: false,\n applyKey: 'after-construct',\n construct: (target) => new RemoteSupervisor({\n enabled: target.enabled === true,\n hostname: String(target.hostname),\n statePath: String(target.statePath),\n ...(target.helperPath !== undefined ? { helperPath: String(target.helperPath) } : {}),\n funnelPort: target.funnelPort as 443 | 8443 | 10000,\n maxConnectionsPerSource: Number(target.maxConnectionsPerSource),\n log: deps.log,\n }),\n begin: async (supervisor) => {\n const originURL = deps.originURL()\n // origin 尚未就绪:这一轮不启动(与迁移前 originURL === undefined 时\n // 直接返回一致;键已写入,origin 就绪后的那次触发会把它建起来)。\n if (originURL === undefined) return\n await supervisor.start(originURL)\n },\n stop: async (supervisor) => {\n if (supervisor === undefined) return\n await supervisor.dispose()\n },\n disabledStatus: () => ({ provider: 'tailscale-funnel', phase: 'disabled', updatedAt: Date.now() }),\n startingStatus: () => ({ provider: 'tailscale-funnel', phase: 'stopped', updatedAt: Date.now() }),\n statusOf: (supervisor) => supervisor?.status() ?? fallbackStatus(),\n }\n return new TransportReconciler<RemoteSupervisor, RemoteStatus>(spec, deps.config, deps.log, fallbackStatus())\n}\n","/**\n * at-most-once dispatch journal:prompt 投递与非 prompt 变更的**同一个** module。\n *\n * 迁移前 `prompt-delivery.ts` 与 `mutation-journal.ts` 是同一份耐久协议的两份\n * 拷贝(盘点结论:key/expired/save 骨架字节级相同,mutation 甚至反向 import\n * prompt 的 validSendId,共享已从错误方向发生)。两者的差别集中在六个面:\n * 指纹覆盖范围、条目字段、回执形状、id 校验、lookup、落盘容器格式——全部收进\n * 注入的 codec;核心只留耐久协议。\n *\n * 核心拥有:key 派生、原子写盘(temp + fsync + rename + 目录 fsync)、加载与\n * 逐字段校验、保留清扫、容量、healthy 降级、在途去重、opener 单例。\n *\n * healthy 的唯一来源是**构造期的解析失败**(文件损坏,续写会写坏格式);\n * 运行期的落盘失败只影响当次投递,下次投递重新尝试落盘。两者曾经共用同一个\n * 标志位,一次瞬时磁盘错误就让整个进程的所有投递永久短路到 DSH 重启\n * (issue #20 / #26)。\n *\n * codec 拥有:一次投递的全部「方言」。它同时是两个 adapter——prompt 回执与\n * mutation 结果——因此这条缝是真的,不是假想的。\n */\n\nimport { createHash, randomUUID } from 'node:crypto'\nimport { existsSync, readFileSync, mkdirSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, statSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { validSendId } from './connection-policy.ts'\nimport { ERROR_CODES, type WireErrorCode } from './wire-errors.ts'\n\n/** 落盘条目:两个通用字段 + codec 私有字段。 */\nexport interface JournalEntry {\n fingerprint: string\n createdAt: number\n [field: string]: unknown\n}\n\n/** operation 的结果:成功带值,失败带 wire 错误码。 */\nexport type OperationResult<T = unknown> =\n | { ok: true; value: T }\n | { ok: false; code: WireErrorCode; message?: string }\n\n/**\n * 一切「不看条目就能定」的结果类别。codec 用一个方法把它们翻译成自己的方言,\n * 而不是让核心知道任何一方的形状。\n */\nexport type DispatchOutcome =\n /** 命中既有条目(重放)。 */\n | { kind: 'replay'; entry: JournalEntry }\n /** 同一 id 搭不同内容。 */\n | { kind: 'mismatch' }\n /** 构造期解析失败:journal 已永久降级,本次进程不再投递。 */\n | { kind: 'unavailable' }\n /** id 的纪元前缀超出保留窗口。 */\n | { kind: 'expired' }\n /** 条目数已达容量上限。 */\n | { kind: 'full' }\n /** 刚跑完的那一次:operation 的结果原样带出(不标 replayed)。 */\n | { kind: 'ran'; entry: JournalEntry; result: OperationResult }\n /** operation 抛错:条目保持 unknown,且不得自动重试。 */\n | { kind: 'crashed' }\n /**\n * 预留条目时落盘失败——上游**没有**被调用,但无从判断 rename 是否已经\n * 生效(save() 可能在 rename 之后才抛),因此条目留在内存里按 unknown 处理。\n */\n | { kind: 'reserve-failed' }\n /** id 形状不合法(仅 mutation 变体开启这道守卫)。 */\n | { kind: 'invalid-id' }\n\nexport interface DispatchCodec<TRequest = unknown, TResult = unknown> {\n /** 实例身份:opener 单例键的一部分(含 persistValues 等变体差异)。 */\n readonly identity: string\n /** 请求指纹。prompt 覆盖 [sessionId, content];mutation 只覆盖 content。 */\n fingerprint(request: TRequest): string\n /** 预留条目时写入的 codec 字段(fingerprint/createdAt 由核心补)。 */\n reserve(request: TRequest, id: string): Record<string, unknown>\n /** operation 成功后就地更新条目。 */\n accepted(entry: JournalEntry, id: string, value: unknown): void\n /** operation 失败后就地更新条目。 */\n rejected(entry: JournalEntry, id: string, code: WireErrorCode): void\n /** 把结果类别翻译成调用方言。 */\n resultOf(outcome: DispatchOutcome, id: string): TResult\n /** id 非法时是否先拒掉(mutation 是;prompt 由 wire 层保证,故否)。 */\n readonly requiresValidId?: boolean\n /** lookup 的匹配规则;缺省表示该实例不提供 lookup。 */\n matches?(entry: JournalEntry, request: unknown): boolean\n /** lookup 未命中时回什么(prompt 要区分 notFound / expired)。 */\n miss?(id: string, expired: boolean): TResult\n /** 落盘容器 → 内存条目表;任何不合法都抛错(核心转成 healthy=false)。 */\n parse(raw: unknown): Record<string, JournalEntry>\n /** 内存条目表 → 落盘容器。 */\n serialize(entries: Record<string, JournalEntry>): unknown\n}\n\nexport interface DispatchJournalOptions<TRequest = unknown, TResult = unknown> {\n codec: DispatchCodec<TRequest, TResult>\n path?: string\n /** 落盘体积上限:prompt 8MB、mutation 4MB(由条目实际大小决定,不统一)。 */\n maxFileBytes?: number\n capacity?: number\n retentionMs?: number\n /**\n * 并发重复是否搭同在途那次。prompt 是(重复方拿到同一个最终回执);\n * mutation 否(重复方立刻拿到 unknown → E_INTERNAL)。两者都由对等快照钉住。\n */\n joinInFlight?: boolean\n /**\n * 落盘失败的诊断输出。由调用方注入它已有的 logger;缺省即静默。\n * 只打阶段与 errno,绝不打条目内容或消息体(plugin 的日志禁令)。\n */\n log?: (message: string) => void\n /**\n * 目录 fsync 里「对 fd 调用 fsync」这一步的覆盖点,缺省即 `fsyncSync`。\n *\n * 存在的唯一理由是可测:Linux CI 上复现不出 Windows 的 EPERM。注入点刻意\n * 停在 fd 这一层而不是替换整个目录 fsync——容忍 EPERM 的判断必须留在真实\n * 路径里被测到,换掉整函数就等于把待测逻辑一起换掉了。\n */\n fsyncFileDescriptor?: (fd: number) => void\n}\n\nconst DEFAULT_CAPACITY = 10_000\nconst DEFAULT_RETENTION_MS = 7 * 24 * 3600 * 1000\n/** 比保留窗口多宽限 5 分钟:边界附近的条目不会被反复删了又写。 */\nconst SWEEP_GRACE_MS = 5 * 60 * 1000\n\n/**\n * 目录 fsync 的三个「平台不支持」码。\n *\n * Windows 上 `openSync(dir, 'r')` 成功但 `fsyncSync(dirfd)` 必定抛错(社区实测\n * 报 EPERM,issue #20 / #26);EISDIR / EINVAL 是同一现象的其他平台变体。\n * 这三个码在本调用点都不代表数据损坏:文件内容已在 rename 之前 fsync 过,且\n * rename 在同一目录内是原子的,跳过的只是目录项的额外耐久性保证。\n */\nconst TOLERATED_DIR_FSYNC_CODES: ReadonlySet<string> = new Set(['EPERM', 'EISDIR', 'EINVAL'])\n\n/** errno 优先,取不到就退回错误本身的字符串(诊断用,不参与控制流)。 */\nfunction errnoOf(error: unknown): string {\n const code = (error as NodeJS.ErrnoException | null)?.code\n return typeof code === 'string' ? code : String(error)\n}\n\n/** 这类错误是否属于「该平台不支持目录 fsync」而非真的写坏了。 */\nexport function isToleratedDirectoryFsyncError(error: unknown): boolean {\n return TOLERATED_DIR_FSYNC_CODES.has(errnoOf(error))\n}\n\n/**\n * 目录 fsync:把 rename 造成的目录项变更推给磁盘。\n *\n * 按 errno 而非 `process.platform` 判断:WSL / Wine 之类环境下平台名并不可靠,\n * 而 errno 才是真正决定「这个平台支不支持」的信号(issue #26 给的两种建议里,\n * 按 errno 的那种才是对的)。其余 errno(EIO、ENOSPC……)照旧抛出。\n */\nexport function fsyncDirectory(dirPath: string, fsync: (fd: number) => void = fsyncSync): void {\n const fd = openSync(dirPath, 'r')\n try {\n fsync(fd)\n } catch (error) {\n if (!isToleratedDirectoryFsyncError(error)) throw error\n } finally {\n closeSync(fd)\n }\n}\n\n/** Durable at-most-once dispatch. Unknown outcomes are never automatically retried. */\nexport class DispatchJournal<TRequest = unknown, TResult = unknown> {\n private entries: Record<string, JournalEntry> = Object.create(null)\n private inFlight = new Map<string, Promise<TResult>>()\n /** 只由构造期的解析失败置位,见下方 catch;运行期落盘失败不碰它。 */\n private healthy = true\n private readonly maxFileBytes: number\n private readonly capacity: number\n private readonly retentionMs: number\n private readonly joinInFlight: boolean\n\n constructor(private readonly options: DispatchJournalOptions<TRequest, TResult>) {\n this.maxFileBytes = options.maxFileBytes ?? 8 * 1024 * 1024\n this.capacity = options.capacity ?? DEFAULT_CAPACITY\n this.retentionMs = options.retentionMs ?? DEFAULT_RETENTION_MS\n this.joinInFlight = options.joinInFlight ?? true\n const path = options.path\n if (path === undefined || !existsSync(path)) return\n try {\n if (statSync(path).size > this.maxFileBytes) throw new Error('oversized journal')\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as { version?: unknown; entries?: unknown }\n if (parsed.version !== 1 || !Array.isArray(parsed.entries) || parsed.entries.length > this.capacity) {\n throw new Error('invalid journal')\n }\n const loaded = this.options.codec.parse(parsed.entries)\n for (const key of Object.keys(loaded)) this.entries[key] = loaded[key]!\n } catch {\n // 唯一会把 healthy 拉黑的地方:文件读不出来或不合法。宁可整个进程都拒绝\n // 投递,也不按一个可能是错的格式续写(续写会污染此后每一次回放)。\n this.healthy = false\n }\n }\n\n private key(deviceId: string, id: string): string {\n return createHash('sha256').update(JSON.stringify([deviceId, id])).digest('hex')\n }\n\n /** id 的纪元前缀是否还在保留窗口内(未来 5 分钟以上同样算非法)。 */\n private expired(id: string, now = Date.now()): boolean {\n const age = now - Number(id.slice(0, 13))\n return age > this.retentionMs || age < -SWEEP_GRACE_MS\n }\n\n private save(): void {\n const path = this.options.path\n if (path === undefined) return\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 })\n const temp = path + '.' + randomUUID() + '.tmp'\n const fd = openSync(temp, 'wx', 0o600)\n try {\n writeFileSync(fd, JSON.stringify({ version: 1, entries: this.options.codec.serialize(this.entries) }))\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n renameSync(temp, path)\n fsyncDirectory(dirname(path), this.options.fsyncFileDescriptor)\n }\n\n /**\n * 落盘一次,失败只报不降级。\n *\n * 与 `healthy` 的分工:`healthy` 只由构造期的解析失败置位,运行期的磁盘错误\n * 不再拉黑整个进程——此前一次 save() 失败就让本进程余下的所有投递永久返回\n * unknown,直到 DSH 重启(issue #20 / #26)。现在只影响当次投递,下一次\n * dispatch 会重新尝试。\n */\n private persist(): boolean {\n try {\n this.save()\n return true\n } catch (error) {\n this.options.log?.('journal 落盘失败,本次投递按未确认处理,下次投递会重新尝试:' + errnoOf(error))\n return false\n }\n }\n\n /** 查询既有回执;`request` 交给 codec 的 matches 判定(缺省即不提供 lookup)。 */\n lookup(deviceId: string, id: string, request?: unknown): TResult | undefined {\n const codec = this.options.codec\n if (codec.matches === undefined) return undefined\n if (!this.healthy) return codec.resultOf({ kind: 'unavailable' }, id)\n const entry = this.entries[this.key(deviceId, id)]\n if (entry !== undefined && codec.matches(entry, request)) return codec.resultOf({ kind: 'replay', entry }, id)\n return codec.miss?.(id, this.expired(id)) ?? codec.resultOf({ kind: 'unavailable' }, id)\n }\n\n /**\n * 幂等投递。未知结果绝不自动重试:条目保持 unknown,重放时由 codec 决定\n * 调用方看到什么。\n */\n async dispatch(deviceId: string, id: string, request: TRequest, operation: () => Promise<OperationResult>): Promise<TResult> {\n const codec = this.options.codec\n // id 守卫在最前(与迁移前 mutation 的次序一致):形状非法的请求不落盘。\n if (codec.requiresValidId === true && !validSendId(id)) {\n return codec.resultOf({ kind: 'invalid-id' }, id)\n }\n const key = this.key(deviceId, id)\n const fingerprint = codec.fingerprint(request)\n const existing = this.entries[key]\n if (existing !== undefined) {\n if (existing.fingerprint !== fingerprint) return codec.resultOf({ kind: 'mismatch' }, id)\n // 搭同在途那次(prompt):并发重复共享一次上游调用,拿同一最终回执。\n const pending = this.inFlight.get(key)\n if (this.joinInFlight && pending !== undefined) return pending\n return codec.resultOf({ kind: 'replay', entry: existing }, id)\n }\n if (!this.healthy) return codec.resultOf({ kind: 'unavailable' }, id)\n if (this.expired(id)) return codec.resultOf({ kind: 'expired' }, id)\n\n const now = Date.now()\n for (const [entryKey, entry] of Object.entries(this.entries)) {\n if (now - entry.createdAt > this.retentionMs + SWEEP_GRACE_MS && !this.inFlight.has(entryKey)) {\n delete this.entries[entryKey]\n }\n }\n if (Object.keys(this.entries).length >= this.capacity) return codec.resultOf({ kind: 'full' }, id)\n\n // 先落盘预约再跑上游:进程在两次之间死掉,重启后也能识别「结果未知」。\n const entry: JournalEntry = { fingerprint, createdAt: Number(id.slice(0, 13)), ...codec.reserve(request, id) }\n this.entries[key] = entry\n if (!this.persist()) {\n // 预约没落盘就不能跑上游。条目**保留**在内存里:save() 可能是在 rename\n // 之后才抛的,磁盘上或许已经有这条 unknown,抹掉等于把它从表里除名。\n return codec.resultOf({ kind: 'reserve-failed' }, id)\n }\n\n const run = (async (): Promise<TResult> => {\n let ran: OperationResult\n try {\n ran = await operation()\n if (ran.ok) codec.accepted(entry, id, ran.value)\n else codec.rejected(entry, id, ran.code)\n } catch {\n // 上游可能在传输失败前已经接受:条目保持 unknown,绝不自动重试。\n return codec.resultOf({ kind: 'crashed' }, id)\n }\n // 结果落盘失败不回滚条目:operation 已经跑过,回滚等于把一次可能已生效的\n // 上游调用从表里除名。下次投递若写成功,会把整表(含本条目)一起写回去。\n this.persist()\n // 刚跑完的那一次:operation 的结果原样带出,不标 replayed。\n return codec.resultOf({ kind: 'ran', entry, result: ran }, id)\n })()\n this.inFlight.set(key, run)\n try { return await run } finally { this.inFlight.delete(key) }\n }\n\n /** 等所有在途投递结束;供进程收尾。 */\n async settled(): Promise<void> {\n while (this.inFlight.size > 0) {\n await Promise.all([...this.inFlight.values()])\n }\n }\n}\n\n// ---------- opener ----------\n\nconst journals = new Map<string, DispatchJournal<never, never>>()\n\n/**\n * 按 path + codec 身份取单例。身份进键是必要的:同一路径用不同 codec/persistValues\n * 打开必须得到不同实例——迁移前的键只有 path,`persistValues` 因此会被静默忽略。\n */\nexport function openDispatchJournal<TRequest, TResult>(\n options: DispatchJournalOptions<TRequest, TResult>,\n): DispatchJournal<TRequest, TResult> {\n const { path, codec } = options\n if (path === undefined) {\n return new DispatchJournal<TRequest, TResult>(options)\n }\n const cacheKey = path + '|' + codec.identity\n const cached = journals.get(cacheKey)\n if (cached !== undefined) return cached as unknown as DispatchJournal<TRequest, TResult>\n const journal = new DispatchJournal<TRequest, TResult>(options)\n journals.set(cacheKey, journal as unknown as DispatchJournal<never, never>)\n return journal\n}\n\n// ---------- prompt 投递 codec ----------\n\n/** prompt 投递的回执:手机侧按 clientSendId 对账。 */\nexport type DeliveryStatus = 'accepted' | 'rejected' | 'unknown' | 'notFound' | 'expired'\nexport interface DeliveryReceipt {\n clientSendId: string\n status: DeliveryStatus\n userSeq?: number\n code?: string\n}\n\n/** prompt 的落盘条目:回执内嵌,sessionId 单独存一份供 lookup 比对。 */\ninterface PromptEntry extends JournalEntry {\n sessionId: string\n receipt: DeliveryReceipt\n}\n\n/** 受理即 accepted;四种会被 APNs 终态清理的失败码之外一律原样上抛。 */\nconst PROMPT_RECEIPT_CODES = ['E_BUSY', 'E_NOT_FOUND', 'E_PROTOCOL', 'E_UNSUPPORTED'] as const\n\n/**\n * prompt 投递方言:\n * - 指纹覆盖 [sessionId, content](同一条 prompt 换会话重发视为不同请求);\n * - 并发重复搭同在途那次(joinInFlight),因此重复方拿到同一最终回执;\n * - 有 lookup,且 sessionId 不匹配即视为未命中;\n * - 落盘是 [key, entry] 二元组数组(历史格式,零迁移)。\n */\nexport const promptDeliveryCodec: DispatchCodec<\n { sessionId: string; content: unknown },\n DeliveryReceipt\n> & { identity: string } = {\n identity: 'prompt-delivery',\n fingerprint: (request) => createHash('sha256')\n .update(JSON.stringify([request.sessionId, request.content]))\n .digest('hex'),\n reserve: (request, id) => ({\n sessionId: request.sessionId,\n receipt: { clientSendId: id, status: 'unknown' },\n }),\n accepted: (entry, id, value) => {\n const receipt = entry.receipt as DeliveryReceipt\n entry.receipt = { clientSendId: id, status: 'accepted', userSeq: value as number }\n },\n rejected: (entry, id, code) => {\n const receipt = entry.receipt as DeliveryReceipt\n entry.receipt = { clientSendId: id, status: 'rejected', code }\n },\n resultOf: (outcome, id) => {\n switch (outcome.kind) {\n case 'replay':\n case 'ran': return (outcome.entry.receipt ?? { clientSendId: id, status: 'unknown' }) as DeliveryReceipt\n case 'mismatch': return { clientSendId: id, status: 'rejected', code: 'E_PROTOCOL' }\n case 'unavailable':\n case 'crashed':\n case 'reserve-failed': return { clientSendId: id, status: 'unknown' }\n case 'expired': return { clientSendId: id, status: 'expired' }\n case 'full': return { clientSendId: id, status: 'rejected', code: 'E_BUSY' }\n case 'invalid-id': return { clientSendId: id, status: 'rejected', code: 'E_PROTOCOL' }\n }\n },\n // prompt 不做 id 守卫:wire 层已在 validateRequest/行内校验保证形状。\n requiresValidId: false,\n matches: (entry, request) => entry.sessionId === request,\n miss: (id, expired) => ({ clientSendId: id, status: expired ? 'expired' : 'notFound' }),\n parse: (raw) => {\n const out: Record<string, JournalEntry> = Object.create(null)\n for (const [key, value] of raw as Array<[string, PromptEntry]>) {\n if (typeof key !== 'string' || !/^[a-f0-9]{64}$/.test(key) || !value || typeof value.sessionId !== 'string' ||\n !Number.isFinite(value.createdAt) || typeof value.fingerprint !== 'string' ||\n !validSendId(value.receipt?.clientSendId) ||\n !['accepted', 'rejected', 'unknown'].includes(value.receipt.status)) throw new Error('invalid entry')\n if (!/^[a-f0-9]{64}$/.test(value.fingerprint) ||\n value.createdAt !== Number(value.receipt.clientSendId.slice(0, 13)) ||\n (value.receipt.status === 'accepted' && !Number.isSafeInteger(value.receipt.userSeq)) ||\n (value.receipt.status === 'rejected' && !(PROMPT_RECEIPT_CODES as readonly string[]).includes(String(value.receipt.code)))) {\n throw new Error('invalid receipt')\n }\n out[key] = value\n }\n return out\n },\n serialize: (entries) => Object.entries(entries).map(([key, entry]) => [key, entry]),\n}\n\n// ---------- schedule/fork mutation codec ----------\n\n/** mutation 的结果:replayed 让调用方知道这是重放而非新执行。 */\nexport type MutationErrorCode = WireErrorCode\n\nexport type MutationOperationResult<T> =\n | { ok: true; value: T }\n | { ok: false; code: MutationErrorCode; message?: string }\n\nexport type MutationDispatchResult<T> =\n | { ok: true; value?: T; replayed?: boolean }\n | { ok: false; code: MutationErrorCode; message?: string; replayed?: boolean }\n\ninterface MutationEntry extends JournalEntry {\n status: 'accepted' | 'rejected' | 'unknown'\n code?: MutationErrorCode\n value?: unknown\n}\n\n/**\n * mutation 方言(schedule 与 fork 共用,fork 打开 persistValues):\n * - 有 id 守卫,且在最前面(形状非法的 clientRequestId 不落盘);\n * - 并发重复不搭在途那次:重复方立刻拿到 unknown → E_INTERNAL「不得自动重试」;\n * - 成功值只在 persistValues 时落盘——fork 需要重启后仍能重放 sessionId,\n * schedule 的 authoritative 资源由客户端重新拉取,不在此复制第二份;\n * - 落盘是对象数组(历史格式,零迁移)。\n */\nexport function scheduleMutationCodec(persistValues: boolean): DispatchCodec<unknown, MutationDispatchResult<unknown>> & { identity: string } {\n const isErrorCode = (value: unknown): value is MutationErrorCode =>\n typeof value === 'string' && value in ERROR_CODES\n return {\n identity: 'mutation:' + (persistValues ? 'persist' : 'memory'),\n fingerprint: (request) => createHash('sha256').update(JSON.stringify(request)).digest('hex'),\n reserve: () => ({ status: 'unknown' }),\n accepted: (entry, _id, value) => {\n entry.status = 'accepted'\n if (persistValues) entry.value = value\n },\n rejected: (entry, _id, code) => {\n entry.status = 'rejected'\n entry.code = code\n },\n resultOf: (outcome, id) => {\n switch (outcome.kind) {\n case 'ran': return outcome.result as MutationDispatchResult<unknown>\n case 'replay': {\n const entry = outcome.entry as MutationEntry\n if (entry.status === 'accepted') return { ok: true, value: entry.value, replayed: true }\n if (entry.status === 'rejected' && entry.code !== undefined) return { ok: false, code: entry.code, replayed: true }\n return { ok: false, code: 'E_INTERNAL', message: 'mutation outcome is unknown; do not retry automatically', replayed: true }\n }\n case 'mismatch': return { ok: false, code: 'E_PROTOCOL', message: 'clientRequestId was reused with different content', replayed: true }\n case 'invalid-id': return { ok: false, code: 'E_PROTOCOL', message: 'invalid clientRequestId' }\n case 'unavailable': return { ok: false, code: 'E_INTERNAL', message: 'mutation journal unavailable' }\n case 'expired': return { ok: false, code: 'E_PROTOCOL', message: 'clientRequestId is outside the retry window' }\n case 'full': return { ok: false, code: 'E_BUSY', message: 'mutation journal is full' }\n case 'reserve-failed': return { ok: false, code: 'E_INTERNAL', message: 'mutation journal could not persist the request' }\n case 'crashed': return { ok: false, code: 'E_INTERNAL', message: 'mutation outcome is unknown; do not retry automatically' }\n }\n },\n requiresValidId: true,\n // mutation 没有 lookup:权威资源由客户端重新拉取。\n parse: (raw) => {\n const out: Record<string, JournalEntry> = Object.create(null)\n for (const value of raw as MutationEntry[]) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('invalid mutation entry')\n const entry = value as Record<string, unknown>\n if (typeof entry.fingerprint !== 'string' || !/^[a-f0-9]{64}$/.test(entry.fingerprint) ||\n typeof entry.createdAt !== 'number' || !Number.isFinite(entry.createdAt) ||\n !['accepted', 'rejected', 'unknown'].includes(String(entry.status))) throw new Error('invalid mutation entry')\n if (entry.status === 'rejected' && !isErrorCode(entry.code)) throw new Error('invalid mutation code')\n const key = String((value as { key?: unknown }).key)\n if (!/^[a-f0-9]{64}$/.test(key)) throw new Error('invalid mutation key')\n out[key] = {\n fingerprint: entry.fingerprint as string,\n createdAt: entry.createdAt as number,\n status: entry.status as MutationEntry['status'],\n ...(isErrorCode(entry.code) ? { code: entry.code } : {}),\n ...(persistValues && entry.value !== undefined ? { value: entry.value } : {}),\n }\n }\n return out\n },\n serialize: (entries) => Object.entries(entries).map(([key, entry]) => ({ key, ...entry })),\n }\n}\n","/**\n * 通知正文的纯文本投影。\n *\n * 为什么需要:s2c.notify 与 APNs 的 body 是\"可直接展示\"的文本(PROTOCOL §6),\n * 客户端不得改写;而来源(助手回复、审批原因、提问)都是 Host 给的 Markdown。\n * 直接截原文会把链接地址、URL 编码的中文路径、代码块符号带进锁屏横幅,\n * 所以 Bridge 出站前在这里统一转成纯文本。\n *\n * 只做\"去标记、留可读文字\",不追求完整 Markdown 语义;任何无法识别的写法原样保留。\n */\n\n/** 把 Markdown 拆成段落并逐段转为单行纯文本;清洗后为空的段落被丢弃。 */\nexport function notificationParagraphs(markdown: string): string[] {\n const text = stripCodeFences(markdown.replace(/\\r\\n?/g, '\\n'))\n return text\n .split(/\\n[ \\t]*\\n/)\n .map(plainParagraph)\n .filter((paragraph) => paragraph.length > 0)\n}\n\n/**\n * 通知正文:`last` 取最后一个非空段落(任务完成 / 异常结束——回复结尾通常是结论),\n * `all` 把全部段落合成一行(审批原因、提问原文)。长度上限由调用方负责。\n */\nexport function notificationPlainText(markdown: string, pick: 'last' | 'all'): string {\n const paragraphs = notificationParagraphs(markdown)\n return pick === 'last' ? (paragraphs.at(-1) ?? '') : paragraphs.join(' ')\n}\n\n/** 围栏代码块整体丢弃:横幅里放不下代码,留一半反而更难读。未闭合的围栏丢到文末。 */\nfunction stripCodeFences(text: string): string {\n return text.replace(/^[ \\t]*(`{3,}|~{3,})[^\\n]*\\n[\\s\\S]*?(?:^[ \\t]*\\1[ \\t]*$|(?![\\s\\S]))/gm, '\\n')\n}\n\nfunction plainParagraph(paragraph: string): string {\n const lines = paragraph\n .split('\\n')\n // 表格分隔行与分割线没有文字内容。\n .filter((line) => !(line.includes('|') && line.includes('-') && /^[\\s|:-]+$/.test(line)))\n .filter((line) => !/^\\s*([*_-])(\\s*\\1){2,}\\s*$/.test(line))\n .map((line) => line\n .replace(/^\\s*(>\\s?)+/, '')\n .replace(/^\\s*#{1,6}\\s+/, '')\n .replace(/^\\s*([-*+]|\\d+[.)])\\s+(\\[[ xX]\\]\\s+)?/, '')\n // 只有以 | 开头的行才当表格行拆格,正文里的竖线保留。\n .replace(/^\\s*\\|(.*?)\\|?\\s*$/, (_row, cells: string) => cells.replace(/\\s*\\|\\s*/g, ' ')))\n return decodePercentRuns(stripInline(lines.join(' ')))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction stripInline(text: string): string {\n return text\n // 图片:alt 文字在横幅里没有意义。\n .replace(/!\\[[^\\]]*\\]\\((?:[^()]|\\([^()]*\\))*\\)/g, '')\n // 行内链接 [文字](地址) 与引用式链接 [文字][id] 只留文字;地址允许一层括号。\n .replace(/\\[([^\\]]+)\\]\\((?:[^()]|\\([^()]*\\))*\\)/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\[[^\\]]*\\]/g, '$1')\n // 自动链接 <https://…> 去尖括号,剩余 HTML 标签直接去掉。\n .replace(/<((?:https?|mailto):[^>\\s]+)>/g, '$1')\n .replace(/<\\/?[A-Za-z][^>]*>/g, '')\n // 行内代码保留内容(常是文件名、命令名),只去反引号。\n .replace(/(`+)([^`]+?)\\1/g, '$2')\n .replace(/(\\*\\*|__|~~)(?=\\S)([\\s\\S]*?\\S)\\1/g, '$2')\n // 单个 * / _ 强调要求两侧是非单词字符,避免误伤 snake_case 与乘号。\n .replace(/(^|[^\\w*])\\*(?=\\S)([^*\\n]*?\\S)\\*(?![\\w*])/g, '$1$2')\n .replace(/(^|[^\\w])_(?=\\S)([^_\\n]*?\\S)_(?!\\w)/g, '$1$2')\n}\n\n/** 正文里残留的 %E6%8F%90 这类编码串解码成原字符;解不开(非法 UTF-8)就保持原样。 */\nfunction decodePercentRuns(text: string): string {\n return text.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {\n try {\n return decodeURIComponent(run)\n } catch {\n return run\n }\n })\n}\n","import type { SessionSummary } from './protocol.ts'\nimport type { DeviceScope } from './device-auth.ts'\nimport type { PushNotification } from './protocol.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\nimport type { CommandAttachment, CommandCatalog, CommandExecution, PermissionCatalog } from './command-api.ts'\n\nexport interface CommandsApiLike {\n list(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<CommandCatalog>>\n execute(req: RpcRequestLike<{ sessionId: string; line: string; attachments: CommandAttachment[]; expectedDefinitionId?: string }>): Promise<RpcResponseLike<CommandExecution>>\n permissions(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<PermissionCatalog>>\n feedback(req: RpcRequestLike<{ sessionId: string; text: string; category?: string }>): Promise<RpcResponseLike<void>>\n export(req: RpcRequestLike<{ sessionId: string }>, signal: AbortSignal): Promise<Response>\n}\n\n/* Stable bridge-facing structural types mapped to rc.2 controllers by\n * DshApiProxy. */\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface RpcRequestLike<T> { rpcId?: string; payload?: T }\ninterface RpcResponseLike<T> { result?: RpcResult<T> }\n\ninterface SessionsApiLike {\n list(req?: RpcRequestLike<{ cursor?: string }>): Promise<RpcResponseLike<{ items: PhoneSessionRow[] }>>\n history(req: RpcRequestLike<{ sessionId: string; beforeSeq?: number; maxMessages?: number }>):\n Promise<RpcResponseLike<HistoryResult>>\n prompt(req: RpcRequestLike<PromptArgs>): Promise<RpcResponseLike<{ accepted: true }>>\n create(req: RpcRequestLike<{ workspaceId?: string; cwd?: string; agentPreset?: string }>): Promise<RpcResponseLike<{ sessionId: string; agentPreset?: string }>>\n fork?(req: RpcRequestLike<{ sessionId: string; atSeq?: number }>): Promise<RpcResponseLike<{ sessionId: string }>>\n models?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<HostSessionModels>>\n skills?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<{ skills: import('./protocol.ts').SkillSummary[] }>>\n selectModel?(req: RpcRequestLike<{\n sessionId: string\n provider: string\n model: string\n reasoningEffort?: string\n }>): Promise<RpcResponseLike<{ selected: HostModelSelection }>>\n rename?(req: RpcRequestLike<{ sessionId: string; title: string }>):\n Promise<RpcResponseLike<{ title: string; seq: number }>>\n cancel?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<Record<string, unknown> | undefined>>\n /** Reads one durable image back after the host verifies the session log references its id. */\n attachment?(req: RpcRequestLike<{ sessionId: string; attachmentId: string }>):\n Promise<RpcResponseLike<{ attachment: { mediaType?: string }; data: string }>>\n /** Complete projection baseline for one session; null when it is gone. */\n projections(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ asOfSeq: number; values: Record<string, unknown> } | null>>\n}\n\ninterface WorkspaceApiLike {\n list?(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }>>\n create?(req: RpcRequestLike<{ path: string }>):\n Promise<RpcResponseLike<{ workspace: WorkspaceViewLike; created: boolean }>>\n archiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n unarchiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n}\n\nexport type ScheduleKind = 'after' | 'at' | 'every' | 'daily' | 'weekly' | 'cron'\n\nexport interface ScheduleTaskViewLike {\n id: string\n kind: ScheduleKind\n title: string\n prompt: string\n scheduledAt: string\n state: 'scheduled' | 'overdue'\n deliveryMode: 'host'\n afterSeconds?: number\n everySeconds?: number\n time?: string\n timeZone?: string\n weekdays?: number[]\n expression?: string\n}\n\nexport interface ScheduleHistoryViewLike {\n id: string\n records: Array<{\n scheduledAt: string\n deliveredAt: string\n messageId: string\n prompt?: string\n }>\n earlierRecordsUnavailable: boolean\n earlierRecordsPruned?: boolean\n retention: { days: number; records: number }\n nextBefore?: string\n}\n\nexport interface ScheduleApiLike {\n list(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ sessionId: string; tasks: ScheduleTaskViewLike[] }>>\n history(req: RpcRequestLike<{ sessionId: string; id: string; limit: number; before?: string }>):\n Promise<RpcResponseLike<{ sessionId: string; history: ScheduleHistoryViewLike }>>\n create(req: RpcRequestLike<{ sessionId: string; title: string; prompt: string; [key: string]: unknown }>):\n Promise<RpcResponseLike<ScheduleTaskViewLike>>\n update(req: RpcRequestLike<{ sessionId: string; id: string; expected: unknown; title?: string; prompt?: string; change?: unknown }>):\n Promise<RpcResponseLike<{ id: string; updated: boolean; record?: ScheduleTaskViewLike; code?: string }>>\n delete(req: RpcRequestLike<{ sessionId: string; id: string }>):\n Promise<RpcResponseLike<{ id: string; deleted: boolean; code?: string }>>\n}\n\ninterface HostApiLike {\n listDirectory?(req: RpcRequestLike<{ path?: string }>, signal?: AbortSignal):\n Promise<RpcResponseLike<DirectoryListingLike>>\n pickDirectory?(req: RpcRequestLike<Record<string, never>>, signal?: AbortSignal):\n Promise<RpcResponseLike<{ path: string | null }>>\n}\n\nexport interface WorkspaceViewLike {\n workspaceId: string\n path: string\n title: string\n sessionIds: string[]\n createdAt?: string\n updatedAt?: string\n}\n\nexport interface DirectoryEntryLike {\n name: string\n path: string\n hidden: boolean\n}\n\nexport interface DirectoryListingLike {\n path: string\n home: string\n crumbs: DirectoryEntryLike[]\n entries: DirectoryEntryLike[]\n truncated: boolean\n}\n\nexport interface HostModelSelection {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\nexport interface HostSessionModels {\n current: HostModelSelection\n routable: boolean\n groups: Array<{\n id: string\n name: string\n models: Array<{\n id: string\n name: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n }>\n }>\n failures: Array<{ id: string; name: string; message: string }>\n}\n\n/**\n * Bridge 方法的结果。失败直接携带 wire 错误码(error vocabulary 见\n * wire-errors.ts):调用方(wire-registry 的行)原样透给手机,不再经过\n * kind 中间层翻译。\n */\nexport type ModelBridgeResult<T> =\n | { ok: true; value: T }\n | { ok: false; code: WireErrorCode; message: string }\n\nexport type SessionManagementResult<T> =\n | { ok: true; value: T }\n | { ok: false; code: WireErrorCode; message: string }\n\nexport interface PromptArgs {\n sessionId: string\n mode: 'queue' | 'steer'\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }\n >\n clientTimeZone?: string\n}\n\nexport interface PhoneSessionRow {\n sessionId: string\n updatedAt: number\n running: boolean\n /** Host marks never-prompted sessions as blank; informational only — the\n * summary projects them as idle (see toSummary) so phones can send. */\n blank?: boolean\n cwd?: string\n origin?: string\n parentSessionId?: string\n projections?: { asOfSeq?: number; values?: Record<string, unknown> }\n}\n\n/**\n * Subagent sessions are host-internal workers of a parent conversation.\n * They must never surface on the phone: not in the project/session list,\n * and not as turn-completion pushes for a session the device cannot open.\n *\n * `parentSessionId` alone is NOT a subagent signal: a session forked from\n * another one (c2s.session.fork) also carries it, as fork lineage, while\n * being an ordinary top-level session the phone must show. Only the host's\n * explicit `origin` marks a worker — mirror the same rule already used for\n * the live mux stream (see dsh-api-proxy.ts's `isSubagent` derivation).\n */\nexport function isSubagentRow(row: PhoneSessionRow): boolean {\n return row.origin === 'subagent';\n}\n\nexport interface HistoryResult {\n events: Array<{ event: SessionEventLike; view?: unknown }>\n hasMore: boolean\n projections?: { values?: Record<string, unknown> }\n}\n\nexport interface SessionEventLike {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface MuxFrameLike {\n /** Internal Host identity hint; never serialized into the phone protocol. */\n isSubagent?: boolean\n type: string\n rpcId?: string\n payload?: any\n sessionId?: string\n event?: SessionEventLike\n key?: string\n value?: unknown\n approvalId?: string\n callId?: string\n toolName?: string\n reason?: string\n questions?: unknown\n questionRpcId?: string\n running?: boolean\n}\n\n/**\n * apiProxy stream items are server-request envelopes: the frame lives in\n * `payload`, while the stable request id used to answer approval/question\n * waits lives beside it. Flattening only `payload` loses that id and makes\n * both interactions silently disappear.\n */\nexport interface ApiStreamItemLike {\n rpcId?: string\n payload: MuxFrameLike\n}\n\nexport function unwrapStreamItem(item: MuxFrameLike | ApiStreamItemLike): MuxFrameLike {\n const nested = item.payload\n if (nested && typeof nested === 'object' && typeof nested.type === 'string') {\n // The envelope id identifies the server request that must be answered.\n // Preserve it even if a future nested payload happens to carry another id.\n return item.rpcId ? { ...nested, rpcId: item.rpcId } : nested\n }\n return item as MuxFrameLike\n}\n\nexport interface ApiProxyLike {\n commands?: CommandsApiLike\n sessions: SessionsApiLike\n workspace?: WorkspaceApiLike\n schedule?: ScheduleApiLike\n host?: HostApiLike\n respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }):\n Promise<{ accepted: boolean; reason?: string }>\n events: {\n mux(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n host(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n }\n}\n\n/**\n * Outcome of answering a pending approval/question. The host distinguishes\n * \"never/no longer pending\" from \"payload rejected\", and collapsing both into\n * a boolean made every rejection read as `question not pending` on the phone.\n */\nexport type PendingResponseOutcome =\n | { ok: true }\n | { ok: false; reason: 'not-pending' | 'bad-response' | 'transport' }\n\n/** Downward sink every connected phone registers (one per WebSocket). */\nexport interface BridgeSink {\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n /**\n * S→C permission gate (R1/P2): whether this sink may receive broadcast or\n * replayed frames that require `scope`. The bridge consults this before\n * every push/replay delivery; unknown broadcast types are denied.\n */\n canReceive(scope: DeviceScope): boolean\n}\n\n/**\n * Offline push fan-out (F-9 离线推送). The bridge forwards every\n * notification-worthy event here; the outlet decides which paired devices\n * (token holders without a live socket) receive an APNs delivery.\n */\nexport interface PushOutlet {\n fanOut(notification: PushNotification): void\n widgetChanged?(): void\n liveActivityChanged?(sessions: SessionSummary[]): void\n /**\n * Whether offline push is currently configured and usable. Drives the\n * welcome capability bit: advertising push while no APNs credentials are\n * loaded would make clients suppress their own local banners and lose\n * notifications entirely.\n */\n isAvailable(): boolean\n}\n","/**\n * 宿主能力探测(host capability probe)的唯一属主。\n *\n * `welcome.capabilities` 的每一位都必须由这里的一张表推导;此前同一条事实\n * 存在两份——getter 里的表达式与各 Bridge 方法内的 `typeof` 再探测——\n * 已经漂移过两次:`models` 位要求 `models && selectModel`,而\n * `c2s.session.models` 只需要 `models`(门过严);`schedules` 位只探\n * `list && create`,而五个 schedule 方法只要求服务存在(部分 Host 上会 TypeError)。\n *\n * 规则:一位一行,表达式取「该位所闸的帧真正需要的最小依赖」。位与帧的绑定\n * 由 wire 注册表的 `capability` 字段声明(见 wire-registry.ts),host-bridge\n * 只负责把表算出来,不再各自判断。\n */\n\nimport type { ApiProxyLike, PushOutlet } from './host-api.ts'\nimport type { WelcomeCapabilities } from './protocol.ts'\n\n/** 由宿主表达式挣得的位;表在 HOST_CAPABILITY_PROBES。 */\nexport type ProbedCapability =\n | 'models'\n | 'skills'\n | 'commands'\n | 'sessionManagement'\n | 'sessionFork'\n | 'sessionRestore'\n | 'projectSelection'\n | 'schedules'\n\n/** 每位一行:Host 上必须存在的依赖。缺失即该位为 false,相关帧回 E_UNSUPPORTED。 */\nexport const HOST_CAPABILITY_PROBES: Record<ProbedCapability, (proxy: ApiProxyLike) => boolean> = {\n // PROTOCOL.md:模型列表必须由 Host 的 session.models 动态提供。切换模型另由\n // c2s.session.selectModel 自己的探测兜底(缺 selectModel 时回 E_UNSUPPORTED)。\n models: (proxy) => typeof proxy.sessions.models === 'function',\n skills: (proxy) => typeof proxy.sessions.skills === 'function',\n commands: (proxy) => typeof proxy.commands?.list === 'function' && typeof proxy.commands?.execute === 'function',\n sessionManagement: (proxy) =>\n typeof proxy.sessions.rename === 'function' &&\n typeof proxy.workspace?.archiveSession === 'function',\n sessionFork: (proxy) => typeof proxy.sessions.fork === 'function',\n sessionRestore: (proxy) => typeof proxy.workspace?.unarchiveSession === 'function',\n projectSelection: (proxy) =>\n typeof proxy.workspace?.list === 'function' &&\n typeof proxy.workspace?.create === 'function',\n // 五个 schedule 帧都只要求 Schedule 服务存在;历史上按 list&&create 探测的取值\n // 会在只实现了部分方法的 Host 上把位压成 false,而方法体内又只判服务存在。\n schedules: (proxy) => proxy.schedule !== undefined,\n}\n\n/** 由 Bridge 自身恒真的位(见 BRIDGE_INVARIANT_CAPABILITIES)。 */\nexport type BridgeInvariantCapability = (typeof BRIDGE_INVARIANT_CAPABILITIES)[number]\n\n/** 由推送出口就绪状态决定的位(APNs 凭据或中继注册)。 */\nexport type OutletCapability = 'push'\n\n/**\n * 门禁必须留在 handler 内的位。`push` 是唯一一个:零配置注册(enrollKey)\n * 必须先于能力门执行,否则首次注册恰好发生在推送尚未就绪时就会被拒,\n * 分布式构建的自动启用链就此断掉(见 wire-push.ts 的同名说明)。\n */\nexport const HANDLER_GATED_CAPABILITIES = ['push'] as const satisfies ReadonlyArray<OutletCapability>\n\n/** 行上的能力门可以引用三类位中的任意一种。 */\nexport type CapabilityGate = ProbedCapability | BridgeInvariantCapability | OutletCapability\n\n/**\n * Bridge 自身恒为真的位:与宿主无关,列出是为了让读者一眼看出 welcome 上哪些位\n * 不是从 Host 推导的。缺失这些位中的任何一个都意味着 Bridge 实现被改动,\n * 改这里不改帧的 gate 属于遗漏。\n */\nexport const BRIDGE_INVARIANT_CAPABILITIES = [\n 'historyPaging',\n 'replay',\n 'approvals',\n 'questions',\n 'pendingSnapshot',\n 'promptDelivery',\n 'notifyAllCategories',\n 'widgetPush',\n 'liveActivityPush',\n 'deviceRevoke',\n] as const satisfies ReadonlyArray<keyof WelcomeCapabilities>\n\n/**\n * 按当前 apiProxy 与推送出口算出 welcome 能力位。`push` 位描述「当前离线推送\n * 可用」:APNs 凭据或中继注册就绪才为 true,否则客户端不应压下自己的本地通知。\n */\nexport function capabilityBits(\n proxy: ApiProxyLike,\n pushOutlet: PushOutlet | undefined,\n): WelcomeCapabilities {\n return {\n historyPaging: true,\n replay: true,\n approvals: true,\n questions: true,\n pendingSnapshot: true,\n promptDelivery: true,\n notifyAllCategories: true,\n models: HOST_CAPABILITY_PROBES.models(proxy),\n skills: HOST_CAPABILITY_PROBES.skills(proxy),\n commands: HOST_CAPABILITY_PROBES.commands(proxy),\n sessionManagement: HOST_CAPABILITY_PROBES.sessionManagement(proxy),\n sessionFork: HOST_CAPABILITY_PROBES.sessionFork(proxy),\n sessionRestore: HOST_CAPABILITY_PROBES.sessionRestore(proxy),\n projectSelection: HOST_CAPABILITY_PROBES.projectSelection(proxy),\n schedules: HOST_CAPABILITY_PROBES.schedules(proxy),\n push: pushOutlet?.isAvailable() === true,\n widgetPush: true,\n liveActivityPush: true,\n deviceRevoke: true,\n }\n}\n","import {\n openDispatchJournal,\n promptDeliveryCodec,\n scheduleMutationCodec,\n type DeliveryReceipt,\n type MutationDispatchResult,\n} from './dispatch-journal.ts'\nimport { randomUUID } from 'node:crypto'\nimport { notificationPlainText } from './notification-text.ts'\nimport type { Envelope, MessageProjection, NotifyCategory, PendingSnapshotPayload, PushNotification, SessionEventKind, SessionSummary, SessionTodoItem, SessionTodoStatus, SessionUsageStats, WelcomeCapabilities } from './protocol.ts'\nimport {\n isSubagentRow,\n unwrapStreamItem,\n} from './host-api.ts'\nimport type {\n ApiProxyLike,\n BridgeSink,\n DirectoryListingLike,\n HostModelSelection,\n HostSessionModels,\n PendingResponseOutcome,\n PhoneSessionRow,\n PromptArgs,\n PushOutlet,\n ScheduleHistoryViewLike,\n ScheduleTaskViewLike,\n SessionEventLike,\n SessionManagementResult,\n ModelBridgeResult,\n MuxFrameLike,\n WorkspaceViewLike,\n} from './host-api.ts'\nexport * from './host-api.ts'\nimport {\n MAX_MESSAGE_PROJECTION_BYTES,\n MAX_SESSION_PAGE_MESSAGES_BYTES,\n DEFAULT_HISTORY_PAGE_MESSAGES,\n MAX_HISTORY_PAGE_MESSAGES,\n MIN_TAIL_MESSAGES,\n canonicalSessionMessages,\n limitMessageProjection,\n limitSessionPageMessages,\n messageText,\n projectEvent,\n projectHistory,\n} from './host-event-projection.ts'\nimport { documentPromptBlock, type PromptDocument } from './document-payload.ts'\nimport type { DispatchJournal } from './dispatch-journal.ts'\nimport { pushScopeFor } from './connection-policy.ts'\nimport { capabilityBits } from './host-capabilities.ts'\nimport { hostFailureOf, wireCodeFor, wireErrorOf, type WireErrorCode } from './wire-errors.ts'\nimport { formatLogLine } from './diagnostics.ts'\n\nexport {\n MAX_MESSAGE_PROJECTION_BYTES,\n MAX_SESSION_PAGE_MESSAGES_BYTES,\n canonicalSessionMessages,\n limitMessageProjection,\n limitSessionPageMessages,\n projectEvent,\n projectHistory,\n} from './host-event-projection.ts'\n\ninterface PendingApproval {\n rpcId: string\n sessionId: string\n toolName: string\n reason: string\n toolArguments?: string\n}\n\ninterface PendingQuestion {\n rpcId: string\n sessionId: string\n questions: unknown\n}\n\n/**\n * 失败结果直接携带 wire 错误码(error vocabulary 见 wire-errors.ts)。\n *\n * 迁移前这里有 kind 中间层('not-found' | 'busy' | 'conflict' | 'invalid'…),\n * 由 5 个映射函数在「Host code ↔ kind ↔ E_*」之间来回翻译,其中\n * E_BUSY→conflict→E_BUSY 的往返已证明有损。现在每个域查一次表,调用方拿到的\n * 就是手机上会看到的错误码。\n */\nexport type ScheduleBridgeResult<T> =\n | { ok: true; value: T; replayed?: boolean }\n | { ok: false; code: WireErrorCode; message: string; replayed?: boolean }\n\nconst MAX_RING_DEFAULT = 2000;\n\n/**\n * Process-wide bridge state: session mirror, pending approvals/questions,\n * and the per-device replay ring. Consumes the in-process mux/host streams\n * and fans projected pushes out to every registered sink.\n */\nlet BRIDGE_SEQ = 0;\n\nexport class HostBridge {\n readonly id = ++BRIDGE_SEQ;\n private summaries = new Map<string, SessionSummary>()\n private activeTools = new Map<string, Map<string, string>>()\n private approvals = new Map<string, PendingApproval>()\n private questions = new Map<string, PendingQuestion>()\n private archivedSessionIds = new Set<string>()\n /** Mirrors archived rows from the last sessions.list so the phone can browse\n * and restore them. Excluded from `summaries` and from the live broadcast. */\n private archivedSummaries = new Map<string, SessionSummary>()\n private subagentSessionIds = new Set<string>()\n private sinks = new Set<BridgeSink>()\n private ring: Array<{ seq: number; type: string; payload: unknown }> = []\n private cursor = 0;\n private userReceiptSeq = 0;\n private abort = new AbortController();\n private started = false\n private disposed = false\n\n readonly promptDeliveries: DispatchJournal<{ sessionId: string; content: unknown }, DeliveryReceipt>\n readonly scheduleMutations: DispatchJournal<unknown, MutationDispatchResult<unknown>>\n readonly forkMutations: DispatchJournal<unknown, MutationDispatchResult<unknown>>\n\n constructor(\n private readonly apiProxy: ApiProxyLike,\n private readonly historyBufferMax: number = MAX_RING_DEFAULT,\n deliveryJournalPath?: string,\n scheduleJournalPath?: string,\n forkJournalPath?: string,\n /** 三个 journal 的落盘失败诊断输出;缺省即静默。不打任何消息体。 */\n private readonly log?: (message: string) => void,\n ) {\n // 三个实例是同一个 module 的三次配置:条目方言由 codec 决定(prompt 回执 /\n // schedule 结果 / fork 结果带持久化 value),落盘路径各自独立、互不影响。\n // log 必须在**首次**构造时就带上:openDispatchJournal 是按 path+codec 身份\n // 缓存的单例,事后补传不会生效。\n this.promptDeliveries = openDispatchJournal({\n path: deliveryJournalPath,\n codec: promptDeliveryCodec,\n joinInFlight: true,\n maxFileBytes: 8 * 1024 * 1024,\n log: this.log,\n })\n this.scheduleMutations = openDispatchJournal({\n path: scheduleJournalPath,\n codec: scheduleMutationCodec(false),\n joinInFlight: false,\n maxFileBytes: 4 * 1024 * 1024,\n log: this.log,\n })\n this.forkMutations = openDispatchJournal({\n path: forkJournalPath,\n codec: scheduleMutationCodec(true),\n joinInFlight: false,\n maxFileBytes: 4 * 1024 * 1024,\n log: this.log,\n })\n }\n\n private pushOutlet: PushOutlet | undefined;\n private widgetFingerprint = ''\n\n /**\n * Wire the offline-push fan-out. Present ⇒ welcome advertises the `push`\n * capability and notify-worthy events are mirrored to APNs.\n */\n setPushOutlet(outlet: PushOutlet | undefined): void {\n this.pushOutlet = outlet;\n }\n\n /**\n * welcome 能力位。委托 host-capabilities.ts 的探测表:此前同一条事实在这里\n * 和各方法体内各存一份,已经漂移过两次(models 位过严、schedules 位与\n * schedule 方法的探测不同源)。\n */\n get capabilities(): WelcomeCapabilities {\n return capabilityBits(this.apiProxy, this.pushOutlet)\n }\n\n diagnostic(message: string): void {\n console.log(formatLogLine('info', message));\n }\n\n currentCursor(): number {\n return this.cursor;\n }\n\n // ---------- sinks ----------\n\n addSink(sink: BridgeSink): void {\n this.sinks.add(sink);\n }\n\n removeSink(sink: BridgeSink): void {\n this.sinks.delete(sink);\n }\n\n /** Whether the ring still holds everything after the cursor. */\n canResumeFrom(cursor: number): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n // A cursor ahead of this process belongs to an older bridge lifetime.\n // Treat it as a gap instead of incorrectly claiming a successful resume.\n return cursor <= this.cursor && cursor + 1 >= oldest;\n }\n\n private sinkSessions = new Map<BridgeSink, Set<string>>();\n private lastAssistantText = new Map<string, string>();\n\n /** Mark a sink as actively viewing a session (suppresses its turn notifications). */\n markSinkOpen(sink: BridgeSink, sessionId: string): void {\n let set = this.sinkSessions.get(sink);\n if (!set) { set = new Set(); this.sinkSessions.set(sink, set); }\n set.add(sessionId);\n }\n\n markSinkClosed(sink: BridgeSink, sessionId: string): void {\n this.sinkSessions.get(sink)?.delete(sessionId);\n }\n\n dropSinkSessions(sink: BridgeSink): void {\n this.sinkSessions.delete(sink);\n }\n\n private isViewedBy(sink: BridgeSink, sessionId: string): boolean {\n return this.sinkSessions.get(sink)?.has(sessionId) ?? false;\n }\n\n /** F-9: when a notification-worthy event fires, mirror it to every\n * online device that is not currently viewing the session (the s2c.notify\n * frame counts toward the seq cursor and joins the replay ring per\n * PROTOCOL §6 + §7), then fan the same payload out to offline devices\n * holding an APNs token. */\n private emitNotify(args: {\n sessionId: string\n category: NotifyCategory\n title: string\n /** Host 原文(可能是 Markdown),出站前在这里统一转纯文本。 */\n body: string\n /** body 清洗后为空(如回复只有代码块)时改用的纯文本,例如会话标题。 */\n fallbackBody?: string\n notificationId: string\n toolName?: string\n detail?: string\n }): void {\n const turnEnd = args.category === 'turn.completed' || args.category === 'session.error';\n if (turnEnd && this.subagentSessionIds.has(args.sessionId)) return;\n // 回合结束取回复最后一段(通常是结论);审批原因与提问原文合成一行。\n const plain = notificationPlainText(args.body, turnEnd ? 'last' : 'all') || (args.fallbackBody ?? '');\n const body = plain.length > 120 ? plain.slice(0, 119) + '…' : plain;\n // 展开页所需的工具名与详情只随审批通知下发;正文 120 字的上限不动,详情另有 600 字上限。\n const approvalExtras = args.category === 'approval.required' && args.toolName\n ? { toolName: args.toolName.slice(0, 80), ...(args.detail ? { detail: args.detail.slice(0, 600) } : {}) }\n : {};\n this.record(\n 's2c.notify',\n {\n notificationId: args.notificationId,\n category: args.category,\n sessionId: args.sessionId,\n title: args.title,\n body,\n ...approvalExtras,\n ts: Date.now(),\n },\n (sink) => this.isViewedBy(sink, args.sessionId),\n );\n this.fanOutPush({\n notificationId: args.notificationId,\n category: args.category,\n sessionId: args.sessionId,\n title: args.title,\n body,\n ...approvalExtras,\n });\n }\n\n /** F-9: when a turn completes, notify every device not viewing the session. */\n private emitTurnCompletedNotify(sessionId: string, ok: boolean): void {\n if (this.subagentSessionIds.has(sessionId)) return;\n\n const row = this.summaries.get(sessionId);\n const title = ok ? '任务完成' : '任务异常结束';\n this.emitNotify({\n sessionId,\n category: ok ? 'turn.completed' : 'session.error',\n title,\n body: this.lastAssistantText.get(sessionId) ?? '',\n fallbackBody: row?.title ?? '',\n notificationId: 'n-' + (this.cursor + 1),\n });\n }\n\n /**\n * Mirror one notification-worthy event to offline devices. Fire-and-forget:\n * push failures must never block or break the WS data plane.\n */\n private fanOutPush(notification: PushNotification): void {\n try {\n this.pushOutlet?.fanOut(notification);\n } catch {\n // outlet misbehavior must not take down the bridge\n }\n }\n\n\n /**\n * Remember the latest assistant text so notifications can quote it. 存完整原文:\n * 按字符数截尾会从链接地址中间切开(横幅只剩 URL 编码的路径),段落选择与\n * Markdown 清洗统一留到 emitNotify。条目随 sessions.list / 归档清理。\n */\n private captureAssistantText(sessionId: string, event: SessionEventLike): void {\n if (event.type !== 'assistant/message') return;\n const text = messageText(event.data).trim();\n if (text.length > 0) this.lastAssistantText.set(sessionId, text);\n }\n\n /**\n * Replay buffered pushes after the given cursor; false when the gap is\n * unrecoverable. Frames go to `target` only — replaying into every sink\n * duplicated the whole window onto devices that never asked for it.\n * Each frame is filtered by the S→C permission policy per sink, so a\n * reader without interactions.respond never gets the missed approval\n * frames back (R1/P2).\n */\n resumeFrom(cursor: number, target?: BridgeSink): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n if (cursor + 1 < oldest) return false;\n const receivers = target !== undefined ? [target] : [...this.sinks];\n for (const entry of this.ring) {\n if (entry.seq > cursor) {\n for (const sink of receivers) {\n const need = pushScopeFor(entry.type, entry.payload)\n if (need === undefined || !sink.canReceive(need)) continue\n sink.replay([entry]);\n }\n }\n }\n for (const sink of receivers) sink.replayDone();\n return true;\n }\n\n refreshLiveActivities(): void {\n try { this.pushOutlet?.liveActivityChanged?.(this.listSessions()) } catch { /* independent delivery */ }\n }\n\n private record(type: string, payload: unknown, except?: (sink: BridgeSink) => boolean): void {\n if (this.disposed) return\n if (type === 's2c.sessions.delta') this.refreshLiveActivities()\n if (type === 's2c.sessions.delta' || type.startsWith('s2c.pending.')) {\n const fingerprint = JSON.stringify([\n this.listSessions().map(({ id, title, status, lastActivityTs, todos, todoItems, pendingApproval, pendingQuestion }) =>\n ({ id, title, status, lastActivityTs, todos, todoItems, pendingApproval, pendingQuestion })),\n this.approvals.size, this.questions.size,\n ])\n if (fingerprint !== this.widgetFingerprint) {\n this.widgetFingerprint = fingerprint\n try { this.pushOutlet?.widgetChanged?.() } catch { /* push cannot break the data plane */ }\n }\n }\n this.cursor += 1;\n const entry = { seq: this.cursor, type, payload };\n this.ring.push(entry);\n if (this.ring.length > this.historyBufferMax) {\n this.ring.splice(0, this.ring.length - this.historyBufferMax);\n }\n const need = pushScopeFor(type, payload)\n for (const sink of this.sinks) {\n if (need === undefined || !sink.canReceive(need)) continue\n if (except && except(sink)) continue\n sink.push(type, payload, entry.seq);\n }\n }\n\n\n // ---------- lifecycle ----------\n\n /** Start consuming host + mux streams. Idempotent; aborts on dispose(). */\n start(): void {\n if (this.started || this.disposed) return\n this.started = true\n void this.runHostStream();\n void this.runMuxStream();\n void this.refreshSummaries();\n }\n\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.abort.abort();\n this.sinks.clear();\n this.sinkSessions.clear();\n this.pushOutlet = undefined;\n }\n\n private async runHostStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.host({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onHostFrame(frame);\n }\n } catch {\n // stream ended (dispose or host shutdown); bridges degrade to RPC-only\n }\n }\n\n private async runMuxStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.mux({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onMuxFrame(frame);\n }\n } catch {\n // ditto\n }\n }\n\n private onHostFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'host/session-added':\n case 'host/session-removed':\n case 'host/workspace-changed':\n case 'host/workspace-removed':\n case 'host/workspace-order-changed':\n void this.refreshSummaries();\n break;\n case 'host/archived-sessions-changed': {\n const archived = (frame as { archivedSessionIds?: unknown }).archivedSessionIds\n if (Array.isArray(archived)) {\n this.archivedSessionIds = new Set(archived.map(String))\n }\n void this.refreshSummaries();\n break;\n }\n case 'host/schedule-changed':\n this.record('s2c.schedule.changed', {})\n break\n case 'host/session-status': {\n const p = frame as { sessionId?: string; running?: boolean };\n const row = this.summaries.get(String(p.sessionId));\n if (row && typeof p?.running === 'boolean') {\n row.status = p.running ? 'running' : 'idle';\n this.pushSummary(row);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private onMuxFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'skills/changed':\n this.record('s2c.skills.changed', {})\n break;\n case 'commands/changed':\n this.record('s2c.commands.changed', {})\n break;\n case 'session/event': {\n const event = frame.event as SessionEventLike | undefined;\n const sessionId = String(frame.sessionId ?? '');\n if (!event || !sessionId) break;\n if (frame.isSubagent === true) this.subagentSessionIds.add(sessionId);\n if (this.subagentSessionIds.has(sessionId)) break;\n if (event.type === 'agent-preset/selected') {\n this.record('s2c.skills.changed', { sessionId });\n }\n this.noteActivity(sessionId, event);\n this.captureAssistantText(sessionId, event);\n const projection = projectEvent(sessionId, event);\n this.captureActivity(sessionId, event, projection?.data.tool as { name?: string; summary?: string } | undefined);\n if (projection) {\n this.record('s2c.session.event', { sessionId, kind: projection.kind, seq: event.seq, data: projection.data });\n }\n if (projection?.kind === 'turn.end') {\n this.emitTurnCompletedNotify(sessionId, projection.data.ok === true);\n }\n break;\n }\n case 'session/projection': {\n const p = frame as { sessionId?: string; key?: string; value?: unknown };\n if (!p.sessionId) break;\n if (p.key === 'workingDirectory' || p.key === 'agentPreset') {\n this.record('s2c.skills.changed', { sessionId: p.sessionId });\n }\n if (p.key === 'modelSelection') {\n const next = (p.value as { next?: { provider?: unknown; model?: unknown; reasoningEffort?: unknown } } | undefined)?.next\n const current = next && typeof next.provider === 'string' && typeof next.model === 'string'\n ? { provider: next.provider, model: next.model, ...(typeof next.reasoningEffort === 'string' ? { reasoningEffort: next.reasoningEffort } : {}) }\n : undefined\n // 当前选择来自实时投影;目录缓存不能让其他客户端的模型切换长期滞留。\n this.record('s2c.models.changed', { sessionId: p.sessionId, ...(current ? { current } : {}) });\n }\n this.applyProjection(p.sessionId, String(p.key ?? ''), p.value);\n break;\n }\n case 'approval/requested': {\n const p = frame as { sessionId?: string; approvalId?: string; toolName?: string; reason?: string; callId?: string };\n if (!p.approvalId || !frame.rpcId) break;\n const toolName = String(p.toolName ?? 'tool');\n const summary = String(p.reason ?? '');\n const sessionId = String(p.sessionId ?? '');\n this.approvals.set(p.approvalId, {\n rpcId: frame.rpcId,\n sessionId,\n toolName,\n reason: summary,\n });\n this.record('s2c.pending.approval', {\n requestId: p.approvalId,\n sessionId,\n toolName,\n summary,\n riskLevel: riskOf(toolName),\n });\n if (p.callId) void this.loadApprovalArguments(p.approvalId, p.callId);\n this.emitNotify({\n sessionId,\n category: 'approval.required',\n title: '需要批准',\n body: toolName + ': ' + summary,\n notificationId: 'apr-' + p.approvalId,\n toolName,\n detail: summary,\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'approval/resolved': {\n const p = frame as { approvalId?: string };\n if (!p.approvalId) break;\n const pending = this.approvals.get(p.approvalId);\n this.approvals.delete(p.approvalId);\n this.record('s2c.pending.cleared', { requestId: p.approvalId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n case 'question/requested': {\n const p = frame as { sessionId?: string; questions?: unknown };\n if (!frame.rpcId) break;\n const requestId = 'q-' + frame.rpcId;\n const sessionId = String(p?.sessionId ?? '');\n this.questions.set(requestId, { rpcId: frame.rpcId, sessionId, questions: p?.questions });\n this.record('s2c.pending.question', { requestId, sessionId, questions: p?.questions ?? [] });\n this.emitNotify({\n sessionId,\n category: 'question.asked',\n title: '有问题需要回答',\n body: firstQuestionText(p?.questions),\n notificationId: requestId,\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'question/resolved': {\n const p = frame as { questionRpcId?: string };\n if (!p.questionRpcId) break;\n const requestId = 'q-' + p.questionRpcId;\n const pending = this.questions.get(requestId);\n this.questions.delete(requestId);\n this.record('s2c.pending.cleared', { requestId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n default:\n break;\n }\n }\n\n\n // ---------- session mirror ----------\n\n async refreshSummaries(): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.list({ rpcId: randomUUID(), payload: {} });\n if (!response.result || !response.result.ok) {\n this.diagnostic('sessions.list rejected: ' + JSON.stringify(response.result ?? null).slice(0, 200));\n return;\n }\n let workspaces: WorkspaceViewLike[] = []\n const workspaceList = this.apiProxy.workspace?.list\n if (typeof workspaceList === 'function') {\n const workspaceResponse = await workspaceList.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: {},\n })\n if (workspaceResponse.result?.ok) {\n workspaces = workspaceResponse.result.value.items ?? []\n this.archivedSessionIds = new Set(\n (workspaceResponse.result.value.archivedSessionIds ?? []).map(String),\n )\n }\n }\n const previousIds = new Set(this.summaries.keys())\n const workspaceBySession = new Map<string, WorkspaceViewLike>()\n for (const workspace of workspaces) {\n for (const sessionId of workspace.sessionIds ?? []) {\n workspaceBySession.set(String(sessionId), workspace)\n }\n }\n const next = new Map<string, SessionSummary>();\n // Archived sessions stay out of the live list and its broadcast so\n // existing clients keep their current behaviour; they are mirrored here\n // and served on demand through c2s.sessions.archived.\n const nextArchived = new Map<string, SessionSummary>();\n for (const row of response.result.value.items ?? []) {\n if (isSubagentRow(row)) this.subagentSessionIds.add(row.sessionId);\n if (this.subagentSessionIds.has(row.sessionId)) continue\n const summary = toSummary(row, this.approvals, this.questions, workspaceBySession.get(row.sessionId));\n if (this.archivedSessionIds.has(row.sessionId)) {\n nextArchived.set(row.sessionId, { ...summary, archived: true });\n continue\n }\n next.set(row.sessionId, summary);\n }\n for (const [id, calls] of this.activeTools) {\n const row = next.get(id);\n if (!row || (row.status !== 'running' && !row.pendingApproval && !row.pendingQuestion)) this.activeTools.delete(id);\n else row.activity = [...calls.values()].at(-1) ?? null;\n }\n // Worker identity is immutable for this bridge lifetime. A stale list or\n // a removed worker must not erase identity learned from the event stream.\n this.summaries = next;\n this.archivedSummaries = nextArchived;\n const removedIds = [...previousIds].filter((id) => !next.has(id))\n // sessions.list is authoritative. This also removes assistant snippets\n // captured for child/subagent event streams that never enter summaries.\n for (const id of this.lastAssistantText.keys()) {\n if (!next.has(id)) this.lastAssistantText.delete(id)\n }\n this.record('s2c.sessions.delta', { upserted: [...next.values()], removedIds });\n } catch {\n // apiProxy absent or transient failure; keep last mirror\n }\n }\n\n /** Cold sessions may lack a title projection; fall back to first user text. */\n private deriveTitleFallback(sessionId: string, messages: MessageProjection[]): void {\n const row = this.summaries.get(sessionId);\n if (!row || row.title.length > 0) return;\n const firstUser = messages.find(m => m.role === 'user' && (m.text ?? '').trim().length > 0);\n if (!firstUser) return;\n row.title = firstUser!.text!.replace(/\\s+/g, ' ').trim().slice(0, 60);\n this.pushSummary(row);\n }\n\n private captureActivity(sessionId: string, event: SessionEventLike, tool?: { name?: string; summary?: string }): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n const data = event.data as { callId?: string } | undefined;\n if (event.type === 'tool/call' && data?.callId) {\n const calls = this.activeTools.get(sessionId) ?? new Map<string, string>();\n const detail = [tool?.name, tool?.summary].filter(Boolean).join(': ');\n calls.set(data.callId, Array.from(detail).slice(0, 160).join(''));\n if (calls.size > 64) calls.delete(calls.keys().next().value!);\n this.activeTools.set(sessionId, calls);\n } else if (event.type === 'tool/result' && data?.callId) {\n this.activeTools.get(sessionId)?.delete(data.callId);\n } else if (event.type === 'turn/start' || event.type === 'turn/end') {\n this.activeTools.delete(sessionId);\n } else return;\n const activity = [...(this.activeTools.get(sessionId)?.values() ?? [])].at(-1) ?? null;\n if (row.activity === activity) return;\n row.activity = activity;\n // Tool progress must not reorder the session list or trigger Widget pushes.\n this.pushSummary(row);\n }\n\n private noteActivity(sessionId: string, event?: SessionEventLike): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n // Recency must reflect USER-VISIBLE milestones only: a new prompt moves\n // a session to the top and a finished turn settles it into place. Token\n // chunks / tool calls / per-step assistant messages fire constantly while\n // an agent works; counting them made concurrently running sessions fight\n // over the top slot in every recency-sorted list.\n switch (event?.type) {\n case 'user/message':\n case 'turn/start':\n case 'turn/end':\n break;\n default:\n return;\n }\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n\n private applyProjection(sessionId: string, key: string, value: unknown): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n if (key === 'title') {\n row.title = typeof value === 'string' ? value : '';\n } else if (key === 'todos') {\n const items = Array.isArray(value) ? value as Array<{ content?: unknown; status?: unknown }> : null;\n const sanitized = sanitizeTodoItems(items);\n row.todoItems = sanitized.length > 0 ? sanitized : null;\n row.todos = sanitized.length > 0\n ? { done: sanitized.filter(i => i.status === 'completed').length, total: sanitized.length }\n : null;\n } else if (key === 'sessionListMetadata') {\n const meta = value as { lastPromptAt?: number } | null;\n if (meta?.lastPromptAt) row.lastActivityTs = Math.max(row.lastActivityTs, meta.lastPromptAt);\n } else if (key === 'sessionStats' || key === 'tokenUsage') {\n // Host usage projections arrive as two independent keys over the mux\n // stream; merge each patch into the summary's stats snapshot so a\n // sessionStats-only or tokenUsage-only host still shows partial data.\n const patch = sanitizeUsageStatsPatch(key, value);\n if (!patch) return;\n row.stats = { ...emptyUsageStats, ...(row.stats ?? {}), ...patch };\n } else {\n return;\n }\n this.pushSummary(row);\n }\n\n private bumpPendingFlags(sessionId: string): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n let approval = false;\n for (const pending of this.approvals.values()) {\n if (pending.sessionId === sessionId) approval = true;\n }\n let question = false;\n for (const pending of this.questions.values()) {\n if (pending.sessionId === sessionId) question = true;\n }\n row.pendingApproval = approval;\n row.pendingQuestion = question;\n this.pushSummary(row);\n }\n\n private pushSummary(row: SessionSummary): void {\n this.record('s2c.sessions.delta', { upserted: [row], removedIds: [] });\n }\n\n listSessions(): SessionSummary[] {\n return [...this.summaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n /**\n * Archived rows from the last sessions.list, newest first. Served only on\n * demand so the live session list and its broadcast keep their shape for\n * clients that predate `c2s.sessions.archived`.\n */\n listArchivedSessions(): SessionSummary[] {\n return [...this.archivedSummaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n /**\n * Complete transient interaction state. Unlike the replay ring, this remains\n * authoritative after a long disconnect and is rehydrated by apiProxy's mux\n * stream when the bridge itself restarts.\n */\n pendingSnapshot(): PendingSnapshotPayload {\n return {\n approvals: [...this.approvals.entries()].map(([requestId, pending]) => ({\n requestId,\n sessionId: pending.sessionId,\n toolName: pending.toolName,\n summary: pending.reason,\n ...(pending.toolArguments !== undefined ? { toolArguments: pending.toolArguments } : {}),\n riskLevel: riskOf(pending.toolName),\n })),\n questions: [...this.questions.entries()].map(([requestId, pending]) => ({\n requestId,\n sessionId: pending.sessionId,\n questions: Array.isArray(pending.questions)\n ? pending.questions as PendingSnapshotPayload['questions'][number]['questions']\n : [],\n })),\n };\n }\n\n /** Resolve only the exact invocation in this session; never guess from the latest tool. */\n private async loadApprovalArguments(requestId: string, callId: string): Promise<void> {\n const pending = this.approvals.get(requestId);\n if (!pending) return;\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId: pending.sessionId, maxMessages: 200 },\n });\n if (this.disposed || this.approvals.get(requestId) !== pending || !response.result?.ok) return;\n const event = (response.result.value.events ?? []).map(row => row.event).find(event => {\n const data = event.data as { callId?: string; name?: string } | undefined;\n return event.type === 'tool/call' && data?.callId === callId && data?.name === pending.toolName;\n });\n const args = (event?.data as { arguments?: unknown } | undefined)?.arguments;\n if (typeof args !== 'string' || !args.trim()) return;\n pending.toolArguments = args;\n this.record('s2c.pending.approval', {\n requestId, sessionId: pending.sessionId, toolName: pending.toolName,\n summary: pending.reason, riskLevel: riskOf(pending.toolName), toolArguments: args,\n });\n } catch {\n // Keep the approval answerable with explicitly unavailable execution details.\n }\n }\n\n // ---------- data operations ----------\n\n /** Tail history for an opened session; pushes s2c.session.tail to the sink. */\n async openSession(sink: BridgeSink, sessionId: string, tailCount: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, maxMessages: clampTail(tailCount) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const projected = projectHistory(result.events ?? []);\n const page = limitSessionPageMessages(projected);\n const messages = page.messages;\n const oldestSeq = messages.length > 0 ? messages[0]!.seq : 0;\n sink.push('s2c.session.tail', {\n sessionId,\n messages,\n oldestSeq,\n hasMore: Boolean(result.hasMore) || page.dropped > 0,\n });\n this.deriveTitleFallback(sessionId, messages);\n // DSH exposes a complete per-session projection baseline; fold it\n // into the summary row when the phone opens a session so the first paint\n // does not depend on list-row hints or frames missed while offline.\n void this.refreshProjections(sessionId);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Pull one session's projection baseline and fold every key\n * through applyProjection — unknown keys are ignored there, `null` means the\n * session no longer exists, and any failure degrades to a diagnostic: opening\n * a session must never fail because a baseline could not be read.\n */\n private async refreshProjections(sessionId: string): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.projections({\n rpcId: randomUUID(),\n payload: { sessionId },\n });\n if (this.disposed) return;\n const result = response.result;\n if (!result) return;\n if (!result.ok) {\n this.diagnostic('sessions.projections failed: ' + result.error.code);\n return;\n }\n const baseline = result.value;\n if (baseline === null || baseline === undefined) return;\n for (const [key, value] of Object.entries(baseline.values ?? {})) {\n this.applyProjection(sessionId, key, value);\n }\n } catch (error) {\n this.diagnostic('sessions.projections threw: ' + String((error as { message?: unknown })?.message ?? error));\n }\n }\n\n async historyPage(\n sessionId: string,\n beforeSeq: number,\n limit: number,\n ): Promise<{ sessionId: string; messages: MessageProjection[]; hasMore: boolean } | null> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, beforeSeq, maxMessages: clampTail(limit) },\n });\n if (!response.result || !response.result.ok) return null;\n const result = response.result.value;\n // Some host versions include the boundary event even though beforeSeq\n // is exclusive. Enforce the phone protocol here so a client can never\n // request and append the same page indefinitely.\n const projected = projectHistory(result.events ?? [])\n .filter((message) => message.seq < beforeSeq);\n const page = limitSessionPageMessages(projected);\n return {\n sessionId,\n messages: page.messages,\n // An empty exclusive page cannot advance beforeSeq. Advertising more\n // here would make the client request the same boundary forever.\n hasMore: page.messages.length > 0 && (Boolean(result.hasMore) || page.dropped > 0),\n };\n } catch {\n return null;\n }\n }\n\n /** Result of one attachment read-back for the phone. */\n async attachmentData(\n sessionId: string,\n attachmentId: string,\n ): Promise<{ mediaType?: string; data: string } | null> {\n const read = this.apiProxy.sessions.attachment\n if (typeof read !== 'function') return null;\n try {\n const response = await read.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, attachmentId },\n });\n if (!response.result || !response.result.ok) return null;\n const data = response.result.value.data;\n if (typeof data !== 'string' || data.length === 0) return null;\n return {\n ...(typeof response.result.value.attachment?.mediaType === 'string'\n ? { mediaType: response.result.value.attachment.mediaType as string }\n : {}),\n data,\n };\n } catch {\n return null;\n }\n }\n\n async commandOperation(operation: 'list' | 'execute' | 'permissions' | 'feedback', payload: Record<string, unknown>): Promise<SessionManagementResult<unknown>> {\n const api = this.apiProxy.commands\n if (!api) return { ok: false, code: 'E_UNSUPPORTED', message: '当前主机不支持手机指令' }\n try {\n const invoke = api[operation] as (request: { rpcId: string; payload: Record<string, unknown> }) => Promise<{ result?: { ok: boolean; value?: unknown; error?: { code: string; message?: string } } }>\n const response = await invoke.call(api, { rpcId: randomUUID(), payload })\n if (response.result?.ok) return { ok: true, value: response.result.value ?? {} }\n // 指令域的失败一律过域表:原先这里无条件回落 E_INTERNAL,于是「指令不存在」\n // 与「主机内部错误」在手机上是同一个码,客户端没法据此分支。\n return wireErrorOf('command', response.result?.error ?? { code: 'command/invalid-response' })\n } catch (error) {\n return wireErrorOf('command', hostFailureOf(error, 'command/invalid-response'))\n }\n }\n\n async exportSession(sessionId: string, signal: AbortSignal): Promise<Response> {\n const api = this.apiProxy.commands\n if (!api) throw Object.assign(new Error('当前主机不支持日志导出'), { code: 'command/export-unsupported' })\n return api.export({ rpcId: randomUUID(), payload: { sessionId } }, signal)\n }\n\n /** 由会话目录决定技能可见性;不读取正文,也不把本机路径发给移动端。 */\n async sessionSkills(sessionId: string): Promise<SessionManagementResult<{ skills: import('./protocol.ts').SkillSummary[] }>> {\n const read = this.apiProxy.sessions.skills\n if (!read) return { ok: false, code: 'E_UNSUPPORTED', message: '当前主机不支持技能目录' }\n try {\n const response = await read.call(this.apiProxy.sessions, { rpcId: randomUUID(), payload: { sessionId } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: '技能目录响应无效' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n const seen = new Set<string>()\n const skills = response.result.value.skills.map(skill => {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(skill.name) || seen.has(skill.name) ||\n typeof skill.description !== 'string' || typeof skill.modelInvocable !== 'boolean') {\n throw new Error('技能目录条目无效')\n }\n seen.add(skill.name)\n return { name: skill.name, description: skill.description, modelInvocable: skill.modelInvocable }\n })\n return { ok: true, value: { skills } }\n } catch {\n return { ok: false, code: 'E_INTERNAL', message: '无法读取当前会话的技能目录' }\n }\n }\n\n async sessionModels(sessionId: string): Promise<ModelBridgeResult<HostSessionModels>> {\n const models = this.apiProxy.sessions.models\n if (typeof models !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'model catalog unavailable on this host version' }\n }\n try {\n const response = await models.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'model catalog returned no result' }\n if (!response.result.ok) return wireErrorOf('model', response.result.error)\n return { ok: true, value: projectSessionModels(response.result.value) }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async selectSessionModel(\n sessionId: string,\n selection: HostModelSelection,\n ): Promise<ModelBridgeResult<HostModelSelection>> {\n const selectModel = this.apiProxy.sessions.selectModel\n if (typeof selectModel !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'model selection unavailable on this host version' }\n }\n try {\n const response = await selectModel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: {\n sessionId,\n provider: selection.provider,\n model: selection.model,\n ...(selection.reasoningEffort ? { reasoningEffort: selection.reasoningEffort } : {}),\n },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'model selection returned no result' }\n if (!response.result.ok) return wireErrorOf('model', response.result.error)\n return { ok: true, value: { ...response.result.value.selected } }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async renameSession(sessionId: string, title: string): Promise<SessionManagementResult<string>> {\n const rename = this.apiProxy.sessions.rename\n if (typeof rename !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session rename unavailable on this host version' }\n }\n try {\n const response = await rename.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, title },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session rename returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n const acceptedTitle = String(response.result.value.title)\n const row = this.summaries.get(sessionId)\n if (row) {\n row.title = acceptedTitle\n this.pushSummary(row)\n }\n return { ok: true, value: acceptedTitle }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async archiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const archive = this.apiProxy.workspace?.archiveSession\n if (typeof archive !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session archive unavailable on this host version' }\n }\n try {\n const response = await archive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session archive returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n this.summaries.delete(sessionId)\n this.lastAssistantText.delete(sessionId)\n this.record('s2c.sessions.delta', { upserted: [], removedIds: [sessionId] })\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async unarchiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const unarchive = this.apiProxy.workspace?.unarchiveSession\n if (typeof unarchive !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session restore unavailable on this host version' }\n }\n try {\n const response = await unarchive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session restore returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n // Rebuild both mirrors from the Host instead of hand-moving the row:\n // refreshSummaries re-emits s2c.sessions.delta, which carries the restored\n // session back to the client's live list.\n await this.refreshSummaries()\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async cancelSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const cancel = this.apiProxy.sessions.cancel\n if (typeof cancel !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session cancel unavailable on this host version' }\n }\n try {\n const response = await cancel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session cancel returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async listWorkspaces(): Promise<SessionManagementResult<Array<{\n id: string; title: string; path: string; sessionIds: string[]\n }>>> {\n const list = this.apiProxy.workspace?.list\n if (typeof list !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'workspace list unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.workspace, { rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'workspace list returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: (response.result.value.items ?? []).map(projectWorkspace) }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async createWorkspace(path: string): Promise<SessionManagementResult<{\n workspace: { id: string; title: string; path: string; sessionIds: string[] }; created: boolean\n }>> {\n const create = this.apiProxy.workspace?.create\n if (typeof create !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'workspace create unavailable on this host version' }\n }\n try {\n const response = await create.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { path },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'workspace create returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n await this.refreshSummaries()\n return {\n ok: true,\n value: { workspace: projectWorkspace(response.result.value.workspace), created: response.result.value.created === true },\n }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async listDirectory(path?: string): Promise<SessionManagementResult<DirectoryListingLike>> {\n const list = this.apiProxy.host?.listDirectory\n if (typeof list !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'directory browsing unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: path && path.trim().length > 0 ? { path } : {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'directory list returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async pickDirectory(): Promise<SessionManagementResult<string | null>> {\n const pick = this.apiProxy.host?.pickDirectory\n if (typeof pick !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'native directory picker unavailable on this host version' }\n }\n try {\n const response = await pick.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'directory picker returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: response.result.value.path }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n /** Create a fresh blank session in an existing workspace or legacy cwd. */\n async createSession(destination: { workspaceId?: string; cwd?: string } = {}): Promise<string | null> {\n try {\n const response = await this.apiProxy.sessions.create({\n rpcId: randomUUID(),\n payload: {\n ...(destination.workspaceId?.trim() ? { workspaceId: destination.workspaceId.trim() } : {}),\n ...(destination.cwd?.trim() ? { cwd: destination.cwd.trim() } : {}),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const sessionId = response.result.value.sessionId;\n await this.refreshSummaries();\n return sessionId;\n } catch {\n return null;\n }\n }\n\n async forkSession(\n deviceId: string,\n payload: { clientRequestId: string; sessionId: string; atSeq?: number },\n ): Promise<ScheduleBridgeResult<{ sessionId: string }>> {\n const fork = this.apiProxy.sessions.fork\n if (typeof fork !== 'function') return { ok: false, code: 'E_UNSUPPORTED', message: 'session fork unavailable on this host version' }\n if (!this.summaries.has(payload.sessionId)) return { ok: false, code: 'E_NOT_FOUND', message: 'session not found' }\n if (this.archivedSessionIds.has(payload.sessionId) || this.subagentSessionIds.has(payload.sessionId)) {\n return { ok: false, code: 'E_PROTOCOL', message: 'archived or subagent sessions cannot be forked' }\n }\n const result = await this.forkMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { sessionId: payload.sessionId, ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq } : {}) },\n async () => {\n const response = await fork.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId: payload.sessionId, ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq } : {}) },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session fork returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: { sessionId: response.result.value.sessionId } }\n },\n )\n if (result.ok) {\n await this.refreshSummaries()\n return { ok: true, value: result.value as { sessionId: string }, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async listSchedules(sessionId: string): Promise<ScheduleBridgeResult<ScheduleTaskViewLike[]>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n try {\n const response = await schedule.list({ rpcId: randomUUID(), payload: { sessionId } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule list returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value.tasks }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async scheduleHistory(\n sessionId: string,\n id: string,\n limit: number,\n before?: string,\n ): Promise<ScheduleBridgeResult<ScheduleHistoryViewLike>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n try {\n const response = await schedule.history({\n rpcId: randomUUID(),\n payload: { sessionId, id, limit, ...(before !== undefined ? { before } : {}) },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule history returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value.history }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async createSchedule(\n deviceId: string,\n payload: Record<string, unknown> & { sessionId: string; clientRequestId: string; title: string; prompt: string },\n ): Promise<ScheduleBridgeResult<ScheduleTaskViewLike>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n const { clientRequestId, sessionId, ...request } = payload\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n { sessionId, request },\n async () => {\n const response = await schedule.create({ rpcId: randomUUID(), payload: { sessionId, ...request } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule create returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as ScheduleTaskViewLike, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async updateSchedule(\n deviceId: string,\n payload: Record<string, unknown> & { sessionId: string; id: string; clientRequestId: string; expected: unknown },\n ): Promise<ScheduleBridgeResult<ScheduleTaskViewLike>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n const { clientRequestId, ...request } = payload\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n request,\n async () => {\n const response = await schedule.update({ rpcId: randomUUID(), payload: request })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule update returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.updated !== true || value.record === undefined) {\n return { ok: false, code: wireCodeFor('schedule', value.code), message: value.code ?? 'schedule update rejected' }\n }\n return { ok: true, value: value.record }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as ScheduleTaskViewLike, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async deleteSchedule(\n deviceId: string,\n payload: { sessionId: string; id: string; clientRequestId: string },\n ): Promise<ScheduleBridgeResult<{ id: string; deleted: true }>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { sessionId: payload.sessionId, id: payload.id },\n async () => {\n const response = await schedule.delete({\n rpcId: randomUUID(),\n payload: { sessionId: payload.sessionId, id: payload.id },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule delete returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n if (response.result.value.deleted !== true) {\n return { ok: false, code: 'E_NOT_FOUND', message: response.result.value.code ?? 'schedule not found' }\n }\n return { ok: true, value: { id: response.result.value.id, deleted: true as const } }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as { id: string; deleted: true }, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async sendPrompt(\n sessionId: string,\n text: string,\n images: Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }> = [],\n documents: PromptDocument[] = [],\n ): Promise<SessionManagementResult<number>> {\n try {\n const content: PromptArgs['content'] = [];\n if (text.trim().length > 0) content.push({ type: 'text', text });\n for (const image of images) content.push({ type: 'image', ...image });\n for (const document of documents) content.push({ type: 'text', text: documentPromptBlock(document) });\n const response = await this.apiProxy.sessions.prompt({\n rpcId: randomUUID(),\n payload: {\n sessionId,\n mode: 'queue',\n content,\n clientTimeZone: localTimeZone(),\n },\n });\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'prompt returned no result' };\n // Preserve the host's error kind so the phone can tell \"retry later\"\n // (E_BUSY) from \"session is gone\" (E_NOT_FOUND) instead of mapping\n // every failure onto E_BUSY.\n if (!response.result.ok) return wireErrorOf('session', response.result.error);\n const row = this.summaries.get(sessionId);\n if (row) {\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n this.userReceiptSeq += 1;\n return { ok: true, value: this.userReceiptSeq };\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) };\n }\n }\n\n\n async respondApproval(\n requestId: string,\n decision: 'allow' | 'deny',\n reason?: string,\n ): Promise<PendingResponseOutcome> {\n const pending = this.approvals.get(requestId);\n if (!pending) return { ok: false, reason: 'not-pending' };\n // Claim the pending entry before responding: a second phone (or a double\n // tap) must not fire another client-response for the same rpcId while the\n // resolved frame is still in flight.\n this.approvals.delete(requestId);\n const outcome = decision === 'allow' ? 'allowed-once' : 'rejected';\n const denialReason = typeof reason === 'string' ? reason.trim().slice(0, 500) : '';\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: {\n ok: true,\n value: {\n sessionId: pending.sessionId,\n approvalId: requestId,\n outcome,\n // PROTOCOL v2: a deny may carry the user's explanation so the\n // model can adjust course instead of blindly retrying.\n ...(denialReason.length > 0 ? { reason: denialReason } : {}),\n },\n },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted) {\n const failure = receiptFailureReason(receipt);\n // Restore only what the user can retry: `not-pending` means the host\n // already settled the request, and restoring would strand a ghost\n // entry that no future resolved frame will ever clear.\n if (failure !== 'not-pending' && !this.approvals.has(requestId)) {\n this.approvals.set(requestId, pending);\n }\n return { ok: false, reason: failure };\n }\n // The claim above removed the entry, so the upcoming approval/resolved\n // frame finds nothing left to bump — refresh the summary flags here or\n // the session badge stays \"pending\" until an unrelated refresh.\n this.bumpPendingFlags(pending.sessionId);\n return { ok: true };\n } catch {\n // Transport-level failure: give the request back so the user can retry.\n if (!this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return { ok: false, reason: 'transport' };\n }\n }\n\n async respondQuestion(requestId: string, answers: unknown): Promise<PendingResponseOutcome> {\n const pending = this.questions.get(requestId);\n if (!pending) return { ok: false, reason: 'not-pending' };\n this.questions.delete(requestId);\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, answer: { answers: normalizeAnswerItems(answers, pending.questions) } } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted) {\n const failure = receiptFailureReason(receipt);\n // Same ghost-entry rule as approvals: `not-pending` is final, never\n // restore — the host has no record left to resolve against.\n if (failure !== 'not-pending' && !this.questions.has(requestId)) {\n this.questions.set(requestId, pending);\n }\n return { ok: false, reason: failure };\n }\n // Mirror the approval path: the claimed entry cannot bump flags when the\n // resolved frame arrives, so recompute them right after acceptance.\n this.bumpPendingFlags(pending.sessionId);\n return { ok: true };\n } catch {\n if (!this.questions.has(requestId)) this.questions.set(requestId, pending);\n return { ok: false, reason: 'transport' };\n }\n }\n}\n\n/**\n * The host validates question answers strictly (core dsh-user-questions via\n * apiProxy): a present-but-empty `custom` fails `matchesQuestions`, and a\n * single-select question rejects `custom` combined with a selection. Clients\n * may send lenient shapes (the phone historically always attached\n * `\"custom\": \"\"`, which made EVERY option-only answer fail), so normalize to\n * exactly what the host accepts before forwarding.\n */\nexport function normalizeAnswerItems(\n raw: unknown,\n questions?: unknown,\n): Array<{ id: string; selected: string[]; custom?: string }> {\n if (!Array.isArray(raw)) return [];\n const askedById = new Map<string, { multiSelect?: unknown }>();\n if (Array.isArray(questions)) {\n for (const q of questions) {\n if (typeof q === 'object' && q !== null && typeof (q as { id?: unknown }).id === 'string') {\n askedById.set((q as { id: string }).id, q as { multiSelect?: unknown });\n }\n }\n }\n const items: Array<{ id: string; selected: string[]; custom?: string }> = [];\n for (const entry of raw) {\n if (typeof entry !== 'object' || entry === null) continue;\n const r = entry as { id?: unknown; selected?: unknown; custom?: unknown };\n if (typeof r.id !== 'string') continue;\n // De-duplicate while preserving order; duplicate labels are rejected by the host.\n const selected = [...new Set(\n Array.isArray(r.selected) ? r.selected.filter((s): s is string => typeof s === 'string') : [],\n )];\n const customText = typeof r.custom === 'string' ? r.custom : '';\n let custom: string | undefined;\n if (customText.trim().length > 0) custom = customText;\n // A single-select answer must be either options or free text, never both.\n if (custom !== undefined && selected.length > 0 && askedById.get(r.id)?.multiSelect !== true) {\n custom = undefined;\n }\n items.push({ id: r.id, selected, ...(custom !== undefined ? { custom } : {}) });\n }\n return items;\n}\n\n/** Map an apiProxy respond receipt onto the failure vocabulary. */\nfunction receiptFailureReason(receipt: { accepted: boolean; reason?: string } | undefined):\n 'not-pending' | 'bad-response' {\n return receipt?.reason === 'not-pending' ? 'not-pending' : 'bad-response';\n}\n\n// ---------- projection helpers ----------\n\n/**\n * 尾部条数收敛到 [MIN_TAIL_MESSAGES, MAX_HISTORY_PAGE_MESSAGES]。\n * 区间与 wire 行校验(tailCount 1..10000、limit 1..500)共用投影 module 的\n * 常量——「一页能要多少条消息」本是一条规则,原先散在三处。\n */\nfunction clampTail(n: number): number {\n if (!Number.isFinite(n)) return DEFAULT_HISTORY_PAGE_MESSAGES;\n return Math.max(MIN_TAIL_MESSAGES, Math.min(MAX_HISTORY_PAGE_MESSAGES, Math.floor(n)));\n}\n\nfunction localTimeZone(): string | undefined {\n try {\n return new Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction riskOf(toolName: string): 'read' | 'write' | 'destructive' {\n if (/bash|pwsh|terminal/.test(toolName)) return 'write';\n if (/edit|write|str_replace|create/.test(toolName)) return 'write';\n if (/delete|remove|kill/.test(toolName)) return 'destructive';\n return 'read';\n}\n\n/** First question's text for the push banner; the questions payload shape is\n * host-version dependent, so extract defensively. */\nexport function firstQuestionText(questions: unknown): string {\n if (!Array.isArray(questions) || questions.length === 0) return 'Agent 等待你的输入';\n const first = questions[0] as { question?: unknown } | undefined;\n const text = String(first?.question ?? '').trim();\n return text || 'Agent 等待你的输入';\n}\n\nconst TODO_STATUSES = new Set(['pending', 'in_progress', 'completed']);\n\n/** Validate a host todo projection once; progress counts and the full\n * checklist both derive from this sanitized list so they never disagree. */\nfunction sanitizeTodoItems(items: Array<{ content?: unknown; status?: unknown }> | null): SessionTodoItem[] {\n if (!items) return [];\n return items\n .map(i => ({ content: String(i.content ?? '').trim(), status: String(i.status ?? '') }))\n .filter(i => i.content.length > 0 && TODO_STATUSES.has(i.status))\n .slice(0, 100)\n .map(i => ({ content: i.content, status: i.status as SessionTodoStatus }));\n}\n\n/** Zero-value stats snapshot used as the merge base for partial patches. */\nconst emptyUsageStats: SessionUsageStats = {\n turns: 0,\n steps: 0,\n llmMs: 0,\n toolMs: 0,\n ttftMs: 0,\n ttftSteps: 0,\n decodeMs: 0,\n decodeTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n};\n\n/** Coerce one host counter to a non-negative integer; junk becomes 0. */\nfunction usageCounter(value: unknown): number {\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n) || n < 0) return 0;\n return Math.floor(n);\n}\n\nconst SESSION_STATS_COUNTERS = [\n 'turns', 'steps', 'llmMs', 'toolMs',\n 'ttftMs', 'ttftSteps', 'decodeMs', 'decodeTokens',\n] as const;\n\nconst TOKEN_USAGE_COUNTERS = [\n 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens',\n] as const;\n\n/** Sanitize one `sessionStats` / `tokenUsage` projection value into the wire\n * stats fields; undefined when the payload carries nothing readable. Host\n * field names differ between the two projections (tokenUsage reports\n * `uncachedInputTokens`, the wire mirrors it as `inputTokens`). */\nfunction sanitizeUsageStatsPatch(key: string, value: unknown): Partial<SessionUsageStats> | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const raw = value as Record<string, unknown>;\n const patch: Partial<SessionUsageStats> = {};\n if (key === 'sessionStats') {\n for (const field of SESSION_STATS_COUNTERS) {\n if (field in raw) patch[field] = usageCounter(raw[field]);\n }\n } else {\n if ('uncachedInputTokens' in raw) patch.inputTokens = usageCounter(raw.uncachedInputTokens);\n for (const field of TOKEN_USAGE_COUNTERS) {\n if (field in raw) patch[field] = usageCounter(raw[field]);\n }\n }\n return Object.keys(patch).length > 0 ? patch : undefined;\n}\n\n/** Combine one session's `sessionStats` + `tokenUsage` projection values into\n * a complete wire stats object; undefined when neither carries anything. */\nfunction usageStatsOf(sessionStats: unknown, tokenUsage: unknown): SessionUsageStats | undefined {\n const patches = [\n sanitizeUsageStatsPatch('sessionStats', sessionStats),\n sanitizeUsageStatsPatch('tokenUsage', tokenUsage),\n ].filter((patch): patch is Partial<SessionUsageStats> => patch !== undefined);\n if (patches.length === 0) return undefined;\n return Object.assign({}, emptyUsageStats, ...patches);\n}\n\nfunction toSummary(\n row: PhoneSessionRow,\n approvals: Map<string, PendingApproval>,\n questions: Map<string, PendingQuestion>,\n workspace?: WorkspaceViewLike,\n): SessionSummary {\n const values = row.projections?.values ?? {};\n const todos = Array.isArray(values.todos) ? values.todos as Array<{ content?: unknown; status?: unknown }> : null;\n let pendingApproval = false;\n for (const pending of approvals.values()) {\n if (pending.sessionId === row.sessionId) pendingApproval = true;\n }\n let pendingQuestion = false;\n for (const pending of questions.values()) {\n if (pending.sessionId === row.sessionId) pendingQuestion = true;\n }\n const cwd = typeof row.cwd === 'string' ? row.cwd : '';\n const label = workspace?.title ?? (cwd ? cwd.split('/').filter(Boolean).pop() : undefined);\n const todoItems = sanitizeTodoItems(todos);\n const stats = usageStatsOf(values.sessionStats, values.tokenUsage);\n return {\n id: row.sessionId,\n title: typeof values.title === 'string' ? values.title : '',\n // A blank session has never run a turn, and the running flag above\n // already covers the live case. Reporting `unknown` here made the phone\n // treat the composer as \"still syncing\" forever: nothing ever converges\n // the state back because the first prompt is exactly what the disabled\n // button cannot send. Idle is truthful for every non-running blank row.\n status: row.running ? 'running' : 'idle',\n lastActivityTs: Number(row.updatedAt ?? Date.now()),\n todos: todoItems.length > 0\n ? {\n done: todoItems.filter(i => i.status === 'completed').length,\n total: todoItems.length,\n }\n : null,\n todoItems: todoItems.length > 0 ? todoItems : null,\n pendingApproval,\n pendingQuestion,\n ...(stats ? { stats } : {}),\n workspaceLabel: label ?? null,\n workspaceId: workspace?.workspaceId ?? null,\n workspacePath: workspace?.path ?? (cwd || null),\n };\n}\n\nfunction projectWorkspace(workspace: WorkspaceViewLike): {\n id: string; title: string; path: string; sessionIds: string[]\n} {\n return {\n id: String(workspace.workspaceId),\n title: String(workspace.title),\n path: String(workspace.path),\n sessionIds: (workspace.sessionIds ?? []).map(String),\n }\n}\n\n/** 把 Host 的模型目录投影成 wire 形状:字段全部收敛为字符串,缺省不补位。 */\nfunction projectSessionModels(value: HostSessionModels): HostSessionModels {\n return {\n current: {\n provider: String(value.current.provider),\n model: String(value.current.model),\n ...(value.current.reasoningEffort ? { reasoningEffort: String(value.current.reasoningEffort) } : {}),\n },\n routable: value.routable === true,\n groups: (value.groups ?? []).map((group) => ({\n id: String(group.id),\n name: String(group.name),\n models: (group.models ?? []).map((model) => ({\n id: String(model.id),\n name: String(model.name),\n ...(model.description ? { description: String(model.description) } : {}),\n ...(model.reasoning ? {\n reasoning: {\n efforts: (model.reasoning.efforts ?? []).map((effort) => ({\n id: String(effort.id),\n name: String(effort.name),\n ...(effort.description ? { description: String(effort.description) } : {}),\n })),\n ...(model.reasoning.defaultEffort\n ? { defaultEffort: String(model.reasoning.defaultEffort) }\n : {}),\n },\n } : {}),\n })),\n })),\n failures: (value.failures ?? []).map((failure) => ({\n id: String(failure.id),\n name: String(failure.name),\n message: String(failure.message),\n })),\n }\n}\n\n/** Project a history page (raw events) into MessageProjection rows. */\n","/**\n * Resident DSH Remote Events client for phone interactions.\n *\n * The Host-side `approval/request` and `user-questions/request` events are\n * owned once by DSH API Remotes. API Gateway then fans each pending waterfall\n * out to every connected Client and settles the first result. DeepPilot joins\n * that official Client plane in-process instead of registering a competing\n * Host waterfall listener or replacing the official Web composer.\n */\nimport { Context } from '@deepseek-ai/cordis'\nimport * as Cordis from '@deepseek-ai/cordis'\n\n/**\n * One Peer admitted to the Host. DSH names it on every in-process carrier call\n * from 0.2.1-alpha.3 on; the Host's own operator Peer stands in before that.\n */\nexport interface HostPeerLike {\n readonly principal: { readonly kind: string }\n readonly signal: AbortSignal\n readonly interactions: 'answer' | 'none'\n}\n\ninterface FetchHandlerLike {\n /** `peer` is required from alpha.3 on and ignored by earlier hosts. */\n fetch(request: Request, peer: HostPeerLike | undefined): Promise<Response>\n}\n\ninterface HostConnectionLike {\n createSharedFetchHandler(channel: '/api'): FetchHandlerLike\n /**\n * alpha.3's only entry point that can name both the principal and the\n * forwarded-event participation. Absent before it, where the service instead\n * holds the operator Peer every call speaks for.\n */\n createPeer?(options: { principal: { kind: 'owner' }; interactions: 'answer' | 'none' }): HostPeerLike\n operator?: HostPeerLike\n}\n\ninterface HostWireStreamLike {\n open(\n endpoint: string,\n payload: unknown,\n uplink: undefined,\n peer: HostPeerLike | undefined,\n signal: AbortSignal,\n ): Promise<AsyncIterable<unknown>>\n}\n\ninterface HostGatewayLike {\n wireStream: HostWireStreamLike\n}\n\ninterface ClientTransportHooksLike {\n fetch(input: string, init: RequestInit): Promise<Response>\n openStream(endpoint: string, payload: unknown, signal: AbortSignal): AsyncIterable<unknown>\n ownsHost: true\n}\n\n/** The rc.2 Client sends relative RPC paths to its in-process carrier. */\nexport function inProcessRemoteRequest(input: string, init: RequestInit): Request {\n // The shared Fetch handler routes by pathname and never performs a network\n // request. Node's Request still requires an absolute URL for relative paths.\n return new Request(new URL(input, 'http://localhost/'), init)\n}\n\ninterface ClientRemoteLike {\n $on(event: string, listener: (this: Context, request: any, next: () => Promise<unknown>) => unknown): () => void\n}\n\ninterface ClientTypertLike {\n contexts: {\n registerClient(key: string, adapter: {\n identity(candidate: Context): string | undefined\n resolve(identity: string): Context\n }): () => void\n }\n}\n\n/** Carrier inputs the explicit Connection installer accepts on both audited hosts. */\ninterface ClientConnectionInstallOptionsLike {\n transport: ClientTransportHooksLike\n interactions: 'answer' | 'none'\n}\n\ninterface ClientPluginModule {\n apply(ctx: Context): void\n /**\n * Explicit installer, present on both audited hosts. Without it the carrier\n * can only arrive through the process global `apply()` reads, and the\n * forwarded-event participation cannot be named at all.\n */\n installConnection?(ctx: Context, options: ClientConnectionInstallOptionsLike): void\n}\n\n/**\n * Install the Connection service on the resident Client.\n *\n * alpha.3 selects `interactions: 'none'` for any caller that supplies no page\n * location, and that mode forbids the forwarded-event stream this resident\n * client exists to answer. Only the explicit installer can name the\n * participation, so it takes precedence; `apply` stays for a host older than\n * the installer, reading the carrier from the process global set around it.\n */\nexport function installResidentConnection(\n connection: ClientPluginModule,\n ctx: Context,\n transport: ClientTransportHooksLike,\n): void {\n if (typeof connection.installConnection === 'function') {\n connection.installConnection(ctx, { transport, interactions: 'answer' })\n return\n }\n connection.apply(ctx)\n}\n\nexport interface RemoteApprovalRequest {\n toolName?: string\n callId?: string\n reason?: string\n signal?: AbortSignal\n}\n\nexport interface RemoteQuestionRequest {\n questions?: unknown\n signal?: AbortSignal\n}\n\nexport interface DshRemoteInteractionHandlers {\n approval(sessionId: string, request: RemoteApprovalRequest, next: () => Promise<unknown>): unknown\n question(sessionId: string, request: RemoteQuestionRequest, next: () => Promise<unknown>): unknown\n}\n\ntype TransportGlobal = typeof globalThis & {\n __DSH_TRANSPORT__?: ClientTransportHooksLike\n}\n\ninterface ClientBundleDefinition {\n id: string\n factory(require: (id: string) => unknown): unknown\n}\n\ntype ClientBundleWindow = {\n __ModuleLoader__: {\n load(definition: ClientBundleDefinition): void\n }\n}\n\ntype LoaderGlobal = {\n window?: ClientBundleWindow\n}\n\ninterface ClientFaces {\n typert: ClientPluginModule\n connection: ClientPluginModule\n gateway: ClientPluginModule\n}\n\nlet clientFaces: Promise<ClientFaces> | undefined\n\n/**\n * Load DSH's browser-distributed Client faces into a tiny Host-side module\n * table. Published `lib/client.js` files register with `window.__ModuleLoader__`\n * rather than exporting ESM values, so a normal Node import cannot consume\n * them. The shim runs only while the three official bundles register; their\n * apply functions then operate against a non-browser Cordis Context.\n */\nfunction pluginModule(value: unknown, specifier: string): ClientPluginModule {\n if (typeof value !== 'object' || value === null) throw new Error(`${specifier} Client bundle returned no exports`)\n const record = value as Record<string, unknown>\n if (typeof record.apply !== 'function') throw new Error(`${specifier} has no Client apply() export`)\n return value as unknown as ClientPluginModule\n}\n\n/**\n * Register one loaded Client face when the browser banner did not.\n *\n * alpha.3 resolves these specifiers through the `node` export condition to a\n * bundle that exports the face as ordinary ESM and never calls\n * `window.__ModuleLoader__.load`, while earlier hosts only ship the banner\n * shape. A banner registration wins, because it is what the bundle's own\n * internal `require()` calls resolve against; the ESM namespace is the fallback.\n */\nexport function adoptClientModule(\n modules: Map<string, unknown>,\n specifier: string,\n loaded: unknown,\n): void {\n const id = specifier.replace(/\\/client$/, '')\n if (!modules.has(id)) modules.set(id, loaded)\n}\n\nasync function loadClientFaces(): Promise<ClientFaces> {\n clientFaces ??= (async () => {\n const modules = new Map<string, unknown>([['@deepseek-ai/cordis', Cordis]])\n const loaderGlobal = globalThis as unknown as LoaderGlobal\n const previousWindow = loaderGlobal.window\n loaderGlobal.window = {\n __ModuleLoader__: {\n load: definition => {\n const exports = definition.factory((id) => {\n if (!modules.has(id)) throw new Error(`resident DSH Client cannot resolve ${JSON.stringify(id)}`)\n return modules.get(id)\n })\n modules.set(definition.id, exports)\n },\n },\n }\n try {\n // Use variable specifiers to preserve DSH's Host/Client TypeScript face\n // split; static imports would merge incompatible Context declarations.\n for (const specifier of [\n '@deepseek-ai/dsh-typert-registry/client',\n '@deepseek-ai/dsh-client-connection/client',\n '@deepseek-ai/dsh-api-gateway/client',\n ]) {\n adoptClientModule(modules, specifier, await import(specifier))\n }\n } finally {\n if (previousWindow === undefined) delete loaderGlobal.window\n else loaderGlobal.window = previousWindow\n }\n return {\n typert: pluginModule(modules.get('@deepseek-ai/dsh-typert-registry'), '@deepseek-ai/dsh-typert-registry/client'),\n connection: pluginModule(modules.get('@deepseek-ai/dsh-client-connection'), '@deepseek-ai/dsh-client-connection/client'),\n gateway: pluginModule(modules.get('@deepseek-ai/dsh-api-gateway'), '@deepseek-ai/dsh-api-gateway/client'),\n }\n })()\n return clientFaces\n}\n\nconst agentScopeKey = Symbol('deeppilot.dsh-client.agent-scope')\n\ntype AgentContext = Context & { [agentScopeKey]?: string }\n\nfunction scopeOf(ctx: Context): string | undefined {\n return (ctx as AgentContext)[agentScopeKey]\n}\n\nfunction createAgentScope(ctx: Context, identity: string): Context {\n const fiber = ctx.plugin(function deeppilotAgentScope() {})\n return fiber.ctx.extend({\n [agentScopeKey]: identity,\n [Context.filter](listenerCtx: Context) {\n const listenerIdentity = scopeOf(listenerCtx)\n return listenerIdentity === undefined || listenerIdentity === identity\n },\n })\n}\n\n/**\n * Mint the Peer every in-process carrier call speaks for.\n *\n * alpha.3 requires a Peer on `fetch` and on `wireStream.open`, and refuses a\n * forwarded-event stream unless that Peer declares `interactions: 'answer'`.\n * Its connection service therefore grows `createPeer`, the only entry point\n * that can name the principal and the participation together. Before that the\n * service holds one operator Peer and defaults every absent Peer to it, so that\n * instance is the drop-in answer. A host with neither yields undefined, which\n * only the pre-alpha.3 carrier accepts.\n */\nfunction createHostPeer(connection: HostConnectionLike): HostPeerLike | undefined {\n if (typeof connection.createPeer === 'function') {\n return connection.createPeer({ principal: { kind: 'owner' }, interactions: 'answer' })\n }\n return connection.operator\n}\n\nfunction inProcessStream(\n gateway: HostGatewayLike,\n endpoint: string,\n payload: unknown,\n signal: AbortSignal,\n peer: HostPeerLike | undefined,\n): AsyncIterable<unknown> {\n return (async function* () {\n const source = await gateway.wireStream.open(endpoint, payload, undefined, peer, signal)\n yield* source\n })()\n}\n\n/**\n * Start one headless official DSH Client backed by the Host's in-process\n * Connection and Gateway carriers.\n *\n * The returned disposer tears down the Remote Events generation, all pending\n * listeners, and every lazily minted Agent scope.\n */\nexport async function startDshRemoteInteractions(\n hostCtx: Context,\n handlers: DshRemoteInteractionHandlers,\n): Promise<() => Promise<void>> {\n const connection = hostCtx.get('connection') as HostConnectionLike | undefined\n const gateway = hostCtx.get('typertGateway') as HostGatewayLike | undefined\n if (connection === undefined) throw new Error('DSH Host connection is unavailable')\n if (gateway === undefined) throw new Error('DSH typertGateway is unavailable')\n\n const faces = await loadClientFaces()\n\n const client = new Context()\n const peer = createHostPeer(connection)\n const fetchHandler = connection.createSharedFetchHandler('/api')\n const transport: ClientTransportHooksLike = {\n fetch: (input, init) => fetchHandler.fetch(inProcessRemoteRequest(input, init), peer),\n openStream: (endpoint, payload, signal) => inProcessStream(gateway, endpoint, payload, signal, peer),\n ownsHost: true,\n }\n\n try {\n faces.typert.apply(client)\n\n // Client Connection reads this hook once during apply() and retains the\n // concrete functions. Restore the process global immediately afterward so\n // no unrelated Client composition inherits DeepPilot's carrier.\n const transportGlobal = globalThis as TransportGlobal\n const previousTransport = transportGlobal.__DSH_TRANSPORT__\n transportGlobal.__DSH_TRANSPORT__ = transport\n try {\n installResidentConnection(faces.connection, client, transport)\n } finally {\n if (previousTransport === undefined) delete transportGlobal.__DSH_TRANSPORT__\n else transportGlobal.__DSH_TRANSPORT__ = previousTransport\n }\n\n const typert = client.get('typert') as ClientTypertLike | undefined\n if (typert === undefined) throw new Error('resident DSH Client typert service was not installed')\n const scopes = new Map<string, Context>()\n typert.contexts.registerClient('agent', {\n identity: candidate => scopeOf(candidate),\n resolve: identity => {\n let scope = scopes.get(identity)\n if (scope === undefined) {\n scope = createAgentScope(client, identity)\n scopes.set(identity, scope)\n }\n return scope\n },\n })\n\n // The Gateway opens its Remote Events generation asynchronously. Register\n // both listeners immediately after apply so a replayed pending request is\n // claimed before the first stream item can be dispatched.\n faces.gateway.apply(client)\n const remote = client.get('remote') as ClientRemoteLike | undefined\n if (remote === undefined) throw new Error('resident DSH Client remote service was not installed')\n remote.$on('approval/request', function (request, next) {\n return handlers.approval(scopeOf(this) ?? '', request as RemoteApprovalRequest, next)\n })\n remote.$on('user-questions/request', function (request, next) {\n return handlers.question(scopeOf(this) ?? '', request as RemoteQuestionRequest, next)\n })\n\n return async () => {\n await client.fiber.dispose()\n }\n } catch (error) {\n await client.fiber.dispose()\n throw error\n }\n}\n","import type { Context } from '@deepseek-ai/cordis'\n\nexport interface CommandSummary {\n name: string\n description: string\n definitionId?: string\n input?: { hint: string; attachments?: boolean }\n}\nexport interface CommandCatalog { commands: CommandSummary[]; permissions: boolean; export: boolean }\nexport interface CommandExecution { commandId: string; result: { kind: 'success' | 'error'; text?: string; sourceEventSeq?: number } }\nexport interface CommandAttachment { type: 'image' | 'document'; name: string; mediaType: string; data: string }\nexport interface PermissionOption { value: string; name: string; description?: string; confirmation: boolean }\nexport interface PermissionCatalog { options: PermissionOption[]; currentValue?: string }\nexport interface CommandApi {\n list(sessionId: string): Promise<CommandCatalog>\n execute(sessionId: string, line: string, attachments: CommandAttachment[], expectedDefinitionId?: string): Promise<CommandExecution>\n permissions(sessionId: string): Promise<PermissionCatalog>\n feedback(sessionId: string, text: string, category?: string): Promise<void>\n export(sessionId: string, signal: AbortSignal): Promise<Response>\n}\n\n/**\n * 给本域自造的失败带上 Host 风格的码。\n *\n * 这些失败不是上游返回的,而是插件在这里判定出来的,所以必须自己声明语义:\n * 没有码的抛出物在 wire-errors 的域表里查不到,一律落进 `E_INTERNAL`,手机端\n * 便无法区分「指令不存在」「定义已变化」「这个指令不收附件」——它们对用户的\n * 下一步动作要求完全不同。码的取值见 `src/wire-errors.ts` 的 `command` 域表。\n */\nfunction fail(code: string, message: string): Error & { code: string } {\n return Object.assign(new Error(message), { code })\n}\n\n/** 只经官方 Gateway 调用命令,保留其 Agent 解析、作用域覆盖和生命周期记录。 */\nexport class DshCommandApi implements CommandApi {\n private peer: unknown\n constructor(private readonly ctx: Context) {}\n get available(): boolean {\n return this.ctx.get('commands') !== undefined && typeof (this.ctx.get('typertGateway') as any)?.invoke === 'function'\n }\n private connection(): any { return this.ctx.get('connection') }\n private owner(): unknown {\n const connection = this.connection()\n // 独立的一元调用身份不参与审批竞争;旧版使用连接提供的 operator。\n return this.peer ??= connection?.createPeer?.({ principal: { kind: 'owner' }, interactions: 'none' }) ?? connection?.operator\n }\n private invoke(namespace: string, method: string, args: Record<string, unknown>): Promise<any> {\n const gateway = this.ctx.get('typertGateway') as any\n return gateway.invoke({ namespace, method, args, peer: this.owner(), signal: AbortSignal.timeout(10 * 60_000) })\n }\n async list(sessionId: string): Promise<CommandCatalog> {\n const rows = await this.invoke('commands', 'list', { agentId: sessionId }) as CommandSummary[]\n const names = new Set<string>()\n const commands = rows.map(row => {\n if (!/^[a-z][a-z0-9_-]{0,127}$/.test(row.name) || names.has(row.name) || typeof row.description !== 'string') throw fail('command/invalid-response', '命令目录无效')\n names.add(row.name)\n return { name: row.name, description: row.description,\n ...(typeof row.definitionId === 'string' ? { definitionId: row.definitionId } : {}),\n ...(row.input && typeof row.input.hint === 'string' ? { input: { hint: row.input.hint, attachments: row.input.attachments === true } } : {}) }\n })\n return { commands, permissions: this.ctx.get('permissionPresets') !== undefined,\n export: typeof this.connection()?.createSharedFetchHandler === 'function' }\n }\n async execute(sessionId: string, line: string, attachments: CommandAttachment[], expectedDefinitionId?: string): Promise<CommandExecution> {\n const catalog = await this.list(sessionId)\n const name = /^\\/([a-z][a-z0-9_-]*)(?=\\s|$)/.exec(line)?.[1]\n const command = catalog.commands.find(row => row.name === name)\n if (!command) throw fail('command/not-found', '当前会话没有这个指令')\n if (expectedDefinitionId !== undefined && command.definitionId !== expectedDefinitionId) throw fail('command/definition-changed', '指令定义已变化,请重新选择')\n if (attachments.length && command.input?.attachments !== true) throw fail('command/attachments-unsupported', '这个指令不接受附件')\n const submittedAttachments: unknown[] = []\n for (const item of attachments) {\n if (item.type === 'image') submittedAttachments.push({ type: 'image', mediaType: item.mediaType, data: item.data })\n else {\n // 移动端文档已提取为正文;按文本文件上传,不冒充原始 PDF 或二进制文件。\n const uploaded = await this.invoke('fileUploads', 'upload', { agentId: sessionId, request: { name: item.name + '.txt', data: item.data } })\n submittedAttachments.push({ type: 'file', receiptId: uploaded.receiptId })\n }\n }\n const result = await this.invoke('commands', 'execute', { agentId: sessionId, line, submittedAttachments })\n if (!result || typeof result.commandId !== 'string' || !['success', 'error'].includes(result.result?.kind)) throw fail('command/invalid-response', '指令执行响应无效')\n return { commandId: result.commandId, result: { kind: result.result.kind,\n ...(typeof result.result.text === 'string' ? { text: result.result.text } : {}),\n ...(Number.isSafeInteger(result.result.sourceEventSeq) && result.result.sourceEventSeq >= 0 ? { sourceEventSeq: result.result.sourceEventSeq } : {}) } }\n }\n async permissions(sessionId: string): Promise<PermissionCatalog> {\n const service = this.ctx.get('permissionPresets') as any\n if (!service) throw fail('command/permissions-unsupported', '当前主机不支持权限预设')\n const catalog = await this.invoke('permissionPresets', 'catalog', {})\n const projection = await (this.ctx.get('sessionController') as any).projections({ sessionId }, new AbortController().signal)\n const currentValue = projection?.values?.permissions?.currentValue\n const seen = new Set<string>()\n const options = catalog.options.map((option: any) => {\n if (typeof option.value !== 'string' || !option.value.trim() || option.value.length > 512 || /[\\r\\n]/.test(option.value) || seen.has(option.value) || typeof option.name !== 'string') throw fail('command/invalid-response', '权限目录无效')\n seen.add(option.value)\n return { value: option.value, name: option.name,\n ...(typeof option.description === 'string' ? { description: option.description } : {}),\n confirmation: option.value === 'auto' || service.resolve(option.value).sandbox === 'danger-full-access' }\n })\n return { ...(typeof currentValue === 'string' ? { currentValue } : {}), options }\n }\n async feedback(sessionId: string, text: string, category?: string): Promise<void> {\n const result = await this.invoke('sessionFeedback', 'record', { request: { sessionId, ...(text.trim() ? { text: text.trim() } : {}), ...(category ? { category } : {}) } })\n // 上游拒绝时用它自己的码(`session/not-found` 等),插件只提供兜底:\n // 反馈保存失败的原因对手机是可执行的,压成一句通用失败会丢掉它。\n if (result?.ok !== true) throw fail(typeof result?.error?.code === 'string' ? result.error.code : 'command/invalid-response', typeof result?.error?.message === 'string' ? result.error.message : '反馈未能保存')\n }\n async export(sessionId: string, signal: AbortSignal): Promise<Response> {\n // 使用与官方浏览器相同的归档路由,包含子会话和附件,避免自制残缺日志。\n const response = await this.connection().createSharedFetchHandler('/api').fetch(\n new Request('http://localhost/api/session.export?includeDescendants=true&sessionId=' + encodeURIComponent(sessionId), { signal }), this.owner())\n if (!response.ok || !response.body || !response.headers.get('content-type')?.includes('application/zip')) throw fail('command/export-unavailable', '主机未能生成会话日志归档')\n return response\n }\n}\n","/**\n * Adapter for the DSH rc.2 controller API.\n *\n * DeepPilot's phone protocol deliberately speaks one stable in-process\n * `apiProxy` vocabulary. This adapter maps it to the current public\n * Session/Workspace controllers and Gateway-backed Remote Events, keeping\n * the phone protocol isolated from the Host API.\n */\nimport { randomUUID } from 'node:crypto'\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n startDshRemoteInteractions,\n type RemoteApprovalRequest,\n type RemoteQuestionRequest,\n} from './dsh-remote-interactions.ts'\nimport { historyWindow, normalizePageLimit } from './host-event-projection.ts'\nimport { DshCommandApi } from './command-api.ts'\nimport type { CommandsApiLike } from './host-api.ts'\nimport type {\n ApiProxyLike,\n DirectoryListingLike,\n HistoryResult,\n HostSessionModels,\n MuxFrameLike,\n PhoneSessionRow,\n ScheduleHistoryViewLike,\n ScheduleTaskViewLike,\n WorkspaceViewLike,\n} from './host-api.ts'\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface SessionControllerLike {\n list(request: { cursor?: string }, signal: AbortSignal): Promise<{ items: readonly unknown[] }>\n inspect(sessionId: string, signal?: AbortSignal): Promise<{ events: readonly unknown[] }>\n create(request: Record<string, unknown>): Promise<{ sessionId: string; agentPreset?: string }>\n fork(request: { sessionId: string; atSeq?: number }): Promise<{ sessionId: string }>\n modelCatalog(): Promise<unknown>\n selectModel(request: Record<string, unknown>): Promise<{ selected: { provider: string; model: string; reasoningEffort?: string } }>\n rename(request: { sessionId: string; title: string }): Promise<{ title: string; seq: number }>\n prompt(request: Record<string, unknown>, signal: AbortSignal): Promise<{ accepted: true }>\n attachment(request: { sessionId: string; attachmentId: string }): Promise<{ attachment: { mediaType?: string }; data: string }>\n cancel(request: { sessionId: string }): { accepted: true }\n /**\n * Non-activating projection baseline for one session.\n *\n * The signal is mandatory: DSH 0.2.1-alpha.2 aborts before it reads anything,\n * so a caller that omits it gets no baseline at all. The bridge's refresh is\n * fire-and-forget and has no lifetime of its own, so callers pass a signal\n * that never aborts.\n */\n projections(request: { sessionId: string }, signal: AbortSignal):\n Promise<{ asOfSeq: number; values: Record<string, unknown> } | null>\n}\n\ninterface WorkspaceControllerLike {\n create(request: { path: string }): Promise<{ workspace: unknown; created: boolean }>\n archiveSession(request: { sessionId: string }): Promise<{ archivedSessionIds: readonly string[] }>\n unarchiveSession(request: { sessionId: string }): Promise<{ archivedSessionIds: readonly string[] }>\n follow(signal: AbortSignal): AsyncIterable<{ type: string; value?: unknown }>\n}\n\ninterface ScheduleControllerLike {\n list(request: { sessionId: string }): Promise<readonly unknown[]>\n history(request: { sessionId: string; id: string; limit: number; before?: string }): Promise<unknown>\n create(sessionId: string, request: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>\n update(request: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>\n delete(request: { sessionId: string; id: string }, signal?: AbortSignal): Promise<unknown>\n}\n\ninterface DirectoryPickerControllerLike {\n list(path: string | undefined, signal: AbortSignal): Promise<DirectoryListingLike>\n pick(signal: AbortSignal): Promise<string | null>\n}\n\ninterface DeferredInteraction {\n resolve(value: unknown): void\n /** Convert the stable phone-protocol response into a Host waterfall result. */\n map(value: unknown): unknown\n}\n\n/** Which first-answer-wins interaction a phone may surface. */\nexport type DshInteractionKind = 'approval' | 'question'\n\nexport interface DshApiProxyOptions {\n /**\n * Decide whether the resident DeepPilot Remote Client represents a usable\n * phone surface. Returning false delegates only this Client delivery; API\n * Gateway keeps the official Web delivery independent.\n */\n shouldSurfaceInteraction?: (kind: DshInteractionKind) => boolean\n}\n\n/** Direct-controller adapter with the shape HostBridge consumes. */\nexport class DshApiProxy implements ApiProxyLike {\n private readonly commandApi: DshCommandApi\n get commands(): CommandsApiLike | undefined {\n if (!this.commandApi.available) return undefined\n return {\n list: request => this.call(() => this.commandApi.list(request.payload!.sessionId)),\n execute: request => this.call(() => this.commandApi.execute(request.payload!.sessionId, request.payload!.line, request.payload!.attachments, request.payload!.expectedDefinitionId)),\n permissions: request => this.call(() => this.commandApi.permissions(request.payload!.sessionId)),\n feedback: request => this.call(() => this.commandApi.feedback(request.payload!.sessionId, request.payload!.text, request.payload!.category)),\n export: (request, signal) => this.commandApi.export(request.payload!.sessionId, signal),\n }\n }\n private readonly session: SessionControllerLike\n private readonly workspaceController: WorkspaceControllerLike | undefined\n private scheduleController: ScheduleControllerLike | undefined\n private scheduleApi: ApiProxyLike['schedule'] | undefined\n private readonly directoryPicker: DirectoryPickerControllerLike | undefined\n private readonly interactions = new Map<string, DeferredInteraction>()\n private readonly shouldSurfaceInteraction: (kind: DshInteractionKind) => boolean\n\n constructor(private readonly ctx: Context, options: DshApiProxyOptions = {}) {\n this.commandApi = new DshCommandApi(ctx)\n const session = ctx.get('sessionController') as SessionControllerLike | undefined\n if (session === undefined) throw new Error('DSH sessionController is unavailable')\n this.session = session\n this.workspaceController = ctx.get('workspaceController') as WorkspaceControllerLike | undefined\n // A profile without the optional workspace service cannot restore a\n // session; keep the phone capability bit honest.\n if (this.workspaceController === undefined) delete this.workspace?.unarchiveSession\n this.directoryPicker = ctx.get('directoryPickerController') as DirectoryPickerControllerLike | undefined\n this.shouldSurfaceInteraction = options.shouldSurfaceInteraction ?? (() => true)\n this.resolveSchedule()\n // 技能服务可能晚于桥接层挂载;能力探测与请求都读取当前服务,不缓存缺席。\n Object.defineProperty(this.sessions, 'skills', { get: () => {\n const catalog = ctx.get('sessionSkillCatalog') as {\n list(request: { sessionId: string }, signal: AbortSignal): Promise<{ skills: import('./protocol.ts').SkillSummary[] }>\n } | undefined\n if (typeof catalog?.list !== 'function') return undefined\n const read: NonNullable<ApiProxyLike['sessions']['skills']> = async (request) =>\n this.call(() => catalog.list({ sessionId: request.payload!.sessionId }, new AbortController().signal))\n return read\n } })\n }\n\n /**\n * Resolve the optional Schedule service lazily, on every read.\n *\n * The bridge is built from a `ctx.inject` on sessionController / connection\n * / typertGateway, and `schedule` is deliberately NOT in that list: it is a\n * capability the host composition may or may not mount. On a DSH 0.2.0 host\n * those three services become ready well before ScheduleService finishes its\n * own async init, so a value captured in the constructor stayed `undefined`\n * for the bridge's whole lifetime and welcome advertised `schedules=false`\n * even with the service mounted.\n *\n * Once resolved the controller is kept: services are not torn down and\n * rebuilt underneath a live bridge, so re-resolving per call would be pure\n * overhead.\n */\n private resolveSchedule(): void {\n if (this.scheduleController !== undefined) return\n const controller = this.ctx.get('schedule') as ScheduleControllerLike | undefined\n if (controller === undefined) return\n this.scheduleController = controller\n this.scheduleApi = this.createScheduleApi(controller)\n }\n\n get schedule(): ApiProxyLike['schedule'] {\n this.resolveSchedule()\n return this.scheduleApi\n }\n\n private createScheduleApi(controller: ScheduleControllerLike): NonNullable<ApiProxyLike['schedule']> {\n return {\n list: async (request) => this.call(async () => ({\n sessionId: request.payload!.sessionId,\n tasks: (await controller.list({ sessionId: request.payload!.sessionId })).map(toScheduleTask),\n })),\n history: async (request) => this.call(async () => {\n const value = await controller.history({\n sessionId: request.payload!.sessionId,\n id: request.payload!.id,\n limit: request.payload!.limit,\n ...(request.payload!.before !== undefined ? { before: request.payload!.before } : {}),\n }) as Record<string, unknown>\n if (typeof value.code === 'string') {\n throw Object.assign(new Error(String(value.code)), { code: value.code })\n }\n return { sessionId: request.payload!.sessionId, history: toScheduleHistory(value) }\n }),\n create: async (request) => this.call(async () => {\n const { sessionId, ...createRequest } = request.payload!\n return toScheduleTask(await controller.create(sessionId, createRequest))\n }),\n update: async (request) => this.call(async () => {\n const payload = request.payload!\n const result = await controller.update({\n ...payload,\n expected: toScheduleExpected(payload.expected),\n }) as Record<string, unknown>\n return {\n id: String(result.id ?? payload.id),\n updated: result.updated === true,\n ...(result.record !== undefined ? { record: toScheduleTask(result.record) } : {}),\n ...(typeof result.code === 'string' ? { code: result.code } : {}),\n }\n }),\n delete: async (request) => this.call(async () => {\n const result = await controller.delete({ sessionId: request.payload!.sessionId, id: request.payload!.id }) as Record<string, unknown>\n return { id: String(result.id), deleted: result.deleted === true, ...(result.code !== undefined ? { code: String(result.code) } : {}) }\n }),\n }\n }\n\n /**\n * Decide whether this Gateway Client can surface a phone card. The decision\n * fails open to false so a broken device registry delegates this delivery\n * while the official Web Client remains able to answer.\n */\n private canSurfaceInteraction(kind: DshInteractionKind): boolean {\n try {\n return this.shouldSurfaceInteraction(kind)\n } catch {\n return false\n }\n }\n\n readonly sessions: ApiProxyLike['sessions'] = {\n list: async () => this.call(async () => {\n const value = await this.session.list({}, new AbortController().signal)\n // Keep worker identity internally so HostBridge can suppress notifications\n // as well as exclude these rows from the phone's session list.\n return { items: value.items.map(toPhoneSessionRow) }\n }),\n history: async (request) => this.call(async () => {\n const sessionId = request.payload!.sessionId\n let inspected: { events: readonly unknown[] }\n try {\n inspected = await this.session.inspect(sessionId)\n } catch (error) {\n // No message data is logged: only the opaque session id and Host's\n // safe error chain, enough to distinguish an unsupported legacy log\n // from a bridge regression.\n console.warn(`[deeppilot] session history unavailable for ${JSON.stringify(sessionId)}: ${toError(error).code}: ${toError(error).message}`)\n throw error\n }\n // 分页窗口的策略住在 host-event-projection(与它依赖的投影规则同处);\n // 适配器只负责拉全量事件、把窗口映射成 HistoryResult。\n const window = historyWindow(inspected.events, {\n beforeSeq: request.payload?.beforeSeq,\n limit: normalizePageLimit(request.payload?.maxMessages),\n })\n return { events: window.events as HistoryResult['events'], hasMore: window.hasMore }\n }),\n prompt: async (request) => this.call(() => this.session.prompt({\n ...request.payload!,\n requestId: request.rpcId ?? randomUUID(),\n }, new AbortController().signal)),\n create: async (request) => this.call(() => this.session.create(request.payload ?? {})),\n fork: async (request) => this.call(() => this.session.fork(request.payload!)),\n models: async (request) => this.call(async () => projectModels(\n await this.session.modelCatalog(),\n String(request.payload?.sessionId ?? ''),\n await this.session.list({}, new AbortController().signal),\n )),\n selectModel: async (request) => this.call(() => this.session.selectModel(request.payload ?? {})),\n rename: async (request) => this.call(() => this.session.rename(request.payload!)),\n cancel: async (request) => this.call(() => this.session.cancel(request.payload!)),\n attachment: async (request) => this.call(() => this.session.attachment(request.payload!)),\n projections: async (request) => this.call(() => this.session.projections(request.payload!, new AbortController().signal)),\n }\n\n readonly workspace: ApiProxyLike['workspace'] = {\n list: async () => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const baseline = await readWorkspaceBaseline(this.workspaceController)\n return {\n items: baseline.items.map(toWorkspaceView),\n archivedSessionIds: baseline.archivedSessionIds.map(String),\n }\n }),\n create: async (request) => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const value = await this.workspaceController.create(request.payload!)\n return { workspace: toWorkspaceView(value.workspace), created: value.created === true }\n }),\n archiveSession: async (request) => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const value = await this.workspaceController.archiveSession(request.payload!)\n return { archivedSessionIds: [...value.archivedSessionIds] }\n }),\n unarchiveSession: async (request) => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const value = await this.workspaceController.unarchiveSession(request.payload!)\n return { archivedSessionIds: [...value.archivedSessionIds] }\n }),\n }\n\n readonly host: ApiProxyLike['host'] = {\n listDirectory: async (request, signal) => this.call(async () => {\n if (this.directoryPicker === undefined) throw unavailable('directory picker unavailable')\n return await this.directoryPicker.list(request.payload?.path, signal ?? new AbortController().signal)\n }),\n pickDirectory: async (_request, signal) => this.call(async () => {\n if (this.directoryPicker === undefined) throw unavailable('directory picker unavailable')\n return { path: await this.directoryPicker.pick(signal ?? new AbortController().signal) }\n }),\n }\n\n readonly events: ApiProxyLike['events'] = {\n mux: (_request, signal) => this.mux(signal),\n host: (_request, signal) => this.hostEvents(signal),\n }\n\n async respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }): Promise<{ accepted: boolean; reason?: string }> {\n const pending = this.interactions.get(message.rpcId)\n if (pending === undefined) return { accepted: false, reason: 'not-pending' }\n if (!message.result.ok) return { accepted: false, reason: 'bad-response' }\n this.interactions.delete(message.rpcId)\n pending.resolve(pending.map(message.result.value))\n return { accepted: true }\n }\n\n private async *mux(signal: AbortSignal): AsyncIterable<MuxFrameLike> {\n const queue = new AsyncFrameQueue(signal)\n const offEvent = this.ctx.on('session/event' as never, ((session: { id: string; header?: { origin?: string } }, event: unknown) => {\n queue.push({\n type: 'session/event', sessionId: String(session.id),\n // Header origin is available before the next list refresh. A header's\n // parentSession alone is fork lineage, not proof of a worker session.\n isSubagent: session.header?.origin === 'subagent',\n event: event as MuxFrameLike['event'],\n })\n }) as never, { global: true })\n const projections = this.ctx.get('sessionProjections') as {\n onChanged?(listener: (session: { id: string }, key: string, value: unknown) => void): () => void\n } | undefined\n const offProjection = projections?.onChanged?.((session, key, value) => {\n queue.push({ type: 'session/projection', sessionId: String(session.id), key, value })\n })\n const offSkills = this.ctx.on('skills/change' as never, (() => {\n queue.push({ type: 'skills/changed' })\n }) as never, { global: true })\n const offCommands = ['commands/change', 'permission-presets/catalog-changed'].map(event =>\n this.ctx.on(event as never, (() => queue.push({ type: 'commands/changed' })) as never, { global: true }))\n\n let disposeInteractions: (() => Promise<void>) | undefined\n try {\n disposeInteractions = await startDshRemoteInteractions(this.ctx, {\n approval: (sessionId, request, next) => this.answerApproval(queue, sessionId, request, next),\n question: (sessionId, request, next) => this.answerQuestion(queue, sessionId, request, next),\n })\n } catch (error) {\n console.warn(`[deeppilot] DSH Remote interaction client unavailable: ${toError(error).message}`)\n }\n\n try {\n yield* queue.iterate()\n } finally {\n offEvent()\n offProjection?.()\n offSkills()\n offCommands.forEach(dispose => dispose())\n await disposeInteractions?.()\n queue.close()\n }\n }\n\n private answerApproval(\n queue: AsyncFrameQueue,\n sessionId: string,\n request: RemoteApprovalRequest,\n next: () => Promise<unknown>,\n ): Promise<unknown> {\n if (!this.canSurfaceInteraction('approval')) return next()\n const rpcId = randomUUID()\n const response = deferred<unknown>()\n const abort = (): void => response.resolve('cancelled')\n request.signal?.addEventListener('abort', abort, { once: true })\n this.interactions.set(rpcId, {\n resolve: response.resolve,\n map: value => {\n const outcome = (value as { outcome?: unknown } | undefined)?.outcome\n return outcome === 'allowed-once' || outcome === 'rejected' ? outcome : 'unavailable'\n },\n })\n queue.push({\n type: 'approval/requested', rpcId, sessionId, approvalId: rpcId,\n toolName: String(request.toolName ?? 'tool'), reason: String(request.reason ?? ''),\n ...(request.callId ? { callId: request.callId } : {}),\n })\n return response.promise.finally(() => {\n request.signal?.removeEventListener('abort', abort)\n this.interactions.delete(rpcId)\n queue.push({ type: 'approval/resolved', approvalId: rpcId })\n })\n }\n\n private answerQuestion(\n queue: AsyncFrameQueue,\n sessionId: string,\n request: RemoteQuestionRequest,\n next: () => Promise<unknown>,\n ): Promise<unknown> {\n if (!this.canSurfaceInteraction('question')) return next()\n const rpcId = randomUUID()\n const response = deferred<unknown>()\n const abort = (): void => response.reject(new Error('question cancelled'))\n request.signal?.addEventListener('abort', abort, { once: true })\n this.interactions.set(rpcId, {\n resolve: response.resolve,\n map: value => (value as { answer?: unknown } | undefined)?.answer ?? value,\n })\n queue.push({ type: 'question/requested', rpcId, sessionId, questions: request.questions ?? [] })\n return response.promise.finally(() => {\n request.signal?.removeEventListener('abort', abort)\n this.interactions.delete(rpcId)\n queue.push({ type: 'question/resolved', questionRpcId: rpcId })\n })\n }\n\n private async *hostEvents(signal: AbortSignal): AsyncIterable<MuxFrameLike> {\n const queue = new AsyncFrameQueue(signal)\n const listen = (event: string, type: string, project?: (...args: unknown[]) => Record<string, unknown>) =>\n this.ctx.on(event as never, ((...args: unknown[]) => queue.push({ type, ...(project?.(...args) ?? {}) })) as never, { global: true })\n const off = [\n listen('api-session/added', 'host/session-added'),\n listen('api-session/removed', 'host/session-removed'),\n listen('api-session/status', 'host/session-status', (sessionId, running) => ({ sessionId: String(sessionId), running: running === true })),\n listen('api-session/activity', 'host/session-added'),\n listen('schedule/changed', 'host/schedule-changed'),\n ]\n const workspaceAbort = new AbortController()\n const stop = (): void => workspaceAbort.abort()\n signal.addEventListener('abort', stop, { once: true })\n const workspaceTask = this.workspaceController === undefined ? undefined : (async () => {\n try {\n for await (const frame of this.workspaceController!.follow(workspaceAbort.signal)) {\n if (frame.type === 'archived') queue.push({ type: 'host/archived-sessions-changed', archivedSessionIds: (frame as { archivedSessionIds?: unknown }).archivedSessionIds } as MuxFrameLike)\n else if (frame.type !== 'baseline') queue.push({ type: 'host/workspace-changed' })\n }\n } catch { /* Host shutdown / withdrawn controller ends the stream. */ }\n })()\n try {\n yield* queue.iterate()\n } finally {\n for (const dispose of off) dispose()\n signal.removeEventListener('abort', stop)\n workspaceAbort.abort()\n void workspaceTask\n queue.close()\n }\n }\n\n private async call<T>(invoke: () => T | Promise<T>): Promise<{ result: RpcResult<T> }> {\n try { return { result: { ok: true, value: await invoke() } } }\n catch (error) { return { result: { ok: false, error: toError(error) } } }\n }\n}\n\nfunction deferred<T>(): { promise: Promise<T>; resolve(value: T): void; reject(reason: unknown): void } {\n let resolve!: (value: T) => void\n let reject!: (reason: unknown) => void\n const promise = new Promise<T>((ok, fail) => { resolve = ok; reject = fail })\n return { promise, resolve, reject }\n}\n\nclass AsyncFrameQueue {\n private readonly frames: MuxFrameLike[] = []\n private wake: (() => void) | undefined\n private closed = false\n constructor(signal: AbortSignal) {\n signal.addEventListener('abort', () => this.close(), { once: true })\n }\n push(frame: MuxFrameLike): void { if (!this.closed) { this.frames.push(frame); this.wake?.() } }\n close(): void { if (!this.closed) { this.closed = true; this.wake?.() } }\n async *iterate(): AsyncIterable<MuxFrameLike> {\n while (!this.closed) {\n const frame = this.frames.shift()\n if (frame !== undefined) { yield frame; continue }\n await new Promise<void>(resolve => { this.wake = resolve })\n this.wake = undefined\n }\n }\n}\n\nfunction unavailable(message: string): Error & { code: string } {\n return Object.assign(new Error(message), { code: 'directory-picker-unavailable' })\n}\nfunction toError(error: unknown): { code: string; message: string } {\n const value = error as { code?: unknown; message?: unknown }\n return { code: typeof value?.code === 'string' ? value.code : 'internal', message: typeof value?.message === 'string' ? value.message : String(error) }\n}\nfunction toPhoneSessionRow(value: unknown): PhoneSessionRow {\n const row = value as Record<string, unknown>\n return {\n sessionId: String(row.sessionId ?? ''), updatedAt: Number(row.updatedAt ?? Date.now()), running: row.running === true,\n ...(row.blank === true ? { blank: true } : {}), ...(typeof row.cwd === 'string' ? { cwd: row.cwd } : {}),\n ...(typeof row.origin === 'string' ? { origin: row.origin } : {}), ...(typeof row.parentSessionId === 'string' ? { parentSessionId: row.parentSessionId } : {}),\n ...(row.projections && typeof row.projections === 'object' ? { projections: row.projections as PhoneSessionRow['projections'] } : {}),\n }\n}\nfunction toWorkspaceView(value: unknown): WorkspaceViewLike {\n const row = value as Record<string, unknown>\n return { workspaceId: String(row.workspaceId ?? ''), title: String(row.title ?? ''), path: String(row.path ?? ''), sessionIds: Array.isArray(row.sessionIds) ? row.sessionIds.map(String) : [] }\n}\nfunction toScheduleExpected(value: unknown): Record<string, unknown> {\n const row = value !== null && typeof value === 'object' ? value as Record<string, unknown> : {}\n const { state: _state, deliveryMode: _deliveryMode, ...record } = row\n return record\n}\nfunction toScheduleTask(value: unknown): ScheduleTaskViewLike {\n const row = value as Record<string, unknown>\n const kind = ['after', 'at', 'every', 'daily', 'weekly', 'cron'].includes(String(row.kind))\n ? String(row.kind) as ScheduleTaskViewLike['kind']\n : 'at'\n const optionalNumber = (key: string): number | undefined => typeof row[key] === 'number' && Number.isFinite(row[key]) ? row[key] as number : undefined\n return {\n id: String(row.id ?? ''),\n kind,\n title: String(row.title ?? ''),\n prompt: String(row.prompt ?? ''),\n scheduledAt: String(row.scheduledAt ?? ''),\n state: row.state === 'overdue' ? 'overdue' : 'scheduled',\n deliveryMode: 'host',\n ...(optionalNumber('afterSeconds') !== undefined ? { afterSeconds: optionalNumber('afterSeconds')! } : {}),\n ...(optionalNumber('everySeconds') !== undefined ? { everySeconds: optionalNumber('everySeconds')! } : {}),\n ...(typeof row.time === 'string' ? { time: row.time } : {}),\n ...(typeof row.timeZone === 'string' ? { timeZone: row.timeZone } : {}),\n ...(Array.isArray(row.weekdays) ? { weekdays: row.weekdays.filter((day): day is number => typeof day === 'number') } : {}),\n ...(typeof row.expression === 'string' ? { expression: row.expression } : {}),\n }\n}\nfunction toScheduleHistory(value: unknown): ScheduleHistoryViewLike {\n const row = value as Record<string, unknown>\n const records = Array.isArray(row.records) ? row.records : []\n return {\n id: String(row.id ?? ''),\n records: records.map((entry) => {\n const item = entry as Record<string, unknown>\n return {\n scheduledAt: String(item.scheduledAt ?? ''),\n deliveredAt: String(item.deliveredAt ?? ''),\n messageId: String(item.messageId ?? ''),\n ...(typeof item.prompt === 'string' ? { prompt: item.prompt } : {}),\n }\n }),\n earlierRecordsUnavailable: row.earlierRecordsUnavailable === true,\n ...(typeof row.earlierRecordsPruned === 'boolean' ? { earlierRecordsPruned: row.earlierRecordsPruned } : {}),\n retention: {\n days: Number((row.retention as { days?: unknown } | undefined)?.days ?? 0),\n records: Number((row.retention as { records?: unknown } | undefined)?.records ?? 0),\n },\n ...(typeof row.nextBefore === 'string' ? { nextBefore: row.nextBefore } : {}),\n }\n}\nasync function readWorkspaceBaseline(controller: WorkspaceControllerLike): Promise<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }> {\n const abort = new AbortController()\n const iterator = controller.follow(abort.signal)[Symbol.asyncIterator]()\n try {\n const first = await iterator.next()\n const baseline = first.value as { type?: string; value?: { items?: unknown[]; archivedSessionIds?: unknown[] } } | undefined\n if (baseline?.type !== 'baseline') throw new Error('workspace follow did not provide a baseline')\n return { items: (baseline.value?.items ?? []).map(toWorkspaceView), archivedSessionIds: (baseline.value?.archivedSessionIds ?? []).map(String) }\n } finally { abort.abort(); await iterator.return?.() }\n}\nasync function projectModels(catalog: unknown, sessionId: string, list: { items: readonly unknown[] }): Promise<HostSessionModels> {\n const value = catalog as { default?: { provider?: string; model?: string; reasoningEffort?: string }; groups?: unknown[]; failures?: unknown[] }\n const row = list.items.map(toPhoneSessionRow).find(item => item.sessionId === sessionId)\n const selected = (row?.projections?.values?.modelSelection as { next?: unknown } | undefined)?.next as { provider?: string; model?: string; reasoningEffort?: string } | undefined\n return {\n current: { provider: String(selected?.provider ?? value.default?.provider ?? ''), model: String(selected?.model ?? value.default?.model ?? ''), ...(typeof selected?.reasoningEffort === 'string' ? { reasoningEffort: selected.reasoningEffort } : {}) },\n routable: true,\n groups: Array.isArray(value.groups) ? value.groups as HostSessionModels['groups'] : [],\n failures: Array.isArray(value.failures) ? value.failures as HostSessionModels['failures'] : [],\n }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { DeviceScope } from './device-auth.ts'\nimport type { DeepPilotReport, PairingGrantSnapshot, PushTestResult, RelayTestResult } from './report-wire.ts'\n\n/** Async snapshot source wired by the plugin entry. */\nexport type ReportSnapshot = () => Promise<DeepPilotReport>\n\nexport type PairingStarter = () => Promise<PairingGrantSnapshot>\nexport type DeviceRevoker = (deviceId: string) => Promise<boolean>\nexport type DeviceRenamer = (deviceId: string, customName: string | null) => Promise<string | null>\nexport type DeviceScopeUpdater = (deviceId: string, scopes: DeviceScope[]) => Promise<DeviceScope[]>\n\n/** Runs the relay connectivity self-test (health + enrollment round-trip). */\nexport type RelayTester = () => Promise<RelayTestResult>\n\n/** Forces one synthetic notification down the active push pathway. */\nexport type PushTester = () => Promise<PushTestResult>\n\n/**\n * The Typert receiver the Gateway resolves for the DeepPilot Bridge report.\n * Snapshot data stays non-secret; the token crosses the boundary only through\n * the explicit, user-triggered revealToken/rotateToken invocations.\n */\nexport class DeepPilotReportService extends TypertRemoteService {\n private readonly snapshot: ReportSnapshot\n private readonly pairingStarter: PairingStarter\n private readonly deviceRevoker: DeviceRevoker\n private readonly deviceRenamer: DeviceRenamer\n private readonly deviceScopeUpdater: DeviceScopeUpdater\n\n private readonly relayTester: RelayTester\n private readonly pushTester: PushTester\n\n constructor(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingStarter: PairingStarter,\n deviceRevoker: DeviceRevoker,\n deviceRenamer: DeviceRenamer,\n deviceScopeUpdater: DeviceScopeUpdater,\n relayTester: RelayTester,\n pushTester: PushTester,\n ) {\n super(ctx, 'deeppilotReport', { namespace: 'deeppilot' })\n this.snapshot = snapshot\n this.pairingStarter = pairingStarter\n this.deviceRevoker = deviceRevoker\n this.deviceRenamer = deviceRenamer\n this.deviceScopeUpdater = deviceScopeUpdater\n this.relayTester = relayTester\n this.pushTester = pushTester\n }\n\n async report(): Promise<DeepPilotReport> {\n return this.snapshot()\n }\n\n async beginPairing(): Promise<PairingGrantSnapshot> {\n return this.pairingStarter()\n }\n\n async revokeDevice(deviceId: string): Promise<boolean> {\n return this.deviceRevoker(deviceId)\n }\n\n async setDeviceName(deviceId: string, customName: string | null): Promise<string | null> {\n return this.deviceRenamer(deviceId, customName)\n }\n\n async setDeviceScopes(deviceId: string, scopes: DeviceScope[]): Promise<DeviceScope[]> {\n return this.deviceScopeUpdater(deviceId, scopes)\n }\n\n async testRelay(): Promise<RelayTestResult> {\n return this.relayTester()\n }\n\n async testPush(): Promise<PushTestResult> {\n return this.pushTester()\n }\n}\n","/**\n * Wire contract for the DeepPilot report Remote (`deeppilot/report`).\n *\n * The web settings page needs the bridge's live facts (paired devices,\n * connections, token state) — those live Host-side. This module is the single\n * source both halves share: the strict result codec (hand-rolled, so the\n * client bundle needs no schema library) and the exact descriptor object.\n *\n * @module dsh-deeppilot/report-wire\n */\n\nimport type { InvocationDescriptor, TypertCodec, TypertRemoteContribution, TypertSchema } from '@deepseek-ai/dsh-typert-protocol'\nimport { DEVICE_SCOPES, normalizeDeviceScopes, type DeviceScope } from './device-auth.ts'\n\nexport interface PairingGrantSnapshot {\n code: string\n expiresAt: number\n audience: string\n}\n\n/** One paired device row in the report. */\nexport interface ReportDevice {\n deviceId: string\n /** Effective operator-facing name: customName when set, otherwise the app-reported name. */\n deviceName: string\n /** User-assigned label; absent means the app-reported name is in use. */\n customName?: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n fingerprint: string\n scopes: DeviceScope[]\n revokedAt?: number\n /** Present once the device registered an APNs token (token itself never leaves the host). */\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n}\n\n/** One step of the relay connectivity self-test. */\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n message: string\n latencyMs?: number\n}\n\n/** Result of deeppilot/testRelay. */\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\n/** Per-device delivery result of deeppilot/testPush. */\nexport interface PushTestDeviceResult {\n name: string\n environment: string\n /** sent | invalid-token | failed — mirrors the dispatch outcome contract. */\n outcome: string\n /** Apple's raw rejection reason when present (BadDeviceToken, …). */\n reason?: string\n /** First 10 hex chars of the stored token, for freshness verification. */\n tokenFingerprint?: string\n}\n\n/**\n * 设置页「连接诊断」区块展示的一条最近失败。\n *\n * 只携带可安全展示的结构化事实:类型、分因、时间、来源哈希(auditLabel\n * 的 12 位十六进制,绝不是原始 IP)、可选 attemptId / 关闭码 / 耗时。\n * 配对码、公钥、APNs token、消息正文永远不进入这里。\n */\nexport interface ReportDiagnosticFailure {\n ts: number\n kind: string\n reason: string\n attemptId?: string\n source?: string\n closeCode?: number\n durationMs?: number\n}\n\n/** 连接诊断快照;不包含 debug 开关状态。 */\nexport interface ReportDiagnostics {\n /** 插件 apply() 的启动时刻。 */\n startedAt: number\n /** 最近失败事件(时间升序,最多 20 条)。 */\n recentFailures: ReportDiagnosticFailure[]\n}\n\nexport interface PushTestResult {\n transport: 'apns' | 'relay' | 'none'\n /** sent = at least one accepted delivery; no-targets / not-configured / failed. */\n overall: 'sent' | 'failed' | 'no-targets' | 'not-configured'\n message?: string\n results: PushTestDeviceResult[]\n}\n\n/** Everything the settings page renders for the bridge. */\nexport interface DeepPilotReport {\n protocolVersion: number\n serverVersion: string\n /** Host plugin version (from package.json). Same value as serverVersion,\n * surfaced under a stable name so the wire contract does not change if\n * the legacy field is ever repurposed. */\n pluginVersion: string\n /** True once the host's background GitHub check has confirmed a newer\n * stable release exists. Undefined / false ⇒ the UI shows only the\n * version line, never the \"new version\" hint. */\n updateAvailable?: boolean\n /** GitHub release page URL, when an update is available. */\n releaseUrl?: string\n /** Whether the master bridge switch is on. */\n enabled: boolean\n /** Absolute protocol-v2 identity registry path. */\n identityPath: string\n /** Whether host identity and the device registry are ready. */\n pairingReady: boolean\n /** Currently connected phone sockets. */\n activeConnections: number\n historyBufferMax: number\n /** Private IPv4 candidates for local QR pairing. */\n lanAddresses: string[]\n /** Independent TLS-only LAN listener; this never represents DSH's own web port. */\n local: {\n phase: 'disabled' | 'starting' | 'online' | 'error' | 'stopped'\n port: number\n endpoints: string[]\n /** `sha256:` SPKI pin of the LAN certificate; present while `online`. */\n tlsFingerprint?: string\n /** The LAN private key was recreated this run: paired LAN devices must pair again. */\n tlsIdentityRegenerated?: boolean\n message?: string\n updatedAt: number\n }\n remote: {\n provider: 'tailscale-funnel'\n phase: 'disabled' | 'starting' | 'login_required' | 'online' | 'error' | 'unavailable' | 'stopped'\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n }\n devices: ReportDevice[]\n /**\n * 连接诊断。可选:旧 Host 不返回时设置页整块跳过,不当作错误。\n * 这是「最近失败」的唯一 report 通道(health 只服务手机)。\n */\n diagnostics?: ReportDiagnostics\n}\n\n/** The npm package identity both contribution registrations claim. */\nexport const REPORT_REMOTE_PACKAGE = 'dsh-deeppilot'\n\n/** Canonical `<namespace>/<method>` endpoint of the report Remote. */\nexport const REPORT_ENDPOINT = 'deeppilot/report'\n\nexport const BEGIN_PAIRING_ENDPOINT = 'deeppilot/beginPairing'\nexport const REVOKE_DEVICE_ENDPOINT = 'deeppilot/revokeDevice'\nexport const SET_DEVICE_NAME_ENDPOINT = 'deeppilot/setDeviceName'\nexport const SET_DEVICE_SCOPES_ENDPOINT = 'deeppilot/setDeviceScopes'\n\nfunction reject(field: string): never {\n throw new TypeError(`deeppilot/report result: invalid ${field}`)\n}\n\nfunction str(source: Record<string, unknown>, key: string, field: string): string {\n const value = source[key]\n if (typeof value !== 'string') reject(field)\n return value\n}\n\n/**\n * Non-negative integer: counters and timestamps (activeConnections,\n * historyBufferMax, updatedAt, lastSeenTs, protocolVersion, etc.). A bare\n * `typeof number` check accepts 1.5, -1, and 1e20 — all of which then\n * surface verbatim on the settings page and break any sort or arithmetic\n * the UI does.\n */\nfunction int(source: Record<string, unknown>, key: string, field: string): number {\n const value = source[key]\n if (\n typeof value !== 'number' ||\n !Number.isFinite(value) ||\n !Number.isInteger(value) ||\n value < 0\n ) reject(field)\n return value\n}\nfunction bool(source: Record<string, unknown>, key: string, field: string): boolean {\n const value = source[key]\n if (typeof value !== 'boolean') reject(field)\n return value\n}\nfunction rec(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) reject(field)\n return value as Record<string, unknown>\n}\n\nfunction parseDevice(value: unknown): ReportDevice {\n const s = rec(value, 'device')\n let apns: ReportDevice['apns']\n if (s.apns !== undefined) {\n const a = rec(s.apns, 'device.apns')\n const environment = str(a, 'environment', 'device.apns.environment')\n if (environment !== 'development' && environment !== 'production') reject('device.apns.environment')\n apns = { environment, updatedAt: int(a, 'updatedAt', 'device.apns.updatedAt') }\n }\n const customName = s.customName\n if (customName !== undefined && (\n typeof customName !== 'string' ||\n customName.length === 0 ||\n customName.length > 64 ||\n customName.trim() !== customName ||\n /[\\u0000-\\u001f\\u007f-\\u009f]/u.test(customName)\n )) reject('device.customName')\n return {\n deviceId: str(s, 'deviceId', 'device.deviceId'),\n deviceName: str(s, 'deviceName', 'device.deviceName'),\n ...(typeof customName === 'string' ? { customName } : {}),\n appVersion: str(s, 'appVersion', 'device.appVersion'),\n firstSeenTs: int(s, 'firstSeenTs', 'device.firstSeenTs'),\n lastSeenTs: int(s, 'lastSeenTs', 'device.lastSeenTs'),\n fingerprint: str(s, 'fingerprint', 'device.fingerprint'),\n scopes: normalizeDeviceScopes(s.scopes),\n ...(s.revokedAt !== undefined ? { revokedAt: int(s, 'revokedAt', 'device.revokedAt') } : {}),\n ...(apns ? { apns } : {}),\n }\n}\n\nfunction parseRemote(value: unknown): DeepPilotReport['remote'] {\n const s = rec(value, 'remote')\n const provider = str(s, 'provider', 'remote.provider')\n const phase = str(s, 'phase', 'remote.phase')\n if (provider !== 'tailscale-funnel') reject('remote.provider')\n const phases = ['disabled', 'starting', 'login_required', 'online', 'error', 'unavailable', 'stopped']\n if (!phases.includes(phase)) reject('remote.phase')\n const publicURL = s.publicURL\n const authURL = s.authURL\n const message = s.message\n if (publicURL !== undefined && typeof publicURL !== 'string') reject('remote.publicURL')\n if (authURL !== undefined && typeof authURL !== 'string') reject('remote.authURL')\n if (message !== undefined && typeof message !== 'string') reject('remote.message')\n return {\n provider,\n phase: phase as DeepPilotReport['remote']['phase'],\n ...(typeof publicURL === 'string' ? { publicURL } : {}),\n ...(typeof authURL === 'string' ? { authURL } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n updatedAt: int(s, 'updatedAt', 'remote.updatedAt'),\n }\n}\n\nfunction parseLocal(value: unknown): DeepPilotReport['local'] {\n const s = rec(value, 'local')\n const phase = str(s, 'phase', 'local.phase')\n const phases = ['disabled', 'starting', 'online', 'error', 'stopped']\n if (!phases.includes(phase)) reject('local.phase')\n const endpoints = s.endpoints\n if (!Array.isArray(endpoints) || endpoints.some((entry) => typeof entry !== 'string')) reject('local.endpoints')\n const message = s.message\n if (message !== undefined && typeof message !== 'string') reject('local.message')\n const tlsFingerprint = s.tlsFingerprint\n if (tlsFingerprint !== undefined && typeof tlsFingerprint !== 'string') reject('local.tlsFingerprint')\n const tlsIdentityRegenerated = s.tlsIdentityRegenerated\n if (tlsIdentityRegenerated !== undefined && typeof tlsIdentityRegenerated !== 'boolean') reject('local.tlsIdentityRegenerated')\n return {\n phase: phase as DeepPilotReport['local']['phase'],\n port: int(s, 'port', 'local.port'),\n endpoints: endpoints as string[],\n ...(typeof tlsFingerprint === 'string' ? { tlsFingerprint } : {}),\n ...(tlsIdentityRegenerated === true ? { tlsIdentityRegenerated: true } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n updatedAt: int(s, 'updatedAt', 'local.updatedAt'),\n }\n}\n\nfunction parseRelayTestStep(value: unknown): RelayTestStep {\n const st = rec(value, 'step')\n const id = str(st, 'id', 'step.id')\n if (id !== 'health' && id !== 'enroll') reject('step.id')\n const latencyMs = st.latencyMs\n if (latencyMs !== undefined) {\n // Same int() check the report uses for counters: latency is always a\n // non-negative whole-millisecond number; a NaN/Infinity/negative arrives\n // from a buggy host and must not silently render as `(NaNms)`.\n if (\n typeof latencyMs !== 'number' ||\n !Number.isFinite(latencyMs) ||\n !Number.isInteger(latencyMs) ||\n latencyMs < 0\n ) reject('step.latencyMs')\n }\n return {\n id,\n ok: bool(st, 'ok', 'step.ok'),\n message: str(st, 'message', 'step.message'),\n ...(typeof latencyMs === 'number' ? { latencyMs } : {}),\n }\n}\n\nfunction parseRelayTestResult(value: unknown): RelayTestResult {\n const s = rec(value, 'result')\n const overall = str(s, 'overall', 'overall')\n if (overall !== 'ok' && overall !== 'failed') reject('overall')\n const stepsRaw = s.steps\n if (!Array.isArray(stepsRaw)) reject('steps')\n return {\n url: str(s, 'url', 'url'),\n overall,\n tokenIssued: bool(s, 'tokenIssued', 'tokenIssued'),\n steps: (stepsRaw as unknown[]).map(parseRelayTestStep),\n }\n}\n\nfunction parsePushTestResult(value: unknown): PushTestResult {\n const s = rec(value, 'result')\n const transport = str(s, 'transport', 'transport')\n if (transport !== 'apns' && transport !== 'relay' && transport !== 'none') reject('transport')\n const overall = str(s, 'overall', 'overall')\n const overalls = ['sent', 'failed', 'no-targets', 'not-configured']\n if (!overalls.includes(overall)) reject('overall')\n const resultsRaw = s.results\n if (!Array.isArray(resultsRaw)) reject('results')\n const results = (resultsRaw as unknown[]).map((value) => {\n const r = rec(value, 'device result')\n const reason = r.reason\n const tokenFingerprint = r.tokenFingerprint\n return {\n name: str(r, 'name', 'result.name'),\n environment: str(r, 'environment', 'result.environment'),\n outcome: str(r, 'outcome', 'result.outcome'),\n ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}),\n ...(typeof tokenFingerprint === 'string' && /^[0-9a-f]{10}$/.test(tokenFingerprint)\n ? { tokenFingerprint }\n : {}),\n }\n })\n const message = s.message\n return {\n transport,\n overall: overall as PushTestResult['overall'],\n ...(typeof message === 'string' && message.length > 0 ? { message } : {}),\n results,\n }\n}\n\nfunction parseDiagnosticFailure(value: unknown): ReportDiagnosticFailure {\n const s = rec(value, 'diagnostics.recentFailures[]')\n // kind / reason 是代码内枚举常量;这里收敛字符集与长度,防止上游把\n // 任意文本(路径、IP、错误消息)塞进设置页。\n const kind = str(s, 'kind', 'diagnostics.kind')\n if (!/^[a-z][a-z0-9-]{0,47}$/.test(kind)) reject('diagnostics.kind')\n const reason = str(s, 'reason', 'diagnostics.reason')\n if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(reason)) reject('diagnostics.reason')\n const attemptId = s.attemptId\n if (attemptId !== undefined && (typeof attemptId !== 'string' || !/^[A-Za-z0-9_-]{1,16}$/.test(attemptId))) {\n reject('diagnostics.attemptId')\n }\n const source = s.source\n if (source !== undefined && (typeof source !== 'string' || !/^[0-9a-f]{8,64}$/.test(source))) {\n reject('diagnostics.source')\n }\n const closeCode = s.closeCode\n if (closeCode !== undefined && (typeof closeCode !== 'number' || !Number.isInteger(closeCode))) {\n reject('diagnostics.closeCode')\n }\n const durationMs = s.durationMs\n if (durationMs !== undefined && (typeof durationMs !== 'number' || !Number.isFinite(durationMs) || durationMs < 0)) {\n reject('diagnostics.durationMs')\n }\n return {\n ts: int(s, 'ts', 'diagnostics.ts'),\n kind,\n reason,\n ...(typeof attemptId === 'string' ? { attemptId } : {}),\n ...(typeof source === 'string' ? { source } : {}),\n ...(typeof closeCode === 'number' ? { closeCode } : {}),\n ...(typeof durationMs === 'number' ? { durationMs: Math.round(durationMs) } : {}),\n }\n}\n\nfunction parseDiagnostics(value: unknown): ReportDiagnostics {\n const s = rec(value, 'diagnostics')\n const failures = s.recentFailures\n if (!Array.isArray(failures)) reject('diagnostics.recentFailures')\n // 页面上是「最近失败」列表,20 条足够;硬上限防止坏 Host 撑爆 UI。\n if (failures.length > 20) reject('diagnostics.recentFailures')\n return {\n startedAt: int(s, 'startedAt', 'diagnostics.startedAt'),\n recentFailures: (failures as unknown[]).map(parseDiagnosticFailure),\n }\n}\n\nfunction parseReport(value: unknown): DeepPilotReport {\n const s = rec(value, 'report')\n const devices = s.devices\n const lanAddresses = s.lanAddresses\n if (!Array.isArray(devices)) reject('devices')\n if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== 'string')) reject('lanAddresses')\n // Update fields are optional: an older Host (without the background\n // GitHub check) won't include them, and the UI must keep rendering.\n const releaseUrl = s.releaseUrl\n return {\n protocolVersion: int(s, 'protocolVersion', 'protocolVersion'),\n serverVersion: str(s, 'serverVersion', 'serverVersion'),\n pluginVersion: str(s, 'pluginVersion', 'pluginVersion'),\n ...(s.updateAvailable === true ? { updateAvailable: true } : {}),\n ...(typeof releaseUrl === 'string' && releaseUrl.length > 0\n ? { releaseUrl } : {}),\n enabled: bool(s, 'enabled', 'enabled'),\n identityPath: str(s, 'identityPath', 'identityPath'),\n pairingReady: bool(s, 'pairingReady', 'pairingReady'),\n activeConnections: int(s, 'activeConnections', 'activeConnections'),\n historyBufferMax: int(s, 'historyBufferMax', 'historyBufferMax'),\n lanAddresses: lanAddresses as string[],\n local: parseLocal(s.local),\n remote: parseRemote(s.remote),\n devices: (devices as unknown[]).map(parseDevice),\n // 旧 Host 没有这个字段:保持可选,设置页整块跳过。\n ...(s.diagnostics !== undefined ? { diagnostics: parseDiagnostics(s.diagnostics) } : {}),\n }\n}\n\nexport const reportSchema: TypertSchema<DeepPilotReport> = { parse: parseReport }\n\nexport const relayTestSchema: TypertSchema<RelayTestResult> = { parse: parseRelayTestResult }\n\nexport const pushTestSchema: TypertSchema<PushTestResult> = { parse: parsePushTestResult }\n\nexport const pairingGrantSchema: TypertSchema<PairingGrantSnapshot> = {\n parse(value: unknown): PairingGrantSnapshot {\n const s = rec(value, 'pairing grant')\n const code = str(s, 'code', 'pairingGrant.code')\n if (code.length < 32) reject('pairingGrant.code')\n return {\n code,\n expiresAt: int(s, 'expiresAt', 'pairingGrant.expiresAt'),\n audience: str(s, 'audience', 'pairingGrant.audience'),\n }\n },\n}\n\nconst deviceIdSchema: TypertSchema<string> = {\n parse(value: unknown): string {\n if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(value)) reject('deviceId')\n return value\n },\n}\n\nconst customNameSchema: TypertSchema<string | null> = {\n parse(value: unknown): string | null {\n if (value === null) return null\n if (typeof value !== 'string') reject('customName')\n const trimmed = value.trim()\n if (trimmed.length === 0 || trimmed.length > 64 || /[\\u0000-\\u001f\\u007f-\\u009f]/u.test(value)) reject('customName')\n return trimmed\n },\n}\n\nconst scopesSchema: TypertSchema<DeviceScope[]> = {\n parse(value: unknown): DeviceScope[] {\n if (!Array.isArray(value) || value.some((scope) => typeof scope !== 'string' || !DEVICE_SCOPES.includes(scope as DeviceScope))) {\n reject('scopes')\n }\n return normalizeDeviceScopes(value)\n },\n}\n\nconst booleanSchema: TypertSchema<boolean> = {\n parse(value: unknown): boolean {\n if (typeof value !== 'boolean') reject('boolean')\n return value\n },\n}\n\n/** The rc.2 Gateway materializes each strict codec through `create()`. */\nfunction strictCodec<T>(typeSymbol: string, schema: TypertSchema<T>): TypertCodec {\n return {\n mode: 'strict',\n typeSymbol,\n create: () => schema,\n }\n}\n\n/**\n * Connectivity self-test for the push relay: health probe plus (when an\n * enroll key is on file) a real enrollment round-trip. Doubles as repair —\n * a token it issues is cached by the bridge.\n */\nexport const TEST_RELAY_ENDPOINT = 'deeppilot/testRelay'\n\n/**\n * Real-delivery push test: forces one synthetic notification down the active\n * push pathway to every registered device, ignoring connected/muted filters —\n * an explicit user action must always be able to prove the pipeline works.\n */\nexport const TEST_PUSH_ENDPOINT = 'deeppilot/testPush'\n\nexport const REPORT_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'report',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeepPilotReport`, reportSchema),\n}\n\nexport const BEGIN_PAIRING_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${BEGIN_PAIRING_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'beginPairing',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#PairingGrantSnapshot`, pairingGrantSchema),\n}\n\nexport const REVOKE_DEVICE_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REVOKE_DEVICE_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'revokeDevice',\n invocation: { kind: 'direct' },\n parameters: [{\n name: 'deviceId',\n wire: 'deviceId',\n source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceId`, deviceIdSchema),\n }],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#Boolean`, booleanSchema),\n}\n\nexport const SET_DEVICE_NAME_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${SET_DEVICE_NAME_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'setDeviceName',\n invocation: { kind: 'direct' },\n parameters: [\n {\n name: 'deviceId', wire: 'deviceId', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceId`, deviceIdSchema),\n },\n {\n name: 'customName', wire: 'customName', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#CustomDeviceName`, customNameSchema),\n },\n ],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#CustomDeviceName`, customNameSchema),\n}\n\nexport const SET_DEVICE_SCOPES_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${SET_DEVICE_SCOPES_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'setDeviceScopes',\n invocation: { kind: 'direct' },\n parameters: [\n {\n name: 'deviceId', wire: 'deviceId', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceId`, deviceIdSchema),\n },\n {\n name: 'scopes', wire: 'scopes', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceScopes`, scopesSchema),\n },\n ],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceScopes`, scopesSchema),\n}\n\nexport const TEST_RELAY_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_RELAY_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testRelay',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#RelayTestResult`, relayTestSchema),\n}\n\nexport const TEST_PUSH_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_PUSH_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testPush',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#PushTestResult`, pushTestSchema),\n}\n\nconst INVOCATION_DESCRIPTORS = [\n REPORT_DESCRIPTOR,\n BEGIN_PAIRING_DESCRIPTOR,\n REVOKE_DEVICE_DESCRIPTOR,\n SET_DEVICE_NAME_DESCRIPTOR,\n SET_DEVICE_SCOPES_DESCRIPTOR,\n TEST_RELAY_DESCRIPTOR,\n TEST_PUSH_DESCRIPTOR,\n]\n\nexport const REPORT_HOST_CONTRIBUTION = {\n package: REPORT_REMOTE_PACKAGE,\n face: 'host' as const,\n schemas: [],\n // This hand-written Remote has no generated reflection exports.\n model: { services: [], events: [], objects: [] },\n invocations: INVOCATION_DESCRIPTORS,\n}\n\nexport const REPORT_REMOTE_CONTRIBUTION: TypertRemoteContribution = {\n package: REPORT_REMOTE_PACKAGE,\n descriptors: INVOCATION_DESCRIPTORS,\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { DeepPilotReportService } from './report-service.ts'\nimport type { DeviceRenamer, DeviceRevoker, DeviceScopeUpdater, PairingStarter, PushTester, RelayTester, ReportSnapshot } from './report-service.ts'\nimport { REPORT_HOST_CONTRIBUTION } from './report-wire.ts'\n\ninterface TypertContributionRegistry {\n register(contribution: typeof REPORT_HOST_CONTRIBUTION): () => void | Promise<void>\n}\n\n/**\n * Provide the report service and register its Remote descriptor. Rides an\n * optional `typert` inject: profiles without the web stack never activate it.\n */\nexport function applyReportRemote(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingStarter: PairingStarter,\n deviceRevoker: DeviceRevoker,\n deviceRenamer: DeviceRenamer,\n deviceScopeUpdater: DeviceScopeUpdater,\n relayTester: RelayTester,\n pushTester: PushTester,\n): void {\n ctx.inject(['typert'], (remoteCtx) => {\n new DeepPilotReportService(remoteCtx, snapshot, pairingStarter, deviceRevoker, deviceRenamer, deviceScopeUpdater, relayTester, pushTester)\n const registry = remoteCtx.typert as unknown as TypertContributionRegistry\n const unregister = registry.register(REPORT_HOST_CONTRIBUTION)\n remoteCtx.effect(() => () => void unregister(), 'dsh-deeppilot: report remote')\n })\n}\n","import { networkInterfaces } from 'node:os'\n\nfunction octetsOf(address: string): [number, number, number, number] | null {\n const octets = address.split('.').map(Number)\n if (octets.length !== 4 || octets.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {\n return null\n }\n return octets as [number, number, number, number]\n}\n\n/** RFC1918 私有地址。名字保持诚实:它**不**包含 CGNAT 段,见 isSharedAddressSpaceIPv4。 */\nexport function isPrivateIPv4(address: string): boolean {\n const parsed = octetsOf(address)\n if (parsed === null) return false\n const [a, b] = parsed\n return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)\n}\n\n/**\n * RFC 6598 共享地址空间(100.64.0.0/10)。\n *\n * Tailscale 与 Headscale 都从这个段里给节点分配地址(Headscale 的\n * `prefixes.v4` 默认就是它),因此它在实践中是私有的,只是不是 RFC1918——\n * 这正是 `isPrivateIPv4` 会把它拒掉的原因。把它单列成一个谓词而不是塞进\n * `isPrivateIPv4`,是为了不谎报那个名字(issue #24)。\n */\nexport function isSharedAddressSpaceIPv4(address: string): boolean {\n const parsed = octetsOf(address)\n if (parsed === null) return false\n const [a, b] = parsed\n return a === 100 && b >= 64 && b <= 127\n}\n\n/**\n * 可以交给手机的私有地址候选:RFC1918 加 CGNAT 段。\n *\n * 候选多于一个时,配对二维码页才会渲染地址选择器——只有一个候选时用户没有\n * 任何办法指定别的地址。把 tailnet 地址放进来,选择器才会出现,物理局域网\n * 离开之后用户仍可切到 tailnet 那个。\n *\n * 排序**不动**:物理 en* 接口仍然排在隧道接口之前,默认拿到的还是局域网地址,\n * 改变的只是「多了一个可选项」这件事本身。\n */\nfunction isRoutablePrivateIPv4(address: string): boolean {\n return isPrivateIPv4(address) || isSharedAddressSpaceIPv4(address)\n}\n\n/** Private IPv4 candidates, preferring physical en* interfaces over tunnels. */\nexport function localLANIPv4Addresses(): string[] {\n const candidates: Array<{ name: string; address: string }> = []\n for (const [name, entries] of Object.entries(networkInterfaces())) {\n for (const entry of entries ?? []) {\n if (entry.family === 'IPv4' && !entry.internal && isRoutablePrivateIPv4(entry.address)) {\n candidates.push({ name, address: entry.address })\n }\n }\n }\n const priority = (name: string): number => name === 'en0' ? 0 : name.startsWith('en') ? 1 : name.startsWith('bridge') ? 2 : 3\n candidates.sort((left, right) => priority(left.name) - priority(right.name) || left.name.localeCompare(right.name))\n return [...new Set(candidates.map(({ address }) => address))]\n}\n","/**\n * Lightweight self-update check for dsh-deeppilot.\n *\n * On Host boot we ask the GitHub Releases API (REST) which is the latest\n * stable tag, compare it to the installed plugin version, and surface a\n * \"newer release exists\" flag plus the GitHub release URL through the\n * report Remote. The settings page renders one small line at the bottom;\n * a successful check is enough — no manual button, no persistent cache\n * (the host process is the lifetime of the answer).\n *\n * Deliberately no third-party dependency: we use {@link https.request}\n * directly to keep parity with the rest of the project (remote-supervisor\n * uses node:http, host-bridge uses ws, etc).\n *\n * Failure policy: every network/parse error collapses to a single log\n * line and the in-memory snapshot stays \"unknown\". The bridge must never\n * crash because GitHub rate-limited us, returned a 5xx, or the user is\n * offline.\n */\n\nimport { request as httpsRequest, type RequestOptions } from 'node:https'\n\n/** GitHub repo (no .git suffix). Public, unauthenticated, low rate limit. */\nconst RELEASES_PATH = '/repos/Mars-Sea/dsh-deeppilot/releases'\n\n/** Hard ceiling on the network round-trip. The host must never hang. */\nconst FETCH_TIMEOUT_MS = 8_000\n\n/** Per-page limit. We only need the first stable release, but pre-releases\n * tend to be listed first; fetching 20 gives the comparator enough room. */\nconst PER_PAGE = 20\n\n/**\n * Public snapshot surfaced to the report Remote. `available` is `false`\n * until the network answer lands; on failure it stays `false` so the UI\n * simply doesn't render the \"new version\" hint.\n */\nexport interface UpdateInfo {\n /** Host plugin version (from package.json). */\n currentVersion: string\n /** True once a newer stable release has been found on GitHub. */\n available: boolean\n /** GitHub release page URL, when an update is available. */\n releaseUrl: string | null\n /** Newer stable tag, when an update is available. */\n latestVersion: string | null\n}\n\ninterface GitHubRelease {\n tag_name?: unknown\n prerelease?: unknown\n draft?: unknown\n html_url?: unknown\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction parseStableEntry(value: unknown): { tag: string; url: string | null } | null {\n if (!isPlainObject(value)) return null\n const tag = value.tag_name\n if (typeof tag !== 'string') return null\n if (value.prerelease === true || value.draft === true) return null\n if (parseStableTag(tag) === null) return null\n const url = value.html_url\n return { tag, url: typeof url === 'string' ? url : null }\n}\n\n/** Parse one stable release from the `tag_name` shape `vX.Y.Z` (the v is\n * optional; `1.2.3` is also accepted). Pre-release tags like `0.3.0-rc.1`\n * return null — the policy is \"stable channel only\". */\nexport function parseStableTag(tag: string): { major: number; minor: number; patch: number } | null {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(tag.trim())\n if (match === null) return null\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n }\n}\n\n/** Semver compare for X.Y.Z. Returns -1 / 0 / 1. */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const pa = parseStableTag(a)\n const pb = parseStableTag(b)\n if (pa === null && pb === null) return 0\n if (pa === null) return -1\n if (pb === null) return 1\n if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1\n if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1\n if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1\n return 0\n}\n\n/** Hit the GitHub Releases API. Resolves with the first stable release\n * GitHub returned, or null if the list contains no stable entries.\n * Network / parse errors reject — the caller is responsible for\n * collapsing them to a log line. */\nexport function fetchLatestStableRelease(): Promise<{ tag: string; url: string | null } | null> {\n return new Promise((resolve, reject) => {\n const options: RequestOptions = {\n method: 'GET',\n host: 'api.github.com',\n path: `${RELEASES_PATH}?per_page=${PER_PAGE}`,\n headers: {\n 'user-agent': 'dsh-deeppilot-update-check',\n 'accept': 'application/vnd.github+json',\n },\n }\n const req = httpsRequest(options, (res) => {\n const status = res.statusCode ?? 0\n if (status < 200 || status >= 300) {\n res.resume()\n reject(new Error(`github releases http ${status}`))\n return\n }\n const chunks: Buffer[] = []\n res.on('data', (chunk: Buffer) => chunks.push(chunk))\n res.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf8')\n const parsed = JSON.parse(body) as unknown\n if (!Array.isArray(parsed)) {\n reject(new Error('github releases: response is not an array'))\n return\n }\n for (const entry of parsed) {\n const stable = parseStableEntry(entry)\n if (stable !== null) {\n resolve(stable)\n return\n }\n }\n resolve(null)\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)))\n }\n })\n res.on('error', (error) => reject(error))\n })\n req.setTimeout(FETCH_TIMEOUT_MS, () => {\n req.destroy(new Error('github releases: timeout after ' + FETCH_TIMEOUT_MS + 'ms'))\n })\n req.on('error', (error) => reject(error))\n req.end()\n })\n}\n\n/**\n * Process-wide check state. Constructed once in `apply()`, queried\n * synchronously by the report snapshot. The check itself fires once in\n * the background shortly after boot; the answer lives for the lifetime\n * of the host process — re-running the page in the Web UI does not\n * trigger another network call.\n */\nexport class UpdateChecker {\n private readonly log: (message: string) => void\n private readonly currentVersion: string\n private readonly fetchImpl: typeof fetchLatestStableRelease\n private readonly initialDelayMs: number\n private snapshot: UpdateInfo\n private inflight: Promise<void> | null = null\n\n constructor(options: {\n log: (message: string) => void\n currentVersion: string\n fetchImpl?: typeof fetchLatestStableRelease\n initialDelayMs?: number\n }) {\n this.log = options.log\n this.currentVersion = options.currentVersion\n this.fetchImpl = options.fetchImpl ?? fetchLatestStableRelease\n this.initialDelayMs = options.initialDelayMs ?? 2_000\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: false,\n releaseUrl: null,\n latestVersion: null,\n }\n }\n\n /** Return the current in-memory snapshot — safe to call from any host\n * thread. Never throws, never awaits. */\n get(): UpdateInfo {\n return this.snapshot\n }\n\n /**\n * Schedule one background refresh after the configured initial delay.\n * Used by the plugin entry to do the first check without blocking boot.\n */\n scheduleInitial(): void {\n if (this.initialDelayMs <= 0) {\n void this.runOnce()\n return\n }\n const timer = setTimeout(() => {\n void this.runOnce()\n }, this.initialDelayMs)\n if (typeof (timer as { unref?: () => void }).unref === 'function') {\n (timer as { unref: () => void }).unref()\n }\n }\n\n private async runOnce(): Promise<void> {\n if (this.inflight !== null) {\n await this.inflight\n return\n }\n const task = (async () => {\n try {\n const stable = await this.fetchImpl()\n if (stable === null) {\n // No stable release on GitHub (yet) — leave `available` false.\n return\n }\n if (compareSemver(stable.tag, this.currentVersion) > 0) {\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: true,\n releaseUrl: stable.url,\n latestVersion: stable.tag,\n }\n } else {\n // Up to date. Clear any previous \"available\" state so a host\n // restart after an upgrade immediately stops showing the hint.\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: false,\n releaseUrl: null,\n latestVersion: null,\n }\n }\n } catch (error) {\n this.log('update check failed: ' + (error instanceof Error ? error.message : String(error)))\n // Leave the previous snapshot in place. Even \"no answer\" is a\n // worse signal than the last successful check.\n }\n })()\n this.inflight = task.finally(() => {\n this.inflight = null\n })\n return this.inflight\n }\n\n /** No-op kept for API symmetry with the host lifecycle wiring. */\n dispose(): void {\n // The initial-delay timer is unref'd; nothing to track here.\n }\n}\n","import type { IncomingMessage } from 'node:http'\nimport { isIP } from 'node:net'\nimport type { Duplex } from 'node:stream'\n\nexport const CLIENT_IP_HEADER = 'x-deeppilot-client-ip'\n\nexport function rejectUpgrade(socket: Duplex, status: number, reason: string, retryAfterSeconds?: number): void {\n const body = JSON.stringify({ error: reason })\n const statusText: Record<number, string> = {\n 401: 'Unauthorized',\n 429: 'Too Many Requests',\n 500: 'Internal Server Error',\n 503: 'Service Unavailable',\n }\n const retryAfter = status === 429 && retryAfterSeconds !== undefined\n ? `Retry-After: ${Math.max(1, Math.ceil(retryAfterSeconds))}\\r\\n`\n : ''\n socket.end(\n 'HTTP/1.1 ' + status + ' ' + (statusText[status] ?? 'Error') + '\\r\\n' +\n retryAfter +\n 'Content-Type: application/json\\r\\n' +\n 'Content-Length: ' + Buffer.byteLength(body) + '\\r\\n' +\n 'Connection: close\\r\\n' +\n '\\r\\n' +\n body,\n )\n}\n\nfunction normalizedAddress(value: string | undefined): string | null {\n if (!value) return null\n const normalized = value.startsWith('::ffff:') ? value.slice(7) : value\n return isIP(normalized) === 0 ? null : normalized\n}\n\nfunction isLoopback(value: string | undefined): boolean {\n const address = normalizedAddress(value)\n return address === '127.0.0.1' || address === '::1'\n}\n\n/**\n * Resolve a stable rate-limit key. The helper-supplied address is trusted only\n * on the private loopback hop; direct clients cannot spoof it.\n */\nexport function requestClientIdentity(\n req: Pick<IncomingMessage, 'headers' | 'socket'>,\n): string {\n if (isLoopback(req.socket.remoteAddress)) {\n const forwarded = req.headers[CLIENT_IP_HEADER]\n const value = Array.isArray(forwarded) ? forwarded[0] : forwarded\n const address = normalizedAddress(value)\n if (address !== null) return address\n }\n return normalizedAddress(req.socket.remoteAddress) ?? 'unknown'\n}\n","import { createHash, createPrivateKey, createPublicKey, X509Certificate } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { generate } from 'selfsigned'\nimport { formatLogLine } from './diagnostics.ts'\n\nexport { LAN_TLS_FINGERPRINT_PATTERN, isLanTlsFingerprint } from './pairing-qr.ts'\n\nexport const LAN_TLS_COMMON_NAME = 'dsh-deeppilot'\nexport const LAN_TLS_KEY_FILE = 'key.pem'\nexport const LAN_TLS_CERT_FILE = 'cert.pem'\n\nconst CERT_VALIDITY_MS = 10 * 365 * 24 * 60 * 60_000\n/** Re-sign this far ahead of expiry so a long-running host never serves a stale leaf. */\nconst CERT_RENEW_LEAD_MS = 30 * 24 * 60 * 60_000\n\nexport interface LanTlsIdentity {\n /** PKCS#8 PEM private key. Never logged or reported. */\n key: string\n /** Self-signed X.509 PEM leaf certificate. */\n cert: string\n /** Pinned by paired devices; changes only when the private key changes. */\n fingerprint: string\n /** Unix ms when the current leaf certificate expires. */\n notAfter: number\n /**\n * The private key was newly created. Devices that pinned the previous\n * identity can no longer connect and must pair again.\n */\n regenerated: boolean\n /** A fresh leaf was signed with the existing key; the fingerprint is unchanged. */\n resigned: boolean\n}\n\n/** Compute the pinned public-key fingerprint of a PEM certificate. */\nexport function spkiFingerprint(certPem: string): string {\n const der = createPublicKey(certPem).export({ type: 'spki', format: 'der' })\n return 'sha256:' + createHash('sha256').update(der).digest('base64url')\n}\n\n/**\n * Load the LAN listener's TLS identity from `dir`, creating or repairing it\n * as needed. The private key is the durable identity: it is created once and\n * reused for every subsequent certificate, so pinned devices survive\n * certificate renewals. Only a missing or unreadable key forces a new one.\n */\nexport async function loadOrCreateLanTlsIdentity(dir: string, now: number = Date.now()): Promise<LanTlsIdentity> {\n await mkdir(dir, { recursive: true, mode: 0o700 })\n const keyPath = join(dir, LAN_TLS_KEY_FILE)\n const certPath = join(dir, LAN_TLS_CERT_FILE)\n\n let key = await readPem(keyPath, (error) => {\n // 读失败(非 ENOENT)会被下面当成「密钥缺失」直接重建:已配对设备的\n // 固定指纹随即失效、必须重新配对。这个后果必须可见,否则用户只会看到\n // 手机突然不认证书,而电脑端毫无线索。\n console.log(formatLogLine('warn', 'LAN TLS private key unreadable, regenerating identity: ' + describeFileError(error)))\n })\n let regenerated = false\n if (key === undefined || !validPrivateKey(key)) {\n regenerated = true\n key = undefined\n }\n\n let cert = regenerated ? undefined : await readPem(certPath, (error) => {\n // 证书读失败可以只重签(密钥不变、指纹不变),仍是需要留痕的异常。\n console.log(formatLogLine('warn', 'LAN TLS certificate unreadable, re-signing with the existing key: ' + describeFileError(error)))\n })\n let resigned = false\n const existing = key !== undefined && cert !== undefined ? inspectCertificate(cert, key, now) : undefined\n if (existing === undefined) {\n const issued = await issueCertificate(key, now)\n if (key === undefined) {\n key = pemFile(issued.key)\n await writePrivate(keyPath, key)\n } else {\n resigned = true\n }\n cert = pemFile(issued.cert)\n await writePrivate(certPath, cert)\n }\n if (key === undefined || cert === undefined) throw new Error('LAN TLS identity unavailable')\n\n return {\n key,\n cert,\n fingerprint: spkiFingerprint(cert),\n notAfter: existing ?? new X509Certificate(cert).validToDate.getTime(),\n regenerated,\n resigned,\n }\n}\n\nasync function readPem(path: string, onError?: (error: unknown) => void): Promise<string | undefined> {\n try {\n const text = await readFile(path, 'utf8')\n return text.trim().length > 0 ? text : undefined\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n // 非 ENOENT 的读取失败交给调用方留痕;随后仍按「文件缺失」重建身份,\n // 保证插件能起来。错误消息只含路径与 errno,不含密钥内容。\n onError?.(error)\n return undefined\n }\n}\n\n/** 读失败的安全描述:文件名 + errno,不带文件内容。 */\nfunction describeFileError(error: unknown): string {\n const code = (error as NodeJS.ErrnoException | undefined)?.code\n const message = error instanceof Error ? error.message : String(error)\n return (typeof code === 'string' ? 'code=' + code + ' ' : '') + message.slice(0, 140)\n}\n\nfunction validPrivateKey(pem: string): boolean {\n try {\n const key = createPrivateKey(pem)\n return key.asymmetricKeyType === 'ec'\n } catch {\n return false\n }\n}\n\n/**\n * Return the certificate's expiry when it is usable with `keyPem`, or\n * `undefined` when it must be re-issued (malformed, wrong key, or expiring).\n */\nfunction inspectCertificate(certPem: string, keyPem: string, now: number): number | undefined {\n try {\n const cert = new X509Certificate(certPem)\n const certSpki = cert.publicKey.export({ type: 'spki', format: 'der' })\n const keySpki = createPublicKey(createPrivateKey(keyPem)).export({ type: 'spki', format: 'der' })\n if (!certSpki.equals(keySpki)) return undefined\n const notAfter = cert.validToDate.getTime()\n const notBefore = cert.validFromDate.getTime()\n if (!(notBefore <= now && notAfter - CERT_RENEW_LEAD_MS > now)) return undefined\n return notAfter\n } catch {\n return undefined\n }\n}\n\nasync function issueCertificate(existingKey: string | undefined, now: number): Promise<{ key: string; cert: string }> {\n // Back-date slightly so a phone whose clock trails the computer still accepts a\n // certificate issued moments ago.\n const notBeforeDate = new Date(now - 5 * 60_000)\n const notAfterDate = new Date(now + CERT_VALIDITY_MS)\n const keyPair = existingKey === undefined\n ? undefined\n : {\n privateKey: createPrivateKey(existingKey).export({ type: 'pkcs8', format: 'pem' }) as string,\n publicKey: createPublicKey(existingKey).export({ type: 'spki', format: 'pem' }) as string,\n }\n const result = await generate(\n [{ name: 'commonName', value: LAN_TLS_COMMON_NAME }],\n {\n keyType: 'ec',\n curve: 'P-256',\n algorithm: 'sha256',\n notBeforeDate,\n notAfterDate,\n ...(keyPair ? { keyPair } : {}),\n extensions: [\n { name: 'basicConstraints', cA: false, critical: true },\n { name: 'keyUsage', digitalSignature: true, keyAgreement: true, critical: true },\n { name: 'extKeyUsage', serverAuth: true },\n { name: 'subjectAltName', altNames: [{ type: 2, value: LAN_TLS_COMMON_NAME }] },\n ],\n },\n )\n return { key: existingKey ?? result.private, cert: result.cert }\n}\n\n/** Exact on-disk form, so a freshly issued value equals its later reload. */\nfunction pemFile(pem: string): string {\n return pem.replace(/\\r\\n/g, '\\n').trimEnd() + '\\n'\n}\n\nasync function writePrivate(path: string, contents: string): Promise<void> {\n const tmp = `${path}.${process.pid}.tmp`\n try {\n await writeFile(tmp, contents, { mode: 0o600 })\n await rename(tmp, path)\n } catch (error) {\n await unlink(tmp).catch(() => {})\n throw error\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createHash, randomBytes } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport type { Duplex } from 'node:stream'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { WebSocketServer } from 'ws'\nimport { BridgeConnection } from './connection.ts'\nimport { PushGateway } from './push-gateway.ts'\nimport { createLocalTransport, createRemoteTransport } from './transport-reconciler.ts'\nimport { ConnectionGate } from './connection-gate.ts'\nimport { HostBridge } from './host-bridge.ts'\nimport { DshApiProxy, type DshInteractionKind } from './dsh-api-proxy.ts'\nimport { applyReportRemote } from './report-remote.ts'\nimport type { DeepPilotReport } from './report-wire.ts'\nimport { DeviceStore, MAX_DEVICES, bridgeDataDir, deviceDisplayName, ensurePrivateBridgeDataDir, expandHome, migrateLegacyBridgeDataDir } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport {\n PairingCodeManager,\n deviceIdForPublicKey,\n loadOrCreateHostAudience,\n normalizeDeviceScopes,\n} from './device-auth.ts'\nimport type { RemoteStatus } from './remote-supervisor.ts'\nimport { localLANIPv4Addresses } from './local-address.ts'\nimport { UpdateChecker, type UpdateInfo } from './update-check.ts'\nimport { normalizeOptions } from './config.ts'\nimport type { Config } from './config.ts'\nimport { rejectUpgrade, requestClientIdentity } from './phone-http.ts'\nimport { AuthRateLimiter } from './auth-rate-limit.ts'\nimport { MAX_APP_VERSION_CHARS, MAX_DEVICE_NAME_CHARS, sanitizeDeviceField } from './connection-policy.ts'\nimport { localEndpointURLs } from './local-policy.ts'\nimport { closeServer, createPhoneServer, listen, type PhoneServerTransportError } from './phone-server.ts'\nimport { loadOrCreateLanTlsIdentity, type LanTlsIdentity } from './lan-tls.ts'\nimport {\n attemptIdFromRequest,\n buildHealthPayload,\n createLogger,\n DiagnosticsStore,\n} from './diagnostics.ts'\n\n/**\n * dsh-deeppilot — data bridge between the DSH host and DeepPilot\n * clients. Owns independent, narrowly routed LAN (TLS-only, pinned by paired\n * devices) and loopback Funnel-origin listeners. The web UI and the rest of\n * DSH's API are never exposed by these listeners.\n *\n * Data plane: an in-process HostBridge consumes a local adapter over DSH\n * Session/Workspace controllers, mirrors session summaries,\n * tracks pending approvals/questions, and fans projected protocol-v2 pushes\n * out to every connected device.\n *\n * Protocol: PROTOCOL.md is normative; src/protocol.ts and the private app's\n * Swift models mirror that v2 contract.\n */\n\nexport const name = 'deeppilot'\n\nexport { HostBridge } from './host-bridge.ts'\nexport { Config } from './config.ts'\nexport { mayReceivePush, shouldPrunePushToken, shouldReEnrollRelayToken } from './push-policy.ts'\n\n/** No web-service requirement: the plugin owns its own transport listeners. */\nexport const inject: string[] = []\n\ntype SubContext = {\n effect: (setup: () => unknown, name?: string) => unknown\n}\n\nconst SERVER_VERSION = readOwnPackageVersion()\nconst MAX_CLIENT_CONNECTIONS = 16\n/**\n * Single-frame bound. Covers the protocol maximum (4 × 8 MB base64 images\n * plus prompt text) with headroom while keeping an unauthenticated client's\n * pre-hello buffering far below ws's 100 MiB default.\n */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024\n\n/**\n * Resolve the host plugin's own version from the installed package.json.\n * Sourced at boot so the wire / UI always agrees with what npm published.\n * `createRequire(import.meta.url)` is the tsdown-bundled ESM equivalent of\n * CommonJS's `require`; the package.json sits next to lib/index.js after\n * the build, so `../package.json` resolves to the published manifest.\n */\nfunction readOwnPackageVersion(): string {\n try {\n const require_ = createRequire(import.meta.url)\n const pkg = require_('../package.json') as { version?: unknown }\n if (typeof pkg.version === 'string' && pkg.version.length > 0) return pkg.version\n } catch {\n // fall through to env / hardcoded default below\n }\n // `npm` injects this for `npm run` / `npm exec` / `npm start` invocations.\n // `process.env.npm_package_version` is unset when DSH loads the plugin\n // directly, so we keep it as a secondary source rather than the truth.\n const envVersion = process.env.npm_package_version\n if (typeof envVersion === 'string' && envVersion.length > 0) return envVersion\n return '0.0.0+unknown'\n}\n\nexport function apply(ctx: Context, options: unknown): void {\n const cfg = normalizeOptions(options)\n\n // 日志唯一出口:统一时间戳 + 级别。失败路径显式传 'warn';其余 info。\n // 这是 P0 可观测性基线——用户贴回来的每一行都能对应到具体时刻。\n const log = createLogger()\n const auditSalt = randomBytes(32)\n const auditLabel = (value: string): string => createHash('sha256')\n .update(auditSalt)\n .update(value)\n .digest('hex')\n .slice(0, 12)\n\n let scheduleRemoteReconcile: (() => void) | undefined\n let scheduleLocalReconcile: (() => void) | undefined\n const currentConfig = (): Config => normalizeOptions(options)\n const enabledNow = (): boolean => currentConfig().enabled === true\n\n // rc.2 commits volatile config edits into live refs and emits on the owning\n // fiber. Reconcile transport listeners after the new values are published.\n ;(ctx as unknown as { on: (name: string, listener: () => void) => void }).on(\n 'loader/volatile-update',\n () => queueMicrotask(() => {\n scheduleLocalReconcile?.()\n scheduleRemoteReconcile?.()\n }),\n )\n\n // Master switch, resolved against the current profile entry.\n // Individual injected services also read currentConfig() when they activate.\n if (currentConfig().enabled !== true) {\n log('disabled via settings; bridge stays inactive (rumors of /phone below are skipped)')\n }\n\n // Resolved asynchronously; route handlers await readiness. Never rejects:\n // a storage failure degrades the bridge instead of killing the host.\n const dataDir = bridgeDataDir()\n\n /** 插件启动时刻:`/phone/health` 与设置页报告都读它。 */\n const startedAt = Date.now()\n\n /**\n * 连接诊断事件:内存环形 256 条 + 按天落盘(插件数据目录 diagnostics/,\n * 保留 7 天、目录上限 5MB)。与 `diagnostics.debug` 完全解耦——失败事件\n * 无条件记录,否则用户不开调试开关就永远看不到「为什么连不上」。\n */\n const diagnostics = new DiagnosticsStore({\n dir: join(dataDir, 'diagnostics'),\n write: (line) => { console.log(line) },\n })\n\n /**\n * 传输层错误(TLS 握手失败 / HTTP 报文解析失败)的统一入口。来源先经\n * auditLabel 哈希再记录;原始 IP 绝不进入事件或日志。\n */\n const recordTransportError = (event: PhoneServerTransportError): void => {\n const code = (event.error as NodeJS.ErrnoException | undefined)?.code\n diagnostics.record('connection-error', event.kind, {\n ...(event.remoteAddress !== undefined ? { source: auditLabel(event.remoteAddress) } : {}),\n detail: typeof code === 'string'\n ? code\n : (event.error instanceof Error ? event.error.message : String(event.error)),\n })\n }\n\n // ---------- offline push (F-9) ----------\n\n // 推送的全部行为住在 PushGateway:enrollment 与持久化、中继注册、sender\n // 缓存与退避、两个 scheduler、PushOutlet 四方法、两个自测。这里只声明它并\n // 注入依赖——apply() 不再持有任何推送状态。详见 src/push-gateway.ts 文件头。\n const pushGateway = new PushGateway({\n config: currentConfig,\n dataDir,\n devices: () => auth.devices,\n audience: () => auth.audience,\n connections: () => connections,\n enabledNow,\n log,\n })\n\n const pairingCodes = new PairingCodeManager()\n const auth: { audience: string | null; devices: DeviceStore | null } = {\n audience: null,\n devices: null,\n }\n const ready = (async () => {\n try {\n try {\n const migratedFrom = await migrateLegacyBridgeDataDir()\n if (migratedFrom !== null) log(`migrated legacy plugin state from ${migratedFrom} to ${dataDir}`)\n } catch (error) {\n log('legacy plugin-state migration skipped: ' + String(error), 'warn')\n }\n await ensurePrivateBridgeDataDir()\n auth.audience = await loadOrCreateHostAudience(join(dataDir, 'host-id'))\n auth.devices = await DeviceStore.load(cfg.devicesPath ?? join(dataDir, 'devices-v2.json'))\n {\n // Startup visibility: makes \"registrations vanished after restart\"\n // instantly diagnosable (0 push rows ⇒ the file itself lost data).\n const rows = auth.devices.list()\n const registered = rows.filter((row) => row.apns !== undefined).length\n log(`device registry loaded from ${expandHome(cfg.devicesPath ?? join(dataDir, 'devices-v2.json'))}: ${rows.length} device(s), ${registered} push registration(s)`)\n }\n // Restore zero-touch push enrollment state (best effort).\n await pushGateway.restore()\n } catch (error) {\n const message = String(error)\n log('auth material unavailable, bridge degraded: ' + message, 'warn')\n return { audience: null, devices: null }\n }\n return { audience: auth.audience, devices: auth.devices }\n })()\n\n const beginPairing = async (): Promise<{ code: string; expiresAt: number; audience: string }> => {\n await ready\n if (auth.audience === null || auth.devices === null) throw new Error('device authentication unavailable')\n return { ...pairingCodes.issue(), audience: auth.audience }\n }\n\n /**\n * Settings-page push self-test: force one synthetic notification down the\n * active pathway to EVERY registered device, deliberately ignoring the\n * connected-skip and category-mute filters — an explicit user action must\n * always be able to prove delivery end to end.\n */\n const connections = new Set<BridgeConnection>()\n\n const closeConnectionsForBridge = (bridge: HostBridge): void => {\n for (const connection of connections) {\n if (!connection.isAttachedTo(bridge)) continue\n connection.closeForServerStop()\n connections.delete(connection)\n }\n }\n\n const closeAllConnections = (): void => {\n for (const connection of connections) connection.closeForServerStop()\n connections.clear()\n }\n\n /**\n * Unbind one device. Single path shared by the settings page (report-service\n * revokeDevice) and the wire-level c2s.device.revoke frame: mark the registry\n * tombstone (which also drops every APNs/WidgetKit/LiveActivity token, so the\n * push fan-out can no longer select it) and hard-drop its live sockets.\n * `except` is the connection that sent the revoke frame — it closes itself\n * with 4401 after acking, so it is not terminated here.\n */\n const revokeDevice = async (deviceId: string, except?: BridgeConnection): Promise<boolean> => {\n const { devices } = await ready\n if (!devices) throw new Error('device registry unavailable')\n const revoked = devices.revoke(deviceId, Date.now())\n if (revoked) {\n for (const connection of [...connections]) {\n if (connection === except || connection.connectedDeviceId !== deviceId) continue\n connection.terminate()\n connections.delete(connection)\n }\n log(`device revoked id=${auditLabel(deviceId)}`)\n }\n return revoked\n }\n\n const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_FRAME_BYTES })\n // The LAN TLS identity is loaded once per plugin lifetime: port or enable\n // toggles reuse it so paired devices keep their pin. `tlsIdentityRegenerated`\n // stays raised for the settings page until the next restart because it\n // means every previously paired LAN device must pair again. 身份留在\n // apply()——它被 listener 启动、配对回显与 report 三处消费,协调器不懂 TLS。\n let lanTlsIdentity: Promise<LanTlsIdentity> | undefined\n let tlsIdentityRegenerated = false\n const loadLanTls = (): Promise<LanTlsIdentity> => {\n lanTlsIdentity ??= (async () => {\n await ready\n const identity = await loadOrCreateLanTlsIdentity(join(dataDir, 'lan-tls'))\n if (identity.regenerated) {\n tlsIdentityRegenerated = true\n log(`LAN TLS identity created: fingerprint=${identity.fingerprint}; previously paired LAN devices must pair again`)\n } else if (identity.resigned) {\n log(`LAN TLS certificate renewed (fingerprint unchanged: ${identity.fingerprint})`)\n }\n return identity\n })()\n // A failed load (disk error) must not poison later reconciles.\n lanTlsIdentity.catch(() => { lanTlsIdentity = undefined })\n return lanTlsIdentity\n }\n\n const updateChecker = new UpdateChecker({ log, currentVersion: SERVER_VERSION })\n updateChecker.scheduleInitial()\n const updateInfo = (): UpdateInfo => updateChecker.get()\n\n // Typert Remote for the web settings page (deeppilot/report).\n applyReportRemote(ctx, async () => {\n let pairingReady = false\n let devices: Array<{\n deviceId: string; deviceName: string; customName?: string; appVersion: string; firstSeenTs: number; lastSeenTs: number\n fingerprint: string; scopes: ReturnType<typeof normalizeDeviceScopes>; revokedAt?: number\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n }> = []\n try {\n await ready\n pairingReady = auth.audience !== null && auth.devices !== null\n // Strip the raw APNs token at the source; the report carries only the\n // registration fact (environment + freshness).\n devices = (auth.devices?.list() ?? [])\n .filter((device) => device.publicKey !== undefined && device.fingerprint !== undefined)\n .map(({ deviceId, deviceName, customName, appVersion, firstSeenTs, lastSeenTs, fingerprint, scopes, revokedAt, apns }) => ({\n deviceId,\n deviceName: deviceDisplayName({ deviceName, customName }),\n ...(customName ? { customName } : {}),\n appVersion,\n firstSeenTs,\n lastSeenTs,\n fingerprint: fingerprint!,\n scopes: normalizeDeviceScopes(scopes),\n ...(revokedAt !== undefined ? { revokedAt } : {}),\n ...(apns ? { apns: { environment: apns.environment, updatedAt: apns.updatedAt } } : {}),\n }))\n } catch {\n // degraded: report the minimum without token facts\n }\n const update = updateInfo()\n const lanAddresses = localLANIPv4Addresses()\n return {\n protocolVersion: 2,\n serverVersion: SERVER_VERSION,\n pluginVersion: update.currentVersion,\n ...(update.available ? { updateAvailable: true } : {}),\n ...(update.releaseUrl !== null ? { releaseUrl: update.releaseUrl } : {}),\n enabled: currentConfig().enabled === true,\n identityPath: expandHome(currentConfig().devicesPath ?? join(bridgeDataDir(), 'devices-v2.json')),\n pairingReady,\n activeConnections: connections.size,\n historyBufferMax: currentConfig().historyBufferMax ?? 2000,\n lanAddresses,\n local: localStatus(lanAddresses),\n remote: remoteStatus(),\n devices,\n // 设置页「连接诊断」区块的数据源。只带结构化事实(类型 / 分因 /\n // 时间 / 来源哈希),不携带 debug 开关状态,也不含任何凭据。\n diagnostics: {\n startedAt,\n recentFailures: diagnostics.recentFailures(20).map((event) => ({\n ts: event.ts,\n kind: event.kind,\n reason: event.reason,\n ...(event.attemptId !== undefined ? { attemptId: event.attemptId } : {}),\n ...(event.source !== undefined ? { source: event.source } : {}),\n ...(event.closeCode !== undefined ? { closeCode: event.closeCode } : {}),\n ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),\n })),\n },\n }\n }, beginPairing, async (deviceId) => {\n // Settings-page revocation shares the exact path used by the wire-level\n // c2s.device.revoke frame (index.ts revokeDevice helper).\n return await revokeDevice(deviceId)\n }, async (deviceId, customName) => {\n const { devices } = await ready\n if (!devices) throw new Error('device registry unavailable')\n const effectiveName = await devices.setCustomName(deviceId, customName)\n if (effectiveName === null) throw new Error('active device not found')\n const normalized = customName === null ? '' : sanitizeDeviceField(customName, MAX_DEVICE_NAME_CHARS)\n log(`device custom name updated id=${auditLabel(deviceId)} custom=${String(normalized.length > 0)}`)\n return normalized === '' ? null : normalized\n }, async (deviceId, scopes) => {\n const { devices } = await ready\n if (!devices) throw new Error('device registry unavailable')\n const updated = devices.setScopes(deviceId, scopes)\n if (updated === null) throw new Error('active device not found')\n // Scope changes take effect on the next signed connection; disconnect the\n // current socket so stale in-memory authority cannot survive the update.\n for (const connection of [...connections]) {\n if (connection.connectedDeviceId !== deviceId) continue\n connection.terminate()\n connections.delete(connection)\n }\n log(`device scopes updated id=${auditLabel(deviceId)} scopes=${updated.join(',')}`)\n return updated\n }, () => pushGateway.relayTest(), () => pushGateway.selfTest())\n const state: { bridge?: HostBridge } = {}\n let pendingUpgrades = 0\n const authRateLimiter = new AuthRateLimiter()\n\n const readJSONBody = async (req: IncomingMessage, maxBytes = 16 * 1024): Promise<unknown> => {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buffer.length\n if (size > maxBytes) throw new Error('request body too large')\n chunks.push(buffer)\n }\n return JSON.parse(Buffer.concat(chunks).toString('utf8')) as unknown\n }\n\n const handlePair = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n res.setHeader('Content-Type', 'application/json')\n // 来源与 attemptId 在最早处解析:之后的每个拒绝分支都要带上它们。\n const source = requestClientIdentity(req)\n const attemptId = attemptIdFromRequest(req)\n const reportPairRejected = (reason: string): void => {\n diagnostics.record('pair-rejected', reason, {\n source: auditLabel(source),\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n }\n if (!enabledNow()) {\n reportPairRejected('bridge-disabled')\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, error: 'bridge disabled' }))\n return\n }\n if (req.method !== 'POST') {\n reportPairRejected('method-not-allowed')\n res.statusCode = 405\n res.setHeader('Allow', 'POST')\n res.end(JSON.stringify({ ok: false, error: 'POST required' }))\n return\n }\n const admission = authRateLimiter.admit(source)\n if (!admission.ok) {\n reportPairRejected('rate-limited')\n res.statusCode = 429\n res.setHeader('Retry-After', String(Math.max(1, Math.ceil(admission.retryAfterMs / 1_000))))\n res.end(JSON.stringify({ ok: false, error: 'pairing rate limited' }))\n return\n }\n try {\n const { devices, audience } = await ready\n if (!devices || !audience) throw new Error('device authentication unavailable')\n const raw = await readJSONBody(req) as Record<string, unknown>\n if (raw === null || typeof raw !== 'object' || raw.v !== 2) throw new TypeError('protocol v2 required')\n const code = typeof raw.code === 'string' ? raw.code : ''\n const publicKey = typeof raw.publicKey === 'string' ? raw.publicKey : ''\n const deviceName = sanitizeDeviceField(raw.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(raw.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n const deviceId = deviceIdForPublicKey(publicKey)\n if (devices.list().length >= MAX_DEVICES && devices.authorized(deviceId) === undefined) {\n reportPairRejected('registry-full')\n res.statusCode = 409\n res.end(JSON.stringify({ ok: false, error: 'device registry is full' }))\n return\n }\n if (!pairingCodes.consume(code)) {\n const failure = authRateLimiter.recordFailure(source)\n reportPairRejected('invalid-code')\n res.statusCode = failure.blocked ? 429 : 401\n if (failure.retryAfterMs > 0) res.setHeader('Retry-After', String(Math.max(1, Math.ceil(failure.retryAfterMs / 1_000))))\n res.end(JSON.stringify({ ok: false, error: failure.blocked ? 'pairing rate limited' : 'pairing code invalid or expired' }))\n return\n }\n const record = devices.register({\n publicKey,\n deviceName,\n appVersion,\n scopes: normalizeDeviceScopes(raw.scopes),\n }, Date.now())\n authRateLimiter.recordSuccess(source)\n log(`device paired id=${auditLabel(record.deviceId)} source=${auditLabel(source)}`)\n // Only a request that arrived over the LAN TLS listener echoes the pin;\n // Funnel pairings terminate TLS at tailscaled with a public CA cert.\n const overLanTls = (req.socket as { encrypted?: boolean }).encrypted === true\n const tlsFingerprint = overLanTls ? (await lanTlsIdentity)?.fingerprint : undefined\n res.statusCode = 201\n res.end(JSON.stringify({\n ok: true,\n v: 2,\n deviceId: record.deviceId,\n audience,\n scopes: record.scopes ?? [],\n ...(tlsFingerprint ? { tlsFingerprint } : {}),\n }))\n } catch (error) {\n const badRequest = error instanceof SyntaxError || error instanceof TypeError\n reportPairRejected(badRequest ? 'bad-request' : 'internal-error')\n res.statusCode = badRequest ? 400 : 503\n res.end(JSON.stringify({ ok: false, error: error instanceof Error ? error.message : 'pairing failed' }))\n } finally {\n admission.release()\n }\n }\n\n const handleUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {\n // 来源与 attemptId 在 socket 建立之前就确定:任何拒绝分支都必须能回答\n // 「谁、在什么时候、带着哪次尝试」连进来。来源只以哈希出现。\n const source = requestClientIdentity(req)\n const sourceLabel = auditLabel(source)\n const attemptId = attemptIdFromRequest(req)\n const reject = (status: number, reason: string): void => {\n diagnostics.record('upgrade-rejected', reason, {\n source: sourceLabel,\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n rejectUpgrade(socket, status, reason)\n }\n void (async () => {\n try {\n if (!enabledNow()) {\n reject(503, 'bridge disabled')\n return\n }\n if (connections.size + pendingUpgrades >= MAX_CLIENT_CONNECTIONS) {\n reject(429, 'too many connections')\n return\n }\n pendingUpgrades += 1\n // 声明在 try 之外:finally 需要归还一个可能未创建成功的门的名额。\n let gate: ConnectionGate | undefined\n try {\n const { devices, audience } = await ready\n if (!audience || !devices) {\n reject(503, 'bridge degraded')\n return\n }\n const bridge = state.bridge\n if (!bridge) {\n reject(503, 'bridge not ready')\n return\n }\n // The gate is created before the ws exists, so the admission slot is\n // held even when the upgrade callback never runs (socket gone\n // mid-upgrade). Its attach timeout covers that case; once the socket\n // is live, the gate's hello deadline takes over.\n gate = new ConnectionGate({\n source,\n devices,\n audience,\n limiter: authRateLimiter,\n log,\n auditLabel,\n diagnostics,\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n if (!gate.admitted) {\n reject(429, 'authentication rate limited')\n return\n }\n const live = gate\n wss.handleUpgrade(req, socket, head, (ws) => {\n if (auth.audience !== audience || state.bridge !== bridge) {\n live.markDead()\n // 认证期间 bridge 被替换:这个 socket 已不可能服务,记录后关。\n diagnostics.record('upgrade-rejected', 'bridge-changed', {\n source: sourceLabel,\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n ws.close(1012, 'bridge changed')\n return\n }\n try {\n const connection = new BridgeConnection(ws, {\n bridge,\n devices,\n serverVersion: SERVER_VERSION,\n audience,\n log,\n debug: currentConfig().diagnostics?.debug === true,\n source: live.source,\n rateLimiter: authRateLimiter,\n auditLabel,\n diagnostics,\n ...(attemptId !== undefined ? { attemptId } : {}),\n onClosed: (closed) => connections.delete(closed),\n onDeviceAuthenticated: (deviceId) => {\n log(`device authenticated id=${auditLabel(deviceId)} source=${auditLabel(live.source)}`)\n },\n onPushEnrollKey: (enrollKey) => pushGateway.enrollKey(enrollKey),\n // A device that unbinds itself from the app must not leave a\n // second socket (e.g. an older install) still live: the\n // registry tombstone is already written by the handler, so\n // this only drops the sibling sockets.\n onDeviceRevoke: (deviceId, except) => revokeDevice(deviceId, except),\n }, live)\n connections.add(connection)\n } catch (error) {\n live.markDead()\n ws.close(1011, 'connection setup failed')\n throw error\n }\n })\n } finally {\n // The attach window ends here: if no socket was produced (the upgrade\n // callback never ran), the slot goes back immediately instead of\n // waiting for the attach timeout. An attached gate keeps its slot\n // until a settle path or its hello deadline.\n gate?.releaseIfNeverAttached()\n pendingUpgrades -= 1\n }\n } catch (error) {\n log('upgrade failed: ' + String(error), 'warn')\n diagnostics.record('upgrade-rejected', 'internal-error', {\n source: sourceLabel,\n ...(attemptId !== undefined ? { attemptId } : {}),\n detail: error instanceof Error ? error.message : String(error),\n })\n rejectUpgrade(socket, 500, 'internal error')\n }\n })()\n }\n\n const handleHealth = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n await ready\n res.setHeader('Content-Type', 'application/json')\n if (!auth.audience || !auth.devices) {\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, degraded: true }))\n return\n }\n // 只加不删:原有字段(ok/enabled/protocolVersion/serverVersion/\n // dataPlane)语义不变;新增字段描述「此刻为什么连不上」:\n // LAN 相位与端口、数据面相位、TLS 是否重建、启动时间、最近一次失败。\n // 刻意不返回 debug 开关状态与地址候选(health 不该成为扫描器的字典)。\n const local = localTransport.status()\n res.statusCode = 200\n res.end(JSON.stringify(buildHealthPayload({\n startedAt,\n enabled: enabledNow(),\n protocolVersion: 2,\n serverVersion: SERVER_VERSION,\n dataPlaneActive: Boolean(state.bridge),\n localPhase: local.phase,\n localPort: local.port,\n tlsIdentityRegenerated,\n lastFailure: diagnostics.lastFailureSummary(),\n })))\n } catch {\n res.statusCode = 500\n res.end(JSON.stringify({ ok: false }))\n }\n }\n\n const phoneHandlers = {\n health: handleHealth,\n pair: handlePair,\n upgrade: handleUpgrade,\n }\n\n // Independent transport listeners. LAN owns a stable configurable port;\n // Funnel keeps its loopback-only ephemeral origin. They share only the\n // narrow handlers above, so a LAN bind failure cannot take remote access\n // down and neither listener exposes DSH's wider web/API surface.\n //\n // 差分、串行、拆除与状态发布的公共协议住在 TransportReconciler;两个传输\n // 各自的差异(skipWhenDisabled / appliedKey 时机 / 错误分支)在\n // createLocalTransport 与 createRemoteTransport 里显式声明并原样保留——\n // 统一它们会改变一条当前稳定路径上的并发语义,属于单独一轮的决策。\n const localTransport = createLocalTransport({\n handlers: phoneHandlers,\n config: currentConfig,\n tls: loadLanTls,\n log,\n onTransportError: recordTransportError,\n })\n const remoteTransport = createRemoteTransport({\n handlers: phoneHandlers,\n config: currentConfig,\n originURL: () => originURL,\n dataDir,\n log,\n })\n const localStatus = (addresses: readonly string[]): DeepPilotReport['local'] => {\n const state = localTransport.status()\n return {\n ...state,\n endpoints: state.phase === 'online' ? localEndpointURLs(addresses, state.port) : [],\n ...(tlsIdentityRegenerated ? { tlsIdentityRegenerated: true } : {}),\n }\n }\n const remoteStatus = (): RemoteStatus => remoteTransport.status()\n\n const originServer = createPhoneServer(phoneHandlers, undefined, {\n onTransportError: recordTransportError,\n })\n let originURL: string | undefined\n scheduleLocalReconcile = () => localTransport.scheduleReconcile()\n scheduleRemoteReconcile = () => remoteTransport.scheduleReconcile()\n\n ;(ctx as unknown as SubContext).effect(() => {\n scheduleLocalReconcile?.()\n void listen(originServer, 0, '127.0.0.1').then(() => {\n const address = originServer.address()\n if (address && typeof address === 'object') {\n originURL = `http://127.0.0.1:${address.port}`\n scheduleRemoteReconcile?.()\n }\n }, (error: unknown) => log('remote origin failed: ' + String(error), 'warn'))\n return async () => {\n scheduleLocalReconcile = undefined\n scheduleRemoteReconcile = undefined\n await Promise.allSettled([\n closeServer(originServer),\n localTransport.dispose(),\n remoteTransport.dispose(),\n ])\n }\n }, 'deeppilot: independent transports')\n\n const sweep = setInterval(() => {\n const now = Date.now()\n for (const connection of connections) {\n if (connection.isStale(now, 60_000)) {\n log('dropping stale connection')\n connection.closeIdle()\n connections.delete(connection)\n }\n }\n }, 30_000)\n ;(ctx as unknown as SubContext).effect(() => () => clearInterval(sweep), 'deeppilot: stale sweep')\n\n // Build the bridge's stable protocol-facing adapter from the public\n // Session/Workspace controllers.\n\n /**\n * Whether the resident Gateway Client represents a real phone surface. A\n * paired device remains answerable even while offline: HostBridge retains an\n * authoritative pending snapshot that the app pulls on reconnect, while\n * APNs is only a best-effort wakeup. With no paired device this Client calls\n * `next()`; Gateway's independent official Web delivery is unaffected.\n */\n const hasPairedPhoneSurface = (_kind: DshInteractionKind): boolean => {\n const devices = auth.devices?.list() ?? []\n return devices.some((device) => device.revokedAt === undefined)\n }\n\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['sessionController', 'connection', 'typertGateway'],\n (sub) => {\n if (currentConfig().enabled !== true) {\n log('bridge disabled; data plane stays inactive')\n return\n }\n const apiCtx = sub as unknown as Context & SubContext\n let proxy: DshApiProxy\n try {\n proxy = new DshApiProxy(apiCtx, { shouldSurfaceInteraction: hasPairedPhoneSurface })\n } catch (error) {\n log('DSH session bridge unavailable: ' + String(error), 'warn')\n return\n }\n const bridge = new HostBridge(\n proxy,\n cfg.historyBufferMax,\n join(dataDir, 'prompt-deliveries-v1.json'),\n join(dataDir, 'schedule-mutations-v1.json'),\n join(dataDir, 'fork-mutations-v1.json'),\n log,\n )\n bridge.setPushOutlet(pushGateway)\n state.bridge = bridge\n bridge.start()\n log('data plane active (mux + host streams)')\n apiCtx.effect(() => () => {\n closeConnectionsForBridge(bridge)\n if (state.bridge === bridge) state.bridge = undefined\n bridge.dispose()\n }, 'deeppilot: host streams')\n },\n )\n\n ;(ctx as unknown as SubContext).effect(() => async () => {\n closeAllConnections()\n const bridge = state.bridge\n state.bridge = undefined\n bridge?.dispose()\n updateChecker.dispose()\n await pushGateway.dispose()\n const wssClosed = new Promise<void>((resolve) => wss.close(() => resolve()))\n await Promise.allSettled([wssClosed])\n // 最后收尾诊断落盘:关闭前那次连接失败必须已经在磁盘上。\n await diagnostics.dispose()\n }, 'deeppilot: process resources')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["DEFAULT_CAPACITY","reject","reject","reject","reject","reject","reject","payloadObject","reject","fsConstants","cryptoSign","alert","http2Connect","createHttpServer","createHttpsServer","isPlainObject","record","httpsRequest"],"sources":["../src/wire-errors.ts","../src/connection-policy.ts","../src/binary-frame.ts","../src/auth-rate-limit.ts","../src/diagnostics.ts","../src/device-auth.ts","../src/wire-interaction.ts","../src/wire-device.ts","../src/token.ts","../src/wire-push.ts","../src/wire-schedule.ts","../src/document-payload.ts","../src/host-event-projection.ts","../src/wire-session.ts","../src/wire-command.ts","../src/plugin-update.ts","../src/wire-plugin-update.ts","../src/wire-registry.ts","../src/connection-gate.ts","../src/connection.ts","../src/funnel-policy.ts","../src/tailscale-version.ts","../src/remote-supervisor.ts","../src/local-policy.ts","../src/config.ts","../src/apns.ts","../src/relay-url.ts","../src/relay-client.ts","../src/widget-push.ts","../src/live-activity.ts","../src/push-policy.ts","../src/relay-test.ts","../src/push-gateway.ts","../src/phone-server.ts","../src/transport-reconciler.ts","../src/dispatch-journal.ts","../src/notification-text.ts","../src/host-api.ts","../src/host-capabilities.ts","../src/prompt-queue.ts","../src/assistant-stream.ts","../src/host-bridge.ts","../src/dsh-remote-interactions.ts","../src/command-api.ts","../src/dsh-api-proxy.ts","../src/report-service.ts","../src/report-wire.ts","../src/report-remote.ts","../src/local-address.ts","../src/update-check.ts","../src/phone-http.ts","../src/lan-tls.ts","../src/index.ts"],"sourcesContent":["/**\n * 错误词表(error vocabulary)的唯一属主。\n *\n * 一条失败信息在系统里要过三种词汇:Host 控制器返回的错误码(`session-not-found`、\n * `schedule_conflict`……)、Bridge 结果里的状态、以及 wire 上的 `E_*` 码。\n * 此前这三层各有映射函数(host-bridge 里 5 个、connection-policy 里 2 个、\n * connection.ts 的 switch 里 5 处内联三元链),一处漂移就会让客户端对\n * 「该不该重试」的判断失真。\n *\n * 本 module 只做一次翻译:每个域一张「Host code -> wire code」表,\n * Bridge 的结果直接携带 wire 码。`E_BUSY` 与 `E_PROTOCOL` 决定客户端是否\n * 重试,因此这张表是 wire 行为的一部分,逐码都有测试把守。\n *\n * 规范见 PROTOCOL.md 错误码表(`E_PROTOCOL` = 未知类型或非法 payload,\n * `E_FORBIDDEN` = 权限不足,`E_UNSUPPORTED` = 能力缺失)。\n */\n\n/** wire 错误码及其规范描述;PROTOCOL.md 错误码表的 TS 镜像。 */\nexport const ERROR_CODES = {\n E_AUTH: 'device proof missing or invalid',\n E_FORBIDDEN: 'device scope does not allow this operation',\n E_PROTOCOL: 'unknown type or malformed payload',\n E_NOT_FOUND: 'session or request not found',\n E_BUSY: 'session is busy',\n E_UNSUPPORTED: 'protocol version or capability unsupported',\n E_INTERNAL: 'internal error',\n // 仅 promptQueue 能力下的 c2s.session.queue.update 会返回,旧客户端不会遇到。\n E_STEER_UNAVAILABLE: 'queued item can no longer steer the current turn',\n // 响应会超过客户端能收的单帧上限(目前只有 c2s.session.attachment 返回);不可自动重试。\n E_TOO_LARGE: 'response exceeds the client frame limit',\n} as const\n\nexport type WireErrorCode = keyof typeof ERROR_CODES\n\n/** Bridge 未携带 code 的结果(内部守卫失败等)落到这一档。 */\nexport const WIRE_ERROR_FALLBACK: WireErrorCode = 'E_INTERNAL'\n\n/**\n * 每个域一张「Host code -> wire code」表。查不到的 Host code 归\n * `E_INTERNAL`:未知的 Host 失败对手机没有可执行含义,重试只会放大故障。\n *\n * 表的取值与迁移前的行为逐码一致(见 tests/wire-errors.test.ts 的对等部分),\n * 即此前各映射链的并集——包括 models 域把 `model-unavailable` 映到\n * `E_NOT_FOUND` 的现行取值。\n *\n * 键写的是 DSH 实际抛出的**带命名空间**的码(旧 host 的扁平写法作为兼容别名\n * 逐条保留,它们同时充当「命名空间换了但尾部没换」时的兜底——查表会先原样命中,\n * 再剥前缀命中,见 lookupCode)。\n *\n * schedule 域同时存在两套词表:会话级 Schedule 服务至今抛扁平 snake_case\n * (`schedule_not_found`),而 0.2.2-alpha.3 起的宿主级模型在写入前校验失败时抛\n * 带命名空间的 `schedule/input-invalid` / `schedule/target-archived`。两套都写进\n * 同一张表:查表原样命中优先,剥前缀只作兜底。\n */\nexport type ErrorDomain = 'session' | 'model' | 'schedule' | 'command' | 'queue'\n\nconst DOMAIN_TABLES: Record<ErrorDomain, Readonly<Record<string, WireErrorCode>>> = {\n // 会话/工作区/目录管理域的 Host 错误码。\n session: {\n 'session/not-found': 'E_NOT_FOUND',\n 'workspace/not-found': 'E_NOT_FOUND',\n 'session/agent-busy': 'E_BUSY',\n // writer-held 与 agent-busy 同类:会话正被别人占着写,稍后重试有意义。\n 'session/writer-held': 'E_BUSY',\n 'session/conflict': 'E_BUSY',\n 'workspace/session-active': 'E_BUSY',\n 'gateway/bad-request': 'E_PROTOCOL',\n 'session/title-invalid': 'E_PROTOCOL',\n 'session/attachment-invalid': 'E_PROTOCOL',\n 'session/invalid-time-zone': 'E_PROTOCOL',\n 'workspace/invalid-path': 'E_PROTOCOL',\n 'workspace/name-conflict': 'E_PROTOCOL',\n 'workspace/move-invalid': 'E_PROTOCOL',\n 'workspace-file/not-found': 'E_NOT_FOUND',\n 'directory-picker/unavailable': 'E_UNSUPPORTED',\n // 旧 host 的扁平写法:保留是为了兼容仍在跑的老 DSH。\n 'session-not-found': 'E_NOT_FOUND',\n 'workspace-not-found': 'E_NOT_FOUND',\n 'agent-busy': 'E_BUSY',\n 'session-conflict': 'E_BUSY',\n 'title-invalid': 'E_PROTOCOL',\n 'workspace-invalid-path': 'E_PROTOCOL',\n 'workspace-name-conflict': 'E_PROTOCOL',\n 'directory-unreadable': 'E_PROTOCOL',\n 'directory-exists': 'E_PROTOCOL',\n 'directory-create-failed': 'E_PROTOCOL',\n 'directory-picker-unavailable': 'E_UNSUPPORTED',\n },\n // 模型目录与切换域的 Host 错误码。\n model: {\n 'session/not-found': 'E_NOT_FOUND',\n 'session/agent-busy': 'E_BUSY',\n 'session/writer-held': 'E_BUSY',\n 'session/conflict': 'E_BUSY',\n 'session/model-unavailable': 'E_NOT_FOUND',\n // 旧 host 的扁平写法。\n 'session-not-found': 'E_NOT_FOUND',\n 'agent-busy': 'E_BUSY',\n 'session-conflict': 'E_BUSY',\n 'model-unavailable': 'E_NOT_FOUND',\n },\n // 定时任务域的 Host 错误码(snake_case 是会话级 Schedule 服务的词表,至今未变)。\n schedule: {\n 'schedule_not_found': 'E_NOT_FOUND',\n 'delivery_cursor_not_found': 'E_NOT_FOUND',\n 'schedule_conflict': 'E_BUSY',\n // DSH 0.2.2-alpha.3 的宿主级模型新增:手动执行已有一次进行中(或仍在投递\n // 下限里),重试同一帧不会推进,按 E_BUSY 交给客户端提示稍后。\n 'schedule_execution_pending': 'E_BUSY',\n // 目标会话已归档:投递永远到不了它,重试无意义,与 schedule_not_found 同档。\n 'schedule/target-archived': 'E_NOT_FOUND',\n // 创建/编辑/执行在写入前校验失败(title/prompt/timing/target 形状):请求\n // 参数与声明不符,与其余 payload 类拒绝同档,客户端改参数才有意义。\n 'schedule/input-invalid': 'E_PROTOCOL',\n 'invalid_prompt': 'E_PROTOCOL',\n 'invalid_selector': 'E_PROTOCOL',\n 'invalid_rule': 'E_PROTOCOL',\n 'invalid_time_zone': 'E_PROTOCOL',\n 'not_future': 'E_PROTOCOL',\n 'time_out_of_range': 'E_PROTOCOL',\n 'frequency_too_high': 'E_PROTOCOL',\n 'schedule_ended': 'E_PROTOCOL',\n // DSH 0.2.1-alpha.1 新增:目标会话属于子代理路由,投递永远到不了它,\n // Host 对 create/update 拒绝(delete 仍放行以便清理旧数据)。\n // 归 E_PROTOCOL 而非 E_UNSUPPORTED:能力本身在宿主上存在,是这次请求\n // 的目标不可满足,重试无意义。与其余「参数/时序不合法」类拒绝同档。\n 'subagent_session': 'E_PROTOCOL',\n },\n // 手机指令域的 Host 错误码。这一域一半是插件在 command-api.ts 自己造的码\n // (`command/not-found` 等),一半是上游 Gateway 的原样拒绝(`gateway/*`、\n // `session/*`)。收进同一张表,理由与别的域相同:HostBridge.commandOperation\n // 此前把整域无条件压成 E_INTERNAL,手机端于是分不清「这个指令不存在」和\n // 「主机内部错误」,只能显示一句通用失败,客户端也没法据此分支。\n command: {\n 'command/not-found': 'E_NOT_FOUND',\n // 目录过期:用户选中的定义已被替换或删除。归 E_NOT_FOUND 与既有\n // `model-unavailable` 同惯例——目标不可用,重试无意义;客户端应重拉目录后\n // 让用户重选,而不是按 E_BUSY 去自动重试。\n 'command/definition-changed': 'E_NOT_FOUND',\n // 该指令不接受附件:请求参数与指令自身的声明不符,与其余 payload 类拒绝同档。\n 'command/attachments-unsupported': 'E_PROTOCOL',\n // 主机没有生成归档,或归档内容类型不符:能力在,是这一次失败,重试有意义。\n 'command/export-unavailable': 'E_INTERNAL',\n // 导出被取消或超时:没有上游码可依,保持 E_INTERNAL 的语义。\n 'command/export-cancelled': 'E_INTERNAL',\n // 主机连命令 API 都没挂载:能力缺失,客户端不该按「稍后重试」处理。\n 'command/export-unsupported': 'E_UNSUPPORTED',\n // 主机返回的目录/执行/反馈结果不合约定的形状:主机侧违约,客户端修不了。\n 'command/invalid-response': 'E_INTERNAL',\n // 主机没挂载权限预设服务:能力缺失,与 directory-picker/unavailable 同档。\n 'command/permissions-unsupported': 'E_UNSUPPORTED',\n // 上游 Gateway 在指令路径上的原样拒绝。\n 'gateway/bad-request': 'E_PROTOCOL',\n 'session/not-found': 'E_NOT_FOUND',\n 'session/agent-busy': 'E_BUSY',\n 'session/writer-held': 'E_BUSY',\n 'session/conflict': 'E_BUSY',\n // 旧 host 的扁平写法。\n 'session-not-found': 'E_NOT_FOUND',\n 'agent-busy': 'E_BUSY',\n 'session-conflict': 'E_BUSY',\n },\n // 运行中排队与转向域(DSH session.updateQueue)。\n queue: {\n // 项已被领取、删除或从未存在;Host 对已消失的会话也回这个码。重试无意义,\n // 客户端以下一帧 s2c.session.queue 为准。\n 'session/queue-item-not-found': 'E_NOT_FOUND',\n // 当前轮已结束,或该项已在转向区:项通常仍在队列里,自动重试同样不会成功。\n // 用专用码而不是 E_NOT_FOUND,见 app-private ADR-0011 第 5 条。\n 'session/steer-unavailable': 'E_STEER_UNAVAILABLE',\n // 子代理会话由父会话路由占用:沿用会话域的映射。\n 'session/agent-busy': 'E_BUSY',\n 'session/writer-held': 'E_BUSY',\n 'gateway/bad-request': 'E_PROTOCOL',\n },\n}\n\n/**\n * 一个 Host 失败在 Bridge 侧的形状。\n *\n * `details` 是 DSH 的 `RemoteError` 附带的结构化载荷(dsh-typert-protocol 的\n * `remote-error.d.ts`),键由错误码决定;prompt 准入那一路的 `reason` 装的就是\n * 被包装掉的真实原因。窄化成只读 `reason` 是因为**只有**这一路会用到它。\n */\nexport interface HostFailure {\n code: string\n message?: string\n readonly details?: { readonly reason?: unknown } | undefined\n}\n\n/**\n * 剥掉 `namespace/` 前缀:`session/agent-busy` -> `agent-busy`。\n *\n * DSH 的 API 层现在抛的是带命名空间的码(`session/agent-busy`、\n * `workspace/not-found`、`gateway/internal`……),而下面的表一开始只有扁平写法。\n * 两边对不上时整表失配、一律落进 `E_INTERNAL`,于是 `E_BUSY` 永远出不来——\n * 而 `E_BUSY` 正是客户端决定「该不该重试」的那个码(issue #26)。\n */\nexport function canonicalHostCode(code: string): string {\n const slash = code.lastIndexOf('/')\n return slash === -1 ? code : code.slice(slash + 1)\n}\n\n/** 查表顺序:原样命中优先,其次剥掉命名空间后命中,最后才是兜底。 */\nfunction lookupCode(domain: ErrorDomain, code: string): WireErrorCode {\n const table = DOMAIN_TABLES[domain]\n return table[code] ?? table[canonicalHostCode(code)] ?? WIRE_ERROR_FALLBACK\n}\n\n/** 一个 Host 失败翻译成 Bridge 的失败结果:直接携带 wire 错误码。 */\nexport function wireErrorOf(\n domain: ErrorDomain,\n error: HostFailure,\n): { ok: false; code: WireErrorCode; message: string } {\n const code = lookupCode(domain, error.code)\n return {\n ok: false,\n code,\n message: describeFailure(error, code),\n }\n}\n\n/**\n * 把任意抛出物收敛成 `HostFailure`,供 `catch` 分支复用同一张域表。\n *\n * 指令链路里有两种失败来源:`DshApiProxy.call` 把插件的失败码放在\n * `{result:{ok:false,error}}` 里正常返回,而导出的归档流是直接抛出的\n * (`AbortError`、主机侧 `Error` 都算)。把「取形状」和「查表」分开,是为了让\n * 两条路都只经一次翻译;没有 code 的抛出物用调用方给的 fallback,避免把\n * `undefined` 当码送进 lookupCode 而落进无法解释的兜底。\n */\nexport function hostFailureOf(error: unknown, fallbackCode: string): HostFailure {\n const value = error as { code?: unknown; message?: unknown } | null | undefined\n const code = typeof value?.code === 'string' && value.code.length > 0 ? value.code : fallbackCode\n const message = typeof value?.message === 'string' && value.message.length > 0 ? value.message : undefined\n return message === undefined ? { code } : { code, message }\n}\n\n/**\n * 组装给手机看的那句话。\n *\n * DSH 把 prompt 准入的**任意**内部失败包成 `session/agent-busy`,真实原因只留在\n * `details.reason`(`@deepseek-ai/dsh-api-session-controller` 的 `types/commands.js`\n * 里那一句 `throw new RemoteError('session/agent-busy', 'prompt rejected', { reason: … })`)。\n * 插件原先只取 `message`,于是手机端永远只显示笼统的「发送未确认」。\n *\n * 只在落到 `E_INTERNAL` 时追加 reason:这类码按定义对客户端没有可执行含义,\n * 多这一句不改变「该不该重试」的判断,却能让用户和排查者看到真正的原因。已映射\n * 的码本身已经可执行,保持原样不动。\n */\nfunction describeFailure(error: HostFailure, wireCode: WireErrorCode): string {\n const base = error.message ?? error.code\n if (wireCode !== WIRE_ERROR_FALLBACK) return base\n const reason = error.details?.reason\n if (typeof reason !== 'string' || reason.length === 0) return base\n return `${base} (${error.code}): ${reason}`\n}\n\n/** 只有一个 Host code(没有附带 message)时取它的 wire 码。 */\nexport function wireCodeFor(domain: ErrorDomain, hostCode: string | undefined): WireErrorCode {\n if (hostCode === undefined) return WIRE_ERROR_FALLBACK\n return lookupCode(domain, hostCode)\n}\n\n/**\n * 回答待决 approval/question 的三种结局。保留三分而不是压成布尔:\n * 「没有这条待决」与「Host 拒收了答案」必须让客户端分得清,否则手机上\n * 任何拒绝都显示成 question not pending。\n */\nexport type PendingResponseReason = 'not-pending' | 'bad-response' | 'transport'\n\nexport function pendingResponseErrorCode(reason: PendingResponseReason): WireErrorCode {\n switch (reason) {\n case 'not-pending': return 'E_NOT_FOUND'\n // Host 拒收了答案批次(形状/标签不匹配)——客户端载荷问题,不是待决消失。\n case 'bad-response': return 'E_PROTOCOL'\n case 'transport': return 'E_INTERNAL'\n }\n}\n\n/** 与 pendingResponseErrorCode 配套的可读信息,同一处维护。 */\nexport function pendingResponseMessage(\n kind: 'approval' | 'question',\n reason: PendingResponseReason,\n): string {\n switch (reason) {\n case 'not-pending': return kind + ' not pending'\n case 'bad-response': return kind + ' answer rejected by host: answer does not match the asked questions'\n case 'transport': return 'host connection failed while answering ' + kind\n }\n}\n","/**\n * 连接层常量与载荷清洗。\n *\n * 本 module 的职责已经收窄:scope 表随 wire 注册表(每帧一行)迁到\n * wire-registry,错误码映射随错误词表迁到 wire-errors,请求形状校验随行内\n * validate 迁到各特性模块。剩下的都是与帧类型无关的东西——资源上限、\n * 清洗函数、envelope 形状守卫、下行广播权限。\n */\n\nimport type { DeviceScope } from './device-auth.ts'\nimport type { Envelope } from './protocol.ts'\n\nexport const AUTH_TIMEOUT_MS = 35_000\nexport const MAX_OUTBOUND_BUFFER_BYTES = 4 * 1024 * 1024\n/**\n * 单帧下行上限:未声明 `maxFrameBytes` 的旧 App 用 URLSessionWebSocketTask 的\n * 默认 `maximumMessageSize`(1 MiB),收到更大的帧会以 EMSGSIZE 失败并断线重连。\n */\nexport const LEGACY_CLIENT_MAX_FRAME_BYTES = 1024 * 1024\n/**\n * 附件回读单帧的硬上限,与客户端声明的上限取小。发送前会先等发送缓冲排空,\n * 但帧本身仍整块进缓冲;留出一半给同时到达的流式帧,避免越过\n * MAX_OUTBOUND_BUFFER_BYTES 被当成慢客户端以 1013 断开。更大的附件要等分块传输。\n */\nexport const MAX_ATTACHMENT_FRAME_BYTES = MAX_OUTBOUND_BUFFER_BYTES / 2\n/** 附件 ack 在 `{mediaType,data}` 之外的信封字段(v/type/id/ts)余量。 */\nexport const ATTACHMENT_ENVELOPE_HEADROOM_BYTES = 1024\n/**\n * Pre-auth frame cap. The 64 MiB cap on a fully authenticated socket exists\n * to support multi-MB image attachments; before hello the only legal frames\n * are c2s.ping and c2s.auth.prove, neither of which can legitimately exceed\n * a few KB. Capping unauthenticated frames at 64 KiB keeps an anonymous TCP\n * peer from forcing expensive JSON.parse work on a 64 MiB payload inside\n * the 5-second auth window.\n */\nexport const PRE_AUTH_FRAME_BYTES = 64 * 1024\nexport const IMAGE_MEDIA_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif'])\nexport const MAX_PROMPT_IMAGES = 4\nexport const MAX_BASE64_CHARS_PER_IMAGE = 8 * 1024 * 1024\nexport const MAX_PROMPT_DOCUMENTS = 4\nexport const MAX_DOCUMENT_TEXT_CHARS = 256 * 1024\nexport const MAX_DOCUMENT_NAME_CHARS = 180\nexport const MAX_DOCUMENT_MEDIA_TYPE_CHARS = 120\n/** Bounds a single prompt's text; the frame itself is capped by ws maxPayload. */\nexport const MAX_PROMPT_TEXT_CHARS = 256 * 1024\n// Client-supplied identity fields land in logs and devices-v2.json — keep them\n// short and free of control characters so they can neither flood the registry\n// nor forge log lines.\nexport const MAX_DEVICE_ID_CHARS = 128\nexport const MAX_DEVICE_NAME_CHARS = 64\nexport const MAX_APP_VERSION_CHARS = 32\n\n/**\n * clientSendId / clientRequestId 的形态:13 位纪元前缀 + UUID。\n * wire 层据此校验载荷,journal 侧据此守卫(见 dispatch-journal.ts)。\n */\nexport function validSendId(id: unknown): id is string {\n return typeof id === 'string' && /^\\d{13}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)\n}\n\nexport function sanitizeDeviceField(value: unknown, maxChars: number): string {\n const raw = typeof value === 'string' ? value : String(value ?? '')\n return raw.replace(/[\\u0000-\\u001f\\u007f]/g, ' ').trim().slice(0, maxChars)\n}\n\n/**\n * Runtime shape guard for a frame after JSON.parse. The old `as Envelope`\n * cast alone let JSON `null` reach `env.v` and let a missing or mistyped\n * `type` reach the registry lookup's string operations — one anonymous frame\n * could crash the host process. Reject anything that is not a plain object\n * with a numeric version and a non-empty string type, so field access below\n * is always safe. The payload is intentionally opaque here; each row\n * validates its own payload shape (see the per-row `validate` in\n * wire-registry.ts).\n */\nexport function isEnvelope(value: unknown): value is Envelope {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const frame = value as Record<string, unknown>\n if (typeof frame.v !== 'number') return false\n if (typeof frame.type !== 'string' || frame.type.length === 0) return false\n if (frame.id !== undefined && typeof frame.id !== 'string') return false\n if (frame.ts !== undefined && typeof frame.ts !== 'number') return false\n if (frame.seq !== undefined && typeof frame.seq !== 'number') return false\n return true\n}\n\n/**\n * 下行广播与重放授权。未知帧类型 fail-closed:不下发。\n *\n * `s2c.session.tail` 与 `s2c.history.page` 是 PROTOCOL.md 下行权限表里的类型,\n * 但由连接点对点直推(不经 record()/重放环)。条目保留:将来任何把它们送进\n * ring 的路径都会自动受到这里的 gate 约束,而不是静默全量广播。\n */\nconst PUSH_SCOPE_BY_TYPE: Partial<Record<string, DeviceScope>> = {\n 's2c.session.event': 'sessions.read',\n 's2c.session.delta': 'sessions.read',\n 's2c.plugin.update.status': 'sessions.read',\n 's2c.plugin.update.progress': 'sessions.manage',\n 's2c.session.queue': 'sessions.read',\n 's2c.skills.changed': 'sessions.read',\n 's2c.commands.changed': 'sessions.read',\n 's2c.models.changed': 'sessions.read',\n 's2c.sessions.delta': 'sessions.read',\n 's2c.session.tail': 'sessions.read',\n 's2c.history.page': 'sessions.read',\n 's2c.pending.approval': 'interactions.respond',\n 's2c.pending.question': 'interactions.respond',\n 's2c.pending.cleared': 'interactions.respond',\n 's2c.schedule.changed': 'schedule.manage',\n}\n\nexport function pushScopeFor(type: string, payload?: unknown): DeviceScope | undefined {\n const fixed = PUSH_SCOPE_BY_TYPE[type]\n if (fixed !== undefined) return fixed\n if (type === 's2c.notify') {\n const category = (payload as { category?: unknown } | undefined)?.category\n if (category === 'approval.required' || category === 'question.asked') return 'interactions.respond'\n if (category === 'turn.completed' || category === 'session.error') return 'sessions.read'\n }\n return undefined\n}\n\nexport function sanitizeImageName(value: string): string {\n return value.replace(/[\\u0000-\\u001F\\u007F]/g, '').trim().slice(0, 120)\n}\n\nexport function sanitizeDocumentField(value: string, maxChars: number): string {\n return value.replace(/[\\u0000-\\u001F\\u007F]/g, ' ').trim().slice(0, maxChars)\n}\n","/**\n * 二进制帧(binaryFrames 能力)的线格式与上限。\n *\n * 为什么有它:会话导出块和附件图片原本以 base64 放进 JSON 文本帧,线上多 1/3\n * 体积,手机还要先解 JSON 再解 base64。二进制帧把原始字节直接放在帧尾:\n *\n * [格式 u8 = 1][头部长度 u32 大端][头部 JSON(UTF-8)][原始字节]\n *\n * 头部就是普通 envelope(v/type/id/ts/payload),只是 payload 里不再有 data\n * 字段,请求 id 关联、诊断都沿用文本帧的规则。只有客户端在请求里带\n * `binary: true` 时 Bridge 才回这种帧;旧客户端永远收不到。\n */\n\n/** 帧首字节。客户端遇到未知值直接丢弃该帧,留给以后扩展。 */\nexport const BINARY_FRAME_FORMAT = 1\n\n/** 二进制模式下导出与附件的单块字节数。连接在发送缓冲降到 256 KiB 以下才发\n * 下一块,缓冲峰值约 512 KiB,远低于 4 MiB 的慢客户端断开线。 */\nexport const BINARY_CHUNK_BYTES = 256 * 1024\n\n/** 二进制模式下单张附件原图的上限,对应 App 既有的 16 MiB base64 上限\n * (16 MiB × 3/4)。超过回 E_TOO_LARGE,App 显示占位图。 */\nexport const MAX_BINARY_ATTACHMENT_BYTES = 12 * 1024 * 1024\n\n/** 把 envelope 头部与原始字节拼成一帧。 */\nexport function encodeBinaryFrame(header: object, body: Uint8Array): Buffer {\n const headerBytes = Buffer.from(JSON.stringify(header), 'utf8')\n const frame = Buffer.allocUnsafe(5 + headerBytes.length + body.length)\n frame.writeUInt8(BINARY_FRAME_FORMAT, 0)\n frame.writeUInt32BE(headerBytes.length, 1)\n headerBytes.copy(frame, 5)\n frame.set(body, 5 + headerBytes.length)\n return frame\n}\n\n/** 解析一帧;格式不对返回 undefined。插件自身不收二进制帧,这里供测试与工具复用。 */\nexport function decodeBinaryFrame(frame: Uint8Array): { header: unknown; body: Buffer } | undefined {\n const buffer = Buffer.from(frame.buffer, frame.byteOffset, frame.byteLength)\n if (buffer.length < 5 || buffer.readUInt8(0) !== BINARY_FRAME_FORMAT) return undefined\n const headerLength = buffer.readUInt32BE(1)\n if (5 + headerLength > buffer.length) return undefined\n try {\n return { header: JSON.parse(buffer.subarray(5, 5 + headerLength).toString('utf8')), body: buffer.subarray(5 + headerLength) }\n } catch {\n return undefined\n }\n}\n","export interface AuthRatePolicy {\n windowMs: number\n attemptsPerSource: number\n globalAttempts: number\n maxUnauthenticatedPerSource: number\n failureWindowMs: number\n failuresBeforeBlock: number\n blockMs: number\n maxSources: number\n}\n\nexport const DEFAULT_AUTH_RATE_POLICY: AuthRatePolicy = {\n windowMs: 60_000,\n attemptsPerSource: 12,\n globalAttempts: 120,\n maxUnauthenticatedPerSource: 2,\n failureWindowMs: 10 * 60_000,\n failuresBeforeBlock: 5,\n blockMs: 15 * 60_000,\n maxSources: 4_096,\n}\n\ninterface SourceState {\n attempts: number[]\n failures: number[]\n blockedUntil: number\n active: number\n lastSeen: number\n}\n\nexport interface AuthAdmission {\n ok: boolean\n retryAfterMs: number\n release: () => void\n}\n\nexport interface AuthFailureResult {\n blocked: boolean\n newlyBlocked: boolean\n retryAfterMs: number\n}\n\nconst noop = (): void => {}\n\n/** Bounded in-memory protection for anonymous authentication attempts. */\nexport class AuthRateLimiter {\n private readonly sources = new Map<string, SourceState>()\n private globalAttempts: number[] = []\n\n constructor(private readonly policy: AuthRatePolicy = DEFAULT_AUTH_RATE_POLICY) {}\n\n admit(source: string, now = Date.now()): AuthAdmission {\n const state = this.source(source, now)\n if (state === null) return { ok: false, retryAfterMs: this.policy.windowMs, release: noop }\n this.prune(state, now)\n state.lastSeen = now\n\n if (state.blockedUntil > now) {\n return { ok: false, retryAfterMs: state.blockedUntil - now, release: noop }\n }\n if (state.active >= this.policy.maxUnauthenticatedPerSource) {\n return { ok: false, retryAfterMs: this.policy.windowMs, release: noop }\n }\n if (state.attempts.length >= this.policy.attemptsPerSource) {\n return { ok: false, retryAfterMs: state.attempts[0]! + this.policy.windowMs - now, release: noop }\n }\n this.globalAttempts = this.globalAttempts.filter((ts) => ts > now - this.policy.windowMs)\n if (this.globalAttempts.length >= this.policy.globalAttempts) {\n return { ok: false, retryAfterMs: this.globalAttempts[0]! + this.policy.windowMs - now, release: noop }\n }\n\n state.attempts.push(now)\n this.globalAttempts.push(now)\n state.active += 1\n let released = false\n return {\n ok: true,\n retryAfterMs: 0,\n release: () => {\n if (released) return\n released = true\n state.active = Math.max(0, state.active - 1)\n },\n }\n }\n\n recordFailure(source: string, now = Date.now()): AuthFailureResult {\n const state = this.source(source, now)\n if (state === null) return { blocked: true, newlyBlocked: false, retryAfterMs: this.policy.blockMs }\n this.prune(state, now)\n state.lastSeen = now\n const wasBlocked = state.blockedUntil > now\n state.failures.push(now)\n if (state.failures.length >= this.policy.failuresBeforeBlock) {\n state.blockedUntil = Math.max(state.blockedUntil, now + this.policy.blockMs)\n }\n return {\n blocked: state.blockedUntil > now,\n newlyBlocked: !wasBlocked && state.blockedUntil > now,\n retryAfterMs: Math.max(0, state.blockedUntil - now),\n }\n }\n\n recordSuccess(source: string, now = Date.now()): void {\n const state = this.sources.get(source)\n if (state === undefined) return\n state.failures = []\n state.blockedUntil = 0\n state.lastSeen = now\n }\n\n private source(source: string, now: number): SourceState | null {\n const existing = this.sources.get(source)\n if (existing !== undefined) return existing\n if (this.sources.size >= this.policy.maxSources) this.pruneSources(now)\n if (this.sources.size >= this.policy.maxSources) return null\n const state: SourceState = { attempts: [], failures: [], blockedUntil: 0, active: 0, lastSeen: now }\n this.sources.set(source, state)\n return state\n }\n\n private prune(state: SourceState, now: number): void {\n state.attempts = state.attempts.filter((ts) => ts > now - this.policy.windowMs)\n state.failures = state.failures.filter((ts) => ts > now - this.policy.failureWindowMs)\n if (state.blockedUntil <= now) state.blockedUntil = 0\n }\n\n private pruneSources(now: number): void {\n const staleBefore = now - Math.max(this.policy.failureWindowMs, this.policy.blockMs)\n for (const [source, state] of this.sources) {\n this.prune(state, now)\n if (state.active === 0 && state.blockedUntil === 0 && state.lastSeen < staleBefore) {\n this.sources.delete(source)\n }\n }\n }\n}\n","/**\n * 连接诊断事件模型(Diagnostics)——插件唯一的「失败可观测」面。\n *\n * 为什么需要独立模块:本插件的日志出口原先是 `apply()` 里的一个闭包\n * (`console.log('[deeppilot] ' + message)`),无时间戳、无级别、无来源;\n * 连接被拒、认证失败、TLS 握手失败都各自静默结束,用户在设置页只看到\n * 「连不上」三个字。把事件模型、节流、环形缓冲与落盘收在这里,目的有三:\n *\n * 1. 一次失败 = 一条结构化事件(时间戳 + kind + reason + 来源哈希 +\n * 可选 attemptId / 关闭码 / 耗时),微信、tailscale、扫描器刷屏时仍\n * 只留可读的十几行;\n * 2. index.ts / connection-gate.ts / phone-server.ts / connection.ts 四个\n * 入口共用同一个 `record(kind, reason, extra)`,失败路径不再各自发明\n * 日志文案;\n * 3. 环形 256 条 + 按天落盘(保留 7 天、目录上限 5MB)让「刚刚那次连不上」\n * 在重启前后都能查;`recent()` / `lastFailure()` 同时供 `/phone/health`\n * 与设置页诊断区块读取。\n *\n * 硬约束(与 AGENTS.md 一致):\n * - 事件里只出现 auditLabel 产生的来源哈希,绝不出现原始 IP;\n * - attemptId 只记录、不校验、不参与任何准入判断;长度 ≤16,字符集\n * `[A-Za-z0-9_-]`,来源不可信(WS query `at` / HTTP header\n * `X-DeepPilot-Attempt`);\n * - 不打印配对码、公钥原文、APNs token、relay 凭据与消息正文。detail\n * 字段只接受代码生成的短文本(如 socket 错误码),最长 160 字符。\n *\n * 与 `diagnostics.debug` 的关系:本模块的事件**无条件记录**。那个开关只\n * 控制现有的 per-frame / 推送日志,失败事件若也绑到它上面,用户不开调试\n * 开关就永远看不到为什么连不上——这正是本轮要修的 P0。\n */\n\nimport { appendFile, mkdir, readdir, stat, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n// ---------- 日志行 ----------\n\n/** 日志级别:失败一律 warn,成功与状态变更 info。 */\nexport type LogLevel = 'info' | 'warn'\n\n/**\n * 统一日志行格式:`[deeppilot] <ISO 时间戳> <LEVEL> <message>`。\n *\n * 时间戳是 P0 的可观测性基线:没有它,用户贴回来的日志无法与手机端时间\n * 对齐,也无法判断「刚才那次」是哪一次。ISO-8601(UTC)避免时区误读。\n */\nexport function formatLogLine(level: LogLevel, message: string, at: number = Date.now()): string {\n return `[deeppilot] ${new Date(at).toISOString()} ${level.toUpperCase()} ${message}`\n}\n\n/**\n * 插件日志函数。单参调用即 info;失败路径显式传 'warn'。\n * 返回值的双参签名可以安全地传给只接受 `(message: string) => void` 的\n * 既有依赖(PushGateway / HostBridge / 两个 transport 行)。\n */\nexport function createLogger(): (message: string, level?: LogLevel) => void {\n return (message: string, level: LogLevel = 'info'): void => {\n console.log(formatLogLine(level, message))\n }\n}\n\n// ---------- 事件模型 ----------\n\n/** 事件类型:覆盖一次握手从传输层到认证层的全部可失败点。 */\nexport type DiagnosticKind =\n /** WS upgrade 在建立 socket 之前被拒(禁用、名额、降级、限流)。 */\n | 'upgrade-rejected'\n /** HTTP 配对请求被拒(码无效、注册表满、限流)。 */\n | 'pair-rejected'\n /** c2s.auth.prove 失败(4401 分因)或 hello 超时(4402)。 */\n | 'auth-failed'\n /** 一条 WS 连接建立成功。 */\n | 'connection-opened'\n /** 非正常关闭(关闭码不是 1000/1001)。 */\n | 'connection-closed'\n /** socket 层 error(ws.on('error') / TLS client error / HTTP clientError)。 */\n | 'connection-error'\n /** Host 身份 / LAN TLS 身份的新建、重建或读取异常。 */\n | 'identity'\n /** 设备注册表落盘失败。 */\n | 'storage'\n\n/** 一条诊断事件。字段刻意保持扁平、可 JSON 序列化。 */\nexport interface DiagnosticEvent {\n /** 事件发生时刻(Unix ms)。 */\n ts: number\n kind: DiagnosticKind\n /** 分因枚举,形如 `nonce-mismatch` / `too-many-connections`;代码内常量。 */\n reason: string\n /** 客户端自带的尝试 id(仅用于串联一次握手的多条日志)。 */\n attemptId?: string\n /** 来源的审计哈希(auditLabel),不是原始 IP。 */\n source?: string\n /** WS 关闭码;只在有 socket 的事件上出现。 */\n closeCode?: number\n /** 从建连到关闭的存活时长(毫秒)。 */\n durationMs?: number\n /** 关闭时这条连接是否已通过认证。 */\n authenticated?: boolean\n /** 代码生成的短说明(socket 错误码等),已去控制字符并截断。 */\n detail?: string\n /** 日志级别;不进入 report wire,只用于本地筛选「最近失败」。 */\n level: LogLevel\n}\n\n/** record 的可选附加信息。 */\nexport interface DiagnosticExtra {\n attemptId?: string\n source?: string\n closeCode?: number\n durationMs?: number\n authenticated?: boolean\n detail?: string\n /** 覆盖默认级别:成功事件(连接建立、host-id 新建)用 'info'。 */\n level?: LogLevel\n}\n\n/**\n * 四个入口(index / connection-gate / phone-server / connection)共用的\n * 最小记录面。依赖方只拿这个接口,单测可以用一个收数组的假实现替换整个\n * 落盘与节流机制。\n */\nexport type DiagnosticsRecorder = Pick<DiagnosticsStore, 'record'>\n\nconst MAX_DETAIL_CHARS = 160\nconst MAX_REASON_CHARS = 64\nconst DEFAULT_CAPACITY = 256\n/** 同 kind + reason + source 在 60 秒内只输出一行。 */\nconst DEFAULT_THROTTLE_MS = 60_000\n/** 日志目录总量上限:超过后停止落盘,但内存环形与终端日志继续工作。 */\nexport const MAX_DIAGNOSTICS_BYTES = 5 * 1024 * 1024\n/** 按天滚动文件保留的日历天数(含今天)。 */\nexport const DIAGNOSTICS_RETENTION_DAYS = 7\n\nconst INFO_KINDS: ReadonlySet<DiagnosticKind> = new Set(['connection-opened'])\n\nfunction defaultLevel(kind: DiagnosticKind): LogLevel {\n return INFO_KINDS.has(kind) ? 'info' : 'warn'\n}\n\n/** 事件文本一律单行:换成空格并去掉控制字符,避免日志被注入换行。 */\nfunction sanitizeText(value: string, maxChars: number): string {\n return value\n .replace(/[\\u0000-\\u001f\\u007f-\\u009f]+/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, maxChars)\n}\n\nfunction normalizeReason(reason: string): string {\n // reason 是代码内常量;仍然收敛一次,防止未来有人把上游错误消息塞进来。\n return sanitizeText(reason, MAX_REASON_CHARS).replace(/\\s+/g, '-')\n}\n\n/**\n * 来源只允许短标识(auditLabel 的 12 位十六进制)。原始 IP 含 `.` / `:`,\n * 会在这一步被丢弃——即便某个调用方忘了哈希,也不会有 IP 落进日志。\n */\nfunction normalizeSource(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n return /^[A-Za-z0-9_-]{1,64}$/.test(value) ? value : undefined\n}\n\n// ---------- attemptId(不可信输入) ----------\n\nexport const ATTEMPT_ID_HEADER = 'x-deeppilot-attempt'\n/** 只允许 URL 安全字符,最长 16:记录用,永不回显或参与判定。 */\nconst ATTEMPT_ID_PATTERN = /^[A-Za-z0-9_-]{1,16}$/\n\n/** 归一化一个客户端提供的 attemptId;不合法即丢弃(不报错、不拒绝请求)。 */\nexport function normalizeAttemptId(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n return ATTEMPT_ID_PATTERN.test(value) ? value : undefined\n}\n\n/**\n * 从一条 HTTP / WS 升级请求里取 attemptId。\n *\n * 优先 HTTP header(真实 HTTP 客户端可自由设置),其次 WS 升级 URL 的\n * `?at=`(浏览器 / URLSession 的 WS 握手无法自定义 header)。两个来源都\n * 经过同一份长度与字符集校验;任何异常(畸形 URL、数组头)都按「没有\n * attemptId」处理——这是纯观测字段,绝不能因为它让握手失败。\n */\nexport function attemptIdFromRequest(request: {\n url?: string | undefined\n headers?: Readonly<Record<string, string | string[] | undefined>> | undefined\n}): string | undefined {\n const header = request.headers?.[ATTEMPT_ID_HEADER]\n const fromHeader = normalizeAttemptId(Array.isArray(header) ? header[0] : header)\n if (fromHeader !== undefined) return fromHeader\n if (typeof request.url !== 'string' || request.url.length === 0) return undefined\n try {\n return normalizeAttemptId(new URL(request.url, 'http://phone.local').searchParams.get('at'))\n } catch {\n return undefined\n }\n}\n\n// ---------- 事件存储 ----------\n\nexport interface DiagnosticsOptions {\n /** 落盘目录(插件数据目录下的 diagnostics/);缺省时只保留内存环形。 */\n dir?: string\n /** 环形缓冲上限,默认 256。 */\n capacity?: number\n /** 同 kind+reason+source 的日志节流窗口,默认 60s。 */\n throttleMs?: number\n /** 时钟注入(测试用)。 */\n now?: () => number\n /** 日志输出注入(测试用);缺省 console.log。 */\n write?: (line: string) => void\n}\n\ninterface ThrottleEntry {\n lastTs: number\n /** 上次输出之后被抑制的事件数;恢复输出时带在行尾。 */\n suppressed: number\n}\n\n/**\n * 内存环形 + 节流日志 + 按天落盘。\n *\n * 职责边界:它不做任何策略判断(不决定是否拒绝连接),只负责把调用方\n * 已经判定的失败事实记下来。因此它可以在四个入口之间安全共用。\n */\nexport class DiagnosticsStore {\n private readonly events: DiagnosticEvent[] = []\n private readonly throttles = new Map<string, ThrottleEntry>()\n private readonly capacity: number\n private readonly throttleMs: number\n private readonly now: () => number\n private readonly write: (line: string) => void\n private readonly dir: string | undefined\n private tail: Promise<void> = Promise.resolve()\n /** 已统计过的文件大小,避免每次 append 前都 stat。 */\n private readonly fileSizes = new Map<string, number>()\n private directoryBytes = 0\n /** 最近一次执行过保留期清理的 UTC 日期(YYYY-MM-DD)。 */\n private prunedDay: string | undefined\n /** 落盘自身失败只报一次:否则磁盘满会把日志刷成新的刷屏源。 */\n private writeFailureReported = false\n\n constructor(options: DiagnosticsOptions = {}) {\n this.capacity = options.capacity ?? DEFAULT_CAPACITY\n this.throttleMs = options.throttleMs ?? DEFAULT_THROTTLE_MS\n this.now = options.now ?? Date.now\n this.write = options.write ?? ((line: string) => { console.log(line) })\n this.dir = options.dir\n }\n\n /**\n * 记录一条事件。事件总是进入内存环形;终端日志与落盘按\n * kind + reason + source 节流(60s 一行,恢复时标注 suppressed 次数)。\n * 返回完整事件,方便调用方测试与断言。\n */\n record(kind: DiagnosticKind, reason: string, extra: DiagnosticExtra = {}): DiagnosticEvent {\n const ts = this.now()\n // attemptId 来自客户端:在这里再收敛一次,任何入口传进来的非法值都丢弃。\n const attemptId = normalizeAttemptId(extra.attemptId)\n const source = normalizeSource(extra.source)\n const event: DiagnosticEvent = {\n ts,\n kind,\n reason: normalizeReason(reason),\n level: extra.level ?? defaultLevel(kind),\n ...(attemptId !== undefined ? { attemptId } : {}),\n ...(source !== undefined ? { source } : {}),\n ...(extra.closeCode !== undefined ? { closeCode: extra.closeCode } : {}),\n ...(extra.durationMs !== undefined ? { durationMs: Math.max(0, Math.round(extra.durationMs)) } : {}),\n ...(extra.authenticated !== undefined ? { authenticated: extra.authenticated } : {}),\n ...(extra.detail !== undefined ? { detail: sanitizeText(extra.detail, MAX_DETAIL_CHARS) } : {}),\n }\n\n this.events.push(event)\n while (this.events.length > this.capacity) this.events.shift()\n\n const throttleKey = `${kind}\\u0000${event.reason}\\u0000${event.source ?? '-'}`\n const previous = this.throttles.get(throttleKey)\n let suppressed = 0\n if (previous !== undefined && ts - previous.lastTs < this.throttleMs) {\n previous.suppressed += 1\n return event\n }\n if (previous !== undefined) suppressed = previous.suppressed\n this.throttles.set(throttleKey, { lastTs: ts, suppressed: 0 })\n\n this.write(formatEventLine(event, suppressed))\n this.persist(event, suppressed)\n return event\n }\n\n /** 最近 `limit` 条事件(时间升序),供 health / report / 测试读取。 */\n recent(limit = 20): DiagnosticEvent[] {\n const size = Math.max(0, Math.trunc(limit))\n return this.events.slice(-size)\n }\n\n /** 最近 `limit` 条失败事件(warn 级),新→旧排序前的升序切片。 */\n recentFailures(limit = 10): DiagnosticEvent[] {\n const failures = this.events.filter((event) => event.level === 'warn')\n const size = Math.max(0, Math.trunc(limit))\n return failures.slice(-size)\n }\n\n /** 最近一条失败事件;没有失败时 undefined。 */\n lastFailure(): DiagnosticEvent | undefined {\n for (let index = this.events.length - 1; index >= 0; index -= 1) {\n const event = this.events[index]\n if (event !== undefined && event.level === 'warn') return event\n }\n return undefined\n }\n\n /** 等价于「诊断面板复制」/health 需要的最近失败摘要。 */\n lastFailureSummary(): Pick<DiagnosticEvent, 'kind' | 'reason' | 'ts'> | undefined {\n const failure = this.lastFailure()\n return failure === undefined ? undefined : { kind: failure.kind, reason: failure.reason, ts: failure.ts }\n }\n\n /** 等待所有排队中的落盘完成(测试与进程收尾用)。 */\n async flush(): Promise<void> {\n await this.tail\n }\n\n /** 进程收尾:不再有新的写入队列,等待既有写入落盘。 */\n async dispose(): Promise<void> {\n await this.flush()\n }\n\n // ---------- 落盘 ----------\n\n /**\n * 追加一行 JSONL。写入串行、失败静默(只报一次),绝不阻塞调用方——\n * 诊断日志不能反过来拖慢或搞崩握手路径。\n */\n private persist(event: DiagnosticEvent, suppressed: number): void {\n const dir = this.dir\n if (dir === undefined) return\n const day = utcDay(event.ts)\n const path = join(dir, `diagnostics-${day}.jsonl`)\n const payload = suppressed > 0 ? { ...event, suppressed } : event\n const line = JSON.stringify(payload) + '\\n'\n const bytes = Buffer.byteLength(line)\n\n this.tail = this.tail.then(async () => {\n try {\n await this.prepareDirectory(dir, day)\n const knownSize = this.fileSizes.get(path)\n const currentSize = knownSize ?? await stat(path).then((info) => info.size).catch(() => 0)\n this.fileSizes.set(path, currentSize)\n if (this.directoryBytes + bytes > MAX_DIAGNOSTICS_BYTES) return\n await mkdir(dir, { recursive: true, mode: 0o700 })\n await appendFile(path, line, { mode: 0o600 })\n this.fileSizes.set(path, currentSize + bytes)\n this.directoryBytes += bytes\n } catch (error) {\n this.reportWriteFailure(error)\n }\n })\n this.tail = this.tail.catch(() => {})\n }\n\n /**\n * 每天第一次落盘时清理保留期之外的文件并统计目录大小;同时限制目录\n * 总量(5MB)。清理失败不影响当次写入。\n */\n private async prepareDirectory(dir: string, day: string): Promise<void> {\n if (this.prunedDay === day) return\n const names = await readdir(dir).catch(() => [] as string[])\n const cutoffDay = utcDayMinus(day, DIAGNOSTICS_RETENTION_DAYS - 1)\n let total = 0\n for (const name of names) {\n const match = /^diagnostics-(\\d{4}-\\d{2}-\\d{2})\\.jsonl$/.exec(name)\n if (match === null) continue\n const full = join(dir, name)\n if (match[1] !== undefined && match[1] < cutoffDay) {\n await unlink(full).catch(() => {})\n this.fileSizes.delete(full)\n continue\n }\n const size = await stat(full).then((info) => info.size).catch(() => 0)\n this.fileSizes.set(full, size)\n total += size\n }\n this.directoryBytes = total\n this.prunedDay = day\n }\n\n private reportWriteFailure(error: unknown): void {\n if (this.writeFailureReported) return\n this.writeFailureReported = true\n this.write(formatLogLine('warn', 'diagnostics write failed: ' + sanitizeText(String(error), MAX_DETAIL_CHARS)))\n }\n}\n\n/** 一条事件的终端日志文本(不含前缀时间戳,由 formatLogLine 统一加)。 */\nfunction formatEventLine(event: DiagnosticEvent, suppressed: number): string {\n const parts: string[] = [event.kind, event.reason]\n if (event.source !== undefined) parts.push('src=' + event.source)\n if (event.attemptId !== undefined) parts.push('attempt=' + event.attemptId)\n if (event.closeCode !== undefined) parts.push('code=' + String(event.closeCode))\n if (event.durationMs !== undefined) parts.push('durationMs=' + String(event.durationMs))\n if (event.authenticated !== undefined) parts.push('auth=' + (event.authenticated ? 'yes' : 'no'))\n if (suppressed > 0) parts.push('suppressed=' + String(suppressed))\n if (event.detail !== undefined) parts.push('detail=' + event.detail)\n return formatLogLine(event.level, parts.join(' '), event.ts)\n}\n\nfunction utcDay(ts: number): string {\n return new Date(ts).toISOString().slice(0, 10)\n}\n\n/** 日期字符串(YYYY-MM-DD)往前推 `days` 天;保留期计算用。 */\nfunction utcDayMinus(day: string, days: number): string {\n const date = new Date(day + 'T00:00:00.000Z')\n date.setUTCDate(date.getUTCDate() - days)\n return date.toISOString().slice(0, 10)\n}\n\n// ---------- /phone/health 载荷 ----------\n\nexport interface HealthPayloadInput {\n /** 插件 apply() 的启动时刻。 */\n startedAt: number\n enabled: boolean\n protocolVersion: number\n serverVersion: string\n /** 数据面(HostBridge)是否已经挂上。 */\n dataPlaneActive: boolean\n /** LAN 传输相位与端口;来自 TransportReconciler.status()。 */\n localPhase: string\n localPort: number\n /** 本次进程是否重建过 LAN TLS 身份。 */\n tlsIdentityRegenerated: boolean\n /** 最近一次失败;没有则为 undefined。 */\n lastFailure: Pick<DiagnosticEvent, 'kind' | 'reason' | 'ts'> | undefined\n}\n\n/**\n * `/phone/health` 的响应体:**只加不删**。\n *\n * 原有 `ok/enabled/protocolVersion/serverVersion/dataPlane` 保持原义;\n * 新增字段只描述「这台电脑此刻能不能被连上」,不含 debug 开关状态,\n * 也不返回局域网地址候选(那是配对页的职责,health 不该成为扫描器\n * 的地址字典):\n * - `local`:LAN 传输此刻的相位与端口;\n * - `dataPlanePhase`:数据面 active / inactive;\n * - `tlsRebuilt`:本次运行是否重建过 TLS 身份(手机据此判断是否需要重新配对);\n * - `startedAt`:插件启动时刻;\n * - `lastFailure`:最近一次失败的类型、分因与时间。\n */\nexport function buildHealthPayload(input: HealthPayloadInput): Record<string, unknown> {\n return {\n ok: true,\n enabled: input.enabled,\n protocolVersion: input.protocolVersion,\n serverVersion: input.serverVersion,\n dataPlane: input.dataPlaneActive,\n dataPlanePhase: input.dataPlaneActive ? 'active' : 'inactive',\n local: { phase: input.localPhase, port: input.localPort },\n tlsRebuilt: input.tlsIdentityRegenerated,\n startedAt: input.startedAt,\n lastFailure: input.lastFailure === undefined\n ? null\n : { kind: input.lastFailure.kind, reason: input.lastFailure.reason, ts: input.lastFailure.ts },\n }\n}\n","import {\n createHash,\n createPublicKey,\n randomBytes,\n timingSafeEqual,\n verify,\n type KeyObject,\n} from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { formatLogLine } from './diagnostics.ts'\n\nexport function getPairingCodeTtlMs(): number {\n return Number(process.env.DEEPPILOT_PAIRING_TTL_MS) || 5 * 60_000\n}\nexport const AUTH_CHALLENGE_TTL_MS = 30_000\n\nexport const DEVICE_SCOPES = [\n 'sessions.read',\n 'prompt.send',\n 'sessions.manage',\n 'interactions.respond',\n 'notifications.register',\n 'schedule.manage',\n] as const\n\nexport type DeviceScope = (typeof DEVICE_SCOPES)[number]\n\nexport const DEFAULT_DEVICE_SCOPES: readonly DeviceScope[] = DEVICE_SCOPES\n\nexport interface PairingGrant {\n code: string\n expiresAt: number\n audience?: string\n}\n\nexport async function loadOrCreateHostAudience(path: string): Promise<string> {\n try {\n const existing = (await readFile(path, 'utf8')).trim()\n if (/^deeppilot:[A-Za-z0-9_-]{22}$/.test(existing)) return existing\n throw new Error(`host audience is malformed at ${path}`)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const audience = 'deeppilot:' + randomBytes(16).toString('base64url')\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, audience + '\\n', { mode: 0o600 })\n // 新建 Host 身份意味着 audience 变了:所有此前配对的设备都会在下一步\n // 认证失败(audience-mismatch)。这是「手机突然全部要重新配对」最直接的\n // 证据,必须留一行;只记事实与路径,不记 audience 原文(可被冒充)。\n console.log(formatLogLine('info', 'host identity created: ' + path + ' (previously paired devices must pair again)'))\n return audience\n}\n\nexport interface AuthChallenge {\n nonce: string\n audience: string\n issuedAt: number\n expiresAt: number\n}\n\nexport interface AuthProofFields extends AuthChallenge {\n deviceId: string\n deviceName: string\n appVersion: string\n resumeCursor?: number\n}\n\nfunction b64urlText(value: string): string {\n return Buffer.from(value, 'utf8').toString('base64url')\n}\n\n/**\n * Cross-language signature input. Text fields are base64url encoded before\n * joining so names cannot create ambiguous separators. Decimal timestamps\n * and cursor values are finite integers, or `-` when the cursor is absent.\n */\nexport function canonicalAuthChallenge(fields: AuthProofFields): Buffer {\n const cursor = fields.resumeCursor === undefined ? '-' : String(fields.resumeCursor)\n return Buffer.from([\n 'deeppilot-auth-v2',\n `device-id:${b64urlText(fields.deviceId)}`,\n `nonce:${fields.nonce}`,\n `audience:${b64urlText(fields.audience)}`,\n `issued-at:${fields.issuedAt}`,\n `expires-at:${fields.expiresAt}`,\n `device-name:${b64urlText(fields.deviceName)}`,\n `app-version:${b64urlText(fields.appVersion)}`,\n `resume-cursor:${cursor}`,\n ].join('\\n'), 'utf8')\n}\n\n/** Accept only an uncompressed ANSI X9.63 P-256 public key (65 bytes). */\nexport function parseP256PublicKey(encoded: string): { key: KeyObject; raw: Buffer } {\n const raw = Buffer.from(encoded, 'base64url')\n if (raw.length !== 65 || raw[0] !== 0x04) {\n throw new TypeError('publicKey must be an uncompressed P-256 X9.63 key')\n }\n // SubjectPublicKeyInfo prefix for id-ecPublicKey / prime256v1 followed by\n // the 65-byte uncompressed point.\n const spkiPrefix = Buffer.from('3059301306072a8648ce3d020106082a8648ce3d030107034200', 'hex')\n const key = createPublicKey({ key: Buffer.concat([spkiPrefix, raw]), format: 'der', type: 'spki' })\n if (key.asymmetricKeyType !== 'ec' || key.asymmetricKeyDetails?.namedCurve !== 'prime256v1') {\n throw new TypeError('publicKey must use P-256')\n }\n return { key, raw }\n}\n\nexport function deviceIdForPublicKey(publicKey: string): string {\n const { raw } = parseP256PublicKey(publicKey)\n return createHash('sha256').update(raw).digest('base64url')\n}\n\nexport function fingerprintForPublicKey(publicKey: string): string {\n const { raw } = parseP256PublicKey(publicKey)\n return createHash('sha256').update(raw).digest('hex')\n}\n\nexport function verifyAuthProof(publicKey: string, fields: AuthProofFields, signature: string): boolean {\n try {\n const { key } = parseP256PublicKey(publicKey)\n const der = Buffer.from(signature, 'base64url')\n if (der.length < 64 || der.length > 80) return false\n return verify('sha256', canonicalAuthChallenge(fields), key, der)\n } catch {\n return false\n }\n}\n\nexport function normalizeDeviceScopes(value: unknown): DeviceScope[] {\n if (!Array.isArray(value)) return [...DEFAULT_DEVICE_SCOPES]\n const allowed = new Set<string>(DEVICE_SCOPES)\n return [...new Set(value.filter((scope): scope is DeviceScope => typeof scope === 'string' && allowed.has(scope)))]\n}\n\n/**\n * 0.8.3 之前的「全部权限」:当时还没有 `schedule.manage`。App 配对时一直申请\n * DeviceScope 的全部取值,设置页也没有收窄权限的入口,所以存量记录恰好等于这五项,\n * 只说明「配对时这项权限还不存在」,不是有意关闭。\n */\nconst LEGACY_FULL_SCOPES: readonly DeviceScope[] = [\n 'sessions.read',\n 'prompt.send',\n 'sessions.manage',\n 'interactions.respond',\n 'notifications.register',\n]\n\n/**\n * 存量设备权限升级:恰好等于旧版全部权限时补上之后新增的默认权限,返回新列表;\n * 其余情况(含任何非完整集合)返回 null,原样保留。只放宽「旧版全量」这一种形态,\n * 避免把将来可能出现的、有意收窄过的记录一并放开。\n */\nexport function upgradeLegacyFullScopes(value: unknown): DeviceScope[] | null {\n if (!Array.isArray(value)) return null\n const current = new Set(value)\n if (current.size !== LEGACY_FULL_SCOPES.length) return null\n if (!LEGACY_FULL_SCOPES.every((scope) => current.has(scope))) return null\n return [...DEFAULT_DEVICE_SCOPES]\n}\n\n/** One active, single-use pairing grant per plugin runtime. */\nexport class PairingCodeManager {\n private active: PairingGrant | null = null\n\n issue(now = Date.now()): PairingGrant {\n const grant = {\n code: randomBytes(24).toString('base64url'),\n expiresAt: now + getPairingCodeTtlMs(),\n }\n this.active = grant\n return { ...grant }\n }\n\n consume(presented: string, now = Date.now()): boolean {\n const active = this.active\n if (active === null || now > active.expiresAt) {\n this.active = null\n return false\n }\n const expected = Buffer.from(active.code)\n const actual = Buffer.from(presented)\n const matches = expected.length === actual.length && timingSafeEqual(expected, actual)\n if (matches) this.active = null\n return matches\n }\n\n invalidate(): void {\n this.active = null\n }\n}\n\nexport function createAuthChallenge(audience: string, now = Date.now()): AuthChallenge {\n return {\n nonce: randomBytes(24).toString('base64url'),\n audience,\n issuedAt: now,\n expiresAt: now + AUTH_CHALLENGE_TTL_MS,\n }\n}\n","/**\n * 交互特性的 wire 行:回答待决的 approval 与 question。\n *\n * 两帧共用 `interactions.respond`。失败的三种结局(没有这条待决 / Host 拒收\n * 答案 / 传输失败)由 wire-errors 的词表翻译成 wire 错误码与可读信息——此前\n * 这条映射住在 connection-policy,和 scope 表、常量混在一起。\n */\n\nimport {\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\nimport { pendingResponseErrorCode, pendingResponseMessage } from './wire-errors.ts'\n\nconst RESPOND: WireFrameRow['scopes'] = ['interactions.respond']\n\nexport const interactionRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.approval.respond',\n stage: 'authenticated',\n scopes: RESPOND,\n doc: 'PROTOCOL.md c2s.approval.respond',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (!isText(p.requestId)) return reject('E_PROTOCOL', 'invalid requestId')\n if (\n !['allow', 'deny'].includes(p.decision as string) ||\n !isOptionalField(p, 'reason', (v) => isText(v, 65536, false))\n ) {\n return reject('E_PROTOCOL', 'invalid approval response')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const decision = payload.decision as 'allow' | 'deny'\n if (decision !== 'allow' && decision !== 'deny') {\n return ctx.fail('E_PROTOCOL', 'requestId and decision required')\n }\n // 可选的拒绝理由必须抵达 Host,模型据此理解工具调用为何被拒。\n const outcome = await ctx.bridge.respondApproval(\n payload.requestId as string,\n decision,\n typeof payload.reason === 'string' ? (payload.reason as string) : undefined,\n )\n if (!outcome.ok) {\n const reason = outcome.reason\n return ctx.fail(pendingResponseErrorCode(reason), pendingResponseMessage('approval', reason))\n }\n ctx.send('s2c.ack', {})\n },\n },\n {\n type: 'c2s.question.respond',\n stage: 'authenticated',\n scopes: RESPOND,\n doc: 'PROTOCOL.md c2s.question.respond',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (!isText(p.requestId)) return reject('E_PROTOCOL', 'invalid requestId')\n if (!Array.isArray(p.answers) || p.answers.length > 100) return reject('E_PROTOCOL', 'invalid answers')\n const ids = new Set<unknown>()\n for (const answer of p.answers as Array<Record<string, unknown>>) {\n if (\n !answer ||\n typeof answer !== 'object' ||\n Array.isArray(answer) ||\n !isText(answer.id) ||\n ids.has(answer.id) ||\n !Array.isArray(answer.selected) ||\n answer.selected.length > 100 ||\n !answer.selected.every((v: unknown) => isText(v, 4096)) ||\n (answer.custom !== undefined && !isText(answer.custom, 65536))\n ) {\n return reject('E_PROTOCOL', 'invalid answer')\n }\n ids.add(answer.id)\n }\n return checked\n },\n handle: async (ctx, payload) => {\n if (!Array.isArray(payload.answers)) return ctx.fail('E_PROTOCOL', 'requestId and answers required')\n const outcome = await ctx.bridge.respondQuestion(payload.requestId as string, payload.answers)\n if (!outcome.ok) {\n const reason = outcome.reason\n return ctx.fail(pendingResponseErrorCode(reason), pendingResponseMessage('question', reason))\n }\n ctx.send('s2c.ack', {})\n },\n },\n]\n","/**\n * 设备生命周期特性的 wire 行:自撤销(解绑)。\n *\n * 该帧不要求任何 scope:设备生命周期操作不能因为 scope 被收窄而无法解绑,\n * 否则它只会永远留在离线推送目标集合里(PROTOCOL.md c2s.device.revoke)。\n * widget 策略取 `handler`:小组件的拒必须是「小组件不得解绑设备」这个具体\n * 理由,通用只读门禁只会说 read-only。\n */\n\nimport {\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\n\nexport const deviceRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.device.revoke',\n stage: 'authenticated',\n scopes: [],\n widgetPolicy: 'handler',\n doc: 'PROTOCOL.md c2s.device.revoke',\n validate: (payload) => {\n // 载荷可整体省略:省略与 `{}` 等价,deviceId 可选。\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n // 校验可选的 deviceId:类型/非空(旧 request-validation 的判定),通过后\n // 才进入 handler 的等值判断。\n if (!isOptionalField(checked.value, 'deviceId', (v) => isText(v))) {\n return reject('E_PROTOCOL', 'invalid deviceId')\n }\n if (checked.value.deviceId !== undefined && typeof checked.value.deviceId !== 'string') {\n return reject('E_PROTOCOL', 'deviceId must be a string')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n if (typeof payload.deviceId === 'string' && payload.deviceId !== deviceId) {\n return ctx.fail('E_FORBIDDEN', 'deviceId does not match the authenticated device')\n }\n if (ctx.widgetClient) {\n return ctx.fail('E_FORBIDDEN', 'widget connection cannot revoke its device')\n }\n // 幂等:已撤销的记录仍然 ack {revoked:true}——设备无法区分首次撤销与\n // 重复撤销,两种情况都不能表现为错误。\n ctx.devices.revoke(deviceId, Date.now())\n ctx.markRevoked()\n // 摘除同设备的其它活跃连接是尽力而为:注册表墓碑已落盘,hook 失败\n // 不能吞掉这次 ack。\n try {\n await ctx.revokeSiblings(deviceId)\n } catch (error) {\n if (ctx.debug) ctx.log('device revoke hook failed: ' + String(error))\n }\n // ack 严格先于关闭,慢客户端才能在 4401 前看到解绑成功。\n ctx.send('s2c.ack', { revoked: true })\n ctx.close(4401, 'device revoked')\n },\n },\n]\n","import type { LiveActivityState } from './protocol.ts'\nimport { MAX_DEVICE_NAME_CHARS, sanitizeDeviceField } from './connection-policy.ts'\nimport { formatLogLine } from './diagnostics.ts'\nimport { randomBytes } from 'node:crypto'\nimport { access, chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, resolve } from 'node:path'\nimport {\n deviceIdForPublicKey,\n fingerprintForPublicKey,\n normalizeDeviceScopes,\n upgradeLegacyFullScopes,\n type DeviceScope,\n} from './device-auth.ts'\n\n/** Expand a leading ~ using the process home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir()\n if (p.startsWith('~/')) return resolve(homedir(), p.slice(2))\n return p\n}\n\nfunction dshDataRoot(): string {\n const dshHome = process.env.DSH_HOME\n if (dshHome && dshHome.trim().length > 0) return resolve(dshHome.trim())\n return resolve(homedir(), '.dsh')\n}\n\n/** DeepPilot data directory: under $DSH_HOME when set, else ~/.dsh. */\nexport function bridgeDataDir(): string {\n return resolve(dshDataRoot(), 'deeppilot')\n}\n\n/** Create or repair the canonical secret-bearing directory as owner-only. */\nexport async function ensurePrivateBridgeDataDir(): Promise<string> {\n const target = bridgeDataDir()\n await mkdir(target, { recursive: true, mode: 0o700 })\n await chmod(target, 0o700)\n return target\n}\n\n/**\n * Move the pre-DeepPilot data directory as one atomic directory rename.\n * Existing canonical data always wins; secrets are never merged or replaced.\n */\nexport async function migrateLegacyBridgeDataDir(): Promise<string | null> {\n const target = bridgeDataDir()\n try {\n await access(target)\n return null\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n\n const legacy = resolve(dshDataRoot(), 'pocket-bridge')\n try {\n await rename(legacy, target)\n return legacy\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\nexport type ApnsEnvironment = 'development' | 'production'\n\n/** Per-device APNs registration (F-9 离线推送). The raw token never leaves the registry file. */\nexport interface DeviceApnsInfo {\n /** Hex device token, lowercased, 32–512 chars. */\n token: string\n environment: ApnsEnvironment\n updatedAt: number\n /** Per-category opt-out mirror; absent keys mean enabled. */\n categories?: Record<string, boolean>\n}\n\nexport interface LiveActivityRegistration extends DeviceApnsInfo {\n activityId: string\n sessionId: string\n expiresAt: number\n endedState?: LiveActivityState\n}\n\nexport interface DeviceRecord {\n deviceId: string\n /** Name reported by the phone. Refreshed on every authenticated hello. */\n deviceName: string\n /** User-assigned name from the DSH settings page; survives app reconnects. */\n customName?: string\n appVersion: string\n /** Uncompressed P-256 X9.63 public key, base64url encoded. */\n publicKey?: string\n /** SHA-256 fingerprint of publicKey, hex encoded for local display. */\n fingerprint?: string\n scopes?: DeviceScope[]\n revokedAt?: number\n firstSeenTs: number\n lastSeenTs: number\n apns?: DeviceApnsInfo\n widgetApns?: DeviceApnsInfo\n liveActivity?: LiveActivityRegistration\n}\n\n/** Name shown to the operator: a user label when present, otherwise the app-reported model name. */\nexport function deviceDisplayName(record: Pick<DeviceRecord, 'deviceName' | 'customName'>): string {\n return record.customName?.trim() ? record.customName : record.deviceName\n}\n\n/** Hex shape of an APNs device token as delivered by iOS (usually 64 chars). */\nconst APNS_TOKEN_PATTERN = /^[0-9a-f]{32,512}$/\n\nexport function isValidApnsToken(token: unknown): token is string {\n return typeof token === 'string' && APNS_TOKEN_PATTERN.test(token)\n}\n\n/** Registry row cap: hello spam with fresh ids must not grow the file forever. */\nexport const MAX_DEVICES = 64\n\n/**\n * Paired-device registry persisted as one JSON document. Whole-document\n * writes (serialized, never interleaved); a corrupt file falls back to an\n * empty registry rather than failing the plugin.\n */\nexport class DeviceStore {\n private devices = new Map<string, DeviceRecord>()\n private flushTail: Promise<void> = Promise.resolve()\n /**\n * 注册表写失败的日志节流:磁盘只读 / 空间不足时每次 flush 都会失败,\n * 不节流会把「写不进去」变成新的刷屏源。同一条错误 60s 只报一次。\n */\n private lastWriteFailureLogAt = 0\n\n private constructor(private readonly filePath: string) {}\n\n static async load(filePath: string): Promise<DeviceStore> {\n const store = new DeviceStore(filePath)\n try {\n const raw = JSON.parse(await readFile(expandHome(filePath), 'utf8')) as {\n devices?: DeviceRecord[]\n }\n let upgraded = 0\n for (const rec of raw.devices ?? []) {\n if (typeof rec.deviceId !== 'string') continue\n // 0.8.3 之前配对的设备只存了当时的五项全量权限,连接时不会补新权限,\n // 自动化任务一直 E_FORBIDDEN(scope schedule.manage required)。载入时一次性补齐并落盘。\n const next = rec.revokedAt === undefined ? upgradeLegacyFullScopes(rec.scopes) : null\n if (next !== null) {\n rec.scopes = next\n upgraded += 1\n }\n store.devices.set(rec.deviceId, rec)\n }\n if (upgraded > 0) {\n console.log(formatLogLine('info', `device scopes upgraded for ${upgraded} legacy device(s): schedule.manage added`))\n void store.flush()\n }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (code === 'ENOENT') {\n // First run on this home: nothing registered yet — normal.\n } else {\n // A truncated/corrupt registry silently wiping pairings and push\n // registrations would be indistinguishable from \"never registered\".\n // Make data loss loud so the cause (crash mid-write, disk, …) shows.\n console.log(formatLogLine('warn', 'device registry unreadable, starting empty: ' + String(error)))\n }\n }\n return store\n }\n\n /** Register one public key after a valid, single-use pairing grant. */\n register(\n record: { publicKey: string; deviceName: string; appVersion: string; scopes?: unknown },\n now: number,\n ): DeviceRecord {\n const deviceId = deviceIdForPublicKey(record.publicKey)\n const existing = this.devices.get(deviceId)\n if (!existing && this.devices.size >= MAX_DEVICES) {\n throw new Error('device registry is full')\n }\n const next: DeviceRecord = {\n deviceId,\n deviceName: record.deviceName,\n ...(existing?.customName ? { customName: existing.customName } : {}),\n appVersion: record.appVersion,\n publicKey: record.publicKey,\n fingerprint: fingerprintForPublicKey(record.publicKey),\n scopes: normalizeDeviceScopes(record.scopes),\n firstSeenTs: existing?.firstSeenTs ?? now,\n lastSeenTs: now,\n ...(existing?.apns ? { apns: existing.apns } : {}),\n ...(existing?.widgetApns ? { widgetApns: existing.widgetApns } : {}),\n ...(existing?.liveActivity ? { liveActivity: existing.liveActivity } : {}),\n }\n this.devices.set(deviceId, next)\n void this.flush()\n return structuredClone(next)\n }\n\n /** Return an active cryptographic identity. */\n authorized(deviceId: string): DeviceRecord | undefined {\n const record = this.devices.get(deviceId)\n if (!record?.publicKey || record.revokedAt !== undefined) return undefined\n return record\n }\n\n /**\n * 认证分因用:把「为什么 authorized() 返回 undefined」拆成三种情形。\n * 只给日志使用,准入判定仍然只走 authorized()——wire 响应不会因为\n * 这个方法泄露设备是否注册过(4401 仍只有一个码)。\n */\n statusOf(deviceId: string): 'active' | 'revoked' | 'unknown' {\n const record = this.devices.get(deviceId)\n if (record === undefined) return 'unknown'\n if (record.revokedAt !== undefined) return 'revoked'\n if (record.publicKey === undefined) return 'unknown'\n return 'active'\n }\n\n markAuthenticated(deviceId: string, deviceName: string, appVersion: string, now: number): void {\n const record = this.authorized(deviceId)\n if (!record) return\n record.deviceName = deviceName || record.deviceName\n record.appVersion = appVersion || record.appVersion\n record.lastSeenTs = now\n void this.flush()\n }\n\n /**\n * Set or clear the operator-facing device label. The phone-reported name is\n * intentionally left untouched so a reconnect cannot erase this value.\n * Returns the effective display name, or null when the device is not active.\n */\n async setCustomName(deviceId: string, customName: string | null): Promise<string | null> {\n const record = this.authorized(deviceId)\n if (!record) return null\n const normalized = customName === null ? '' : sanitizeDeviceField(customName, MAX_DEVICE_NAME_CHARS)\n if (normalized === (record.customName ?? '')) return deviceDisplayName(record)\n const previous = record.customName\n if (normalized === '') delete record.customName\n else record.customName = normalized\n try {\n await this.flush()\n } catch (error) {\n if (previous === undefined) delete record.customName\n else record.customName = previous\n throw error\n }\n return deviceDisplayName(record)\n }\n\n revoke(deviceId: string, now: number): boolean {\n const record = this.devices.get(deviceId)\n if (!record || record.revokedAt !== undefined) return false\n record.revokedAt = now\n delete record.apns\n delete record.widgetApns\n delete record.liveActivity\n void this.flush()\n return true\n }\n\n setScopes(deviceId: string, scopes: unknown): DeviceScope[] | null {\n const record = this.authorized(deviceId)\n if (!record) return null\n record.scopes = normalizeDeviceScopes(scopes)\n void this.flush()\n return [...record.scopes]\n }\n\n list(): DeviceRecord[] {\n return [...this.devices.values()].map((record) => structuredClone(record))\n }\n\n /**\n * Store (or refresh) the APNs registration of a paired device. Idempotent:\n * an unchanged registration does not rewrite the registry file, so the\n * app's re-register-on-every-handshake policy stays write-quiet.\n */\n setPushToken(\n deviceId: string,\n token: string,\n environment: ApnsEnvironment,\n categories: Record<string, boolean> | undefined,\n now: number,\n ): void {\n const normalized = token.toLowerCase()\n if (!isValidApnsToken(normalized)) return\n const record = this.authorized(deviceId)\n if (!record) return\n const next: DeviceApnsInfo = { token: normalized, environment, updatedAt: now }\n if (categories && typeof categories === 'object') {\n const clean: Record<string, boolean> = {}\n for (const [key, value] of Object.entries(categories)) {\n if (/^[a-z.]{1,64}$/.test(key) && typeof value === 'boolean') clean[key] = value\n }\n if (Object.keys(clean).length > 0) next.categories = clean\n }\n const current = record.apns\n if (\n current &&\n current.token === next.token &&\n current.environment === next.environment &&\n JSON.stringify(current.categories ?? {}) === JSON.stringify(next.categories ?? {})\n ) {\n return\n }\n this.detachDuplicateToken('apns', normalized, deviceId)\n record.apns = next\n void this.flush()\n }\n\n /** Drop a device's APNs registration (APNs reported the token unregistered). */\n clearPushToken(deviceId: string): void {\n const record = this.devices.get(deviceId)\n if (!record?.apns) return\n delete record.apns\n void this.flush()\n }\n\n setLiveActivity(deviceId: string, registration: LiveActivityRegistration): void {\n const record = this.authorized(deviceId)\n if (!record || !isValidApnsToken(registration.token)) return\n const old = record.liveActivity\n // Rotation/reconnect cannot extend a round or erase its terminal state.\n record.liveActivity = old?.activityId === registration.activityId\n ? { ...registration, expiresAt: old.expiresAt, endedState: old.endedState }\n : registration\n void this.flush()\n }\n\n endLiveActivity(deviceId: string, token: string, state: LiveActivityState): void {\n const registration = this.devices.get(deviceId)?.liveActivity\n if (!registration || registration.token !== token || registration.endedState) return\n registration.endedState = state\n void this.flush()\n }\n\n clearLiveActivity(deviceId: string, activityId: string, token?: string): void {\n const record = this.devices.get(deviceId)\n if (record?.liveActivity?.activityId !== activityId ||\n (token !== undefined && record.liveActivity.token !== token)) return\n delete record.liveActivity\n void this.flush()\n }\n\n setWidgetPushToken(deviceId: string, token: string, environment: ApnsEnvironment, now: number): void {\n const record = this.authorized(deviceId)\n if (!record || !isValidApnsToken(token)) return\n // Match c2s.push.register: hex tokens are stored lowercased, so an\n // uppercase re-send is the same registration, not a rotation.\n const normalized = token.toLowerCase()\n const previous = record.widgetApns\n if (previous?.token === normalized && previous.environment === environment &&\n now - previous.updatedAt < 60 * 60 * 1000) return\n this.detachDuplicateToken('widgetApns', normalized, deviceId)\n record.widgetApns = { token: normalized, environment, updatedAt: now }\n void this.flush()\n }\n\n /** Compare-and-clear protects a rotated token from a delayed APNs rejection. */\n clearWidgetPushToken(deviceId: string, token: string): void {\n const record = this.devices.get(deviceId)\n if (record?.widgetApns?.token !== token) return\n delete record.widgetApns\n void this.flush()\n }\n\n /**\n * One physical device owns one APNs token, but a re-pair or a botched unbind\n * can leave the same token attached to a second deviceId. Registering it\n * under `keepDeviceId` detaches it from every other record — only that one\n * token field, never the other record's pairing key or scopes. No flush of\n * its own: the caller batches it with the write that follows, so a single\n * registry write covers both changes.\n */\n private detachDuplicateToken(\n field: 'apns' | 'widgetApns',\n token: string,\n keepDeviceId: string,\n ): void {\n for (const record of this.devices.values()) {\n if (record.deviceId === keepDeviceId) continue\n if (field === 'apns') {\n if (record.apns?.token === token) delete record.apns\n } else if (record.widgetApns?.token === token) {\n delete record.widgetApns\n }\n }\n }\n\n /** Serialized so concurrent touches can never interleave half-written JSON. */\n private flush(): Promise<void> {\n const next = this.flushTail.then(() => this.writeFile())\n this.flushTail = next.catch(() => {\n // 真正的写失败已由 writeFile 自己的 catch 记录(含节流)。\n })\n return next\n }\n\n /** Resolves once every queued registry write has landed (test support). */\n async drain(): Promise<void> {\n await this.flushTail\n }\n\n private async writeFile(): Promise<void> {\n const full = expandHome(this.filePath)\n const body = JSON.stringify(\n { version: 2, devices: this.list() },\n null,\n 2,\n )\n try {\n await mkdir(dirname(full), { recursive: true })\n // Atomic replace: a restart mid-write previously left a truncated JSON\n // behind, which load() treats as corrupt and drops ENTIRELY — losing\n // pairings and push registrations on every unlucky shutdown.\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, body + '\\n', { mode: 0o600 })\n await rename(temp, full)\n } catch (error) {\n // best-effort persistence; the bridge still works without it.\n // 但失败本身必须留一行:注册表静默不落盘时,重启后配对消失会被\n // 误认为「设备自己掉了」,真正原因(磁盘 / 权限)只有这里可见。\n this.logWriteFailure(error)\n }\n }\n\n /** 写失败日志按 60s 节流:磁盘满时 flush 会被反复触发,不能让它刷屏。 */\n private logWriteFailure(error: unknown): void {\n const now = Date.now()\n if (now - this.lastWriteFailureLogAt < 60_000) return\n this.lastWriteFailureLogAt = now\n const code = (error as NodeJS.ErrnoException | undefined)?.code\n const message = error instanceof Error ? error.message : String(error)\n console.log(formatLogLine('warn', 'device registry write failed'\n + (typeof code === 'string' ? ' code=' + code : '')\n + ': ' + message.slice(0, 160)))\n }\n}\n","/**\n * 离线推送特性的 wire 行:APNs token 注册(手机与小组件两份)与 Live Activity\n * 注册。\n *\n * 三处需要特别注意的次序,迁移前一模一样保留:\n * 1. 零配置注册(enrollKey)必须早于 push 能力门——首次注册恰恰发生在推送尚未\n * 就绪时,而钥匙本身会把中继模式打开;\n * 2. token 写入必须早于就绪门——推送还在引导中或中继临时不可用时,token 已落盘,\n * 恢复后无需重新注册;\n * 3. 任何 await 之后都要重新读取设备记录:权限可能在等待期间被改窄。\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport { isValidApnsToken } from './token.ts'\nimport {\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\n\n/** Live Activity 需要的三个 scope:通知注册 + 会话读取 + 交互应答。 */\nconst LIVE_ACTIVITY_SCOPES: WireFrameRow['scopes'] = [\n 'notifications.register',\n 'sessions.read',\n 'interactions.respond',\n]\n\n/** 小组件总览需要的三个 scope:与 Live Activity 同组(PROTOCOL.md 下行权限一节)。 */\nconst WIDGET_PUSH_SCOPES: WireFrameRow['scopes'] = [\n 'notifications.register',\n 'sessions.read',\n 'interactions.respond',\n]\n\n/** enrollKey 的清洗:只保留可打印 ASCII,截到 128(与迁移前逐字一致)。 */\nfunction cleanEnrollKey(value: unknown): string {\n return typeof value === 'string' ? value.trim().replace(/[^\\x20-\\x7e]/g, '').slice(0, 128) : ''\n}\n\nexport const pushRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.push.register',\n stage: 'authenticated',\n scopes: ['notifications.register'],\n // 注意:不在此声明 capability 门。零配置注册(enrollKey)必须先于 push\n // 能力门执行——首次注册恰恰发生在推送尚未就绪时。门由 handler 在注册之后\n // 就地检查,与迁移前的次序一致。\n doc: 'PROTOCOL.md c2s.push.register',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n // token 的形状检查原在 handler 内(G3);此处并入校验,接受集不变。\n const token = typeof p.deviceToken === 'string' ? (p.deviceToken as string).trim() : ''\n if (!isValidApnsToken(token)) return reject('E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n if (p.environment !== undefined && p.environment !== 'production' && p.environment !== 'development') {\n return reject('E_PROTOCOL', 'invalid APNs environment')\n }\n if (!isOptionalField(p, 'enrollKey', (v) => isText(v, 128))) return reject('E_PROTOCOL', 'invalid enrollKey')\n if (\n p.categories !== undefined &&\n (typeof p.categories !== 'object' || p.categories === null || Array.isArray(p.categories) ||\n Object.values(p.categories as Record<string, unknown>).some((v) => typeof v !== 'boolean'))\n ) {\n return reject('E_PROTOCOL', 'invalid categories')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n const token = String(payload.deviceToken).trim()\n const environment: ApnsEnvironment = payload.environment === 'production' ? 'production' : 'development'\n const categories = typeof payload.categories === 'object' && payload.categories !== null\n ? (payload.categories as Record<string, boolean>)\n : undefined\n // 零配置注册先于能力门(见文件头说明 1)。\n if (typeof payload.enrollKey === 'string') {\n const enrollKey = cleanEnrollKey(payload.enrollKey)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) await ctx.enrollPushKey(enrollKey)\n }\n // token 先落盘,再评估就绪(见文件头说明 2);await 之后重新读取权限。\n const record = ctx.devices.authorized(deviceId)\n if (!record?.scopes?.includes('notifications.register')) {\n return ctx.fail('E_FORBIDDEN', 'device authorization changed')\n }\n ctx.devices.setPushToken(deviceId, token, environment, categories, Date.now())\n if (ctx.bridge.capabilities.push !== true) {\n if (ctx.debug) ctx.log('push register held: bridge not ready')\n return ctx.fail('E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n if (ctx.debug) ctx.log('push token registered env=' + environment)\n // enabled 让 App 立即翻转本地能力位,不必等下一次握手。\n ctx.send('s2c.ack', { enabled: true })\n },\n },\n {\n type: 'c2s.widget.push.register',\n stage: 'authenticated',\n scopes: WIDGET_PUSH_SCOPES,\n widgetPolicy: 'allowed',\n // 同 c2s.push.register:能力门在 handler 内、注册之后检查。\n doc: 'PROTOCOL.md c2s.widget.push.register',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n const token = typeof p.deviceToken === 'string' ? (p.deviceToken as string).trim() : ''\n if (!isValidApnsToken(token)) return reject('E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n if (p.environment !== undefined && p.environment !== 'production' && p.environment !== 'development') {\n return reject('E_PROTOCOL', 'invalid APNs environment')\n }\n if (!isOptionalField(p, 'enrollKey', (v) => isText(v, 128))) return reject('E_PROTOCOL', 'invalid enrollKey')\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n const token = String(payload.deviceToken).trim()\n const environment: ApnsEnvironment = payload.environment === 'production' ? 'production' : 'development'\n if (typeof payload.enrollKey === 'string') {\n const enrollKey = cleanEnrollKey(payload.enrollKey)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) await ctx.enrollPushKey(enrollKey)\n }\n const record = ctx.devices.authorized(deviceId)\n if (\n !record?.scopes?.includes('notifications.register') ||\n !record.scopes.includes('sessions.read') ||\n !record.scopes.includes('interactions.respond')\n ) {\n return ctx.fail('E_FORBIDDEN', 'device authorization changed')\n }\n ctx.devices.setWidgetPushToken(deviceId, token, environment, Date.now())\n if (ctx.bridge.capabilities.push !== true) {\n if (ctx.debug) ctx.log('push register held: bridge not ready')\n return ctx.fail('E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n ctx.send('s2c.ack', { enabled: true })\n },\n },\n {\n type: 'c2s.liveActivity.unregister',\n stage: 'authenticated',\n scopes: ['notifications.register'],\n doc: 'PROTOCOL.md c2s.liveActivity.unregister',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.activityId, 128)) return reject('E_PROTOCOL', 'invalid activityId')\n return checked\n },\n handle: (ctx, payload) => {\n const activityId = payload.activityId as string\n // 注销正在注册中的活动:抬高端次使在途的注册失效。\n if (ctx.pendingLiveActivityId === activityId) {\n ctx.nextLiveActivityGeneration()\n ctx.setPendingLiveActivityId(undefined)\n }\n ctx.devices.clearLiveActivity(ctx.deviceId!, activityId)\n ctx.send('s2c.ack', {})\n },\n },\n {\n type: 'c2s.liveActivity.register',\n stage: 'authenticated',\n scopes: LIVE_ACTIVITY_SCOPES,\n doc: 'PROTOCOL.md c2s.liveActivity.register',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (!isText(p.activityId, 128)) return reject('E_PROTOCOL', 'invalid activityId')\n if (\n !isText(p.sessionId) ||\n typeof p.deviceToken !== 'string' ||\n !/^[0-9a-fA-F]{32,512}$/.test(p.deviceToken) ||\n !['development', 'production'].includes(p.environment as string) ||\n !isOptionalField(p, 'enrollKey', (v) => isText(v, 128))\n ) {\n return reject('E_PROTOCOL', 'invalid live activity registration')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId\n if (deviceId === undefined) return ctx.fail('E_INTERNAL', 'device identity unavailable')\n const activityId = payload.activityId as string\n const generation = ctx.nextLiveActivityGeneration()\n ctx.setPendingLiveActivityId(activityId)\n // 注意:此处 enrollKey 原样透传,未做 push.register 那样的可打印 ASCII\n // 清洗。两条路径的清洗规则不一致属于已知不对称,本次按原行为保留,\n // 是否统一留待专门决策(不在本轮 wire 对等清单内)。\n if (typeof payload.enrollKey === 'string' && payload.enrollKey) {\n await ctx.enrollPushKey(payload.enrollKey as string)\n }\n if (generation !== ctx.liveActivityGeneration) {\n return ctx.fail('E_BUSY', 'live activity registration superseded')\n }\n // await 之后重新校验:权限可能已被改窄,连接可能已关闭。\n const record = ctx.devices.authorized(deviceId)\n if (\n !record ||\n !LIVE_ACTIVITY_SCOPES.every((scope) => record.scopes?.includes(scope))\n ) {\n return ctx.fail('E_FORBIDDEN', 'live activity permissions required')\n }\n if (!ctx.bridge.listSessions().some((row) => row.id === payload.sessionId)) {\n return ctx.fail('E_NOT_FOUND', 'session not found')\n }\n const previous = record.liveActivity\n if (previous?.activityId === activityId && previous.sessionId !== payload.sessionId) {\n return ctx.fail('E_PROTOCOL', 'activity is bound to another session')\n }\n ctx.devices.setLiveActivity(record.deviceId, {\n activityId,\n sessionId: payload.sessionId as string,\n token: String(payload.deviceToken).toLowerCase(),\n environment: payload.environment as ApnsEnvironment,\n updatedAt: Date.now(),\n expiresAt: Date.now() + 8 * 60 * 60 * 1000,\n })\n ctx.bridge.refreshLiveActivities()\n ctx.send('s2c.ack', { enabled: ctx.bridge.capabilities.push })\n },\n },\n]\n","/**\n * 定时任务特性的 wire 行:会话级(列表、历史、创建、修改、删除)与宿主级\n * (list/view/create/edit/run/limits)两套。\n *\n * scope 取 `schedule.manage` + `sessions.read`:PROTOCOL.md scope 映射表写明\n * 「`schedule.manage` 定时任务/提醒的列表、历史、创建、修改和删除;同时需要\n * `sessions.read` 才能读取任务内容」。迁移前这条组合一半住在 requiredScope、\n * 一半住在 connection.ts 调用点的一行补充规则里(334-336),现在是一行声明。\n *\n * 五个会话级帧共享能力位 `schedules`;该位现在只要求 Schedule 服务存在\n * (见 host-capabilities.ts 的 G4 修正)。六个宿主级帧共享 `schedulesHost`:\n * 该位由 adapter 判别位推导,false/缺失时新帧一律 E_UNSUPPORTED,客户端只能\n * 走会话级帧(PROTOCOL.md「Schedule 宿主级任务扩展」)。\n */\n\nimport { validSendId } from './connection-policy.ts'\nimport {\n accept,\n isInteger,\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type WireFrameRow,\n} from './wire-registry.ts'\n\n/** PROTOCOL.md scope 映射表:schedule.manage 需搭配 sessions.read。 */\nconst SCHEDULE_SCOPES: WireFrameRow['scopes'] = ['schedule.manage', 'sessions.read']\n\n/** 普通 JSON 对象(宿主级 timing/target/patch 都是嵌套对象)。 */\nfunction isObjectValue(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** `{time, time_zone}` 本地时间输入:取值合法性由 Host 完整校验。 */\nfunction isLocalTimeInput(value: unknown): boolean {\n const row = value as Record<string, unknown>\n return isObjectValue(value) && isText(value.time) && isText(value.time_zone)\n}\n\nfunction isWeekdayList(value: unknown): boolean {\n return Array.isArray(value) && value.length > 0 && value.every((day) => isInteger(day, 1, 7))\n}\n\n/**\n * 宿主级 timing 判别联合的形状检查。\n *\n * 只判结构与数量级(PROTOCOL.md 章节限额);日期格式、时区与频率是否可达由\n * Host 的 Schedule 服务负责——重复一遍只会制造两套口径。\n */\nfunction isHostTiming(value: unknown): boolean {\n if (!isObjectValue(value)) return false\n switch (value.kind) {\n case 'after':\n return isInteger(value.after_seconds, 1, Number.MAX_SAFE_INTEGER)\n case 'at':\n return isText(value.at) || (\n isObjectValue(value.at) &&\n isText(value.at.date) &&\n isText(value.at.time) &&\n isText(value.at.time_zone)\n )\n case 'every':\n return isInteger(value.every_seconds, 60, Number.MAX_SAFE_INTEGER)\n case 'daily':\n return isLocalTimeInput(value.daily)\n case 'weekly':\n return isObjectValue(value.weekly) &&\n isLocalTimeInput(value.weekly) &&\n isWeekdayList(value.weekly.weekdays)\n case 'cron':\n return isObjectValue(value.cron) &&\n isText(value.cron.expression) &&\n isText(value.cron.time_zone)\n case 'rrule':\n return isText(value.rrule)\n default:\n return false\n }\n}\n\n/** `{cwd?, agentPreset?}`:省略即 Host 默认。 */\nfunction isHostCreateSpec(value: unknown): boolean {\n return isObjectValue(value) &&\n isOptionalField(value, 'cwd', (v) => isText(v)) &&\n isOptionalField(value, 'agentPreset', (v) => isText(v))\n}\n\n/**\n * 创建/编辑请求里的 target。`session` 必须给 sessionId;`new_once`/`new` 的\n * 会话由 Host 预留或新建,`create` 省略即默认。客户端若把查看结果里的\n * `new_once.sessionId` 原样回传,也接受(未知/冗余字段不该让编辑失败)。\n */\nfunction isHostTarget(value: unknown): boolean {\n if (!isObjectValue(value)) return false\n switch (value.kind) {\n case 'session':\n return isText(value.sessionId)\n case 'new_once':\n return isOptionalField(value, 'sessionId', (v) => isText(v)) &&\n isOptionalField(value, 'create', isHostCreateSpec)\n case 'new':\n return isOptionalField(value, 'create', isHostCreateSpec)\n default:\n return false\n }\n}\n\n/** 编辑补丁:至少一个字段,且各字段形状合法。 */\nfunction isHostPatch(value: unknown): boolean {\n if (!isObjectValue(value)) return false\n const present = ['title', 'prompt', 'timing', 'target', 'active']\n .filter((key) => value[key] !== undefined)\n if (present.length === 0) return false\n return isOptionalField(value, 'title', (v) => isText(v, 120)) &&\n isOptionalField(value, 'prompt', (v) => isText(v, 256 * 1024)) &&\n isOptionalField(value, 'timing', isHostTiming) &&\n isOptionalField(value, 'target', isHostTarget) &&\n isOptionalField(value, 'active', (v) => typeof v === 'boolean')\n}\n\n\nexport const scheduleRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.schedule.list',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.list',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.sessionId)) return reject('E_PROTOCOL', 'invalid sessionId')\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.listSchedules(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.snapshot', { sessionId: payload.sessionId, tasks: result.value })\n },\n },\n {\n type: 'c2s.schedule.history',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.history',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n // 宿主级模型下 sessionId 可选并被忽略(PROTOCOL.md「投递历史」);会话级\n // 宿主仍要求它,但那条边界放在 handler,校验只描述形状。\n if (\n !isOptionalField(checked.value, 'sessionId', (v) => isText(v)) ||\n !isText(checked.value.id) ||\n !isInteger(checked.value.limit, 1, 100) ||\n !isOptionalField(checked.value, 'before', (v) => isText(v))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule history')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const sessionId = payload.sessionId as string | undefined\n // 会话级宿主上缺少 sessionId 仍是非法载荷:判定与扩展前的校验逐字一致\n // (E_PROTOCOL + 同一句话),只是改在 handler 判,因为校验拿不到宿主形态。\n if (sessionId === undefined && ctx.bridge.capabilities.schedulesHost !== true) {\n return ctx.fail('E_PROTOCOL', 'invalid schedule history')\n }\n const result = await ctx.bridge.scheduleHistory(\n sessionId ?? '',\n payload.id as string,\n payload.limit as number,\n payload.before as string | undefined,\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.history', {\n ...(sessionId !== undefined ? { sessionId } : {}),\n history: result.value,\n })\n },\n },\n {\n type: 'c2s.schedule.create',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !validSendId(p.clientRequestId) ||\n !isText(p.sessionId) ||\n !isText(p.title, 120) ||\n !isText(p.prompt, 256 * 1024)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule create fields')\n }\n // 恰好一个时机选择器:after_seconds / at / every_seconds / daily / weekly / cron。\n const selectors = ['after_seconds', 'at', 'every_seconds', 'daily', 'weekly', 'cron']\n .filter((key) => p[key] !== undefined)\n if (selectors.length !== 1) return reject('E_PROTOCOL', 'schedule requires exactly one selector')\n if (p.after_seconds !== undefined && !isInteger(p.after_seconds, 1, Number.MAX_SAFE_INTEGER)) {\n return reject('E_PROTOCOL', 'invalid after_seconds')\n }\n if (p.every_seconds !== undefined && !isInteger(p.every_seconds, 60, Number.MAX_SAFE_INTEGER)) {\n return reject('E_PROTOCOL', 'invalid every_seconds')\n }\n if (\n p.at !== undefined &&\n typeof p.at !== 'string' &&\n (typeof p.at !== 'object' || p.at === null || Array.isArray(p.at))\n ) {\n return reject('E_PROTOCOL', 'invalid at selector')\n }\n return accept(p)\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.createSchedule(\n ctx.deviceId!,\n payload as Record<string, unknown> & { sessionId: string; clientRequestId: string; title: string; prompt: string },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n sessionId: payload.sessionId,\n task: result.value,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.update',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.update',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !validSendId(p.clientRequestId) ||\n !isText(p.sessionId) ||\n !isText(p.id) ||\n p.expected === undefined ||\n typeof p.expected !== 'object' ||\n Array.isArray(p.expected)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule update fields')\n }\n if (\n !isOptionalField(p, 'title', (v) => isText(v, 120)) ||\n !isOptionalField(p, 'prompt', (v) => isText(v, 256 * 1024))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule update content')\n }\n if (\n p.change !== undefined &&\n (typeof p.change !== 'object' || p.change === null || Array.isArray(p.change))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule timing change')\n }\n return accept(p)\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.updateSchedule(\n ctx.deviceId!,\n payload as Record<string, unknown> & { sessionId: string; id: string; clientRequestId: string; expected: unknown },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n sessionId: payload.sessionId,\n task: result.value,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.delete',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedules',\n doc: 'PROTOCOL.md c2s.schedule.delete',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (\n !validSendId(checked.value.clientRequestId) ||\n !isText(checked.value.sessionId) ||\n !isText(checked.value.id)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule delete fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.deleteSchedule(ctx.deviceId!, {\n sessionId: payload.sessionId as string,\n id: payload.id as string,\n clientRequestId: payload.clientRequestId as string,\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n sessionId: payload.sessionId,\n deleted: true,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n // ---------- 宿主级模型(可选 v2,`schedulesHost`) ----------\n // 旧帧在宿主级 Host 上仍由 Bridge 映射(见 host-bridge),因此这六行只服务\n // 认识新能力位的客户端。\n {\n type: 'c2s.schedule.host.list',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.list',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !isOptionalField(p, 'active', (v) => typeof v === 'boolean') ||\n !isOptionalField(p, 'limit', (v) => isInteger(v, 1, 500)) ||\n !isOptionalField(p, 'offset', (v) => isInteger(v, 0, Number.MAX_SAFE_INTEGER))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule host list')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.listHostSchedules({\n ...(payload.active !== undefined ? { active: payload.active as boolean } : {}),\n ...(payload.limit !== undefined ? { limit: payload.limit as number } : {}),\n ...(payload.offset !== undefined ? { offset: payload.offset as number } : {}),\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.host.snapshot', result.value)\n },\n },\n {\n type: 'c2s.schedule.host.view',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.view',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.id)) return reject('E_PROTOCOL', 'invalid schedule id')\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.viewHostSchedule(payload.id as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.host.task', { task: result.value })\n },\n },\n {\n type: 'c2s.schedule.host.create',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !validSendId(p.clientRequestId) ||\n !isText(p.title, 120) ||\n !isText(p.prompt, 256 * 1024) ||\n !isHostTiming(p.timing) ||\n !isOptionalField(p, 'target', isHostTarget)\n ) {\n return reject('E_PROTOCOL', 'invalid schedule host create fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.createHostSchedule(\n ctx.deviceId!,\n payload as Record<string, unknown> & {\n clientRequestId: string\n title: string\n prompt: string\n timing: unknown\n target?: unknown\n },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n task: result.value,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.host.edit',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.edit',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const p = checked.value\n if (\n !validSendId(p.clientRequestId) ||\n !isText(p.id) ||\n !isOptionalField(p, 'expectedUpdatedAt', (v) => v === null || isText(v))\n ) {\n return reject('E_PROTOCOL', 'invalid schedule host edit fields')\n }\n if (p.action !== 'update' && p.action !== 'delete') {\n return reject('E_PROTOCOL', 'invalid schedule host edit action')\n }\n // delete 不需要 patch;update 必须有至少一个可写字段。\n if (p.action === 'update' && !isHostPatch(p.patch)) {\n return reject('E_PROTOCOL', 'invalid schedule host edit patch')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.editHostSchedule(\n ctx.deviceId!,\n payload as Record<string, unknown> & {\n clientRequestId: string\n id: string\n action: 'update' | 'delete'\n expectedUpdatedAt?: string | null\n patch?: unknown\n },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n const value = result.value\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n ...('deleted' in value ? { deleted: true } : { task: value.task }),\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.host.run',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.run',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!validSendId(checked.value.clientRequestId) || !isText(checked.value.id)) {\n return reject('E_PROTOCOL', 'invalid schedule host run fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.runHostSchedule(\n ctx.deviceId!,\n { clientRequestId: payload.clientRequestId as string, id: payload.id as string },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n task: result.value,\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.host.deleteEnded',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.deleteEnded',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!validSendId(checked.value.clientRequestId)) {\n return reject('E_PROTOCOL', 'invalid schedule host delete ended fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.deleteEndedHostSchedules(\n ctx.deviceId!,\n { clientRequestId: payload.clientRequestId as string },\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.updated', {\n clientRequestId: payload.clientRequestId,\n // 重放时条数未知(journal 不保存成功值),省略 deletedEnded,只标 replayed。\n ...(result.value.deleted !== undefined ? { deletedEnded: result.value.deleted } : {}),\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.schedule.host.limits',\n stage: 'authenticated',\n scopes: SCHEDULE_SCOPES,\n capability: 'schedulesHost',\n doc: 'PROTOCOL.md c2s.schedule.host.limits',\n validate: (payload) => payloadObject(payload),\n handle: async (ctx) => {\n const result = await ctx.bridge.scheduleLimits()\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.schedule.host.limits', result.value)\n },\n },\n]\n","export interface PromptDocument {\n name: string\n mediaType: string\n text: string\n truncated?: boolean\n}\n\ninterface DocumentMarker {\n v: 1\n name: string\n mediaType: string\n truncated?: boolean\n}\n\nconst PREFIX = '[DeepPilot document:'\nconst SUFFIX = ']'\n\n/**\n * DSH 0.1.2 has durable image attachments but no generic file content block.\n * Documents therefore travel as a bounded, explicitly-labelled text block.\n * The marker lets the canonical phone row recover an attachment chip without\n * exposing the complete document body as ordinary user-authored bubble text.\n */\nexport function documentPromptBlock(document: PromptDocument): string {\n const marker: DocumentMarker = {\n v: 1,\n name: document.name,\n mediaType: document.mediaType,\n ...(document.truncated ? { truncated: true } : {}),\n }\n const encoded = Buffer.from(JSON.stringify(marker), 'utf8').toString('base64url')\n return `${PREFIX}${encoded}${SUFFIX}\\nAttached document: ${document.name}\\n\\n${document.text}`\n}\n\nexport function projectedDocument(text: string): PromptDocument | undefined {\n if (!text.startsWith(PREFIX)) return undefined\n const end = text.indexOf(SUFFIX, PREFIX.length)\n if (end < 0) return undefined\n try {\n const decoded = JSON.parse(Buffer.from(text.slice(PREFIX.length, end), 'base64url').toString('utf8')) as Partial<DocumentMarker>\n if (decoded.v !== 1 || typeof decoded.name !== 'string' || typeof decoded.mediaType !== 'string') return undefined\n if (!decoded.name.trim() || !decoded.mediaType.trim()) return undefined\n return {\n name: decoded.name,\n mediaType: decoded.mediaType,\n text: text.slice(end + SUFFIX.length).replace(/^\\nAttached document:[^\\n]*\\n\\n/, ''),\n ...(decoded.truncated === true ? { truncated: true } : {}),\n }\n } catch {\n return undefined\n }\n}\n","import type { MessageProjection, SessionEventKind, ToolEditProjection } from './protocol.ts'\nimport type { SessionEventLike } from './host-api.ts'\nimport { projectedDocument } from './document-payload.ts'\n\nexport const MAX_MESSAGE_PROJECTION_BYTES = 256 * 1024\n/// Aggregate message-array budget for one tail/history frame. The complete\n/// envelope stays below the legacy iOS 1 MB decoder cap with ample metadata\n/// headroom, while each individual projection keeps its 256 KB allowance.\nexport const MAX_SESSION_PAGE_MESSAGES_BYTES = 900 * 1024\n\n/** One durable host event sequence becomes exactly one phone message row.\n * Keep the last projection when a host history response repeats an event. */\nexport function canonicalSessionMessages(messages: MessageProjection[]): MessageProjection[] {\n const bySequence = new Map<number, MessageProjection>()\n for (const message of messages) bySequence.set(message.seq, message)\n return [...bySequence.values()].sort((a, b) => a.seq - b.seq)\n}\n\nexport function limitSessionPageMessages(messages: MessageProjection[]): {\n messages: MessageProjection[]\n dropped: number\n} {\n const canonical = canonicalSessionMessages(messages)\n let bytes = 2 // JSON array brackets\n const kept: MessageProjection[] = []\n for (let index = canonical.length - 1; index >= 0; index -= 1) {\n const message = canonical[index]!\n const candidateBytes = jsonBytes(message) + (kept.length > 0 ? 1 : 0)\n if (bytes + candidateBytes > MAX_SESSION_PAGE_MESSAGES_BYTES) break\n kept.unshift(message)\n bytes += candidateBytes\n }\n return { messages: kept, dropped: canonical.length - kept.length }\n}\n\nexport function projectEvent(sessionId: string, event: SessionEventLike): { kind: SessionEventKind; data: Record<string, unknown> } | null {\n switch (event.type) {\n case 'turn/start':\n return { kind: 'turn.start', data: {} };\n case 'turn/end': {\n const data = event.data as { reason?: { kind?: string } } | undefined;\n return { kind: 'turn.end', data: { ok: data?.reason?.kind === 'completed' } };\n }\n case 'system/message': {\n // DSH 0.1.5 (session format V3) records the system prompt in message\n // history as a native surface event: {turn, step, message: SystemMessage}.\n // The SystemMessage content is model-facing prompt prose, not\n // conversation content — projecting it onto the phone's message list\n // would duplicate it on every page and leak prompt internals the user\n // never typed. Skip it explicitly (rather than via `default`) so a\n // future shape change forces a deliberate decision here.\n return null;\n }\n case 'user/message':\n return {\n kind: 'message.final',\n data: { ...limitMessageProjection({\n seq: event.seq,\n role: userRoleOf(event.data),\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ...(contextProjectionOf(event.data)),\n ts: tsOf(event),\n }) },\n };\n case 'assistant/chunk': {\n if (chunkTypeOf(event.data) === 'reasoning-delta') {\n return { kind: 'thinking.delta', data: limitRealtimeText({ text: chunkText(event.data), ts: tsOf(event) }) };\n }\n return { kind: 'message.delta', data: limitRealtimeText({ text: chunkText(event.data), ts: tsOf(event) }) };\n }\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n // Pure-reasoning or tool-only steps carry no visible answer; emitting\n // them produced empty bubbles on the phone.\n if (!text.trim() && !thinking.trim()) return null;\n return {\n kind: 'message.final',\n data: { ...limitMessageProjection({ seq: event.seq, role: 'assistant', text, ...(thinking ? { thinking } : {}), ts: tsOf(event) }) },\n };\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n return {\n kind: 'tool.start',\n data: {\n seq: event.seq,\n role: 'tool',\n tool: {\n name: String(data?.name ?? 'tool'),\n state: 'running',\n summary: summarizeArgs(data?.arguments),\n // Echo the invocation id so clients can pair the later result with\n // this exact row instead of guessing.\n ...(data?.callId ? { callId: String(data.callId) } : {}),\n ...editField(data?.name, data?.arguments),\n },\n ts: tsOf(event)\n }\n };\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; error?: unknown; message?: { content?: unknown } } | undefined;\n const ok = data?.error === undefined;\n const result = limitMessageProjection({\n seq: event.seq, role: 'tool', ts: tsOf(event),\n tool: { name: 'result', state: ok ? 'ok' : 'error', summary: ok ? summarizeResult(data?.message?.content) : '失败' },\n ...(ok ? attachmentProjection(data?.message) : {}),\n });\n return {\n kind: 'tool.end',\n data: {\n seq: result.seq, role: result.role, ts: result.ts, ok,\n summary: result.tool?.summary,\n ...(result.attachments ? { attachments: result.attachments } : {}),\n ...(data?.callId ? { callId: String(data.callId) } : {}),\n }\n };\n }\n default:\n return null;\n }\n}\n\nfunction tsOf(event: SessionEventLike): number {\n return typeof event.time === 'number' ? event.time : Date.now();\n}\n\n// ---------- user-role source classification ----------\n//\n// The DSH host logs every model-visible user-role message as `user/message`,\n// but only some of them are human prompts: synthetic `agent.inject()` context\n// (runtime-context snapshots, background-job notices, workspace instructions,\n// skill content, …) rides the same event type. The durable message carries a\n// `source` whose `kind` tells them apart, and the host's own trajectory view\n// renders only `source.kind === 'user'` as a human prompt — everything else is\n// injected context. The bridge mirrors that classification so the phone can\n// keep the two apart instead of guessing from text shapes.\n\n/** Read the durable message source off one user/message payload. Handles both\n * bare-message payloads and older `{message: {...}}` wrappers; undefined when\n * the shape carries no readable source (legacy hosts). */\nfunction userMessageSource(data: unknown): Record<string, unknown> | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const obj = data as { source?: unknown; message?: { source?: unknown } };\n if (obj.source && typeof obj.source === 'object') {\n return obj.source as Record<string, unknown>;\n }\n if (obj.message && typeof obj.message === 'object' &&\n obj.message.source && typeof obj.message.source === 'object') {\n return obj.message.source as Record<string, unknown>;\n }\n return undefined;\n}\n\n/** Wire role for one user/message payload. A payload without any readable\n * source degrades to 'user' so history written by older hosts stays visible;\n * a present source follows the host's own trajectory rule — anything whose\n * `kind` is not 'user' is injected context and projects as 'system'. */\nfunction userRoleOf(data: unknown): 'user' | 'system' {\n const source = userMessageSource(data);\n if (!source) return 'user';\n return source.kind === 'user' ? 'user' : 'system';\n}\n\n/** Producer name of one injected-context source, mirroring how the DSH client\n * runtime derives its trajectory label: plugin name, skill name, instruction\n * paths, session-reference labels, or the raw kind as fallback. */\nfunction contextLabelOf(source: Record<string, unknown>): string | undefined {\n const kind = typeof source.kind === 'string' ? source.kind : '';\n const joined = (member: string): string | undefined => {\n const list = source[member];\n if (!Array.isArray(list)) return undefined;\n const names = list.flatMap((entry) => {\n if (!entry || typeof entry !== 'object') return [];\n const record = entry as Record<string, unknown>;\n return [typeof record.label === 'string' ? record.label :\n (typeof record.path === 'string' ? record.path : '')];\n }).filter((name) => name.length > 0);\n return names.length > 0 ? names.join(', ') : undefined;\n };\n switch (kind) {\n case 'session-reference':\n return joined('references') ?? (kind || undefined);\n case 'agent-instructions':\n return joined('changes') ?? (kind || undefined);\n case 'plugin':\n return typeof source.plugin === 'string' && source.plugin.length > 0 ? source.plugin : kind || undefined;\n case 'skill-invocation':\n return typeof source.name === 'string' && source.name.length > 0 ? source.name : kind || undefined;\n default:\n return kind || undefined;\n }\n}\n\n/** Semantic ContextForm declared by the producer ('snapshot', 'notice', …);\n * anything unrecognized stays undefined so clients render it opaque. */\nfunction contextFormOf(source: Record<string, unknown>): string | undefined {\n if (typeof source.form !== 'string' || source.form.length === 0) return undefined;\n // Same known-vocabulary guard as the host's trajectory UI.\n const known = ['instructions', 'catalog', 'snapshot', 'notice', 'relay', 'recall'];\n return known.includes(source.form) ? source.form : undefined;\n}\n\n/** Optional `context` metadata for one system row; {} on user rows. */\nfunction contextProjectionOf(data: unknown): { context?: { label?: string; form?: string } } {\n if (userRoleOf(data) !== 'system') return {};\n const source = userMessageSource(data);\n if (!source) return {};\n const label = contextLabelOf(source);\n const form = contextFormOf(source);\n if (!label && !form) return {};\n return { context: { ...(label ? { label } : {}), ...(form ? { form } : {}) } };\n}\n\n/** Extract plain text from user/assistant message payloads across shapes. */\nexport function messageText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; text?: unknown; message?: unknown };\n if (typeof obj.text === 'string') return obj.text;\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageText(obj.message);\n return contentText(obj.content);\n}\n\nfunction contentText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map(part => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'text' && typeof piece.text === 'string') {\n return projectedDocument(piece.text) ? '' : piece.text\n }\n }\n return '';\n }).join('');\n }\n return '';\n}\n\nfunction messageAttachments(data: unknown): Array<{\n kind: 'image' | 'document'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n truncated?: boolean\n}> {\n if (!data || typeof data !== 'object') return [];\n const obj = data as { content?: unknown; message?: unknown };\n if (obj.message && typeof obj.message === 'object') return messageAttachments(obj.message);\n if (!Array.isArray(obj.content)) return [];\n const attachments: Array<{\n kind: 'image' | 'document'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n truncated?: boolean\n }> = []\n for (const part of obj.content) {\n if (!part || typeof part !== 'object') continue\n // The host normalizes uploaded bytes into an ImageAttachmentRef\n // (attachmentId/mediaType/bytes/width/height...); the id is what clients\n // need to read the image back through c2s.session.attachment.\n const block = part as {\n type?: string\n text?: unknown\n attachment?: {\n name?: unknown\n mediaType?: unknown\n attachmentId?: unknown\n width?: unknown\n height?: unknown\n }\n };\n if (block.type === 'text' && typeof block.text === 'string') {\n const document = projectedDocument(block.text)\n if (document) {\n attachments.push({\n kind: 'document',\n name: document.name,\n mediaType: document.mediaType,\n ...(document.truncated ? { truncated: true } : {}),\n })\n }\n continue\n }\n if (block.type !== 'image' || !block.attachment) continue\n const attachmentId = typeof block.attachment.attachmentId === 'string' && block.attachment.attachmentId.length > 0\n ? block.attachment.attachmentId\n : undefined;\n const width = typeof block.attachment.width === 'number' && Number.isFinite(block.attachment.width)\n ? block.attachment.width\n : undefined;\n const height = typeof block.attachment.height === 'number' && Number.isFinite(block.attachment.height)\n ? block.attachment.height\n : undefined;\n attachments.push({\n kind: 'image',\n ...(typeof block.attachment.name === 'string' ? { name: block.attachment.name } : {}),\n ...(typeof block.attachment.mediaType === 'string' ? { mediaType: block.attachment.mediaType } : {}),\n ...(attachmentId ? { attachmentId } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(height !== undefined ? { height } : {}),\n })\n }\n return attachments\n}\n\nfunction attachmentProjection(data: unknown): { attachments?: ReturnType<typeof messageAttachments> } {\n const attachments = messageAttachments(data);\n return attachments.length > 0 ? { attachments } : {};\n}\n\n/** Extract reasoning (\"thinking\") text from assistant message payloads. */\nfunction messageThinking(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; message?: unknown };\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageThinking(obj.message);\n return reasoningContent(obj.content);\n}\n\nfunction reasoningContent(content: unknown): string {\n if (!Array.isArray(content)) return '';\n return content.map(part => {\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'reasoning' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n}\n\n/** Stream chunk type of an assistant/chunk payload ('' when unwrapped). */\nfunction chunkTypeOf(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n return String((obj.chunk as { type?: unknown }).type ?? '');\n }\n // Unwrapped payloads carry plain text deltas.\n return 'text-delta';\n}\n\nfunction chunkText(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n // StreamChunk shape: {turn, step, chunk: {type: 'text-delta'|'reasoning-delta', text}}\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n const inner = obj.chunk as { type?: string; text?: string };\n if ((inner.type === 'text-delta' || inner.type === 'reasoning-delta') && typeof inner.text === 'string') {\n return inner.text;\n }\n return '';\n }\n const direct = data as { text?: unknown };\n return typeof direct.text === 'string' ? direct.text : '';\n}\n\nfunction editField(name: unknown, raw: unknown): { edit?: ToolEditProjection } {\n const edit = toolEditOf(name, raw)\n return edit ? { edit } : {}\n}\n\nfunction summarizeArgs(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '';\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const parts: string[] = [];\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') parts.push(key + '=' + truncate(value.replace(/\\s+/g, ' '), 60));\n }\n return truncate(parts.join(' '), 90);\n } catch {\n return truncate(raw, 90);\n }\n}\n\n/** 宿主的文件编辑工具名(DSH `dsh-tool-str-replace-editor`)。 */\nconst EDITOR_TOOL_NAME = 'str_replace_editor'\n/** 改动正文单侧上限(UTF-8 字节);超出则两侧都不发,只标 truncated。 */\nexport const MAX_TOOL_EDIT_TEXT_BYTES = 16 * 1024\n\n/**\n * 把编辑类工具调用的原始参数归一化成 `ToolEditProjection`。\n *\n * 只认 `str_replace_editor` 的 `str_replace` / `create` / `insert`(`view` 不改文件),\n * 其他工具或无法解析的参数返回 undefined,工具行保持原样。与 iOS `ToolEditDiff.parse`\n * 的解析口径一致:`new_str` 缺失视为删除(空串)。\n */\nexport function toolEditOf(name: unknown, raw: unknown): ToolEditProjection | undefined {\n if (name !== EDITOR_TOOL_NAME || typeof raw !== 'string' || raw.length === 0) return undefined\n let args: Record<string, unknown>\n try {\n const parsed = JSON.parse(raw) as unknown\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined\n args = parsed as Record<string, unknown>\n } catch {\n return undefined\n }\n const path = args.path\n if (typeof path !== 'string' || path.length === 0) return undefined\n let edit: ToolEditProjection\n switch (args.command) {\n case 'str_replace':\n if (typeof args.old_str !== 'string') return undefined\n edit = { path, operation: 'replace', oldText: args.old_str, newText: typeof args.new_str === 'string' ? args.new_str : '' }\n break\n case 'create':\n if (typeof args.file_text !== 'string') return undefined\n edit = { path, operation: 'create', newText: args.file_text }\n break\n case 'insert':\n if (typeof args.new_str !== 'string') return undefined\n edit = {\n path, operation: 'insert', newText: args.new_str,\n ...(Number.isSafeInteger(args.insert_line) ? { insertLine: args.insert_line as number } : {}),\n }\n break\n default:\n return undefined\n }\n const oversized = [edit.oldText, edit.newText]\n .some((text) => text !== undefined && Buffer.byteLength(text, 'utf8') > MAX_TOOL_EDIT_TEXT_BYTES)\n return oversized ? withoutEditBodies(edit) : edit\n}\n\n/** 去掉改动正文,只留路径、操作与 truncated 标记(超限或整条消息超限时用)。 */\nfunction withoutEditBodies(edit: ToolEditProjection): ToolEditProjection {\n return {\n path: edit.path,\n operation: edit.operation,\n ...(edit.insertLine !== undefined ? { insertLine: edit.insertLine } : {}),\n truncated: true,\n }\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : text.slice(0, max - 1) + '…';\n}\n\nexport function projectHistory(events: Array<{ event: SessionEventLike; view?: unknown }>): MessageProjection[] {\n const messages: MessageProjection[] = [];\n const toolByCall = new Map<string, MessageProjection>();\n for (const entry of events) {\n const event = entry.event;\n const base = { seq: event.seq, ts: tsOf(event) };\n switch (event.type) {\n // DSH 0.1.5 (session format V3) records the system prompt in message\n // history as a native `system/message` surface event. It is\n // model-facing prompt prose, not conversation content — see\n // projectEvent for the rationale. Skip explicitly.\n case 'system/message':\n break;\n case 'user/message':\n messages.push({\n ...base,\n role: userRoleOf(event.data),\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ...(contextProjectionOf(event.data)),\n });\n break;\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n if (!text.trim() && !thinking.trim()) break;\n messages.push({ ...base, role: 'assistant', text, ...(thinking ? { thinking } : {}) });\n break;\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n const row: MessageProjection = {\n ...base,\n role: 'tool',\n tool: {\n name: String(data?.name ?? 'tool'),\n state: 'running',\n summary: summarizeArgs(data?.arguments),\n ...editField(data?.name, data?.arguments),\n },\n };\n messages.push(row);\n if (data?.callId) toolByCall.set(String(data.callId), row);\n break;\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; message?: { content?: unknown }; error?: unknown } | undefined;\n const callId = data?.callId ? String(data.callId) : undefined;\n const target = callId ? toolByCall.get(callId) : undefined;\n const failed = data?.error !== undefined;\n const summary = failed ? '失败' : summarizeResult(data?.message?.content);\n // A tool result may carry images as well as text — browser and\n // computer-use backends return screenshots this way. `summarizeResult`\n // covers only the textual part, so project the binary blocks as\n // attachments too; otherwise the phone renders a text-only summary and\n // the image is silently dropped. The durable attachmentId is what lets\n // the client read the bytes back through c2s.session.attachment.\n const attachments = failed ? {} : attachmentProjection(data?.message);\n if (target?.tool) {\n target.tool = { ...target.tool, state: failed ? 'error' : 'ok', summary };\n if (attachments.attachments) Object.assign(target, attachments);\n } else {\n messages.push({\n ...base,\n role: 'tool',\n tool: { name: 'result', state: failed ? 'error' : 'ok', summary },\n ...attachments,\n });\n }\n break;\n }\n default:\n break;\n }\n }\n return canonicalSessionMessages(messages.map(limitMessageProjection));\n}\n\n/**\n * Enforce PROTOCOL.md's per-message 256 KB ceiling by UTF-8 JSON byte size.\n * Keep structural identity and attachment references intact; progressively\n * shorten human-readable fields until the serialized projection fits.\n */\nexport function limitMessageProjection(message: MessageProjection): MessageProjection {\n if (jsonBytes(message) <= MAX_MESSAGE_PROJECTION_BYTES) return message\n const next: MessageProjection = {\n ...message,\n ...(message.tool ? {\n tool: {\n ...message.tool,\n name: truncateUtf8(message.tool.name, 4 * 1024),\n summary: truncateUtf8(message.tool.summary, 64 * 1024),\n // 整条消息超限时先丢改动正文(不截半段,半段对比会误导),只留路径与 truncated。\n ...(message.tool.edit ? { edit: withoutEditBodies(message.tool.edit) } : {}),\n },\n } : {}),\n ...(message.attachments ? {\n attachments: message.attachments.slice(0, 16).map((attachment) => ({\n ...attachment,\n ...(attachment.name ? { name: truncateUtf8(attachment.name, 4 * 1024) } : {}),\n ...(attachment.mediaType ? { mediaType: truncateUtf8(attachment.mediaType, 256) } : {}),\n ...(attachment.attachmentId ? { attachmentId: truncateUtf8(attachment.attachmentId, 4 * 1024) } : {}),\n })),\n } : {}),\n ...(message.context ? {\n context: {\n ...(message.context.label ? { label: truncateUtf8(message.context.label, 8 * 1024) } : {}),\n ...(message.context.form ? { form: truncateUtf8(message.context.form, 256) } : {}),\n },\n } : {}),\n truncated: true,\n }\n\n const textFields: Array<{\n get: () => string\n set: (value: string) => void\n }> = []\n if (typeof next.text === 'string') textFields.push({\n get: () => next.text ?? '',\n set: (value) => { next.text = value },\n })\n if (typeof next.thinking === 'string') textFields.push({\n get: () => next.thinking ?? '',\n set: (value) => { next.thinking = value },\n })\n if (next.tool) textFields.push({\n get: () => next.tool?.summary ?? '',\n set: (value) => { if (next.tool) next.tool.summary = value },\n })\n if (next.context?.label) textFields.push({\n get: () => next.context?.label ?? '',\n set: (value) => { if (next.context) next.context.label = value },\n })\n\n while (jsonBytes(next) > MAX_MESSAGE_PROJECTION_BYTES) {\n const largest = textFields\n .map((field) => ({ field, bytes: Buffer.byteLength(field.get(), 'utf8') }))\n .sort((a, b) => b.bytes - a.bytes)[0]\n if (largest && largest.bytes > 0) {\n largest.field.set(truncateUtf8(largest.field.get(), Math.floor(largest.bytes / 2)))\n continue\n }\n if (next.attachments && next.attachments.length > 0) {\n next.attachments = next.attachments.slice(0, -1)\n continue\n }\n break\n }\n return next\n}\n\nfunction limitRealtimeText(data: { text: string; ts: number }): Record<string, unknown> {\n if (jsonBytes(data) <= MAX_MESSAGE_PROJECTION_BYTES) return data\n let text = data.text\n const next: Record<string, unknown> = { ...data, truncated: true }\n while (jsonBytes(next) > MAX_MESSAGE_PROJECTION_BYTES && text.length > 0) {\n text = truncateUtf8(text, Math.floor(Buffer.byteLength(text, 'utf8') / 2))\n next.text = text\n }\n return next\n}\n\nfunction jsonBytes(value: unknown): number {\n return Buffer.byteLength(JSON.stringify(value), 'utf8')\n}\n\nfunction truncateUtf8(value: string, maxBytes: number): string {\n if (maxBytes <= 0) return ''\n if (Buffer.byteLength(value, 'utf8') <= maxBytes) return value\n let low = 0\n let high = value.length\n while (low < high) {\n const mid = Math.ceil((low + high) / 2)\n const candidate = value.slice(0, mid)\n if (Buffer.byteLength(candidate, 'utf8') <= maxBytes) low = mid\n else high = mid - 1\n }\n let end = low\n if (end > 0 && /[\\uD800-\\uDBFF]/.test(value[end - 1]!)) end -= 1\n return value.slice(0, end)\n}\n\nfunction summarizeResult(content: unknown): string {\n const text = contentText(content).replace(/\\s+/g, ' ').trim();\n return truncate(text, 90);\n}\n\n// ---------- 历史分页窗口 ----------\n\n/** 参与窗口计算的原始事件:可投影的最小形状。 */\nexport interface HistoryWindowEvent {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface HistoryWindowOptions {\n /** 只取 seq 严格小于该值的事件(翻页边界)。 */\n beforeSeq?: number\n /** 目标消息条数;小于 1 一律按 1 处理。 */\n limit: number\n}\n\nexport interface HistoryWindow {\n /** 窗口内的原始事件,旧→新,含投影为空者。 */\n events: Array<{ event: HistoryWindowEvent }>\n /** 起点之前是否还有更多事件(客户端可继续翻页)。 */\n hasMore: boolean\n}\n\n/** 一页能要多少条消息:wire 校验与 bridge clamp 共用这一条规则。 */\nexport const MIN_HISTORY_PAGE_MESSAGES = 1\nexport const MAX_HISTORY_PAGE_MESSAGES = 500\n/** 打开会话时的默认尾部条数。 */\nexport const DEFAULT_HISTORY_PAGE_MESSAGES = 100\n/** 打开会话时至少要拉多少条:少于这个数客户端一屏都铺不满。 */\nexport const MIN_TAIL_MESSAGES = 10\n\n/** 把 wire 声明的页大小收敛到合法区间。 */\nexport function normalizePageLimit(value: unknown): number {\n return typeof value === 'number' && Number.isInteger(value)\n ? Math.max(MIN_HISTORY_PAGE_MESSAGES, Math.min(MAX_HISTORY_PAGE_MESSAGES, value))\n : DEFAULT_HISTORY_PAGE_MESSAGES\n}\n\n/**\n * 会话历史的分页窗口:从候选事件里取出「投影后末尾 `limit` 条消息」所在的那\n * 一段原始事件。\n *\n * 迁移前这段循环住在 host adapter(dsh-api-proxy.ts 的 sessions.history)里,\n * 按块累积、每轮对累计窗口重跑一次投影(稀疏投影时最坏 O(N²/L))。它与自己\n * 依赖的投影规则分居两地,而 `projectHistory` 是顺序依赖的(tool/call 与\n * tool/result 靠 toolByCall 配对),所以这里只做一次前向投影,再按消息归属\n * 反推窗口起点。\n *\n * 注意:窗口**不是**最小的那个后缀。现算法按 `limit` 的整数倍从末端取块,\n * 因此窗口可能含若干投影为空的前导事件;本实现按消息归属取起点,同样保留\n * 这些前导事件之前的所有内容——两者的差异由 tests/history-window-parity.test.ts\n * 逐条钉住(75 例确定性语料,含切断 call/result 配对与重复 seq 的形状)。\n */\nexport function historyWindow(\n source: readonly unknown[],\n options: HistoryWindowOptions,\n): HistoryWindow {\n // 页大小走同一条规则:非有限值回落默认,其余收敛到 [1, 500]。\n const limit = normalizePageLimit(options.limit)\n const beforeSeq = options.beforeSeq\n const candidates: HistoryWindowEvent[] = []\n for (const raw of source) {\n if (typeof raw !== 'object' || raw === null) continue\n const event = raw as HistoryWindowEvent\n if (typeof event.type !== 'string' || typeof event.seq !== 'number') continue\n if (beforeSeq !== undefined && !(event.seq < beforeSeq)) continue\n candidates.push(event)\n }\n const entries = candidates.map((event) => ({ event }))\n if (entries.length === 0) return { events: [], hasMore: false }\n\n const projected = projectHistory(entries)\n if (projected.length <= limit) return { events: entries, hasMore: false }\n\n // 每条消息对应的原始下标:投影按 seq 去重(后者胜出),因此取该 seq 最后一次\n // 出现的下标。窗口必须包含这些下标,否则消息会被裁掉。\n const lastIndexBySeq = new Map<number, number>()\n for (let index = 0; index < candidates.length; index += 1) {\n lastIndexBySeq.set(candidates[index]!.seq, index)\n }\n const keep = projected.slice(projected.length - limit)\n let front = candidates.length\n for (const message of keep) {\n const index = lastIndexBySeq.get(message.seq)\n if (index !== undefined && index < front) front = index\n }\n // hasMore 只表示「窗口之前还有能产出消息的事件」:旧实现按 limit 整数倍取块,\n // 窗口之前只剩投影为空的前导事件时也报 true,客户端会多发一次翻页才拿到空页。\n // 这里对前缀做一次投影来定性,代价 O(前 `front` 个事件)。\n const older = front > 0 ? projectHistory(entries.slice(0, front)) : []\n return { events: entries.slice(front), hasMore: older.length > 0 }\n}\n","/**\n * 会话特性的 wire 行:列表/归档、打开与关闭、创建/分支/重命名/取消、历史与\n * 附件、模型目录与切换、prompt 投递。\n *\n * handler 体从 connection.ts 的 switch 原样迁来,`this.` 换成 `ctx.`;\n * 该校验的判定与顺序和迁移前一致——先是原 request-validation 的形状检查,\n * 后是原 case 体内的形状检查——因此同一载荷在新旧路径上得到同一个错误码与\n * 同一条信息(tests/wire-parity.test.ts 在迁移期逐条对等,之后由\n * tests/wire-rows.test.ts 固定为显式期望)。\n */\n\nimport { validSendId } from './connection-policy.ts'\nimport { normalizePageLimit } from './host-event-projection.ts'\nimport { BINARY_CHUNK_BYTES, MAX_BINARY_ATTACHMENT_BYTES } from './binary-frame.ts'\nimport type { SessionAttachmentBinaryAck } from './protocol.ts'\nimport {\n ATTACHMENT_ENVELOPE_HEADROOM_BYTES,\n IMAGE_MEDIA_TYPES,\n LEGACY_CLIENT_MAX_FRAME_BYTES,\n MAX_ATTACHMENT_FRAME_BYTES,\n MAX_BASE64_CHARS_PER_IMAGE,\n MAX_DOCUMENT_MEDIA_TYPE_CHARS,\n MAX_DOCUMENT_NAME_CHARS,\n MAX_DOCUMENT_TEXT_CHARS,\n MAX_PROMPT_DOCUMENTS,\n MAX_PROMPT_IMAGES,\n MAX_PROMPT_TEXT_CHARS,\n sanitizeDocumentField,\n sanitizeImageName,\n} from './connection-policy.ts'\nimport {\n accept,\n isInteger,\n isOptionalField,\n isText,\n payloadObject,\n reject,\n type CheckedPayload,\n type WireFrameRow,\n} from './wire-registry.ts'\n\n/** 会话读取域的帧共同要求 sessions.read(PROTOCOL.md scope 映射表)。 */\nconst READ: WireFrameRow['scopes'] = ['sessions.read']\nconst MANAGE: WireFrameRow['scopes'] = ['sessions.manage']\n\n/** 原 request-validation 的 sessionId 检查:所有 c2s.session.* 共用的第一道。 */\nfunction requireSessionId(payload: Record<string, unknown>): CheckedPayload | undefined {\n if (!isText(payload.sessionId)) return reject('E_PROTOCOL', 'invalid sessionId')\n return undefined\n}\n\nexport const sessionRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.ping',\n stage: 'pre-auth',\n scopes: [],\n widgetPolicy: 'allowed',\n doc: 'PROTOCOL.md 连接与鉴权',\n handle: (ctx) => {\n ctx.send('s2c.pong', { serverTime: Date.now() })\n },\n },\n {\n // 握手帧。机制(验签、scope 载入、welcome、续传)仍在连接里:这是认证\n // 状态机的地盘,行只登记「它存在、无需 scope、认证前可到」。\n type: 'c2s.auth.prove',\n stage: 'pre-auth',\n scopes: [],\n widgetPolicy: 'allowed',\n doc: 'PROTOCOL.md 连接与鉴权',\n handle: (ctx) => ctx.prove(),\n },\n {\n type: 'c2s.sessions.list',\n stage: 'authenticated',\n scopes: READ,\n widgetPolicy: 'allowed',\n doc: 'PROTOCOL.md c2s.sessions.list',\n validate: (payload) => payloadObject(payload),\n handle: (ctx) => {\n ctx.send('s2c.sessions.snapshot', { full: true, sessions: ctx.bridge.listSessions() })\n },\n },\n {\n // 与 list 同语义的无参帧:载荷可省略或为 null(G2)。\n type: 'c2s.sessions.archived',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.sessions.archived',\n validate: (payload) => payloadObject(payload),\n handle: (ctx) => {\n ctx.send('s2c.sessions.archived.snapshot', { sessions: ctx.bridge.listArchivedSessions() })\n },\n },\n {\n type: 'c2s.pending.list',\n stage: 'authenticated',\n scopes: ['interactions.respond'],\n widgetPolicy: 'allowed',\n capability: 'pendingSnapshot',\n doc: 'PROTOCOL.md c2s.pending.list',\n validate: (payload) => payloadObject(payload),\n handle: (ctx) => {\n ctx.send('s2c.pending.snapshot', ctx.bridge.pendingSnapshot())\n },\n },\n {\n type: 'c2s.workspaces.list',\n stage: 'authenticated',\n scopes: READ,\n capability: 'projectSelection',\n doc: 'PROTOCOL.md c2s.workspaces.list',\n validate: (payload) => payloadObject(payload),\n handle: async (ctx) => {\n const result = await ctx.bridge.listWorkspaces()\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.workspaces.snapshot', { workspaces: result.value })\n },\n },\n {\n type: 'c2s.workspace.create',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'projectSelection',\n doc: 'PROTOCOL.md c2s.workspace.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isText(checked.value.path, 32768)) return reject('E_PROTOCOL', 'invalid path')\n return checked\n },\n handle: async (ctx, payload) => {\n const path = String(payload.path).trim()\n // 空路径是载荷问题,先于能力检查(与迁移前次序一致)。\n if (!path) return ctx.fail('E_PROTOCOL', 'non-empty path required')\n const result = await ctx.bridge.createWorkspace(path)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.workspace.created', result.value)\n },\n },\n {\n type: 'c2s.directory.list',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.directory.list',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (!isOptionalField(checked.value, 'path', (v) => isText(v, 32768, false))) {\n return reject('E_PROTOCOL', 'invalid path')\n }\n if (checked.value.path !== undefined && typeof checked.value.path !== 'string') {\n return reject('E_PROTOCOL', 'path must be a string')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.listDirectory(payload.path as string | undefined)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.directory.listing', result.value)\n },\n },\n {\n type: 'c2s.directory.pick',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.directory.pick',\n validate: (payload) => payloadObject(payload),\n handle: async (ctx) => {\n const result = await ctx.bridge.pickDirectory()\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.directory.picked', { path: result.value })\n },\n },\n {\n type: 'c2s.session.open',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.open',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isOptionalField(checked.value, 'tailCount', (v) => isInteger(v, 1, 10000))) {\n return reject('E_PROTOCOL', 'invalid tailCount')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const sessionId = payload.sessionId as string\n const buffered = ctx.bufferOpenEvents(sessionId)\n const ok = await ctx.bridge.openSession(ctx, sessionId, (payload.tailCount as number | undefined) ?? 100)\n if (!ok) {\n // 打开失败:仅当缓冲仍属于本次尝试时才移除,避免清掉并发打开的新缓冲。\n ctx.discardOpenBuffer(sessionId, buffered)\n return ctx.fail('E_NOT_FOUND', 'session history unavailable')\n }\n // 并发的 close 或替换打开会使本次尝试失效:它的 tail 已经发出,但不应\n // 在用户离开页面后重新激活实时投递。\n if (!ctx.flushOpenBuffer(sessionId, buffered)) return\n },\n },\n {\n type: 'c2s.session.close',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.close',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: (ctx, payload) => {\n ctx.closeOpenSession(payload.sessionId as string)\n ctx.send('s2c.ack', {})\n },\n },\n {\n type: 'c2s.session.create',\n stage: 'authenticated',\n scopes: MANAGE,\n doc: 'PROTOCOL.md c2s.session.create',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n if (\n !isOptionalField(checked.value, 'workspaceId', (v) => isText(v)) ||\n !isOptionalField(checked.value, 'cwd', (v) => isText(v, 32768)) ||\n (checked.value.workspaceId !== undefined && checked.value.cwd !== undefined)\n ) {\n return reject('E_PROTOCOL', 'invalid workspace selection')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const workspaceId = typeof payload.workspaceId === 'string' ? String(payload.workspaceId).trim() : ''\n const cwd = typeof payload.cwd === 'string' ? String(payload.cwd).trim() : ''\n if (workspaceId && cwd) return ctx.fail('E_PROTOCOL', 'workspaceId and cwd are mutually exclusive')\n const newId = await ctx.bridge.createSession({\n ...(workspaceId ? { workspaceId } : {}),\n ...(cwd ? { cwd } : {}),\n })\n if (!newId) return ctx.fail('E_INTERNAL', 'session create failed')\n ctx.send('s2c.ack', { sessionId: newId })\n },\n },\n {\n // PROTOCOL.md:分支会话要求 sessions.read 与 sessions.manage 两个 scope\n // (迁移前只校验了 sessions.manage,调用点的补充规则漏掉了本帧,见 G1)。\n type: 'c2s.session.fork',\n stage: 'authenticated',\n scopes: [...MANAGE, ...READ],\n capability: 'sessionFork',\n doc: 'PROTOCOL.md Session 分支扩展',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!validSendId(checked.value.clientRequestId) || !isOptionalField(checked.value, 'atSeq', (v) => isInteger(v, 0, Number.MAX_SAFE_INTEGER))) {\n return reject('E_PROTOCOL', 'invalid session fork fields')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n const deviceId = ctx.deviceId!\n const result = await ctx.bridge.forkSession(deviceId, {\n sessionId: payload.sessionId as string,\n clientRequestId: payload.clientRequestId as string,\n ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq as number } : {}),\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.forked', {\n clientRequestId: payload.clientRequestId,\n sourceSessionId: payload.sessionId,\n sessionId: result.value.sessionId,\n ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq } : {}),\n ...(result.replayed ? { replayed: true } : {}),\n })\n },\n },\n {\n type: 'c2s.session.rename',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'sessionManagement',\n doc: 'PROTOCOL.md c2s.session.rename',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isText(checked.value.title, 4096)) return reject('E_PROTOCOL', 'invalid title')\n return checked\n },\n handle: async (ctx, payload) => {\n const title = String(payload.title).trim()\n if (title.length === 0) return ctx.fail('E_PROTOCOL', 'sessionId and non-empty title required')\n const result = await ctx.bridge.renameSession(payload.sessionId as string, title)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.renamed', { sessionId: payload.sessionId, title: result.value })\n },\n },\n {\n type: 'c2s.session.archive',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'sessionManagement',\n doc: 'PROTOCOL.md c2s.session.archive',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.archiveSession(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.archived', { sessionId: payload.sessionId })\n },\n },\n {\n type: 'c2s.session.unarchive',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'sessionRestore',\n doc: 'PROTOCOL.md c2s.session.unarchive',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.unarchiveSession(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.unarchived', { sessionId: payload.sessionId })\n },\n },\n {\n type: 'c2s.session.cancel',\n stage: 'authenticated',\n scopes: MANAGE,\n doc: 'PROTOCOL.md c2s.session.cancel',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.cancelSession(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.ack', { sessionId: payload.sessionId })\n },\n },\n {\n type: 'c2s.session.history',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.history',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (\n !isInteger(checked.value.beforeSeq, 0, Number.MAX_SAFE_INTEGER) ||\n !isOptionalField(checked.value, 'limit', (v) => isInteger(v, 1, 500))\n ) {\n return reject('E_PROTOCOL', 'invalid history range')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n if (typeof payload.beforeSeq !== 'number') {\n return ctx.fail('E_PROTOCOL', 'sessionId and beforeSeq required')\n }\n const page = await ctx.bridge.historyPage(\n payload.sessionId as string,\n payload.beforeSeq,\n normalizePageLimit(payload.limit),\n )\n if (!page) return ctx.fail('E_NOT_FOUND', 'history unavailable')\n ctx.send('s2c.history.page', page)\n },\n },\n {\n type: 'c2s.session.attachment',\n stage: 'authenticated',\n scopes: READ,\n doc: 'PROTOCOL.md c2s.session.attachment',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isText(checked.value.attachmentId)) return reject('E_PROTOCOL', 'invalid attachmentId')\n const maxFrameBytes = checked.value.maxFrameBytes\n if (maxFrameBytes !== undefined && (!Number.isSafeInteger(maxFrameBytes) || (maxFrameBytes as number) <= 0)) {\n return reject('E_PROTOCOL', 'invalid maxFrameBytes')\n }\n if (checked.value.binary !== undefined && typeof checked.value.binary !== 'boolean') return reject('E_PROTOCOL', 'invalid binary')\n return checked\n },\n handle: async (ctx, payload) => {\n const attachmentId = payload.attachmentId as string\n if (attachmentId.length === 0) return ctx.fail('E_PROTOCOL', 'sessionId and attachmentId required')\n const image = await ctx.bridge.attachmentData(payload.sessionId as string, attachmentId)\n if (!image) return ctx.fail('E_NOT_FOUND', 'attachment unavailable')\n if (payload.binary === true && ctx.sendBinary) {\n // 二进制分块:不受单帧上限约束,只受整图上限约束。宿主给的是 base64,这里解一次即可。\n const bytes = Buffer.from(image.data, 'base64')\n if (bytes.length === 0) return ctx.fail('E_NOT_FOUND', 'attachment unavailable')\n if (bytes.length > MAX_BINARY_ATTACHMENT_BYTES) {\n return ctx.fail('E_TOO_LARGE', `attachment ${bytes.length} bytes exceeds limit`)\n }\n let index = 0\n try {\n for (let offset = 0; offset < bytes.length; offset += BINARY_CHUNK_BYTES) {\n await ctx.waitForWritable?.()\n ctx.sendBinary('s2c.session.attachment.chunk', { index: index++ }, bytes.subarray(offset, offset + BINARY_CHUNK_BYTES))\n }\n } catch {\n return // 连接已关闭,结果无人接收。\n }\n const ack: SessionAttachmentBinaryAck = { ...(image.mediaType !== undefined ? { mediaType: image.mediaType } : {}), chunks: index, bytes: bytes.length }\n return ctx.send('s2c.ack', ack)\n }\n // 没带 maxFrameBytes 的是旧 App:超过 1 MiB 的帧会让它断线重连,所以先在这里拒绝,\n // 让它显示占位图。base64 与 mediaType 都是 ASCII,字符数即字节数。\n const clientLimit = (payload.maxFrameBytes as number | undefined) ?? LEGACY_CLIENT_MAX_FRAME_BYTES\n const frameBytes = image.data.length + (image.mediaType?.length ?? 0) + ATTACHMENT_ENVELOPE_HEADROOM_BYTES\n if (frameBytes > Math.min(clientLimit, MAX_ATTACHMENT_FRAME_BYTES)) {\n return ctx.fail('E_TOO_LARGE', `attachment frame ${frameBytes} bytes exceeds limit`)\n }\n // 多张图片同时回读时逐个等缓冲排空再发,避免几块大帧叠进发送缓冲触发 1013。\n try {\n await ctx.waitForWritable?.()\n } catch {\n return // 连接已关闭,结果无人接收。\n }\n ctx.send('s2c.ack', image)\n },\n },\n {\n type: 'c2s.session.skills',\n stage: 'authenticated',\n scopes: READ,\n capability: 'skills',\n doc: 'PROTOCOL.md c2s.session.skills',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.sessionSkills(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.skills', { sessionId: payload.sessionId, ...result.value })\n },\n },\n {\n type: 'c2s.session.models',\n stage: 'authenticated',\n scopes: READ,\n capability: 'models',\n doc: 'PROTOCOL.md c2s.session.models',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n return requireSessionId(checked.value) ?? checked\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.sessionModels(payload.sessionId as string)\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.models', { sessionId: payload.sessionId, ...result.value })\n },\n },\n {\n type: 'c2s.session.selectModel',\n stage: 'authenticated',\n scopes: MANAGE,\n capability: 'models',\n doc: 'PROTOCOL.md c2s.session.selectModel',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (\n !isText(checked.value.provider, 256) ||\n !isText(checked.value.model, 1024) ||\n !isOptionalField(checked.value, 'reasoningEffort', (v) => isText(v, 128, false))\n ) {\n return reject('E_PROTOCOL', 'invalid model selection')\n }\n return checked\n },\n handle: async (ctx, payload) => {\n if (!String(payload.provider).trim() || !String(payload.model).trim()) {\n return ctx.fail('E_PROTOCOL', 'sessionId, provider and model required')\n }\n const result = await ctx.bridge.selectSessionModel(payload.sessionId as string, {\n provider: String(payload.provider).trim(),\n model: String(payload.model).trim(),\n ...(String(payload.reasoningEffort ?? '').trim()\n ? { reasoningEffort: String(payload.reasoningEffort).trim() }\n : {}),\n })\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.session.modelSelected', { sessionId: payload.sessionId, selected: result.value })\n },\n },\n {\n // 投递回执查询:journal 语义,能力位恒真但仍绑定,缺位即说明 Bridge 被改动。\n type: 'c2s.session.delivery',\n stage: 'authenticated',\n scopes: ['prompt.send'],\n capability: 'promptDelivery',\n doc: 'PROTOCOL.md c2s.session.delivery',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!validSendId(checked.value.clientSendId)) return reject('E_PROTOCOL', 'invalid clientSendId')\n return checked\n },\n handle: (ctx, payload) => {\n ctx.send('s2c.ack', ctx.bridge.promptDeliveries.lookup(\n ctx.deviceId!,\n payload.clientSendId as string,\n payload.sessionId as string,\n ))\n },\n },\n {\n type: 'c2s.session.sendPrompt',\n stage: 'authenticated',\n scopes: ['prompt.send'],\n doc: 'PROTOCOL.md c2s.session.sendPrompt',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n const p = checked.value\n if (p.clientSendId !== undefined && !validSendId(p.clientSendId)) {\n return reject('E_PROTOCOL', 'invalid clientSendId')\n }\n // mode 缺省即 queue;steer 是否可用要看能力位,留给 handle 判断(validate 拿不到 bridge)。\n if (p.mode !== undefined && p.mode !== 'queue' && p.mode !== 'steer') {\n return reject('E_PROTOCOL', 'invalid prompt mode')\n }\n if (\n !isOptionalField(p, 'text', (v) => isText(v, MAX_PROMPT_TEXT_CHARS, false)) ||\n !isOptionalField(p, 'images', Array.isArray) ||\n !isOptionalField(p, 'documents', Array.isArray)\n ) {\n return reject('E_PROTOCOL', 'invalid prompt fields')\n }\n // 附件条目形状:原 request-validation 的检查整体先于 handler 的资源上限,\n // 次序保持不变,同一条载荷在两条路径上得到同一条信息。\n for (const key of ['images', 'documents']) {\n const items = p[key] as unknown[] | undefined\n if (items && items.some((v) => v === null || typeof v !== 'object' || Array.isArray(v))) {\n return reject('E_PROTOCOL', 'invalid attachment')\n }\n for (const item of (items ?? []) as Array<Record<string, unknown>>) {\n if (item.name !== undefined && !isText(item.name, 4096, false)) {\n return reject('E_PROTOCOL', 'invalid attachment name')\n }\n if (item.truncated !== undefined && typeof item.truncated !== 'boolean') {\n return reject('E_PROTOCOL', 'invalid truncated flag')\n }\n }\n }\n const text = typeof p.text === 'string' ? p.text : ''\n const rawImages = Array.isArray(p.images) ? p.images : []\n const rawDocuments = Array.isArray(p.documents) ? p.documents : []\n if (text.trim().length === 0 && rawImages.length === 0 && rawDocuments.length === 0) {\n return reject('E_PROTOCOL', 'sessionId and prompt content required')\n }\n if (rawImages.length > MAX_PROMPT_IMAGES) return reject('E_PROTOCOL', 'too many images')\n if (rawDocuments.length > MAX_PROMPT_DOCUMENTS || rawImages.length + rawDocuments.length > MAX_PROMPT_IMAGES) {\n return reject('E_PROTOCOL', 'too many prompt attachments')\n }\n const images: Array<{ mediaType: string; data: string; name?: string }> = []\n for (const image of rawImages as Array<Record<string, unknown>>) {\n const mediaType = String(image?.mediaType)\n const data = image?.data\n if (\n !IMAGE_MEDIA_TYPES.has(mediaType) ||\n typeof data !== 'string' ||\n data.length === 0 ||\n data.length > MAX_BASE64_CHARS_PER_IMAGE\n ) {\n return reject('E_PROTOCOL', 'invalid image attachment')\n }\n images.push({\n mediaType,\n data,\n ...(typeof image.name === 'string' && sanitizeImageName(image.name).length > 0\n ? { name: sanitizeImageName(image.name) }\n : {}),\n })\n }\n const documents: Array<{ mediaType: string; name: string; text: string; truncated?: boolean }> = []\n for (const document of rawDocuments as Array<Record<string, unknown>>) {\n const name = document?.name\n const mediaType = document?.mediaType\n const documentText = document?.text\n if (\n typeof name !== 'string' ||\n typeof mediaType !== 'string' ||\n typeof documentText !== 'string' ||\n documentText.length === 0 ||\n documentText.length > MAX_DOCUMENT_TEXT_CHARS\n ) {\n return reject('E_PROTOCOL', 'invalid document attachment')\n }\n const cleanName = sanitizeDocumentField(name, MAX_DOCUMENT_NAME_CHARS)\n const cleanMediaType = sanitizeDocumentField(mediaType, MAX_DOCUMENT_MEDIA_TYPE_CHARS).toLowerCase()\n if (!cleanName || !cleanMediaType || cleanMediaType.startsWith('image/')) {\n return reject('E_PROTOCOL', 'invalid document attachment')\n }\n documents.push({\n name: cleanName,\n mediaType: cleanMediaType,\n text: documentText,\n ...(document.truncated === true ? { truncated: true } : {}),\n })\n }\n return accept({\n sessionId: p.sessionId,\n text,\n images,\n documents,\n ...(p.mode === 'steer' ? { mode: 'steer' } : {}),\n ...(p.clientSendId !== undefined ? { clientSendId: p.clientSendId } : {}),\n })\n },\n handle: async (ctx, payload) => {\n const sessionId = payload.sessionId as string\n const text = payload.text as string\n const images = payload.images as Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }>\n const documents = payload.documents as Array<{ mediaType: string; name: string; text: string; truncated?: boolean }>\n const clientSendId = payload.clientSendId as string | undefined\n const steer = payload.mode === 'steer'\n // 转向依赖 Host 的排队能力;queue 与省略等价,旧 Host 上照旧可发。\n if (steer && ctx.bridge.capabilities.promptQueue !== true) {\n return ctx.fail('E_UNSUPPORTED', 'prompt queue not supported')\n }\n const mode = steer ? 'steer' as const : 'queue' as const\n if (clientSendId !== undefined) {\n // 幂等投递:同一 clientSendId 重复到达时复用回执,不重复入队。\n // mode 只在 steer 时计入指纹:升级前落盘的同 ID 条目不含 mode,\n // 按 queue 重发必须仍被认作同一请求,而不是内容不一致。\n const receipt = await ctx.bridge.promptDeliveries.dispatch(\n ctx.deviceId!,\n clientSendId,\n { sessionId, content: { text, images, documents, ...(steer ? { mode } : {}) } },\n () => ctx.bridge.sendPrompt(sessionId, text, images, documents, { mode, clientSendId }),\n )\n ctx.send('s2c.ack', receipt)\n return\n }\n const userSeq = await ctx.bridge.sendPrompt(sessionId, text, images, documents, { mode })\n if (!userSeq.ok) return ctx.fail(userSeq.code, userSeq.message)\n ctx.send('s2c.ack', { userSeq: userSeq.value })\n },\n },\n {\n // 删除或转向一条排队项。与发送同属 prompt.send:都在改这个会话接下来要跑的输入。\n type: 'c2s.session.queue.update',\n stage: 'authenticated',\n scopes: ['prompt.send'],\n capability: 'promptQueue',\n doc: 'PROTOCOL.md c2s.session.queue.update',\n validate: (payload) => {\n const checked = payloadObject(payload)\n if (!checked.ok) return checked\n const invalid = requireSessionId(checked.value)\n if (invalid) return invalid\n if (!isText(checked.value.itemId, 256)) return reject('E_PROTOCOL', 'invalid itemId')\n const action = checked.value.action\n // edit 是协议预留值:首版不开放文本编辑(DSH 只接受非空纯文本,带图片的项无法原样编辑)。\n if (action === 'edit') return reject('E_UNSUPPORTED', 'queue edit not supported')\n if (action !== 'remove' && action !== 'steer') return reject('E_PROTOCOL', 'invalid queue action')\n return accept({ sessionId: checked.value.sessionId, itemId: checked.value.itemId, action })\n },\n handle: async (ctx, payload) => {\n const result = await ctx.bridge.updateQueue(\n payload.sessionId as string,\n payload.itemId as string,\n payload.action as 'remove' | 'steer',\n )\n if (!result.ok) return ctx.fail(result.code, result.message)\n ctx.send('s2c.ack', result.value)\n },\n },\n]\n","import { isText, payloadObject, reject, type CheckedPayload, type WireFrameRow } from './wire-registry.ts'\nimport { hostFailureOf, wireErrorOf } from './wire-errors.ts'\nimport type { CommandAttachment } from './command-api.ts'\nimport type { SessionExportFormat } from './protocol.ts'\nimport { BINARY_CHUNK_BYTES } from './binary-frame.ts'\n\nfunction sessionPayload(value: unknown): CheckedPayload {\n const result = payloadObject(value)\n return result.ok && !isText(result.value.sessionId) ? reject('E_PROTOCOL', '会话标识无效') : result\n}\nconst categories = ['task-result', 'instruction-following', 'product-interaction', 'service-stability', 'resource-cost', 'security-privacy-permission', 'other']\nexport const commandRows: readonly WireFrameRow[] = [\n ...(['commands', 'permissions', 'feedback', 'command'] as const).map((name): WireFrameRow => ({\n type: `c2s.session.${name}`, stage: 'authenticated', capability: 'commands',\n scopes: name === 'commands' || name === 'permissions' ? ['sessions.read'] : ['prompt.send'],\n doc: `PROTOCOL.md c2s.session.${name}`,\n validate: value => {\n const result = sessionPayload(value)\n if (!result.ok) return result\n const p = result.value\n if (name === 'command') {\n if (typeof p.line !== 'string' || p.line.length > 64 * 1024 || !/^\\/[a-z][a-z0-9_-]*(?:\\s|$)/.test(p.line)) return reject('E_PROTOCOL', '指令格式无效')\n if (p.expectedDefinitionId !== undefined && (typeof p.expectedDefinitionId !== 'string' || !p.expectedDefinitionId || p.expectedDefinitionId.length > 512)) return reject('E_PROTOCOL', '指令定义标识无效')\n const attachments = p.attachments ?? []\n if (!Array.isArray(attachments) || attachments.length > 4) return reject('E_PROTOCOL', '附件数量无效')\n for (const item of attachments as CommandAttachment[]) {\n if (!item || !['image', 'document'].includes(item.type) || typeof item.name !== 'string' || item.name.length > 512 ||\n typeof item.mediaType !== 'string' || typeof item.data !== 'string' || item.data.length > 16 * 1024 * 1024 ||\n !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(item.data) ||\n (item.type === 'image' && !['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(item.mediaType))) return reject('E_PROTOCOL', '附件格式无效')\n }\n return { ok: true, value: { ...p, attachments } }\n }\n if (name === 'feedback' && (typeof p.text !== 'string' || p.text.length > 64 * 1024 || (p.category !== undefined && !categories.includes(String(p.category))))) return reject('E_PROTOCOL', '反馈内容无效')\n return result\n },\n handle: async (ctx, payload) => {\n const operation = name === 'commands' ? 'list' : name === 'command' ? 'execute' : name\n const result = await ctx.bridge.commandOperation(operation, payload)\n if (!result.ok) return ctx.fail(result.code, result.message)\n const type = name === 'commands' ? 's2c.session.commands' : name === 'permissions' ? 's2c.session.permissions' : name === 'command' ? 's2c.session.commandResult' : 's2c.ack'\n ctx.send(type, { sessionId: payload.sessionId, ...(result.value as object) })\n },\n })),\n {\n type: 'c2s.session.export', stage: 'authenticated', scopes: ['sessions.read'], capability: 'commands',\n conditionalCapabilities: ['sessionMarkdown'],\n doc: 'PROTOCOL.md c2s.session.export',\n validate: value => {\n const result = sessionPayload(value)\n if (!result.ok) return result\n if (result.value.format !== undefined && result.value.format !== 'zip' && result.value.format !== 'markdown') return reject('E_PROTOCOL', '导出格式无效')\n if (result.value.binary !== undefined && typeof result.value.binary !== 'boolean') return reject('E_PROTOCOL', '传输方式无效')\n return result\n },\n handle: async (ctx, payload) => {\n const format = payload.format as SessionExportFormat | undefined\n if (format === 'markdown' && ctx.bridge.capabilities.sessionMarkdown !== true) return ctx.fail('E_UNSUPPORTED', '当前主机不支持导出对话正文')\n const signal = AbortSignal.any([ctx.signal ?? new AbortController().signal, AbortSignal.timeout(10 * 60_000)])\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined\n const cancel = () => { void reader?.cancel().catch(() => {}) }\n try {\n const response = format === undefined\n ? await ctx.bridge.exportSession(payload.sessionId as string, signal)\n : await ctx.bridge.exportSession(payload.sessionId as string, signal, format)\n reader = response.body!.getReader()\n signal.addEventListener('abort', cancel, { once: true })\n // 客户端带 binary:true(且已看到 binaryFrames 能力)才走二进制帧;旧客户端的帧逐字节不变。\n const binary = payload.binary === true && ctx.sendBinary !== undefined\n let index = 0, bytes = 0\n const sendChunk = async (chunk: Uint8Array) => {\n bytes += chunk.length\n await ctx.waitForWritable?.()\n if (binary) ctx.sendBinary!('s2c.session.export.chunk', { index: index++ }, chunk)\n else ctx.send('s2c.session.export.chunk', { index: index++, data: Buffer.from(chunk).toString('base64') })\n }\n // 二进制模式攒满 256 KiB 再发:宿主流每次只给几十 KiB,逐次转发会产生大量小帧。\n let carry: Buffer | undefined\n while (true) {\n signal.throwIfAborted()\n const next = await reader.read()\n if (next.done) break\n if (!binary) {\n for (let offset = 0; offset < next.value.length; offset += 48 * 1024) await sendChunk(next.value.subarray(offset, offset + 48 * 1024))\n continue\n }\n carry = carry ? Buffer.concat([carry, next.value]) : Buffer.from(next.value)\n while (carry.length >= BINARY_CHUNK_BYTES) {\n await sendChunk(carry.subarray(0, BINARY_CHUNK_BYTES))\n carry = carry.subarray(BINARY_CHUNK_BYTES)\n }\n }\n signal.throwIfAborted()\n if (carry !== undefined && carry.length > 0) await sendChunk(carry)\n // 缺省格式的旧客户端按原回执结束;显式选择格式才附媒体类型,避免改变旧帧字节。\n ctx.send('s2c.ack', { sessionId: payload.sessionId, fileName: format === 'markdown' ? 'dsh-session.md' : 'dsh-session-log.zip', chunks: index, bytes,\n ...(format === undefined ? {} : { mediaType: format === 'markdown' ? 'text/markdown; charset=utf-8' : 'application/zip' }) })\n } catch (error) {\n // 归档失败不止一种:主机没生成归档、内容类型不符、十分钟超时,或者本次\n // 连接断开。此前这里统一成一句「请重试」,把主机给的真实原因也一并吞掉;\n // 现在按域表翻译,主机的中文原因原样带给手机,只有取消/超时这类没有上游\n // 码的抛出物才落回兜底文案。断线同样走这里——它必须留在「不报成功」一侧。\n const failure = hostFailureOf(error, 'command/export-cancelled')\n const message = failure.code === 'command/export-cancelled' ? '会话日志导出失败,请重试' : failure.message\n const mapped = wireErrorOf('command', { ...failure, message })\n ctx.fail(mapped.code, mapped.message)\n }\n finally { signal.removeEventListener('abort', cancel); await reader?.cancel().catch(() => {}); reader?.releaseLock() }\n },\n },\n]\n","import { createHash, randomUUID } from 'node:crypto'\nimport { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { PROTOCOL_VERSION, type PluginUpdatePhase, type PluginUpdateProgressPayload, type PluginUpdateStatusPayload } from './protocol.ts'\n\n/** Structural subset checked against dsh-plugin-manager's real published declarations.\n * Keep this optional: older supported hosts do not ship updateBundle. */\nexport interface UpdateChangeResult {\n changed: boolean\n application: 'applied' | 'restart-required' | 'overridden' | 'failed' | 'cancelled'\n stage: 'install' | 'enable' | 'remove'\n target: string\n version?: string\n restartReason?: string\n error?: { code: string; incompatible?: unknown[] }\n packageResult?: { incompatible?: unknown[]; kind?: 'pnpm-missing' | 'timeout' | 'not-found' | 'no-matching-version' | 'network' | 'disk-full' | 'permission' | 'build-blocked' | 'integrity' | 'unknown' }\n pendingBuilds?: string[]\n}\nexport interface PluginUpdateRegistries {\n registry: string | null\n fallbackRegistries: readonly string[]\n resolved: string | null\n}\n\nexport interface PluginManagerForUpdate {\n listVersions(name: string): Promise<\n | { status: 'listed'; installed: string; registry: string | null; versions: readonly { version: string; bundle: boolean; incompatible?: { name: string; version: string; runtimeVersion: string; requirements: Record<string, string> } }[] }\n | { status: 'refused'; problem: string; reason: string }>\n registries(): Promise<PluginUpdateRegistries>\n listBundles(): Promise<readonly { name: string; version?: string }[]>\n updateBundle(name: string, version: string, options?: { registry?: string | null; requestId?: string }): Promise<{\n from: string; to: string; step: 'check' | 'remove' | 'install'; result: UpdateChangeResult; restore?: UpdateChangeResult\n }>\n}\n\nconst PACKAGE = 'dsh-deeppilot'\nconst PHASES: readonly PluginUpdatePhase[] = ['preparing', 'installing', 'verifying', 'restart-required', 'done', 'rolled-back', 'failed']\nexport interface UpdateJournalRecord {\n clientRequestId: string\n fromVersion: string\n toVersion: string\n phase: PluginUpdatePhase\n startedAt: number\n message: string\n}\ninterface StoredRecord extends UpdateJournalRecord {\n ownerKey: string\n restartVersion?: string\n restartPhase?: 'done' | 'rolled-back'\n restartBoot?: string\n}\ninterface SharedState {\n bootId: string\n records: StoredRecord[]\n listeners: Set<(event: PluginUpdateProgressPayload, owner: string) => void>\n runningVersion: string\n active?: Promise<void>\n pending?: { record: StoredRecord; accepted: Promise<UpdateJournalRecord> }\n healthy: boolean\n}\n// A plugin HMR unload must not own or cancel the package-manager operation. The\n// process-wide cell also stops the newly loaded copy mistaking an active update\n// for an interrupted one. Only durable, credential-free facts cross restarts.\nconst CELLS = Symbol.for('deeppilot.plugin-update.cells.v1')\nconst globals = globalThis as typeof globalThis & { [CELLS]?: Map<string, SharedState> }\nconst cells = globals[CELLS] ??= new Map<string, SharedState>()\nconst ownerKey = (deviceId: string) => createHash('sha256').update(deviceId).digest('hex')\nconst validVersion = (value: unknown): value is string => typeof value === 'string' && /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/.test(value) && value.length <= 128\nexport const validUpdateRequestId = (value: unknown): value is string => typeof value === 'string' && /^[A-Za-z0-9._:-]{1,128}$/.test(value)\nconst terminal = (phase: PluginUpdatePhase) => ['done', 'failed', 'rolled-back', 'restart-required'].includes(phase)\nconst publicRecord = ({ ownerKey: _owner, restartVersion: _restartVersion, restartPhase: _restartPhase, restartBoot: _restartBoot, ...record }: StoredRecord): UpdateJournalRecord => ({ ...record })\n\nexport class PluginUpdateError extends Error {\n constructor(readonly code: 'E_PROTOCOL' | 'E_FORBIDDEN' | 'E_UNSUPPORTED' | 'E_BUSY' | 'E_INTERNAL', message: string) { super(message) }\n}\n\nexport interface PluginUpdaterOptions {\n manager: PluginManagerForUpdate\n currentVersion: string\n journalPath: string\n /** Tests supply an isolated registry response; production never reads npm credentials. */\n fetch?: typeof fetch\n}\n\n/** 精确镜像 alpha2 公开 registryPlan,避免静态依赖让旧宿主无法加载插件;由真实函数对照测试守卫。 */\nexport function updateRegistryPlan(requested: string | null | undefined, configured: PluginUpdateRegistries): Array<string | null> {\n const own = configured.resolved === null ? null : normalizeUpdateRegistry(configured.resolved)\n const fallbacks = configured.fallbackRegistries.map(normalizeUpdateRegistry)\n const ownIsPublic = own !== null && (own === 'https://registry.npmjs.org/' || fallbacks.includes(own))\n const keyOf = (registry: string | null) => registry === null ? own : normalizeUpdateRegistry(registry)\n const known: Array<string | null> = []\n const keys: Array<string | null> = []\n for (const registry of [configured.registry, ...configured.fallbackRegistries]) {\n if (registry === null && !ownIsPublic) continue\n const key = keyOf(registry)\n if (keys.includes(key)) continue\n known.push(registry === null ? null : normalizeUpdateRegistry(registry))\n keys.push(key)\n }\n const first = requested === undefined ? configured.registry : requested\n const firstKey = keyOf(first)\n const normalizedFirst = first === null ? null : normalizeUpdateRegistry(first)\n if ((first === null || firstKey === own) && !ownIsPublic) return [normalizedFirst]\n if (!keys.includes(firstKey)) return [normalizedFirst]\n return [normalizedFirst, ...known.filter((_registry, index) => keys[index] !== firstKey)]\n}\n\nfunction normalizeUpdateRegistry(registry: string): string {\n const parsed = new URL(registry)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') throw new Error('registry unavailable')\n if (!parsed.pathname.endsWith('/')) parsed.pathname += '/'\n return parsed.href\n}\n\nfunction updateRegistrySources(requested: string | null, configured: PluginUpdateRegistries): string[] {\n return [...new Set(updateRegistryPlan(requested, configured).map(registry => {\n const resolved = registry === null ? configured.resolved : registry\n if (!resolved) throw new Error('registry unresolved')\n return normalizeUpdateRegistry(resolved)\n }))]\n}\n\n/** 只接受具有完整规范摘要的强 SRI;宽松 base64 解码不能把截断值或弱摘要当作安装凭据。 */\nfunction strongIntegrity(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const match = /^(sha256|sha384|sha512)-([A-Za-z0-9+/]+={0,2})$/.exec(value)\n if (!match) return undefined\n const digest = Buffer.from(match[2]!, 'base64')\n const bytes = { sha256: 32, sha384: 48, sha512: 64 }[match[1] as 'sha256' | 'sha384' | 'sha512']\n return digest.length === bytes && digest.toString('base64') === match[2] ? value : undefined\n}\n\n/** 精确版本事实由元数据读取;软件源地址、上游响应体及包管理器诊断不进入对外错误。 */\nasync function readTargetManifestFacts(registry: string, version: string, fetcher: typeof fetch): Promise<{ protocolVersion?: number; integrity?: string }> {\n const base = new URL(registry)\n if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password || base.search || base.hash || !validVersion(version)) throw new Error('registry unavailable')\n base.pathname = base.pathname.replace(/\\/?$/, '/') + encodeURIComponent(PACKAGE) + '/' + encodeURIComponent(version)\n const response = await fetcher(base, { signal: AbortSignal.timeout(10_000), redirect: 'error', headers: { accept: 'application/json' } })\n if (!response.ok || !response.body) throw new Error('manifest unavailable')\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let bytes = 0\n try {\n while (true) {\n const next = await reader.read()\n if (next.done) break\n bytes += next.value.length\n if (bytes > 1024 * 1024) throw new Error('manifest too large')\n chunks.push(next.value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const manifest = JSON.parse(Buffer.concat(chunks).toString('utf8')) as { name?: unknown; version?: unknown; deepPilot?: { protocolVersion?: unknown }; dsh?: { bundle?: unknown }; dist?: { integrity?: unknown } }\n if (manifest.name !== PACKAGE || manifest.version !== version || !manifest.dsh?.bundle) throw new Error('manifest mismatch')\n const protocol = manifest.deepPilot?.protocolVersion\n return { protocolVersion: typeof protocol === 'number' && Number.isSafeInteger(protocol) && protocol > 0 ? protocol : undefined,\n integrity: strongIntegrity(manifest.dist?.integrity) }\n}\n\n/** 检查界面只预览首个软件源的协议;实际安装必须通过全部可回退源的强校验。 */\nexport async function readTargetProtocol(registry: string, version: string, fetcher: typeof fetch = fetch): Promise<number | undefined> {\n return (await readTargetManifestFacts(registry, version, fetcher)).protocolVersion\n}\n\nexport class PluginUpdater {\n private readonly state: SharedState\n private readonly path: string\n private readonly listVersions: PluginManagerForUpdate['listVersions']\n private readonly updateBundle: PluginManagerForUpdate['updateBundle']\n private readonly listBundles: PluginManagerForUpdate['listBundles']\n private readonly registries: PluginManagerForUpdate['registries']\n\n constructor(private readonly options: PluginUpdaterOptions) {\n this.path = resolve(options.journalPath)\n // Capture the host service while this context is live. No continuation uses\n // the disposed plugin context or the phone socket's abort signal.\n this.listVersions = options.manager.listVersions.bind(options.manager)\n this.updateBundle = options.manager.updateBundle.bind(options.manager)\n this.listBundles = options.manager.listBundles.bind(options.manager)\n this.registries = options.manager.registries.bind(options.manager)\n let state = cells.get(this.path)\n if (!state) {\n state = { bootId: randomUUID(), records: [], listeners: new Set(), runningVersion: options.currentVersion, healthy: true }\n if (existsSync(this.path)) {\n try {\n if (statSync(this.path).size > 256 * 1024) throw new Error('oversized journal')\n const data = JSON.parse(readFileSync(this.path, 'utf8')) as { version?: unknown; records?: StoredRecord[] }\n if (data.version !== 1 || !Array.isArray(data.records) || data.records.length > 128 || data.records.some(record =>\n !record || !validUpdateRequestId(record.clientRequestId) || !validVersion(record.fromVersion) || !validVersion(record.toVersion) ||\n !PHASES.includes(record.phase) || !Number.isSafeInteger(record.startedAt) || record.startedAt < 0 ||\n typeof record.message !== 'string' || record.message.length > 1024 || !/^[a-f0-9]{64}$/.test(record.ownerKey) ||\n (record.restartVersion !== undefined && !validVersion(record.restartVersion)) ||\n (record.restartBoot !== undefined && (typeof record.restartBoot !== 'string' || record.restartBoot.length > 128)) ||\n (record.restartPhase !== undefined && record.restartPhase !== 'done' && record.restartPhase !== 'rolled-back')) ||\n new Set(data.records.map(record => record.clientRequestId)).size !== data.records.length) throw new Error('invalid journal')\n state.records = data.records\n } catch { state.healthy = false }\n }\n cells.set(this.path, state)\n }\n this.state = state\n state.runningVersion = options.currentVersion\n }\n\n private save(): void {\n mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 })\n const temp = this.path + '.' + randomUUID() + '.tmp'\n const fd = openSync(temp, 'wx', 0o600)\n try { writeFileSync(fd, JSON.stringify({ version: 1, records: this.state.records })); fsyncSync(fd) } finally { closeSync(fd) }\n renameSync(temp, this.path)\n const directory = openSync(dirname(this.path), 'r')\n try { fsyncSync(directory) } catch (error) {\n // These platforms cannot fsync directories. File fsync and same-directory\n // rename already succeeded; all actual I/O failures still refuse progress.\n if (!['EPERM', 'EISDIR', 'EINVAL'].includes(String((error as NodeJS.ErrnoException).code))) throw error\n } finally { closeSync(directory) }\n }\n\n private transition(record: StoredRecord, phase: PluginUpdatePhase, message: string): void {\n const before = { ...record }\n record.phase = phase\n record.message = message\n if (phase === 'restart-required') record.restartBoot = this.state.bootId\n // Never publish a terminal result that cannot survive the reconnect.\n try { this.save() } catch (error) { Object.assign(record, before); record.restartBoot = before.restartBoot; throw error }\n for (const listener of this.state.listeners) {\n try { listener({ clientRequestId: record.clientRequestId, phase, message }, record.ownerKey) } catch { /* disconnected sink */ }\n }\n }\n\n subscribe(deviceId: string, listener: (event: PluginUpdateProgressPayload) => void): () => void {\n const key = ownerKey(deviceId)\n const wrapped = (event: PluginUpdateProgressPayload, owner: string) => { if (owner === key) listener(event) }\n this.state.listeners.add(wrapped)\n return () => { this.state.listeners.delete(wrapped) }\n }\n\n private recover(): void {\n if (this.state.active || !this.state.healthy) return\n for (const record of this.state.records) {\n if (record.phase === 'restart-required' && record.restartBoot !== this.state.bootId && this.options.currentVersion === (record.restartVersion ?? record.toVersion)) {\n this.transition(record, record.restartPhase ?? 'done', record.restartPhase === 'rolled-back' ? '重启后已运行旧版本,宿主回滚完成' : '重启后已运行目标版本,更新完成')\n } else if (!terminal(record.phase)) {\n if (this.options.currentVersion === record.toVersion) this.transition(record, 'done', '已重新连接到目标版本,更新完成')\n else this.transition(record, 'failed', '上次更新已中断,结果无法确认;请检查当前版本,原请求不会自动重试')\n }\n }\n }\n\n async check(deviceId: string): Promise<PluginUpdateStatusPayload> {\n let reason: string | undefined\n try { this.recover() } catch { reason = '更新日志保存失败,暂时无法确认更新结果' }\n if (!this.state.healthy) reason = '更新日志无法读取,已停用安装以保护上次更新记录'\n const finish = (status: PluginUpdateStatusPayload): PluginUpdateStatusPayload => {\n // The host operation can settle while registry reads are in flight. Take\n // the journal snapshot when replying, so a fresh terminal never regresses\n // to the preparing/installing snapshot captured at check's start.\n const journal = [...this.state.records].reverse().find(record => record.ownerKey === ownerKey(deviceId))\n return { ...status, ...(journal ? { journal: publicRecord(journal) } : {}) }\n }\n const base = {\n currentVersion: this.options.currentVersion, protocolVersion: PROTOCOL_VERSION,\n latestVersion: null, updateAvailable: false, compatible: false, protocolCompatible: false,\n versions: [], checkedAt: Date.now(),\n }\n try {\n const listed = await this.listVersions(PACKAGE)\n if (listed.status !== 'listed') return finish({ ...base, reason: reason ?? '暂时无法读取可更新版本,请稍后重试' })\n const registry = listed.registry ?? (await this.registries()).resolved\n if (!registry) return finish({ ...base, reason: reason ?? '无法确定软件源,暂时不能核对目标版本协议' })\n const versions = await Promise.all(listed.versions.slice(0, 10).map(async entry => {\n let protocolVersion: number | undefined\n try { protocolVersion = await readTargetProtocol(registry, entry.version, this.options.fetch) } catch { /* unavailable target stays unselectable */ }\n return { version: entry.version, compatible: entry.bundle && entry.incompatible === undefined, ...(protocolVersion === undefined ? {} : { protocolVersion }) }\n }))\n const latest = versions[0]\n return finish({ ...base, latestVersion: latest?.version ?? null, updateAvailable: versions.length > 0,\n compatible: latest?.compatible ?? false, protocolCompatible: latest?.protocolVersion === PROTOCOL_VERSION, versions,\n ...(reason ? { reason } : versions.some(entry => entry.protocolVersion === undefined) ? { reason: '部分目标版本缺少可核验的协议声明,暂时不可安装' } : {}) })\n } catch { return finish({ ...base, reason: reason ?? '暂时无法检查更新,请确认网络后重试' }) }\n }\n\n /** The apply frame is the phone's explicit confirmation. check() never mutates packages. */\n async apply(deviceId: string, version: string, clientRequestId: string): Promise<UpdateJournalRecord> {\n if (!validVersion(version) || !validUpdateRequestId(clientRequestId)) throw new PluginUpdateError('E_PROTOCOL', '更新版本或请求标识无效')\n if (!this.state.healthy) throw new PluginUpdateError('E_INTERNAL', '更新日志无法读取,暂时不能安装')\n const previous = this.state.records.find(record => record.clientRequestId === clientRequestId)\n if (previous) {\n if (previous.ownerKey !== ownerKey(deviceId)) throw new PluginUpdateError('E_FORBIDDEN', '更新请求不属于当前设备')\n if (previous.toVersion !== version) throw new PluginUpdateError('E_PROTOCOL', '同一更新请求不能更换目标版本')\n this.recover()\n return publicRecord(previous)\n }\n const pending = this.state.pending\n if (pending?.record.clientRequestId === clientRequestId) {\n if (pending.record.ownerKey !== ownerKey(deviceId)) throw new PluginUpdateError('E_FORBIDDEN', '更新请求不属于当前设备')\n if (pending.record.toVersion !== version) throw new PluginUpdateError('E_PROTOCOL', '同一更新请求不能更换目标版本')\n return pending.accepted\n }\n if (this.state.active) throw new PluginUpdateError('E_BUSY', '已有插件更新正在进行,请稍后检查结果')\n if (this.state.records.length >= 128) throw new PluginUpdateError('E_INTERNAL', '更新记录已满,请在电脑端检查后再更新')\n const record: StoredRecord = { clientRequestId, fromVersion: this.options.currentVersion, toVersion: version,\n phase: 'preparing', startedAt: Date.now(), message: '目标校验已通过,准备开始安装', ownerKey: ownerKey(deviceId) }\n let accept!: (record: UpdateJournalRecord) => void\n let refuse!: (error: unknown) => void\n const accepted = new Promise<UpdateJournalRecord>((resolve_, reject_) => { accept = resolve_; refuse = reject_ })\n this.state.pending = { record, accepted }\n // 预检第一次等待前就保留活动槽;同编号共享受理结果,其他请求不能越过预检并发安装。\n const operation = Promise.resolve().then(async () => {\n let registry: string | null\n try {\n registry = await this.prepare(record)\n this.state.records.push(record)\n try { this.save() } catch {\n this.state.records.splice(this.state.records.indexOf(record), 1)\n throw new PluginUpdateError('E_INTERNAL', '更新日志保存失败,尚未开始安装')\n }\n } catch (error) {\n if (this.state.active === operation) this.state.active = undefined\n if (this.state.pending?.record === record) this.state.pending = undefined\n refuse(error instanceof PluginUpdateError ? error : new PluginUpdateError('E_INTERNAL', '无法核对目标版本,尚未开始安装'))\n return\n }\n if (this.state.pending?.record === record) this.state.pending = undefined\n accept(publicRecord(record))\n // 先让 wire 发送受理回执,再进入可能卸载本插件的宿主操作;不借用 socket 的生命周期。\n await new Promise<void>(resolve_ => setImmediate(resolve_))\n try { await this.run(record, registry) } catch {\n try { this.transition(record, 'failed', '更新结果无法确认,请检查当前版本;原请求不会自动重试') } catch { /* 保留最后一个耐久中间态供重连核对。 */ }\n }\n }).finally(() => { if (this.state.active === operation) this.state.active = undefined })\n this.state.active = operation\n return accepted\n }\n\n /** Useful to isolated tests; production never waits for this from disposal. */\n async settled(): Promise<void> { await this.state.active }\n\n /** 所有可执行目标校验在受理前完成;宿主安装及其回滚处理仍属于独立的后续任务。 */\n private async prepare(record: StoredRecord): Promise<string | null> {\n let listed: Awaited<ReturnType<PluginManagerForUpdate['listVersions']>>\n try { listed = await this.listVersions(PACKAGE) } catch { throw new PluginUpdateError('E_INTERNAL', '无法核对可更新版本,尚未开始安装') }\n if (listed.status !== 'listed' || listed.installed !== record.fromVersion) throw new PluginUpdateError('E_PROTOCOL', '安装版本已变化或无法读取,请重新检查更新')\n const target = listed.versions.find(entry => entry.version === record.toVersion)\n if (!target?.bundle || target.incompatible !== undefined) throw new PluginUpdateError('E_UNSUPPORTED', '目标版本不存在或不兼容当前宿主,尚未开始安装')\n try {\n const sources = updateRegistrySources(listed.registry, await this.registries())\n let integrity: string | undefined\n for (const source of sources) {\n const facts = await readTargetManifestFacts(source, record.toVersion, this.options.fetch ?? fetch)\n if (facts.protocolVersion !== PROTOCOL_VERSION) throw new PluginUpdateError('E_UNSUPPORTED', '候选软件源的目标协议不兼容或缺少声明,尚未开始安装')\n if (!facts.integrity) throw new PluginUpdateError('E_UNSUPPORTED', '候选软件源缺少有效的强完整性摘要,尚未开始安装')\n if (integrity !== undefined && facts.integrity !== integrity) throw new PluginUpdateError('E_UNSUPPORTED', '候选软件源的目标包完整性摘要不一致,尚未开始安装')\n integrity = facts.integrity\n }\n // 宿主的查询和安装阶段会重新计算计划;预检中配置变动时不能继续使用旧候选集合。\n const current = updateRegistrySources(listed.registry, await this.registries())\n if (JSON.stringify(current) !== JSON.stringify(sources)) throw new PluginUpdateError('E_BUSY', '软件源配置已变化,请重新检查更新')\n } catch (error) {\n if (error instanceof PluginUpdateError) throw error\n throw new PluginUpdateError('E_INTERNAL', '无法核验全部候选软件源的目标协议与完整性,尚未开始安装')\n }\n return listed.registry\n }\n\n private async run(record: StoredRecord, registry: string | null): Promise<void> {\n this.transition(record, 'installing', '宿主正在安装目标版本,连接可能暂时中断')\n const outcome = await this.updateBundle(PACKAGE, record.toVersion, { registry, requestId: record.clientRequestId })\n const result = outcome.result\n if (outcome.from !== record.fromVersion || outcome.to !== record.toVersion) return this.transition(record, 'failed', '宿主返回的更新版本不一致,请检查当前安装')\n if (outcome.restore) {\n const restored = outcome.restore\n if (restored.application === 'restart-required') {\n record.restartVersion = record.fromVersion\n record.restartPhase = 'rolled-back'\n return this.transition(record, 'restart-required', '目标安装失败,旧版已重新安装;请在电脑端重启 DSH 后确认恢复')\n }\n if (restored.application === 'applied' && (restored.version === undefined || restored.version === record.fromVersion)) {\n const installed = (await this.listBundles()).find(bundle => bundle.name === PACKAGE)\n if (installed?.version === record.fromVersion) return this.transition(record, 'rolled-back', '目标安装失败,宿主已自动恢复旧版本')\n return this.transition(record, 'failed', '宿主已尝试回滚,但无法核对实际旧版本,请在电脑端检查插件')\n }\n return this.transition(record, 'failed', '目标安装失败,宿主恢复旧版也未完成,请在电脑端检查插件')\n }\n if (outcome.step !== 'install' || result.application === 'failed' || result.application === 'cancelled' || result.application === 'overridden') {\n return this.transition(record, 'failed', installFailureMessage(result))\n }\n if (result.version !== undefined && result.version !== record.toVersion) return this.transition(record, 'failed', '实际安装版本与目标不一致,请在电脑端检查')\n if (result.application === 'restart-required') return this.transition(record, 'restart-required', '目标版本已安装,宿主需要重启;请在电脑端重启 DSH 后重新连接')\n this.transition(record, 'verifying', '正在核对实际安装版本')\n const installed = (await this.listBundles()).find(bundle => bundle.name === PACKAGE)\n if (installed?.version !== record.toVersion) return this.transition(record, 'failed', '无法确认实际安装版本,请在电脑端检查')\n if (this.state.runningVersion !== record.toVersion) return this.transition(record, 'restart-required', '目标版本已安装,当前插件尚未重新加载;请在电脑端重启 DSH 后重新连接')\n this.transition(record, 'done', '目标版本已加载,更新完成')\n }\n}\n\nfunction installFailureMessage(result: UpdateChangeResult): string {\n if (result.error?.code === 'incompatible-version' || result.error?.incompatible?.length || result.packageResult?.incompatible?.length) return '目标版本不兼容当前宿主,安装已拒绝(incompatible-version)'\n if (result.pendingBuilds?.length || result.packageResult?.kind === 'build-blocked') return '安装需要在电脑端明确批准构建脚本,更新未完成(build-blocked)'\n const kind = result.packageResult?.kind\n const reasons: Partial<Record<NonNullable<typeof kind>, string>> = {\n 'pnpm-missing': '电脑端缺少包管理器,更新未完成', timeout: '安装等待超时,请检查电脑端后重试',\n 'not-found': '软件源尚未提供目标包,更新未完成', 'no-matching-version': '软件源尚未提供目标版本,更新未完成',\n network: '安装无法连接软件源,请恢复网络后重新检查', 'disk-full': '电脑端磁盘空间不足,更新未完成',\n permission: '电脑端没有写入插件的权限,更新未完成', integrity: '目标包完整性校验失败,更新未完成',\n }\n if (kind && reasons[kind]) return `${reasons[kind]}(${kind})`\n if (result.application === 'cancelled') return '宿主已取消目标版本安装,请检查当前版本'\n return '宿主未完成目标版本安装,请检查当前版本'\n}\n\nexport function supportsPluginUpdate(value: unknown): value is PluginManagerForUpdate {\n if (!value || typeof value !== 'object') return false\n return ['listVersions', 'updateBundle', 'listBundles', 'registries'].every(key => typeof (value as Record<string, unknown>)[key] === 'function')\n}\n","import type { HostBridge } from './host-bridge.ts'\nimport { PluginUpdateError, type PluginUpdater, validUpdateRequestId } from './plugin-update.ts'\nimport type { CheckedPayload, FrameContext, WireFrameRow } from './wire-registry.ts'\n\n// Keep registry imports type-only: registry statically assembles these rows.\nfunction payloadObject(value: unknown): CheckedPayload {\n if (value === undefined || value === null) return { ok: true, value: {} }\n if (typeof value !== 'object' || Array.isArray(value)) return { ok: false, code: 'E_PROTOCOL', message: '更新请求必须是对象' }\n return { ok: true, value: value as Record<string, unknown> }\n}\n\nconst updaters = new WeakMap<HostBridge, PluginUpdater>()\n\n/** Bridge replacement and optional host service discovery can arrive in either order. */\nexport function setPluginUpdater(bridge: HostBridge, updater: PluginUpdater | undefined): void {\n if (updater) updaters.set(bridge, updater)\n else updaters.delete(bridge)\n}\nexport function pluginUpdaterFor(bridge: HostBridge): PluginUpdater | undefined { return updaters.get(bridge) }\n\n// 观察者属于有管理权限的连接;某次请求失败或历史终态重放不能注销其他请求仍需的观察。\nconst observers = new WeakMap<AbortSignal, Set<PluginUpdater>>()\nfunction observe(ctx: FrameContext, updater: PluginUpdater, deviceId: string): void {\n if (!ctx.canReceive('sessions.manage')) return\n const signal = ctx.signal\n if (signal?.aborted || (signal && observers.get(signal)?.has(updater))) return\n let stop = () => {}\n const unsubscribe = updater.subscribe(deviceId, event => {\n if (!ctx.canReceive('sessions.manage')) { stop(); return }\n ctx.send('s2c.plugin.update.progress', event)\n // 实际 socket 均提供取消信号;无连接生命周期的调用只观察到本次终态。\n if (!signal && finished(event.phase)) stop()\n })\n stop = () => {\n unsubscribe()\n signal?.removeEventListener('abort', stop)\n if (signal) observers.get(signal)?.delete(updater)\n }\n if (signal) {\n let connections = observers.get(signal)\n if (!connections) { connections = new Set(); observers.set(signal, connections) }\n connections.add(updater)\n signal.addEventListener('abort', stop, { once: true })\n }\n}\nconst finished = (phase: string | undefined) => phase === undefined || ['done', 'failed', 'rolled-back', 'restart-required'].includes(phase)\n\nexport const pluginUpdateRows: readonly WireFrameRow[] = [\n {\n type: 'c2s.plugin.update.check', stage: 'authenticated', scopes: ['sessions.read'],\n widgetPolicy: 'deny', capability: 'pluginUpdate', doc: 'PROTOCOL.md c2s.plugin.update.check',\n validate: payloadObject,\n handle: async (ctx) => {\n const updater = updaters.get(ctx.bridge)\n if (!updater) return ctx.fail('E_UNSUPPORTED', '当前宿主不支持插件自更新')\n if (!ctx.deviceId) return ctx.fail('E_FORBIDDEN', '请先完成设备认证')\n observe(ctx, updater, ctx.deviceId)\n const status = await updater.check(ctx.deviceId)\n ctx.send('s2c.plugin.update.status', status)\n },\n },\n {\n type: 'c2s.plugin.update.apply', stage: 'authenticated', scopes: ['sessions.manage'],\n widgetPolicy: 'deny', capability: 'pluginUpdate', doc: 'PROTOCOL.md c2s.plugin.update.apply',\n validate: value => {\n const result = payloadObject(value)\n if (!result.ok) return result\n const version = result.value.version\n if (typeof version !== 'string' || version.length > 128 || version.trim().length === 0 || !validUpdateRequestId(result.value.clientRequestId)) return { ok: false, code: 'E_PROTOCOL', message: '更新版本或请求标识无效' }\n return result\n },\n handle: async (ctx, payload) => {\n const updater = updaters.get(ctx.bridge)\n if (!updater) return ctx.fail('E_UNSUPPORTED', '当前宿主不支持插件自更新')\n if (!ctx.deviceId) return ctx.fail('E_FORBIDDEN', '请先完成设备认证')\n // 只有受理成功的活动请求才建立观察;监听生命周期归连接,拒绝和历史重放均不注销它。\n try {\n const record = await updater.apply(ctx.deviceId, payload.version as string, payload.clientRequestId as string)\n ctx.send('s2c.ack', { clientRequestId: record.clientRequestId, accepted: true })\n if (ctx.canReceive('sessions.manage')) {\n ctx.send('s2c.plugin.update.progress', { clientRequestId: record.clientRequestId, phase: record.phase, message: record.message })\n if (!finished(record.phase)) observe(ctx, updater, ctx.deviceId)\n }\n } catch (error) {\n if (error instanceof PluginUpdateError) return ctx.fail(error.code, error.message)\n ctx.fail('E_INTERNAL', '暂时无法启动插件更新,请检查当前版本')\n }\n },\n },\n]\n","/**\n * wire 注册表(wire registry)——phone wire 缝的 host 侧属主。\n *\n * 一帧一行:`stage`(认证前/后)、`scopes`(全部满足)、`widgetPolicy`\n * (widget 短连接可读性)、`capability`(welcome 位门)、`validate`\n * (形状与资源上限)、`handle`(帧逻辑)。此前这些事实分散在四个模块里,\n * 各用一种风格枚举帧类型:connection.ts 的 switch、connection-policy 的\n * requiredScope if 链、request-validation 的 if 链、host-bridge 的\n * capabilities getter 与方法内 typeof 探测。\n *\n * 本 module 只提供表的形状、检查与 dispatch;handler 体按特性住在\n * wire-session / wire-schedule / wire-push / wire-device / wire-interaction,\n * 各自向这里登记自己的行(静态导入,无 import 副作用)。\n *\n * 帧的不变量由 tests/wire-registry.test.ts 逐行断言:每位被闸的帧都绑定一个\n * 已存在的 welcome 位、每个 scope 都是合法 DeviceScope、每个类型都有行、\n * 每行都指向 PROTOCOL.md 的一个小节。\n */\n\nimport type { Envelope } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { WelcomeCapabilities } from './protocol.ts'\nimport type { DeviceStore } from './token.ts'\nimport type { DeviceScope } from './device-auth.ts'\nimport type { CapabilityGate } from './host-capabilities.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\n\n/** 出站帧字面量:与入站共用一份词汇,record() 随后只接受这个子集。 */\nexport type S2CType =\n | 's2c.ack'\n | 's2c.auth.challenge'\n | 's2c.directory.listing'\n | 's2c.directory.picked'\n | 's2c.error'\n | 's2c.history.page'\n | 's2c.notify'\n | 's2c.pending.approval'\n | 's2c.pending.cleared'\n | 's2c.pending.question'\n | 's2c.pending.snapshot'\n | 's2c.pong'\n | 's2c.resume.done'\n | 's2c.resync'\n | 's2c.schedule.changed'\n | 's2c.schedule.history'\n | 's2c.schedule.host.limits'\n | 's2c.schedule.host.snapshot'\n | 's2c.schedule.host.task'\n | 's2c.schedule.snapshot'\n | 's2c.schedule.updated'\n | 's2c.session.archived'\n | 's2c.session.event'\n | 's2c.session.delta'\n | 's2c.plugin.update.status'\n | 's2c.plugin.update.progress'\n | 's2c.session.forked'\n | 's2c.session.models'\n | 's2c.session.skills'\n | 's2c.skills.changed'\n | 's2c.commands.changed'\n | 's2c.models.changed'\n | 's2c.session.commands'\n | 's2c.session.commandResult'\n | 's2c.session.permissions'\n | 's2c.session.queue'\n | 's2c.session.export.chunk'\n | 's2c.session.attachment.chunk'\n | 's2c.session.modelSelected'\n | 's2c.session.renamed'\n | 's2c.session.tail'\n | 's2c.session.unarchived'\n | 's2c.sessions.archived.snapshot'\n | 's2c.sessions.delta'\n | 's2c.sessions.snapshot'\n | 's2c.welcome'\n | 's2c.workspace.created'\n | 's2c.workspaces.snapshot'\n\n/**\n * 入站帧字面量 = 本 Bridge 实现的全部客户端帧。\n *\n * 注意 `c2s.resume` 不在其中:PROTOCOL.md 把它与 c2s.ping 并列为控制帧,但\n * 续传由 `c2s.auth.prove` 的 `resumeCursor` 字段承载,没有独立的 resume 帧\n * (历史上 requiredScope 为它留过一个无人使用的条目,见 G7)。\n */\nexport type C2SType =\n | 'c2s.ping'\n | 'c2s.auth.prove'\n | 'c2s.sessions.list'\n | 'c2s.sessions.archived'\n | 'c2s.pending.list'\n | 'c2s.workspaces.list'\n | 'c2s.workspace.create'\n | 'c2s.directory.list'\n | 'c2s.directory.pick'\n | 'c2s.session.open'\n | 'c2s.session.close'\n | 'c2s.session.create'\n | 'c2s.session.fork'\n | 'c2s.session.rename'\n | 'c2s.session.archive'\n | 'c2s.session.unarchive'\n | 'c2s.session.cancel'\n | 'c2s.session.history'\n | 'c2s.session.attachment'\n | 'c2s.session.models'\n | 'c2s.session.skills'\n | 'c2s.session.commands'\n | 'c2s.session.command'\n | 'c2s.session.permissions'\n | 'c2s.session.feedback'\n | 'c2s.session.export'\n | 'c2s.plugin.update.check'\n | 'c2s.plugin.update.apply'\n | 'c2s.session.selectModel'\n | 'c2s.session.delivery'\n | 'c2s.session.sendPrompt'\n | 'c2s.session.queue.update'\n | 'c2s.schedule.list'\n | 'c2s.schedule.history'\n | 'c2s.schedule.create'\n | 'c2s.schedule.update'\n | 'c2s.schedule.delete'\n | 'c2s.schedule.host.list'\n | 'c2s.schedule.host.view'\n | 'c2s.schedule.host.create'\n | 'c2s.schedule.host.edit'\n | 'c2s.schedule.host.run'\n | 'c2s.schedule.host.deleteEnded'\n | 'c2s.schedule.host.limits'\n | 'c2s.approval.respond'\n | 'c2s.question.respond'\n | 'c2s.device.revoke'\n | 'c2s.liveActivity.register'\n | 'c2s.liveActivity.unregister'\n | 'c2s.push.register'\n | 'c2s.widget.push.register'\n\n/** 帧在连接生命周期里允许出现的阶段。 */\nexport type FrameStage = 'pre-auth' | 'authenticated'\n\n/**\n * widget 短连接(clientRole: \"widget\")的帧策略。\n *\n * - `allowed`:只读面板需要的少量帧;\n * - `deny`:通用只读拒绝;\n * - `handler`:交给 handler 用自己的理由拒绝——用于 c2s.device.revoke,\n * 通用门禁只会说 \"read-only\",而规范要求给出「小组件不得解绑设备」的\n * 具体原因(PROTOCOL.md device.revoke 一节)。\n */\nexport type WidgetPolicy = 'allowed' | 'deny' | 'handler'\n\nexport const DEFAULT_WIDGET_POLICY: WidgetPolicy = 'deny'\n\n/** 拒绝:wire 错误码 + 可读信息。undefined 表示通过。 */\nexport interface WireRejection {\n code: WireErrorCode\n message: string\n}\n\n/**\n * 校验结果:通过时带上「归一化后的载荷」——形状检查与归一化同处一行,\n * handler 拿到的就是可直接使用的值(比如 sendPrompt 的 images/documents\n * 已在此时完成媒体类型与长度清洗)。拒绝时带 wire 错误码与可读信息。\n */\nexport type CheckedPayload =\n | { ok: true; value: Record<string, unknown> }\n | ({ ok: false } & WireRejection)\n\n/** 一帧在打开历史期间缓存的实时事件;连接持有所有权。 */\nexport interface OpenEventBuffer {\n readonly frames: Array<{ type: string; payload: unknown; seq?: number }>\n}\n\n/**\n * handler 看到的每帧 facade:把当前帧的 id 绑进 send/fail,handler 不必传 id;\n * 连接自身状态(打开中的会话缓冲、live activity 代次、撤销标志)以少量操作\n * 暴露,所有权仍在 connection —— 注册表只拥有帧的事实。\n */\nexport interface FrameContext extends BridgeSink {\n readonly signal?: AbortSignal\n waitForWritable?(): Promise<void>\n readonly frame: Envelope\n readonly deviceId: string | undefined\n readonly widgetClient: boolean\n readonly bridge: HostBridge\n readonly devices: DeviceStore\n readonly debug: boolean\n log(message: string): void\n send(type: S2CType, payload: unknown): void\n /** 二进制帧:`payload` 进头部,`body` 是原始字节。缺省表示连接不支持(测试替身)。 */\n sendBinary?(type: S2CType, payload: unknown, body: Uint8Array): void\n fail(code: WireErrorCode, message: string): void\n close(code: number, reason: string): void\n bufferOpenEvents(sessionId: string): OpenEventBuffer\n /** 打开失败:仅当缓冲仍属于本次尝试时才移除。 */\n discardOpenBuffer(sessionId: string, buffer: OpenEventBuffer): void\n /** 打开成功:认领缓冲并按其到达顺序补发;被并发操作取代时返回 false。 */\n flushOpenBuffer(sessionId: string, buffer: OpenEventBuffer): boolean\n closeOpenSession(sessionId: string): void\n /** 抬升 Live Activity 注册代次并返回新值;在途注册用旧值比对即知自己已被取代。 */\n nextLiveActivityGeneration(): number\n /** 当前代次:await 之后重新比对用。 */\n readonly liveActivityGeneration: number\n readonly pendingLiveActivityId: string | undefined\n setPendingLiveActivityId(activityId: string | undefined): void\n markRevoked(): void\n /** c2s.auth.prove 的机制由连接门实现(认证是安全判定,不属于特性模块)。 */\n prove(): Promise<void>\n enrollPushKey(enrollKey: string): Promise<void> | void\n revokeSiblings(deviceId: string): Promise<unknown> | unknown\n}\n\nexport interface WireFrameRow {\n readonly type: C2SType\n readonly stage: FrameStage\n /** 全部满足才放行;空数组表示控制帧,不要求业务 scope。 */\n readonly scopes: readonly DeviceScope[]\n readonly widgetPolicy?: WidgetPolicy\n readonly capability?: CapabilityGate\n /** 只约束某种载荷,门留在 handler(例如同一导出帧的可选 Markdown 格式)。 */\n readonly conditionalCapabilities?: readonly CapabilityGate[]\n /** PROTOCOL.md 小节名;tests/wire-registry.test.ts 断言其存在。 */\n readonly doc: string\n readonly validate?: (payload: unknown) => CheckedPayload\n readonly handle: (ctx: FrameContext, payload: Record<string, unknown>) => Promise<void> | void\n}\n\n// ---------- 检查(connection 在 dispatch 前依次调用) ----------\n\n/** 第一个缺失的 scope;消息与迁移前一致(`scope <name> required`)。 */\nexport function scopeRejection(\n row: WireFrameRow,\n scopes: ReadonlySet<DeviceScope>,\n): WireRejection | undefined {\n for (const scope of row.scopes) {\n if (!scopes.has(scope)) return { code: 'E_FORBIDDEN', message: `scope ${scope} required` }\n }\n return undefined\n}\n\n/** 能力位关闭时拒绝。校验先于本检查,与迁移前的次序一致。 */\nexport function capabilityRejection(\n row: WireFrameRow,\n capabilities: WelcomeCapabilities,\n): WireRejection | undefined {\n if (row.capability === undefined) return undefined\n if (capabilities[row.capability] === true) return undefined\n return { code: 'E_UNSUPPORTED', message: 'capability unavailable on this host version' }\n}\n\nexport function widgetPolicyOf(row: WireFrameRow): WidgetPolicy {\n return row.widgetPolicy ?? DEFAULT_WIDGET_POLICY\n}\n\n/** 校验一帧载荷;未登记 validate 的行原样通过。 */\nexport function validatePayload(row: WireFrameRow, payload: unknown): CheckedPayload {\n if (row.validate === undefined) {\n return { ok: true, value: (payload ?? {}) as Record<string, unknown> }\n }\n return row.validate(payload)\n}\n\n/** 执行一帧。检查(阶段/scope/能力/校验)由 connection 按序先行完成。 */\nexport async function dispatchFrame(\n row: WireFrameRow,\n ctx: FrameContext,\n payload: Record<string, unknown>,\n): Promise<void> {\n await row.handle(ctx, payload)\n}\n\n// ---------- 行内校验共用工具 ----------\n\n/**\n * 载荷必须是普通对象。此前这条判定住在 request-validation 里,对\n * `c2s.sessions.list` 等无参帧豁免、对 `c2s.sessions.archived` 却不通融\n * (同语义两种待遇,见 G2);现在豁免由各行的 validate 自己声明。\n */\nexport function payloadObject(value: unknown): CheckedPayload {\n if (value === undefined || value === null) return { ok: true, value: {} }\n if (typeof value !== 'object' || Array.isArray(value)) {\n return { ok: false, code: 'E_PROTOCOL', message: 'payload must be an object' }\n }\n return { ok: true, value: value as Record<string, unknown> }\n}\n\n/** 字符串字段:类型正确、不超长、非空(可放宽)。 */\nexport function isText(value: unknown, max = 4096, nonempty = true): boolean {\n return typeof value === 'string' && value.length <= max && (!nonempty || value.trim().length > 0)\n}\n\n/** 可选字段:缺省合法,给出时必须满足 check。 */\nexport function isOptionalField(\n payload: Record<string, unknown>,\n key: string,\n check: (value: unknown) => boolean,\n): boolean {\n return payload[key] === undefined || check(payload[key])\n}\n\nexport function isInteger(value: unknown, min: number, max: number): boolean {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= min && value <= max\n}\n\nexport function reject(code: WireErrorCode, message: string): CheckedPayload {\n return { ok: false, code, message }\n}\n\nexport function accept(value: Record<string, unknown>): CheckedPayload {\n return { ok: true, value }\n}\n\n// ---------- 表 ----------\n\nimport { interactionRows } from './wire-interaction.ts'\nimport { deviceRows } from './wire-device.ts'\nimport { pushRows } from './wire-push.ts'\nimport { scheduleRows } from './wire-schedule.ts'\nimport { sessionRows } from './wire-session.ts'\nimport { commandRows } from './wire-command.ts'\nimport { pluginUpdateRows } from './wire-plugin-update.ts'\n\n/** 全部入站行。装配是静态的:新增特性 module 必须在这里显式出现。 */\nexport const WIRE_FRAME_ROWS: readonly WireFrameRow[] = [\n ...sessionRows,\n ...commandRows,\n ...pluginUpdateRows,\n ...scheduleRows,\n ...interactionRows,\n ...pushRows,\n ...deviceRows,\n]\n\nconst ROWS_BY_TYPE = new Map<C2SType, WireFrameRow>(WIRE_FRAME_ROWS.map((row) => [row.type, row]))\n\nexport function registryRowFor(type: string): WireFrameRow | undefined {\n return ROWS_BY_TYPE.get(type as C2SType)\n}\n\n/** 供 build 期不变量检查与测试使用:每行可索引。 */\nexport function wireFrameRows(): readonly WireFrameRow[] {\n return WIRE_FRAME_ROWS\n}\n","/**\n * 连接门(ConnectionGate)——「一个匿名 socket 被允许做什么」的唯一属主。\n *\n * 迁移前,这些规则散在五个地方:connection.ts 的 onMessage(长度上限、信封\n * 卫生、pre-auth 白名单、scope/widget 门)、connection.ts 的 prove()(认证)、\n * index.ts 的 handleUpgrade(连接数、名额、失败记账、6 个手工 release 点)、\n * auth-rate-limit.ts(策略)、token.ts(注册表语义)。理解一次 4401 关闭要跨\n * 400 行来回跳。\n *\n * 现在:门拥有 ①认证状态机的全部迁移 ②认证名额的获取与释放(单一释放点)\n * ③「此刻这一帧是否被允许进入」的判定。连接只剩传输、welcome 发射、sink 台账\n * 与连接自有缓冲;注册表行仍住在 wire-registry 及其特性 module 里,门只负责\n * 在正确的时机调用它们。\n *\n * 名额的生命周期分两段,各有保证:\n * - 接入段:门在 handleUpgrade 里构造(admit 发生于此)。若 upgrade 回调根本\n * 不执行(upgrade 期间 socket 被销毁),5 秒兜底会释放名额并判门死刑;\n * - 认证段:attach 之后由 35 秒 hello 计时器兜底,任何一条落定路径\n * (成功/证明无效/超时/关闭)都经 settleAuthentication 的 exactly-once\n * 守卫释放一次。\n */\n\nimport { PROTOCOL_VERSION } from './protocol.ts'\nimport type { AuthProofPayload, Envelope } from './protocol.ts'\nimport type { HostBridge } from './host-bridge.ts'\nimport type { DeviceStore } from './token.ts'\nimport type { DiagnosticsRecorder } from './diagnostics.ts'\nimport {\n createAuthChallenge,\n verifyAuthProof,\n type AuthChallenge,\n type DeviceScope,\n} from './device-auth.ts'\nimport {\n AUTH_TIMEOUT_MS,\n MAX_APP_VERSION_CHARS,\n MAX_DEVICE_ID_CHARS,\n MAX_DEVICE_NAME_CHARS,\n PRE_AUTH_FRAME_BYTES,\n sanitizeDeviceField,\n isEnvelope,\n} from './connection-policy.ts'\nimport { AuthRateLimiter } from './auth-rate-limit.ts'\nimport {\n capabilityRejection,\n dispatchFrame,\n registryRowFor,\n scopeRejection,\n validatePayload,\n widgetPolicyOf,\n type FrameContext,\n type OpenEventBuffer,\n type S2CType,\n} from './wire-registry.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\n\n/** 认证落定的四种结局;与迁移前 index.ts 收到的 reason 一致。 */\nexport type SettleReason = 'success' | 'invalid-proof' | 'timeout' | 'closed'\n\n/**\n * 4401 的七种分因。**只进日志与诊断事件**:wire 响应仍然只有一个 4401\n * 关闭码,绝不透露设备是否注册过、签名哪一步失败。\n */\nexport type AuthFailureReason =\n | 'unknown-device'\n | 'revoked'\n | 'bad-signature'\n | 'nonce-mismatch'\n | 'audience-mismatch'\n | 'challenge-expired'\n | 'clock-skew'\n\n/**\n * 把一次认证失败归一到一个原因。判定顺序就是语义优先级:\n *\n * 1. 注册表状态先行——设备不存在 / 已撤销时,后面任何字段都不可信;\n * 2. nonce / audience 不匹配:挑战对象不是这一次(重放或串线);\n * 3. 服务器时钟已过挑战有效期:挑战过期;\n * 4. 客户端回显的 issuedAt / expiresAt 与挑战不同:客户端时钟或状态偏差;\n * 5. 其余(签名缺失 / 验签失败):bad-signature。\n *\n * 纯函数,连接门与单测共用同一份判定。\n */\nexport function classifyAuthFailure(input: {\n device: 'active' | 'revoked' | 'unknown'\n proof: Partial<AuthProofPayload>\n challenge: AuthChallenge\n now: number\n}): AuthFailureReason {\n if (input.device === 'revoked') return 'revoked'\n if (input.device === 'unknown') return 'unknown-device'\n const { proof, challenge, now } = input\n if (proof.nonce !== challenge.nonce) return 'nonce-mismatch'\n if (proof.audience !== challenge.audience) return 'audience-mismatch'\n if (now > challenge.expiresAt) return 'challenge-expired'\n if (proof.issuedAt !== challenge.issuedAt || proof.expiresAt !== challenge.expiresAt) return 'clock-skew'\n return 'bad-signature'\n}\n\n/** 认证成功的产物:连接据它发 welcome、注册 sink、决定重放。 */\nexport interface AuthenticatedIdentity {\n deviceId: string\n scopes: ReadonlySet<DeviceScope>\n widgetClient: boolean\n resumeCursor: number | undefined\n deviceName: string\n appVersion: string\n}\n\n/**\n * 门看到的宿主,由连接实现:传输、sink 转发、连接自有缓冲,以及认证成功与\n * 审计两个通知。行(wire-registry)看到的 FrameContext 是它的一个按帧投影。\n */\nexport interface GateHost {\n readonly signal?: AbortSignal\n waitForWritable?(): Promise<void>\n readonly bridge: HostBridge\n readonly devices: DeviceStore\n readonly debug: boolean\n log(message: string): void\n send(type: S2CType, payload: unknown, id?: string): void\n /** 二进制帧发送;仅在客户端请求里带 `binary: true` 时由行调用。 */\n sendBinary?(type: S2CType, payload: unknown, body: Uint8Array, id?: string): void\n fail(id: string | undefined, code: WireErrorCode, message: string): void\n close(code: number, reason: string): void\n // ---------- BridgeSink 转发(openSession 等行需要把上下文当作 sink) ----------\n canReceive(scope: DeviceScope): boolean\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n // ---------- 连接自有状态 ----------\n bufferOpenEvents(sessionId: string): OpenEventBuffer\n discardOpenBuffer(sessionId: string, buffer: OpenEventBuffer): void\n flushOpenBuffer(sessionId: string, buffer: OpenEventBuffer): boolean\n closeOpenSession(sessionId: string): void\n nextLiveActivityGeneration(): number\n readonly liveActivityGeneration: number\n readonly pendingLiveActivityId: string | undefined\n setPendingLiveActivityId(activityId: string | undefined): void\n markRevoked(): void\n enrollPushKey(enrollKey: string): Promise<void> | void\n revokeSiblings(deviceId: string): Promise<unknown> | unknown\n // ---------- 通知 ----------\n /** 认证成功:由连接发 welcome、注册 sink、决定重放/重同步。 */\n onAuthenticated(identity: AuthenticatedIdentity): void\n /** 隐私 preserving 审计事件(有效 hello 之后)。 */\n deviceAuthenticated(deviceId: string): void\n /** 认证落定:纯观测通知(名额释放与限流记账已在门内完成)。 */\n settled(ok: boolean, reason: SettleReason): void\n}\n\nexport interface ConnectionGateOptions {\n /** 客户端身份,用于限流(IP 或回环标识)。 */\n source: string\n devices: DeviceStore\n /** 稳定 Host 身份,写入每次挑战。 */\n audience: string\n limiter: AuthRateLimiter\n log: (message: string) => void\n /** 审计标签函数:把敏感标识变成短哈希,供日志使用。 */\n auditLabel?: (value: string) => string\n /** 客户端带来的 attemptId;只用于串联日志,不参与任何判定。 */\n attemptId?: string\n /** 诊断事件出口;缺省时失败路径不额外记录(行为不变)。 */\n diagnostics?: DiagnosticsRecorder\n /** 未 attach 的兜底时限:upgrade 回调根本不执行时释放名额。 */\n attachTimeoutMs?: number\n}\n\n/** upgrade 正常是毫秒级;5 秒已极宽,只为「回调根本不执行」兜底。 */\nconst DEFAULT_ATTACH_TIMEOUT_MS = 5_000\n\nexport class ConnectionGate {\n private authenticated = false\n private settled = false\n private revoked = false\n private deviceId: string | undefined\n private scopes = new Set<DeviceScope>()\n private widgetClient = false\n private readonly challenge: AuthChallenge\n private helloTimer: NodeJS.Timeout | undefined\n private attachTimer: NodeJS.Timeout | undefined\n private attached = false\n private host: GateHost | undefined\n private admission: { release: () => void } | undefined\n private dead = false\n\n constructor(private readonly options: ConnectionGateOptions) {\n this.challenge = createAuthChallenge(options.audience)\n // 名额在此获取:早于 upgrade 回调,因此「回调不执行」也有释放保证。\n const admission = options.limiter.admit(options.source)\n this.admission = admission.ok ? admission : undefined\n if (!admission.ok) {\n this.dead = true\n return\n }\n this.attachTimer = setTimeout(() => {\n this.attachTimer = undefined\n // 从未 attach:回调没跑,socket 不会再来。释放名额,门作废。\n if (!this.attached && !this.settled) {\n this.dead = true\n this.releaseAdmission()\n }\n }, options.attachTimeoutMs ?? DEFAULT_ATTACH_TIMEOUT_MS)\n this.attachTimer.unref()\n }\n\n /** 名额是否拿到;false 时 handleUpgrade 应直接拒掉这个源。 */\n get admitted(): boolean {\n return !this.dead\n }\n\n /** 客户端身份:限流与审计日志共用同一个值。 */\n get source(): string {\n return this.options.source\n }\n\n /**\n * ws 就绪:接线宿主、下发挑战并启动 hello 计时器。只应被调用一次——\n * 由连接在构造时调用(那一刻 ws 才真正存在)。\n */\n attach(host: GateHost): void {\n if (this.dead || this.attached) return\n this.attached = true\n this.host = host\n if (this.attachTimer !== undefined) {\n clearTimeout(this.attachTimer)\n this.attachTimer = undefined\n }\n host.send('s2c.auth.challenge', this.challenge)\n this.helloTimer = setTimeout(() => {\n this.helloTimer = undefined\n if (this.authenticated) return\n this.settle(false, 'timeout')\n // 4402 与 4401 对称:超时同样是「手机握手失败」,没有理由只让认证\n // 失败有记录、超时无声无息。\n this.reportAuthFailure('timeout', 4402)\n this.host?.close(4402, 'auth timeout')\n }, AUTH_TIMEOUT_MS)\n this.helloTimer.unref()\n }\n\n /**\n * 接入窗口结束:若始终没有 socket 接上(upgrade 回调没执行),立刻归还名额,\n * 不必等 5 秒兜底。已 attach 的门不在此释放——它的名额由落定路径或 hello\n * 计时器负责。\n */\n releaseIfNeverAttached(): void {\n if (!this.attached && !this.settled) {\n this.dead = true\n this.releaseAdmission()\n }\n }\n\n /** 拒绝一个已创建但最终不会接线的门(bridge 变更、构造失败)。 */\n markDead(): void {\n this.dead = true\n this.releaseAdmission()\n }\n\n /** socket 关闭:未落定则按 closed 收尾(并释放名额)。 */\n onClose(): void {\n if (!this.settled) this.settle(false, 'closed')\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n if (this.attachTimer !== undefined) clearTimeout(this.attachTimer)\n }\n\n /**\n * 处理一条原始帧。内部完成全部 pre-auth 规则(长度上限、信封卫生、版本、\n * pre-auth 白名单、撤销吞帧),再进入认证、授权与分发。\n */\n async handleFrame(raw: string): Promise<void> {\n const host = this.host\n if (this.dead || host === undefined) return\n // 撤销后的设备正在关闭:吞掉一切,不再回应一个已不存在的设备。\n if (this.revoked) return\n // 长度上限先行:预认证帧很小,超限即拒且不解析——不让匿名端用 64MiB\n // 载荷在认证窗口内消耗 CPU。\n if (!this.authenticated && raw.length > PRE_AUTH_FRAME_BYTES) {\n host.close(1009, 'pre-auth frame too large')\n return\n }\n let env: Envelope\n try {\n const parsed: unknown = JSON.parse(raw)\n if (!isEnvelope(parsed)) {\n host.fail(undefined, 'E_PROTOCOL', 'malformed frame')\n return\n }\n env = parsed\n } catch {\n host.fail(undefined, 'E_PROTOCOL', 'frame is not valid JSON')\n return\n }\n if (env.v !== PROTOCOL_VERSION) {\n host.fail(env.id, 'E_UNSUPPORTED', 'unsupported protocol version')\n host.close(4500, 'protocol version mismatch')\n return\n }\n\n if (!this.authenticated) {\n // 匿名端对每个非控制帧只得到一个答复:透露哪些名字已注册等于泄露帧清单。\n const control = registryRowFor(env.type)\n if (control === undefined || control.stage !== 'pre-auth') {\n host.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n return\n }\n await dispatchFrame(control, this.context(env), {})\n return\n }\n\n const row = registryRowFor(env.type)\n // widget 只读门在前,且覆盖未知类型:短连接只准发白名册行,其余一律\n // 「只读」,不泄露帧清单。\n if (this.widgetClient && (row === undefined || widgetPolicyOf(row) === 'deny')) {\n return host.fail(env.id, 'E_FORBIDDEN', 'widget connection is read-only')\n }\n // 未知类型直接 E_PROTOCOL(G6)。先查 scope 会让没有 sessions.read 的\n // 设备听到关于一个不存在帧的「权限不足」。\n if (row === undefined) {\n host.fail(env.id, 'E_PROTOCOL', 'unknown type: ' + env.type)\n return\n }\n const scope = scopeRejection(row, this.scopes)\n if (scope !== undefined) return host.fail(env.id, scope.code, scope.message)\n // 校验先于能力门,与迁移前「载荷形状先于 handler 看能力」的次序一致。\n const validated = validatePayload(row, env.payload)\n if (!validated.ok) return host.fail(env.id, validated.code, validated.message)\n const capability = capabilityRejection(row, host.bridge.capabilities)\n if (capability !== undefined) return host.fail(env.id, capability.code, capability.message)\n\n await dispatchFrame(row, this.context(env), validated.value)\n }\n\n /** 门持有的撤销标记由 device.revoke 行置位。 */\n markRevoked(): void {\n this.revoked = true\n }\n\n // ---------- 认证 ----------\n\n /** c2s.auth.prove 的行 handler 调用这里:验签、载入 scope、记账、落定。 */\n async authenticate(env: Envelope): Promise<void> {\n const host = this.host\n // 刚自撤销的 socket 正在关闭,不能再认证(注册表墓碑本来也会拒)。\n if (this.revoked || host === undefined) return\n const p = (env.payload ?? {}) as Partial<AuthProofPayload>\n if (!p.deviceId) {\n host.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.reportAuthFailure('device-id-missing', 4403)\n host.close(4403, 'deviceId required')\n return\n }\n const deviceId = sanitizeDeviceField(p.deviceId, MAX_DEVICE_ID_CHARS)\n if (!deviceId) {\n host.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.reportAuthFailure('device-id-missing', 4403)\n host.close(4403, 'deviceId required')\n return\n }\n const deviceName = sanitizeDeviceField(p.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(p.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n // 注册表状态先行:authorized() 仍是唯一准入判定,statusOf() 只把\n // 「未知 / 已撤销 / 可认证」拆开,让 4401 在日志里能分因。\n const deviceState = this.options.devices.statusOf(deviceId)\n const record = deviceState === 'active' ? this.options.devices.authorized(deviceId) : undefined\n const resumeCursor = typeof p.resumeCursor === 'number' && Number.isInteger(p.resumeCursor) && p.resumeCursor >= 0\n ? p.resumeCursor\n : undefined\n const challengeMatches = p.nonce === this.challenge.nonce &&\n p.audience === this.challenge.audience &&\n p.issuedAt === this.challenge.issuedAt &&\n p.expiresAt === this.challenge.expiresAt &&\n Date.now() <= this.challenge.expiresAt\n const proofValid = record?.publicKey !== undefined && typeof p.signature === 'string' && challengeMatches &&\n verifyAuthProof(record.publicKey, {\n deviceId,\n deviceName,\n appVersion,\n resumeCursor,\n ...this.challenge,\n }, p.signature)\n if (!proofValid || record === undefined) {\n const reason = classifyAuthFailure({\n device: deviceState,\n proof: p,\n challenge: this.challenge,\n now: Date.now(),\n })\n host.fail(env.id, 'E_AUTH', 'device proof missing or invalid')\n this.reportAuthFailure(reason, 4401)\n this.settle(false, 'invalid-proof')\n host.close(4401, 'invalid device proof')\n return\n }\n this.settle(true, 'success')\n this.authenticated = true\n this.deviceId = deviceId\n this.widgetClient = p.clientRole === 'widget'\n this.scopes = new Set(record.scopes ?? [])\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n if (!this.widgetClient) {\n this.options.devices.markAuthenticated(deviceId, deviceName, appVersion, Date.now())\n }\n host.deviceAuthenticated(deviceId)\n // 欢迎帧与重放由连接负责:认证是安全判定,欢迎是数据面展示。\n host.onAuthenticated({\n deviceId,\n scopes: this.scopes,\n widgetClient: this.widgetClient,\n resumeCursor: this.widgetClient ? undefined : resumeCursor,\n deviceName,\n appVersion,\n })\n }\n\n /**\n * 认证失败事件。来源只写 auditLabel 哈希(没有哈希函数时宁可不写,\n * 也不把原始 IP 落盘);attemptId 原样带出,便于把同一次握手的\n * upgrade / auth 两条日志串起来。\n */\n private reportAuthFailure(reason: string, closeCode: number): void {\n const source = this.options.auditLabel?.(this.options.source)\n this.options.diagnostics?.record('auth-failed', reason, {\n ...(source !== undefined ? { source } : {}),\n ...(this.options.attemptId !== undefined ? { attemptId: this.options.attemptId } : {}),\n closeCode,\n })\n }\n\n /** 限流记账与名额释放只此一处;exactly-once 由 settled 守卫保证。 */\n private settle(ok: boolean, reason: SettleReason): void {\n if (this.settled) return\n this.settled = true\n this.releaseAdmission()\n this.host?.settled(ok, reason)\n if (ok) {\n this.options.limiter.recordSuccess(this.options.source)\n return\n }\n const failure = this.options.limiter.recordFailure(this.options.source)\n if (failure.newlyBlocked) {\n const label = this.options.auditLabel?.(this.options.source) ?? this.options.source\n this.options.log(`authentication source blocked source=${label}`)\n }\n }\n\n private releaseAdmission(): void {\n this.admission?.release()\n this.admission = undefined\n }\n\n /** 把宿主投影成行看到的每帧上下文:id 绑进 send/fail,身份来自门。 */\n private context(env: Envelope): FrameContext {\n const host = this.host!\n return {\n frame: env,\n signal: host.signal,\n waitForWritable: host.waitForWritable?.bind(host),\n deviceId: this.deviceId,\n widgetClient: this.widgetClient,\n bridge: host.bridge,\n devices: host.devices,\n debug: host.debug,\n log: (message) => host.log(message),\n send: (type, payload) => host.send(type, payload, env.id),\n ...(host.sendBinary ? { sendBinary: (type: S2CType, payload: unknown, body: Uint8Array) => host.sendBinary!(type, payload, body, env.id) } : {}),\n fail: (code, message) => host.fail(env.id, code, message),\n close: (code, reason) => host.close(code, reason),\n canReceive: (scope) => host.canReceive(scope),\n push: (type, payload, seq) => host.push(type, payload, seq),\n lastCursor: () => host.lastCursor(),\n replay: (entries) => host.replay(entries),\n replayDone: () => host.replayDone(),\n resync: () => host.resync(),\n bufferOpenEvents: (sessionId) => host.bufferOpenEvents(sessionId),\n discardOpenBuffer: (sessionId, buffer) => host.discardOpenBuffer(sessionId, buffer),\n flushOpenBuffer: (sessionId, buffer) => host.flushOpenBuffer(sessionId, buffer),\n closeOpenSession: (sessionId) => host.closeOpenSession(sessionId),\n nextLiveActivityGeneration: () => host.nextLiveActivityGeneration(),\n get liveActivityGeneration() {\n return host.liveActivityGeneration\n },\n get pendingLiveActivityId() {\n return host.pendingLiveActivityId\n },\n setPendingLiveActivityId: (activityId) => host.setPendingLiveActivityId(activityId),\n markRevoked: () => this.markRevoked(),\n prove: () => this.authenticate(env),\n enrollPushKey: (enrollKey) => host.enrollPushKey(enrollKey),\n revokeSiblings: (deviceId) => host.revokeSiblings(deviceId),\n }\n }\n}\n","import type { WebSocket } from 'ws'\nimport { PROTOCOL_VERSION } from './protocol.ts'\nimport type { Envelope } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { DeviceStore } from './token.ts'\nimport type { DeviceScope } from './device-auth.ts'\nimport { MAX_OUTBOUND_BUFFER_BYTES } from './connection-policy.ts'\nimport { encodeBinaryFrame } from './binary-frame.ts'\nimport { AuthRateLimiter } from './auth-rate-limit.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\nimport type { DiagnosticsRecorder } from './diagnostics.ts'\nimport { ConnectionGate, type AuthenticatedIdentity, type GateHost } from './connection-gate.ts'\nimport type { OpenEventBuffer, S2CType } from './wire-registry.ts'\n\nexport {\n AUTH_TIMEOUT_MS,\n MAX_OUTBOUND_BUFFER_BYTES,\n PRE_AUTH_FRAME_BYTES,\n} from './connection-policy.ts'\n\nexport interface ConnectionStateDeps {\n bridge: HostBridge\n devices: DeviceStore\n serverVersion: string\n /** Stable Host identity included in every signed challenge. */\n audience: string\n debug?: boolean\n log: (message: string) => void\n /** 客户端身份,供连接门做限流;缺省时按本地回环源处理。 */\n source?: string\n /** 认证名额与失败记账由连接门持有(见 connection-gate.ts)。 */\n rateLimiter?: AuthRateLimiter\n /** 审计标签函数:把客户端身份变成短哈希供日志使用;缺省时直接用原值。 */\n auditLabel?: (value: string) => string\n /** 诊断事件出口;连接建立 / 非正常关闭 / socket error 各记一条。 */\n diagnostics?: DiagnosticsRecorder\n /** 客户端带来的 attemptId(WS 升级 query `at`),只记录不校验。 */\n attemptId?: string\n /** Called once when the socket closes (cleanly or not). */\n onClosed?: (connection: BridgeConnection) => void\n /**\n * 认证落定通知:纯观测用。名额释放与限流记账已由连接门内部完成,\n * 接入方不再需要据此做任何事;测试用它断言「只落定一次」。\n */\n onAuthenticationSettled?: (ok: boolean, reason: 'success' | 'invalid-proof' | 'timeout' | 'closed') => void\n /** Emits a privacy-preserving audit event after a valid hello. */\n onDeviceAuthenticated?: (deviceId: string) => void\n /**\n * Zero-touch push enrollment: fired when a distributed app presents the\n * distributor's shared enrollKey during c2s.push.register. May perform the\n * relay round-trip; the register handler awaits it before evaluating push\n * readiness, so the very first registration can switch the feature on.\n */\n onPushEnrollKey?: (enrollKey: string) => Promise<void> | void\n /**\n * c2s.device.revoke (device unbind): the registered device revoked itself\n * and every OTHER active connection of that device must be dropped.\n * `except` is the connection that sent the frame — its close is already\n * owned by the handler (ack first, then 4401). The hook must not flip the\n * registry itself; the handler calls DeviceStore.revoke before this.\n */\n onDeviceRevoke?: (deviceId: string, except: BridgeConnection) => Promise<unknown> | unknown\n}\n\n/**\n * One connected phone. Implements BridgeSink so the HostBridge can push\n * projected frames and replays.\n *\n * 本模块现在只剩传输与连接自有状态:socket、send/fail/close、背压、idle 计时、\n * welcome 发射、sink 台账、打开中的会话缓冲、Live Activity 代次。帧的规则\n * (pre-auth、认证、授权、分发)全部属于 ConnectionGate;帧的事实属于\n * wire-registry。连接通过实现 GateHost 把这两者接起来。\n */\nexport class BridgeConnection implements BridgeSink {\n private closed = false\n /**\n * Realtime events that arrive while a session's history snapshot is in\n * flight. The wire contract requires tail first; sending these immediately\n * lets the later tail roll the client back over messages it just rendered.\n */\n private readonly openingSessionEvents = new Map<string, Array<{\n type: string\n payload: unknown\n seq?: number\n }>>()\n private readonly openSessions = new Set<string>()\n private liveActivityRegistrationGeneration = 0\n private pendingLiveActivityId?: string\n private lastActivity = Date.now()\n private readonly streamAbort = new AbortController()\n /** 建连时刻:关闭事件用它算存活时长。 */\n private readonly openedAt = Date.now()\n /** 来源的审计哈希;没有 auditLabel 时留空,绝不记录原始 IP。 */\n private readonly sourceLabel: string | undefined\n\n /** True when no inbound frame arrived within maxIdleMs. */\n isStale(now: number, maxIdleMs: number): boolean {\n return now - this.lastActivity > maxIdleMs\n }\n\n /** 认证成功后由门回填,供 welcome 与 sink 注册使用。 */\n private identity: AuthenticatedIdentity | undefined\n\n /** 帧规则(pre-auth、认证、授权、分发)的属主;由接入方创建并交进来。 */\n readonly gate: ConnectionGate\n\n constructor(\n private readonly ws: WebSocket,\n private readonly deps: ConnectionStateDeps,\n /** 由接入方预先创建的门(提前占名额以在握手前限流);缺省时连接自建。 */\n gate: ConnectionGate = new ConnectionGate({\n source: deps.source ?? 'local',\n devices: deps.devices,\n audience: deps.audience,\n limiter: deps.rateLimiter ?? new AuthRateLimiter(),\n log: (message) => deps.log(message),\n ...(deps.auditLabel ? { auditLabel: deps.auditLabel } : {}),\n ...(deps.diagnostics ? { diagnostics: deps.diagnostics } : {}),\n ...(deps.attemptId !== undefined ? { attemptId: deps.attemptId } : {}),\n }),\n ) {\n const connection = this\n const sourceLabel = deps.auditLabel?.(deps.source ?? 'local')\n this.sourceLabel = sourceLabel\n const host: GateHost = {\n signal: this.streamAbort.signal,\n waitForWritable: async () => {\n while (this.ws.bufferedAmount > 256 * 1024) {\n this.streamAbort.signal.throwIfAborted()\n await new Promise(resolve => setTimeout(resolve, 10))\n }\n this.streamAbort.signal.throwIfAborted()\n },\n get bridge() { return connection.deps.bridge },\n get devices() { return connection.deps.devices },\n get debug() { return deps.debug === true },\n log: (message) => deps.log(message),\n send: (type: S2CType, payload, id) => connection.send(type, payload, id),\n sendBinary: (type: S2CType, payload, body, id) => connection.send(type, payload, id, undefined, body),\n fail: (id, code, message) => connection.fail(id, code, message),\n close: (code, reason) => connection.close(code, reason),\n canReceive: (scope) => connection.canReceive(scope),\n push: (type, payload, seq) => connection.push(type, payload, seq),\n lastCursor: () => connection.lastCursor(),\n replay: (entries) => connection.replay(entries),\n replayDone: () => connection.replayDone(),\n resync: () => connection.resync(),\n bufferOpenEvents: (sessionId) => connection.bufferOpenEvents(sessionId),\n discardOpenBuffer: (sessionId, buffer) => connection.discardOpenBuffer(sessionId, buffer),\n flushOpenBuffer: (sessionId, buffer) => connection.flushOpenBuffer(sessionId, buffer),\n closeOpenSession: (sessionId) => connection.closeOpenSession(sessionId),\n nextLiveActivityGeneration: () => {\n connection.liveActivityRegistrationGeneration += 1\n return connection.liveActivityRegistrationGeneration\n },\n get liveActivityGeneration() { return connection.liveActivityRegistrationGeneration },\n get pendingLiveActivityId() { return connection.pendingLiveActivityId },\n setPendingLiveActivityId: (activityId) => { connection.pendingLiveActivityId = activityId },\n markRevoked: () => connection.gate.markRevoked(),\n enrollPushKey: (enrollKey) => deps.onPushEnrollKey?.(enrollKey),\n revokeSiblings: (deviceId) => deps.onDeviceRevoke?.(deviceId, connection),\n onAuthenticated: (identity) => connection.onAuthenticated(identity),\n deviceAuthenticated: (deviceId) => deps.onDeviceAuthenticated?.(deviceId),\n settled: (ok, reason) => deps.onAuthenticationSettled?.(ok, reason),\n }\n this.gate = gate\n\n ws.on('message', (data) => {\n this.lastActivity = Date.now()\n void gate.handleFrame(String(data)).catch((error) => {\n // A frame must never take down the host process: fail only this\n // socket. Protocol-shape mistakes are handled inside the gate; this\n // is the last line of defense for unexpected handler errors.\n if (this.deps.debug === true) this.deps.log('frame handler failed: ' + String(error))\n if (this.closed) return\n this.terminate()\n })\n })\n ws.on('close', (code?: number) => {\n this.onClose(code)\n deps.onClosed?.(this)\n })\n // socket 层错误此前是空处理:手机只看到「连接断开」,电脑端毫无线索。\n // 这里只记录事实(错误消息、是否已认证);close 事件仍会跟上,原有\n // 「error 之后由 close 收尾」的语义不变。\n ws.on('error', (error?: unknown) => {\n this.deps.diagnostics?.record('connection-error', 'socket-error', {\n ...(this.sourceLabel !== undefined ? { source: this.sourceLabel } : {}),\n ...(this.deps.attemptId !== undefined ? { attemptId: this.deps.attemptId } : {}),\n authenticated: this.identity !== undefined,\n detail: error instanceof Error ? error.message : String(error ?? 'unknown'),\n })\n })\n // The challenge goes out the moment the socket is live; the hello deadline\n // is the gate's, so an upgrade that never produced a socket still has the\n // attach timeout to release its admission slot.\n gate.attach(host)\n // 建连成功一行:来源哈希 + attemptId。放在 attach 之后,保证日志顺序\n // 与线上事件顺序一致(先说挑战,再说连接已建立)。\n this.deps.diagnostics?.record('connection-opened', 'socket-open', {\n ...(sourceLabel !== undefined ? { source: sourceLabel } : {}),\n ...(deps.attemptId !== undefined ? { attemptId: deps.attemptId } : {}),\n })\n }\n\n /** Hard-drop the socket (server-side stale sweep). */\n terminate(): void {\n this.ws.terminate()\n }\n\n /** Protocol-compliant idle timeout: let the peer observe a normal 1001 close. */\n closeIdle(): void {\n this.close(1001, 'idle timeout')\n }\n\n /** Announce an orderly plugin/data-plane shutdown before closing the socket. */\n closeForServerStop(): void {\n this.fail(undefined, 'E_INTERNAL', 'server stopping')\n this.close(1001, 'server stopping')\n }\n\n /** Used by dependency-lifecycle cleanup to avoid closing a replacement bridge. */\n isAttachedTo(bridge: HostBridge): boolean {\n return this.deps.bridge === bridge\n }\n\n /** Device identity once hello succeeded; undefined before that. */\n get connectedDeviceId(): string | undefined {\n return this.identity?.deviceId\n }\n\n get suppressesAlertPush(): boolean {\n return this.identity?.widgetClient !== true\n }\n\n // ---------- BridgeSink ----------\n\n /** S→C permission gate consulted by the bridge for every broadcast/replay\n * frame: a device only receives what its scopes grant (R1/P2). */\n canReceive(scope: DeviceScope): boolean {\n return this.identity?.scopes.has(scope) ?? false\n }\n\n push(type: string, payload: unknown, seq?: number): void {\n if (type === 's2c.notify') payload = { ...(payload as object), hostAudience: this.deps.audience }\n // 队列快照与会话事件一样要排在 tail 之后:打开期间到达的先进缓冲。\n if (type === 's2c.session.event' || type === 's2c.session.queue') {\n const sessionId = (payload as { sessionId?: unknown } | undefined)?.sessionId\n if (typeof sessionId === 'string') {\n const buffered = this.openingSessionEvents.get(sessionId)\n if (buffered) {\n buffered.push({ type, payload, ...(seq !== undefined ? { seq } : {}) })\n return\n }\n }\n }\n if (this.deps.debug === true) this.deps.log('push ' + type + ' seq=' + String(seq))\n this.send(type, payload, undefined, seq)\n }\n\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void {\n for (const entry of entries) this.push(entry.type, entry.payload, entry.seq)\n }\n\n replayDone(): void {\n this.push('s2c.resume.done', {})\n }\n\n resync(): void {\n this.push('s2c.resync', { reason: 'gap' })\n }\n\n lastCursor(): number {\n return this.deps.bridge.currentCursor()\n }\n\n // ---------- lifecycle ----------\n\n /** 认证成功:门只做安全判定,欢迎与重放归连接(数据面)。 */\n private onAuthenticated(identity: AuthenticatedIdentity): void {\n this.identity = identity\n const cursor = identity.resumeCursor\n // Each replayed business frame is authorized separately by HostBridge.\n const canResume = cursor !== undefined && this.deps.bridge.canResumeFrom(cursor)\n // Welcome strictly precedes any replayed pushes.\n this.send('s2c.welcome', {\n protocolVersion: PROTOCOL_VERSION,\n serverVersion: this.deps.serverVersion,\n deviceId: identity.deviceId,\n scopes: [...identity.scopes],\n capabilities: this.deps.bridge.capabilities,\n cursor: this.deps.bridge.currentCursor(),\n resumed: canResume,\n })\n if (!identity.widgetClient) this.deps.bridge.addSink(this)\n if (cursor !== undefined) {\n if (canResume) {\n this.deps.bridge.resumeFrom(cursor, this)\n } else {\n this.resync()\n }\n }\n }\n\n private onClose(code?: number): void {\n if (this.closed) return\n this.closed = true\n // 非正常关闭(不是 1000 normal / 1001 going-away)各一行:关闭码、\n // 是否已认证、存活时长与来源哈希。正常关闭不记,避免每次退出都刷屏。\n // 缺省(没有 close 帧,如底层 socket 直接消失)按 1005 处理。\n const closeCode = code ?? 1005\n if (closeCode !== 1000 && closeCode !== 1001) {\n this.deps.diagnostics?.record('connection-closed', 'abnormal-close', {\n ...(this.sourceLabel !== undefined ? { source: this.sourceLabel } : {}),\n ...(this.deps.attemptId !== undefined ? { attemptId: this.deps.attemptId } : {}),\n closeCode,\n durationMs: Date.now() - this.openedAt,\n authenticated: this.identity !== undefined,\n })\n }\n this.streamAbort.abort()\n // Unsettled sockets (never proven) settle as 'closed' and give back their\n // admission slot; already-settled ones are a no-op inside the gate.\n this.gate.onClose()\n for (const id of this.openSessions) {\n this.deps.bridge.markSinkClosed(this, id)\n }\n this.openSessions.clear()\n this.openingSessionEvents.clear()\n this.deps.bridge.dropSinkSessions(this)\n if (this.identity !== undefined) this.deps.bridge.removeSink(this)\n }\n\n private close(code: number, reason: string): void {\n if (this.closed) return\n try {\n this.ws.close(code, reason)\n } catch {\n this.ws.terminate()\n }\n }\n\n /** `body` 存在时按二进制帧发送(见 binary-frame.ts),头部仍是同一个 envelope。 */\n private send(type: string, payload: unknown, id?: string, seq?: number, body?: Uint8Array): void {\n const envelope: Envelope = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n ...(id !== undefined ? { id } : {}),\n ...(seq !== undefined ? { seq } : {}),\n payload,\n }\n if (this.ws.readyState !== this.ws.OPEN) return\n if (this.ws.bufferedAmount > MAX_OUTBOUND_BUFFER_BYTES) {\n this.close(1013, 'client too slow')\n return\n }\n this.ws.send(body === undefined ? JSON.stringify(envelope) : encodeBinaryFrame(envelope, body))\n }\n\n private fail(id: string | undefined, code: WireErrorCode, message: string): void {\n this.send('s2c.error', { code, message }, id)\n }\n\n // ---------- connection-owned session bookkeeping ----------\n\n private bufferOpenEvents(sessionId: string): OpenEventBuffer {\n const frames: OpenEventBuffer['frames'] = []\n const buffer: OpenEventBuffer = { frames }\n this.openingSessionEvents.set(sessionId, frames)\n return buffer\n }\n\n private discardOpenBuffer(sessionId: string, buffer: OpenEventBuffer): void {\n if (this.openingSessionEvents.get(sessionId) === buffer.frames) {\n this.openingSessionEvents.delete(sessionId)\n }\n }\n\n private flushOpenBuffer(sessionId: string, buffer: OpenEventBuffer): boolean {\n if (this.openingSessionEvents.get(sessionId) !== buffer.frames) return false\n this.openSessions.add(sessionId)\n this.deps.bridge.markSinkOpen(this, sessionId)\n this.openingSessionEvents.delete(sessionId)\n for (const frame of buffer.frames) this.push(frame.type, frame.payload, frame.seq)\n return true\n }\n\n private closeOpenSession(sessionId: string): void {\n this.openingSessionEvents.delete(sessionId)\n this.openSessions.delete(sessionId)\n this.deps.bridge.markSinkClosed(this, sessionId)\n }\n}\n\n/** 供接入方引用:帧上下文的类型仍由 wire-registry 属主。 */\nexport type { S2CType }\n","export const DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE = 8\nexport const MAX_FUNNEL_CONNECTIONS_PER_SOURCE = 16\n\nexport function normalizeFunnelConnectionLimit(value: unknown): number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= MAX_FUNNEL_CONNECTIONS_PER_SOURCE\n ? value\n : DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE\n}\n","/**\n * 内嵌 helper 上报的 tailscale.com 模块版本。\n *\n * 版本的唯一来源是 helper 启动事件(`helper/main.go` 读 Go 构建信息里的\n * `tailscale.com` 依赖版本),不是 go.mod、也不是插件常量:用户可能通过\n * `remote.helperPath` 换成自己编译的 helper,界面必须显示实际在跑的那一个。\n *\n * 两条边界都要守卫:\n * - 宿主侧 `parseHelperEvent` 把 helper 的 stdout 当成不可信输入,格式不对\n * 就丢弃,避免把任意文本渲染进设置页;\n * - 设置页侧 `reportSchema` 同样校验 Host RPC 返回值,旧 Host 不带该字段\n * 时保持 undefined(页面显示「未获取」),不当作错误。\n *\n * 接受 Go 模块版本形态:`v1.104.2`、`v1.104.2-pre.1`、\n * `v0.0.0-20260101000000-abcdef123456`。长度上限防止异常输入撑爆界面。\n */\nexport const TAILSCALE_VERSION_PATTERN = /^v?\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.-]{1,64})?$/\n\nconst MAX_TAILSCALE_VERSION_LENGTH = 80\n\n/** 判断外部输入是否是合法的 Tailscale 模块版本字符串。 */\nexport function isTailscaleVersion(value: unknown): value is string {\n return typeof value === 'string' &&\n value.length <= MAX_TAILSCALE_VERSION_LENGTH &&\n TAILSCALE_VERSION_PATTERN.test(value)\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { access, mkdir, readdir, rm, writeFile } from 'node:fs/promises'\nimport { constants as fsConstants } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { expandHome } from './token.ts'\nimport { normalizeFunnelConnectionLimit } from './funnel-policy.ts'\nimport { isTailscaleVersion } from './tailscale-version.ts'\n\nexport type RemotePhase =\n | 'disabled'\n | 'starting'\n | 'login_required'\n | 'online'\n | 'error'\n | 'unavailable'\n | 'stopped'\n\nexport interface RemoteStatus {\n provider: 'tailscale-funnel'\n phase: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n /**\n * tailscale.com module version embedded in the helper, reported with its\n * first `starting` event. Undefined until a helper has started in this\n * plugin process — a disabled remote connection never spawns one, so the\n * settings page shows「未获取」rather than guessing from go.mod.\n */\n tailscaleVersion?: string\n updatedAt: number\n}\n\nexport interface RemoteSupervisorOptions {\n enabled: boolean\n hostname: string\n statePath: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n maxConnectionsPerSource?: number\n log: (message: string) => void\n}\n\ninterface HelperEvent {\n phase?: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n tailscaleVersion?: string\n}\n\nconst RESTART_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000]\n/**\n * When the helper dies with a Go panic, the supervisor keeps only the last\n * 2KB of stderr for the status message — enough for a summary but not for the\n * full goroutine dump that would pinpoint the crash (and be needed to report\n * upstream). Each crash therefore also writes the full stderr into the state\n * directory as crash-<millis>.log (0600), and the log line points at it.\n * Only the newest dumps are retained to bound disk usage.\n */\nexport const MAX_CRASH_DUMPS = 6\n/**\n * Throttle for configuration-level failures (helper binary missing, state dir\n * unwritable). The environment will not self-heal between attempts, so the\n * previous \"1s..30s exponential\" backoff was a CPU/for-loop on a misconfigured\n * Host. 60s matches the APNs sender-failure throttle on the host plugin\n * (index.ts SENDER_FAILURE_RETRY_MS) and the relay enrollment throttle.\n */\nconst UNAVAILABLE_RETRY_MS = 60_000\n\n/**\n * Exposed for tests: the backoff schedule depends on the failure category\n * (unavailable = config, won't self-heal; crash = transient). Tying this to\n * the constants above keeps the public schedule easy to assert against.\n */\nexport function restartDelayMs(kind: 'unavailable' | 'crash', attempt: number): number {\n if (kind === 'unavailable') return UNAVAILABLE_RETRY_MS\n const index = Math.min(attempt, RESTART_DELAYS_MS.length - 1)\n // RESTART_DELAYS_MS is a non-empty literal array, so the indexed value is\n // always defined; the explicit fallback is just to satisfy the strict\n // noUncheckedIndexedAccess setting without a non-null assertion.\n return RESTART_DELAYS_MS[index] ?? RESTART_DELAYS_MS[RESTART_DELAYS_MS.length - 1]!\n}\n\nexport const DEFAULT_REMOTE_HOSTNAME = 'dsh-deeppilot'\n\n/** Preserve custom node names while migrating every pre-DeepPilot default. */\nexport function normalizeRemoteHostname(value: string | undefined): string {\n const hostname = value?.trim() ?? ''\n if (hostname === '' || ['dsh-phone', 'dsh-pocket', 'harnesspocket'].includes(hostname.toLowerCase())) {\n return DEFAULT_REMOTE_HOSTNAME\n }\n return hostname\n}\n\nexport function tunnelHelperArguments(\n originURL: string,\n statePath: string,\n options: Pick<RemoteSupervisorOptions, 'hostname' | 'funnelPort' | 'maxConnectionsPerSource'>,\n): string[] {\n return [\n '--origin', originURL,\n '--hostname', normalizeRemoteHostname(options.hostname),\n '--state-dir', statePath,\n '--port', String(options.funnelPort ?? 443),\n '--max-connections-per-source', String(normalizeFunnelConnectionLimit(options.maxConnectionsPerSource)),\n ]\n}\n\n/**\n * Summarize a fatal helper crash from its stderr tail. Go panics dump the\n * actual reason on the `panic:` line while the trailing stack frames are\n * `file.go:NN +0xOFF` noise — the old behavior of reporting only the final\n * line surfaced exactly that noise (e.g. `.../singleflight.go:194 +0x45c`)\n * and hid the real panic message. Keep the crash header line (a raw\n * `panic:`, a runtime `fatal error:`, or net/http's `http: panic serving`),\n * plus an optional `[signal ...]` continuation and the first non-runtime\n * stack frame's symbol + `file.go:NN` as a location hint. Pointer offsets\n * are not architecture stable, so they are deliberately dropped.\n *\n * The frame symbol sits on the line just above the indented source line in a\n * Go dump, e.g.:\n * tailscale.com/net/dnscache.(*Resolver).lookupIP(0x1400012c000, {...})\n * tailscale.com@v1.102.3/net/dnscache/dnscache.go:604 +0x25c\n * The first symbol (shortened) is used because a bare `file.go:NN` is not\n * unique across tailscale (singleflight.go is one example).\n */\nexport function helperCrashSummary(stderr: string): string {\n const lines = stderr.split(/\\r?\\n/)\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim()\n if (line === '') continue\n // `panic:` line, a runtime `fatal error:`, or net/http's\n // `http: panic serving` (optionally prefixed by Go's log timestamp).\n const logTimestamp = /^(\\d{4}\\/\\d{2}\\/\\d{2} \\d{2}:\\d{2}:\\d{2} )/.exec(line)?.[1] ?? ''\n if (!/^(panic:|fatal error:|http: panic serving)/.test(logTimestamp.length > 0 ? line.slice(logTimestamp.length) : line)) {\n continue\n }\n let summary = logTimestamp.length > 0 ? line.slice(logTimestamp.length) : line\n const next = lines[i + 1]?.trim()\n if (next?.startsWith('[signal')) summary += ' ' + next\n // Walk the dump for the first non-runtime frame: symbol line followed by\n // an indented `path/file.go:NN` source line. Track the last symbol seen\n // before each source line.\n let lastSymbol: string | undefined\n for (let j = i + 1; j < lines.length; j++) {\n const frame = lines[j]!\n const trimmed = frame.trim()\n if (trimmed === '') continue\n // A symbol/argument line: `package.(*Type).method(args)` — not a source\n // line (which is indented and ends with `file.go:NN +0x…`). Take the\n // last symbol preceding a source line; the first source line after the\n // crash header is the crash point.\n const sourceMatch = /^\\s+(\\S+\\.go:\\d+)/.exec(frame)\n if (sourceMatch !== null) {\n const source = sourceMatch[1]!\n if (source.startsWith('runtime/') || source.startsWith('runtime\\\\')) {\n // runtime/debug.Stack() noise — skip to the next frame.\n lastSymbol = undefined\n continue\n }\n const slash = Math.max(source.lastIndexOf('/'), source.lastIndexOf('\\\\'))\n const file = slash >= 0 ? source.slice(slash + 1) : source\n const symbol = lastSymbol !== undefined ? shortenSymbol(lastSymbol) : undefined\n summary += ' @ ' + (symbol !== undefined ? `${symbol} (${file})` : file)\n break\n }\n // Otherwise this is a frame symbol (or a goroutine heading / created-by\n // note); remember the last one so the following source line can pair.\n if (trimmed.includes('(') || trimmed.endsWith(')')) {\n lastSymbol = trimmed\n }\n }\n return summary.replace(/\\s+/g, ' ').trim()\n }\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim()\n if (line !== '') return line\n }\n return ''\n}\n\n/** Shrink a Go frame symbol to `pkg.Receiver.method` form (drop module-root\n * path, generic type arguments and the trailing call arguments), so the\n * summary hint stays readable. Keeps a `.funcN` closure suffix when present\n * (e.g. `doCall.func2`), since it identifies the exact closure.\n *\n * A symbol looks like:\n * tailscale.com/util/singleflight.(*Group[...]).doCall.func2(0x…, …)\n * The trailing call arguments form a balanced paren group that starts at\n * the function name — that group is stripped first; generics appear only\n * inside `(*Group[...])` receiver brackets before the final method dot, so\n * they are removed by trimming from the first `[` to the matching `]`.\n */\nexport function shortenSymbol(symbol: string): string {\n const trimmed = symbol.trim()\n let end = trimmed.length\n if (trimmed.endsWith(')')) {\n let depth = 0\n for (let i = trimmed.length - 1; i >= 0; i--) {\n const ch = trimmed[i]!\n if (ch === ')') depth++\n else if (ch === '(') {\n depth--\n if (depth === 0) {\n end = i\n break\n }\n }\n }\n }\n let name = trimmed.slice(0, end).trim()\n // Remove generic instantiations `[...]` inside receiver/type brackets.\n const bracketOpen = name.indexOf('[')\n if (bracketOpen >= 0) {\n const bracketClose = name.indexOf(']', bracketOpen)\n if (bracketClose > bracketOpen) {\n name = name.slice(0, bracketOpen) + name.slice(bracketClose + 1)\n }\n }\n // Keep only the last path segment (after the final /).\n const slash = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\\\'))\n return slash >= 0 ? name.slice(slash + 1) : name\n}\n\n/** A normalized fatal-crash analysis used by the exit handler to produce a\n * bounded status message and, when a full dump is available, persist it. */\nexport interface CrashAnalysis {\n summary: string\n dumpPath?: string\n}\n\n/** Shared by crash summary and dump: whether the stderr looks like a Go\n * panic/fatal-error crash (vs. an ordinary exit). */\nfunction looksLikeCrash(stderr: string): boolean {\n return /(?:^|\\n)\\s*(?:\\d{4}\\/\\d{2}\\/\\d{2} \\d{2}:\\d{2}:\\d{2} )?(?:panic:|fatal error:|http: panic serving)/.test(stderr)\n}\n\n/** Turn a crash stderr tail into a status message and optionally persist the\n * full dump under the state dir. Keeps at most MAX_CRASH_DUMPS dump files. */\nexport async function analyzeCrashExit(stderr: string, statePath: string): Promise<CrashAnalysis> {\n const summary = helperCrashSummary(stderr) || 'helper exited'\n if (!looksLikeCrash(stderr) || stderr.trim() === '') return { summary }\n const target = statePath.trim()\n if (target === '') return { summary }\n try {\n const file = join(target, `crash-${Date.now()}.log`)\n await writeFile(file, stderr, { mode: 0o600 })\n await trimCrashDumps(target)\n return { summary, dumpPath: file }\n } catch {\n return { summary }\n }\n}\n\n/** Remove oldest crash-*.log files beyond MAX_CRASH_DUMPS. */\nasync function trimCrashDumps(dir: string): Promise<void> {\n let names: string[]\n try {\n names = (await readdir(dir)).filter((name) => name.startsWith('crash-') && name.endsWith('.log'))\n } catch {\n return\n }\n if (names.length <= MAX_CRASH_DUMPS) return\n names.sort()\n const excess = names.length - MAX_CRASH_DUMPS\n await Promise.all(names.slice(0, excess).map((name) => rm(join(dir, name), { force: true }).catch(() => {})))\n}\n\n/** Parse one helper IPC line without ever evaluating or interpolating it. */\nexport function parseHelperEvent(line: string): HelperEvent | null {\n try {\n const value = JSON.parse(line) as Record<string, unknown>\n const phases: RemotePhase[] = ['starting', 'login_required', 'online', 'error', 'stopped']\n if (typeof value.phase !== 'string' || !phases.includes(value.phase as RemotePhase)) return null\n return {\n phase: value.phase as RemotePhase,\n ...(typeof value.publicURL === 'string' ? { publicURL: value.publicURL } : {}),\n ...(typeof value.authURL === 'string' ? { authURL: value.authURL } : {}),\n ...(typeof value.message === 'string' ? { message: value.message.slice(0, 500) } : {}),\n // The version is rendered on the settings page, so the helper's stdout\n // stays untrusted input: a shape that is not a Go module version is\n // dropped instead of forwarded.\n ...(isTailscaleVersion(value.tailscaleVersion) ? { tailscaleVersion: value.tailscaleVersion } : {}),\n }\n } catch {\n return null\n }\n}\n\nfunction isTailscaleAuthURL(value: string | undefined): boolean {\n if (!value) return false\n try {\n const url = new URL(value)\n return url.protocol === 'https:' &&\n (url.hostname === 'login.tailscale.com' || url.hostname.endsWith('.login.tailscale.com'))\n } catch {\n return false\n }\n}\n\n/** Translate Node's platform/architecture names to the GOOS/GOARCH directory\n * names used by the committed helper matrix. */\nexport function bundledHelperPlatformDir(\n platform: NodeJS.Platform = process.platform,\n arch: string = process.arch,\n): string {\n const goos = platform === 'win32' ? 'windows' : platform\n const goarch = arch === 'x64' ? 'amd64' : arch\n return `${goos}-${goarch}`\n}\n\n/** Build the list of candidate locations for the embedded tunnel helper, in\n * priority order. The first existing executable wins at start() time. The\n * order matters: explicit config (handled by the caller) > npm install\n * layout > DSH-bundled layout > user data dir. */\nexport function bundledHelperCandidates(\n platform: NodeJS.Platform = process.platform,\n arch: string = process.arch,\n): string[] {\n const here = dirname(fileURLToPath(import.meta.url))\n const pkgRoot = resolve(here, '..')\n const fileName = platform === 'win32' ? 'dsh-deeppilot-tunnel.exe' : 'dsh-deeppilot-tunnel'\n const platformDir = bundledHelperPlatformDir(platform, arch)\n\n const candidates: string[] = []\n // 1. Standard npm layout: <pkgRoot>/bin/<os>-<arch>/<file>\n candidates.push(resolve(pkgRoot, 'bin', platformDir, fileName))\n // 2. DSH sometimes relocates lib/ into apps/web or a build cache while\n // leaving bin/ beside the original package root. Try a few hops up.\n candidates.push(resolve(pkgRoot, '..', '..', '..', 'node_modules', 'dsh-deeppilot', 'bin', platformDir, fileName))\n candidates.push(resolve(pkgRoot, '..', '..', 'dsh-deeppilot', 'bin', platformDir, fileName))\n candidates.push(resolve(pkgRoot, '..', '..', '..', '..', 'node_modules', 'dsh-deeppilot', 'bin', platformDir, fileName))\n\n // 3. As a last resort, ask the loader to resolve the binary via Node's\n // package resolution. Works when bin/ is shipped but import.meta.url\n // points somewhere unexpected.\n try {\n const require = createRequire(import.meta.url)\n const resolved = require.resolve(`dsh-deeppilot/bin/${platformDir}/${fileName}`)\n if (!candidates.includes(resolved)) candidates.push(resolved)\n } catch {\n /* package.json exports field may not list bin/ — ignore */\n }\n\n // 4. User data dir fallback so power users can drop a custom build in.\n const home = process.env.DSH_HOME?.trim()\n || process.env.HOME\n || process.env.USERPROFILE\n if (home && home.length > 0) {\n const dataDir = resolve(home, '.dsh')\n candidates.push(join(dataDir, 'deeppilot', 'bin', platformDir, fileName))\n }\n\n return candidates\n}\n\n/** Owns exactly one embedded tunnel helper and restarts it after failures. */\nexport class RemoteSupervisor {\n private child: ChildProcess | undefined\n private restartTimer: NodeJS.Timeout | undefined\n private restartAttempt = 0\n private stopping = false\n private statusValue: RemoteStatus\n\n constructor(private readonly options: RemoteSupervisorOptions) {\n this.statusValue = {\n provider: 'tailscale-funnel',\n phase: options.enabled ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n }\n\n status(): RemoteStatus {\n return { ...this.statusValue }\n }\n\n async start(originURL: string): Promise<void> {\n if (!this.options.enabled || this.child !== undefined || this.stopping) return\n const statePath = expandHome(this.options.statePath)\n // Resolve a usable helper path. An explicit remote.helperPath wins; if it\n // is missing, fall back to a small set of candidate locations so that\n // DSH-bundled layouts and manually-dropped builds still work.\n const configured = this.options.helperPath?.trim() ?? ''\n const candidates = configured\n ? [expandHome(configured)]\n : bundledHelperCandidates()\n let helper: string | undefined\n let lastError: unknown\n for (const candidate of candidates) {\n try {\n await access(candidate, fsConstants.X_OK)\n helper = candidate\n break\n } catch (error) {\n lastError = error\n }\n }\n if (helper === undefined) {\n // A dispose() that raced these checks must not be overwritten by a\n // late failure report.\n if (this.stopping) return\n const platform = `${process.platform}-${process.arch}`\n const message = configured\n ? `embedded tunnel helper unavailable: ${configured}: ${String(lastError ?? 'not found')}`\n : `embedded tunnel helper not found for ${platform} (tried: ${candidates.join(', ')}); set remote.helperPath to override`\n this.setStatus({ phase: 'unavailable', message })\n this.scheduleRestart(originURL, 'unavailable')\n return\n }\n try {\n await mkdir(statePath, { recursive: true, mode: 0o700 })\n } catch (error) {\n if (this.stopping) return\n this.setStatus({ phase: 'unavailable', message: `cannot create remote state dir: ${String(error)}` })\n this.scheduleRestart(originURL, 'unavailable')\n return\n }\n\n // dispose() may have run while the filesystem checks above were in\n // flight; spawning afterwards would orphan a helper process that nothing\n // will ever reap (dispose already returned seeing no child).\n if (this.stopping) return\n\n this.setStatus({ phase: 'starting', message: undefined })\n const child = spawn(helper, tunnelHelperArguments(originURL, statePath, this.options), {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n PATH: process.env.PATH ?? '/usr/bin:/bin',\n TMPDIR: process.env.TMPDIR ?? '/tmp',\n },\n })\n this.child = child\n if (child.stdout === null || child.stderr === null) {\n this.setStatus({ phase: 'error', message: 'helper stdio unavailable' })\n child.kill('SIGTERM')\n return\n }\n\n let stdoutBuffer = ''\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', (chunk: string) => {\n stdoutBuffer += chunk\n const lines = stdoutBuffer.split('\\n')\n stdoutBuffer = lines.pop() ?? ''\n for (const line of lines) this.acceptLine(line)\n })\n\n // Keep enough stderr to write a full crash dump on exit; the summary the\n // user sees is derived from this buffer.\n let stderrBuffer = ''\n child.stderr.setEncoding('utf8')\n child.stderr.on('data', (chunk: string) => {\n stderrBuffer = (stderrBuffer + chunk).slice(-16_000)\n })\n\n child.once('error', (error) => {\n this.setStatus({ phase: 'error', message: `helper launch failed: ${String(error)}` })\n })\n child.once('exit', () => {\n if (this.child === child) this.child = undefined\n if (this.stopping) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n void analyzeCrashExit(stderrBuffer, this.options.statePath).then((analysis) => {\n if (this.stopping) return\n const message = analysis.summary + (analysis.dumpPath !== undefined ? ` (full dump: ${analysis.dumpPath})` : '')\n this.setStatus({\n phase: 'error',\n message,\n })\n this.scheduleRestart(originURL, 'crash')\n })\n })\n }\n\n async dispose(): Promise<void> {\n this.stopping = true\n if (this.restartTimer !== undefined) clearTimeout(this.restartTimer)\n this.restartTimer = undefined\n const child = this.child\n this.child = undefined\n if (child === undefined) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n await new Promise<void>((resolveDone) => {\n const force = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n }, 3_000)\n child.once('exit', () => {\n clearTimeout(force)\n resolveDone()\n })\n child.kill('SIGTERM')\n })\n this.setStatus({ phase: 'stopped', message: undefined })\n }\n\n private acceptLine(line: string): void {\n const event = parseHelperEvent(line)\n if (event === null || event.phase === undefined) return\n if (event.phase === 'login_required') {\n // UserLogf is diagnostic output, not a typed auth callback. Accept only\n // the known login origin, and never let an incidental runtime URL knock\n // an already-online Funnel back into the setup state.\n if (this.statusValue.phase === 'online' || !isTailscaleAuthURL(event.authURL)) return\n }\n if (event.phase === 'online') this.restartAttempt = 0\n this.setStatus({ ...event, phase: event.phase })\n }\n\n private scheduleRestart(originURL: string, kind: 'unavailable' | 'crash' = 'crash'): void {\n if (this.stopping || this.restartTimer !== undefined) return\n // Configuration-level failures (helper missing, state dir unwritable)\n // cannot self-heal in a tight loop; throttle to one probe per minute so\n // a misconfigured Host stays observable without becoming a CPU sink.\n // Crashes still get the original exponential backoff so a transient\n // helper failure recovers quickly.\n const delay = kind === 'unavailable'\n ? UNAVAILABLE_RETRY_MS\n : RESTART_DELAYS_MS[Math.min(this.restartAttempt, RESTART_DELAYS_MS.length - 1)]\n if (kind === 'crash') this.restartAttempt += 1\n this.restartTimer = setTimeout(() => {\n this.restartTimer = undefined\n void this.start(originURL)\n }, delay)\n this.restartTimer.unref?.()\n }\n\n private setStatus(next: Partial<RemoteStatus> & Pick<RemoteStatus, 'phase'>): void {\n const cleared: Partial<RemoteStatus> = next.phase === 'online'\n ? { authURL: undefined, message: undefined }\n : next.phase === 'login_required'\n ? { publicURL: undefined, message: undefined }\n : next.phase === 'starting' || next.phase === 'stopped' || next.phase === 'disabled'\n ? { publicURL: undefined, authURL: undefined, message: undefined }\n : {}\n this.statusValue = {\n ...this.statusValue,\n ...cleared,\n ...next,\n updatedAt: Date.now(),\n }\n if (next.phase === 'online') {\n this.options.log('remote Funnel online')\n } else if (next.phase === 'login_required') {\n this.options.log('remote Funnel requires browser authorization')\n } else if (next.phase === 'error' || next.phase === 'unavailable') {\n this.options.log(`remote Funnel ${next.phase}: ${next.message ?? 'unknown error'}`)\n }\n }\n}\n","export const DEFAULT_LOCAL_PORT = 3098\nexport const MIN_LOCAL_PORT = 1024\nexport const MAX_LOCAL_PORT = 65_535\n\nexport function normalizeLocalPort(value: unknown): number {\n return typeof value === 'number'\n && Number.isInteger(value)\n && value >= MIN_LOCAL_PORT\n && value <= MAX_LOCAL_PORT\n ? value\n : DEFAULT_LOCAL_PORT\n}\n\n/** The LAN listener is TLS-only, so every advertised endpoint is `https://`. */\nexport function localEndpointURLs(addresses: readonly string[], port: number): string[] {\n const normalizedPort = normalizeLocalPort(port)\n return [...new Set(addresses)].map((address) => `https://${address}:${normalizedPort}`)\n}\n\nexport function localListenError(error: unknown, port: number): string {\n const code = (error as NodeJS.ErrnoException | undefined)?.code\n if (code === 'EADDRINUSE') return `local port ${port} is already in use`\n if (code === 'EACCES') return `permission denied while opening local port ${port}`\n return error instanceof Error ? error.message : String(error)\n}\n","import { join } from 'node:path'\nimport z from '@deepseek-ai/schemastery'\nimport {\n DEFAULT_REMOTE_HOSTNAME,\n} from './remote-supervisor.ts'\nimport { DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE, MAX_FUNNEL_CONNECTIONS_PER_SOURCE } from './funnel-policy.ts'\nimport { bridgeDataDir } from './token.ts'\nimport { DEFAULT_LOCAL_PORT, MAX_LOCAL_PORT, MIN_LOCAL_PORT } from './local-policy.ts'\n\nexport interface Config {\n /** Master switch; when false the plugin activates and does nothing. */\n enabled?: boolean\n /** Protocol-v2 device registry JSON path. */\n devicesPath?: string\n /** Replay ring buffer bound (frames) per deployment. */\n historyBufferMax?: number\n /** Testing & troubleshooting, rendered as its own settings section. */\n diagnostics?: {\n /** Verbose per-frame diagnostics (never prints token or message bodies). */\n debug?: boolean\n }\n /** Independent LAN transport. Never exposes the wider DSH web server. */\n local?: {\n enabled?: boolean\n port?: number\n }\n /** Optional embedded remote transport. Reconciled when settings change. */\n remote?: {\n enabled?: boolean\n provider?: 'tailscale-funnel'\n hostname?: string\n statePath?: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n /** Concurrent Funnel WebSockets allowed from one public source address. */\n maxConnectionsPerSource?: number\n }\n /**\n * Offline push (F-9). `apns` sends directly from the computer with the user's\n * Apple credentials. `relay` sends notify projections to an operator-run\n * relay for distributed builds. The device reports its own APNs environment,\n * so development and TestFlight/App Store devices may coexist.\n */\n push?: {\n /** `none` (default), `apns`, or `relay`. */\n provider?: 'none' | 'apns' | 'relay'\n /** Generic mode removes conversation-derived titles and bodies before outbound push. */\n contentMode?: 'preview' | 'generic'\n /** Apple Developer team id (JWT iss claim). */\n teamId?: string\n /** APNs auth key id (JWT kid header). */\n keyId?: string\n /** `.p8` private key path. */\n keyPath?: string\n /** App bundle id — the apns-topic header. */\n bundleId?: string\n /** Relay base URL. */\n relayUrl?: string\n /** Per-user bearer token issued by the relay operator. */\n relayToken?: string\n }\n}\n\n/** Operator-run relay used by distributed builds; overridable via config. */\nexport const DEFAULT_RELAY_URL = 'https://pilot.hailab.dev'\n\nexport const Config = z.object({\n // rc.2 accepts live settings writes only for fields declared volatile.\n enabled: z.boolean().default(true).volatile(),\n devicesPath: z.string().default(join(bridgeDataDir(), 'devices-v2.json')),\n historyBufferMax: z.natural().min(100).default(2000),\n diagnostics: z.object({\n debug: z.boolean().default(false)\n .description('打印握手、推送等诊断日志;不会打印配对令牌、APNs token 或消息内容'),\n }).default({\n debug: false,\n }).volatile(),\n local: z.object({\n enabled: z.boolean().default(true),\n port: z.natural()\n .min(MIN_LOCAL_PORT)\n .max(MAX_LOCAL_PORT)\n .default(DEFAULT_LOCAL_PORT)\n .description('DeepPilot 局域网独立端口(仅 TLS;默认 3098,修改后本地连接会短暂重连)'),\n }).default({\n enabled: true,\n port: DEFAULT_LOCAL_PORT,\n }).volatile(),\n remote: z.object({\n enabled: z.boolean().default(false),\n provider: z.union(['tailscale-funnel'] as const).default('tailscale-funnel'),\n hostname: z.string().default(DEFAULT_REMOTE_HOSTNAME),\n statePath: z.string().default(join(bridgeDataDir(), 'tailscale')),\n helperPath: z.string().default(''),\n funnelPort: z.union([443, 8443, 10000] as const).default(443),\n maxConnectionsPerSource: z.natural()\n .min(1)\n .max(MAX_FUNNEL_CONNECTIONS_PER_SOURCE)\n .default(DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE)\n .description('Funnel 每个来源允许的并发连接数(1–16,修改后远程连接会短暂重连)'),\n }).default({\n enabled: false,\n provider: 'tailscale-funnel',\n hostname: DEFAULT_REMOTE_HOSTNAME,\n statePath: join(bridgeDataDir(), 'tailscale'),\n helperPath: '',\n funnelPort: 443,\n maxConnectionsPerSource: DEFAULT_FUNNEL_CONNECTIONS_PER_SOURCE,\n }).volatile(),\n push: z.object({\n provider: z.union(['none', 'apns', 'relay'] as const).default('none'),\n contentMode: z.union(['preview', 'generic'] as const).default('preview'),\n teamId: z.string().default(''),\n keyId: z.string().default(''),\n keyPath: z.string().default(join(bridgeDataDir(), 'apns', 'AuthKey.p8')),\n bundleId: z.string().default('dev.hailab.deeppilot'),\n relayUrl: z.string().default(DEFAULT_RELAY_URL),\n relayToken: z.string().default(''),\n }).default({\n provider: 'none',\n contentMode: 'preview',\n teamId: '',\n keyId: '',\n keyPath: join(bridgeDataDir(), 'apns', 'AuthKey.p8'),\n bundleId: 'dev.hailab.deeppilot',\n relayUrl: DEFAULT_RELAY_URL,\n relayToken: '',\n }),\n})\n\n/**\n * Deep-unwrap live-update (volatile) references to plain values.\n *\n * On a 0.1.7 host, `apply()`'s options — and even a direct `Config(raw)` call\n * — carry `{ get() }` reference objects for volatile fields. Every consumer of\n * `currentConfig()` compares plain values (`=== true`, numeric bounds, object\n * fields), and schemastery refuses to re-parse a reference, so normalization\n * unwraps on the way in and on the way out. A JSON-shaped config value cannot\n * carry a function, so the `get` check cannot false-positive.\n */\nexport function plainConfig(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (typeof (value as { get?: unknown }).get === 'function') {\n // A reference's snapshot is recursively plain data (references cannot\n // nest inside references), so one unwrap layer suffices; recursion keeps\n // plain containers working around it.\n return plainConfig((value as { get: () => unknown }).get())\n }\n if (Array.isArray(value)) return value.map(plainConfig)\n const plain: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) plain[key] = plainConfig(item)\n return plain\n}\n\n/**\n * Cordis hands the second argument in different shapes depending on host\n * composition: a reactive options getter, the resolved config value, or\n * nothing when the patch row omits `config`. Normalize all of them — and\n * unwrap volatile references so the returned Config is plain data.\n */\nexport function normalizeOptions(options: unknown): Config {\n if (typeof options === 'function') return plainConfig((options as () => unknown)()) as Config\n if (options && typeof options === 'object') return plainConfig(options) as Config\n const validated = (Config as unknown as (data: unknown) => Config)(undefined)\n return plainConfig(validated ?? {}) as Config\n}\n","/**\n * Minimal APNs provider client (HTTP/2) with zero npm dependencies.\n *\n * Implements exactly what the bridge needs:\n * - ES256 provider token (JWT) signed with an Apple .p8 key, refreshed under\n * the 1-hour freshness window Apple enforces;\n * - one long-lived HTTP/2 session per environment, recreated transparently\n * after GOAWAY/errors;\n * - alert pushes carrying the notify projection (category/thread/collapse),\n * with `interruption-level: time-sensitive` for approval/question events;\n * - outcome classification so callers can prune dead device tokens.\n *\n * Privacy: logs carry outcomes and masked token prefixes only — never message\n * bodies or full tokens.\n */\n\nimport { connect as http2Connect, type ClientHttp2Session } from 'node:http2'\nimport { createHash, createPrivateKey, sign as cryptoSign, type KeyObject } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushDelivery, PushNotification } from './protocol.ts'\n\nexport interface ApnsClientOptions {\n teamId: string\n keyId: string\n /** Absolute path to the .p8 private key file. */\n keyPath: string\n bundleId: string\n debug?: boolean\n log: (message: string) => void\n}\n\nexport type ApnsOutcome = 'sent' | 'invalid-token' | 'failed'\n\n/** Outcome plus Apple's rejection reason (when present) for diagnosis. */\nexport interface ApnsSendResult {\n outcome: ApnsOutcome\n reason?: string\n}\n\n/** Classify Apple's reason without losing recoverable configuration errors. */\nexport function classifyApnsReason(reason: string): ApnsOutcome {\n return reason === 'Unregistered' || reason === 'ExpiredToken' ? 'invalid-token' : 'failed'\n}\n\nconst PROVIDER_TOKEN_TTL_MS = 50 * 60 * 1000 // Apple allows 1h; refresh early.\nconst REQUEST_TIMEOUT_MS = 10_000\n\n/** base64url without padding. */\nfunction b64url(input: Buffer | string): string {\n return Buffer.from(input).toString('base64url')\n}\n\n/** Sign one ES256 JWT for the given signing input with a P-256 private key. */\nexport function es256Jwt(signingInput: string, key: KeyObject): string {\n const signature = cryptoSign('sha256', Buffer.from(signingInput, 'utf8'), {\n key,\n // JOSE/ES256 wants raw r||s; node emits DER by default.\n dsaEncoding: 'ieee-p1363',\n })\n return signingInput + '.' + b64url(signature)\n}\n\n/** Strip PEM armor from a .p8 file and decode to PKCS#8 DER. */\nexport function p8ToDer(pem: string): Buffer {\n const body = pem\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\n .replace(/-----END PRIVATE KEY-----/, '')\n .replace(/\\s+/g, '')\n return Buffer.from(body, 'base64')\n}\n\nexport interface LoadedKey {\n key: KeyObject\n}\n\n/** Pure payload builder so tests can assert the wire format without sockets. */\nexport function apnsPayload(notification: PushDelivery): Record<string, unknown> {\n if ('kind' in notification && notification.kind === 'liveactivity') return { aps: {\n timestamp: notification.timestamp, event: notification.event,\n 'content-state': notification.contentState,\n ...(notification.event === 'end' ? { 'dismissal-date': notification.timestamp + 300 } : { 'stale-date': notification.timestamp + 180 }),\n } }\n if ('kind' in notification && notification.kind === 'widget') return { aps: { 'content-changed': true } }\n const alert = notification as PushNotification\n return alertPayload(alert)\n}\n\nfunction alertPayload(notification: PushNotification): Record<string, unknown> {\n // 需要用户动手或任务出事的通知穿透专注模式;任务完成保持普通级别。\n const timeSensitive = notification.category === 'approval.required' || notification.category === 'question.asked' ||\n notification.category === 'session.error'\n return {\n aps: {\n alert: {\n title: notification.title.slice(0, 120),\n body: notification.body.slice(0, 200),\n },\n sound: 'default',\n category: notification.category,\n // 让 Notification Service Extension 有机会按类别给横幅附上阶段缩略图。\n 'mutable-content': 1,\n 'thread-id': notification.hostAudience ? createHash('sha256').update(`${notification.hostAudience}:${notification.sessionId}`).digest('hex') : notification.sessionId.slice(0, 64),\n ...(timeSensitive ? { 'interruption-level': 'time-sensitive' } : {}),\n },\n ...(notification.hostAudience ? { hostAudience: notification.hostAudience } : {}),\n sessionId: notification.sessionId,\n notificationId: notification.notificationId,\n kind: notification.category,\n // 长按展开页使用;仅审批通知携带,generic 模式下已在 pushContent 中剔除。\n ...(notification.category === 'approval.required' && notification.toolName\n ? { toolName: notification.toolName.slice(0, 80), ...(notification.detail ? { detail: notification.detail.slice(0, 600) } : {}) }\n : {}),\n }\n}\n\n/** One delivery request: the notify projection plus its target device token.\n * The environment comes from the device's own registration (its build kind),\n * so one client serves both sandbox and production devices. */\nexport type ApnsSendRequest = PushDelivery & {\n deviceToken: string\n environment: ApnsEnvironment\n}\n\nexport function pushHeaders(bundleId: string, notification: PushDelivery): Record<string, string> {\n if ('kind' in notification && notification.kind === 'liveactivity') return {\n 'apns-topic': bundleId + '.push-type.liveactivity',\n 'apns-push-type': 'liveactivity', 'apns-priority': notification.event === 'end' ? '10' : '5',\n 'apns-expiration': String(notification.timestamp + 180),\n }\n if ('kind' in notification && notification.kind === 'widget') {\n return {\n 'apns-topic': bundleId + '.push-type.widgets',\n 'apns-push-type': 'widgets',\n 'apns-priority': '5',\n 'apns-collapse-id': 'widget-overview',\n }\n }\n return {\n 'apns-topic': bundleId, 'apns-push-type': 'alert', 'apns-priority': '10',\n 'apns-collapse-id': collapseIdFor(notification as PushNotification),\n }\n}\n\n/** collapse-id accepts ≤64 bytes of ASCII; keep it stable per session+event. */\nexport function collapseIdFor(notification: PushNotification): string {\n const raw = `${notification.hostAudience ? notification.hostAudience + ':' : ''}${notification.category}:${notification.sessionId}`\n const readable = raw.replace(/[^a-zA-Z0-9.:-]/g, '')\n const digest = createHash('sha256').update(raw, 'utf8').digest('hex').slice(0, 12)\n return `${readable.slice(0, 51)}:${digest}`\n}\n\nfunction authorityFor(environment: ApnsEnvironment): string {\n return environment === 'production' ? 'api.push.apple.com' : 'api.sandbox.push.apple.com'\n}\n\nexport class ApnsClient {\n private readonly log: (message: string) => void\n private readonly debug: boolean\n private readonly opts: ApnsClientOptions\n\n private providerToken = ''\n private providerTokenIssuedAt = 0\n private key: KeyObject | undefined\n\n constructor(opts: ApnsClientOptions) {\n this.opts = opts\n this.log = opts.log\n this.debug = opts.debug === true\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.sessions.values()]\n this.sessions.clear()\n await Promise.all(\n sessions\n .filter((session) => !session.destroyed)\n .map((session) => new Promise<void>((resolve) => session.close(() => resolve()))),\n )\n }\n\n // ---------- credentials ----------\n\n private async ensureProviderToken(): Promise<string> {\n if (this.providerToken && Date.now() - this.providerTokenIssuedAt < PROVIDER_TOKEN_TTL_MS) {\n return this.providerToken\n }\n if (!this.key) {\n const pem = await readFile(this.opts.keyPath, 'utf8')\n this.key = createPrivateKey({ key: p8ToDer(pem), format: 'der', type: 'pkcs8' })\n }\n const issuedAt = Math.floor(Date.now() / 1000)\n const header = b64url(JSON.stringify({ alg: 'ES256', kid: this.opts.keyId }))\n const claims = b64url(JSON.stringify({ iss: this.opts.teamId, iat: issuedAt }))\n this.providerToken = es256Jwt(`${header}.${claims}`, this.key)\n this.providerTokenIssuedAt = Date.now()\n return this.providerToken\n }\n\n // ---------- transport ----------\n\n /** One long-lived HTTP/2 session per Apple host (sandbox + production). */\n private readonly sessions = new Map<string, ClientHttp2Session>()\n\n private ensureSession(authority: string): ClientHttp2Session {\n const existing = this.sessions.get(authority)\n if (existing && !existing.destroyed && !existing.closed) return existing\n const session = http2Connect(`https://${authority}`)\n // Keep a handler attached so background errors never become uncaught\n // exceptions; the next send() recreates the session when dead.\n session.on('error', (error) => {\n if (this.debug) this.log('apns session error (' + authority + '): ' + String(error))\n this.sessions.delete(authority)\n })\n this.sessions.set(authority, session)\n return session\n }\n\n /**\n * Deliver one alert. Never throws — every failure path resolves to an\n * outcome so fan-out loops cannot crash the host on a flaky network.\n */\n async send(request: ApnsSendRequest): Promise<ApnsSendResult> {\n const { deviceToken, environment, ...notification } = request\n let stream: ReturnType<ClientHttp2Session['request']> | undefined\n try {\n const token = await this.ensureProviderToken()\n const body = JSON.stringify(apnsPayload(notification))\n const session = this.ensureSession(authorityFor(environment))\n const done = new Promise<ApnsSendResult>((resolve) => {\n const req = session.request({\n [':method']: 'POST',\n [':path']: '/3/device/' + deviceToken,\n authorization: 'bearer ' + token,\n ...pushHeaders(this.opts.bundleId, notification),\n // Stale approvals arriving hours later are worse than none.\n 'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600),\n 'content-type': 'application/json',\n 'content-length': String(Buffer.byteLength(body)),\n })\n stream = req\n let status = 0\n let responseBody = ''\n const settle = (outcome: ApnsOutcome, reason?: string): void => {\n if (this.debug) {\n this.log(`apns ${outcome}${reason ? ' (' + reason + ')' : ''} (${this.maskToken(deviceToken)})`)\n }\n resolve(reason !== undefined && reason !== '' ? { outcome, reason } : { outcome })\n }\n const timer = setTimeout(() => {\n req.close()\n settle('failed')\n }, REQUEST_TIMEOUT_MS)\n timer.unref?.()\n req.on('response', (headers) => {\n status = Number(headers[':status'] ?? 0)\n })\n req.on('data', (chunk: Buffer) => {\n responseBody += chunk.toString('utf8')\n })\n req.on('error', () => {\n clearTimeout(timer)\n settle('failed')\n })\n req.on('end', () => {\n clearTimeout(timer)\n if (status === 200) return settle('sent')\n let reason = ''\n try {\n reason = String((JSON.parse(responseBody) as { reason?: string }).reason ?? '')\n } catch {\n // non-JSON body; fall through with empty reason\n }\n if (status !== 200 && !reason) reason = 'HTTP ' + String(status)\n // Unregistered/ExpiredToken are authoritative lifecycle verdicts.\n // BadDeviceToken can also mean the provider selected the wrong APNs\n // environment; pruning then destroys a valid registration exactly\n // when the operator needs its stored environment to diagnose config.\n const outcome = classifyApnsReason(reason)\n if (outcome === 'invalid-token') return settle(outcome, reason)\n if (this.debug) this.log(`apns rejected status=${status} reason=${reason}`)\n settle('failed', reason)\n })\n req.end(body)\n })\n return await done\n } catch (error) {\n // Credential load failures and dead sessions land here; force both to be\n // rebuilt on the next attempt.\n this.key = undefined\n this.providerToken = ''\n this.sessions.clear()\n if (this.debug) this.log('apns send failed: ' + String(error))\n return { outcome: 'failed', reason: String(error).slice(0, 120) }\n } finally {\n try { stream?.close() } catch { /* already closed */ }\n }\n }\n\n maskToken(token: string): string {\n return token.length <= 10 ? '…' : token.slice(0, 6) + '…' + token.slice(-4)\n }\n}\n","/**\n * Normalize an operator-configured relay URL without a backtracking regular\n * expression. Scanning from the end keeps even pathological inputs linear.\n */\nexport function normalizeRelayBaseUrl(value: string): string {\n const trimmed = value.trim()\n let end = trimmed.length\n while (end > 0 && trimmed.charCodeAt(end - 1) === 0x2f) end -= 1\n return end === trimmed.length ? trimmed : trimmed.slice(0, end)\n}\n","/**\n * Client for the operator-run push relay (relay/server.js). Used when the App\n * is distributed under the operator's team: bridges cannot hold the\n * distributor's .p8, so they forward notify projections over HTTPS and let\n * the relay do APNs.\n *\n * Outcome contract mirrors src/apns.ts exactly so the dispatcher prunes dead\n * tokens identically on either path:\n * sent | invalid-token (terminal Unregistered/ExpiredToken) | failed\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushDelivery } from './protocol.ts'\nimport { normalizeRelayBaseUrl } from './relay-url.ts'\n\nexport interface RelayClientOptions {\n /** Base URL of the relay, e.g. https://relay.example.com */\n url: string\n /** Per-user bearer token issued by the relay operator (or via enroll). */\n token?: string\n timeoutMs?: number\n debug?: boolean\n log: (message: string) => void\n}\n\nexport interface RelaySendRequest {\n deviceToken: string\n environment: ApnsEnvironment\n notification: PushDelivery\n}\n\nexport class RelayClient {\n private readonly base: string\n private readonly token: string\n private readonly timeoutMs: number\n private readonly debug: boolean\n private readonly log: (message: string) => void\n\n constructor(opts: RelayClientOptions) {\n this.base = normalizeRelayBaseUrl(opts.url)\n this.token = (opts.token ?? '').trim()\n this.timeoutMs = opts.timeoutMs ?? 10_000\n this.debug = opts.debug === true\n this.log = opts.log\n }\n\n /**\n * Zero-touch enrollment: exchange the distributor's shared key (baked into\n * the distributed app) for a stable per-bridge bearer token. Idempotent —\n * relays derive the same token for the same clientId. Returns null on any\n * failure; callers treat that as \"not enrolled yet\", not as an error.\n */\n async enroll(clientId: string, enrollKey: string): Promise<string | null> {\n try {\n const response = await fetch(this.base + '/v1/enroll', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (!response.ok) {\n if (this.debug) this.log(`enroll http ${response.status}`)\n return null\n }\n const body = (await response.json()) as { token?: unknown }\n if (typeof body.token === 'string' && body.token.startsWith('rl_')) return body.token\n if (this.debug) this.log('enroll returned no usable token')\n return null\n } catch (error) {\n if (this.debug) this.log('enroll failed: ' + String(error))\n return null\n }\n }\n\n async send(request: RelaySendRequest): Promise<{ outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }> {\n try {\n const response = await fetch(this.base + '/v1/push', {\n method: 'POST',\n headers: {\n authorization: 'Bearer ' + this.token,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n deviceToken: request.deviceToken,\n environment: request.environment,\n notification: request.notification,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (response.status === 401 || response.status === 429) {\n // Credential/quota problems are configuration errors, not token death;\n // surface as failed so the user fixes the config instead of silently\n // losing registrations.\n if (this.debug) this.log(`relay rejected status=${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n if (!response.ok) {\n if (this.debug) this.log(`relay http ${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n const body = (await response.json()) as { outcome?: string; reason?: string }\n if (body.outcome === 'sent') return { outcome: 'sent' }\n if (body.outcome === 'invalid-token') return { outcome: 'invalid-token', reason: body.reason }\n if (this.debug) this.log('relay outcome=' + String(body.outcome) + ' reason=' + String(body.reason ?? ''))\n return { outcome: 'failed', reason: body.reason }\n } catch (error) {\n if (this.debug) this.log('relay send failed: ' + String(error))\n const message = error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)\n return { outcome: 'failed', reason: message }\n }\n }\n}\n","/** Coalesce bursts and bound continuous updates, without starving the trailing\n * state. One scheduler per host, not per token or per streaming text frame. */\nexport class WidgetPushScheduler {\n private timer?: ReturnType<typeof setTimeout>\n private lastSent = 0\n private disposed = false\n private sending = false\n private dirty = false\n\n constructor(private readonly send: () => Promise<void>, private readonly intervalMs = 30_000) {}\n\n changed(): void {\n if (this.disposed) return\n this.dirty = true\n if (this.timer || this.sending) return\n this.timer = setTimeout(() => {\n this.timer = undefined\n this.dirty = false\n this.sending = true\n this.lastSent = Date.now()\n void this.send().catch(() => {}).finally(() => {\n this.sending = false\n if (this.dirty) this.changed()\n })\n }, Math.max(Math.min(1000, this.intervalMs), this.intervalMs - (Date.now() - this.lastSent)))\n this.timer.unref()\n }\n\n dispose(): void {\n this.disposed = true\n if (this.timer) clearTimeout(this.timer)\n this.timer = undefined\n }\n}\n","import type { DeviceStore } from './token.ts'\nimport type { LiveActivityState, LiveActivityPushNotification, SessionSummary } from './protocol.ts'\nimport type { ApnsSendResult } from './apns.ts'\nimport { WidgetPushScheduler } from './widget-push.ts'\n\nexport function liveActivityState(session?: SessionSummary): LiveActivityState {\n const total = Math.max(0, session?.todos?.total ?? session?.todoItems?.length ?? 0)\n const done = Math.min(total, Math.max(0, session?.todos?.done ?? 0))\n const task = session?.todoItems?.find(item => item.status === 'in_progress') ??\n session?.todoItems?.find(item => item.status === 'pending')\n return {\n title: Array.from(session?.title ?? '').slice(0, 100).join(''),\n task: Array.from(session?.activity?.trim() || task?.content || '').slice(0, 160).join(''), done, total,\n phase: !session || total === 0 ? 'unavailable' : session.pendingApproval ? 'approval' :\n session.pendingQuestion ? 'question' : session.status === 'running' ? 'running' :\n session.status === 'idle' ? 'ended' : session.status === 'error' ? 'failed' : 'unavailable',\n }\n}\n\n/** Persist terminal state before coalescing so a rapid next round cannot revive an activity. */\nexport class LiveActivityPushManager {\n private latest = new Map<string, SessionSummary>()\n private sent = new Map<string, string>()\n private readonly scheduler: WidgetPushScheduler\n constructor(\n private readonly devices: () => DeviceStore | undefined,\n private readonly send: (token: string, environment: 'development' | 'production', notification: LiveActivityPushNotification) => Promise<ApnsSendResult>,\n intervalMs = 15_000,\n ) { this.scheduler = new WidgetPushScheduler(() => this.flush(), intervalMs) }\n\n changed(sessions: SessionSummary[]): void {\n this.latest = new Map(sessions.map(session => [session.id, session]))\n for (const device of this.devices()?.list() ?? []) {\n const r = device.liveActivity\n if (!r) continue\n const state = liveActivityState(this.latest.get(r.sessionId))\n if (state.phase === 'ended' || state.phase === 'failed' || state.phase === 'unavailable') {\n this.devices()?.endLiveActivity(device.deviceId, r.token, state)\n }\n }\n this.scheduler.changed()\n }\n\n async flush(): Promise<void> {\n const devices = this.devices()\n if (!devices) return\n const valid = new Set<string>()\n for (const device of devices.list()) {\n const r = device.liveActivity\n if (!r) continue\n const key = device.deviceId + ':' + r.token\n valid.add(key)\n if (device.revokedAt !== undefined || !['notifications.register', 'sessions.read', 'interactions.respond'].every(scope => device.scopes?.some(value => value === scope)) || r.expiresAt <= Date.now()) {\n devices.clearLiveActivity(device.deviceId, r.activityId, r.token)\n continue\n }\n const state = r.endedState ?? liveActivityState(this.latest.get(r.sessionId))\n const fingerprint = JSON.stringify(state)\n if (this.sent.get(key) === fingerprint) continue\n // Never send a queued registration after it has rotated or been removed.\n if (devices.authorized(device.deviceId)?.liveActivity?.token !== r.token) continue\n const result = await this.send(r.token, r.environment, {\n kind: 'liveactivity', event: r.endedState ? 'end' : 'update',\n timestamp: Math.floor(Date.now() / 1000), contentState: state,\n }).catch((): ApnsSendResult => ({ outcome: 'failed' }))\n if (result.outcome === 'sent') {\n this.sent.set(key, fingerprint)\n // Retain the terminal tombstone until expiry/unregister: reconnect and token rotation cannot restart this round.\n } else if (result.outcome === 'invalid-token') {\n devices.clearLiveActivity(device.deviceId, r.activityId, r.token)\n } else {\n this.scheduler.changed()\n }\n }\n for (const key of this.sent.keys()) if (!valid.has(key)) this.sent.delete(key)\n }\n dispose(): void { this.scheduler.dispose() }\n}\n","import type { PushNotification } from './protocol.ts'\nimport { pushScopeFor } from './connection-policy.ts'\n\n/** Prune only when the provider supplies an authoritative token-lifecycle verdict. */\nexport function shouldPrunePushToken(\n outcome: 'sent' | 'invalid-token' | 'failed',\n reason?: string,\n): boolean {\n return outcome === 'invalid-token' && (reason === 'Unregistered' || reason === 'ExpiredToken')\n}\n\n/** APNs requires both registration and the same content permission as WS. */\nexport function mayReceivePush(device: { scopes?: readonly string[] }, notification: unknown): boolean {\n const scope = pushScopeFor('s2c.notify', notification)\n return scope !== undefined && device.scopes?.includes('notifications.register') === true &&\n device.scopes.includes(scope)\n}\n\n/**\n * Zero-touch relay self-heal: HTTP 401 means the relay no longer honors the\n * cached credential. Only auto-enrolled cells with a still-current token may\n * re-derive it; an explicitly configured relay token remains user-owned\n * configuration and is never silently rewritten.\n */\nexport function shouldReEnrollRelayToken(\n transport: 'apns' | 'relay',\n outcome: 'sent' | 'invalid-token' | 'failed',\n reason: string | undefined,\n opts: { usedCellToken: boolean; hasEnrollKey: boolean; tokenStillCurrent: boolean },\n): boolean {\n return (\n transport === 'relay' &&\n outcome === 'failed' &&\n reason === 'HTTP 401' &&\n opts.hasEnrollKey &&\n opts.usedCellToken &&\n opts.tokenStillCurrent\n )\n}\n\n/** Apply before either transport receives the payload, including the Relay. */\nexport function pushContent(notification: PushNotification, mode?: 'preview' | 'generic'): PushNotification {\n if (mode !== 'generic') return notification\n // generic 模式下工具名与详情同样不得出站。\n const { toolName: _toolName, detail: _detail, ...rest } = notification\n notification = rest\n const bodies: Record<PushNotification['category'], string> = {\n 'turn.completed': 'A task has completed.',\n 'approval.required': 'An approval needs your attention.',\n 'question.asked': 'A question needs your answer.',\n 'session.error': 'A task needs your attention.',\n }\n return { ...notification, title: 'DeepPilot', body: bodies[notification.category] ?? 'Open DeepPilot for an update.' }\n}\n","/**\n * Relay connectivity self-test used by the settings page \"测试中继\" button.\n *\n * Two steps mirror what zero-touch enrollment actually does:\n * 1. health — GET {url}/healthz → is the relay reachable?\n * 2. enroll — POST {url}/v1/enroll → does the shared key grant a token?\n *\n * The enroll step doubles as a repair path: a token it issues is handed back\n * via `onEnrolled` so the bridge caches it and flips push readiness without\n * waiting for the next app registration.\n *\n * Transport is injectable (`fetchImpl`) so tests never touch the network.\n */\n\nimport { normalizeRelayBaseUrl } from './relay-url.ts'\n\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n /** Human-readable, user-facing (zh) outcome; safe to render verbatim. */\n message: string\n latencyMs?: number\n}\n\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n /** True when the probe produced/cached a usable relay token. */\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\nexport interface RelayProbeOptions {\n url: string\n /** Stable per-bridge identity for enrollment; generated upstream if absent. */\n clientId?: string\n /** Distributor's shared key; absent → enroll step reports why it cannot run. */\n enrollKey?: string\n /** True when the user configured relayToken manually (enroll unnecessary). */\n manualToken?: boolean\n timeoutMs?: number\n fetchImpl?: typeof fetch\n /** Called with an issued token so the caller can persist it. */\n onEnrolled?: (token: string) => void\n}\n\nconst DEFAULT_TIMEOUT_MS = 6_000\n\nasync function requestJson(\n fetchImpl: typeof fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<{ status: number; body: unknown }> {\n const response = await fetchImpl(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs),\n })\n let body: unknown = null\n try {\n body = await response.json()\n } catch {\n // non-JSON body keeps body null; status still drives the verdict\n }\n return { status: response.status, body }\n}\n\nexport async function runRelayProbe(options: RelayProbeOptions): Promise<RelayTestResult> {\n const base = normalizeRelayBaseUrl(options.url)\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const fetchImpl = options.fetchImpl ?? fetch\n const steps: RelayTestStep[] = []\n let tokenIssued = false\n\n // ---- step 1: reachability ----\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/healthz`,\n { method: 'GET' },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n if (status === 200 && (body as { ok?: unknown })?.ok === true) {\n steps.push({ id: 'health', ok: true, message: '中继服务可达', latencyMs })\n } else {\n steps.push({ id: 'health', ok: false, message: `中继响应异常(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'health', ok: false, message: '无法连接中继:' + reason })\n }\n\n // ---- step 2: enrollment ----\n if ((options.manualToken ?? false)) {\n steps.push({ id: 'enroll', ok: true, message: '已手动配置 relayToken,跳过注册验证' })\n } else if (!options.enrollKey) {\n steps.push({ id: 'enroll', ok: false, message: '尚无注册密钥:等待分发版 App 首次注册后才能验证注册' })\n } else {\n const clientId = options.clientId ?? 'u_' + Math.random().toString(36).slice(2)\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/v1/enroll`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey: options.enrollKey }),\n },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n const token = (body as { token?: unknown })?.token\n if (status === 200 && typeof token === 'string' && token.startsWith('rl_')) {\n tokenIssued = true\n options.onEnrolled?.(token)\n steps.push({ id: 'enroll', ok: true, message: '注册成功,已取得推送凭证', latencyMs })\n } else if (status === 403) {\n steps.push({ id: 'enroll', ok: false, message: '注册被拒:注册密钥不匹配(检查 App 内 DSPushEnrollKey 与服务器 RELAY_ENROLL_KEY)', latencyMs })\n } else if (status === 429) {\n steps.push({ id: 'enroll', ok: false, message: '尝试过于频繁,稍后再试', latencyMs })\n } else {\n steps.push({ id: 'enroll', ok: false, message: `注册失败(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'enroll', ok: false, message: '注册请求失败:' + reason })\n }\n }\n\n const overall: RelayTestResult['overall'] =\n steps.length > 0 && steps.some((step) => step.id === 'health' && step.ok) &&\n steps.every((step) => step.ok)\n ? 'ok'\n : 'failed'\n return { url: base, overall, tokenIssued, steps }\n}\n","/**\n * 推送网关(PushGateway)——离线推送的单一属主。\n *\n * 迁移前,这约 450 行住在 `apply()` 的闭包里:enrollment cell 与持久化、\n * `handlePushEnrollKey`、`resolvePushConfig`、`ensureRelayEnrolled`、\n * `senderFor` + cachedSender + 失败退避、两个 scheduler、`makePushOutlet`、\n * 两个自测。它今天的「接口」其实是「被 WidgetPushScheduler、\n * LiveActivityPushManager、PushOutlet 和两个 report 回调各自捕获的那几段代码」\n * ——没有任何一处能回答「推送为什么没到」。\n *\n * 现在:一个 module,实现 `PushOutlet`(HostBridge 消费的那四个方法)并额外\n * 暴露自测与零配置注册入口。enrollment、持久化、sender 缓存、退避、401 自愈\n * 全部入内;`apply()` 只做依赖注入。\n *\n * 两处本次收敛的重复:\n * - effective provider 推导(原先在 resolvePushConfig 与 relay 自测里各一份);\n * - 401 自愈三元组(原先在 widget / liveActivity / fanOut 三处各一份,\n * 见 healRelayCredential)。\n */\n\nimport { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport {\n DEFAULT_RELAY_URL,\n type Config,\n} from './config.ts'\nimport { ApnsClient } from './apns.ts'\nimport { RelayClient } from './relay-client.ts'\nimport { WidgetPushScheduler } from './widget-push.ts'\nimport { LiveActivityPushManager } from './live-activity.ts'\nimport {\n mayReceivePush,\n pushContent,\n shouldPrunePushToken,\n shouldReEnrollRelayToken,\n} from './push-policy.ts'\nimport { deviceDisplayName, expandHome, type DeviceStore } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushDelivery, PushNotification, SessionSummary } from './protocol.ts'\nimport type { PushOutlet } from './host-api.ts'\nimport type { PushTestResult, RelayTestResult } from './report-wire.ts'\nimport { runRelayProbe } from './relay-test.ts'\n\n/** 零配置注册的持久化单元(deeppilot/push-relay.json)。 */\ninterface RelayEnrollmentCell {\n clientId?: string\n autoRelay?: boolean\n enrollKey?: string\n token?: string\n}\n\ntype PushConfigSnapshot =\n | { kind: 'apns'; teamId: string; keyId: string; keyPath: string; bundleId: string }\n | { kind: 'relay'; url: string; token: string }\n\ninterface SendOutcome { outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }\ntype PushSender = (\n request: { deviceToken: string; environment: ApnsEnvironment; notification: PushDelivery },\n) => Promise<SendOutcome>\n\ninterface CachedSender {\n fingerprint: string\n send: PushSender\n dispose?: () => Promise<void>\n}\n\n/** 274 秒(约 4.5 分钟)内的失败只记一次;其间同一指纹不再重试。 */\nconst SENDER_FAILURE_RETRY_MS = 60_000\n/** 中继注册失败的节流:一次/分钟,避免中继抖动把每次通知变成一次外呼。 */\nconst ENROLL_RETRY_MS = 60_000\n\n/** 网关需要的一切都从外部注入;它自己不创建配置、设备表或连接集合。 */\nexport interface PushGatewayDeps {\n config: () => Config\n /** 推送数据目录(push-relay.json 与 APNs 密钥默认路径的宿主)。 */\n dataDir: string\n devices: () => DeviceStore | null\n audience: () => string | null\n /** 当前活跃连接:用于跳过已有 WebSocket 的设备。 */\n connections: () => Iterable<{\n connectedDeviceId: string | undefined\n suppressesAlertPush: boolean\n }>\n enabledNow: () => boolean\n log: (message: string) => void\n /** 中继探测的传输实现:默认真实 fetch,测试注入假件以免碰网络。 */\n fetchImpl?: typeof fetch\n}\n\nexport class PushGateway implements PushOutlet {\n private readonly pushRelayPath: string\n private readonly enrollmentCell: RelayEnrollmentCell = {}\n private enrollmentWriteTail: Promise<void> = Promise.resolve()\n private enrollAttemptFor: string | undefined\n private enrollLastAttemptAt = 0\n private cachedSender: CachedSender | undefined\n private senderFailedFor: { fingerprint: string; at: number } | undefined\n\n private readonly widgetPush: WidgetPushScheduler\n private readonly liveActivityPush: LiveActivityPushManager\n\n constructor(private readonly deps: PushGatewayDeps) {\n this.pushRelayPath = join(deps.dataDir, 'push-relay.json')\n this.widgetPush = new WidgetPushScheduler(async () => { await this.flushWidgetPushes() })\n this.liveActivityPush = new LiveActivityPushManager(\n () => deps.devices() ?? undefined,\n async (deviceToken, environment, notification) => this.sendLiveActivity(deviceToken, environment, notification),\n )\n }\n\n // ---------- 生命周期 ----------\n\n /** 首启恢复零配置注册状态(best effort)。 */\n async restore(): Promise<void> {\n try {\n const raw = JSON.parse(await readFile(this.pushRelayPath, 'utf8')) as RelayEnrollmentCell\n if (typeof raw.clientId === 'string') this.enrollmentCell.clientId = raw.clientId\n if (typeof raw.enrollKey === 'string') this.enrollmentCell.enrollKey = raw.enrollKey\n if (typeof raw.token === 'string') this.enrollmentCell.token = raw.token\n if (raw.autoRelay === true) this.enrollmentCell.autoRelay = true\n } catch {\n // first boot: no enrollment yet\n }\n }\n\n /** 写盘串行化:并发注册不会交错写坏同一个文件。 */\n private persistEnrollment(): void {\n const snapshot = JSON.stringify({ version: 1, ...this.enrollmentCell }, null, 2) + '\\n'\n this.enrollmentWriteTail = this.enrollmentWriteTail.then(async () => {\n const tempPath = this.pushRelayPath + '.' + randomBytes(6).toString('hex') + '.tmp'\n try {\n await mkdir(this.deps.dataDir, { recursive: true })\n await writeFile(tempPath, snapshot, { mode: 0o600 })\n await rename(tempPath, this.pushRelayPath)\n } catch {\n await unlink(tempPath).catch(() => {})\n // best-effort persistence; enrollment retries on next trigger\n }\n })\n }\n\n /** 等注册状态的写盘尾部排空;测试与收尾都用它取得确定性。 */\n async persisted(): Promise<void> {\n await this.enrollmentWriteTail\n }\n\n /** 进程收尾:等写盘尾部落盘,释放 sender。 */\n async dispose(): Promise<void> {\n this.widgetPush.dispose()\n this.liveActivityPush.dispose()\n const sender = this.cachedSender\n this.cachedSender = undefined\n await Promise.allSettled([\n this.enrollmentWriteTail,\n sender?.dispose?.() ?? Promise.resolve(),\n ])\n }\n\n // ---------- PushOutlet(HostBridge 消费) ----------\n\n widgetChanged(): void {\n this.widgetPush.changed()\n }\n\n liveActivityChanged(sessions: SessionSummary[]): void {\n this.liveActivityPush.changed(sessions)\n }\n\n /** 能力位必须说真话:只在 provider 完全就绪时才广告 push。 */\n isAvailable(): boolean {\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) return false\n // 中继模式要等注册产出 token 才算就绪;提前广告会让客户端压掉本地通知,\n // 而那条通知永远不会到。\n if (resolved.value.kind === 'relay' && !resolved.value.token) return false\n return true\n }\n\n /**\n * 把一个值得通知的事件扇到持有 APNs token 的已配对设备。规则:\n * - 有活跃 WebSocket 的设备跳过(它们已收到 WS 帧,会自己弹本地通知);\n * - 只考虑被授予 `notifications.register` 的设备——scope 被收窄的设备不得\n * 再收到离线推送(R1/P2 S→C 权限策略);\n * - 每设备按它自己注册的环境投递,沙盒与生产设备可共存;\n * - 设备按类别的静音开关抑制对应类别;\n * - 只有 APNs 的终态 Unregistered/ExpiredToken 才清理存储;\n * BadDeviceToken 可能是环境不匹配,保留可诊断性。\n */\n fanOut(sourceNotification: PushNotification): void {\n const notification = pushContent(\n { ...sourceNotification, hostAudience: this.deps.audience() ?? undefined },\n this.deps.config().push?.contentMode,\n )\n void (async () => {\n let resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok && resolved.reason === 'relay token not enrolled yet') {\n // 注册时带着 enrollKey 但注册还没跑完——立刻试一次再重新解析。\n await this.ensureRelayEnrolled(this.relayUrl())\n resolved = this.resolvePushConfig(this.deps.config())\n }\n if (!resolved.ok) return\n const devices = this.deps.devices()\n if (!devices) return\n const send = await this.senderFor(resolved.value)\n if (!send) return\n const transport = resolved.value.kind\n const connectedIds = new Set<string>()\n for (const connection of this.deps.connections()) {\n const id = connection.connectedDeviceId\n if (id && connection.suppressesAlertPush) connectedIds.add(id)\n }\n // 可观测性优先:推送失败曾经完全静默(受 debug 门控),现场无法诊断。\n // 现在每次派送与每次跳过都留一行平铺日志——只有类别与结果,绝不带消息体。\n const candidates = devices.list().filter((device) => {\n const registration = device.apns\n if (!registration) return false\n if (connectedIds.has(device.deviceId)) return false\n if (!mayReceivePush(device, notification)) {\n if (this.deps.config().diagnostics?.debug === true) {\n this.deps.log(`push skip \"${deviceDisplayName(device)}\": notification permission not granted`)\n }\n return false\n }\n if (registration.categories?.[notification.category] === false) {\n if (this.deps.config().diagnostics?.debug === true) {\n this.deps.log(`push skip \"${deviceDisplayName(device)}\": category ${notification.category} muted`)\n }\n return false\n }\n return true\n })\n if (candidates.length === 0) {\n const tokenized = devices.list().filter((device) => device.apns !== undefined).length\n this.deps.log(`push(${transport}) ${notification.category}: no offline targets (connected=${connectedIds.size}, tokenized=${tokenized})`)\n return\n }\n // 自愈输入每次派送解析一次:实际发送所用的 URL,以及凭据是否来自零配置\n // 单元(显式配置的 relayToken 永不被改写)。\n const relayUrl = resolved.value.kind === 'relay' ? resolved.value.url : undefined\n const relayTokenUsed = resolved.value.kind === 'relay' ? resolved.value.token : undefined\n const usedCellToken = relayTokenUsed !== undefined && relayTokenUsed === this.enrollmentCell.token\n const hasEnrollKey = Boolean(this.enrollmentCell.enrollKey)\n for (const device of candidates) {\n const registration = device.apns!\n void send({ deviceToken: registration.token, environment: registration.environment, notification })\n .then(({ outcome, reason }) => {\n this.deps.log(`push(${transport}) ${notification.category} → \"${deviceDisplayName(device)}\" [${registration.environment}] = ${outcome}${reason ? ' (' + reason + ')' : ''}`)\n if (shouldPrunePushToken(outcome, reason)) {\n devices.clearPushToken(device.deviceId)\n this.deps.log(`push: pruned stale token of \"${deviceDisplayName(device)}\" (${reason ?? 'unknown'}) — app re-registers on next launch`)\n return\n }\n if (\n relayUrl !== undefined &&\n shouldReEnrollRelayToken(transport, outcome, reason, {\n usedCellToken,\n hasEnrollKey,\n // Compare-and-clear:另一请求携带旧凭据的迟到 401 不得擦掉\n // 已被更早回调刷新过的 token。\n tokenStillCurrent: this.enrollmentCell.token === relayTokenUsed,\n })\n ) {\n // 中继不再认这个凭据。丢掉并从 enroll key 重新派生;\n // ensureRelayEnrolled 自己的节流避免并行 401 打爆端点,\n // senderFor 的配置指纹会在下次派送时用新 token 重建客户端。\n void this.healRelayCredential(relayUrl, relayTokenUsed, 'push relay credential rejected (HTTP 401); re-enrolling')\n }\n })\n .catch(() => {})\n }\n })()\n }\n\n // ---------- 零配置注册与自测(report remote 与 wire 帧消费) ----------\n\n /** 分布式 App 在 c2s.push.register 里呈上分发方的共享钥匙时触发。 */\n async enrollKey(enrollKey: string): Promise<void> {\n if (this.enrollmentCell.enrollKey !== enrollKey) {\n this.enrollmentCell.enrollKey = enrollKey\n }\n const configuredProvider = this.deps.config().push?.provider\n // 只在用户没有显式选择时才翻转。\n if (!configuredProvider || configuredProvider === 'none') {\n if (!this.enrollmentCell.autoRelay) {\n this.enrollmentCell.autoRelay = true\n this.deps.log('push relay mode auto-enabled by enrolled app')\n }\n }\n this.persistEnrollment()\n // 内联完成注册,让 register handler 看到最终就绪状态:第一条离线通知\n // 不应依赖一次重连。\n await this.ensureRelayEnrolled(this.relayUrl())\n }\n\n /** 设置页推送自测:强制一条合成通知走完整链路到每个已注册设备。 */\n async selfTest(): Promise<PushTestResult> {\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) {\n return {\n transport: 'none',\n overall: 'not-configured',\n message: '推送未启用(' + resolved.reason + ')。可先用「测试访问与注册」完成中继注册,或在配置中设置 push.provider',\n results: [],\n }\n }\n const devices = this.deps.devices()\n const tokenized = (devices?.list() ?? []).filter((device) => device.apns !== undefined)\n if (!devices || tokenized.length === 0) {\n return {\n transport: resolved.value.kind,\n overall: 'no-targets',\n message: '还没有设备注册离线推送——在手机上打开 DeepPilot 并允许系统通知,等状态变为「已就绪」后再试',\n results: [],\n }\n }\n const send = await this.senderFor(resolved.value)\n if (!send) {\n return { transport: resolved.value.kind, overall: 'failed', message: '发送通道不可用(检查 .p8 密钥文件或中继配置)', results: [] }\n }\n const notification: PushNotification = {\n notificationId: 'test-' + Date.now(),\n category: 'turn.completed',\n sessionId: 'push-test',\n title: 'DeepPilot 测试推送',\n body: '收到这条通知说明离线推送链路正常',\n }\n const results = await Promise.all(tokenized.map(async (device) => {\n const registration = device.apns!\n const { outcome, reason } = await send({\n deviceToken: registration.token,\n environment: registration.environment,\n notification,\n })\n return {\n name: deviceDisplayName(device),\n environment: registration.environment,\n outcome,\n // 前 10 位十六进制让运维能核对存储的 token 与设备当前持有的一致\n // (重装会轮换 token)。\n tokenFingerprint: registration.token.slice(0, 10),\n ...(reason !== undefined ? { reason } : {}),\n }\n }))\n const overall: PushTestResult['overall'] = results.some((r) => r.outcome === 'sent') ? 'sent' : 'failed'\n this.deps.log('push self-test: ' + overall + ' (' + results.map((r) => `\"${r.name}\"=${r.outcome}${r.reason ? '/' + r.reason : ''}`).join(', ') + ')')\n return { transport: resolved.value.kind, overall, results }\n }\n\n /** 设置页中继自测:健康检查 + 注册往返,成功即完成一次注册。 */\n async relayTest(): Promise<RelayTestResult> {\n const push = this.deps.config().push ?? {}\n const configured = push.provider ?? 'none'\n const effective = configured === 'none' && this.enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effective !== 'relay') {\n return {\n url: '',\n overall: 'failed',\n tokenIssued: false,\n steps: [{ id: 'health', ok: false, message: `当前推送模式不是中继(provider=${configured})。启用方式二选一:① 零配置——在 ios/project.yml 填写 DSPushEnrollKey(与服务器 RELAY_ENROLL_KEY 一致)并重新安装 App,打开 App 即自动启用;② 手动——将 push.provider 设为 relay 并填入 relayToken` }],\n }\n }\n const url = this.relayUrl()\n if (!/^https:\\/\\//i.test(url)) {\n return {\n url,\n overall: 'failed',\n tokenIssued: false,\n steps: [{ id: 'health', ok: false, message: 'relayUrl 必须是 https 地址:注册请求携带共享密钥,明文 HTTP 会把它暴露给链路上的任何节点' }],\n }\n }\n // 注册步骤需要一个身份:现在铸造,于是一次成功的自测同时就是一次完成的注册。\n if (!this.enrollmentCell.clientId && this.enrollmentCell.enrollKey) {\n this.enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n this.persistEnrollment()\n }\n return await runRelayProbe({\n url,\n clientId: this.enrollmentCell.clientId,\n enrollKey: this.enrollmentCell.enrollKey,\n manualToken: Boolean((push.relayToken ?? '').trim()),\n ...(this.deps.fetchImpl !== undefined ? { fetchImpl: this.deps.fetchImpl } : {}),\n onEnrolled: (token) => {\n this.enrollmentCell.token = token\n this.persistEnrollment()\n this.deps.log('push relay enrollment succeeded (via settings self-test)')\n },\n })\n }\n\n // ---------- 内部:配置解析 / 注册 / sender ----------\n\n private relayUrl(): string {\n return (this.deps.config().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n }\n\n private resolvePushConfig(config: Config):\n | { ok: true; value: PushConfigSnapshot }\n | { ok: false; reason: string } {\n const push = config.push ?? {}\n // 已注册的分布式 App 在用户没有显式选择时自动启用中继模式。\n const configured = push.provider ?? 'none'\n const effectiveProvider = configured === 'none' && this.enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effectiveProvider === 'relay') {\n const url = this.relayUrl()\n const token = (push.relayToken ?? '').trim() || this.enrollmentCell.token || ''\n if (!/^https:\\/\\//i.test(url)) return { ok: false, reason: 'relayUrl must be an https URL' }\n if (!token) return { ok: false, reason: 'relay token not enrolled yet' }\n return { ok: true, value: { kind: 'relay', url, token } }\n }\n if (effectiveProvider === 'apns') {\n const teamId = (push.teamId ?? '').trim()\n const keyId = (push.keyId ?? '').trim()\n const keyPath = expandHome((push.keyPath ?? '').trim() || join(this.deps.dataDir, 'apns', 'AuthKey.p8'))\n const bundleId = (push.bundleId ?? '').trim()\n if (!teamId || !keyId || !bundleId) return { ok: false, reason: 'teamId/keyId/bundleId missing' }\n return { ok: true, value: { kind: 'apns', teamId, keyId, keyPath, bundleId } }\n }\n return { ok: false, reason: 'provider disabled' }\n }\n\n /**\n * 对运营方中继做零配置注册。幂等且结果缓存在持久化单元里;一次失败只留\n * 一行日志,直到配置指纹变化。\n */\n private async ensureRelayEnrolled(url: string): Promise<string | undefined> {\n // 注册体携带分发方的共享钥匙;配置错的 http:// relayUrl 绝不能让它明文\n // 泄露。(发送路径的请求已由 resolvePushConfig 把门,注册调用点没有。)\n if (!/^https:\\/\\//i.test(url.trim())) {\n this.deps.log('push relay enrollment refused: relayUrl must be an https URL')\n return undefined\n }\n if (this.enrollmentCell.token) return this.enrollmentCell.token\n const fingerprint = url + ':' + String(this.enrollmentCell.enrollKey ?? '')\n if (fingerprint !== this.enrollAttemptFor) {\n this.enrollAttemptFor = fingerprint\n this.enrollLastAttemptAt = 0\n }\n // 同一指纹反复失败:节流到一分钟一次,宕机的中继不会把每次通知变成一次\n // 外呼风暴,同时瞬时故障仍能快速恢复。\n if (Date.now() - this.enrollLastAttemptAt < ENROLL_RETRY_MS) return undefined\n this.enrollLastAttemptAt = Date.now()\n try {\n if (!this.enrollmentCell.clientId) {\n this.enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n this.persistEnrollment()\n }\n const client = new RelayClient({ url, debug: this.deps.config().diagnostics?.debug === true, log: this.deps.log })\n const token = await client.enroll(this.enrollmentCell.clientId, this.enrollmentCell.enrollKey ?? '')\n if (!token) {\n this.deps.log('push relay enrollment failed (' + url + '); will retry on next trigger')\n return undefined\n }\n this.enrollmentCell.token = token\n this.persistEnrollment()\n this.deps.log('push relay enrollment succeeded')\n return token\n } catch (error) {\n this.deps.log('push relay enrollment error: ' + String(error))\n return undefined\n }\n }\n\n /**\n * 按当前配置惰性构建 sender。坏配置(读不到的 .p8)只让该指纹失效并留\n * 一行日志,而不是每个事件都失败。\n */\n private async senderFor(resolved: PushConfigSnapshot): Promise<PushSender | undefined> {\n const fingerprint = JSON.stringify(resolved)\n if (this.cachedSender?.fingerprint === fingerprint) return this.cachedSender.send\n // 最近的失败只在短窗口内阻止重试:长期坏配置不能把每个事件都变成日志风暴,\n // 但同一配置在密钥文件补放之后 MUST 再获得一次机会。\n if (\n this.senderFailedFor?.fingerprint === fingerprint &&\n Date.now() - this.senderFailedFor.at < SENDER_FAILURE_RETRY_MS\n ) {\n return undefined\n }\n if (this.cachedSender) {\n await this.cachedSender.dispose?.().catch(() => {})\n this.cachedSender = undefined\n }\n if (resolved.kind === 'relay') {\n // resolvePushConfig 已保证单元里存在 token(配置 token 或已完成的注册)。\n const client = new RelayClient({\n url: resolved.url,\n token: resolved.token,\n debug: this.deps.config().diagnostics?.debug === true,\n log: this.deps.log,\n })\n this.cachedSender = {\n fingerprint,\n send: (request) => client.send(request),\n }\n this.deps.log('push relay enabled')\n } else {\n try {\n await readFile(expandHome(resolved.keyPath), 'utf8')\n } catch (error) {\n this.senderFailedFor = { fingerprint, at: Date.now() }\n this.deps.log('apns push unavailable (key unreadable at ' + resolved.keyPath + '): ' + String(error))\n return undefined\n }\n const client = new ApnsClient({\n teamId: resolved.teamId,\n keyId: resolved.keyId,\n keyPath: resolved.keyPath,\n bundleId: resolved.bundleId,\n debug: this.deps.config().diagnostics?.debug === true,\n log: this.deps.log,\n })\n this.cachedSender = {\n fingerprint,\n send: (request) => client.send({\n ...request.notification,\n deviceToken: request.deviceToken,\n environment: request.environment,\n }),\n dispose: () => client.dispose(),\n }\n this.deps.log('apns push enabled')\n }\n this.senderFailedFor = undefined\n return this.cachedSender.send\n }\n\n /**\n * 小组件总览推送:令牌新鲜、三项 scope 齐全、7 天内的设备,按环境去重后\n * 各投一次;中继拒绝凭据时自愈。\n */\n private async flushWidgetPushes(): Promise<void> {\n if (!this.deps.enabledNow()) return\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) return\n const devices = this.deps.devices()\n const send = await this.senderFor(resolved.value)\n if (!devices || !send) return\n const sent = new Set<string>()\n for (const device of devices.list()) {\n const registration = device.widgetApns\n if (!registration || device.revokedAt !== undefined ||\n !(['notifications.register', 'sessions.read', 'interactions.respond'] as const).every((scope) =>\n device.scopes?.includes(scope)) ||\n Date.now() - registration.updatedAt > 7 * 24 * 60 * 60 * 1000) continue\n const key = registration.environment + ':' + registration.token\n if (sent.has(key)) continue\n sent.add(key)\n const { outcome, reason } = await send({\n deviceToken: registration.token, environment: registration.environment,\n notification: { kind: 'widget' },\n })\n if (shouldPrunePushToken(outcome, reason)) {\n devices.clearWidgetPushToken(device.deviceId, registration.token)\n }\n if (resolved.value.kind === 'relay' && reason === 'HTTP 401') {\n await this.healRelayCredential(resolved.value.url, resolved.value.token)\n }\n }\n }\n\n /** Live Activity 推送:与 alert 推送共用 sender 与自愈。 */\n private async sendLiveActivity(\n deviceToken: string,\n environment: ApnsEnvironment,\n notification: PushDelivery,\n ): Promise<SendOutcome> {\n if (!this.deps.enabledNow()) return { outcome: 'failed' }\n const resolved = this.resolvePushConfig(this.deps.config())\n if (!resolved.ok) return { outcome: 'failed' }\n const send = await this.senderFor(resolved.value)\n if (!send) return { outcome: 'failed' }\n const result = await send({ deviceToken, environment, notification })\n if (resolved.value.kind === 'relay' && result.reason === 'HTTP 401') {\n await this.healRelayCredential(resolved.value.url, resolved.value.token)\n }\n return result\n }\n\n /**\n * 401 自愈(原先是三份拷贝:widget / liveActivity / fanOut)。\n * 中继不再认这个凭据:丢掉它,从 enroll key 重新派生。节流由\n * ensureRelayEnrolled 自己负责,并行 401 不会打爆端点。\n */\n private async healRelayCredential(\n url: string,\n token: string | undefined,\n logLine?: string,\n ): Promise<void> {\n if (token === undefined || this.enrollmentCell.token !== token) return\n if (!this.enrollmentCell.enrollKey) return\n this.enrollmentCell.token = undefined\n this.persistEnrollment()\n if (logLine !== undefined) this.deps.log(logLine)\n await this.ensureRelayEnrolled(url)\n }\n}\n","import { createServer as createHttpServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http'\nimport { createServer as createHttpsServer } from 'node:https'\nimport type { Duplex } from 'node:stream'\n\nexport interface PhoneServerHandlers {\n health(req: IncomingMessage, res: ServerResponse): void | Promise<void>\n pair(req: IncomingMessage, res: ServerResponse): void | Promise<void>\n upgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void\n}\n\nexport interface PhoneServerTls {\n key: string\n cert: string\n}\n\n/** 传输层错误:TLS 握手失败,或 HTTP 报文解析失败。 */\nexport interface PhoneServerTransportError {\n kind: 'tls-client-error' | 'client-error'\n /** 原始错误对象;调用方负责脱敏,不直接落日志。 */\n error: unknown\n /** 对端地址(原始值);调用方必须哈希后再记录。 */\n remoteAddress: string | undefined\n}\n\nexport interface PhoneServerOptions {\n /**\n * 传输层错误出口。缺省时不挂监听,保持 Node 默认行为。\n * 这两类错误此前完全静默:手机证书不对时浏览器侧只看到 «连接已重置»,\n * 电脑端没有任何一行日志,无法区分「证书钉住失败」与「端口没开」。\n */\n onTransportError?: (event: PhoneServerTransportError) => void\n}\n\ninterface ErrorSocket {\n remoteAddress?: string | undefined\n destroy(): void\n}\n\n/**\n * A deliberately narrow transport listener. Both the LAN endpoint and the\n * loopback-only Funnel origin use this factory, so neither can accidentally\n * inherit DSH's wider web/API route surface.\n *\n * With `tls` the listener speaks HTTPS/WSS only (the LAN posture); without\n * it the listener is plain HTTP, which is reserved for the loopback Funnel\n * origin where tailscaled terminates TLS.\n */\nexport function createPhoneServer(\n handlers: PhoneServerHandlers,\n tls?: PhoneServerTls,\n options?: PhoneServerOptions,\n): Server {\n const onRequest = (req: IncomingMessage, res: ServerResponse): void => {\n const path = requestPath(req)\n if (path === '/phone/health') {\n void handlers.health(req, res)\n } else if (path === '/phone/pair') {\n void handlers.pair(req, res)\n } else {\n res.statusCode = 404\n res.end('not found')\n }\n }\n const server: Server = tls === undefined\n ? createHttpServer(onRequest)\n : createHttpsServer({ key: tls.key, cert: tls.cert, minVersion: 'TLSv1.2' }, onRequest)\n server.on('upgrade', (req, socket, head) => {\n if (requestPath(req) !== '/phone') {\n socket.end('HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n')\n return\n }\n handlers.upgrade(req, socket, head)\n })\n const onTransportError = options?.onTransportError\n if (onTransportError !== undefined) {\n // TLS 握手失败(证书钉住不符、客户端版本过旧……)。\n if (tls !== undefined) {\n const tlsServer = server as unknown as {\n on(event: 'tlsClientError', listener: (error: Error, socket: ErrorSocket) => void): unknown\n }\n tlsServer.on('tlsClientError', (error, socket) => {\n onTransportError({ kind: 'tls-client-error', error, remoteAddress: socket.remoteAddress })\n socket.destroy()\n })\n }\n // HTTP 报文层错误(畸形请求行 / 头)。挂上监听即接管 Node 的默认\n // 400 响应,因此必须自己收尾;直接销毁,不向未认证对端多写一个字节。\n server.on('clientError', (error: Error, socket: ErrorSocket) => {\n onTransportError({ kind: 'client-error', error, remoteAddress: socket.remoteAddress })\n socket.destroy()\n })\n }\n return server\n}\n\nfunction requestPath(req: IncomingMessage): string {\n try {\n return new URL(req.url ?? '/', 'http://phone.local').pathname\n } catch {\n return '/'\n }\n}\n\nexport function listen(server: Server, port: number, host: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const onError = (error: Error): void => {\n server.off('listening', onListening)\n reject(error)\n }\n const onListening = (): void => {\n server.off('error', onError)\n resolve()\n }\n server.once('error', onError)\n server.once('listening', onListening)\n server.listen(port, host)\n })\n}\n\nexport function closeServer(server: Server | undefined): Promise<void> {\n if (server === undefined || !server.listening) return Promise.resolve()\n return new Promise((resolve) => server.close(() => resolve()))\n}\n","/**\n * 传输协调器(TransportReconciler)——独立传输接入的公共协议,只写一次。\n *\n * 迁移前 LAN 与 Funnel 各有一份 40 行的 reconcile 闭包,做同一件事:算目标值 →\n * 与 appliedKey 比对 → 拆除前一个 → 起新的 → 更新状态,外加一条串行 tail 防\n * 并发。两份拷贝之间还飘着三处**刻意保留的差异**(见 TransportSpec)——本轮\n * 的目标是给 `apply()` 造出接口,不是统一传输的并发语义,因此差异原样保留并\n * 在此声明,而不是悄悄抹平。\n *\n * 每个传输一行声明(见 index.ts 的 createLocalTransport / createRemoteTransport),\n * 协调器只拥有差分、串行、拆除与状态发布。\n */\n\nimport type { Config } from './config.ts'\nimport type { DeepPilotReport } from './report-wire.ts'\n\n/** 状态的最小面:phase 必填,其余字段由各传输自己定义。 */\nexport interface TransportStatus {\n phase: string\n updatedAt: number\n}\n\n/** 目标值:参与差分的稳定对象。 */\nexport type TransportTarget = Record<string, unknown>\n\nexport interface TransportSpec<TInstance, TStatus extends TransportStatus> {\n /** 从当前配置算出的目标值;其 JSON 即差分键。 */\n keyOf(config: Config): TransportTarget\n /**\n * `true`:目标未启用时提前返回并直接置 disabled 状态(LAN 式)。\n * `false`:仍构造实例,由实例内部的 no-op 路径把状态置为 disabled(Funnel 式)。\n */\n skipWhenDisabled: boolean\n /**\n * appliedKey 的写入时机。LAN 在拆除旧 listener 之前写,Funnel 在 supervisor\n * 构造之后、异步 start 之前写——两者在「reconcile 等待期间又来一次\n * volatile-update」时行为不同。差异保留,见文件头说明。\n */\n applyKey: 'before-teardown' | 'after-construct'\n /** 构造实例:同步、即刻取得权威性(Funnel 的 appliedKey 在此之后写入)。 */\n construct(target: TransportTarget): TInstance\n /**\n * 启动实例:异步部分(TLS 加载、监听、拉起 helper)。可返回一个新实例来\n * 替换——LAN 的 listener 要等 TLS 身份就绪后才存在。抛错即走错误分支。\n */\n begin(instance: TInstance, guard: BeginGuard<TInstance>): Promise<TInstance | void>\n /** 拆掉一个实例。 */\n /** 拆除实例;`undefined` 表示没有可拆的(首轮或已拆)。 */\n stop(instance: TInstance | undefined): Promise<void>\n /** 目标未启用且 skipWhenDisabled 时的状态。 */\n disabledStatus(target: TransportTarget): TStatus\n /** 启动过程中的状态。 */\n startingStatus(target: TransportTarget): TStatus\n /** 启动失败时的状态;缺省表示该行没有错误分支,错误原样上抛。 */\n errorStatus?(error: unknown, target: TransportTarget): TStatus\n /** 状态查询:实例在场时问它;返回 undefined 表示「仍是我刚设的过渡态」。 */\n statusOf(instance: TInstance | undefined): TStatus | undefined\n /** 目标未启用时记的日志;缺省不记。 */\n disabledMessage?: string\n /**\n * 启动失败时记的日志;缺省由协调器给一句通用的。\n * 必须把 target 传进来:LAN 的端口来自配置,硬编码 0 会让所有\n * EADDRINUSE / EACCES 日志都写「port 0 is already in use」,\n * 用户无法知道是哪一个端口冲突(P0 端口 bug)。\n */\n failureMessage?: (error: unknown, target: TransportTarget) => string\n}\n\n/**\n * begin 期间的守卫:让行在不复制协调器内部状态的前提下表达「我已被取代」。\n * LAN 原先在 TLS 加载后与 listen 后各查一次(appliedLocalKey / localServer),\n * 这两个检查对应 isCurrent() 与 owns()。\n */\nexport interface BeginGuard<TInstance> {\n /** 这次触发是否仍是当前配置(差分键未被后续触发改写)。 */\n isCurrent(): boolean\n /** 传入的实例是否仍是协调器持有的那个(未被后续触发替换)。 */\n owns(instance: TInstance): boolean\n /** 进程是否正在销毁。 */\n isDisposed(): boolean\n}\n\nexport class TransportReconciler<TInstance, TStatus extends TransportStatus> {\n private instance: TInstance | undefined\n private appliedKey: string | undefined\n private disposed = false\n private tail: Promise<void> = Promise.resolve()\n private currentStatus: TStatus\n\n constructor(\n private readonly spec: TransportSpec<TInstance, TStatus>,\n private readonly config: () => Config,\n private readonly log: (message: string) => void,\n initialStatus: TStatus,\n ) {\n this.currentStatus = initialStatus\n }\n\n /**\n * 当前状态;report 快照直接读它。实例不在场时返回协调器自己维护的状态\n * (disabled / error / 初始),不被 `statusOf(undefined)` 的缺省值覆盖——\n * 否则一次启动失败会把状态翻转回 idle。\n */\n status(): TStatus {\n if (this.instance === undefined) return this.currentStatus\n return this.spec.statusOf(this.instance) ?? this.currentStatus\n }\n\n /** 由 loader/volatile-update 触发:串行执行,前一个没完就不抢。 */\n scheduleReconcile(): void {\n this.tail = this.tail\n .then(() => this.reconcile())\n .catch((error) => this.log('reconcile failed: ' + String(error)))\n }\n\n /** 等当前在途的那一次完成;供 teardown 使用。 */\n async settled(): Promise<void> {\n await this.tail\n }\n\n /** 一次协调:差分 → 拆除 → 启动 → 状态。 */\n async reconcile(): Promise<void> {\n if (this.disposed) return\n const target = this.spec.keyOf(this.config())\n const nextKey = JSON.stringify(target)\n if (nextKey === this.appliedKey) return\n\n if (this.spec.applyKey === 'before-teardown') {\n this.appliedKey = nextKey\n }\n\n const previous = this.instance\n this.instance = undefined\n // 没有可拆的就不调用 stop:LAN 的 closeServer(undefined) 虽是安全路径,\n // 但不该让每个 spec 都为「空实例」写防御分支。\n if (previous !== undefined) await this.spec.stop(previous).catch(() => {})\n if (this.disposed) return\n\n if (target.enabled !== true && this.spec.skipWhenDisabled) {\n this.currentStatus = this.spec.disabledStatus(target)\n if (this.spec.disabledMessage !== undefined) this.log(this.spec.disabledMessage)\n return\n }\n\n this.currentStatus = this.spec.startingStatus(target)\n try {\n const constructed = this.spec.construct(target)\n this.instance = constructed\n if (this.spec.applyKey === 'after-construct') {\n this.appliedKey = nextKey\n }\n const guard: BeginGuard<TInstance> = {\n isCurrent: () => this.appliedKey === nextKey,\n owns: (instance) => this.instance === instance,\n isDisposed: () => this.disposed,\n }\n const replacement = await this.spec.begin(constructed, guard)\n if (replacement !== undefined) this.instance = replacement\n if (this.disposed) {\n await this.spec.stop(this.instance).catch(() => {})\n return\n }\n this.currentStatus = this.spec.statusOf(this.instance) ?? this.currentStatus\n } catch (error) {\n if (this.spec.errorStatus === undefined) throw error\n // 有错误分支的行(LAN 式):半成品实例要拆掉并清空,否则 status() 会\n // 继续问它要状态,把 error 覆盖成 online。Funnel 式没有错误分支,\n // 实例的自身状态机会把 phase 落到 error,因此保留。\n const partial = this.instance\n this.instance = undefined\n if (partial !== undefined) await this.spec.stop(partial).catch(() => {})\n this.currentStatus = this.spec.errorStatus(error, target)\n this.log(this.spec.failureMessage?.(error, target) ?? 'transport failed: ' + String(error))\n }\n }\n\n /** 进程收尾:标记销毁、拆掉实例、等在途的那一次完成。 */\n async dispose(): Promise<void> {\n this.disposed = true\n const active = this.instance\n this.instance = undefined\n await this.spec.stop(active).catch(() => {})\n await this.tail\n }\n}\n\n// ---------- 两个传输行 ----------\n\nimport type { Server } from 'node:http'\nimport { join } from 'node:path'\nimport {\n closeServer,\n createPhoneServer,\n listen,\n type PhoneServerHandlers,\n type PhoneServerTransportError,\n} from './phone-server.ts'\nimport {\n localListenError,\n normalizeLocalPort,\n} from './local-policy.ts'\nimport {\n RemoteSupervisor,\n normalizeRemoteHostname,\n type RemoteStatus,\n} from './remote-supervisor.ts'\nimport { normalizeFunnelConnectionLimit } from './funnel-policy.ts'\n\n/** LAN 行的状态即 report 的 `local` 段。 */\nexport type LocalStatus = DeepPilotReport['local']\n\n/** LAN 实例:一个(可能还在起步的)TLS listener。 */\ninterface LocalInstance {\n /** TLS 就绪后才填上;在此之前协调器仍持有这个占位实例。 */\n server: Server | undefined\n tlsFingerprint: string | undefined\n port: number\n /** 上线时刻:状态查询复用同一时间戳,不随查询漂移(与迁移前一致)。 */\n onlineAt: number\n}\n\n/** LAN 需要的外部能力:窄 handler 面、TLS 身份加载器、当前配置。 */\nexport interface LocalTransportDeps {\n handlers: PhoneServerHandlers\n config: () => Config\n /** 加载(必要时创建)LAN TLS 身份;由 apply() 持有,reconciler 不懂 TLS。 */\n tls: () => Promise<{ key: string; cert: string; fingerprint: string }>\n log: (message: string) => void\n /** TLS 握手 / HTTP 报文层错误出口;index 负责把来源哈希后记录。 */\n onTransportError?: (event: PhoneServerTransportError) => void\n}\n\n/**\n * LAN 传输:TLS-only,绑定 0.0.0.0 的稳定端口。\n *\n * 三处刻意保留的差异:`skipWhenDisabled: true`(未启用即提前返回并置\n * disabled)、`applyKey: 'before-teardown'`(拆除旧 listener 之前写键)、\n * 有错误分支(监听失败落到 `phase: 'error'`)。另有两处 supersede 检查原样\n * 保留:TLS 加载后查 `isCurrent()`,listen 后查 `owns()`。\n */\nexport function createLocalTransport(deps: LocalTransportDeps): TransportReconciler<LocalInstance, LocalStatus> {\n const spec: TransportSpec<LocalInstance, LocalStatus> = {\n keyOf: (config) => ({\n enabled: config.enabled === true && config.local?.enabled !== false,\n port: normalizeLocalPort(config.local?.port),\n }),\n skipWhenDisabled: true,\n applyKey: 'before-teardown',\n // 只占位:listener 要等 TLS 身份就绪后才存在。\n construct: (target) => ({ server: undefined, tlsFingerprint: undefined, port: normalizeLocalPort(target.port), onlineAt: Date.now() }),\n begin: async (instance, guard) => {\n const tls = await deps.tls()\n // 被更新的触发取代:不建 listener,状态停在 starting(与迁移前一致)。\n if (!guard.isCurrent() || guard.isDisposed()) return\n const port = instance.port\n const server = createPhoneServer(\n deps.handlers,\n { key: tls.key, cert: tls.cert },\n deps.onTransportError !== undefined ? { onTransportError: deps.onTransportError } : undefined,\n )\n instance.server = server\n instance.tlsFingerprint = tls.fingerprint\n await listen(server, port, '0.0.0.0')\n // listen 期间被取代:拆掉这个半成品,不要让它在端口上继续听着。\n if (!guard.owns(instance) || guard.isDisposed()) {\n instance.server = undefined\n await closeServer(server)\n return\n }\n instance.onlineAt = Date.now()\n deps.log(`local transport listening on https://0.0.0.0:${port} (tls fingerprint ${tls.fingerprint})`)\n },\n stop: async (instance) => {\n if (instance?.server === undefined) return\n const server = instance.server\n instance.server = undefined\n await closeServer(server)\n },\n disabledStatus: (target) => ({\n phase: 'disabled', port: normalizeLocalPort(target.port), endpoints: [], updatedAt: Date.now(),\n }),\n startingStatus: (target) => ({\n phase: 'starting', port: normalizeLocalPort(target.port), endpoints: [], updatedAt: Date.now(),\n }),\n errorStatus: (error, target) => ({\n phase: 'error',\n port: normalizeLocalPort(target.port),\n endpoints: [],\n message: localListenError(error, normalizeLocalPort(target.port)),\n updatedAt: Date.now(),\n }),\n disabledMessage: 'local transport disabled',\n // 端口从 target 取,不再硬编码 0:EADDRINUSE 的日志必须能指向用户\n // 实际配置的那个端口。\n failureMessage: (error, target) => 'local transport failed: ' + localListenError(error, normalizeLocalPort(target.port)),\n statusOf: (instance) => {\n if (instance === undefined || instance.server === undefined) return undefined\n return {\n phase: 'online',\n port: instance.port,\n endpoints: [],\n tlsFingerprint: instance.tlsFingerprint,\n updatedAt: instance.onlineAt,\n }\n },\n }\n return new TransportReconciler<LocalInstance, LocalStatus>(spec, deps.config, deps.log, {\n phase: deps.config().enabled === true && deps.config().local?.enabled !== false ? 'starting' : 'disabled',\n port: normalizeLocalPort(deps.config().local?.port),\n endpoints: [],\n updatedAt: Date.now(),\n })\n}\n\n/** Funnel 需要的外部能力:窄 handler 面、loopback origin 地址、数据目录。 */\nexport interface RemoteTransportDeps {\n handlers: PhoneServerHandlers\n config: () => Config\n /** loopback origin 的地址;未就绪时该行不协调。 */\n originURL: () => string | undefined\n dataDir: string\n log: (message: string) => void\n}\n\n/**\n * Funnel 传输:loopback origin + Tailscale Funnel helper。\n *\n * 三处刻意保留的差异:`skipWhenDisabled: false`(未启用仍构造 supervisor,\n * 由其内部 no-op 把状态置 disabled)、`applyKey: 'after-construct'`\n * (supervisor 构造后、异步 start 之前写键)、没有错误分支(supervisor 自己\n * 的状态机负责 phase)。\n */\nexport function createRemoteTransport(deps: RemoteTransportDeps): TransportReconciler<RemoteSupervisor, RemoteStatus> {\n const fallbackStatus = (): RemoteStatus => ({\n provider: 'tailscale-funnel',\n phase: deps.config().remote?.enabled === true ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n })\n const spec: TransportSpec<RemoteSupervisor, RemoteStatus> = {\n keyOf: (config) => {\n const remoteConfig = config.remote ?? {}\n const remotePort: 443 | 8443 | 10000 = remoteConfig.funnelPort === 8443 || remoteConfig.funnelPort === 10000\n ? remoteConfig.funnelPort\n : 443\n return {\n enabled: config.enabled === true && remoteConfig.enabled === true && remoteConfig.provider === 'tailscale-funnel',\n hostname: normalizeRemoteHostname(remoteConfig.hostname),\n statePath: remoteConfig.statePath?.trim() || join(deps.dataDir, 'tailscale'),\n helperPath: remoteConfig.helperPath?.trim() || undefined,\n funnelPort: remotePort,\n maxConnectionsPerSource: normalizeFunnelConnectionLimit(remoteConfig.maxConnectionsPerSource),\n }\n },\n skipWhenDisabled: false,\n applyKey: 'after-construct',\n construct: (target) => new RemoteSupervisor({\n enabled: target.enabled === true,\n hostname: String(target.hostname),\n statePath: String(target.statePath),\n ...(target.helperPath !== undefined ? { helperPath: String(target.helperPath) } : {}),\n funnelPort: target.funnelPort as 443 | 8443 | 10000,\n maxConnectionsPerSource: Number(target.maxConnectionsPerSource),\n log: deps.log,\n }),\n begin: async (supervisor) => {\n const originURL = deps.originURL()\n // origin 尚未就绪:这一轮不启动(与迁移前 originURL === undefined 时\n // 直接返回一致;键已写入,origin 就绪后的那次触发会把它建起来)。\n if (originURL === undefined) return\n await supervisor.start(originURL)\n },\n stop: async (supervisor) => {\n if (supervisor === undefined) return\n await supervisor.dispose()\n },\n disabledStatus: () => ({ provider: 'tailscale-funnel', phase: 'disabled', updatedAt: Date.now() }),\n startingStatus: () => ({ provider: 'tailscale-funnel', phase: 'stopped', updatedAt: Date.now() }),\n statusOf: (supervisor) => supervisor?.status() ?? fallbackStatus(),\n }\n return new TransportReconciler<RemoteSupervisor, RemoteStatus>(spec, deps.config, deps.log, fallbackStatus())\n}\n","/**\n * at-most-once dispatch journal:prompt 投递与非 prompt 变更的**同一个** module。\n *\n * 迁移前 `prompt-delivery.ts` 与 `mutation-journal.ts` 是同一份耐久协议的两份\n * 拷贝(盘点结论:key/expired/save 骨架字节级相同,mutation 甚至反向 import\n * prompt 的 validSendId,共享已从错误方向发生)。两者的差别集中在六个面:\n * 指纹覆盖范围、条目字段、回执形状、id 校验、lookup、落盘容器格式——全部收进\n * 注入的 codec;核心只留耐久协议。\n *\n * 核心拥有:key 派生、原子写盘(temp + fsync + rename + 目录 fsync)、加载与\n * 逐字段校验、保留清扫、容量、healthy 降级、在途去重、opener 单例。\n *\n * healthy 的唯一来源是**构造期的解析失败**(文件损坏,续写会写坏格式);\n * 运行期的落盘失败只影响当次投递,下次投递重新尝试落盘。两者曾经共用同一个\n * 标志位,一次瞬时磁盘错误就让整个进程的所有投递永久短路到 DSH 重启\n * (issue #20 / #26)。\n *\n * codec 拥有:一次投递的全部「方言」。它同时是两个 adapter——prompt 回执与\n * mutation 结果——因此这条缝是真的,不是假想的。\n */\n\nimport { createHash, randomUUID } from 'node:crypto'\nimport { existsSync, readFileSync, mkdirSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, statSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { validSendId } from './connection-policy.ts'\nimport { ERROR_CODES, type WireErrorCode } from './wire-errors.ts'\n\n/** 落盘条目:两个通用字段 + codec 私有字段。 */\nexport interface JournalEntry {\n fingerprint: string\n createdAt: number\n [field: string]: unknown\n}\n\n/** operation 的结果:成功带值,失败带 wire 错误码。 */\nexport type OperationResult<T = unknown> =\n | { ok: true; value: T }\n | { ok: false; code: WireErrorCode; message?: string }\n\n/**\n * 一切「不看条目就能定」的结果类别。codec 用一个方法把它们翻译成自己的方言,\n * 而不是让核心知道任何一方的形状。\n */\nexport type DispatchOutcome =\n /** 命中既有条目(重放)。 */\n | { kind: 'replay'; entry: JournalEntry }\n /** 同一 id 搭不同内容。 */\n | { kind: 'mismatch' }\n /** 构造期解析失败:journal 已永久降级,本次进程不再投递。 */\n | { kind: 'unavailable' }\n /** id 的纪元前缀超出保留窗口。 */\n | { kind: 'expired' }\n /** 条目数已达容量上限。 */\n | { kind: 'full' }\n /** 刚跑完的那一次:operation 的结果原样带出(不标 replayed)。 */\n | { kind: 'ran'; entry: JournalEntry; result: OperationResult }\n /** operation 抛错:条目保持 unknown,且不得自动重试。 */\n | { kind: 'crashed' }\n /**\n * 预留条目时落盘失败——上游**没有**被调用,但无从判断 rename 是否已经\n * 生效(save() 可能在 rename 之后才抛),因此条目留在内存里按 unknown 处理。\n */\n | { kind: 'reserve-failed' }\n /** id 形状不合法(仅 mutation 变体开启这道守卫)。 */\n | { kind: 'invalid-id' }\n\nexport interface DispatchCodec<TRequest = unknown, TResult = unknown> {\n /** 实例身份:opener 单例键的一部分(含 persistValues 等变体差异)。 */\n readonly identity: string\n /** 请求指纹。prompt 覆盖 [sessionId, content];mutation 只覆盖 content。 */\n fingerprint(request: TRequest): string\n /** 预留条目时写入的 codec 字段(fingerprint/createdAt 由核心补)。 */\n reserve(request: TRequest, id: string): Record<string, unknown>\n /** operation 成功后就地更新条目。 */\n accepted(entry: JournalEntry, id: string, value: unknown): void\n /** operation 失败后就地更新条目。 */\n rejected(entry: JournalEntry, id: string, code: WireErrorCode): void\n /** 把结果类别翻译成调用方言。 */\n resultOf(outcome: DispatchOutcome, id: string): TResult\n /** id 非法时是否先拒掉(mutation 是;prompt 由 wire 层保证,故否)。 */\n readonly requiresValidId?: boolean\n /** lookup 的匹配规则;缺省表示该实例不提供 lookup。 */\n matches?(entry: JournalEntry, request: unknown): boolean\n /** lookup 未命中时回什么(prompt 要区分 notFound / expired)。 */\n miss?(id: string, expired: boolean): TResult\n /** 落盘容器 → 内存条目表;任何不合法都抛错(核心转成 healthy=false)。 */\n parse(raw: unknown): Record<string, JournalEntry>\n /** 内存条目表 → 落盘容器。 */\n serialize(entries: Record<string, JournalEntry>): unknown\n}\n\nexport interface DispatchJournalOptions<TRequest = unknown, TResult = unknown> {\n codec: DispatchCodec<TRequest, TResult>\n path?: string\n /** 落盘体积上限:prompt 8MB、mutation 4MB(由条目实际大小决定,不统一)。 */\n maxFileBytes?: number\n capacity?: number\n retentionMs?: number\n /**\n * 并发重复是否搭同在途那次。prompt 是(重复方拿到同一个最终回执);\n * mutation 否(重复方立刻拿到 unknown → E_INTERNAL)。两者都由对等快照钉住。\n */\n joinInFlight?: boolean\n /**\n * 落盘失败的诊断输出。由调用方注入它已有的 logger;缺省即静默。\n * 只打阶段与 errno,绝不打条目内容或消息体(plugin 的日志禁令)。\n */\n log?: (message: string) => void\n /**\n * 目录 fsync 里「对 fd 调用 fsync」这一步的覆盖点,缺省即 `fsyncSync`。\n *\n * 存在的唯一理由是可测:Linux CI 上复现不出 Windows 的 EPERM。注入点刻意\n * 停在 fd 这一层而不是替换整个目录 fsync——容忍 EPERM 的判断必须留在真实\n * 路径里被测到,换掉整函数就等于把待测逻辑一起换掉了。\n */\n fsyncFileDescriptor?: (fd: number) => void\n}\n\nconst DEFAULT_CAPACITY = 10_000\nconst DEFAULT_RETENTION_MS = 7 * 24 * 3600 * 1000\n/** 比保留窗口多宽限 5 分钟:边界附近的条目不会被反复删了又写。 */\nconst SWEEP_GRACE_MS = 5 * 60 * 1000\n\n/**\n * 目录 fsync 的三个「平台不支持」码。\n *\n * Windows 上 `openSync(dir, 'r')` 成功但 `fsyncSync(dirfd)` 必定抛错(社区实测\n * 报 EPERM,issue #20 / #26);EISDIR / EINVAL 是同一现象的其他平台变体。\n * 这三个码在本调用点都不代表数据损坏:文件内容已在 rename 之前 fsync 过,且\n * rename 在同一目录内是原子的,跳过的只是目录项的额外耐久性保证。\n */\nconst TOLERATED_DIR_FSYNC_CODES: ReadonlySet<string> = new Set(['EPERM', 'EISDIR', 'EINVAL'])\n\n/** errno 优先,取不到就退回错误本身的字符串(诊断用,不参与控制流)。 */\nfunction errnoOf(error: unknown): string {\n const code = (error as NodeJS.ErrnoException | null)?.code\n return typeof code === 'string' ? code : String(error)\n}\n\n/** 这类错误是否属于「该平台不支持目录 fsync」而非真的写坏了。 */\nexport function isToleratedDirectoryFsyncError(error: unknown): boolean {\n return TOLERATED_DIR_FSYNC_CODES.has(errnoOf(error))\n}\n\n/**\n * 目录 fsync:把 rename 造成的目录项变更推给磁盘。\n *\n * 按 errno 而非 `process.platform` 判断:WSL / Wine 之类环境下平台名并不可靠,\n * 而 errno 才是真正决定「这个平台支不支持」的信号(issue #26 给的两种建议里,\n * 按 errno 的那种才是对的)。其余 errno(EIO、ENOSPC……)照旧抛出。\n */\nexport function fsyncDirectory(dirPath: string, fsync: (fd: number) => void = fsyncSync): void {\n const fd = openSync(dirPath, 'r')\n try {\n fsync(fd)\n } catch (error) {\n if (!isToleratedDirectoryFsyncError(error)) throw error\n } finally {\n closeSync(fd)\n }\n}\n\n/** Durable at-most-once dispatch. Unknown outcomes are never automatically retried. */\nexport class DispatchJournal<TRequest = unknown, TResult = unknown> {\n private entries: Record<string, JournalEntry> = Object.create(null)\n private inFlight = new Map<string, Promise<TResult>>()\n /** 只由构造期的解析失败置位,见下方 catch;运行期落盘失败不碰它。 */\n private healthy = true\n private readonly maxFileBytes: number\n private readonly capacity: number\n private readonly retentionMs: number\n private readonly joinInFlight: boolean\n\n constructor(private readonly options: DispatchJournalOptions<TRequest, TResult>) {\n this.maxFileBytes = options.maxFileBytes ?? 8 * 1024 * 1024\n this.capacity = options.capacity ?? DEFAULT_CAPACITY\n this.retentionMs = options.retentionMs ?? DEFAULT_RETENTION_MS\n this.joinInFlight = options.joinInFlight ?? true\n const path = options.path\n if (path === undefined || !existsSync(path)) return\n try {\n if (statSync(path).size > this.maxFileBytes) throw new Error('oversized journal')\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as { version?: unknown; entries?: unknown }\n if (parsed.version !== 1 || !Array.isArray(parsed.entries) || parsed.entries.length > this.capacity) {\n throw new Error('invalid journal')\n }\n const loaded = this.options.codec.parse(parsed.entries)\n for (const key of Object.keys(loaded)) this.entries[key] = loaded[key]!\n } catch {\n // 唯一会把 healthy 拉黑的地方:文件读不出来或不合法。宁可整个进程都拒绝\n // 投递,也不按一个可能是错的格式续写(续写会污染此后每一次回放)。\n this.healthy = false\n }\n }\n\n private key(deviceId: string, id: string): string {\n return createHash('sha256').update(JSON.stringify([deviceId, id])).digest('hex')\n }\n\n /** id 的纪元前缀是否还在保留窗口内(未来 5 分钟以上同样算非法)。 */\n private expired(id: string, now = Date.now()): boolean {\n const age = now - Number(id.slice(0, 13))\n return age > this.retentionMs || age < -SWEEP_GRACE_MS\n }\n\n private save(): void {\n const path = this.options.path\n if (path === undefined) return\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 })\n const temp = path + '.' + randomUUID() + '.tmp'\n const fd = openSync(temp, 'wx', 0o600)\n try {\n writeFileSync(fd, JSON.stringify({ version: 1, entries: this.options.codec.serialize(this.entries) }))\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n renameSync(temp, path)\n fsyncDirectory(dirname(path), this.options.fsyncFileDescriptor)\n }\n\n /**\n * 落盘一次,失败只报不降级。\n *\n * 与 `healthy` 的分工:`healthy` 只由构造期的解析失败置位,运行期的磁盘错误\n * 不再拉黑整个进程——此前一次 save() 失败就让本进程余下的所有投递永久返回\n * unknown,直到 DSH 重启(issue #20 / #26)。现在只影响当次投递,下一次\n * dispatch 会重新尝试。\n */\n private persist(): boolean {\n try {\n this.save()\n return true\n } catch (error) {\n this.options.log?.('journal 落盘失败,本次投递按未确认处理,下次投递会重新尝试:' + errnoOf(error))\n return false\n }\n }\n\n /** 查询既有回执;`request` 交给 codec 的 matches 判定(缺省即不提供 lookup)。 */\n lookup(deviceId: string, id: string, request?: unknown): TResult | undefined {\n const codec = this.options.codec\n if (codec.matches === undefined) return undefined\n if (!this.healthy) return codec.resultOf({ kind: 'unavailable' }, id)\n const entry = this.entries[this.key(deviceId, id)]\n if (entry !== undefined && codec.matches(entry, request)) return codec.resultOf({ kind: 'replay', entry }, id)\n return codec.miss?.(id, this.expired(id)) ?? codec.resultOf({ kind: 'unavailable' }, id)\n }\n\n /**\n * 幂等投递。未知结果绝不自动重试:条目保持 unknown,重放时由 codec 决定\n * 调用方看到什么。\n */\n async dispatch(deviceId: string, id: string, request: TRequest, operation: () => Promise<OperationResult>): Promise<TResult> {\n const codec = this.options.codec\n // id 守卫在最前(与迁移前 mutation 的次序一致):形状非法的请求不落盘。\n if (codec.requiresValidId === true && !validSendId(id)) {\n return codec.resultOf({ kind: 'invalid-id' }, id)\n }\n const key = this.key(deviceId, id)\n const fingerprint = codec.fingerprint(request)\n const existing = this.entries[key]\n if (existing !== undefined) {\n if (existing.fingerprint !== fingerprint) return codec.resultOf({ kind: 'mismatch' }, id)\n // 搭同在途那次(prompt):并发重复共享一次上游调用,拿同一最终回执。\n const pending = this.inFlight.get(key)\n if (this.joinInFlight && pending !== undefined) return pending\n return codec.resultOf({ kind: 'replay', entry: existing }, id)\n }\n if (!this.healthy) return codec.resultOf({ kind: 'unavailable' }, id)\n if (this.expired(id)) return codec.resultOf({ kind: 'expired' }, id)\n\n const now = Date.now()\n for (const [entryKey, entry] of Object.entries(this.entries)) {\n if (now - entry.createdAt > this.retentionMs + SWEEP_GRACE_MS && !this.inFlight.has(entryKey)) {\n delete this.entries[entryKey]\n }\n }\n if (Object.keys(this.entries).length >= this.capacity) return codec.resultOf({ kind: 'full' }, id)\n\n // 先落盘预约再跑上游:进程在两次之间死掉,重启后也能识别「结果未知」。\n const entry: JournalEntry = { fingerprint, createdAt: Number(id.slice(0, 13)), ...codec.reserve(request, id) }\n this.entries[key] = entry\n if (!this.persist()) {\n // 预约没落盘就不能跑上游。条目**保留**在内存里:save() 可能是在 rename\n // 之后才抛的,磁盘上或许已经有这条 unknown,抹掉等于把它从表里除名。\n return codec.resultOf({ kind: 'reserve-failed' }, id)\n }\n\n const run = (async (): Promise<TResult> => {\n let ran: OperationResult\n try {\n ran = await operation()\n if (ran.ok) codec.accepted(entry, id, ran.value)\n else codec.rejected(entry, id, ran.code)\n } catch {\n // 上游可能在传输失败前已经接受:条目保持 unknown,绝不自动重试。\n return codec.resultOf({ kind: 'crashed' }, id)\n }\n // 结果落盘失败不回滚条目:operation 已经跑过,回滚等于把一次可能已生效的\n // 上游调用从表里除名。下次投递若写成功,会把整表(含本条目)一起写回去。\n this.persist()\n // 刚跑完的那一次:operation 的结果原样带出,不标 replayed。\n return codec.resultOf({ kind: 'ran', entry, result: ran }, id)\n })()\n this.inFlight.set(key, run)\n try { return await run } finally { this.inFlight.delete(key) }\n }\n\n /** 等所有在途投递结束;供进程收尾。 */\n async settled(): Promise<void> {\n while (this.inFlight.size > 0) {\n await Promise.all([...this.inFlight.values()])\n }\n }\n}\n\n// ---------- opener ----------\n\nconst journals = new Map<string, DispatchJournal<never, never>>()\n\n/**\n * 按 path + codec 身份取单例。身份进键是必要的:同一路径用不同 codec/persistValues\n * 打开必须得到不同实例——迁移前的键只有 path,`persistValues` 因此会被静默忽略。\n */\nexport function openDispatchJournal<TRequest, TResult>(\n options: DispatchJournalOptions<TRequest, TResult>,\n): DispatchJournal<TRequest, TResult> {\n const { path, codec } = options\n if (path === undefined) {\n return new DispatchJournal<TRequest, TResult>(options)\n }\n const cacheKey = path + '|' + codec.identity\n const cached = journals.get(cacheKey)\n if (cached !== undefined) return cached as unknown as DispatchJournal<TRequest, TResult>\n const journal = new DispatchJournal<TRequest, TResult>(options)\n journals.set(cacheKey, journal as unknown as DispatchJournal<never, never>)\n return journal\n}\n\n// ---------- prompt 投递 codec ----------\n\n/** prompt 投递的回执:手机侧按 clientSendId 对账。 */\nexport type DeliveryStatus = 'accepted' | 'rejected' | 'unknown' | 'notFound' | 'expired'\nexport interface DeliveryReceipt {\n clientSendId: string\n status: DeliveryStatus\n userSeq?: number\n code?: string\n}\n\n/** prompt 的落盘条目:回执内嵌,sessionId 单独存一份供 lookup 比对。 */\ninterface PromptEntry extends JournalEntry {\n sessionId: string\n receipt: DeliveryReceipt\n}\n\n/** 受理即 accepted;四种会被 APNs 终态清理的失败码之外一律原样上抛。 */\nconst PROMPT_RECEIPT_CODES = ['E_BUSY', 'E_NOT_FOUND', 'E_PROTOCOL', 'E_UNSUPPORTED'] as const\n\n/**\n * prompt 投递方言:\n * - 指纹覆盖 [sessionId, content](同一条 prompt 换会话重发视为不同请求);\n * - 并发重复搭同在途那次(joinInFlight),因此重复方拿到同一最终回执;\n * - 有 lookup,且 sessionId 不匹配即视为未命中;\n * - 落盘是 [key, entry] 二元组数组(历史格式,零迁移)。\n */\nexport const promptDeliveryCodec: DispatchCodec<\n { sessionId: string; content: unknown },\n DeliveryReceipt\n> & { identity: string } = {\n identity: 'prompt-delivery',\n fingerprint: (request) => createHash('sha256')\n .update(JSON.stringify([request.sessionId, request.content]))\n .digest('hex'),\n reserve: (request, id) => ({\n sessionId: request.sessionId,\n receipt: { clientSendId: id, status: 'unknown' },\n }),\n accepted: (entry, id, value) => {\n const receipt = entry.receipt as DeliveryReceipt\n entry.receipt = { clientSendId: id, status: 'accepted', userSeq: value as number }\n },\n rejected: (entry, id, code) => {\n const receipt = entry.receipt as DeliveryReceipt\n entry.receipt = { clientSendId: id, status: 'rejected', code }\n },\n resultOf: (outcome, id) => {\n switch (outcome.kind) {\n case 'replay':\n case 'ran': return (outcome.entry.receipt ?? { clientSendId: id, status: 'unknown' }) as DeliveryReceipt\n case 'mismatch': return { clientSendId: id, status: 'rejected', code: 'E_PROTOCOL' }\n case 'unavailable':\n case 'crashed':\n case 'reserve-failed': return { clientSendId: id, status: 'unknown' }\n case 'expired': return { clientSendId: id, status: 'expired' }\n case 'full': return { clientSendId: id, status: 'rejected', code: 'E_BUSY' }\n case 'invalid-id': return { clientSendId: id, status: 'rejected', code: 'E_PROTOCOL' }\n }\n },\n // prompt 不做 id 守卫:wire 层已在 validateRequest/行内校验保证形状。\n requiresValidId: false,\n matches: (entry, request) => entry.sessionId === request,\n miss: (id, expired) => ({ clientSendId: id, status: expired ? 'expired' : 'notFound' }),\n parse: (raw) => {\n const out: Record<string, JournalEntry> = Object.create(null)\n for (const [key, value] of raw as Array<[string, PromptEntry]>) {\n if (typeof key !== 'string' || !/^[a-f0-9]{64}$/.test(key) || !value || typeof value.sessionId !== 'string' ||\n !Number.isFinite(value.createdAt) || typeof value.fingerprint !== 'string' ||\n !validSendId(value.receipt?.clientSendId) ||\n !['accepted', 'rejected', 'unknown'].includes(value.receipt.status)) throw new Error('invalid entry')\n if (!/^[a-f0-9]{64}$/.test(value.fingerprint) ||\n value.createdAt !== Number(value.receipt.clientSendId.slice(0, 13)) ||\n (value.receipt.status === 'accepted' && !Number.isSafeInteger(value.receipt.userSeq)) ||\n (value.receipt.status === 'rejected' && !(PROMPT_RECEIPT_CODES as readonly string[]).includes(String(value.receipt.code)))) {\n throw new Error('invalid receipt')\n }\n out[key] = value\n }\n return out\n },\n serialize: (entries) => Object.entries(entries).map(([key, entry]) => [key, entry]),\n}\n\n// ---------- schedule/fork mutation codec ----------\n\n/** mutation 的结果:replayed 让调用方知道这是重放而非新执行。 */\nexport type MutationErrorCode = WireErrorCode\n\nexport type MutationOperationResult<T> =\n | { ok: true; value: T }\n | { ok: false; code: MutationErrorCode; message?: string }\n\nexport type MutationDispatchResult<T> =\n | { ok: true; value?: T; replayed?: boolean }\n | { ok: false; code: MutationErrorCode; message?: string; replayed?: boolean }\n\ninterface MutationEntry extends JournalEntry {\n status: 'accepted' | 'rejected' | 'unknown'\n code?: MutationErrorCode\n value?: unknown\n}\n\n/**\n * mutation 方言(schedule 与 fork 共用,fork 打开 persistValues):\n * - 有 id 守卫,且在最前面(形状非法的 clientRequestId 不落盘);\n * - 并发重复不搭在途那次:重复方立刻拿到 unknown → E_INTERNAL「不得自动重试」;\n * - 成功值只在 persistValues 时落盘——fork 需要重启后仍能重放 sessionId,\n * schedule 的 authoritative 资源由客户端重新拉取,不在此复制第二份;\n * - 落盘是对象数组(历史格式,零迁移)。\n */\nexport function scheduleMutationCodec(persistValues: boolean): DispatchCodec<unknown, MutationDispatchResult<unknown>> & { identity: string } {\n const isErrorCode = (value: unknown): value is MutationErrorCode =>\n typeof value === 'string' && value in ERROR_CODES\n return {\n identity: 'mutation:' + (persistValues ? 'persist' : 'memory'),\n fingerprint: (request) => createHash('sha256').update(JSON.stringify(request)).digest('hex'),\n reserve: () => ({ status: 'unknown' }),\n accepted: (entry, _id, value) => {\n entry.status = 'accepted'\n if (persistValues) entry.value = value\n },\n rejected: (entry, _id, code) => {\n entry.status = 'rejected'\n entry.code = code\n },\n resultOf: (outcome, id) => {\n switch (outcome.kind) {\n case 'ran': return outcome.result as MutationDispatchResult<unknown>\n case 'replay': {\n const entry = outcome.entry as MutationEntry\n if (entry.status === 'accepted') return { ok: true, value: entry.value, replayed: true }\n if (entry.status === 'rejected' && entry.code !== undefined) return { ok: false, code: entry.code, replayed: true }\n return { ok: false, code: 'E_INTERNAL', message: 'mutation outcome is unknown; do not retry automatically', replayed: true }\n }\n case 'mismatch': return { ok: false, code: 'E_PROTOCOL', message: 'clientRequestId was reused with different content', replayed: true }\n case 'invalid-id': return { ok: false, code: 'E_PROTOCOL', message: 'invalid clientRequestId' }\n case 'unavailable': return { ok: false, code: 'E_INTERNAL', message: 'mutation journal unavailable' }\n case 'expired': return { ok: false, code: 'E_PROTOCOL', message: 'clientRequestId is outside the retry window' }\n case 'full': return { ok: false, code: 'E_BUSY', message: 'mutation journal is full' }\n case 'reserve-failed': return { ok: false, code: 'E_INTERNAL', message: 'mutation journal could not persist the request' }\n case 'crashed': return { ok: false, code: 'E_INTERNAL', message: 'mutation outcome is unknown; do not retry automatically' }\n }\n },\n requiresValidId: true,\n // mutation 没有 lookup:权威资源由客户端重新拉取。\n parse: (raw) => {\n const out: Record<string, JournalEntry> = Object.create(null)\n for (const value of raw as MutationEntry[]) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('invalid mutation entry')\n const entry = value as Record<string, unknown>\n if (typeof entry.fingerprint !== 'string' || !/^[a-f0-9]{64}$/.test(entry.fingerprint) ||\n typeof entry.createdAt !== 'number' || !Number.isFinite(entry.createdAt) ||\n !['accepted', 'rejected', 'unknown'].includes(String(entry.status))) throw new Error('invalid mutation entry')\n if (entry.status === 'rejected' && !isErrorCode(entry.code)) throw new Error('invalid mutation code')\n const key = String((value as { key?: unknown }).key)\n if (!/^[a-f0-9]{64}$/.test(key)) throw new Error('invalid mutation key')\n out[key] = {\n fingerprint: entry.fingerprint as string,\n createdAt: entry.createdAt as number,\n status: entry.status as MutationEntry['status'],\n ...(isErrorCode(entry.code) ? { code: entry.code } : {}),\n ...(persistValues && entry.value !== undefined ? { value: entry.value } : {}),\n }\n }\n return out\n },\n serialize: (entries) => Object.entries(entries).map(([key, entry]) => ({ key, ...entry })),\n }\n}\n","/**\n * 通知正文的纯文本投影。\n *\n * 为什么需要:s2c.notify 与 APNs 的 body 是\"可直接展示\"的文本(PROTOCOL §6),\n * 客户端不得改写;而来源(助手回复、审批原因、提问)都是 Host 给的 Markdown。\n * 直接截原文会把链接地址、URL 编码的中文路径、代码块符号带进锁屏横幅,\n * 所以 Bridge 出站前在这里统一转成纯文本。\n *\n * 只做\"去标记、留可读文字\",不追求完整 Markdown 语义;任何无法识别的写法原样保留。\n */\n\n/** 把 Markdown 拆成段落并逐段转为单行纯文本;清洗后为空的段落被丢弃。 */\nexport function notificationParagraphs(markdown: string): string[] {\n const text = stripCodeFences(markdown.replace(/\\r\\n?/g, '\\n'))\n return text\n .split(/\\n[ \\t]*\\n/)\n .map(plainParagraph)\n .filter((paragraph) => paragraph.length > 0)\n}\n\n/**\n * 通知正文:`last` 取最后一个非空段落(任务完成 / 异常结束——回复结尾通常是结论),\n * `all` 把全部段落合成一行(审批原因、提问原文)。长度上限由调用方负责。\n */\nexport function notificationPlainText(markdown: string, pick: 'last' | 'all'): string {\n const paragraphs = notificationParagraphs(markdown)\n return pick === 'last' ? (paragraphs.at(-1) ?? '') : paragraphs.join(' ')\n}\n\n/** 围栏代码块整体丢弃:横幅里放不下代码,留一半反而更难读。未闭合的围栏丢到文末。 */\nfunction stripCodeFences(text: string): string {\n return text.replace(/^[ \\t]*(`{3,}|~{3,})[^\\n]*\\n[\\s\\S]*?(?:^[ \\t]*\\1[ \\t]*$|(?![\\s\\S]))/gm, '\\n')\n}\n\nfunction plainParagraph(paragraph: string): string {\n const lines = paragraph\n .split('\\n')\n // 表格分隔行与分割线没有文字内容。\n .filter((line) => !(line.includes('|') && line.includes('-') && /^[\\s|:-]+$/.test(line)))\n .filter((line) => !/^\\s*([*_-])(\\s*\\1){2,}\\s*$/.test(line))\n .map((line) => line\n .replace(/^\\s*(>\\s?)+/, '')\n .replace(/^\\s*#{1,6}\\s+/, '')\n .replace(/^\\s*([-*+]|\\d+[.)])\\s+(\\[[ xX]\\]\\s+)?/, '')\n // 只有以 | 开头的行才当表格行拆格,正文里的竖线保留。\n .replace(/^\\s*\\|(.*?)\\|?\\s*$/, (_row, cells: string) => cells.replace(/\\s*\\|\\s*/g, ' ')))\n return decodePercentRuns(stripInline(lines.join(' ')))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction stripInline(text: string): string {\n return text\n // 图片:alt 文字在横幅里没有意义。\n .replace(/!\\[[^\\]]*\\]\\((?:[^()]|\\([^()]*\\))*\\)/g, '')\n // 行内链接 [文字](地址) 与引用式链接 [文字][id] 只留文字;地址允许一层括号。\n .replace(/\\[([^\\]]+)\\]\\((?:[^()]|\\([^()]*\\))*\\)/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\[[^\\]]*\\]/g, '$1')\n // 自动链接 <https://…> 去尖括号,剩余 HTML 标签直接去掉。\n .replace(/<((?:https?|mailto):[^>\\s]+)>/g, '$1')\n .replace(/<\\/?[A-Za-z][^>]*>/g, '')\n // 行内代码保留内容(常是文件名、命令名),只去反引号。\n .replace(/(`+)([^`]+?)\\1/g, '$2')\n .replace(/(\\*\\*|__|~~)(?=\\S)([\\s\\S]*?\\S)\\1/g, '$2')\n // 单个 * / _ 强调要求两侧是非单词字符,避免误伤 snake_case 与乘号。\n .replace(/(^|[^\\w*])\\*(?=\\S)([^*\\n]*?\\S)\\*(?![\\w*])/g, '$1$2')\n .replace(/(^|[^\\w])_(?=\\S)([^_\\n]*?\\S)_(?!\\w)/g, '$1$2')\n}\n\n/** 正文里残留的 %E6%8F%90 这类编码串解码成原字符;解不开(非法 UTF-8)就保持原样。 */\nfunction decodePercentRuns(text: string): string {\n return text.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {\n try {\n return decodeURIComponent(run)\n } catch {\n return run\n }\n })\n}\n","import type { SessionExportFormat, SessionSummary } from './protocol.ts'\nimport type {\n HostSchedulePatch,\n HostScheduleTargetRequest,\n HostScheduleTask,\n HostScheduleTiming,\n} from './protocol.ts'\nimport type { DeviceScope } from './device-auth.ts'\nimport type { PushNotification } from './protocol.ts'\nimport type { WireErrorCode } from './wire-errors.ts'\nimport type { CommandAttachment, CommandCatalog, CommandExecution, PermissionCatalog } from './command-api.ts'\n\nexport interface CommandsApiLike {\n list(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<CommandCatalog>>\n execute(req: RpcRequestLike<{ sessionId: string; line: string; attachments: CommandAttachment[]; expectedDefinitionId?: string }>): Promise<RpcResponseLike<CommandExecution>>\n permissions(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<PermissionCatalog>>\n feedback(req: RpcRequestLike<{ sessionId: string; text: string; category?: string }>): Promise<RpcResponseLike<void>>\n readonly markdownAvailable?: boolean\n export(req: RpcRequestLike<{ sessionId: string; format?: SessionExportFormat }>, signal: AbortSignal): Promise<Response>\n}\n\n/* Stable bridge-facing structural types mapped to rc.2 controllers by\n * DshApiProxy. */\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface RpcRequestLike<T> { rpcId?: string; payload?: T }\ninterface RpcResponseLike<T> { result?: RpcResult<T> }\n\ninterface SessionsApiLike {\n list(req?: RpcRequestLike<{ cursor?: string }>): Promise<RpcResponseLike<{ items: PhoneSessionRow[] }>>\n history(req: RpcRequestLike<{ sessionId: string; beforeSeq?: number; maxMessages?: number }>):\n Promise<RpcResponseLike<HistoryResult>>\n prompt(req: RpcRequestLike<PromptArgs>): Promise<RpcResponseLike<{ accepted: true }>>\n create(req: RpcRequestLike<{ workspaceId?: string; cwd?: string; agentPreset?: string }>): Promise<RpcResponseLike<{ sessionId: string; agentPreset?: string }>>\n fork?(req: RpcRequestLike<{ sessionId: string; atSeq?: number }>): Promise<RpcResponseLike<{ sessionId: string }>>\n models?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<HostSessionModels>>\n skills?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<{ skills: import('./protocol.ts').SkillSummary[] }>>\n selectModel?(req: RpcRequestLike<{\n sessionId: string\n provider: string\n model: string\n reasoningEffort?: string\n }>): Promise<RpcResponseLike<{ selected: HostModelSelection }>>\n rename?(req: RpcRequestLike<{ sessionId: string; title: string }>):\n Promise<RpcResponseLike<{ title: string; seq: number }>>\n cancel?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<Record<string, unknown> | undefined>>\n /** Reads one durable image back after the host verifies the session log references its id. */\n attachment?(req: RpcRequestLike<{ sessionId: string; attachmentId: string }>):\n Promise<RpcResponseLike<{ attachment: { mediaType?: string }; data: string }>>\n /** Remove or steer one pending inbox item. Absent on hosts without the\n * DSH session.updateQueue RPC; the promptQueue capability bit probes it. */\n updateQueue?(req: RpcRequestLike<QueueUpdateArgs>): Promise<RpcResponseLike<{ accepted: true }>>\n /** Complete projection baseline for one session; null when it is gone. */\n projections(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ asOfSeq: number; values: Record<string, unknown> } | null>>\n}\n\ninterface WorkspaceApiLike {\n list?(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }>>\n create?(req: RpcRequestLike<{ path: string }>):\n Promise<RpcResponseLike<{ workspace: WorkspaceViewLike; created: boolean }>>\n archiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n unarchiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n}\n\n/* ---------- Schedule:会话级(legacy)与宿主级(host)双形态 ----------\n *\n * DSH 0.2.2-alpha.3 把 `@deepseek-ai/dsh-schedule` 从 per-session 模型改成宿主级\n * 模型(任务带交付 target;没有 update/delete,只有 edit;list 返回分页对象)。\n * 插件必须双形态共存,因此 `ApiProxyLike.schedule` 是一个带判别位的联合:缺省\n * `mode` 表示会话级(旧宿主与既有假 service 都是这个形状),`mode: 'host'` 表示\n * 下面这套宿主级接口。Bridge 只认这两个分支,不再按版本号字符串猜测。\n */\n\nexport type ScheduleKind = 'after' | 'at' | 'every' | 'daily' | 'weekly' | 'cron'\n\nexport interface ScheduleTaskViewLike {\n id: string\n kind: ScheduleKind\n title: string\n prompt: string\n scheduledAt: string\n state: 'scheduled' | 'overdue'\n deliveryMode: 'host'\n afterSeconds?: number\n everySeconds?: number\n time?: string\n timeZone?: string\n weekdays?: number[]\n expression?: string\n}\n\nexport interface ScheduleHistoryViewLike {\n id: string\n records: Array<{\n scheduledAt: string\n deliveredAt: string\n messageId: string\n prompt?: string\n }>\n earlierRecordsUnavailable: boolean\n earlierRecordsPruned?: boolean\n retention: { days: number; records: number }\n nextBefore?: string\n}\n\n/** 会话级 Schedule 服务(DSH ≤ 0.2.2-alpha.2):任务绑定单一 Session。 */\nexport interface LegacyScheduleApiLike {\n /** 判别位缺省即 legacy:旧宿主与既有测试夹具不必补字段。 */\n readonly mode?: 'legacy'\n list(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ sessionId: string; tasks: ScheduleTaskViewLike[] }>>\n history(req: RpcRequestLike<{ sessionId: string; id: string; limit: number; before?: string }>):\n Promise<RpcResponseLike<{ sessionId: string; history: ScheduleHistoryViewLike }>>\n create(req: RpcRequestLike<{ sessionId: string; title: string; prompt: string; [key: string]: unknown }>):\n Promise<RpcResponseLike<ScheduleTaskViewLike>>\n update(req: RpcRequestLike<{ sessionId: string; id: string; expected: unknown; title?: string; prompt?: string; change?: unknown }>):\n Promise<RpcResponseLike<{ id: string; updated: boolean; record?: ScheduleTaskViewLike; code?: string }>>\n delete(req: RpcRequestLike<{ sessionId: string; id: string }>):\n Promise<RpcResponseLike<{ id: string; deleted: boolean; code?: string }>>\n}\n\n/** 宿主级任务视图:与 wire 的 `HostScheduleTask` 同一形状(Bridge 直接透传)。 */\nexport type HostScheduleTaskView = HostScheduleTask\n\nexport interface HostScheduleListRequestLike {\n /** true 只列运行中;false 只列暂停/结束;省略列全部。 */\n active?: boolean\n limit?: number\n offset?: number\n}\n\nexport interface HostScheduleListResultLike {\n tasks: HostScheduleTaskView[]\n shown: number\n total: number\n limit: number\n offset: number\n}\n\n/** 查看结果:命中或非变更 miss(Host 用 code 表示 miss,不是抛错)。 */\nexport type HostScheduleViewOutcome =\n | { kind: 'task'; task: HostScheduleTaskView }\n | { kind: 'not-found'; id: string }\n\n/** 编辑结果:更新后的任务、已删除、miss,或乐观锁冲突(wire 上回 E_BUSY)。 */\nexport type HostScheduleEditOutcome =\n | { kind: 'task'; task: HostScheduleTaskView }\n | { kind: 'deleted'; id: string }\n | { kind: 'not-found'; id: string }\n | { kind: 'conflict'; id: string }\n\n/** 手动执行结果:已投递、已有执行在进行中(E_BUSY),或目标已不存在。 */\nexport type HostScheduleRunOutcome =\n | { kind: 'task'; task: HostScheduleTaskView }\n | { kind: 'pending'; id: string }\n | { kind: 'not-found'; id: string }\n\n/** 清理已结束任务的结果:删除的条数(宿主上没有已结束任务时为 0)。 */\nexport interface HostScheduleDeleteEndedResult {\n deleted: number\n}\n\nexport interface HostScheduleLimitsView {\n /** 以新建会话方式投递的最小间隔秒数;客户端只展示,不自行节流。 */\n newSessionIntervalSeconds: number\n}\n\nexport interface HostScheduleCreateRequestLike {\n title: string\n prompt: string\n timing: HostScheduleTiming\n /** 省略时由 Host 默认:首次投递新建并复用会话。 */\n target?: HostScheduleTargetRequest\n}\n\nexport type HostScheduleEditRequestLike =\n | { id: string; action: 'update'; patch: HostSchedulePatch; expectedUpdatedAt?: string | null }\n | { id: string; action: 'delete'; expectedUpdatedAt?: string | null }\n\n/**\n * 宿主级 Schedule 服务(DSH 0.2.2-alpha.3 起)。\n *\n * 探测只看形状:`edit` 存在且 `update` 不存在。`remoteXxx` 是同一服务的\n * 「人类 Remote 调用」变体(不记录 acting Session);Bridge 就是人类 Remote\n * 调用方,因此直接用公开方法并省略 actorSessionId。\n */\nexport interface HostScheduleApiLike {\n readonly mode: 'host'\n list(req: RpcRequestLike<HostScheduleListRequestLike>):\n Promise<RpcResponseLike<HostScheduleListResultLike>>\n view(req: RpcRequestLike<{ id: string }>): Promise<RpcResponseLike<HostScheduleViewOutcome>>\n create(req: RpcRequestLike<HostScheduleCreateRequestLike>):\n Promise<RpcResponseLike<HostScheduleTaskView>>\n edit(req: RpcRequestLike<HostScheduleEditRequestLike>):\n Promise<RpcResponseLike<HostScheduleEditOutcome>>\n run(req: RpcRequestLike<{ id: string }>): Promise<RpcResponseLike<HostScheduleRunOutcome>>\n history(req: RpcRequestLike<{ id: string; limit: number; before?: string }>):\n Promise<RpcResponseLike<ScheduleHistoryViewLike>>\n limits(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<HostScheduleLimitsView>>\n /** 删除宿主上全部 `status=\"ended\"` 的任务;进行中与暂停的任务不受影响。 */\n deleteEnded(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<HostScheduleDeleteEndedResult>>\n}\n\nexport type ScheduleApiLike = LegacyScheduleApiLike | HostScheduleApiLike\n\ninterface HostApiLike {\n listDirectory?(req: RpcRequestLike<{ path?: string }>, signal?: AbortSignal):\n Promise<RpcResponseLike<DirectoryListingLike>>\n pickDirectory?(req: RpcRequestLike<Record<string, never>>, signal?: AbortSignal):\n Promise<RpcResponseLike<{ path: string | null }>>\n}\n\nexport interface WorkspaceViewLike {\n workspaceId: string\n path: string\n title: string\n sessionIds: string[]\n createdAt?: string\n updatedAt?: string\n}\n\nexport interface DirectoryEntryLike {\n name: string\n path: string\n hidden: boolean\n}\n\nexport interface DirectoryListingLike {\n path: string\n home: string\n crumbs: DirectoryEntryLike[]\n entries: DirectoryEntryLike[]\n truncated: boolean\n}\n\nexport interface HostModelSelection {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\nexport interface HostSessionModels {\n current: HostModelSelection\n routable: boolean\n groups: Array<{\n id: string\n name: string\n models: Array<{\n id: string\n name: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n }>\n }>\n failures: Array<{ id: string; name: string; message: string }>\n}\n\n/**\n * Bridge 方法的结果。失败直接携带 wire 错误码(error vocabulary 见\n * wire-errors.ts):调用方(wire-registry 的行)原样透给手机,不再经过\n * kind 中间层翻译。\n */\nexport type ModelBridgeResult<T> =\n | { ok: true; value: T }\n | { ok: false; code: WireErrorCode; message: string }\n\nexport type SessionManagementResult<T> =\n | { ok: true; value: T }\n | { ok: false; code: WireErrorCode; message: string }\n\nexport interface PromptArgs {\n sessionId: string\n mode: 'queue' | 'steer'\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }\n >\n clientTimeZone?: string\n}\n\n/** DSH session.updateQueue request; `edit` is reserved on the wire and never sent. */\nexport interface QueueUpdateArgs {\n sessionId: string\n itemId: string\n action: { kind: 'remove' } | { kind: 'steer' }\n}\n\nexport interface PhoneSessionRow {\n sessionId: string\n updatedAt: number\n running: boolean\n /** Host marks never-prompted sessions as blank; informational only — the\n * summary projects them as idle (see toSummary) so phones can send. */\n blank?: boolean\n cwd?: string\n origin?: string\n parentSessionId?: string\n projections?: { asOfSeq?: number; values?: Record<string, unknown> }\n}\n\n/**\n * Subagent sessions are host-internal workers of a parent conversation.\n * They must never surface on the phone: not in the project/session list,\n * and not as turn-completion pushes for a session the device cannot open.\n *\n * `parentSessionId` alone is NOT a subagent signal: a session forked from\n * another one (c2s.session.fork) also carries it, as fork lineage, while\n * being an ordinary top-level session the phone must show. Only the host's\n * explicit `origin` marks a worker — mirror the same rule already used for\n * the live mux stream (see dsh-api-proxy.ts's `isSubagent` derivation).\n */\nexport function isSubagentRow(row: PhoneSessionRow): boolean {\n return row.origin === 'subagent';\n}\n\nexport interface HistoryResult {\n events: Array<{ event: SessionEventLike; view?: unknown }>\n hasMore: boolean\n projections?: { values?: Record<string, unknown> }\n}\n\nexport interface SessionEventLike {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface MuxFrameLike {\n /** Internal Host identity hint; never serialized into the phone protocol. */\n isSubagent?: boolean\n type: string\n rpcId?: string\n payload?: any\n sessionId?: string\n event?: SessionEventLike\n key?: string\n value?: unknown\n approvalId?: string\n callId?: string\n toolName?: string\n reason?: string\n questions?: unknown\n questionRpcId?: string\n running?: boolean\n}\n\n/**\n * apiProxy stream items are server-request envelopes: the frame lives in\n * `payload`, while the stable request id used to answer approval/question\n * waits lives beside it. Flattening only `payload` loses that id and makes\n * both interactions silently disappear.\n */\nexport interface ApiStreamItemLike {\n rpcId?: string\n payload: MuxFrameLike\n}\n\nexport function unwrapStreamItem(item: MuxFrameLike | ApiStreamItemLike): MuxFrameLike {\n const nested = item.payload\n if (nested && typeof nested === 'object' && typeof nested.type === 'string') {\n // The envelope id identifies the server request that must be answered.\n // Preserve it even if a future nested payload happens to carry another id.\n return item.rpcId ? { ...nested, rpcId: item.rpcId } : nested\n }\n return item as MuxFrameLike\n}\n\nexport interface ApiProxyLike {\n /** 真实宿主反射声明了助手流帧时才为 true;旧代理缺省关闭。 */\n readonly assistantStreamAvailable?: boolean\n commands?: CommandsApiLike\n sessions: SessionsApiLike\n workspace?: WorkspaceApiLike\n schedule?: ScheduleApiLike\n host?: HostApiLike\n respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }):\n Promise<{ accepted: boolean; reason?: string }>\n events: {\n mux(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n host(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n }\n}\n\n/**\n * Outcome of answering a pending approval/question. The host distinguishes\n * \"never/no longer pending\" from \"payload rejected\", and collapsing both into\n * a boolean made every rejection read as `question not pending` on the phone.\n */\nexport type PendingResponseOutcome =\n | { ok: true }\n | { ok: false; reason: 'not-pending' | 'bad-response' | 'transport' }\n\n/** Downward sink every connected phone registers (one per WebSocket). */\nexport interface BridgeSink {\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n /**\n * S→C permission gate (R1/P2): whether this sink may receive broadcast or\n * replayed frames that require `scope`. The bridge consults this before\n * every push/replay delivery; unknown broadcast types are denied.\n */\n canReceive(scope: DeviceScope): boolean\n}\n\n/**\n * Offline push fan-out (F-9 离线推送). The bridge forwards every\n * notification-worthy event here; the outlet decides which paired devices\n * (token holders without a live socket) receive an APNs delivery.\n */\nexport interface PushOutlet {\n fanOut(notification: PushNotification): void\n widgetChanged?(): void\n liveActivityChanged?(sessions: SessionSummary[]): void\n /**\n * Whether offline push is currently configured and usable. Drives the\n * welcome capability bit: advertising push while no APNs credentials are\n * loaded would make clients suppress their own local banners and lose\n * notifications entirely.\n */\n isAvailable(): boolean\n}\n","/**\n * 宿主能力探测(host capability probe)的唯一属主。\n *\n * `welcome.capabilities` 的每一位都必须由这里的一张表推导;此前同一条事实\n * 存在两份——getter 里的表达式与各 Bridge 方法内的 `typeof` 再探测——\n * 已经漂移过两次:`models` 位要求 `models && selectModel`,而\n * `c2s.session.models` 只需要 `models`(门过严);`schedules` 位只探\n * `list && create`,而五个 schedule 方法只要求服务存在(部分 Host 上会 TypeError)。\n * 新增的 `schedulesHost` 取同一个 adapter 的判别位(与会话级位同源),不再单独\n * 探方法——同类漂移在宿主级模型上会直接表现为「新帧被门挡住」或「能力位撒谎」。\n *\n * 规则:一位一行,表达式取「该位所闸的帧真正需要的最小依赖」。位与帧的绑定\n * 由 wire 注册表的 `capability` 字段声明(见 wire-registry.ts),host-bridge\n * 只负责把表算出来,不再各自判断。\n */\n\nimport type { ApiProxyLike, PushOutlet } from './host-api.ts'\nimport type { WelcomeCapabilities } from './protocol.ts'\n\n/** 由宿主表达式挣得的位;表在 HOST_CAPABILITY_PROBES。 */\nexport type ProbedCapability =\n | 'models'\n | 'skills'\n | 'commands'\n | 'sessionManagement'\n | 'sessionFork'\n | 'sessionRestore'\n | 'projectSelection'\n | 'schedules'\n | 'schedulesHost'\n | 'promptQueue'\n | 'sessionMarkdown'\n | 'assistantStream'\n | 'pluginUpdate'\n\n// 自更新服务由宿主可选注入,生命周期与 Bridge 数据源相互独立。按代理实例绑定\n// 就绪探针,避免用「模块被导入」冒充服务已就绪,也不让能力表反向依赖 wire 行表。\nconst pluginUpdateProbes = new WeakMap<ApiProxyLike, () => boolean>()\nexport function setPluginUpdateProbe(proxy: ApiProxyLike, probe: (() => boolean) | undefined): void {\n if (probe) pluginUpdateProbes.set(proxy, probe)\n else pluginUpdateProbes.delete(proxy)\n}\n\n/** 每位一行:Host 上必须存在的依赖。缺失即该位为 false,相关帧回 E_UNSUPPORTED。 */\nexport const HOST_CAPABILITY_PROBES: Record<ProbedCapability, (proxy: ApiProxyLike) => boolean> = {\n // PROTOCOL.md:模型列表必须由 Host 的 session.models 动态提供。切换模型另由\n // c2s.session.selectModel 自己的探测兜底(缺 selectModel 时回 E_UNSUPPORTED)。\n models: (proxy) => typeof proxy.sessions.models === 'function',\n skills: (proxy) => typeof proxy.sessions.skills === 'function',\n commands: (proxy) => typeof proxy.commands?.list === 'function' && typeof proxy.commands?.execute === 'function',\n sessionManagement: (proxy) =>\n typeof proxy.sessions.rename === 'function' &&\n typeof proxy.workspace?.archiveSession === 'function',\n sessionFork: (proxy) => typeof proxy.sessions.fork === 'function',\n sessionRestore: (proxy) => typeof proxy.workspace?.unarchiveSession === 'function',\n projectSelection: (proxy) =>\n typeof proxy.workspace?.list === 'function' &&\n typeof proxy.workspace?.create === 'function',\n // 五个会话级 schedule 帧都只要求 Schedule 服务存在;历史上按 list&&create 探测\n // 的取值会在只实现了部分方法的 Host 上把位压成 false,而方法体内又只判服务存在。\n schedules: (proxy) => proxy.schedule !== undefined,\n // 宿主级能力位与 schedules 同源:取同一个已解析 adapter 的判别位,而不是再按\n // 控制器的某个方法探一遍(该方法探测与能力位曾漂移过两次,见文件头)。\n schedulesHost: (proxy) => proxy.schedule?.mode === 'host',\n // 排队快照来自 inbox 投影,增删转向走 session.updateQueue;两者在支持的 DSH\n // 版本里同时提供(DSH_RELEASE_MEMORY 2026-10-24 实测 0.2.0-rc.2),因此只探方法。\n promptQueue: (proxy) => typeof proxy.sessions.updateQueue === 'function',\n sessionMarkdown: (proxy) => proxy.commands?.markdownAvailable === true && typeof proxy.commands?.export === 'function',\n assistantStream: (proxy) => proxy.assistantStreamAvailable === true,\n pluginUpdate: (proxy) => pluginUpdateProbes.get(proxy)?.() === true,\n}\n\n/** 由 Bridge 自身恒真的位(见 BRIDGE_INVARIANT_CAPABILITIES)。 */\nexport type BridgeInvariantCapability = (typeof BRIDGE_INVARIANT_CAPABILITIES)[number]\n\n/** 只约束出站瞬时帧的能力,无须用入站请求触发。 */\nexport const OUTBOUND_HOST_CAPABILITIES = {\n assistantStream: 's2c.session.delta',\n} as const\n\n/** 由推送出口就绪状态决定的位(APNs 凭据或中继注册)。 */\nexport type OutletCapability = 'push'\n\n/**\n * 门禁必须留在 handler 内的位。`push` 是唯一一个:零配置注册(enrollKey)\n * 必须先于能力门执行,否则首次注册恰好发生在推送尚未就绪时就会被拒,\n * 分布式构建的自动启用链就此断掉(见 wire-push.ts 的同名说明)。\n */\nexport const HANDLER_GATED_CAPABILITIES = ['push'] as const satisfies ReadonlyArray<OutletCapability>\n\n/** 行上的能力门可以引用三类位中的任意一种。 */\nexport type CapabilityGate = ProbedCapability | BridgeInvariantCapability | OutletCapability\n\n/**\n * Bridge 自身恒为真的位:与宿主无关,列出是为了让读者一眼看出 welcome 上哪些位\n * 不是从 Host 推导的。缺失这些位中的任何一个都意味着 Bridge 实现被改动,\n * 改这里不改帧的 gate 属于遗漏。\n */\nexport const BRIDGE_INVARIANT_CAPABILITIES = [\n 'historyPaging',\n 'replay',\n 'approvals',\n 'questions',\n 'pendingSnapshot',\n 'promptDelivery',\n 'notifyAllCategories',\n 'widgetPush',\n 'liveActivityPush',\n 'deviceRevoke',\n] as const satisfies ReadonlyArray<keyof WelcomeCapabilities>\n\n/**\n * 按当前 apiProxy 与推送出口算出 welcome 能力位。`push` 位描述「当前离线推送\n * 可用」:APNs 凭据或中继注册就绪才为 true,否则客户端不应压下自己的本地通知。\n */\nexport function capabilityBits(\n proxy: ApiProxyLike,\n pushOutlet: PushOutlet | undefined,\n): WelcomeCapabilities {\n return {\n historyPaging: true,\n replay: true,\n // 插件自身能力,不依赖宿主:导出块与附件可按请求改用二进制帧。\n binaryFrames: true,\n approvals: true,\n questions: true,\n pendingSnapshot: true,\n promptDelivery: true,\n notifyAllCategories: true,\n models: HOST_CAPABILITY_PROBES.models(proxy),\n skills: HOST_CAPABILITY_PROBES.skills(proxy),\n commands: HOST_CAPABILITY_PROBES.commands(proxy),\n sessionManagement: HOST_CAPABILITY_PROBES.sessionManagement(proxy),\n sessionFork: HOST_CAPABILITY_PROBES.sessionFork(proxy),\n sessionRestore: HOST_CAPABILITY_PROBES.sessionRestore(proxy),\n projectSelection: HOST_CAPABILITY_PROBES.projectSelection(proxy),\n schedules: HOST_CAPABILITY_PROBES.schedules(proxy),\n schedulesHost: HOST_CAPABILITY_PROBES.schedulesHost(proxy),\n promptQueue: HOST_CAPABILITY_PROBES.promptQueue(proxy),\n sessionMarkdown: HOST_CAPABILITY_PROBES.sessionMarkdown(proxy),\n assistantStream: HOST_CAPABILITY_PROBES.assistantStream(proxy),\n pluginUpdate: HOST_CAPABILITY_PROBES.pluginUpdate(proxy),\n push: pushOutlet?.isAvailable() === true,\n widgetPush: true,\n liveActivityPush: true,\n deviceRevoke: true,\n }\n}\n","/**\n * 运行中排队与转向(PROTOCOL.md「运行中排队与转向扩展」)的纯函数部分。\n *\n * 职责:\n * - Host `requestId` 与 wire `clientSendId` 的互转:Bridge 发送带 clientSendId 的\n * prompt 时以 `dp:` 前缀拼出确定的 requestId,Host 把它记为用户来源的 `rpcId`;\n * 快照时反向剥掉前缀,发起设备即可把待发送项与队列项对上。\n * - 把 DSH `inbox` 投影(`{ \"next-turn\": Message[], \"next-step\": Message[] }`,\n * 来源是持久化的 `agent/inbox/spliced` 事件)折叠成 `s2c.session.queue` 的条目。\n *\n * 不持有状态:缓存、去重与推送时机在 host-bridge。\n */\n\nimport { validSendId } from './connection-policy.ts'\nimport { projectedDocument } from './document-payload.ts'\nimport type { SessionQueueItem } from './protocol.ts'\n\nconst REQUEST_ID_PREFIX = 'dp:'\n\n/** 快照正文上限(Unicode 码点)。只用于列表展示,完整正文仍在 Host 日志里。 */\nexport const MAX_QUEUE_TEXT_CODE_POINTS = 4096\n\n/** clientSendId -> Host requestId。 */\nexport function promptRequestId(clientSendId: string): string {\n return REQUEST_ID_PREFIX + clientSendId\n}\n\n/** Host 来源 rpcId -> clientSendId;不是本 Bridge 用 clientSendId 发出的返回 undefined。 */\nexport function clientSendIdOf(rpcId: unknown): string | undefined {\n if (typeof rpcId !== 'string' || !rpcId.startsWith(REQUEST_ID_PREFIX)) return undefined\n const id = rpcId.slice(REQUEST_ID_PREFIX.length)\n return validSendId(id) ? id : undefined\n}\n\ninterface InboxMessageLike {\n id?: unknown\n content?: unknown\n source?: { kind?: unknown; rpcId?: unknown }\n}\n\n/**\n * inbox 投影 -> 队列条目,保持 Host 的先进先出顺序:先 `next-turn`(排队),\n * 再 `next-step`(转向)。值形状不对时返回 undefined,调用方据此不推送,\n * 而不是把「读不懂」当成「队列为空」——客户端会把消失的项当作已发出。\n */\nexport function projectQueue(value: unknown): SessionQueueItem[] | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const inbox = value as Record<string, unknown>\n const nextTurn = inbox['next-turn']\n const nextStep = inbox['next-step']\n if (!Array.isArray(nextTurn) || !Array.isArray(nextStep)) return undefined\n const items: SessionQueueItem[] = []\n for (const [list, placement] of [[nextTurn, 'queued'], [nextStep, 'steering']] as const) {\n for (const message of list as InboxMessageLike[]) {\n const item = queueItemOf(message, placement)\n if (item) items.push(item)\n }\n }\n return items\n}\n\nfunction queueItemOf(message: InboxMessageLike, placement: SessionQueueItem['placement']): SessionQueueItem | undefined {\n if (message === null || typeof message !== 'object' || typeof message.id !== 'string' || message.id.length === 0) {\n return undefined\n }\n const texts: string[] = []\n let imageCount = 0\n let documentCount = 0\n for (const block of Array.isArray(message.content) ? message.content as Array<{ type?: unknown; text?: unknown }> : []) {\n if (block === null || typeof block !== 'object') continue\n if (block.type === 'image') {\n imageCount += 1\n } else if (block.type === 'text' && typeof block.text === 'string') {\n // 文档以带标记的文本块随 prompt 发出(document-payload.ts):计数,不进正文。\n if (projectedDocument(block.text) !== undefined) documentCount += 1\n else texts.push(block.text)\n }\n }\n const codePoints = Array.from(texts.join('\\n'))\n const truncated = codePoints.length > MAX_QUEUE_TEXT_CODE_POINTS\n const source = message.source\n const clientSendId = source?.kind === 'user' ? clientSendIdOf(source.rpcId) : undefined\n return {\n itemId: message.id,\n placement,\n text: truncated ? codePoints.slice(0, MAX_QUEUE_TEXT_CODE_POINTS).join('') : codePoints.join(''),\n ...(truncated ? { truncated: true } : {}),\n imageCount,\n documentCount,\n ...(clientSendId !== undefined ? { clientSendId } : {}),\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { SessionEventLike } from './host-api.ts'\nimport type { SessionDeltaPayload, SessionStreamOutcome } from './protocol.ts'\n\n/** Structural mirror of rc.3 / alpha.2 agent/assistant-stream; older hosts need no import. */\nexport type AssistantStreamFrame =\n | { type: 'start'; attemptId: string; revision: number; turn: number; step: number }\n | { type: 'chunk'; attemptId: string; revision: number; index: number; time: number; chunk: { type: string; text?: string } }\n | { type: 'end'; attemptId: string; revision: number; index: number; outcome: AssistantStreamOutcome }\n\nexport type AssistantStreamOutcome = SessionStreamOutcome\nexport type AssistantStreamDelta = SessionDeltaPayload\n\nexport const ASSISTANT_STREAM_FLUSH_MS = 50\nexport const MAX_ASSISTANT_DELTA_BYTES = 256 * 1024\n\ntype TextKind = 'message.delta' | 'thinking.delta'\ninterface PendingText { kind: TextKind; text: string; ts: number; truncated?: true }\ninterface Attempt {\n attemptId: string\n streamId: string\n revision: number\n nextIndex: number\n turn: number\n step: number\n finalSeq?: number\n pending: PendingText[]\n pendingBytes: number\n timer?: ReturnType<typeof setTimeout>\n}\n\n/** Bound by serialized UTF-8 size, including escaping, without splitting a Unicode code point. */\nfunction bounded(payload: AssistantStreamDelta): AssistantStreamDelta {\n if (Buffer.byteLength(JSON.stringify(payload)) <= MAX_ASSISTANT_DELTA_BYTES) return payload\n const points = Array.from(payload.text)\n let low = 0\n let high = points.length\n while (low < high) {\n const mid = Math.ceil((low + high) / 2)\n const candidate = { ...payload, text: points.slice(0, mid).join(''), truncated: true }\n if (Buffer.byteLength(JSON.stringify(candidate)) <= MAX_ASSISTANT_DELTA_BYTES) low = mid\n else high = mid - 1\n }\n return { ...payload, text: points.slice(0, low).join(''), truncated: true }\n}\n\nfunction isFrame(value: unknown): value is AssistantStreamFrame {\n if (!value || typeof value !== 'object') return false\n const frame = value as AssistantStreamFrame\n if (typeof frame.attemptId !== 'string' || !frame.attemptId || !Number.isSafeInteger(frame.revision)) return false\n if (frame.type === 'start') return Number.isSafeInteger(frame.turn) && Number.isSafeInteger(frame.step)\n if (frame.type !== 'chunk' && frame.type !== 'end') return false\n if (!Number.isSafeInteger(frame.index) || frame.index < 0) return false\n if (frame.type === 'chunk') return Number.isSafeInteger(frame.time) && !!frame.chunk && typeof frame.chunk.type === 'string'\n return frame.outcome?.kind === 'abandoned' || (frame.outcome?.kind === 'committed' &&\n ['assistant/message', 'assistant/attempt'].includes(frame.outcome.eventType) && Number.isSafeInteger(frame.outcome.seq))\n}\n\n/**\n * One process-local attempt per session. Only the small pending window retains text;\n * the durable session event remains the source of truth for the completed reply.\n */\nexport class AssistantStream {\n private readonly attempts = new Map<string, Attempt>()\n\n constructor(\n private readonly emit: (payload: AssistantStreamDelta) => void,\n private readonly isViewed: (sessionId: string) => boolean,\n ) {}\n\n accept(sessionId: string, value: unknown): void {\n if (!sessionId || !isFrame(value)) return\n const frame = value\n let state = this.attempts.get(sessionId)\n if (frame.type === 'start') {\n // The Host's attempt counter resets when its Agent is replaced. A fresh\n // bridge id avoids colliding with a still-rendered attempt after replacement.\n if (state?.attemptId === frame.attemptId && frame.revision <= state.revision) return\n this.abandon(sessionId)\n state = { attemptId: frame.attemptId, streamId: randomUUID(), revision: frame.revision,\n nextIndex: 0, turn: frame.turn, step: frame.step, pending: [], pendingBytes: 0 }\n this.attempts.set(sessionId, state)\n this.emit({ sessionId, streamId: state.streamId, kind: 'stream.start', text: '', ts: Date.now() })\n return\n }\n if (!state || state.attemptId !== frame.attemptId || frame.revision <= state.revision || frame.index < state.nextIndex) return\n state.revision = frame.revision\n if (frame.type === 'end') {\n if (frame.outcome.kind === 'committed') this.flush(sessionId)\n this.clearPending(sessionId)\n this.attempts.delete(sessionId)\n this.emit({ sessionId, streamId: state.streamId, kind: 'stream.end', text: '', ts: Date.now(), outcome: frame.outcome })\n return\n }\n state.nextIndex = frame.index + 1\n if (state.finalSeq !== undefined || !this.isViewed(sessionId)) return\n const kind = frame.chunk.type === 'text-delta' ? 'message.delta'\n : frame.chunk.type === 'reasoning-delta' ? 'thinking.delta' : undefined\n if (!kind || typeof frame.chunk.text !== 'string' || frame.chunk.text.length === 0) return\n // 只合并相邻同类片段,不能把正文→推理→正文重排为正文→推理。\n // 达到两帧预算时提前发出窗口,防止同步突发流用大量短分段撑大内存。\n const previous = state.pending.at(-1)?.kind === kind ? state.pending.at(-1) : undefined\n if (previous?.truncated) return\n const payload = bounded({ sessionId, streamId: state.streamId, kind,\n text: (previous?.text ?? '') + frame.chunk.text, ts: frame.time })\n const next: PendingText = { kind, text: payload.text, ts: payload.ts, ...(payload.truncated ? { truncated: true } : {}) }\n const oldBytes = previous ? Buffer.byteLength(JSON.stringify(previous)) : 0\n const newBytes = Buffer.byteLength(JSON.stringify(next))\n if (state.pendingBytes - oldBytes + newBytes > 2 * MAX_ASSISTANT_DELTA_BYTES) this.flush(sessionId)\n if (previous && state.pending.at(-1) === previous) {\n state.pending[state.pending.length - 1] = next\n state.pendingBytes -= oldBytes\n } else if (previous) {\n // 提前 flush 已发出旧片段,本次只排入新增部分,不能重复发送累计前缀。\n const tail = bounded({ sessionId, streamId: state.streamId, kind, text: frame.chunk.text, ts: frame.time })\n next.text = tail.text\n next.truncated = tail.truncated ? true : undefined\n state.pending.push(next)\n } else state.pending.push(next)\n state.pendingBytes += Buffer.byteLength(JSON.stringify(next))\n state.timer ??= setTimeout(() => this.flush(sessionId), ASSISTANT_STREAM_FLUSH_MS)\n state.timer.unref?.()\n }\n\n /** The Host appends the durable event BEFORE publishing committed end. */\n beforeEvent(sessionId: string, event: SessionEventLike): string | undefined {\n const state = this.attempts.get(sessionId)\n if (!state || (event.type !== 'assistant/message' && event.type !== 'assistant/attempt')) return undefined\n const data = event.data as { turn?: number; step?: number } | undefined\n if (data?.turn !== state.turn || data?.step !== state.step) return undefined\n this.flush(sessionId)\n state.finalSeq = event.seq\n return state.streamId\n }\n\n flush(sessionId: string): void {\n const state = this.attempts.get(sessionId)\n if (!state) return\n if (state.timer) clearTimeout(state.timer)\n state.timer = undefined\n const pending = state.pending\n state.pending = []\n state.pendingBytes = 0\n for (const text of pending) this.emit({ sessionId, streamId: state.streamId, ...text })\n }\n\n /** Closing the last viewer must not deliver its buffered text on a later open. */\n clearPending(sessionId: string): void {\n const state = this.attempts.get(sessionId)\n if (!state) return\n if (state.timer) clearTimeout(state.timer)\n state.timer = undefined\n state.pending = []\n state.pendingBytes = 0\n }\n\n abandon(sessionId: string): void {\n const state = this.attempts.get(sessionId)\n if (!state) return\n this.clearPending(sessionId)\n this.attempts.delete(sessionId)\n this.emit({ sessionId, streamId: state.streamId, kind: 'stream.end', text: '', ts: Date.now(), outcome: { kind: 'abandoned' } })\n }\n\n dispose(): void {\n for (const sessionId of this.attempts.keys()) this.clearPending(sessionId)\n this.attempts.clear()\n }\n}\n","import {\n openDispatchJournal,\n promptDeliveryCodec,\n scheduleMutationCodec,\n type DeliveryReceipt,\n type MutationDispatchResult,\n} from './dispatch-journal.ts'\nimport { randomUUID } from 'node:crypto'\nimport { notificationPlainText } from './notification-text.ts'\nimport type { Envelope, MessageProjection, NotifyCategory, PendingSnapshotPayload, PushNotification, SessionEventKind, SessionSummary, SessionTodoItem, SessionTodoStatus, SessionQueueItem, SessionUsageStats, WelcomeCapabilities } from './protocol.ts'\nimport {\n isSubagentRow,\n unwrapStreamItem,\n} from './host-api.ts'\nimport type {\n ApiProxyLike,\n BridgeSink,\n DirectoryListingLike,\n HostModelSelection,\n HostScheduleApiLike,\n HostScheduleCreateRequestLike,\n HostScheduleListRequestLike,\n HostScheduleListResultLike,\n HostScheduleTaskView,\n HostSessionModels,\n PendingResponseOutcome,\n PhoneSessionRow,\n PromptArgs,\n PushOutlet,\n ScheduleHistoryViewLike,\n ScheduleTaskViewLike,\n SessionEventLike,\n SessionManagementResult,\n ModelBridgeResult,\n MuxFrameLike,\n WorkspaceViewLike,\n} from './host-api.ts'\nimport type { HostSchedulePatch, HostScheduleTargetRequest, HostScheduleTiming } from './protocol.ts'\nexport * from './host-api.ts'\nimport {\n MAX_MESSAGE_PROJECTION_BYTES,\n MAX_SESSION_PAGE_MESSAGES_BYTES,\n DEFAULT_HISTORY_PAGE_MESSAGES,\n MAX_HISTORY_PAGE_MESSAGES,\n MIN_TAIL_MESSAGES,\n canonicalSessionMessages,\n limitMessageProjection,\n limitSessionPageMessages,\n messageText,\n projectEvent,\n projectHistory,\n} from './host-event-projection.ts'\nimport { documentPromptBlock, type PromptDocument } from './document-payload.ts'\nimport type { DispatchJournal } from './dispatch-journal.ts'\nimport { pushScopeFor } from './connection-policy.ts'\nimport { capabilityBits } from './host-capabilities.ts'\nimport { projectQueue, promptRequestId } from './prompt-queue.ts'\nimport { hostFailureOf, wireCodeFor, wireErrorOf, type WireErrorCode } from './wire-errors.ts'\nimport { formatLogLine } from './diagnostics.ts'\nimport { AssistantStream, type AssistantStreamDelta } from './assistant-stream.ts'\n\nexport {\n MAX_MESSAGE_PROJECTION_BYTES,\n MAX_SESSION_PAGE_MESSAGES_BYTES,\n canonicalSessionMessages,\n limitMessageProjection,\n limitSessionPageMessages,\n projectEvent,\n projectHistory,\n} from './host-event-projection.ts'\n\ninterface PendingApproval {\n rpcId: string\n sessionId: string\n toolName: string\n reason: string\n toolArguments?: string\n}\n\ninterface PendingQuestion {\n rpcId: string\n sessionId: string\n questions: unknown\n}\n\n/**\n * 失败结果直接携带 wire 错误码(error vocabulary 见 wire-errors.ts)。\n *\n * 迁移前这里有 kind 中间层('not-found' | 'busy' | 'conflict' | 'invalid'…),\n * 由 5 个映射函数在「Host code ↔ kind ↔ E_*」之间来回翻译,其中\n * E_BUSY→conflict→E_BUSY 的往返已证明有损。现在每个域查一次表,调用方拿到的\n * 就是手机上会看到的错误码。\n */\nexport type ScheduleBridgeResult<T> =\n | { ok: true; value: T; replayed?: boolean }\n | { ok: false; code: WireErrorCode; message: string; replayed?: boolean }\n\n/**\n * 宿主级编辑(update / delete 合一的 `edit`)的 Bridge 结果值。\n *\n * 与 legacy 的 updated/deleted 两个独立帧结果分开表达,wire handler 用\n * `'deleted' in value` 落成 `deleted:true` 或 `task`。\n */\nexport type HostScheduleEditValue =\n | { task: HostScheduleTaskView }\n | { deleted: true; id: string }\n\nconst MAX_RING_DEFAULT = 2000;\n\n/**\n * Process-wide bridge state: session mirror, pending approvals/questions,\n * and the per-device replay ring. Consumes the in-process mux/host streams\n * and fans projected pushes out to every registered sink.\n */\nlet BRIDGE_SEQ = 0;\n\nexport class HostBridge {\n readonly id = ++BRIDGE_SEQ;\n private summaries = new Map<string, SessionSummary>()\n private activeTools = new Map<string, Map<string, string>>()\n private approvals = new Map<string, PendingApproval>()\n private questions = new Map<string, PendingQuestion>()\n private archivedSessionIds = new Set<string>()\n /** Mirrors archived rows from the last sessions.list so the phone can browse\n * and restore them. Excluded from `summaries` and from the live broadcast. */\n private archivedSummaries = new Map<string, SessionSummary>()\n private subagentSessionIds = new Set<string>()\n /**\n * 每个会话最近一次已知的队列(inbox 投影的折叠结果)及其指纹。来源是实时\n * 投影通知或打开会话时读到的基线;指纹用来丢掉内容没变的重复通知。\n */\n private queues = new Map<string, { items: SessionQueueItem[]; fingerprint: string }>()\n private sinks = new Set<BridgeSink>()\n private readonly assistantStream = new AssistantStream(\n payload => this.pushAssistantDelta(payload),\n sessionId => this.hasStreamViewer(sessionId),\n )\n private ring: Array<{ seq: number; type: string; payload: unknown }> = []\n private cursor = 0;\n private userReceiptSeq = 0;\n private abort = new AbortController();\n private started = false\n private disposed = false\n\n readonly promptDeliveries: DispatchJournal<{ sessionId: string; content: unknown }, DeliveryReceipt>\n readonly scheduleMutations: DispatchJournal<unknown, MutationDispatchResult<unknown>>\n readonly forkMutations: DispatchJournal<unknown, MutationDispatchResult<unknown>>\n\n constructor(\n private readonly apiProxy: ApiProxyLike,\n private readonly historyBufferMax: number = MAX_RING_DEFAULT,\n deliveryJournalPath?: string,\n scheduleJournalPath?: string,\n forkJournalPath?: string,\n /** 三个 journal 的落盘失败诊断输出;缺省即静默。不打任何消息体。 */\n private readonly log?: (message: string) => void,\n ) {\n // 三个实例是同一个 module 的三次配置:条目方言由 codec 决定(prompt 回执 /\n // schedule 结果 / fork 结果带持久化 value),落盘路径各自独立、互不影响。\n // log 必须在**首次**构造时就带上:openDispatchJournal 是按 path+codec 身份\n // 缓存的单例,事后补传不会生效。\n this.promptDeliveries = openDispatchJournal({\n path: deliveryJournalPath,\n codec: promptDeliveryCodec,\n joinInFlight: true,\n maxFileBytes: 8 * 1024 * 1024,\n log: this.log,\n })\n this.scheduleMutations = openDispatchJournal({\n path: scheduleJournalPath,\n codec: scheduleMutationCodec(false),\n joinInFlight: false,\n maxFileBytes: 4 * 1024 * 1024,\n log: this.log,\n })\n this.forkMutations = openDispatchJournal({\n path: forkJournalPath,\n codec: scheduleMutationCodec(true),\n joinInFlight: false,\n maxFileBytes: 4 * 1024 * 1024,\n log: this.log,\n })\n }\n\n private pushOutlet: PushOutlet | undefined;\n private widgetFingerprint = ''\n\n /**\n * Wire the offline-push fan-out. Present ⇒ welcome advertises the `push`\n * capability and notify-worthy events are mirrored to APNs.\n */\n setPushOutlet(outlet: PushOutlet | undefined): void {\n this.pushOutlet = outlet;\n }\n\n /**\n * welcome 能力位。委托 host-capabilities.ts 的探测表:此前同一条事实在这里\n * 和各方法体内各存一份,已经漂移过两次(models 位过严、schedules 位与\n * schedule 方法的探测不同源)。\n */\n get capabilities(): WelcomeCapabilities {\n return capabilityBits(this.apiProxy, this.pushOutlet)\n }\n\n diagnostic(message: string): void {\n console.log(formatLogLine('info', message));\n }\n\n currentCursor(): number {\n return this.cursor;\n }\n\n // ---------- sinks ----------\n\n addSink(sink: BridgeSink): void {\n this.sinks.add(sink);\n }\n\n removeSink(sink: BridgeSink): void {\n this.sinks.delete(sink);\n this.dropSinkSessions(sink);\n }\n\n /** Whether the ring still holds everything after the cursor. */\n canResumeFrom(cursor: number): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n // A cursor ahead of this process belongs to an older bridge lifetime.\n // Treat it as a gap instead of incorrectly claiming a successful resume.\n return cursor <= this.cursor && cursor + 1 >= oldest;\n }\n\n private sinkSessions = new Map<BridgeSink, Set<string>>();\n private lastAssistantText = new Map<string, string>();\n\n /** Mark a sink as actively viewing a session (suppresses its turn notifications). */\n markSinkOpen(sink: BridgeSink, sessionId: string): void {\n let set = this.sinkSessions.get(sink);\n if (!set) { set = new Set(); this.sinkSessions.set(sink, set); }\n set.add(sessionId);\n }\n\n markSinkClosed(sink: BridgeSink, sessionId: string): void {\n this.sinkSessions.get(sink)?.delete(sessionId);\n if (!this.hasStreamViewer(sessionId)) this.assistantStream.clearPending(sessionId);\n }\n\n dropSinkSessions(sink: BridgeSink): void {\n const sessions = this.sinkSessions.get(sink);\n this.sinkSessions.delete(sink);\n for (const sessionId of sessions ?? []) {\n if (!this.hasStreamViewer(sessionId)) this.assistantStream.clearPending(sessionId);\n }\n }\n\n private hasStreamViewer(sessionId: string): boolean {\n if (this.disposed || this.subagentSessionIds.has(sessionId)) return false;\n return [...this.sinks].some(sink => this.isViewedBy(sink, sessionId) && sink.canReceive('sessions.read'));\n }\n\n /** Instant publications bypass record(): no cursor, ring, notification or push side effect. */\n private pushAssistantDelta(payload: AssistantStreamDelta): void {\n if (this.disposed || this.subagentSessionIds.has(payload.sessionId)) return;\n for (const sink of this.sinks) {\n if (this.isViewedBy(sink, payload.sessionId) && sink.canReceive('sessions.read')) {\n sink.push('s2c.session.delta', payload);\n }\n }\n }\n\n private isViewedBy(sink: BridgeSink, sessionId: string): boolean {\n return this.sinkSessions.get(sink)?.has(sessionId) ?? false;\n }\n\n /** F-9: when a notification-worthy event fires, mirror it to every\n * online device that is not currently viewing the session (the s2c.notify\n * frame counts toward the seq cursor and joins the replay ring per\n * PROTOCOL §6 + §7), then fan the same payload out to offline devices\n * holding an APNs token. */\n private emitNotify(args: {\n sessionId: string\n category: NotifyCategory\n title: string\n /** Host 原文(可能是 Markdown),出站前在这里统一转纯文本。 */\n body: string\n /** body 清洗后为空(如回复只有代码块)时改用的纯文本,例如会话标题。 */\n fallbackBody?: string\n notificationId: string\n toolName?: string\n detail?: string\n }): void {\n const turnEnd = args.category === 'turn.completed' || args.category === 'session.error';\n if (turnEnd && this.subagentSessionIds.has(args.sessionId)) return;\n // 回合结束取回复最后一段(通常是结论);审批原因与提问原文合成一行。\n const plain = notificationPlainText(args.body, turnEnd ? 'last' : 'all') || (args.fallbackBody ?? '');\n const body = plain.length > 120 ? plain.slice(0, 119) + '…' : plain;\n // 展开页所需的工具名与详情只随审批通知下发;正文 120 字的上限不动,详情另有 600 字上限。\n const approvalExtras = args.category === 'approval.required' && args.toolName\n ? { toolName: args.toolName.slice(0, 80), ...(args.detail ? { detail: args.detail.slice(0, 600) } : {}) }\n : {};\n this.record(\n 's2c.notify',\n {\n notificationId: args.notificationId,\n category: args.category,\n sessionId: args.sessionId,\n title: args.title,\n body,\n ...approvalExtras,\n ts: Date.now(),\n },\n (sink) => this.isViewedBy(sink, args.sessionId),\n );\n this.fanOutPush({\n notificationId: args.notificationId,\n category: args.category,\n sessionId: args.sessionId,\n title: args.title,\n body,\n ...approvalExtras,\n });\n }\n\n /** F-9: when a turn completes, notify every device not viewing the session. */\n private emitTurnCompletedNotify(sessionId: string, ok: boolean): void {\n if (this.subagentSessionIds.has(sessionId)) return;\n\n const row = this.summaries.get(sessionId);\n const title = ok ? '任务完成' : '任务异常结束';\n this.emitNotify({\n sessionId,\n category: ok ? 'turn.completed' : 'session.error',\n title,\n body: this.lastAssistantText.get(sessionId) ?? '',\n fallbackBody: row?.title ?? '',\n notificationId: 'n-' + (this.cursor + 1),\n });\n }\n\n /**\n * Mirror one notification-worthy event to offline devices. Fire-and-forget:\n * push failures must never block or break the WS data plane.\n */\n private fanOutPush(notification: PushNotification): void {\n try {\n this.pushOutlet?.fanOut(notification);\n } catch {\n // outlet misbehavior must not take down the bridge\n }\n }\n\n\n /**\n * Remember the latest assistant text so notifications can quote it. 存完整原文:\n * 按字符数截尾会从链接地址中间切开(横幅只剩 URL 编码的路径),段落选择与\n * Markdown 清洗统一留到 emitNotify。条目随 sessions.list / 归档清理。\n */\n private captureAssistantText(sessionId: string, event: SessionEventLike): void {\n if (event.type !== 'assistant/message') return;\n const text = messageText(event.data).trim();\n if (text.length > 0) this.lastAssistantText.set(sessionId, text);\n }\n\n /**\n * Replay buffered pushes after the given cursor; false when the gap is\n * unrecoverable. Frames go to `target` only — replaying into every sink\n * duplicated the whole window onto devices that never asked for it.\n * Each frame is filtered by the S→C permission policy per sink, so a\n * reader without interactions.respond never gets the missed approval\n * frames back (R1/P2).\n */\n resumeFrom(cursor: number, target?: BridgeSink): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n if (cursor + 1 < oldest) return false;\n const receivers = target !== undefined ? [target] : [...this.sinks];\n for (const entry of this.ring) {\n if (entry.seq > cursor) {\n for (const sink of receivers) {\n const need = pushScopeFor(entry.type, entry.payload)\n if (need === undefined || !sink.canReceive(need)) continue\n sink.replay([entry]);\n }\n }\n }\n for (const sink of receivers) sink.replayDone();\n return true;\n }\n\n refreshLiveActivities(): void {\n try { this.pushOutlet?.liveActivityChanged?.(this.listSessions()) } catch { /* independent delivery */ }\n }\n\n private record(type: string, payload: unknown, except?: (sink: BridgeSink) => boolean): void {\n if (this.disposed) return\n if (type === 's2c.sessions.delta') this.refreshLiveActivities()\n if (type === 's2c.sessions.delta' || type.startsWith('s2c.pending.')) {\n const fingerprint = JSON.stringify([\n this.listSessions().map(({ id, title, status, lastActivityTs, todos, todoItems, pendingApproval, pendingQuestion }) =>\n ({ id, title, status, lastActivityTs, todos, todoItems, pendingApproval, pendingQuestion })),\n this.approvals.size, this.questions.size,\n ])\n if (fingerprint !== this.widgetFingerprint) {\n this.widgetFingerprint = fingerprint\n try { this.pushOutlet?.widgetChanged?.() } catch { /* push cannot break the data plane */ }\n }\n }\n this.cursor += 1;\n const entry = { seq: this.cursor, type, payload };\n this.ring.push(entry);\n if (this.ring.length > this.historyBufferMax) {\n this.ring.splice(0, this.ring.length - this.historyBufferMax);\n }\n const need = pushScopeFor(type, payload)\n for (const sink of this.sinks) {\n if (need === undefined || !sink.canReceive(need)) continue\n if (except && except(sink)) continue\n sink.push(type, payload, entry.seq);\n }\n }\n\n\n // ---------- lifecycle ----------\n\n /** Start consuming host + mux streams. Idempotent; aborts on dispose(). */\n start(): void {\n if (this.started || this.disposed) return\n this.started = true\n void this.runHostStream();\n void this.runMuxStream();\n void this.refreshSummaries();\n }\n\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.abort.abort();\n this.assistantStream.dispose();\n this.sinks.clear();\n this.sinkSessions.clear();\n this.pushOutlet = undefined;\n }\n\n private async runHostStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.host({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onHostFrame(frame);\n }\n } catch {\n // stream ended (dispose or host shutdown); bridges degrade to RPC-only\n }\n }\n\n private async runMuxStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.mux({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onMuxFrame(frame);\n }\n } catch {\n // ditto\n } finally {\n this.assistantStream.dispose();\n }\n }\n\n private onHostFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'host/session-added':\n case 'host/session-removed':\n case 'host/workspace-changed':\n case 'host/workspace-removed':\n case 'host/workspace-order-changed':\n void this.refreshSummaries();\n break;\n case 'host/archived-sessions-changed': {\n const archived = (frame as { archivedSessionIds?: unknown }).archivedSessionIds\n if (Array.isArray(archived)) {\n this.archivedSessionIds = new Set(archived.map(String))\n }\n void this.refreshSummaries();\n break;\n }\n case 'host/schedule-changed':\n this.record('s2c.schedule.changed', {})\n break\n case 'host/session-status': {\n const p = frame as { sessionId?: string; running?: boolean };\n const row = this.summaries.get(String(p.sessionId));\n if (row && typeof p?.running === 'boolean') {\n row.status = p.running ? 'running' : 'idle';\n this.pushSummary(row);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private onMuxFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'skills/changed':\n this.record('s2c.skills.changed', {})\n break;\n case 'commands/changed':\n this.record('s2c.commands.changed', {})\n break;\n case 'session/assistant-stream': {\n const sessionId = String(frame.sessionId ?? '');\n if (frame.isSubagent === true) this.subagentSessionIds.add(sessionId);\n if (this.subagentSessionIds.has(sessionId)) {\n this.assistantStream.abandon(sessionId);\n break;\n }\n this.assistantStream.accept(sessionId, frame.value);\n break;\n }\n case 'session/assistant-stream-disposed':\n this.assistantStream.abandon(String(frame.sessionId ?? ''));\n break;\n case 'session/event': {\n const event = frame.event as SessionEventLike | undefined;\n const sessionId = String(frame.sessionId ?? '');\n if (!event || !sessionId) break;\n if (frame.isSubagent === true) this.subagentSessionIds.add(sessionId);\n if (this.subagentSessionIds.has(sessionId)) {\n this.assistantStream.abandon(sessionId);\n break;\n }\n const streamId = this.assistantStream.beforeEvent(sessionId, event);\n if (event.type === 'turn/end') this.assistantStream.abandon(sessionId);\n if (event.type === 'agent-preset/selected') {\n this.record('s2c.skills.changed', { sessionId });\n }\n this.noteActivity(sessionId, event);\n this.captureAssistantText(sessionId, event);\n const projection = projectEvent(sessionId, event);\n this.captureActivity(sessionId, event, projection?.data.tool as { name?: string; summary?: string } | undefined);\n if (projection) {\n this.record('s2c.session.event', { sessionId, kind: projection.kind, seq: event.seq,\n data: { ...projection.data, ...(streamId && projection.kind === 'message.final' ? { streamId } : {}) } });\n }\n if (projection?.kind === 'turn.end') {\n this.emitTurnCompletedNotify(sessionId, projection.data.ok === true);\n }\n break;\n }\n case 'session/projection': {\n const p = frame as { sessionId?: string; key?: string; value?: unknown };\n if (!p.sessionId) break;\n if (p.key === 'workingDirectory' || p.key === 'agentPreset') {\n this.record('s2c.skills.changed', { sessionId: p.sessionId });\n }\n if (p.key === 'modelSelection') {\n const next = (p.value as { next?: { provider?: unknown; model?: unknown; reasoningEffort?: unknown } } | undefined)?.next\n const current = next && typeof next.provider === 'string' && typeof next.model === 'string'\n ? { provider: next.provider, model: next.model, ...(typeof next.reasoningEffort === 'string' ? { reasoningEffort: next.reasoningEffort } : {}) }\n : undefined\n // 当前选择来自实时投影;目录缓存不能让其他客户端的模型切换长期滞留。\n this.record('s2c.models.changed', { sessionId: p.sessionId, ...(current ? { current } : {}) });\n }\n if (p.key === 'inbox') this.applyInbox(p.sessionId, p.value, 'live');\n this.applyProjection(p.sessionId, String(p.key ?? ''), p.value);\n break;\n }\n case 'approval/requested': {\n const p = frame as { sessionId?: string; approvalId?: string; toolName?: string; reason?: string; callId?: string };\n if (!p.approvalId || !frame.rpcId) break;\n const toolName = String(p.toolName ?? 'tool');\n const summary = String(p.reason ?? '');\n const sessionId = String(p.sessionId ?? '');\n this.approvals.set(p.approvalId, {\n rpcId: frame.rpcId,\n sessionId,\n toolName,\n reason: summary,\n });\n this.record('s2c.pending.approval', {\n requestId: p.approvalId,\n sessionId,\n toolName,\n summary,\n riskLevel: riskOf(toolName),\n });\n if (p.callId) void this.loadApprovalArguments(p.approvalId, p.callId);\n this.emitNotify({\n sessionId,\n category: 'approval.required',\n title: '需要批准',\n body: toolName + ': ' + summary,\n notificationId: 'apr-' + p.approvalId,\n toolName,\n detail: summary,\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'approval/resolved': {\n const p = frame as { approvalId?: string };\n if (!p.approvalId) break;\n const pending = this.approvals.get(p.approvalId);\n this.approvals.delete(p.approvalId);\n this.record('s2c.pending.cleared', { requestId: p.approvalId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n case 'question/requested': {\n const p = frame as { sessionId?: string; questions?: unknown };\n if (!frame.rpcId) break;\n const requestId = 'q-' + frame.rpcId;\n const sessionId = String(p?.sessionId ?? '');\n this.questions.set(requestId, { rpcId: frame.rpcId, sessionId, questions: p?.questions });\n this.record('s2c.pending.question', { requestId, sessionId, questions: p?.questions ?? [] });\n this.emitNotify({\n sessionId,\n category: 'question.asked',\n title: '有问题需要回答',\n body: firstQuestionText(p?.questions),\n notificationId: requestId,\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'question/resolved': {\n const p = frame as { questionRpcId?: string };\n if (!p.questionRpcId) break;\n const requestId = 'q-' + p.questionRpcId;\n const pending = this.questions.get(requestId);\n this.questions.delete(requestId);\n this.record('s2c.pending.cleared', { requestId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n default:\n break;\n }\n }\n\n\n // ---------- session mirror ----------\n\n async refreshSummaries(): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.list({ rpcId: randomUUID(), payload: {} });\n if (!response.result || !response.result.ok) {\n this.diagnostic('sessions.list rejected: ' + JSON.stringify(response.result ?? null).slice(0, 200));\n return;\n }\n let workspaces: WorkspaceViewLike[] = []\n const workspaceList = this.apiProxy.workspace?.list\n if (typeof workspaceList === 'function') {\n const workspaceResponse = await workspaceList.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: {},\n })\n if (workspaceResponse.result?.ok) {\n workspaces = workspaceResponse.result.value.items ?? []\n this.archivedSessionIds = new Set(\n (workspaceResponse.result.value.archivedSessionIds ?? []).map(String),\n )\n }\n }\n const previousIds = new Set(this.summaries.keys())\n const workspaceBySession = new Map<string, WorkspaceViewLike>()\n for (const workspace of workspaces) {\n for (const sessionId of workspace.sessionIds ?? []) {\n workspaceBySession.set(String(sessionId), workspace)\n }\n }\n const next = new Map<string, SessionSummary>();\n // Archived sessions stay out of the live list and its broadcast so\n // existing clients keep their current behaviour; they are mirrored here\n // and served on demand through c2s.sessions.archived.\n const nextArchived = new Map<string, SessionSummary>();\n for (const row of response.result.value.items ?? []) {\n if (isSubagentRow(row)) this.subagentSessionIds.add(row.sessionId);\n if (this.subagentSessionIds.has(row.sessionId)) continue\n const summary = toSummary(row, this.approvals, this.questions, workspaceBySession.get(row.sessionId));\n if (this.archivedSessionIds.has(row.sessionId)) {\n nextArchived.set(row.sessionId, { ...summary, archived: true });\n continue\n }\n next.set(row.sessionId, summary);\n }\n for (const [id, calls] of this.activeTools) {\n const row = next.get(id);\n if (!row || (row.status !== 'running' && !row.pendingApproval && !row.pendingQuestion)) this.activeTools.delete(id);\n else row.activity = [...calls.values()].at(-1) ?? null;\n }\n // Worker identity is immutable for this bridge lifetime. A stale list or\n // a removed worker must not erase identity learned from the event stream.\n this.summaries = next;\n this.archivedSummaries = nextArchived;\n const removedIds = [...previousIds].filter((id) => !next.has(id))\n // sessions.list is authoritative. This also removes assistant snippets\n // captured for child/subagent event streams that never enter summaries.\n for (const id of this.lastAssistantText.keys()) {\n if (!next.has(id)) this.lastAssistantText.delete(id)\n }\n this.record('s2c.sessions.delta', { upserted: [...next.values()], removedIds });\n } catch {\n // apiProxy absent or transient failure; keep last mirror\n }\n }\n\n /** Cold sessions may lack a title projection; fall back to first user text. */\n private deriveTitleFallback(sessionId: string, messages: MessageProjection[]): void {\n const row = this.summaries.get(sessionId);\n if (!row || row.title.length > 0) return;\n const firstUser = messages.find(m => m.role === 'user' && (m.text ?? '').trim().length > 0);\n if (!firstUser) return;\n row.title = firstUser!.text!.replace(/\\s+/g, ' ').trim().slice(0, 60);\n this.pushSummary(row);\n }\n\n private captureActivity(sessionId: string, event: SessionEventLike, tool?: { name?: string; summary?: string }): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n const data = event.data as { callId?: string } | undefined;\n if (event.type === 'tool/call' && data?.callId) {\n const calls = this.activeTools.get(sessionId) ?? new Map<string, string>();\n const detail = [tool?.name, tool?.summary].filter(Boolean).join(': ');\n calls.set(data.callId, Array.from(detail).slice(0, 160).join(''));\n if (calls.size > 64) calls.delete(calls.keys().next().value!);\n this.activeTools.set(sessionId, calls);\n } else if (event.type === 'tool/result' && data?.callId) {\n this.activeTools.get(sessionId)?.delete(data.callId);\n } else if (event.type === 'turn/start' || event.type === 'turn/end') {\n this.activeTools.delete(sessionId);\n } else return;\n const activity = [...(this.activeTools.get(sessionId)?.values() ?? [])].at(-1) ?? null;\n if (row.activity === activity) return;\n row.activity = activity;\n // Tool progress must not reorder the session list or trigger Widget pushes.\n this.pushSummary(row);\n }\n\n private noteActivity(sessionId: string, event?: SessionEventLike): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n // Recency must reflect USER-VISIBLE milestones only: a new prompt moves\n // a session to the top and a finished turn settles it into place. Token\n // chunks / tool calls / per-step assistant messages fire constantly while\n // an agent works; counting them made concurrently running sessions fight\n // over the top slot in every recency-sorted list.\n switch (event?.type) {\n case 'user/message':\n case 'turn/start':\n case 'turn/end':\n break;\n default:\n return;\n }\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n\n /**\n * 折叠一次 inbox 投影。只有实时变化才广播 s2c.session.queue;基线只补缓存,\n * 打开会话的那台设备另由 pushQueueSnapshot 点对点拿到快照。\n *\n * 基线只补缺不覆盖:实时通知带的是完整投影值,收到过就以它为准;基线读取与\n * 实时变化并发时,较早算出的基线不得盖掉较新的实时值。\n */\n private applyInbox(sessionId: string, value: unknown, origin: 'live' | 'baseline'): void {\n if (this.capabilities.promptQueue !== true) return;\n // 子代理会话由父会话路由,不向手机提供队列。\n if (this.subagentSessionIds.has(sessionId)) return;\n if (origin === 'baseline' && this.queues.has(sessionId)) return;\n const items = projectQueue(value);\n // 读不懂的值不当作空队列:客户端会把消失的项当成已发出。\n if (items === undefined) return;\n const fingerprint = JSON.stringify(items);\n const previous = this.queues.get(sessionId);\n this.queues.set(sessionId, { items, fingerprint });\n if (origin === 'live' && previous?.fingerprint !== fingerprint) {\n this.record('s2c.session.queue', { sessionId, items });\n }\n }\n\n /**\n * 打开会话时补发的快照:不占 seq,与 tail 同属打开响应。还没读到过该会话的\n * 投影(基线失败、Host 不提供或子代理会话)时不发,因为未知不等于空队列。\n */\n private pushQueueSnapshot(sink: BridgeSink, sessionId: string): void {\n if (this.disposed) return;\n const queue = this.queues.get(sessionId);\n if (queue === undefined) return;\n sink.push('s2c.session.queue', { sessionId, items: queue.items });\n }\n\n private applyProjection(sessionId: string, key: string, value: unknown): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n if (key === 'title') {\n row.title = typeof value === 'string' ? value : '';\n } else if (key === 'todos') {\n const items = Array.isArray(value) ? value as Array<{ content?: unknown; status?: unknown }> : null;\n const sanitized = sanitizeTodoItems(items);\n row.todoItems = sanitized.length > 0 ? sanitized : null;\n row.todos = sanitized.length > 0\n ? { done: sanitized.filter(i => i.status === 'completed').length, total: sanitized.length }\n : null;\n } else if (key === 'sessionListMetadata') {\n const meta = value as { lastPromptAt?: number } | null;\n if (meta?.lastPromptAt) row.lastActivityTs = Math.max(row.lastActivityTs, meta.lastPromptAt);\n } else if (key === 'sessionStats' || key === 'tokenUsage') {\n // Host usage projections arrive as two independent keys over the mux\n // stream; merge each patch into the summary's stats snapshot so a\n // sessionStats-only or tokenUsage-only host still shows partial data.\n const patch = sanitizeUsageStatsPatch(key, value);\n if (!patch) return;\n row.stats = { ...emptyUsageStats, ...(row.stats ?? {}), ...patch };\n } else {\n return;\n }\n this.pushSummary(row);\n }\n\n private bumpPendingFlags(sessionId: string): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n let approval = false;\n for (const pending of this.approvals.values()) {\n if (pending.sessionId === sessionId) approval = true;\n }\n let question = false;\n for (const pending of this.questions.values()) {\n if (pending.sessionId === sessionId) question = true;\n }\n row.pendingApproval = approval;\n row.pendingQuestion = question;\n this.pushSummary(row);\n }\n\n private pushSummary(row: SessionSummary): void {\n this.record('s2c.sessions.delta', { upserted: [row], removedIds: [] });\n }\n\n listSessions(): SessionSummary[] {\n return [...this.summaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n /**\n * Archived rows from the last sessions.list, newest first. Served only on\n * demand so the live session list and its broadcast keep their shape for\n * clients that predate `c2s.sessions.archived`.\n */\n listArchivedSessions(): SessionSummary[] {\n return [...this.archivedSummaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n /**\n * Complete transient interaction state. Unlike the replay ring, this remains\n * authoritative after a long disconnect and is rehydrated by apiProxy's mux\n * stream when the bridge itself restarts.\n */\n pendingSnapshot(): PendingSnapshotPayload {\n return {\n approvals: [...this.approvals.entries()].map(([requestId, pending]) => ({\n requestId,\n sessionId: pending.sessionId,\n toolName: pending.toolName,\n summary: pending.reason,\n ...(pending.toolArguments !== undefined ? { toolArguments: pending.toolArguments } : {}),\n riskLevel: riskOf(pending.toolName),\n })),\n questions: [...this.questions.entries()].map(([requestId, pending]) => ({\n requestId,\n sessionId: pending.sessionId,\n questions: Array.isArray(pending.questions)\n ? pending.questions as PendingSnapshotPayload['questions'][number]['questions']\n : [],\n })),\n };\n }\n\n /** Resolve only the exact invocation in this session; never guess from the latest tool. */\n private async loadApprovalArguments(requestId: string, callId: string): Promise<void> {\n const pending = this.approvals.get(requestId);\n if (!pending) return;\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId: pending.sessionId, maxMessages: 200 },\n });\n if (this.disposed || this.approvals.get(requestId) !== pending || !response.result?.ok) return;\n const event = (response.result.value.events ?? []).map(row => row.event).find(event => {\n const data = event.data as { callId?: string; name?: string } | undefined;\n return event.type === 'tool/call' && data?.callId === callId && data?.name === pending.toolName;\n });\n const args = (event?.data as { arguments?: unknown } | undefined)?.arguments;\n if (typeof args !== 'string' || !args.trim()) return;\n pending.toolArguments = args;\n this.record('s2c.pending.approval', {\n requestId, sessionId: pending.sessionId, toolName: pending.toolName,\n summary: pending.reason, riskLevel: riskOf(pending.toolName), toolArguments: args,\n });\n } catch {\n // Keep the approval answerable with explicitly unavailable execution details.\n }\n }\n\n // ---------- data operations ----------\n\n /** Tail history for an opened session; pushes s2c.session.tail to the sink. */\n async openSession(sink: BridgeSink, sessionId: string, tailCount: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, maxMessages: clampTail(tailCount) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const projected = projectHistory(result.events ?? []);\n const page = limitSessionPageMessages(projected);\n const messages = page.messages;\n const oldestSeq = messages.length > 0 ? messages[0]!.seq : 0;\n sink.push('s2c.session.tail', {\n sessionId,\n messages,\n oldestSeq,\n hasMore: Boolean(result.hasMore) || page.dropped > 0,\n });\n this.deriveTitleFallback(sessionId, messages);\n // DSH exposes a complete per-session projection baseline; fold it\n // into the summary row when the phone opens a session so the first paint\n // does not depend on list-row hints or frames missed while offline.\n // The queue snapshot follows the baseline because a bridge that has not\n // yet seen a live inbox change for this session only learns it there.\n void this.refreshProjections(sessionId).then(() => this.pushQueueSnapshot(sink, sessionId));\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Pull one session's projection baseline and fold every key\n * through applyProjection — unknown keys are ignored there, `null` means the\n * session no longer exists, and any failure degrades to a diagnostic: opening\n * a session must never fail because a baseline could not be read.\n */\n private async refreshProjections(sessionId: string): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.projections({\n rpcId: randomUUID(),\n payload: { sessionId },\n });\n if (this.disposed) return;\n const result = response.result;\n if (!result) return;\n if (!result.ok) {\n this.diagnostic('sessions.projections failed: ' + result.error.code);\n return;\n }\n const baseline = result.value;\n if (baseline === null || baseline === undefined) {\n this.queues.delete(sessionId);\n return;\n }\n for (const [key, value] of Object.entries(baseline.values ?? {})) {\n if (key === 'inbox') this.applyInbox(sessionId, value, 'baseline');\n this.applyProjection(sessionId, key, value);\n }\n } catch (error) {\n this.diagnostic('sessions.projections threw: ' + String((error as { message?: unknown })?.message ?? error));\n }\n }\n\n async historyPage(\n sessionId: string,\n beforeSeq: number,\n limit: number,\n ): Promise<{ sessionId: string; messages: MessageProjection[]; hasMore: boolean } | null> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, beforeSeq, maxMessages: clampTail(limit) },\n });\n if (!response.result || !response.result.ok) return null;\n const result = response.result.value;\n // Some host versions include the boundary event even though beforeSeq\n // is exclusive. Enforce the phone protocol here so a client can never\n // request and append the same page indefinitely.\n const projected = projectHistory(result.events ?? [])\n .filter((message) => message.seq < beforeSeq);\n const page = limitSessionPageMessages(projected);\n return {\n sessionId,\n messages: page.messages,\n // An empty exclusive page cannot advance beforeSeq. Advertising more\n // here would make the client request the same boundary forever.\n hasMore: page.messages.length > 0 && (Boolean(result.hasMore) || page.dropped > 0),\n };\n } catch {\n return null;\n }\n }\n\n /** Result of one attachment read-back for the phone. */\n async attachmentData(\n sessionId: string,\n attachmentId: string,\n ): Promise<{ mediaType?: string; data: string } | null> {\n const read = this.apiProxy.sessions.attachment\n if (typeof read !== 'function') return null;\n try {\n const response = await read.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, attachmentId },\n });\n if (!response.result || !response.result.ok) return null;\n const data = response.result.value.data;\n if (typeof data !== 'string' || data.length === 0) return null;\n return {\n ...(typeof response.result.value.attachment?.mediaType === 'string'\n ? { mediaType: response.result.value.attachment.mediaType as string }\n : {}),\n data,\n };\n } catch {\n return null;\n }\n }\n\n async commandOperation(operation: 'list' | 'execute' | 'permissions' | 'feedback', payload: Record<string, unknown>): Promise<SessionManagementResult<unknown>> {\n const api = this.apiProxy.commands\n if (!api) return { ok: false, code: 'E_UNSUPPORTED', message: '当前主机不支持手机指令' }\n try {\n const invoke = api[operation] as (request: { rpcId: string; payload: Record<string, unknown> }) => Promise<{ result?: { ok: boolean; value?: unknown; error?: { code: string; message?: string } } }>\n const response = await invoke.call(api, { rpcId: randomUUID(), payload })\n if (response.result?.ok) return { ok: true, value: response.result.value ?? {} }\n // 指令域的失败一律过域表:原先这里无条件回落 E_INTERNAL,于是「指令不存在」\n // 与「主机内部错误」在手机上是同一个码,客户端没法据此分支。\n return wireErrorOf('command', response.result?.error ?? { code: 'command/invalid-response' })\n } catch (error) {\n return wireErrorOf('command', hostFailureOf(error, 'command/invalid-response'))\n }\n }\n\n async exportSession(sessionId: string, signal: AbortSignal, format?: import('./protocol.ts').SessionExportFormat): Promise<Response> {\n const api = this.apiProxy.commands\n if (!api) throw Object.assign(new Error('当前主机不支持日志导出'), { code: 'command/export-unsupported' })\n return api.export({ rpcId: randomUUID(), payload: { sessionId, ...(format === undefined ? {} : { format }) } }, signal)\n }\n\n /** 由会话目录决定技能可见性;不读取正文,也不把本机路径发给移动端。 */\n async sessionSkills(sessionId: string): Promise<SessionManagementResult<{ skills: import('./protocol.ts').SkillSummary[] }>> {\n const read = this.apiProxy.sessions.skills\n if (!read) return { ok: false, code: 'E_UNSUPPORTED', message: '当前主机不支持技能目录' }\n try {\n const response = await read.call(this.apiProxy.sessions, { rpcId: randomUUID(), payload: { sessionId } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: '技能目录响应无效' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n const seen = new Set<string>()\n const skills = response.result.value.skills.map(skill => {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(skill.name) || seen.has(skill.name) ||\n typeof skill.description !== 'string' || typeof skill.modelInvocable !== 'boolean') {\n throw new Error('技能目录条目无效')\n }\n seen.add(skill.name)\n return { name: skill.name, description: skill.description, modelInvocable: skill.modelInvocable }\n })\n return { ok: true, value: { skills } }\n } catch {\n return { ok: false, code: 'E_INTERNAL', message: '无法读取当前会话的技能目录' }\n }\n }\n\n async sessionModels(sessionId: string): Promise<ModelBridgeResult<HostSessionModels>> {\n const models = this.apiProxy.sessions.models\n if (typeof models !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'model catalog unavailable on this host version' }\n }\n try {\n const response = await models.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'model catalog returned no result' }\n if (!response.result.ok) return wireErrorOf('model', response.result.error)\n return { ok: true, value: projectSessionModels(response.result.value) }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async selectSessionModel(\n sessionId: string,\n selection: HostModelSelection,\n ): Promise<ModelBridgeResult<HostModelSelection>> {\n const selectModel = this.apiProxy.sessions.selectModel\n if (typeof selectModel !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'model selection unavailable on this host version' }\n }\n try {\n const response = await selectModel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: {\n sessionId,\n provider: selection.provider,\n model: selection.model,\n ...(selection.reasoningEffort ? { reasoningEffort: selection.reasoningEffort } : {}),\n },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'model selection returned no result' }\n if (!response.result.ok) return wireErrorOf('model', response.result.error)\n return { ok: true, value: { ...response.result.value.selected } }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async renameSession(sessionId: string, title: string): Promise<SessionManagementResult<string>> {\n const rename = this.apiProxy.sessions.rename\n if (typeof rename !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session rename unavailable on this host version' }\n }\n try {\n const response = await rename.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, title },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session rename returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n const acceptedTitle = String(response.result.value.title)\n const row = this.summaries.get(sessionId)\n if (row) {\n row.title = acceptedTitle\n this.pushSummary(row)\n }\n return { ok: true, value: acceptedTitle }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async archiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const archive = this.apiProxy.workspace?.archiveSession\n if (typeof archive !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session archive unavailable on this host version' }\n }\n try {\n const response = await archive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session archive returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n this.summaries.delete(sessionId)\n this.lastAssistantText.delete(sessionId)\n this.record('s2c.sessions.delta', { upserted: [], removedIds: [sessionId] })\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async unarchiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const unarchive = this.apiProxy.workspace?.unarchiveSession\n if (typeof unarchive !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session restore unavailable on this host version' }\n }\n try {\n const response = await unarchive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session restore returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n // Rebuild both mirrors from the Host instead of hand-moving the row:\n // refreshSummaries re-emits s2c.sessions.delta, which carries the restored\n // session back to the client's live list.\n await this.refreshSummaries()\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n /**\n * 删除或转向一条排队项。ack 只表示 Host 已提交变更,真实状态以随后的\n * s2c.session.queue 为准;不做幂等,项已消失时 Host 回 queue-item-not-found。\n */\n async updateQueue(\n sessionId: string,\n itemId: string,\n action: 'remove' | 'steer',\n ): Promise<SessionManagementResult<{ sessionId: string; itemId: string }>> {\n const update = this.apiProxy.sessions.updateQueue\n if (typeof update !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'prompt queue unavailable on this host version' }\n }\n try {\n const response = await update.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, itemId, action: { kind: action } },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'queue update returned no result' }\n if (!response.result.ok) return wireErrorOf('queue', response.result.error)\n return { ok: true, value: { sessionId, itemId } }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async cancelSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const cancel = this.apiProxy.sessions.cancel\n if (typeof cancel !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'session cancel unavailable on this host version' }\n }\n try {\n const response = await cancel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session cancel returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async listWorkspaces(): Promise<SessionManagementResult<Array<{\n id: string; title: string; path: string; sessionIds: string[]\n }>>> {\n const list = this.apiProxy.workspace?.list\n if (typeof list !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'workspace list unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.workspace, { rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'workspace list returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: (response.result.value.items ?? []).map(projectWorkspace) }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async createWorkspace(path: string): Promise<SessionManagementResult<{\n workspace: { id: string; title: string; path: string; sessionIds: string[] }; created: boolean\n }>> {\n const create = this.apiProxy.workspace?.create\n if (typeof create !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'workspace create unavailable on this host version' }\n }\n try {\n const response = await create.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { path },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'workspace create returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n await this.refreshSummaries()\n return {\n ok: true,\n value: { workspace: projectWorkspace(response.result.value.workspace), created: response.result.value.created === true },\n }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async listDirectory(path?: string): Promise<SessionManagementResult<DirectoryListingLike>> {\n const list = this.apiProxy.host?.listDirectory\n if (typeof list !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'directory browsing unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: path && path.trim().length > 0 ? { path } : {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'directory list returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async pickDirectory(): Promise<SessionManagementResult<string | null>> {\n const pick = this.apiProxy.host?.pickDirectory\n if (typeof pick !== 'function') {\n return { ok: false, code: 'E_UNSUPPORTED', message: 'native directory picker unavailable on this host version' }\n }\n try {\n const response = await pick.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'directory picker returned no result' }\n if (!response.result.ok) return wireErrorOf('session', response.result.error)\n return { ok: true, value: response.result.value.path }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n /** Create a fresh blank session in an existing workspace or legacy cwd. */\n async createSession(destination: { workspaceId?: string; cwd?: string } = {}): Promise<string | null> {\n try {\n const response = await this.apiProxy.sessions.create({\n rpcId: randomUUID(),\n payload: {\n ...(destination.workspaceId?.trim() ? { workspaceId: destination.workspaceId.trim() } : {}),\n ...(destination.cwd?.trim() ? { cwd: destination.cwd.trim() } : {}),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const sessionId = response.result.value.sessionId;\n await this.refreshSummaries();\n return sessionId;\n } catch {\n return null;\n }\n }\n\n async forkSession(\n deviceId: string,\n payload: { clientRequestId: string; sessionId: string; atSeq?: number },\n ): Promise<ScheduleBridgeResult<{ sessionId: string }>> {\n const fork = this.apiProxy.sessions.fork\n if (typeof fork !== 'function') return { ok: false, code: 'E_UNSUPPORTED', message: 'session fork unavailable on this host version' }\n if (!this.summaries.has(payload.sessionId)) return { ok: false, code: 'E_NOT_FOUND', message: 'session not found' }\n if (this.archivedSessionIds.has(payload.sessionId) || this.subagentSessionIds.has(payload.sessionId)) {\n return { ok: false, code: 'E_PROTOCOL', message: 'archived or subagent sessions cannot be forked' }\n }\n const result = await this.forkMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { sessionId: payload.sessionId, ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq } : {}) },\n async () => {\n const response = await fork.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId: payload.sessionId, ...(payload.atSeq !== undefined ? { atSeq: payload.atSeq } : {}) },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'session fork returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: { sessionId: response.result.value.sessionId } }\n },\n )\n if (result.ok) {\n await this.refreshSummaries()\n return { ok: true, value: result.value as { sessionId: string }, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n /**\n * 当前 adapter 是否宿主级计划任务模型。\n *\n * 能力位 `schedulesHost`(host-capabilities.ts)与新帧、旧帧映射全部读这一个\n * 判别位:探测只发生一次(DshApiProxy.resolveSchedule),这里不再看控制器\n * 方法,也不看版本号字符串。\n */\n private get hostSchedule(): HostScheduleApiLike | undefined {\n const schedule = this.apiProxy.schedule\n return schedule?.mode === 'host' ? schedule : undefined\n }\n\n async listSchedules(sessionId: string): Promise<ScheduleBridgeResult<ScheduleTaskViewLike[]>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n // 旧帧映射(PROTOCOL.md「旧帧在宿主级模型上的映射」):宿主级列表全量分页后\n // 过滤目标会话,反向投影成旧视图;旧 App 不需要升级。\n if (schedule.mode === 'host') return this.listHostSchedulesAsLegacy(schedule, sessionId)\n try {\n const response = await schedule.list({ rpcId: randomUUID(), payload: { sessionId } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule list returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value.tasks }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n /** 宿主级列表 -> 旧会话级视图:过滤 session 目标、丢弃 ended、按 scheduledAt 升序。 */\n private async listHostSchedulesAsLegacy(\n schedule: HostScheduleApiLike,\n sessionId: string,\n ): Promise<ScheduleBridgeResult<ScheduleTaskViewLike[]>> {\n try {\n const tasks: HostScheduleTaskView[] = []\n let offset = 0\n // 全量分页:`total` 是过滤后总数;空页或凑满 total 即收口,避免 Host 在\n // 翻页期间变更导致的死循环。\n for (;;) {\n const response = await schedule.list({\n rpcId: randomUUID(),\n payload: { limit: HOST_SCHEDULE_PAGE_SIZE, offset },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule list returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const page = response.result.value\n tasks.push(...page.tasks)\n offset += page.tasks.length\n if (page.tasks.length === 0 || offset >= page.total) break\n }\n const views: ScheduleTaskViewLike[] = []\n for (const task of tasks) {\n if (task.target.kind !== 'session' || task.target.sessionId !== sessionId) continue\n if (task.status === 'ended') continue\n const view = toLegacyScheduleView(task)\n if (view !== undefined) views.push(view)\n }\n views.sort((left, right) => left.scheduledAt < right.scheduledAt ? -1 : left.scheduledAt > right.scheduledAt ? 1 : 0)\n return { ok: true, value: views }\n } catch (error) {\n return wireErrorOf('schedule', hostFailureOf(error, 'internal'))\n }\n }\n\n async listHostSchedules(\n request: HostScheduleListRequestLike = {},\n ): Promise<ScheduleBridgeResult<HostScheduleListResultLike>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n try {\n const response = await schedule.list({ rpcId: randomUUID(), payload: request })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule host list returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return wireErrorOf('schedule', hostFailureOf(error, 'internal'))\n }\n }\n\n async viewHostSchedule(id: string): Promise<ScheduleBridgeResult<HostScheduleTaskView>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n try {\n const response = await schedule.view({ rpcId: randomUUID(), payload: { id } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule host view returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.kind === 'not-found') return { ok: false, code: 'E_NOT_FOUND', message: 'schedule not found' }\n return { ok: true, value: value.task }\n } catch (error) {\n return wireErrorOf('schedule', hostFailureOf(error, 'internal'))\n }\n }\n\n async createHostSchedule(\n deviceId: string,\n payload: Record<string, unknown> & {\n clientRequestId: string\n title: string\n prompt: string\n timing: unknown\n target?: unknown\n },\n ): Promise<ScheduleBridgeResult<HostScheduleTaskView>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n const { clientRequestId, ...request } = payload\n // 幂等:新帧与旧帧共用 scheduleMutations journal 与 clientRequestId;\n // journal 只存请求指纹,不落提醒正文。\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n request,\n async () => {\n const response = await schedule.create({\n rpcId: randomUUID(),\n payload: {\n title: request.title as string,\n prompt: request.prompt as string,\n timing: request.timing as HostScheduleTiming,\n ...(request.target !== undefined\n ? { target: request.target as HostScheduleTargetRequest }\n : {}),\n } satisfies HostScheduleCreateRequestLike,\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule host create returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as HostScheduleTaskView, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async editHostSchedule(\n deviceId: string,\n payload: Record<string, unknown> & {\n clientRequestId: string\n id: string\n action: 'update' | 'delete'\n expectedUpdatedAt?: string | null\n patch?: unknown\n },\n ): Promise<ScheduleBridgeResult<HostScheduleEditValue>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n const { clientRequestId, ...request } = payload\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n request,\n async () => {\n const expected = request.expectedUpdatedAt !== undefined\n ? { expectedUpdatedAt: request.expectedUpdatedAt as string | null }\n : {}\n const response = await schedule.edit({\n rpcId: randomUUID(),\n payload: request.action === 'update'\n ? { id: request.id as string, action: 'update', patch: request.patch as HostSchedulePatch, ...expected }\n : { id: request.id as string, action: 'delete', ...expected },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule host edit returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.kind === 'conflict') return { ok: false, code: 'E_BUSY', message: 'schedule changed since it was read' }\n if (value.kind === 'not-found') return { ok: false, code: 'E_NOT_FOUND', message: 'schedule not found' }\n if (request.action === 'delete') {\n if (value.kind !== 'deleted') return { ok: false, code: 'E_INTERNAL', message: 'schedule delete returned an updated task' }\n return { ok: true, value: { deleted: true as const, id: value.id } }\n }\n if (value.kind !== 'task') return { ok: false, code: 'E_INTERNAL', message: 'schedule update returned a deletion' }\n return { ok: true, value: { task: value.task } }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as HostScheduleEditValue, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async runHostSchedule(\n deviceId: string,\n payload: { clientRequestId: string; id: string },\n ): Promise<ScheduleBridgeResult<HostScheduleTaskView>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { id: payload.id },\n async () => {\n const response = await schedule.run({ rpcId: randomUUID(), payload: { id: payload.id } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule host run returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.kind === 'pending') return { ok: false, code: 'E_BUSY', message: 'schedule execution already pending' }\n if (value.kind === 'not-found') return { ok: false, code: 'E_NOT_FOUND', message: 'schedule not found' }\n return { ok: true, value: value.task }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as HostScheduleTaskView, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n /**\n * 删除宿主上全部已结束(`status=\"ended\"`)的任务,返回删除条数。\n *\n * 与其他变更帧共用 scheduleMutations journal:同一 clientRequestId 重放时直接回放\n * 首次结果,不会再删一次。成功后广播 `s2c.schedule.changed`;失败时同样广播——上游\n * 文档说明失败的批次无法确定哪些行已删除,客户端应重新读取列表。\n */\n async deleteEndedHostSchedules(\n deviceId: string,\n payload: { clientRequestId: string },\n ): Promise<ScheduleBridgeResult<{ deleted?: number }>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { operation: 'deleteEnded' },\n async () => {\n const response = await schedule.deleteEnded({ rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule delete ended returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n },\n )\n this.record('s2c.schedule.changed', {})\n if (result.ok) {\n // schedule journal 不落盘成功值(persistValues=false),重放时拿不到条数,只回 replayed。\n const value = result.value as { deleted: number } | undefined\n return { ok: true, value: value ?? {}, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async scheduleLimits(): Promise<ScheduleBridgeResult<{ newSessionIntervalSeconds: number }>> {\n const schedule = this.hostSchedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'host-level schedules unavailable on this host version' }\n try {\n const response = await schedule.limits({ rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule limits returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return wireErrorOf('schedule', hostFailureOf(error, 'internal'))\n }\n }\n\n async scheduleHistory(\n sessionId: string,\n id: string,\n limit: number,\n before?: string,\n ): Promise<ScheduleBridgeResult<ScheduleHistoryViewLike>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n // 宿主级模型下 id 是任务的唯一标识,sessionId 被忽略(旧帧映射表)。\n if (schedule.mode === 'host') {\n try {\n const response = await schedule.history({\n rpcId: randomUUID(),\n payload: { id, limit, ...(before !== undefined ? { before } : {}) },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule history returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return wireErrorOf('schedule', hostFailureOf(error, 'internal'))\n }\n }\n try {\n const response = await schedule.history({\n rpcId: randomUUID(),\n payload: { sessionId, id, limit, ...(before !== undefined ? { before } : {}) },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule history returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value.history }\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) }\n }\n }\n\n async createSchedule(\n deviceId: string,\n payload: Record<string, unknown> & { sessionId: string; clientRequestId: string; title: string; prompt: string },\n ): Promise<ScheduleBridgeResult<ScheduleTaskViewLike>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n // 旧帧映射:选择器 -> timing,target 固定为请求里的会话;成功回旧视图。\n if (schedule.mode === 'host') {\n const { clientRequestId, sessionId, title, prompt, ...selectors } = payload\n const timing = hostTimingFromLegacySelector(selectors)\n if (timing === undefined) return { ok: false, code: 'E_PROTOCOL', message: 'unsupported schedule selector' }\n const request = { title, prompt, timing, target: { kind: 'session', sessionId } satisfies HostScheduleTargetRequest }\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n request,\n async () => {\n const response = await schedule.create({\n rpcId: randomUUID(),\n payload: {\n title,\n prompt,\n timing,\n target: { kind: 'session', sessionId },\n } satisfies HostScheduleCreateRequestLike,\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule create returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const legacy = toLegacyScheduleView(response.result.value)\n if (legacy === undefined) return { ok: false, code: 'E_PROTOCOL', message: 'schedule cannot be represented in the session-scoped view' }\n return { ok: true, value: legacy }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as ScheduleTaskViewLike, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n const { clientRequestId, sessionId, ...request } = payload\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n { sessionId, request },\n async () => {\n const response = await schedule.create({ rpcId: randomUUID(), payload: { sessionId, ...request } })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule create returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n return { ok: true, value: response.result.value }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as ScheduleTaskViewLike, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async updateSchedule(\n deviceId: string,\n payload: Record<string, unknown> & { sessionId: string; id: string; clientRequestId: string; expected: unknown },\n ): Promise<ScheduleBridgeResult<ScheduleTaskViewLike>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n // 旧帧映射(尽力而为的冲突保护):先读当前任务,与 expected 不一致回 E_BUSY;\n // 一致则把 title/prompt/change 翻成 patch 走 edit。\n if (schedule.mode === 'host') {\n const request = {\n sessionId: payload.sessionId,\n id: payload.id,\n expected: payload.expected,\n title: payload.title,\n prompt: payload.prompt,\n change: payload.change,\n }\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n request,\n async () => {\n const current = await schedule.view({ rpcId: randomUUID(), payload: { id: payload.id } })\n if (!current.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule view returned no result' }\n if (!current.result.ok) return wireErrorOf('schedule', current.result.error)\n if (current.result.value.kind === 'not-found') return { ok: false, code: 'E_NOT_FOUND', message: 'schedule not found' }\n const currentTask = current.result.value.task\n if (!legacyScheduleMatchesExpected(currentTask, payload.expected)) {\n return { ok: false, code: 'E_BUSY', message: 'schedule changed since it was read' }\n }\n const patch = hostPatchFromLegacyUpdate(payload)\n if (Object.keys(patch).length === 0) {\n // 旧帧允许只带 expected 的无操作更新;宿主级 edit 要求 patch 非空,\n // 因此按成功返回当前任务,不写 Host。\n const legacy = toLegacyScheduleView(currentTask)\n if (legacy === undefined) return { ok: false, code: 'E_PROTOCOL', message: 'schedule cannot be represented in the session-scoped view' }\n return { ok: true, value: legacy }\n }\n const response = await schedule.edit({\n rpcId: randomUUID(),\n payload: { id: payload.id, action: 'update', patch },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule edit returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.kind === 'conflict') return { ok: false, code: 'E_BUSY', message: 'schedule changed since it was read' }\n if (value.kind === 'not-found') return { ok: false, code: 'E_NOT_FOUND', message: 'schedule not found' }\n if (value.kind !== 'task') return { ok: false, code: 'E_INTERNAL', message: 'schedule edit returned an unexpected deletion' }\n const legacy = toLegacyScheduleView(value.task)\n if (legacy === undefined) return { ok: false, code: 'E_PROTOCOL', message: 'schedule cannot be represented in the session-scoped view' }\n return { ok: true, value: legacy }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as ScheduleTaskViewLike, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n const { clientRequestId, ...request } = payload\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n clientRequestId,\n request,\n async () => {\n const response = await schedule.update({ rpcId: randomUUID(), payload: request })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule update returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.updated !== true || value.record === undefined) {\n return { ok: false, code: wireCodeFor('schedule', value.code), message: value.code ?? 'schedule update rejected' }\n }\n return { ok: true, value: value.record }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as ScheduleTaskViewLike, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async deleteSchedule(\n deviceId: string,\n payload: { sessionId: string; id: string; clientRequestId: string },\n ): Promise<ScheduleBridgeResult<{ id: string; deleted: true }>> {\n const schedule = this.apiProxy.schedule\n if (schedule === undefined) return { ok: false, code: 'E_UNSUPPORTED', message: 'schedules unavailable on this host version' }\n // 旧帧映射:delete 直接走宿主级 edit({action:\"delete\"})。\n if (schedule.mode === 'host') {\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { sessionId: payload.sessionId, id: payload.id },\n async () => {\n const response = await schedule.edit({\n rpcId: randomUUID(),\n payload: { id: payload.id, action: 'delete' },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule delete returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n const value = response.result.value\n if (value.kind === 'deleted') return { ok: true, value: { id: value.id, deleted: true as const } }\n if (value.kind === 'not-found') return { ok: false, code: 'E_NOT_FOUND', message: 'schedule not found' }\n if (value.kind === 'conflict') return { ok: false, code: 'E_BUSY', message: 'schedule changed since it was read' }\n return { ok: false, code: 'E_INTERNAL', message: 'schedule delete returned an updated task' }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as { id: string; deleted: true }, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n const result = await this.scheduleMutations.dispatch(\n deviceId,\n payload.clientRequestId,\n { sessionId: payload.sessionId, id: payload.id },\n async () => {\n const response = await schedule.delete({\n rpcId: randomUUID(),\n payload: { sessionId: payload.sessionId, id: payload.id },\n })\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'schedule delete returned no result' }\n if (!response.result.ok) return wireErrorOf('schedule', response.result.error)\n if (response.result.value.deleted !== true) {\n return { ok: false, code: 'E_NOT_FOUND', message: response.result.value.code ?? 'schedule not found' }\n }\n return { ok: true, value: { id: response.result.value.id, deleted: true as const } }\n },\n )\n if (result.ok) {\n this.record('s2c.schedule.changed', {})\n return { ok: true, value: result.value as { id: string; deleted: true }, replayed: result.replayed }\n }\n return { ok: false, code: result.code, message: result.message ?? result.code, replayed: result.replayed }\n }\n\n async sendPrompt(\n sessionId: string,\n text: string,\n images: Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }> = [],\n documents: PromptDocument[] = [],\n options: { mode?: 'queue' | 'steer'; clientSendId?: string } = {},\n ): Promise<SessionManagementResult<number>> {\n try {\n const content: PromptArgs['content'] = [];\n if (text.trim().length > 0) content.push({ type: 'text', text });\n for (const image of images) content.push({ type: 'image', ...image });\n for (const document of documents) content.push({ type: 'text', text: documentPromptBlock(document) });\n const response = await this.apiProxy.sessions.prompt({\n // rpcId 即 Host requestId:带 clientSendId 时用确定值,日志崩溃后的重新派发\n // 由 Host 按 requestId 去重;队列快照也靠它把项还原成 clientSendId。\n rpcId: options.clientSendId !== undefined ? promptRequestId(options.clientSendId) : randomUUID(),\n payload: {\n sessionId,\n mode: options.mode ?? 'queue',\n content,\n clientTimeZone: localTimeZone(),\n },\n });\n if (!response.result) return { ok: false, code: 'E_INTERNAL', message: 'prompt returned no result' };\n // Preserve the host's error kind so the phone can tell \"retry later\"\n // (E_BUSY) from \"session is gone\" (E_NOT_FOUND) instead of mapping\n // every failure onto E_BUSY.\n if (!response.result.ok) return wireErrorOf('session', response.result.error);\n const row = this.summaries.get(sessionId);\n if (row) {\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n this.userReceiptSeq += 1;\n return { ok: true, value: this.userReceiptSeq };\n } catch (error) {\n return { ok: false, code: 'E_INTERNAL', message: String(error) };\n }\n }\n\n\n async respondApproval(\n requestId: string,\n decision: 'allow' | 'deny',\n reason?: string,\n ): Promise<PendingResponseOutcome> {\n const pending = this.approvals.get(requestId);\n if (!pending) return { ok: false, reason: 'not-pending' };\n // Claim the pending entry before responding: a second phone (or a double\n // tap) must not fire another client-response for the same rpcId while the\n // resolved frame is still in flight.\n this.approvals.delete(requestId);\n const outcome = decision === 'allow' ? 'allowed-once' : 'rejected';\n const denialReason = typeof reason === 'string' ? reason.trim().slice(0, 500) : '';\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: {\n ok: true,\n value: {\n sessionId: pending.sessionId,\n approvalId: requestId,\n outcome,\n // PROTOCOL v2: a deny may carry the user's explanation so the\n // model can adjust course instead of blindly retrying.\n ...(denialReason.length > 0 ? { reason: denialReason } : {}),\n },\n },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted) {\n const failure = receiptFailureReason(receipt);\n // Restore only what the user can retry: `not-pending` means the host\n // already settled the request, and restoring would strand a ghost\n // entry that no future resolved frame will ever clear.\n if (failure !== 'not-pending' && !this.approvals.has(requestId)) {\n this.approvals.set(requestId, pending);\n }\n return { ok: false, reason: failure };\n }\n // The claim above removed the entry, so the upcoming approval/resolved\n // frame finds nothing left to bump — refresh the summary flags here or\n // the session badge stays \"pending\" until an unrelated refresh.\n this.bumpPendingFlags(pending.sessionId);\n return { ok: true };\n } catch {\n // Transport-level failure: give the request back so the user can retry.\n if (!this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return { ok: false, reason: 'transport' };\n }\n }\n\n async respondQuestion(requestId: string, answers: unknown): Promise<PendingResponseOutcome> {\n const pending = this.questions.get(requestId);\n if (!pending) return { ok: false, reason: 'not-pending' };\n this.questions.delete(requestId);\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, answer: { answers: normalizeAnswerItems(answers, pending.questions) } } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted) {\n const failure = receiptFailureReason(receipt);\n // Same ghost-entry rule as approvals: `not-pending` is final, never\n // restore — the host has no record left to resolve against.\n if (failure !== 'not-pending' && !this.questions.has(requestId)) {\n this.questions.set(requestId, pending);\n }\n return { ok: false, reason: failure };\n }\n // Mirror the approval path: the claimed entry cannot bump flags when the\n // resolved frame arrives, so recompute them right after acceptance.\n this.bumpPendingFlags(pending.sessionId);\n return { ok: true };\n } catch {\n if (!this.questions.has(requestId)) this.questions.set(requestId, pending);\n return { ok: false, reason: 'transport' };\n }\n }\n}\n\n/**\n * The host validates question answers strictly (core dsh-user-questions via\n * apiProxy): a present-but-empty `custom` fails `matchesQuestions`, and a\n * single-select question rejects `custom` combined with a selection. Clients\n * may send lenient shapes (the phone historically always attached\n * `\"custom\": \"\"`, which made EVERY option-only answer fail), so normalize to\n * exactly what the host accepts before forwarding.\n */\nexport function normalizeAnswerItems(\n raw: unknown,\n questions?: unknown,\n): Array<{ id: string; selected: string[]; custom?: string }> {\n if (!Array.isArray(raw)) return [];\n const askedById = new Map<string, { multiSelect?: unknown }>();\n if (Array.isArray(questions)) {\n for (const q of questions) {\n if (typeof q === 'object' && q !== null && typeof (q as { id?: unknown }).id === 'string') {\n askedById.set((q as { id: string }).id, q as { multiSelect?: unknown });\n }\n }\n }\n const items: Array<{ id: string; selected: string[]; custom?: string }> = [];\n for (const entry of raw) {\n if (typeof entry !== 'object' || entry === null) continue;\n const r = entry as { id?: unknown; selected?: unknown; custom?: unknown };\n if (typeof r.id !== 'string') continue;\n // De-duplicate while preserving order; duplicate labels are rejected by the host.\n const selected = [...new Set(\n Array.isArray(r.selected) ? r.selected.filter((s): s is string => typeof s === 'string') : [],\n )];\n const customText = typeof r.custom === 'string' ? r.custom : '';\n let custom: string | undefined;\n if (customText.trim().length > 0) custom = customText;\n // A single-select answer must be either options or free text, never both.\n if (custom !== undefined && selected.length > 0 && askedById.get(r.id)?.multiSelect !== true) {\n custom = undefined;\n }\n items.push({ id: r.id, selected, ...(custom !== undefined ? { custom } : {}) });\n }\n return items;\n}\n\n/** Map an apiProxy respond receipt onto the failure vocabulary. */\nfunction receiptFailureReason(receipt: { accepted: boolean; reason?: string } | undefined):\n 'not-pending' | 'bad-response' {\n return receipt?.reason === 'not-pending' ? 'not-pending' : 'bad-response';\n}\n\n// ---------- projection helpers ----------\n\n/**\n * 尾部条数收敛到 [MIN_TAIL_MESSAGES, MAX_HISTORY_PAGE_MESSAGES]。\n * 区间与 wire 行校验(tailCount 1..10000、limit 1..500)共用投影 module 的\n * 常量——「一页能要多少条消息」本是一条规则,原先散在三处。\n */\nfunction clampTail(n: number): number {\n if (!Number.isFinite(n)) return DEFAULT_HISTORY_PAGE_MESSAGES;\n return Math.max(MIN_TAIL_MESSAGES, Math.min(MAX_HISTORY_PAGE_MESSAGES, Math.floor(n)));\n}\n\nfunction localTimeZone(): string | undefined {\n try {\n return new Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction riskOf(toolName: string): 'read' | 'write' | 'destructive' {\n if (/bash|pwsh|terminal/.test(toolName)) return 'write';\n if (/edit|write|str_replace|create/.test(toolName)) return 'write';\n if (/delete|remove|kill/.test(toolName)) return 'destructive';\n return 'read';\n}\n\n/** First question's text for the push banner; the questions payload shape is\n * host-version dependent, so extract defensively. */\nexport function firstQuestionText(questions: unknown): string {\n if (!Array.isArray(questions) || questions.length === 0) return 'Agent 等待你的输入';\n const first = questions[0] as { question?: unknown } | undefined;\n const text = String(first?.question ?? '').trim();\n return text || 'Agent 等待你的输入';\n}\n\nconst TODO_STATUSES = new Set(['pending', 'in_progress', 'completed']);\n\n/** Validate a host todo projection once; progress counts and the full\n * checklist both derive from this sanitized list so they never disagree. */\nfunction sanitizeTodoItems(items: Array<{ content?: unknown; status?: unknown }> | null): SessionTodoItem[] {\n if (!items) return [];\n return items\n .map(i => ({ content: String(i.content ?? '').trim(), status: String(i.status ?? '') }))\n .filter(i => i.content.length > 0 && TODO_STATUSES.has(i.status))\n .slice(0, 100)\n .map(i => ({ content: i.content, status: i.status as SessionTodoStatus }));\n}\n\n/** Zero-value stats snapshot used as the merge base for partial patches. */\nconst emptyUsageStats: SessionUsageStats = {\n turns: 0,\n steps: 0,\n llmMs: 0,\n toolMs: 0,\n ttftMs: 0,\n ttftSteps: 0,\n decodeMs: 0,\n decodeTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n};\n\n/** Coerce one host counter to a non-negative integer; junk becomes 0. */\nfunction usageCounter(value: unknown): number {\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n) || n < 0) return 0;\n return Math.floor(n);\n}\n\nconst SESSION_STATS_COUNTERS = [\n 'turns', 'steps', 'llmMs', 'toolMs',\n 'ttftMs', 'ttftSteps', 'decodeMs', 'decodeTokens',\n] as const;\n\nconst TOKEN_USAGE_COUNTERS = [\n 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens',\n] as const;\n\n/** Sanitize one `sessionStats` / `tokenUsage` projection value into the wire\n * stats fields; undefined when the payload carries nothing readable. Host\n * field names differ between the two projections (tokenUsage reports\n * `uncachedInputTokens`, the wire mirrors it as `inputTokens`). */\nfunction sanitizeUsageStatsPatch(key: string, value: unknown): Partial<SessionUsageStats> | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const raw = value as Record<string, unknown>;\n const patch: Partial<SessionUsageStats> = {};\n if (key === 'sessionStats') {\n for (const field of SESSION_STATS_COUNTERS) {\n if (field in raw) patch[field] = usageCounter(raw[field]);\n }\n } else {\n if ('uncachedInputTokens' in raw) patch.inputTokens = usageCounter(raw.uncachedInputTokens);\n for (const field of TOKEN_USAGE_COUNTERS) {\n if (field in raw) patch[field] = usageCounter(raw[field]);\n }\n }\n return Object.keys(patch).length > 0 ? patch : undefined;\n}\n\n/** Combine one session's `sessionStats` + `tokenUsage` projection values into\n * a complete wire stats object; undefined when neither carries anything. */\nfunction usageStatsOf(sessionStats: unknown, tokenUsage: unknown): SessionUsageStats | undefined {\n const patches = [\n sanitizeUsageStatsPatch('sessionStats', sessionStats),\n sanitizeUsageStatsPatch('tokenUsage', tokenUsage),\n ].filter((patch): patch is Partial<SessionUsageStats> => patch !== undefined);\n if (patches.length === 0) return undefined;\n return Object.assign({}, emptyUsageStats, ...patches);\n}\n\nfunction toSummary(\n row: PhoneSessionRow,\n approvals: Map<string, PendingApproval>,\n questions: Map<string, PendingQuestion>,\n workspace?: WorkspaceViewLike,\n): SessionSummary {\n const values = row.projections?.values ?? {};\n const todos = Array.isArray(values.todos) ? values.todos as Array<{ content?: unknown; status?: unknown }> : null;\n let pendingApproval = false;\n for (const pending of approvals.values()) {\n if (pending.sessionId === row.sessionId) pendingApproval = true;\n }\n let pendingQuestion = false;\n for (const pending of questions.values()) {\n if (pending.sessionId === row.sessionId) pendingQuestion = true;\n }\n const cwd = typeof row.cwd === 'string' ? row.cwd : '';\n const label = workspace?.title ?? (cwd ? cwd.split('/').filter(Boolean).pop() : undefined);\n const todoItems = sanitizeTodoItems(todos);\n const stats = usageStatsOf(values.sessionStats, values.tokenUsage);\n return {\n id: row.sessionId,\n title: typeof values.title === 'string' ? values.title : '',\n // A blank session has never run a turn, and the running flag above\n // already covers the live case. Reporting `unknown` here made the phone\n // treat the composer as \"still syncing\" forever: nothing ever converges\n // the state back because the first prompt is exactly what the disabled\n // button cannot send. Idle is truthful for every non-running blank row.\n status: row.running ? 'running' : 'idle',\n lastActivityTs: Number(row.updatedAt ?? Date.now()),\n todos: todoItems.length > 0\n ? {\n done: todoItems.filter(i => i.status === 'completed').length,\n total: todoItems.length,\n }\n : null,\n todoItems: todoItems.length > 0 ? todoItems : null,\n pendingApproval,\n pendingQuestion,\n ...(stats ? { stats } : {}),\n workspaceLabel: label ?? null,\n workspaceId: workspace?.workspaceId ?? null,\n workspacePath: workspace?.path ?? (cwd || null),\n };\n}\n\nfunction projectWorkspace(workspace: WorkspaceViewLike): {\n id: string; title: string; path: string; sessionIds: string[]\n} {\n return {\n id: String(workspace.workspaceId),\n title: String(workspace.title),\n path: String(workspace.path),\n sessionIds: (workspace.sessionIds ?? []).map(String),\n }\n}\n\n/** 把 Host 的模型目录投影成 wire 形状:字段全部收敛为字符串,缺省不补位。 */\nfunction projectSessionModels(value: HostSessionModels): HostSessionModels {\n return {\n current: {\n provider: String(value.current.provider),\n model: String(value.current.model),\n ...(value.current.reasoningEffort ? { reasoningEffort: String(value.current.reasoningEffort) } : {}),\n },\n routable: value.routable === true,\n groups: (value.groups ?? []).map((group) => ({\n id: String(group.id),\n name: String(group.name),\n models: (group.models ?? []).map((model) => ({\n id: String(model.id),\n name: String(model.name),\n ...(model.description ? { description: String(model.description) } : {}),\n ...(model.reasoning ? {\n reasoning: {\n efforts: (model.reasoning.efforts ?? []).map((effort) => ({\n id: String(effort.id),\n name: String(effort.name),\n ...(effort.description ? { description: String(effort.description) } : {}),\n })),\n ...(model.reasoning.defaultEffort\n ? { defaultEffort: String(model.reasoning.defaultEffort) }\n : {}),\n },\n } : {}),\n })),\n })),\n failures: (value.failures ?? []).map((failure) => ({\n id: String(failure.id),\n name: String(failure.name),\n message: String(failure.message),\n })),\n }\n}\n\n/** Project a history page (raw events) into MessageProjection rows. */\n\n/* ---------- 旧帧 -> 宿主级模型的映射(PROTOCOL.md「旧帧在宿主级模型上的映射」) ----------\n *\n * 旧 App 只认识会话级视图;宿主级模型的任务带 target 且没有 update/delete。\n * 这些纯函数只负责「把新任务视图反向投影成旧视图」与「把旧请求翻成宿主级\n * 请求」,不含任何 I/O;冲突保护是尽力而为的:旧视图没有 updatedAt,因此用\n * `expected` 与当前任务的旧投影逐字段比较。\n */\n\n/** 宿主级列表全量拉取时的页大小(Host 允许的最大值)。 */\nconst HOST_SCHEDULE_PAGE_SIZE = 500\n\n/** 参与旧帧冲突比较的字段:`state`/`deliveryMode` 是派生字段,不参与。 */\nconst LEGACY_PROJECTION_FIELDS = [\n 'id',\n 'kind',\n 'title',\n 'prompt',\n 'scheduledAt',\n 'afterSeconds',\n 'everySeconds',\n 'time',\n 'timeZone',\n 'weekdays',\n 'expression',\n] as const\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction legacyNumber(...values: unknown[]): number | undefined {\n for (const value of values) {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\nfunction legacyString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\nfunction normalizeAtInput(\n value: unknown,\n): string | { date: string; time: string; time_zone: string } | undefined {\n if (typeof value === 'string' && value.length > 0) return value\n if (!isPlainObject(value)) return undefined\n const date = legacyString(value.date)\n const time = legacyString(value.time)\n const zone = legacyString(value.time_zone, value.timeZone)\n return date !== undefined && time !== undefined && zone !== undefined\n ? { date, time, time_zone: zone }\n : undefined\n}\n\nfunction normalizeLocalTime(value: unknown): { time: string; time_zone: string } | undefined {\n if (!isPlainObject(value)) return undefined\n const time = legacyString(value.time)\n const zone = legacyString(value.time_zone, value.timeZone)\n return time !== undefined && zone !== undefined ? { time, time_zone: zone } : undefined\n}\n\nfunction normalizeWeekdays(value: unknown): number[] | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined\n const days = value.filter(\n (day): day is number => typeof day === 'number' && Number.isSafeInteger(day) && day >= 1 && day <= 7,\n )\n return days.length === value.length ? days : undefined\n}\n\n/** 旧创建帧的选择器 -> 宿主级 timing。旧 wire 校验保证恰好一个选择器。 */\nfunction hostTimingFromLegacySelector(\n selectors: Record<string, unknown>,\n): HostScheduleTiming | undefined {\n const after = legacyNumber(selectors.after_seconds)\n if (after !== undefined) return { kind: 'after', after_seconds: after }\n if (selectors.at !== undefined) {\n const at = normalizeAtInput(selectors.at)\n return at === undefined ? undefined : { kind: 'at', at }\n }\n const every = legacyNumber(selectors.every_seconds)\n if (every !== undefined) return { kind: 'every', every_seconds: every }\n if (selectors.daily !== undefined) {\n const daily = normalizeLocalTime(selectors.daily)\n return daily === undefined ? undefined : { kind: 'daily', daily }\n }\n if (selectors.weekly !== undefined) {\n if (!isPlainObject(selectors.weekly)) return undefined\n const time = normalizeLocalTime(selectors.weekly)\n const weekdays = normalizeWeekdays(selectors.weekly.weekdays)\n return time === undefined || weekdays === undefined\n ? undefined\n : { kind: 'weekly', weekly: { ...time, weekdays } }\n }\n if (selectors.cron !== undefined) {\n if (!isPlainObject(selectors.cron)) return undefined\n const expression = legacyString(selectors.cron.expression)\n const zone = legacyString(selectors.cron.time_zone, selectors.cron.timeZone)\n return expression === undefined || zone === undefined\n ? undefined\n : { kind: 'cron', cron: { expression, time_zone: zone } }\n }\n return undefined\n}\n\n/**\n * 旧更新帧的 `change` -> 宿主级 timing。\n *\n * 两种历史写法都认:选择器式(`{kind:'daily', daily:{...}}`,与创建帧同形)与\n * 扁平式(`{kind:'daily', time, timeZone}` / `{kind:'cron', expression}`)。\n */\nfunction hostTimingFromLegacyChange(change: unknown): HostScheduleTiming | undefined {\n if (!isPlainObject(change)) return undefined\n const selector = hostTimingFromLegacySelector(change)\n if (selector !== undefined) return selector\n switch (change.kind) {\n case 'daily': {\n const time = legacyString(change.time)\n const zone = legacyString(change.time_zone, change.timeZone)\n return time === undefined || zone === undefined ? undefined : { kind: 'daily', daily: { time, time_zone: zone } }\n }\n case 'weekly': {\n const time = legacyString(change.time)\n const zone = legacyString(change.time_zone, change.timeZone)\n const weekdays = normalizeWeekdays(change.weekdays)\n return time === undefined || zone === undefined || weekdays === undefined\n ? undefined\n : { kind: 'weekly', weekly: { time, time_zone: zone, weekdays } }\n }\n case 'cron': {\n const expression = legacyString(change.expression)\n const zone = legacyString(change.time_zone, change.timeZone)\n return expression === undefined || zone === undefined\n ? undefined\n : { kind: 'cron', cron: { expression, time_zone: zone } }\n }\n case 'rrule': {\n const rrule = legacyString(change.rrule)\n return rrule === undefined ? undefined : { kind: 'rrule', rrule }\n }\n default:\n return undefined\n }\n}\n\n/** 旧更新帧的 title/prompt/change -> 宿主级 patch。 */\nfunction hostPatchFromLegacyUpdate(payload: Record<string, unknown>): HostSchedulePatch {\n const patch: HostSchedulePatch = {}\n if (typeof payload.title === 'string') patch.title = payload.title\n if (typeof payload.prompt === 'string') patch.prompt = payload.prompt\n const timing = hostTimingFromLegacyChange(payload.change)\n if (timing !== undefined) patch.timing = timing\n return patch\n}\n\n/**\n * 宿主级任务视图 -> 旧会话级视图。\n *\n * `state` 只由 `scheduledAt` 是否已过推导(PROTOCOL.md 映射表);`rrule` 在旧枚举\n * 里没有对应 kind,返回 undefined 让调用方跳过——旧客户端按枚举解码列表,塞进\n * 未知 kind 会让整页解码失败,静默隐藏比让旧 App 打不开列表更安全。\n */\nfunction toLegacyScheduleView(task: HostScheduleTaskView): ScheduleTaskViewLike | undefined {\n const instant = Date.parse(task.scheduledAt)\n const base = {\n id: task.id,\n title: task.title,\n prompt: task.prompt,\n scheduledAt: task.scheduledAt,\n state: Number.isFinite(instant) && instant < Date.now() ? 'overdue' as const : 'scheduled' as const,\n deliveryMode: 'host' as const,\n }\n switch (task.timing.kind) {\n case 'after':\n return { ...base, kind: 'after', afterSeconds: task.timing.after_seconds }\n case 'at':\n return { ...base, kind: 'at' }\n case 'every':\n return { ...base, kind: 'every', everySeconds: task.timing.every_seconds }\n case 'daily':\n return {\n ...base,\n kind: 'daily',\n time: task.timing.daily.time,\n timeZone: task.timing.daily.time_zone,\n }\n case 'weekly':\n return {\n ...base,\n kind: 'weekly',\n time: task.timing.weekly.time,\n timeZone: task.timing.weekly.time_zone,\n weekdays: [...task.timing.weekly.weekdays],\n }\n case 'cron':\n return {\n ...base,\n kind: 'cron',\n expression: task.timing.cron.expression,\n timeZone: task.timing.cron.time_zone,\n }\n default:\n return undefined\n }\n}\n\n/** 旧帧的冲突保护:当前任务的旧投影与客户端回传的 `expected` 逐字段相等。 */\nfunction legacyScheduleMatchesExpected(task: HostScheduleTaskView, expected: unknown): boolean {\n const view = toLegacyScheduleView(task)\n if (view === undefined) return false\n if (!isPlainObject(expected)) return false\n const actual = view as unknown as Record<string, unknown>\n return LEGACY_PROJECTION_FIELDS.every(\n (key) => JSON.stringify(actual[key]) === JSON.stringify(expected[key]),\n )\n}\n","/**\n * Resident DSH Remote Events client for phone interactions.\n *\n * The Host-side `approval/request` and `user-questions/request` events are\n * owned once by DSH API Remotes. API Gateway then fans each pending waterfall\n * out to every connected Client and settles the first result. DeepPilot joins\n * that official Client plane in-process instead of registering a competing\n * Host waterfall listener or replacing the official Web composer.\n */\nimport { Context } from '@deepseek-ai/cordis'\nimport * as Cordis from '@deepseek-ai/cordis'\n\n/**\n * One Peer admitted to the Host. DSH names it on every in-process carrier call\n * from 0.2.1-alpha.3 on; the Host's own operator Peer stands in before that.\n */\nexport interface HostPeerLike {\n readonly principal: { readonly kind: string }\n readonly signal: AbortSignal\n readonly interactions: 'answer' | 'none'\n}\n\ninterface FetchHandlerLike {\n /** `peer` is required from alpha.3 on and ignored by earlier hosts. */\n fetch(request: Request, peer: HostPeerLike | undefined): Promise<Response>\n}\n\ninterface HostConnectionLike {\n createSharedFetchHandler(channel: '/api'): FetchHandlerLike\n /**\n * alpha.3's only entry point that can name both the principal and the\n * forwarded-event participation. Absent before it, where the service instead\n * holds the operator Peer every call speaks for.\n */\n createPeer?(options: { principal: { kind: 'owner' }; interactions: 'answer' | 'none' }): HostPeerLike\n operator?: HostPeerLike\n}\n\ninterface HostWireStreamLike {\n open(\n endpoint: string,\n payload: unknown,\n uplink: undefined,\n peer: HostPeerLike | undefined,\n signal: AbortSignal,\n ): Promise<AsyncIterable<unknown>>\n}\n\ninterface HostGatewayLike {\n wireStream: HostWireStreamLike\n}\n\ninterface ClientTransportHooksLike {\n fetch(input: string, init: RequestInit): Promise<Response>\n openStream(endpoint: string, payload: unknown, signal: AbortSignal): AsyncIterable<unknown>\n ownsHost: true\n}\n\n/** The rc.2 Client sends relative RPC paths to its in-process carrier. */\nexport function inProcessRemoteRequest(input: string, init: RequestInit): Request {\n // The shared Fetch handler routes by pathname and never performs a network\n // request. Node's Request still requires an absolute URL for relative paths.\n return new Request(new URL(input, 'http://localhost/'), init)\n}\n\ninterface ClientRemoteLike {\n $on(event: string, listener: (this: Context, request: any, next: () => Promise<unknown>) => unknown): () => void\n}\n\ninterface ClientTypertLike {\n contexts: {\n registerClient(key: string, adapter: {\n identity(candidate: Context): string | undefined\n resolve(identity: string): Context\n }): () => void\n }\n}\n\n/** Carrier inputs the explicit Connection installer accepts on both audited hosts. */\ninterface ClientConnectionInstallOptionsLike {\n transport: ClientTransportHooksLike\n interactions: 'answer' | 'none'\n}\n\ninterface ClientPluginModule {\n apply(ctx: Context): void\n /**\n * Explicit installer, present on both audited hosts. Without it the carrier\n * can only arrive through the process global `apply()` reads, and the\n * forwarded-event participation cannot be named at all.\n */\n installConnection?(ctx: Context, options: ClientConnectionInstallOptionsLike): void\n}\n\n/**\n * Install the Connection service on the resident Client.\n *\n * alpha.3 selects `interactions: 'none'` for any caller that supplies no page\n * location, and that mode forbids the forwarded-event stream this resident\n * client exists to answer. Only the explicit installer can name the\n * participation, so it takes precedence; `apply` stays for a host older than\n * the installer, reading the carrier from the process global set around it.\n */\nexport function installResidentConnection(\n connection: ClientPluginModule,\n ctx: Context,\n transport: ClientTransportHooksLike,\n): void {\n if (typeof connection.installConnection === 'function') {\n connection.installConnection(ctx, { transport, interactions: 'answer' })\n return\n }\n connection.apply(ctx)\n}\n\nexport interface RemoteApprovalRequest {\n toolName?: string\n callId?: string\n reason?: string\n signal?: AbortSignal\n}\n\nexport interface RemoteQuestionRequest {\n questions?: unknown\n signal?: AbortSignal\n}\n\nexport interface DshRemoteInteractionHandlers {\n approval(sessionId: string, request: RemoteApprovalRequest, next: () => Promise<unknown>): unknown\n question(sessionId: string, request: RemoteQuestionRequest, next: () => Promise<unknown>): unknown\n}\n\ntype TransportGlobal = typeof globalThis & {\n __DSH_TRANSPORT__?: ClientTransportHooksLike\n}\n\ninterface ClientBundleDefinition {\n id: string\n factory(require: (id: string) => unknown): unknown\n}\n\ntype ClientBundleWindow = {\n __ModuleLoader__: {\n load(definition: ClientBundleDefinition): void\n }\n}\n\ntype LoaderGlobal = {\n window?: ClientBundleWindow\n}\n\ninterface ClientFaces {\n typert: ClientPluginModule\n connection: ClientPluginModule\n gateway: ClientPluginModule\n}\n\nlet clientFaces: Promise<ClientFaces> | undefined\n\n/**\n * Load DSH's browser-distributed Client faces into a tiny Host-side module\n * table. Published `lib/client.js` files register with `window.__ModuleLoader__`\n * rather than exporting ESM values, so a normal Node import cannot consume\n * them. The shim runs only while the three official bundles register; their\n * apply functions then operate against a non-browser Cordis Context.\n */\nfunction pluginModule(value: unknown, specifier: string): ClientPluginModule {\n if (typeof value !== 'object' || value === null) throw new Error(`${specifier} Client bundle returned no exports`)\n const record = value as Record<string, unknown>\n if (typeof record.apply !== 'function') throw new Error(`${specifier} has no Client apply() export`)\n return value as unknown as ClientPluginModule\n}\n\n/**\n * Register one loaded Client face when the browser banner did not.\n *\n * alpha.3 resolves these specifiers through the `node` export condition to a\n * bundle that exports the face as ordinary ESM and never calls\n * `window.__ModuleLoader__.load`, while earlier hosts only ship the banner\n * shape. A banner registration wins, because it is what the bundle's own\n * internal `require()` calls resolve against; the ESM namespace is the fallback.\n */\nexport function adoptClientModule(\n modules: Map<string, unknown>,\n specifier: string,\n loaded: unknown,\n): void {\n const id = specifier.replace(/\\/client$/, '')\n if (!modules.has(id)) modules.set(id, loaded)\n}\n\nasync function loadClientFaces(): Promise<ClientFaces> {\n clientFaces ??= (async () => {\n const modules = new Map<string, unknown>([['@deepseek-ai/cordis', Cordis]])\n const loaderGlobal = globalThis as unknown as LoaderGlobal\n const previousWindow = loaderGlobal.window\n loaderGlobal.window = {\n __ModuleLoader__: {\n load: definition => {\n const exports = definition.factory((id) => {\n if (!modules.has(id)) throw new Error(`resident DSH Client cannot resolve ${JSON.stringify(id)}`)\n return modules.get(id)\n })\n modules.set(definition.id, exports)\n },\n },\n }\n try {\n // Use variable specifiers to preserve DSH's Host/Client TypeScript face\n // split; static imports would merge incompatible Context declarations.\n for (const specifier of [\n '@deepseek-ai/dsh-typert-registry/client',\n '@deepseek-ai/dsh-client-connection/client',\n '@deepseek-ai/dsh-api-gateway/client',\n ]) {\n adoptClientModule(modules, specifier, await import(specifier))\n }\n } finally {\n if (previousWindow === undefined) delete loaderGlobal.window\n else loaderGlobal.window = previousWindow\n }\n return {\n typert: pluginModule(modules.get('@deepseek-ai/dsh-typert-registry'), '@deepseek-ai/dsh-typert-registry/client'),\n connection: pluginModule(modules.get('@deepseek-ai/dsh-client-connection'), '@deepseek-ai/dsh-client-connection/client'),\n gateway: pluginModule(modules.get('@deepseek-ai/dsh-api-gateway'), '@deepseek-ai/dsh-api-gateway/client'),\n }\n })()\n return clientFaces\n}\n\nconst agentScopeKey = Symbol('deeppilot.dsh-client.agent-scope')\n\ntype AgentContext = Context & { [agentScopeKey]?: string }\n\nfunction scopeOf(ctx: Context): string | undefined {\n return (ctx as AgentContext)[agentScopeKey]\n}\n\nfunction createAgentScope(ctx: Context, identity: string): Context {\n const fiber = ctx.plugin(function deeppilotAgentScope() {})\n return fiber.ctx.extend({\n [agentScopeKey]: identity,\n [Context.filter](listenerCtx: Context) {\n const listenerIdentity = scopeOf(listenerCtx)\n return listenerIdentity === undefined || listenerIdentity === identity\n },\n })\n}\n\n/**\n * Mint the Peer every in-process carrier call speaks for.\n *\n * alpha.3 requires a Peer on `fetch` and on `wireStream.open`, and refuses a\n * forwarded-event stream unless that Peer declares `interactions: 'answer'`.\n * Its connection service therefore grows `createPeer`, the only entry point\n * that can name the principal and the participation together. Before that the\n * service holds one operator Peer and defaults every absent Peer to it, so that\n * instance is the drop-in answer. A host with neither yields undefined, which\n * only the pre-alpha.3 carrier accepts.\n */\nfunction createHostPeer(connection: HostConnectionLike): HostPeerLike | undefined {\n if (typeof connection.createPeer === 'function') {\n return connection.createPeer({ principal: { kind: 'owner' }, interactions: 'answer' })\n }\n return connection.operator\n}\n\nfunction inProcessStream(\n gateway: HostGatewayLike,\n endpoint: string,\n payload: unknown,\n signal: AbortSignal,\n peer: HostPeerLike | undefined,\n): AsyncIterable<unknown> {\n return (async function* () {\n const source = await gateway.wireStream.open(endpoint, payload, undefined, peer, signal)\n yield* source\n })()\n}\n\n/**\n * Start one headless official DSH Client backed by the Host's in-process\n * Connection and Gateway carriers.\n *\n * The returned disposer tears down the Remote Events generation, all pending\n * listeners, and every lazily minted Agent scope.\n */\nexport async function startDshRemoteInteractions(\n hostCtx: Context,\n handlers: DshRemoteInteractionHandlers,\n): Promise<() => Promise<void>> {\n const connection = hostCtx.get('connection') as HostConnectionLike | undefined\n const gateway = hostCtx.get('typertGateway') as HostGatewayLike | undefined\n if (connection === undefined) throw new Error('DSH Host connection is unavailable')\n if (gateway === undefined) throw new Error('DSH typertGateway is unavailable')\n\n const faces = await loadClientFaces()\n\n const client = new Context()\n const peer = createHostPeer(connection)\n const fetchHandler = connection.createSharedFetchHandler('/api')\n const transport: ClientTransportHooksLike = {\n fetch: (input, init) => fetchHandler.fetch(inProcessRemoteRequest(input, init), peer),\n openStream: (endpoint, payload, signal) => inProcessStream(gateway, endpoint, payload, signal, peer),\n ownsHost: true,\n }\n\n try {\n faces.typert.apply(client)\n\n // Client Connection reads this hook once during apply() and retains the\n // concrete functions. Restore the process global immediately afterward so\n // no unrelated Client composition inherits DeepPilot's carrier.\n const transportGlobal = globalThis as TransportGlobal\n const previousTransport = transportGlobal.__DSH_TRANSPORT__\n transportGlobal.__DSH_TRANSPORT__ = transport\n try {\n installResidentConnection(faces.connection, client, transport)\n } finally {\n if (previousTransport === undefined) delete transportGlobal.__DSH_TRANSPORT__\n else transportGlobal.__DSH_TRANSPORT__ = previousTransport\n }\n\n const typert = client.get('typert') as ClientTypertLike | undefined\n if (typert === undefined) throw new Error('resident DSH Client typert service was not installed')\n const scopes = new Map<string, Context>()\n typert.contexts.registerClient('agent', {\n identity: candidate => scopeOf(candidate),\n resolve: identity => {\n let scope = scopes.get(identity)\n if (scope === undefined) {\n scope = createAgentScope(client, identity)\n scopes.set(identity, scope)\n }\n return scope\n },\n })\n\n // The Gateway opens its Remote Events generation asynchronously. Register\n // both listeners immediately after apply so a replayed pending request is\n // claimed before the first stream item can be dispatched.\n faces.gateway.apply(client)\n const remote = client.get('remote') as ClientRemoteLike | undefined\n if (remote === undefined) throw new Error('resident DSH Client remote service was not installed')\n remote.$on('approval/request', function (request, next) {\n return handlers.approval(scopeOf(this) ?? '', request as RemoteApprovalRequest, next)\n })\n remote.$on('user-questions/request', function (request, next) {\n return handlers.question(scopeOf(this) ?? '', request as RemoteQuestionRequest, next)\n })\n\n return async () => {\n await client.fiber.dispose()\n }\n } catch (error) {\n await client.fiber.dispose()\n throw error\n }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { SessionExportFormat } from './protocol.ts'\n\nexport interface CommandSummary {\n name: string\n description: string\n definitionId?: string\n input?: { hint: string; attachments?: boolean }\n}\nexport interface CommandCatalog { commands: CommandSummary[]; permissions: boolean; export: boolean }\nexport interface CommandExecution { commandId: string; result: { kind: 'success' | 'error'; text?: string; sourceEventSeq?: number } }\nexport interface CommandAttachment { type: 'image' | 'document'; name: string; mediaType: string; data: string }\nexport interface PermissionOption { value: string; name: string; description?: string; confirmation: boolean }\nexport interface PermissionCatalog { options: PermissionOption[]; currentValue?: string }\nexport interface CommandApi {\n list(sessionId: string): Promise<CommandCatalog>\n execute(sessionId: string, line: string, attachments: CommandAttachment[], expectedDefinitionId?: string): Promise<CommandExecution>\n permissions(sessionId: string): Promise<PermissionCatalog>\n feedback(sessionId: string, text: string, category?: string): Promise<void>\n readonly markdownAvailable?: boolean\n export(sessionId: string, signal: AbortSignal, format?: SessionExportFormat): Promise<Response>\n}\n\n/**\n * 给本域自造的失败带上 Host 风格的码。\n *\n * 这些失败不是上游返回的,而是插件在这里判定出来的,所以必须自己声明语义:\n * 没有码的抛出物在 wire-errors 的域表里查不到,一律落进 `E_INTERNAL`,手机端\n * 便无法区分「指令不存在」「定义已变化」「这个指令不收附件」——它们对用户的\n * 下一步动作要求完全不同。码的取值见 `src/wire-errors.ts` 的 `command` 域表。\n */\nfunction fail(code: string, message: string): Error & { code: string } {\n return Object.assign(new Error(message), { code })\n}\n\n/** 只经官方 Gateway 调用命令,保留其 Agent 解析、作用域覆盖和生命周期记录。 */\nexport class DshCommandApi implements CommandApi {\n private peer: unknown\n constructor(private readonly ctx: Context) {}\n get available(): boolean {\n return this.ctx.get('commands') !== undefined && typeof (this.ctx.get('typertGateway') as any)?.invoke === 'function'\n }\n get markdownAvailable(): boolean {\n const connection = this.connection()\n // 共享 fetch 方法在无导出插件时也存在;实际路由和正文读取依赖都就绪才宣告能力。\n // alpha2 的连接服务持有此路由表;旧宿主或未知实现没有表时保守关闭入口。\n const route = connection?.fetchRoutes instanceof Map ? connection.fetchRoutes.get('/api/session.markdown') : undefined\n return typeof connection?.createSharedFetchHandler === 'function' &&\n route?.methods instanceof Set && route.methods.has('GET') && typeof route.fetch === 'function' &&\n typeof (this.ctx.get('sessionQuery') as any)?.readSession === 'function'\n }\n private connection(): any { return this.ctx.get('connection') }\n private owner(): unknown {\n const connection = this.connection()\n // 独立的一元调用身份不参与审批竞争;旧版使用连接提供的 operator。\n return this.peer ??= connection?.createPeer?.({ principal: { kind: 'owner' }, interactions: 'none' }) ?? connection?.operator\n }\n private invoke(namespace: string, method: string, args: Record<string, unknown>): Promise<any> {\n const gateway = this.ctx.get('typertGateway') as any\n return gateway.invoke({ namespace, method, args, peer: this.owner(), signal: AbortSignal.timeout(10 * 60_000) })\n }\n async list(sessionId: string): Promise<CommandCatalog> {\n const rows = await this.invoke('commands', 'list', { agentId: sessionId }) as CommandSummary[]\n const names = new Set<string>()\n const commands = rows.map(row => {\n if (!/^[a-z][a-z0-9_-]{0,127}$/.test(row.name) || names.has(row.name) || typeof row.description !== 'string') throw fail('command/invalid-response', '命令目录无效')\n names.add(row.name)\n return { name: row.name, description: row.description,\n ...(typeof row.definitionId === 'string' ? { definitionId: row.definitionId } : {}),\n ...(row.input && typeof row.input.hint === 'string' ? { input: { hint: row.input.hint, attachments: row.input.attachments === true } } : {}) }\n })\n return { commands, permissions: this.ctx.get('permissionPresets') !== undefined,\n export: typeof this.connection()?.createSharedFetchHandler === 'function' }\n }\n async execute(sessionId: string, line: string, attachments: CommandAttachment[], expectedDefinitionId?: string): Promise<CommandExecution> {\n const catalog = await this.list(sessionId)\n const name = /^\\/([a-z][a-z0-9_-]*)(?=\\s|$)/.exec(line)?.[1]\n const command = catalog.commands.find(row => row.name === name)\n if (!command) throw fail('command/not-found', '当前会话没有这个指令')\n if (expectedDefinitionId !== undefined && command.definitionId !== expectedDefinitionId) throw fail('command/definition-changed', '指令定义已变化,请重新选择')\n if (attachments.length && command.input?.attachments !== true) throw fail('command/attachments-unsupported', '这个指令不接受附件')\n const submittedAttachments: unknown[] = []\n for (const item of attachments) {\n if (item.type === 'image') submittedAttachments.push({ type: 'image', mediaType: item.mediaType, data: item.data })\n else {\n // 移动端文档已提取为正文;按文本文件上传,不冒充原始 PDF 或二进制文件。\n const uploaded = await this.invoke('fileUploads', 'upload', { agentId: sessionId, request: { name: item.name + '.txt', data: item.data } })\n submittedAttachments.push({ type: 'file', receiptId: uploaded.receiptId })\n }\n }\n const result = await this.invoke('commands', 'execute', { agentId: sessionId, line, submittedAttachments })\n if (!result || typeof result.commandId !== 'string' || !['success', 'error'].includes(result.result?.kind)) throw fail('command/invalid-response', '指令执行响应无效')\n return { commandId: result.commandId, result: { kind: result.result.kind,\n ...(typeof result.result.text === 'string' ? { text: result.result.text } : {}),\n ...(Number.isSafeInteger(result.result.sourceEventSeq) && result.result.sourceEventSeq >= 0 ? { sourceEventSeq: result.result.sourceEventSeq } : {}) } }\n }\n async permissions(sessionId: string): Promise<PermissionCatalog> {\n const service = this.ctx.get('permissionPresets') as any\n if (!service) throw fail('command/permissions-unsupported', '当前主机不支持权限预设')\n const catalog = await this.invoke('permissionPresets', 'catalog', {})\n const projection = await (this.ctx.get('sessionController') as any).projections({ sessionId }, new AbortController().signal)\n const currentValue = projection?.values?.permissions?.currentValue\n const seen = new Set<string>()\n const options = catalog.options.map((option: any) => {\n if (typeof option.value !== 'string' || !option.value.trim() || option.value.length > 512 || /[\\r\\n]/.test(option.value) || seen.has(option.value) || typeof option.name !== 'string') throw fail('command/invalid-response', '权限目录无效')\n seen.add(option.value)\n return { value: option.value, name: option.name,\n ...(typeof option.description === 'string' ? { description: option.description } : {}),\n confirmation: option.value === 'auto' || service.resolve(option.value).sandbox === 'danger-full-access' }\n })\n return { ...(typeof currentValue === 'string' ? { currentValue } : {}), options }\n }\n async feedback(sessionId: string, text: string, category?: string): Promise<void> {\n const result = await this.invoke('sessionFeedback', 'record', { request: { sessionId, ...(text.trim() ? { text: text.trim() } : {}), ...(category ? { category } : {}) } })\n // 上游拒绝时用它自己的码(`session/not-found` 等),插件只提供兜底:\n // 反馈保存失败的原因对手机是可执行的,压成一句通用失败会丢掉它。\n if (result?.ok !== true) throw fail(typeof result?.error?.code === 'string' ? result.error.code : 'command/invalid-response', typeof result?.error?.message === 'string' ? result.error.message : '反馈未能保存')\n }\n async export(sessionId: string, signal: AbortSignal, format?: SessionExportFormat): Promise<Response> {\n // 完整归档与对话正文各用官方路由;缺省沿用原 URL,保留子会话和附件。\n const markdown = format === 'markdown'\n const path = markdown ? '/api/session.markdown?sessionId=' : '/api/session.export?includeDescendants=true&sessionId='\n const response = await this.connection().createSharedFetchHandler('/api').fetch(\n new Request('http://localhost' + path + encodeURIComponent(sessionId), { signal }), this.owner())\n const contentType = response.headers.get('content-type')\n const matches = markdown ? contentType?.split(';')[0]?.trim().toLowerCase() === 'text/markdown' : contentType?.includes('application/zip')\n if (!response.ok || !response.body || !matches) throw fail('command/export-unavailable', markdown ? '主机未能生成会话对话正文' : '主机未能生成会话日志归档')\n return response\n }\n}\n","/**\n * Adapter for the DSH rc.2 controller API.\n *\n * DeepPilot's phone protocol deliberately speaks one stable in-process\n * `apiProxy` vocabulary. This adapter maps it to the current public\n * Session/Workspace controllers and Gateway-backed Remote Events, keeping\n * the phone protocol isolated from the Host API.\n */\nimport { randomUUID } from 'node:crypto'\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n startDshRemoteInteractions,\n type RemoteApprovalRequest,\n type RemoteQuestionRequest,\n} from './dsh-remote-interactions.ts'\nimport { historyWindow, normalizePageLimit } from './host-event-projection.ts'\nimport { DshCommandApi } from './command-api.ts'\nimport type { CommandsApiLike } from './host-api.ts'\nimport type {\n ApiProxyLike,\n DirectoryListingLike,\n HistoryResult,\n HostScheduleCreateRequestLike,\n HostScheduleDeleteEndedResult,\n HostScheduleEditOutcome,\n HostScheduleEditRequestLike,\n HostScheduleLimitsView,\n HostScheduleListResultLike,\n HostScheduleRunOutcome,\n HostScheduleTaskView,\n HostScheduleViewOutcome,\n HostSessionModels,\n LegacyScheduleApiLike,\n MuxFrameLike,\n PhoneSessionRow,\n ScheduleHistoryViewLike,\n ScheduleTaskViewLike,\n SessionEventLike,\n WorkspaceViewLike,\n} from './host-api.ts'\nimport type {\n HostScheduleCreateSpec,\n HostScheduleDeliveryReceipt,\n HostSchedulePatch,\n HostScheduleTarget,\n HostScheduleTargetRequest,\n HostScheduleTiming,\n} from './protocol.ts'\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface SessionControllerLike {\n list(request: { cursor?: string }, signal: AbortSignal): Promise<{ items: readonly unknown[] }>\n /**\n * Whole-log read used by `sessions.history`.\n *\n * DSH 0.2.2-alpha.2 removed this method in favour of `page`. The seven earlier\n * admitted hosts still have it and cannot be served by `page` instead: their\n * `SessionPageRequest.throughSeq` is mandatory and validated, while this\n * adapter has no log cursor to pass. `sessions.history` therefore branches on\n * the presence of `inspect` rather than switching every host to one path.\n */\n inspect?(sessionId: string, signal?: AbortSignal): Promise<{ events: readonly unknown[] }>\n /**\n * One message-aligned history page; present on every admitted host.\n *\n * Its `beforeSeq` is a log offset, not the seq the phone sends, so a backward\n * cut has to travel as the inclusive `throughSeq` instead.\n */\n page?(request: {\n address: { kind: 'session'; sessionId: string }\n throughSeq?: number\n maxMessages?: number\n }, signal: AbortSignal): Promise<{ records: readonly unknown[]; hasMore: boolean }>\n create(request: Record<string, unknown>): Promise<{ sessionId: string; agentPreset?: string }>\n fork(request: { sessionId: string; atSeq?: number }): Promise<{ sessionId: string }>\n modelCatalog(): Promise<unknown>\n selectModel(request: Record<string, unknown>): Promise<{ selected: { provider: string; model: string; reasoningEffort?: string } }>\n rename(request: { sessionId: string; title: string }): Promise<{ title: string; seq: number }>\n prompt(request: Record<string, unknown>, signal: AbortSignal): Promise<{ accepted: true }>\n attachment(request: { sessionId: string; attachmentId: string }): Promise<{ attachment: { mediaType?: string }; data: string }>\n cancel(request: { sessionId: string }): { accepted: true }\n /** Absent on hosts that predate DSH's pending-queue RPC. */\n updateQueue?(request: { sessionId: string; itemId: string; action: { kind: string } }): Promise<{ accepted: true }>\n /**\n * Non-activating projection baseline for one session.\n *\n * The signal is mandatory: DSH 0.2.1-alpha.2 aborts before it reads anything,\n * so a caller that omits it gets no baseline at all. The bridge's refresh is\n * fire-and-forget and has no lifetime of its own, so callers pass a signal\n * that never aborts.\n */\n projections(request: { sessionId: string }, signal: AbortSignal):\n Promise<{ asOfSeq: number; values: Record<string, unknown> } | null>\n}\n\ninterface WorkspaceControllerLike {\n create(request: { path: string }): Promise<{ workspace: unknown; created: boolean }>\n archiveSession(request: { sessionId: string }): Promise<{ archivedSessionIds: readonly string[] }>\n unarchiveSession(request: { sessionId: string }): Promise<{ archivedSessionIds: readonly string[] }>\n follow(signal: AbortSignal): AsyncIterable<{ type: string; value?: unknown }>\n}\n\n/** 会话级 Schedule 控制器(DSH ≤ 0.2.2-alpha.2):任务绑定单一 Session。 */\ninterface LegacyScheduleControllerLike {\n list(request: { sessionId: string }): Promise<readonly unknown[]>\n history(request: { sessionId: string; id: string; limit: number; before?: string }): Promise<unknown>\n create(sessionId: string, request: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>\n update(request: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>\n delete(request: { sessionId: string; id: string }, signal?: AbortSignal): Promise<unknown>\n}\n\n/**\n * 宿主级 Schedule 控制器(DSH 0.2.2-alpha.3 起)。\n *\n * 方法按真实服务面声明:`list` 返回分页对象、`view` 返回任务或 miss 结果、\n * `create/edit/run` 返回任务/删除/冲突/进行中等结果对象,`edit` 取代了\n * update/delete,`deliveryLimits` 是同步读取。返回值保持 unknown:适配层负责\n * 形状归一化,控制器细节不外泄给 Bridge。\n */\ninterface HostScheduleControllerLike {\n list(request?: { active?: boolean; limit?: number; offset?: number }): Promise<unknown>\n view(request: { id: string }): Promise<unknown>\n create(request: Record<string, unknown>, actorSessionId?: unknown, signal?: AbortSignal): Promise<unknown>\n edit(request: Record<string, unknown>, actorSessionId?: unknown, signal?: AbortSignal): Promise<unknown>\n run(request: { id: string }, signal?: AbortSignal): Promise<unknown>\n history(request: { id: string; limit: number; before?: string }): Promise<unknown>\n deliveryLimits(): unknown\n /** 上游名沿用 alpha.2 的 inactive,alpha.3 实际删除的是 `status=\"ended\"` 的任务。 */\n deleteInactive(): Promise<unknown>\n}\n\ntype ScheduleControllerLike = LegacyScheduleControllerLike | HostScheduleControllerLike\n\n/**\n * 形状探测:宿主级模型只有 `edit`(没有 `update`)。不按版本号字符串判断——\n * 版本号无法说明 Schedule bundle 是否挂载、也无法覆盖回退组合。\n */\nfunction isHostScheduleController(controller: ScheduleControllerLike): controller is HostScheduleControllerLike {\n const value = controller as { edit?: unknown; update?: unknown }\n return typeof value.edit === 'function' && typeof value.update !== 'function'\n}\n\ninterface DirectoryPickerControllerLike {\n list(path: string | undefined, signal: AbortSignal): Promise<DirectoryListingLike>\n pick(signal: AbortSignal): Promise<string | null>\n}\n\ninterface DeferredInteraction {\n resolve(value: unknown): void\n /** Convert the stable phone-protocol response into a Host waterfall result. */\n map(value: unknown): unknown\n}\n\n/** Which first-answer-wins interaction a phone may surface. */\nexport type DshInteractionKind = 'approval' | 'question'\n\nexport interface DshApiProxyOptions {\n /**\n * Decide whether the resident DeepPilot Remote Client represents a usable\n * phone surface. Returning false delegates only this Client delivery; API\n * Gateway keeps the official Web delivery independent.\n */\n shouldSurfaceInteraction?: (kind: DshInteractionKind) => boolean\n}\n\n/** Direct-controller adapter with the shape HostBridge consumes. */\nexport class DshApiProxy implements ApiProxyLike {\n /** Probe the Host's generated reflection, not this adapter's listener registration. */\n get assistantStreamAvailable(): boolean {\n const typert = this.ctx.get('typert') as {\n getPackage?(name: string, face: 'host'): {\n model?: { services?: Array<{ key?: string; types?: Array<{ name?: string }> }> }\n } | undefined\n } | undefined\n try {\n return typert?.getPackage?.('@deepseek-ai/dsh-api-session-controller', 'host')?.model?.services\n ?.some(service => service.key === 'sessionController' &&\n service.types?.some(type => type.name === 'SessionAssistantStreamFrame')) === true\n } catch {\n // 可选反射尚未注册时只关闭流能力,不让 welcome 握手随它失败。\n return false\n }\n }\n\n private readonly commandApi: DshCommandApi\n get commands(): CommandsApiLike | undefined {\n if (!this.commandApi.available) return undefined\n return {\n list: request => this.call(() => this.commandApi.list(request.payload!.sessionId)),\n execute: request => this.call(() => this.commandApi.execute(request.payload!.sessionId, request.payload!.line, request.payload!.attachments, request.payload!.expectedDefinitionId)),\n permissions: request => this.call(() => this.commandApi.permissions(request.payload!.sessionId)),\n feedback: request => this.call(() => this.commandApi.feedback(request.payload!.sessionId, request.payload!.text, request.payload!.category)),\n markdownAvailable: this.commandApi.markdownAvailable,\n export: (request, signal) => this.commandApi.export(request.payload!.sessionId, signal, request.payload!.format),\n }\n }\n private readonly session: SessionControllerLike\n private readonly workspaceController: WorkspaceControllerLike | undefined\n private scheduleController: ScheduleControllerLike | undefined\n private scheduleApi: ApiProxyLike['schedule'] | undefined\n private readonly directoryPicker: DirectoryPickerControllerLike | undefined\n private readonly interactions = new Map<string, DeferredInteraction>()\n private readonly shouldSurfaceInteraction: (kind: DshInteractionKind) => boolean\n\n constructor(private readonly ctx: Context, options: DshApiProxyOptions = {}) {\n this.commandApi = new DshCommandApi(ctx)\n const session = ctx.get('sessionController') as SessionControllerLike | undefined\n if (session === undefined) throw new Error('DSH sessionController is unavailable')\n this.session = session\n this.workspaceController = ctx.get('workspaceController') as WorkspaceControllerLike | undefined\n // A profile without the optional workspace service cannot restore a\n // session; keep the phone capability bit honest.\n if (this.workspaceController === undefined) delete this.workspace?.unarchiveSession\n // 同理:Host 没有 updateQueue 时撤掉入口,promptQueue 能力位随之为 false。\n if (typeof this.session.updateQueue !== 'function') delete this.sessions.updateQueue\n this.directoryPicker = ctx.get('directoryPickerController') as DirectoryPickerControllerLike | undefined\n this.shouldSurfaceInteraction = options.shouldSurfaceInteraction ?? (() => true)\n this.resolveSchedule()\n // 技能服务可能晚于桥接层挂载;能力探测与请求都读取当前服务,不缓存缺席。\n Object.defineProperty(this.sessions, 'skills', { get: () => {\n const catalog = ctx.get('sessionSkillCatalog') as {\n list(request: { sessionId: string }, signal: AbortSignal): Promise<{ skills: import('./protocol.ts').SkillSummary[] }>\n } | undefined\n if (typeof catalog?.list !== 'function') return undefined\n const read: NonNullable<ApiProxyLike['sessions']['skills']> = async (request) =>\n this.call(() => catalog.list({ sessionId: request.payload!.sessionId }, new AbortController().signal))\n return read\n } })\n }\n\n /**\n * Resolve the optional Schedule service lazily, on every read.\n *\n * The bridge is built from a `ctx.inject` on sessionController / connection\n * / typertGateway, and `schedule` is deliberately NOT in that list: it is a\n * capability the host composition may or may not mount. On a DSH 0.2.0 host\n * those three services become ready well before ScheduleService finishes its\n * own async init, so a value captured in the constructor stayed `undefined`\n * for the bridge's whole lifetime and welcome advertised `schedules=false`\n * even with the service mounted.\n *\n * Once resolved the controller is kept: services are not torn down and\n * rebuilt underneath a live bridge, so re-resolving per call would be pure\n * overhead.\n */\n private resolveSchedule(): void {\n if (this.scheduleController !== undefined) return\n const controller = this.ctx.get('schedule') as ScheduleControllerLike | undefined\n if (controller === undefined) return\n this.scheduleController = controller\n this.scheduleApi = isHostScheduleController(controller)\n ? this.createHostScheduleApi(controller)\n : this.createLegacyScheduleApi(controller)\n }\n\n get schedule(): ApiProxyLike['schedule'] {\n this.resolveSchedule()\n return this.scheduleApi\n }\n\n /** 会话级适配器:行为与宿主级扩展加入前逐字节一致,只多一个判别位。 */\n private createLegacyScheduleApi(controller: LegacyScheduleControllerLike): LegacyScheduleApiLike {\n return {\n mode: 'legacy',\n list: async (request) => this.call(async () => ({\n sessionId: request.payload!.sessionId,\n tasks: (await controller.list({ sessionId: request.payload!.sessionId })).map(toScheduleTask),\n })),\n history: async (request) => this.call(async () => {\n const value = await controller.history({\n sessionId: request.payload!.sessionId,\n id: request.payload!.id,\n limit: request.payload!.limit,\n ...(request.payload!.before !== undefined ? { before: request.payload!.before } : {}),\n }) as Record<string, unknown>\n if (typeof value.code === 'string') {\n throw Object.assign(new Error(String(value.code)), { code: value.code })\n }\n return { sessionId: request.payload!.sessionId, history: toScheduleHistory(value) }\n }),\n create: async (request) => this.call(async () => {\n const { sessionId, ...createRequest } = request.payload!\n return toScheduleTask(await controller.create(sessionId, createRequest))\n }),\n update: async (request) => this.call(async () => {\n const payload = request.payload!\n const result = await controller.update({\n ...payload,\n expected: toScheduleExpected(payload.expected),\n }) as Record<string, unknown>\n return {\n id: String(result.id ?? payload.id),\n updated: result.updated === true,\n ...(result.record !== undefined ? { record: toScheduleTask(result.record) } : {}),\n ...(typeof result.code === 'string' ? { code: result.code } : {}),\n }\n }),\n delete: async (request) => this.call(async () => {\n const result = await controller.delete({ sessionId: request.payload!.sessionId, id: request.payload!.id }) as Record<string, unknown>\n return { id: String(result.id), deleted: result.deleted === true, ...(result.code !== undefined ? { code: String(result.code) } : {}) }\n }),\n }\n }\n\n /**\n * 宿主级适配器:把 DSH `ScheduleService` 的分页列表/结果对象归一化成 Bridge\n * 消费的稳定视图。\n *\n * 失败分两类:`view/edit/run/history` 的「找不到」是**结果对象**里的 code\n * (非变更 miss,不抛错),这里翻译成 outcome 或带 code 的 Error;\n * `create/edit/run` 的输入非法与目标已归档是 RemoteError 抛出,由\n * `this.call` 收成 `{code}`,再由 Bridge 按错误词表映射。\n */\n private createHostScheduleApi(controller: HostScheduleControllerLike): NonNullable<ApiProxyLike['schedule']> {\n return {\n mode: 'host',\n list: async (request) => this.call(async () => {\n const payload = request.payload ?? {}\n const value = await controller.list({\n ...(payload.active !== undefined ? { active: payload.active } : {}),\n ...(payload.limit !== undefined ? { limit: payload.limit } : {}),\n ...(payload.offset !== undefined ? { offset: payload.offset } : {}),\n }) as Record<string, unknown>\n const items = Array.isArray(value.items) ? value.items : []\n return {\n tasks: items.map(toHostScheduleTask),\n shown: finiteCount(value.shown, items.length),\n total: finiteCount(value.total, items.length),\n limit: finiteCount(value.limit, payload.limit ?? 100),\n offset: finiteCount(value.offset, payload.offset ?? 0),\n } satisfies HostScheduleListResultLike\n }),\n view: async (request) => this.call(async () => {\n const value = await controller.view({ id: request.payload!.id }) as Record<string, unknown>\n return toHostScheduleViewOutcome(value, request.payload!.id)\n }),\n create: async (request) => this.call(async () => {\n const payload = request.payload!\n const created = await controller.create({\n title: payload.title,\n prompt: payload.prompt,\n timing: payload.timing,\n ...(payload.target !== undefined ? { target: normalizeTargetRequest(payload.target) } : {}),\n } satisfies HostScheduleCreateRequestLike)\n return toHostScheduleTask(created)\n }),\n edit: async (request) => this.call(async () => {\n const payload = request.payload!\n const expected = payload.expectedUpdatedAt !== undefined\n ? { expectedUpdatedAt: payload.expectedUpdatedAt }\n : {}\n const edit: HostScheduleEditRequestLike = payload.action === 'update'\n ? { id: payload.id, action: 'update', patch: normalizeHostPatch(payload.patch), ...expected }\n : { id: payload.id, action: 'delete', ...expected }\n const value = await controller.edit(edit) as Record<string, unknown>\n return toHostScheduleEditOutcome(value, payload.id)\n }),\n run: async (request) => this.call(async () => {\n const value = await controller.run({ id: request.payload!.id }) as Record<string, unknown>\n return toHostScheduleRunOutcome(value, request.payload!.id)\n }),\n history: async (request) => this.call(async () => {\n const value = await controller.history({\n id: request.payload!.id,\n limit: request.payload!.limit,\n ...(request.payload!.before !== undefined ? { before: request.payload!.before } : {}),\n }) as Record<string, unknown>\n if (typeof value.code === 'string') {\n throw Object.assign(new Error(String(value.code)), { code: value.code })\n }\n return toScheduleHistory(value)\n }),\n limits: async () => this.call(async () => {\n const value = controller.deliveryLimits() as { newSessionIntervalSeconds?: unknown } | undefined\n return {\n newSessionIntervalSeconds: Math.max(0, Math.trunc(Number(value?.newSessionIntervalSeconds) || 0)),\n } satisfies HostScheduleLimitsView\n }),\n deleteEnded: async () => this.call(async () => {\n const value = await controller.deleteInactive() as { deleted?: unknown } | undefined\n return {\n deleted: Math.max(0, Math.trunc(Number(value?.deleted) || 0)),\n } satisfies HostScheduleDeleteEndedResult\n }),\n }\n }\n\n /**\n * Decide whether this Gateway Client can surface a phone card. The decision\n * fails open to false so a broken device registry delegates this delivery\n * while the official Web Client remains able to answer.\n */\n private canSurfaceInteraction(kind: DshInteractionKind): boolean {\n try {\n return this.shouldSurfaceInteraction(kind)\n } catch {\n return false\n }\n }\n\n readonly sessions: ApiProxyLike['sessions'] = {\n list: async () => this.call(async () => {\n const value = await this.session.list({}, new AbortController().signal)\n // Keep worker identity internally so HostBridge can suppress notifications\n // as well as exclude these rows from the phone's session list.\n return { items: value.items.map(toPhoneSessionRow) }\n }),\n history: async (request) => this.call(async () => {\n const sessionId = request.payload!.sessionId\n const beforeSeq = request.payload?.beforeSeq\n const limit = normalizePageLimit(request.payload?.maxMessages)\n const inspect = this.session.inspect\n // DSH 0.2.2-alpha.2 dropped `inspect`; the host's own `page` covers the same\n // read with message-aligned windowing. Keep the old path for every host that\n // still has it, because those hosts' `page` demands a log cursor.\n if (inspect === undefined) {\n const page = await this.session.page!({\n address: { kind: 'session', sessionId },\n // `beforeSeq` is exclusive and expressed in seqs; `throughSeq` is an\n // inclusive seq cut, so shift by one. -1 is the host's empty page.\n ...(beforeSeq === undefined ? {} : { throughSeq: beforeSeq - 1 }),\n maxMessages: limit,\n }, new AbortController().signal)\n // Records are `{type:'event', event}` envelopes; the bridge contract is\n // `{event}`. host-bridge re-checks the seq boundary itself, so the\n // inclusive cut cannot make a client loop on the same page.\n return {\n events: page.records.map((record) => ({\n event: (record as { event: SessionEventLike }).event,\n })),\n hasMore: page.hasMore,\n }\n }\n let inspected: { events: readonly unknown[] }\n try {\n inspected = await inspect.call(this.session, sessionId)\n } catch (error) {\n // No message data is logged: only the opaque session id and Host's\n // safe error chain, enough to distinguish an unsupported legacy log\n // from a bridge regression.\n console.warn(`[deeppilot] session history unavailable for ${JSON.stringify(sessionId)}: ${toError(error).code}: ${toError(error).message}`)\n throw error\n }\n // 分页窗口的策略住在 host-event-projection(与它依赖的投影规则同处);\n // 适配器只负责拉全量事件、把窗口映射成 HistoryResult。\n const window = historyWindow(inspected.events, {\n beforeSeq,\n limit,\n })\n return { events: window.events as HistoryResult['events'], hasMore: window.hasMore }\n }),\n prompt: async (request) => this.call(() => this.session.prompt({\n ...request.payload!,\n requestId: request.rpcId ?? randomUUID(),\n }, new AbortController().signal)),\n create: async (request) => this.call(() => this.session.create(request.payload ?? {})),\n fork: async (request) => this.call(() => this.session.fork(request.payload!)),\n models: async (request) => this.call(async () => projectModels(\n await this.session.modelCatalog(),\n String(request.payload?.sessionId ?? ''),\n await this.session.list({}, new AbortController().signal),\n )),\n selectModel: async (request) => this.call(() => this.session.selectModel(request.payload ?? {})),\n rename: async (request) => this.call(() => this.session.rename(request.payload!)),\n cancel: async (request) => this.call(() => this.session.cancel(request.payload!)),\n updateQueue: async (request) => this.call(() => this.session.updateQueue!(request.payload!)),\n attachment: async (request) => this.call(() => this.session.attachment(request.payload!)),\n projections: async (request) => this.call(() => this.session.projections(request.payload!, new AbortController().signal)),\n }\n\n readonly workspace: ApiProxyLike['workspace'] = {\n list: async () => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const baseline = await readWorkspaceBaseline(this.workspaceController)\n return {\n items: baseline.items.map(toWorkspaceView),\n archivedSessionIds: baseline.archivedSessionIds.map(String),\n }\n }),\n create: async (request) => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const value = await this.workspaceController.create(request.payload!)\n return { workspace: toWorkspaceView(value.workspace), created: value.created === true }\n }),\n archiveSession: async (request) => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const value = await this.workspaceController.archiveSession(request.payload!)\n return { archivedSessionIds: [...value.archivedSessionIds] }\n }),\n unarchiveSession: async (request) => this.call(async () => {\n if (this.workspaceController === undefined) throw unavailable('workspace controller unavailable')\n const value = await this.workspaceController.unarchiveSession(request.payload!)\n return { archivedSessionIds: [...value.archivedSessionIds] }\n }),\n }\n\n readonly host: ApiProxyLike['host'] = {\n listDirectory: async (request, signal) => this.call(async () => {\n if (this.directoryPicker === undefined) throw unavailable('directory picker unavailable')\n return await this.directoryPicker.list(request.payload?.path, signal ?? new AbortController().signal)\n }),\n pickDirectory: async (_request, signal) => this.call(async () => {\n if (this.directoryPicker === undefined) throw unavailable('directory picker unavailable')\n return { path: await this.directoryPicker.pick(signal ?? new AbortController().signal) }\n }),\n }\n\n readonly events: ApiProxyLike['events'] = {\n mux: (_request, signal) => this.mux(signal),\n host: (_request, signal) => this.hostEvents(signal),\n }\n\n async respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }): Promise<{ accepted: boolean; reason?: string }> {\n const pending = this.interactions.get(message.rpcId)\n if (pending === undefined) return { accepted: false, reason: 'not-pending' }\n if (!message.result.ok) return { accepted: false, reason: 'bad-response' }\n this.interactions.delete(message.rpcId)\n pending.resolve(pending.map(message.result.value))\n return { accepted: true }\n }\n\n private async *mux(signal: AbortSignal): AsyncIterable<MuxFrameLike> {\n const queue = new AsyncFrameQueue(signal)\n const offEvent = this.ctx.on('session/event' as never, ((session: { id: string; header?: { origin?: string } }, event: unknown) => {\n queue.push({\n type: 'session/event', sessionId: String(session.id),\n // Header origin is available before the next list refresh. A header's\n // parentSession alone is fork lineage, not proof of a worker session.\n isSubagent: session.header?.origin === 'subagent',\n event: event as MuxFrameLike['event'],\n })\n }) as never, { global: true })\n // This is an agent-scoped publication with ONE payload argument. Reading\n // `(session, frame)` here silently loses every real Host stream frame.\n const offAssistantStream = this.ctx.on('agent/assistant-stream' as never, (({ agent, frame }: {\n agent: { session: { id: string; header?: { origin?: string } } }; frame: unknown\n }) => {\n queue.push({ type: 'session/assistant-stream', sessionId: String(agent.session.id),\n isSubagent: agent.session.header?.origin === 'subagent', value: frame })\n }) as never, { global: true })\n const offAgentDisposed = this.ctx.on('agent/disposed' as never, (({ agent }: {\n agent: { session: { id: string } }\n }) => queue.push({ type: 'session/assistant-stream-disposed', sessionId: String(agent.session.id) })) as never, { global: true })\n const projections = this.ctx.get('sessionProjections') as {\n onChanged?(listener: (session: { id: string }, key: string, value: unknown) => void): () => void\n } | undefined\n const offProjection = projections?.onChanged?.((session, key, value) => {\n queue.push({ type: 'session/projection', sessionId: String(session.id), key, value })\n })\n const offSkills = this.ctx.on('skills/change' as never, (() => {\n queue.push({ type: 'skills/changed' })\n }) as never, { global: true })\n const offCommands = ['commands/change', 'permission-presets/catalog-changed'].map(event =>\n this.ctx.on(event as never, (() => queue.push({ type: 'commands/changed' })) as never, { global: true }))\n\n let disposeInteractions: (() => Promise<void>) | undefined\n try {\n disposeInteractions = await startDshRemoteInteractions(this.ctx, {\n approval: (sessionId, request, next) => this.answerApproval(queue, sessionId, request, next),\n question: (sessionId, request, next) => this.answerQuestion(queue, sessionId, request, next),\n })\n } catch (error) {\n console.warn(`[deeppilot] DSH Remote interaction client unavailable: ${toError(error).message}`)\n }\n\n try {\n yield* queue.iterate()\n } finally {\n offEvent()\n offAssistantStream()\n offAgentDisposed()\n offProjection?.()\n offSkills()\n offCommands.forEach(dispose => dispose())\n await disposeInteractions?.()\n queue.close()\n }\n }\n\n private answerApproval(\n queue: AsyncFrameQueue,\n sessionId: string,\n request: RemoteApprovalRequest,\n next: () => Promise<unknown>,\n ): Promise<unknown> {\n if (!this.canSurfaceInteraction('approval')) return next()\n const rpcId = randomUUID()\n const response = deferred<unknown>()\n const abort = (): void => response.resolve('cancelled')\n request.signal?.addEventListener('abort', abort, { once: true })\n this.interactions.set(rpcId, {\n resolve: response.resolve,\n map: value => {\n const outcome = (value as { outcome?: unknown } | undefined)?.outcome\n return outcome === 'allowed-once' || outcome === 'rejected' ? outcome : 'unavailable'\n },\n })\n queue.push({\n type: 'approval/requested', rpcId, sessionId, approvalId: rpcId,\n toolName: String(request.toolName ?? 'tool'), reason: String(request.reason ?? ''),\n ...(request.callId ? { callId: request.callId } : {}),\n })\n return response.promise.finally(() => {\n request.signal?.removeEventListener('abort', abort)\n this.interactions.delete(rpcId)\n queue.push({ type: 'approval/resolved', approvalId: rpcId })\n })\n }\n\n private answerQuestion(\n queue: AsyncFrameQueue,\n sessionId: string,\n request: RemoteQuestionRequest,\n next: () => Promise<unknown>,\n ): Promise<unknown> {\n if (!this.canSurfaceInteraction('question')) return next()\n const rpcId = randomUUID()\n const response = deferred<unknown>()\n const abort = (): void => response.reject(new Error('question cancelled'))\n request.signal?.addEventListener('abort', abort, { once: true })\n this.interactions.set(rpcId, {\n resolve: response.resolve,\n map: value => (value as { answer?: unknown } | undefined)?.answer ?? value,\n })\n queue.push({ type: 'question/requested', rpcId, sessionId, questions: request.questions ?? [] })\n return response.promise.finally(() => {\n request.signal?.removeEventListener('abort', abort)\n this.interactions.delete(rpcId)\n queue.push({ type: 'question/resolved', questionRpcId: rpcId })\n })\n }\n\n private async *hostEvents(signal: AbortSignal): AsyncIterable<MuxFrameLike> {\n const queue = new AsyncFrameQueue(signal)\n const listen = (event: string, type: string, project?: (...args: unknown[]) => Record<string, unknown>) =>\n this.ctx.on(event as never, ((...args: unknown[]) => queue.push({ type, ...(project?.(...args) ?? {}) })) as never, { global: true })\n const off = [\n listen('api-session/added', 'host/session-added'),\n listen('api-session/removed', 'host/session-removed'),\n listen('api-session/status', 'host/session-status', (sessionId, running) => ({ sessionId: String(sessionId), running: running === true })),\n listen('api-session/activity', 'host/session-added'),\n listen('schedule/changed', 'host/schedule-changed'),\n ]\n const workspaceAbort = new AbortController()\n const stop = (): void => workspaceAbort.abort()\n signal.addEventListener('abort', stop, { once: true })\n const workspaceTask = this.workspaceController === undefined ? undefined : (async () => {\n try {\n for await (const frame of this.workspaceController!.follow(workspaceAbort.signal)) {\n if (frame.type === 'archived') queue.push({ type: 'host/archived-sessions-changed', archivedSessionIds: (frame as { archivedSessionIds?: unknown }).archivedSessionIds } as MuxFrameLike)\n else if (frame.type !== 'baseline') queue.push({ type: 'host/workspace-changed' })\n }\n } catch { /* Host shutdown / withdrawn controller ends the stream. */ }\n })()\n try {\n yield* queue.iterate()\n } finally {\n for (const dispose of off) dispose()\n signal.removeEventListener('abort', stop)\n workspaceAbort.abort()\n void workspaceTask\n queue.close()\n }\n }\n\n private async call<T>(invoke: () => T | Promise<T>): Promise<{ result: RpcResult<T> }> {\n try { return { result: { ok: true, value: await invoke() } } }\n catch (error) { return { result: { ok: false, error: toError(error) } } }\n }\n}\n\nfunction deferred<T>(): { promise: Promise<T>; resolve(value: T): void; reject(reason: unknown): void } {\n let resolve!: (value: T) => void\n let reject!: (reason: unknown) => void\n const promise = new Promise<T>((ok, fail) => { resolve = ok; reject = fail })\n return { promise, resolve, reject }\n}\n\nclass AsyncFrameQueue {\n private readonly frames: MuxFrameLike[] = []\n private wake: (() => void) | undefined\n private closed = false\n constructor(signal: AbortSignal) {\n signal.addEventListener('abort', () => this.close(), { once: true })\n }\n push(frame: MuxFrameLike): void { if (!this.closed) { this.frames.push(frame); this.wake?.() } }\n close(): void { if (!this.closed) { this.closed = true; this.wake?.() } }\n async *iterate(): AsyncIterable<MuxFrameLike> {\n while (!this.closed) {\n const frame = this.frames.shift()\n if (frame !== undefined) { yield frame; continue }\n await new Promise<void>(resolve => { this.wake = resolve })\n this.wake = undefined\n }\n }\n}\n\nfunction unavailable(message: string): Error & { code: string } {\n return Object.assign(new Error(message), { code: 'directory-picker-unavailable' })\n}\nfunction toError(error: unknown): { code: string; message: string } {\n const value = error as { code?: unknown; message?: unknown }\n return { code: typeof value?.code === 'string' ? value.code : 'internal', message: typeof value?.message === 'string' ? value.message : String(error) }\n}\nfunction toPhoneSessionRow(value: unknown): PhoneSessionRow {\n const row = value as Record<string, unknown>\n return {\n sessionId: String(row.sessionId ?? ''), updatedAt: Number(row.updatedAt ?? Date.now()), running: row.running === true,\n ...(row.blank === true ? { blank: true } : {}), ...(typeof row.cwd === 'string' ? { cwd: row.cwd } : {}),\n ...(typeof row.origin === 'string' ? { origin: row.origin } : {}), ...(typeof row.parentSessionId === 'string' ? { parentSessionId: row.parentSessionId } : {}),\n ...(row.projections && typeof row.projections === 'object' ? { projections: row.projections as PhoneSessionRow['projections'] } : {}),\n }\n}\nfunction toWorkspaceView(value: unknown): WorkspaceViewLike {\n const row = value as Record<string, unknown>\n return { workspaceId: String(row.workspaceId ?? ''), title: String(row.title ?? ''), path: String(row.path ?? ''), sessionIds: Array.isArray(row.sessionIds) ? row.sessionIds.map(String) : [] }\n}\nfunction toScheduleExpected(value: unknown): Record<string, unknown> {\n const row = value !== null && typeof value === 'object' ? value as Record<string, unknown> : {}\n const { state: _state, deliveryMode: _deliveryMode, ...record } = row\n return record\n}\nfunction toScheduleTask(value: unknown): ScheduleTaskViewLike {\n const row = value as Record<string, unknown>\n const kind = ['after', 'at', 'every', 'daily', 'weekly', 'cron'].includes(String(row.kind))\n ? String(row.kind) as ScheduleTaskViewLike['kind']\n : 'at'\n const optionalNumber = (key: string): number | undefined => typeof row[key] === 'number' && Number.isFinite(row[key]) ? row[key] as number : undefined\n return {\n id: String(row.id ?? ''),\n kind,\n title: String(row.title ?? ''),\n prompt: String(row.prompt ?? ''),\n scheduledAt: String(row.scheduledAt ?? ''),\n state: row.state === 'overdue' ? 'overdue' : 'scheduled',\n deliveryMode: 'host',\n ...(optionalNumber('afterSeconds') !== undefined ? { afterSeconds: optionalNumber('afterSeconds')! } : {}),\n ...(optionalNumber('everySeconds') !== undefined ? { everySeconds: optionalNumber('everySeconds')! } : {}),\n ...(typeof row.time === 'string' ? { time: row.time } : {}),\n ...(typeof row.timeZone === 'string' ? { timeZone: row.timeZone } : {}),\n ...(Array.isArray(row.weekdays) ? { weekdays: row.weekdays.filter((day): day is number => typeof day === 'number') } : {}),\n ...(typeof row.expression === 'string' ? { expression: row.expression } : {}),\n }\n}\nfunction toScheduleHistory(value: unknown): ScheduleHistoryViewLike {\n const row = value as Record<string, unknown>\n const records = Array.isArray(row.records) ? row.records : []\n return {\n id: String(row.id ?? ''),\n records: records.map((entry) => {\n const item = entry as Record<string, unknown>\n return {\n scheduledAt: String(item.scheduledAt ?? ''),\n deliveredAt: String(item.deliveredAt ?? ''),\n messageId: String(item.messageId ?? ''),\n ...(typeof item.prompt === 'string' ? { prompt: item.prompt } : {}),\n }\n }),\n earlierRecordsUnavailable: row.earlierRecordsUnavailable === true,\n ...(typeof row.earlierRecordsPruned === 'boolean' ? { earlierRecordsPruned: row.earlierRecordsPruned } : {}),\n retention: {\n days: Number((row.retention as { days?: unknown } | undefined)?.days ?? 0),\n records: Number((row.retention as { records?: unknown } | undefined)?.records ?? 0),\n },\n ...(typeof row.nextBefore === 'string' ? { nextBefore: row.nextBefore } : {}),\n }\n}\n/* ---------- 宿主级 Schedule 的形状归一化 ---------- */\n\n/**\n * 请求侧 target:`new_once`/`new` 的会话由 Host 预留或新建,客户端若把查看结果\n * 里的 `sessionId` 原样回传,不向上游透传(Host 的创建/编辑请求没有这个字段)。\n */\nfunction normalizeTargetRequest(value: HostScheduleTargetRequest): HostScheduleTargetRequest {\n if (value.kind === 'session') {\n return { kind: 'session', sessionId: String(value.sessionId ?? '') }\n }\n const create = toHostScheduleCreateSpec(value.create)\n return { kind: value.kind, ...(create !== undefined ? { create } : {}) }\n}\n\n/** 编辑补丁归一化:字段按契约逐个透传,target 走请求侧投影。 */\nfunction normalizeHostPatch(value: HostSchedulePatch): HostSchedulePatch {\n return {\n ...(value.title !== undefined ? { title: value.title } : {}),\n ...(value.prompt !== undefined ? { prompt: value.prompt } : {}),\n ...(value.timing !== undefined ? { timing: value.timing } : {}),\n ...(value.target !== undefined ? { target: normalizeTargetRequest(value.target) } : {}),\n ...(value.active !== undefined ? { active: value.active } : {}),\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return isRecord(value) ? value : {}\n}\n\n/** 非负安全整数;形状不符时退回 fallback(分页字段只用于展示)。 */\nfunction finiteCount(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : fallback\n}\n\nfunction positiveSeconds(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0\n}\n\nfunction hostResultCode(value: Record<string, unknown>): string | undefined {\n return typeof value.code === 'string' ? value.code : undefined\n}\n\n/** 结果对象里的「非变更 miss」不是抛错,这里翻译成 outcome 或带 code 的 Error。 */\nfunction throwHostResultCode(code: string): never {\n throw Object.assign(new Error(code), { code })\n}\n\nfunction toHostScheduleCreateSpec(value: unknown): HostScheduleCreateSpec | undefined {\n const row = toRecord(value)\n const spec: HostScheduleCreateSpec = {\n ...(typeof row.cwd === 'string' ? { cwd: row.cwd } : {}),\n ...(typeof row.agentPreset === 'string' ? { agentPreset: row.agentPreset } : {}),\n }\n return Object.keys(spec).length > 0 ? spec : undefined\n}\n\n/**\n * 交付目标投影。`session` 与 `new_once` 带 sessionId(后者是创建时 Host 预留\n * 的会话);`new` 每次投递新建会话。`create` 省略时按 Host 默认。\n */\nfunction toHostScheduleTarget(value: unknown): HostScheduleTarget {\n const target = toRecord(value)\n const create = toHostScheduleCreateSpec(target.create)\n if (target.kind === 'session') {\n return { kind: 'session', sessionId: String(target.sessionId ?? '') }\n }\n if (target.kind === 'new_once') {\n return {\n kind: 'new_once',\n sessionId: String(target.sessionId ?? ''),\n ...(create !== undefined ? { create } : {}),\n }\n }\n return { kind: 'new', ...(create !== undefined ? { create } : {}) }\n}\n\nfunction toHostScheduleReceipt(value: unknown): HostScheduleDeliveryReceipt {\n const row = toRecord(value)\n return {\n scheduledAt: String(row.scheduledAt ?? ''),\n deliveredAt: String(row.deliveredAt ?? ''),\n messageId: String(row.messageId ?? ''),\n targetSessionId: String(row.targetSessionId ?? ''),\n }\n}\n\n/**\n * timing 反向投影:DSH 存储记录用 camelCase(`afterSeconds`/`timeZone`),wire\n * 用 PROTOCOL.md 的 snake_case 嵌套结构。\n *\n * `at` 存储后只剩 `scheduledAt`(原始绝对时刻不是独立字段),因此用排定时刻\n * 回填——对 active 的一次性任务它就是目标时刻。\n */\nfunction toHostScheduleTiming(row: Record<string, unknown>): HostScheduleTiming {\n switch (row.kind) {\n case 'after':\n return { kind: 'after', after_seconds: positiveSeconds(row.afterSeconds) }\n case 'every':\n return { kind: 'every', every_seconds: positiveSeconds(row.everySeconds) }\n case 'daily':\n return { kind: 'daily', daily: localTimeInput(row) }\n case 'weekly':\n return {\n kind: 'weekly',\n weekly: {\n ...localTimeInput(row),\n weekdays: Array.isArray(row.weekdays)\n ? row.weekdays.filter((day): day is number => typeof day === 'number')\n : [],\n },\n }\n case 'cron':\n return {\n kind: 'cron',\n cron: { expression: String(row.expression ?? ''), time_zone: String(row.timeZone ?? '') },\n }\n case 'rrule':\n return { kind: 'rrule', rrule: String(row.rrule ?? '') }\n default:\n return { kind: 'at', at: String(row.scheduledAt ?? '') }\n }\n}\n\nfunction localTimeInput(row: Record<string, unknown>): { time: string; time_zone: string } {\n return { time: String(row.time ?? ''), time_zone: String(row.timeZone ?? '') }\n}\n\n/** Host `ScheduleCatalogEntry` -> wire `schedule.host.task`(丢弃 involvedSessionIds)。 */\nfunction toHostScheduleTask(value: unknown): HostScheduleTaskView {\n const row = toRecord(value)\n return {\n id: String(row.id ?? ''),\n title: String(row.title ?? ''),\n prompt: String(row.prompt ?? ''),\n timing: toHostScheduleTiming(row),\n target: toHostScheduleTarget(row.target),\n // Host 只给这三个状态;形态异常时按 active 保留(至少让任务在列表里可见,\n // 不能让一条真实任务因为字段异常而消失)。\n status: row.status === 'paused' || row.status === 'ended' ? row.status : 'active',\n scheduledAt: String(row.scheduledAt ?? ''),\n ...(typeof row.createdAt === 'string' ? { createdAt: row.createdAt } : {}),\n ...(typeof row.updatedAt === 'string' ? { updatedAt: row.updatedAt } : {}),\n ...(typeof row.executionActive === 'boolean' ? { executionActive: row.executionActive } : {}),\n ...(typeof row.deliveryFloorEndsAt === 'string' ? { deliveryFloorEndsAt: row.deliveryFloorEndsAt } : {}),\n ...(isRecord(row.lastDelivery) ? { lastDelivery: toHostScheduleReceipt(row.lastDelivery) } : {}),\n }\n}\n\nfunction toHostScheduleViewOutcome(value: Record<string, unknown>, id: string): HostScheduleViewOutcome {\n const code = hostResultCode(value)\n if (code === 'schedule_not_found') return { kind: 'not-found', id }\n if (code !== undefined) throwHostResultCode(code)\n return { kind: 'task', task: toHostScheduleTask(value) }\n}\n\nfunction toHostScheduleEditOutcome(value: Record<string, unknown>, id: string): HostScheduleEditOutcome {\n const code = hostResultCode(value)\n if (code === 'schedule_conflict') return { kind: 'conflict', id }\n if (code === 'schedule_not_found') return { kind: 'not-found', id }\n if (code !== undefined) throwHostResultCode(code)\n if (value.deleted === true) return { kind: 'deleted', id: String(value.id ?? id) }\n return { kind: 'task', task: toHostScheduleTask(value) }\n}\n\nfunction toHostScheduleRunOutcome(value: Record<string, unknown>, id: string): HostScheduleRunOutcome {\n const code = hostResultCode(value)\n if (code === 'schedule_execution_pending') return { kind: 'pending', id }\n if (code === 'schedule_not_found') return { kind: 'not-found', id }\n if (code !== undefined) throwHostResultCode(code)\n return { kind: 'task', task: toHostScheduleTask(value) }\n}\n\nasync function readWorkspaceBaseline(controller: WorkspaceControllerLike): Promise<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }> {\n const abort = new AbortController()\n const iterator = controller.follow(abort.signal)[Symbol.asyncIterator]()\n try {\n const first = await iterator.next()\n const baseline = first.value as { type?: string; value?: { items?: unknown[]; archivedSessionIds?: unknown[] } } | undefined\n if (baseline?.type !== 'baseline') throw new Error('workspace follow did not provide a baseline')\n return { items: (baseline.value?.items ?? []).map(toWorkspaceView), archivedSessionIds: (baseline.value?.archivedSessionIds ?? []).map(String) }\n } finally { abort.abort(); await iterator.return?.() }\n}\nasync function projectModels(catalog: unknown, sessionId: string, list: { items: readonly unknown[] }): Promise<HostSessionModels> {\n const value = catalog as { default?: { provider?: string; model?: string; reasoningEffort?: string }; groups?: unknown[]; failures?: unknown[] }\n const row = list.items.map(toPhoneSessionRow).find(item => item.sessionId === sessionId)\n const selected = (row?.projections?.values?.modelSelection as { next?: unknown } | undefined)?.next as { provider?: string; model?: string; reasoningEffort?: string } | undefined\n return {\n current: { provider: String(selected?.provider ?? value.default?.provider ?? ''), model: String(selected?.model ?? value.default?.model ?? ''), ...(typeof selected?.reasoningEffort === 'string' ? { reasoningEffort: selected.reasoningEffort } : {}) },\n routable: true,\n groups: Array.isArray(value.groups) ? value.groups as HostSessionModels['groups'] : [],\n failures: Array.isArray(value.failures) ? value.failures as HostSessionModels['failures'] : [],\n }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { DeviceScope } from './device-auth.ts'\nimport type { DeepPilotReport, PairingGrantSnapshot, PushTestResult, RelayTestResult } from './report-wire.ts'\n\n/** Async snapshot source wired by the plugin entry. */\nexport type ReportSnapshot = () => Promise<DeepPilotReport>\n\nexport type PairingStarter = () => Promise<PairingGrantSnapshot>\nexport type DeviceRevoker = (deviceId: string) => Promise<boolean>\nexport type DeviceRenamer = (deviceId: string, customName: string | null) => Promise<string | null>\nexport type DeviceScopeUpdater = (deviceId: string, scopes: DeviceScope[]) => Promise<DeviceScope[]>\n\n/** Runs the relay connectivity self-test (health + enrollment round-trip). */\nexport type RelayTester = () => Promise<RelayTestResult>\n\n/** Forces one synthetic notification down the active push pathway. */\nexport type PushTester = () => Promise<PushTestResult>\n\n/**\n * The Typert receiver the Gateway resolves for the DeepPilot Bridge report.\n * Snapshot data stays non-secret; the token crosses the boundary only through\n * the explicit, user-triggered revealToken/rotateToken invocations.\n */\nexport class DeepPilotReportService extends TypertRemoteService {\n private readonly snapshot: ReportSnapshot\n private readonly pairingStarter: PairingStarter\n private readonly deviceRevoker: DeviceRevoker\n private readonly deviceRenamer: DeviceRenamer\n private readonly deviceScopeUpdater: DeviceScopeUpdater\n\n private readonly relayTester: RelayTester\n private readonly pushTester: PushTester\n\n constructor(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingStarter: PairingStarter,\n deviceRevoker: DeviceRevoker,\n deviceRenamer: DeviceRenamer,\n deviceScopeUpdater: DeviceScopeUpdater,\n relayTester: RelayTester,\n pushTester: PushTester,\n ) {\n super(ctx, 'deeppilotReport', { namespace: 'deeppilot' })\n this.snapshot = snapshot\n this.pairingStarter = pairingStarter\n this.deviceRevoker = deviceRevoker\n this.deviceRenamer = deviceRenamer\n this.deviceScopeUpdater = deviceScopeUpdater\n this.relayTester = relayTester\n this.pushTester = pushTester\n }\n\n async report(): Promise<DeepPilotReport> {\n return this.snapshot()\n }\n\n async beginPairing(): Promise<PairingGrantSnapshot> {\n return this.pairingStarter()\n }\n\n async revokeDevice(deviceId: string): Promise<boolean> {\n return this.deviceRevoker(deviceId)\n }\n\n async setDeviceName(deviceId: string, customName: string | null): Promise<string | null> {\n return this.deviceRenamer(deviceId, customName)\n }\n\n async setDeviceScopes(deviceId: string, scopes: DeviceScope[]): Promise<DeviceScope[]> {\n return this.deviceScopeUpdater(deviceId, scopes)\n }\n\n async testRelay(): Promise<RelayTestResult> {\n return this.relayTester()\n }\n\n async testPush(): Promise<PushTestResult> {\n return this.pushTester()\n }\n}\n","/**\n * Wire contract for the DeepPilot report Remote (`deeppilot/report`).\n *\n * The web settings page needs the bridge's live facts (paired devices,\n * connections, token state) — those live Host-side. This module is the single\n * source both halves share: the strict result codec (hand-rolled, so the\n * client bundle needs no schema library) and the exact descriptor object.\n *\n * @module dsh-deeppilot/report-wire\n */\n\nimport type { InvocationDescriptor, TypertCodec, TypertRemoteContribution, TypertSchema } from '@deepseek-ai/dsh-typert-protocol'\nimport { isTailscaleVersion } from './tailscale-version.ts'\nimport { DEVICE_SCOPES, normalizeDeviceScopes, type DeviceScope } from './device-auth.ts'\n\nexport interface PairingGrantSnapshot {\n code: string\n expiresAt: number\n audience: string\n}\n\n/** One paired device row in the report. */\nexport interface ReportDevice {\n deviceId: string\n /** Effective operator-facing name: customName when set, otherwise the app-reported name. */\n deviceName: string\n /** User-assigned label; absent means the app-reported name is in use. */\n customName?: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n fingerprint: string\n scopes: DeviceScope[]\n revokedAt?: number\n /** Present once the device registered an APNs token (token itself never leaves the host). */\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n}\n\n/** One step of the relay connectivity self-test. */\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n message: string\n latencyMs?: number\n}\n\n/** Result of deeppilot/testRelay. */\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\n/** Per-device delivery result of deeppilot/testPush. */\nexport interface PushTestDeviceResult {\n name: string\n environment: string\n /** sent | invalid-token | failed — mirrors the dispatch outcome contract. */\n outcome: string\n /** Apple's raw rejection reason when present (BadDeviceToken, …). */\n reason?: string\n /** First 10 hex chars of the stored token, for freshness verification. */\n tokenFingerprint?: string\n}\n\n/**\n * 设置页「连接诊断」区块展示的一条最近失败。\n *\n * 只携带可安全展示的结构化事实:类型、分因、时间、来源哈希(auditLabel\n * 的 12 位十六进制,绝不是原始 IP)、可选 attemptId / 关闭码 / 耗时。\n * 配对码、公钥、APNs token、消息正文永远不进入这里。\n */\nexport interface ReportDiagnosticFailure {\n ts: number\n kind: string\n reason: string\n attemptId?: string\n source?: string\n closeCode?: number\n durationMs?: number\n}\n\n/** 连接诊断快照;不包含 debug 开关状态。 */\nexport interface ReportDiagnostics {\n /** 插件 apply() 的启动时刻。 */\n startedAt: number\n /** 最近失败事件(时间升序,最多 20 条)。 */\n recentFailures: ReportDiagnosticFailure[]\n}\n\nexport interface PushTestResult {\n transport: 'apns' | 'relay' | 'none'\n /** sent = at least one accepted delivery; no-targets / not-configured / failed. */\n overall: 'sent' | 'failed' | 'no-targets' | 'not-configured'\n message?: string\n results: PushTestDeviceResult[]\n}\n\n/** Everything the settings page renders for the bridge. */\nexport interface DeepPilotReport {\n protocolVersion: number\n serverVersion: string\n /** Host plugin version (from package.json). Same value as serverVersion,\n * surfaced under a stable name so the wire contract does not change if\n * the legacy field is ever repurposed. */\n pluginVersion: string\n /** True once the host's background GitHub check has confirmed a newer\n * stable release exists. Undefined / false ⇒ the UI shows only the\n * version line, never the \"new version\" hint. */\n updateAvailable?: boolean\n /** GitHub release page URL, when an update is available. */\n releaseUrl?: string\n /** Whether the master bridge switch is on. */\n enabled: boolean\n /** Absolute protocol-v2 identity registry path. */\n identityPath: string\n /** Whether host identity and the device registry are ready. */\n pairingReady: boolean\n /** Currently connected phone sockets. */\n activeConnections: number\n historyBufferMax: number\n /** Private IPv4 candidates for local QR pairing. */\n lanAddresses: string[]\n /** Independent TLS-only LAN listener; this never represents DSH's own web port. */\n local: {\n phase: 'disabled' | 'starting' | 'online' | 'error' | 'stopped'\n port: number\n endpoints: string[]\n /** `sha256:` SPKI pin of the LAN certificate; present while `online`. */\n tlsFingerprint?: string\n /** The LAN private key was recreated this run: paired LAN devices must pair again. */\n tlsIdentityRegenerated?: boolean\n message?: string\n updatedAt: number\n }\n remote: {\n provider: 'tailscale-funnel'\n phase: 'disabled' | 'starting' | 'login_required' | 'online' | 'error' | 'unavailable' | 'stopped'\n publicURL?: string\n authURL?: string\n message?: string\n /**\n * 内嵌 helper 自报的 tailscale.com 模块版本(如 `v1.104.2`)。可选:\n * 远程连接未启用、helper 缺失或尚未启动时 Host 不带这个字段,设置页\n * 显示「未获取」;旧 Host 同样不带,页面不能因此报错。\n */\n tailscaleVersion?: string\n updatedAt: number\n }\n devices: ReportDevice[]\n /**\n * 连接诊断。可选:旧 Host 不返回时设置页整块跳过,不当作错误。\n * 这是「最近失败」的唯一 report 通道(health 只服务手机)。\n */\n diagnostics?: ReportDiagnostics\n}\n\n/** The npm package identity both contribution registrations claim. */\nexport const REPORT_REMOTE_PACKAGE = 'dsh-deeppilot'\n\n/** Canonical `<namespace>/<method>` endpoint of the report Remote. */\nexport const REPORT_ENDPOINT = 'deeppilot/report'\n\nexport const BEGIN_PAIRING_ENDPOINT = 'deeppilot/beginPairing'\nexport const REVOKE_DEVICE_ENDPOINT = 'deeppilot/revokeDevice'\nexport const SET_DEVICE_NAME_ENDPOINT = 'deeppilot/setDeviceName'\nexport const SET_DEVICE_SCOPES_ENDPOINT = 'deeppilot/setDeviceScopes'\n\nfunction reject(field: string): never {\n throw new TypeError(`deeppilot/report result: invalid ${field}`)\n}\n\nfunction str(source: Record<string, unknown>, key: string, field: string): string {\n const value = source[key]\n if (typeof value !== 'string') reject(field)\n return value\n}\n\n/**\n * Non-negative integer: counters and timestamps (activeConnections,\n * historyBufferMax, updatedAt, lastSeenTs, protocolVersion, etc.). A bare\n * `typeof number` check accepts 1.5, -1, and 1e20 — all of which then\n * surface verbatim on the settings page and break any sort or arithmetic\n * the UI does.\n */\nfunction int(source: Record<string, unknown>, key: string, field: string): number {\n const value = source[key]\n if (\n typeof value !== 'number' ||\n !Number.isFinite(value) ||\n !Number.isInteger(value) ||\n value < 0\n ) reject(field)\n return value\n}\nfunction bool(source: Record<string, unknown>, key: string, field: string): boolean {\n const value = source[key]\n if (typeof value !== 'boolean') reject(field)\n return value\n}\nfunction rec(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) reject(field)\n return value as Record<string, unknown>\n}\n\nfunction parseDevice(value: unknown): ReportDevice {\n const s = rec(value, 'device')\n let apns: ReportDevice['apns']\n if (s.apns !== undefined) {\n const a = rec(s.apns, 'device.apns')\n const environment = str(a, 'environment', 'device.apns.environment')\n if (environment !== 'development' && environment !== 'production') reject('device.apns.environment')\n apns = { environment, updatedAt: int(a, 'updatedAt', 'device.apns.updatedAt') }\n }\n const customName = s.customName\n if (customName !== undefined && (\n typeof customName !== 'string' ||\n customName.length === 0 ||\n customName.length > 64 ||\n customName.trim() !== customName ||\n /[\\u0000-\\u001f\\u007f-\\u009f]/u.test(customName)\n )) reject('device.customName')\n return {\n deviceId: str(s, 'deviceId', 'device.deviceId'),\n deviceName: str(s, 'deviceName', 'device.deviceName'),\n ...(typeof customName === 'string' ? { customName } : {}),\n appVersion: str(s, 'appVersion', 'device.appVersion'),\n firstSeenTs: int(s, 'firstSeenTs', 'device.firstSeenTs'),\n lastSeenTs: int(s, 'lastSeenTs', 'device.lastSeenTs'),\n fingerprint: str(s, 'fingerprint', 'device.fingerprint'),\n scopes: normalizeDeviceScopes(s.scopes),\n ...(s.revokedAt !== undefined ? { revokedAt: int(s, 'revokedAt', 'device.revokedAt') } : {}),\n ...(apns ? { apns } : {}),\n }\n}\n\nfunction parseRemote(value: unknown): DeepPilotReport['remote'] {\n const s = rec(value, 'remote')\n const provider = str(s, 'provider', 'remote.provider')\n const phase = str(s, 'phase', 'remote.phase')\n if (provider !== 'tailscale-funnel') reject('remote.provider')\n const phases = ['disabled', 'starting', 'login_required', 'online', 'error', 'unavailable', 'stopped']\n if (!phases.includes(phase)) reject('remote.phase')\n const publicURL = s.publicURL\n const authURL = s.authURL\n const message = s.message\n const tailscaleVersion = s.tailscaleVersion\n if (publicURL !== undefined && typeof publicURL !== 'string') reject('remote.publicURL')\n if (authURL !== undefined && typeof authURL !== 'string') reject('remote.authURL')\n if (message !== undefined && typeof message !== 'string') reject('remote.message')\n // 页面直接渲染这个字符串:只接受 Go 模块版本形态,坏值拒绝整份报告,\n // 而不是把任意文本带进设置页。\n if (tailscaleVersion !== undefined && !isTailscaleVersion(tailscaleVersion)) reject('remote.tailscaleVersion')\n return {\n provider,\n phase: phase as DeepPilotReport['remote']['phase'],\n ...(typeof publicURL === 'string' ? { publicURL } : {}),\n ...(typeof authURL === 'string' ? { authURL } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n ...(typeof tailscaleVersion === 'string' ? { tailscaleVersion } : {}),\n updatedAt: int(s, 'updatedAt', 'remote.updatedAt'),\n }\n}\n\nfunction parseLocal(value: unknown): DeepPilotReport['local'] {\n const s = rec(value, 'local')\n const phase = str(s, 'phase', 'local.phase')\n const phases = ['disabled', 'starting', 'online', 'error', 'stopped']\n if (!phases.includes(phase)) reject('local.phase')\n const endpoints = s.endpoints\n if (!Array.isArray(endpoints) || endpoints.some((entry) => typeof entry !== 'string')) reject('local.endpoints')\n const message = s.message\n if (message !== undefined && typeof message !== 'string') reject('local.message')\n const tlsFingerprint = s.tlsFingerprint\n if (tlsFingerprint !== undefined && typeof tlsFingerprint !== 'string') reject('local.tlsFingerprint')\n const tlsIdentityRegenerated = s.tlsIdentityRegenerated\n if (tlsIdentityRegenerated !== undefined && typeof tlsIdentityRegenerated !== 'boolean') reject('local.tlsIdentityRegenerated')\n return {\n phase: phase as DeepPilotReport['local']['phase'],\n port: int(s, 'port', 'local.port'),\n endpoints: endpoints as string[],\n ...(typeof tlsFingerprint === 'string' ? { tlsFingerprint } : {}),\n ...(tlsIdentityRegenerated === true ? { tlsIdentityRegenerated: true } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n updatedAt: int(s, 'updatedAt', 'local.updatedAt'),\n }\n}\n\nfunction parseRelayTestStep(value: unknown): RelayTestStep {\n const st = rec(value, 'step')\n const id = str(st, 'id', 'step.id')\n if (id !== 'health' && id !== 'enroll') reject('step.id')\n const latencyMs = st.latencyMs\n if (latencyMs !== undefined) {\n // Same int() check the report uses for counters: latency is always a\n // non-negative whole-millisecond number; a NaN/Infinity/negative arrives\n // from a buggy host and must not silently render as `(NaNms)`.\n if (\n typeof latencyMs !== 'number' ||\n !Number.isFinite(latencyMs) ||\n !Number.isInteger(latencyMs) ||\n latencyMs < 0\n ) reject('step.latencyMs')\n }\n return {\n id,\n ok: bool(st, 'ok', 'step.ok'),\n message: str(st, 'message', 'step.message'),\n ...(typeof latencyMs === 'number' ? { latencyMs } : {}),\n }\n}\n\nfunction parseRelayTestResult(value: unknown): RelayTestResult {\n const s = rec(value, 'result')\n const overall = str(s, 'overall', 'overall')\n if (overall !== 'ok' && overall !== 'failed') reject('overall')\n const stepsRaw = s.steps\n if (!Array.isArray(stepsRaw)) reject('steps')\n return {\n url: str(s, 'url', 'url'),\n overall,\n tokenIssued: bool(s, 'tokenIssued', 'tokenIssued'),\n steps: (stepsRaw as unknown[]).map(parseRelayTestStep),\n }\n}\n\nfunction parsePushTestResult(value: unknown): PushTestResult {\n const s = rec(value, 'result')\n const transport = str(s, 'transport', 'transport')\n if (transport !== 'apns' && transport !== 'relay' && transport !== 'none') reject('transport')\n const overall = str(s, 'overall', 'overall')\n const overalls = ['sent', 'failed', 'no-targets', 'not-configured']\n if (!overalls.includes(overall)) reject('overall')\n const resultsRaw = s.results\n if (!Array.isArray(resultsRaw)) reject('results')\n const results = (resultsRaw as unknown[]).map((value) => {\n const r = rec(value, 'device result')\n const reason = r.reason\n const tokenFingerprint = r.tokenFingerprint\n return {\n name: str(r, 'name', 'result.name'),\n environment: str(r, 'environment', 'result.environment'),\n outcome: str(r, 'outcome', 'result.outcome'),\n ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}),\n ...(typeof tokenFingerprint === 'string' && /^[0-9a-f]{10}$/.test(tokenFingerprint)\n ? { tokenFingerprint }\n : {}),\n }\n })\n const message = s.message\n return {\n transport,\n overall: overall as PushTestResult['overall'],\n ...(typeof message === 'string' && message.length > 0 ? { message } : {}),\n results,\n }\n}\n\nfunction parseDiagnosticFailure(value: unknown): ReportDiagnosticFailure {\n const s = rec(value, 'diagnostics.recentFailures[]')\n // kind / reason 是代码内枚举常量;这里收敛字符集与长度,防止上游把\n // 任意文本(路径、IP、错误消息)塞进设置页。\n const kind = str(s, 'kind', 'diagnostics.kind')\n if (!/^[a-z][a-z0-9-]{0,47}$/.test(kind)) reject('diagnostics.kind')\n const reason = str(s, 'reason', 'diagnostics.reason')\n if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(reason)) reject('diagnostics.reason')\n const attemptId = s.attemptId\n if (attemptId !== undefined && (typeof attemptId !== 'string' || !/^[A-Za-z0-9_-]{1,16}$/.test(attemptId))) {\n reject('diagnostics.attemptId')\n }\n const source = s.source\n if (source !== undefined && (typeof source !== 'string' || !/^[0-9a-f]{8,64}$/.test(source))) {\n reject('diagnostics.source')\n }\n const closeCode = s.closeCode\n if (closeCode !== undefined && (typeof closeCode !== 'number' || !Number.isInteger(closeCode))) {\n reject('diagnostics.closeCode')\n }\n const durationMs = s.durationMs\n if (durationMs !== undefined && (typeof durationMs !== 'number' || !Number.isFinite(durationMs) || durationMs < 0)) {\n reject('diagnostics.durationMs')\n }\n return {\n ts: int(s, 'ts', 'diagnostics.ts'),\n kind,\n reason,\n ...(typeof attemptId === 'string' ? { attemptId } : {}),\n ...(typeof source === 'string' ? { source } : {}),\n ...(typeof closeCode === 'number' ? { closeCode } : {}),\n ...(typeof durationMs === 'number' ? { durationMs: Math.round(durationMs) } : {}),\n }\n}\n\nfunction parseDiagnostics(value: unknown): ReportDiagnostics {\n const s = rec(value, 'diagnostics')\n const failures = s.recentFailures\n if (!Array.isArray(failures)) reject('diagnostics.recentFailures')\n // 页面上是「最近失败」列表,20 条足够;硬上限防止坏 Host 撑爆 UI。\n if (failures.length > 20) reject('diagnostics.recentFailures')\n return {\n startedAt: int(s, 'startedAt', 'diagnostics.startedAt'),\n recentFailures: (failures as unknown[]).map(parseDiagnosticFailure),\n }\n}\n\nfunction parseReport(value: unknown): DeepPilotReport {\n const s = rec(value, 'report')\n const devices = s.devices\n const lanAddresses = s.lanAddresses\n if (!Array.isArray(devices)) reject('devices')\n if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== 'string')) reject('lanAddresses')\n // Update fields are optional: an older Host (without the background\n // GitHub check) won't include them, and the UI must keep rendering.\n const releaseUrl = s.releaseUrl\n return {\n protocolVersion: int(s, 'protocolVersion', 'protocolVersion'),\n serverVersion: str(s, 'serverVersion', 'serverVersion'),\n pluginVersion: str(s, 'pluginVersion', 'pluginVersion'),\n ...(s.updateAvailable === true ? { updateAvailable: true } : {}),\n ...(typeof releaseUrl === 'string' && releaseUrl.length > 0\n ? { releaseUrl } : {}),\n enabled: bool(s, 'enabled', 'enabled'),\n identityPath: str(s, 'identityPath', 'identityPath'),\n pairingReady: bool(s, 'pairingReady', 'pairingReady'),\n activeConnections: int(s, 'activeConnections', 'activeConnections'),\n historyBufferMax: int(s, 'historyBufferMax', 'historyBufferMax'),\n lanAddresses: lanAddresses as string[],\n local: parseLocal(s.local),\n remote: parseRemote(s.remote),\n devices: (devices as unknown[]).map(parseDevice),\n // 旧 Host 没有这个字段:保持可选,设置页整块跳过。\n ...(s.diagnostics !== undefined ? { diagnostics: parseDiagnostics(s.diagnostics) } : {}),\n }\n}\n\nexport const reportSchema: TypertSchema<DeepPilotReport> = { parse: parseReport }\n\nexport const relayTestSchema: TypertSchema<RelayTestResult> = { parse: parseRelayTestResult }\n\nexport const pushTestSchema: TypertSchema<PushTestResult> = { parse: parsePushTestResult }\n\nexport const pairingGrantSchema: TypertSchema<PairingGrantSnapshot> = {\n parse(value: unknown): PairingGrantSnapshot {\n const s = rec(value, 'pairing grant')\n const code = str(s, 'code', 'pairingGrant.code')\n if (code.length < 32) reject('pairingGrant.code')\n return {\n code,\n expiresAt: int(s, 'expiresAt', 'pairingGrant.expiresAt'),\n audience: str(s, 'audience', 'pairingGrant.audience'),\n }\n },\n}\n\nconst deviceIdSchema: TypertSchema<string> = {\n parse(value: unknown): string {\n if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(value)) reject('deviceId')\n return value\n },\n}\n\nconst customNameSchema: TypertSchema<string | null> = {\n parse(value: unknown): string | null {\n if (value === null) return null\n if (typeof value !== 'string') reject('customName')\n const trimmed = value.trim()\n if (trimmed.length === 0 || trimmed.length > 64 || /[\\u0000-\\u001f\\u007f-\\u009f]/u.test(value)) reject('customName')\n return trimmed\n },\n}\n\nconst scopesSchema: TypertSchema<DeviceScope[]> = {\n parse(value: unknown): DeviceScope[] {\n if (!Array.isArray(value) || value.some((scope) => typeof scope !== 'string' || !DEVICE_SCOPES.includes(scope as DeviceScope))) {\n reject('scopes')\n }\n return normalizeDeviceScopes(value)\n },\n}\n\nconst booleanSchema: TypertSchema<boolean> = {\n parse(value: unknown): boolean {\n if (typeof value !== 'boolean') reject('boolean')\n return value\n },\n}\n\n/** The rc.2 Gateway materializes each strict codec through `create()`. */\nfunction strictCodec<T>(typeSymbol: string, schema: TypertSchema<T>): TypertCodec {\n return {\n mode: 'strict',\n typeSymbol,\n create: () => schema,\n }\n}\n\n/**\n * Connectivity self-test for the push relay: health probe plus (when an\n * enroll key is on file) a real enrollment round-trip. Doubles as repair —\n * a token it issues is cached by the bridge.\n */\nexport const TEST_RELAY_ENDPOINT = 'deeppilot/testRelay'\n\n/**\n * Real-delivery push test: forces one synthetic notification down the active\n * push pathway to every registered device, ignoring connected/muted filters —\n * an explicit user action must always be able to prove the pipeline works.\n */\nexport const TEST_PUSH_ENDPOINT = 'deeppilot/testPush'\n\nexport const REPORT_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'report',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeepPilotReport`, reportSchema),\n}\n\nexport const BEGIN_PAIRING_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${BEGIN_PAIRING_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'beginPairing',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#PairingGrantSnapshot`, pairingGrantSchema),\n}\n\nexport const REVOKE_DEVICE_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REVOKE_DEVICE_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'revokeDevice',\n invocation: { kind: 'direct' },\n parameters: [{\n name: 'deviceId',\n wire: 'deviceId',\n source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceId`, deviceIdSchema),\n }],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#Boolean`, booleanSchema),\n}\n\nexport const SET_DEVICE_NAME_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${SET_DEVICE_NAME_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'setDeviceName',\n invocation: { kind: 'direct' },\n parameters: [\n {\n name: 'deviceId', wire: 'deviceId', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceId`, deviceIdSchema),\n },\n {\n name: 'customName', wire: 'customName', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#CustomDeviceName`, customNameSchema),\n },\n ],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#CustomDeviceName`, customNameSchema),\n}\n\nexport const SET_DEVICE_SCOPES_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${SET_DEVICE_SCOPES_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'setDeviceScopes',\n invocation: { kind: 'direct' },\n parameters: [\n {\n name: 'deviceId', wire: 'deviceId', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceId`, deviceIdSchema),\n },\n {\n name: 'scopes', wire: 'scopes', source: 'json',\n codec: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceScopes`, scopesSchema),\n },\n ],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#DeviceScopes`, scopesSchema),\n}\n\nexport const TEST_RELAY_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_RELAY_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testRelay',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#RelayTestResult`, relayTestSchema),\n}\n\nexport const TEST_PUSH_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_PUSH_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testPush',\n invocation: { kind: 'direct' },\n parameters: [],\n result: strictCodec(`${REPORT_REMOTE_PACKAGE}#PushTestResult`, pushTestSchema),\n}\n\nconst INVOCATION_DESCRIPTORS = [\n REPORT_DESCRIPTOR,\n BEGIN_PAIRING_DESCRIPTOR,\n REVOKE_DEVICE_DESCRIPTOR,\n SET_DEVICE_NAME_DESCRIPTOR,\n SET_DEVICE_SCOPES_DESCRIPTOR,\n TEST_RELAY_DESCRIPTOR,\n TEST_PUSH_DESCRIPTOR,\n]\n\nexport const REPORT_HOST_CONTRIBUTION = {\n package: REPORT_REMOTE_PACKAGE,\n face: 'host' as const,\n schemas: [],\n // This hand-written Remote has no generated reflection exports.\n model: { services: [], events: [], objects: [] },\n invocations: INVOCATION_DESCRIPTORS,\n}\n\nexport const REPORT_REMOTE_CONTRIBUTION: TypertRemoteContribution = {\n package: REPORT_REMOTE_PACKAGE,\n descriptors: INVOCATION_DESCRIPTORS,\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { DeepPilotReportService } from './report-service.ts'\nimport type { DeviceRenamer, DeviceRevoker, DeviceScopeUpdater, PairingStarter, PushTester, RelayTester, ReportSnapshot } from './report-service.ts'\nimport { REPORT_HOST_CONTRIBUTION } from './report-wire.ts'\n\ninterface TypertContributionRegistry {\n register(contribution: typeof REPORT_HOST_CONTRIBUTION): () => void | Promise<void>\n}\n\n/**\n * Provide the report service and register its Remote descriptor. Rides an\n * optional `typert` inject: profiles without the web stack never activate it.\n */\nexport function applyReportRemote(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingStarter: PairingStarter,\n deviceRevoker: DeviceRevoker,\n deviceRenamer: DeviceRenamer,\n deviceScopeUpdater: DeviceScopeUpdater,\n relayTester: RelayTester,\n pushTester: PushTester,\n): void {\n ctx.inject(['typert'], (remoteCtx) => {\n new DeepPilotReportService(remoteCtx, snapshot, pairingStarter, deviceRevoker, deviceRenamer, deviceScopeUpdater, relayTester, pushTester)\n const registry = remoteCtx.typert as unknown as TypertContributionRegistry\n const unregister = registry.register(REPORT_HOST_CONTRIBUTION)\n remoteCtx.effect(() => () => void unregister(), 'dsh-deeppilot: report remote')\n })\n}\n","import { networkInterfaces } from 'node:os'\n\nfunction octetsOf(address: string): [number, number, number, number] | null {\n const octets = address.split('.').map(Number)\n if (octets.length !== 4 || octets.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {\n return null\n }\n return octets as [number, number, number, number]\n}\n\n/** RFC1918 私有地址。名字保持诚实:它**不**包含 CGNAT 段,见 isSharedAddressSpaceIPv4。 */\nexport function isPrivateIPv4(address: string): boolean {\n const parsed = octetsOf(address)\n if (parsed === null) return false\n const [a, b] = parsed\n return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)\n}\n\n/**\n * RFC 6598 共享地址空间(100.64.0.0/10)。\n *\n * Tailscale 与 Headscale 都从这个段里给节点分配地址(Headscale 的\n * `prefixes.v4` 默认就是它),因此它在实践中是私有的,只是不是 RFC1918——\n * 这正是 `isPrivateIPv4` 会把它拒掉的原因。把它单列成一个谓词而不是塞进\n * `isPrivateIPv4`,是为了不谎报那个名字(issue #24)。\n */\nexport function isSharedAddressSpaceIPv4(address: string): boolean {\n const parsed = octetsOf(address)\n if (parsed === null) return false\n const [a, b] = parsed\n return a === 100 && b >= 64 && b <= 127\n}\n\n/**\n * 可以交给手机的私有地址候选:RFC1918 加 CGNAT 段。\n *\n * 候选多于一个时,配对二维码页才会渲染地址选择器——只有一个候选时用户没有\n * 任何办法指定别的地址。把 tailnet 地址放进来,选择器才会出现,物理局域网\n * 离开之后用户仍可切到 tailnet 那个。\n *\n * 排序**不动**:物理 en* 接口仍然排在隧道接口之前,默认拿到的还是局域网地址,\n * 改变的只是「多了一个可选项」这件事本身。\n */\nfunction isRoutablePrivateIPv4(address: string): boolean {\n return isPrivateIPv4(address) || isSharedAddressSpaceIPv4(address)\n}\n\n/** Private IPv4 candidates, preferring physical en* interfaces over tunnels. */\nexport function localLANIPv4Addresses(): string[] {\n const candidates: Array<{ name: string; address: string }> = []\n for (const [name, entries] of Object.entries(networkInterfaces())) {\n for (const entry of entries ?? []) {\n if (entry.family === 'IPv4' && !entry.internal && isRoutablePrivateIPv4(entry.address)) {\n candidates.push({ name, address: entry.address })\n }\n }\n }\n const priority = (name: string): number => name === 'en0' ? 0 : name.startsWith('en') ? 1 : name.startsWith('bridge') ? 2 : 3\n candidates.sort((left, right) => priority(left.name) - priority(right.name) || left.name.localeCompare(right.name))\n return [...new Set(candidates.map(({ address }) => address))]\n}\n","/**\n * Lightweight self-update check for dsh-deeppilot.\n *\n * On Host boot we ask the GitHub Releases API (REST) which is the latest\n * stable tag, compare it to the installed plugin version, and surface a\n * \"newer release exists\" flag plus the GitHub release URL through the\n * report Remote. The settings page renders one small line at the bottom;\n * a successful check is enough — no manual button, no persistent cache\n * (the host process is the lifetime of the answer).\n *\n * Deliberately no third-party dependency: we use {@link https.request}\n * directly to keep parity with the rest of the project (remote-supervisor\n * uses node:http, host-bridge uses ws, etc).\n *\n * Failure policy: every network/parse error collapses to a single log\n * line and the in-memory snapshot stays \"unknown\". The bridge must never\n * crash because GitHub rate-limited us, returned a 5xx, or the user is\n * offline.\n */\n\nimport { request as httpsRequest, type RequestOptions } from 'node:https'\n\n/** GitHub repo (no .git suffix). Public, unauthenticated, low rate limit. */\nconst RELEASES_PATH = '/repos/Mars-Sea/dsh-deeppilot/releases'\n\n/** Hard ceiling on the network round-trip. The host must never hang. */\nconst FETCH_TIMEOUT_MS = 8_000\n\n/** Per-page limit. We only need the first stable release, but pre-releases\n * tend to be listed first; fetching 20 gives the comparator enough room. */\nconst PER_PAGE = 20\n\n/**\n * Public snapshot surfaced to the report Remote. `available` is `false`\n * until the network answer lands; on failure it stays `false` so the UI\n * simply doesn't render the \"new version\" hint.\n */\nexport interface UpdateInfo {\n /** Host plugin version (from package.json). */\n currentVersion: string\n /** True once a newer stable release has been found on GitHub. */\n available: boolean\n /** GitHub release page URL, when an update is available. */\n releaseUrl: string | null\n /** Newer stable tag, when an update is available. */\n latestVersion: string | null\n}\n\ninterface GitHubRelease {\n tag_name?: unknown\n prerelease?: unknown\n draft?: unknown\n html_url?: unknown\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction parseStableEntry(value: unknown): { tag: string; url: string | null } | null {\n if (!isPlainObject(value)) return null\n const tag = value.tag_name\n if (typeof tag !== 'string') return null\n if (value.prerelease === true || value.draft === true) return null\n if (parseStableTag(tag) === null) return null\n const url = value.html_url\n return { tag, url: typeof url === 'string' ? url : null }\n}\n\n/** Parse one stable release from the `tag_name` shape `vX.Y.Z` (the v is\n * optional; `1.2.3` is also accepted). Pre-release tags like `0.3.0-rc.1`\n * return null — the policy is \"stable channel only\". */\nexport function parseStableTag(tag: string): { major: number; minor: number; patch: number } | null {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(tag.trim())\n if (match === null) return null\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n }\n}\n\n/** Semver compare for X.Y.Z. Returns -1 / 0 / 1. */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const pa = parseStableTag(a)\n const pb = parseStableTag(b)\n if (pa === null && pb === null) return 0\n if (pa === null) return -1\n if (pb === null) return 1\n if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1\n if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1\n if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1\n return 0\n}\n\n/** Hit the GitHub Releases API. Resolves with the first stable release\n * GitHub returned, or null if the list contains no stable entries.\n * Network / parse errors reject — the caller is responsible for\n * collapsing them to a log line. */\nexport function fetchLatestStableRelease(): Promise<{ tag: string; url: string | null } | null> {\n return new Promise((resolve, reject) => {\n const options: RequestOptions = {\n method: 'GET',\n host: 'api.github.com',\n path: `${RELEASES_PATH}?per_page=${PER_PAGE}`,\n headers: {\n 'user-agent': 'dsh-deeppilot-update-check',\n 'accept': 'application/vnd.github+json',\n },\n }\n const req = httpsRequest(options, (res) => {\n const status = res.statusCode ?? 0\n if (status < 200 || status >= 300) {\n res.resume()\n reject(new Error(`github releases http ${status}`))\n return\n }\n const chunks: Buffer[] = []\n res.on('data', (chunk: Buffer) => chunks.push(chunk))\n res.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf8')\n const parsed = JSON.parse(body) as unknown\n if (!Array.isArray(parsed)) {\n reject(new Error('github releases: response is not an array'))\n return\n }\n for (const entry of parsed) {\n const stable = parseStableEntry(entry)\n if (stable !== null) {\n resolve(stable)\n return\n }\n }\n resolve(null)\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)))\n }\n })\n res.on('error', (error) => reject(error))\n })\n req.setTimeout(FETCH_TIMEOUT_MS, () => {\n req.destroy(new Error('github releases: timeout after ' + FETCH_TIMEOUT_MS + 'ms'))\n })\n req.on('error', (error) => reject(error))\n req.end()\n })\n}\n\n/**\n * Process-wide check state. Constructed once in `apply()`, queried\n * synchronously by the report snapshot. The check itself fires once in\n * the background shortly after boot; the answer lives for the lifetime\n * of the host process — re-running the page in the Web UI does not\n * trigger another network call.\n */\nexport class UpdateChecker {\n private readonly log: (message: string) => void\n private readonly currentVersion: string\n private readonly fetchImpl: typeof fetchLatestStableRelease\n private readonly initialDelayMs: number\n private snapshot: UpdateInfo\n private inflight: Promise<void> | null = null\n\n constructor(options: {\n log: (message: string) => void\n currentVersion: string\n fetchImpl?: typeof fetchLatestStableRelease\n initialDelayMs?: number\n }) {\n this.log = options.log\n this.currentVersion = options.currentVersion\n this.fetchImpl = options.fetchImpl ?? fetchLatestStableRelease\n this.initialDelayMs = options.initialDelayMs ?? 2_000\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: false,\n releaseUrl: null,\n latestVersion: null,\n }\n }\n\n /** Return the current in-memory snapshot — safe to call from any host\n * thread. Never throws, never awaits. */\n get(): UpdateInfo {\n return this.snapshot\n }\n\n /**\n * Schedule one background refresh after the configured initial delay.\n * Used by the plugin entry to do the first check without blocking boot.\n */\n scheduleInitial(): void {\n if (this.initialDelayMs <= 0) {\n void this.runOnce()\n return\n }\n const timer = setTimeout(() => {\n void this.runOnce()\n }, this.initialDelayMs)\n if (typeof (timer as { unref?: () => void }).unref === 'function') {\n (timer as { unref: () => void }).unref()\n }\n }\n\n private async runOnce(): Promise<void> {\n if (this.inflight !== null) {\n await this.inflight\n return\n }\n const task = (async () => {\n try {\n const stable = await this.fetchImpl()\n if (stable === null) {\n // No stable release on GitHub (yet) — leave `available` false.\n return\n }\n if (compareSemver(stable.tag, this.currentVersion) > 0) {\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: true,\n releaseUrl: stable.url,\n latestVersion: stable.tag,\n }\n } else {\n // Up to date. Clear any previous \"available\" state so a host\n // restart after an upgrade immediately stops showing the hint.\n this.snapshot = {\n currentVersion: this.currentVersion,\n available: false,\n releaseUrl: null,\n latestVersion: null,\n }\n }\n } catch (error) {\n this.log('update check failed: ' + (error instanceof Error ? error.message : String(error)))\n // Leave the previous snapshot in place. Even \"no answer\" is a\n // worse signal than the last successful check.\n }\n })()\n this.inflight = task.finally(() => {\n this.inflight = null\n })\n return this.inflight\n }\n\n /** No-op kept for API symmetry with the host lifecycle wiring. */\n dispose(): void {\n // The initial-delay timer is unref'd; nothing to track here.\n }\n}\n","import type { IncomingMessage } from 'node:http'\nimport { isIP } from 'node:net'\nimport type { Duplex } from 'node:stream'\n\nexport const CLIENT_IP_HEADER = 'x-deeppilot-client-ip'\n\nexport function rejectUpgrade(socket: Duplex, status: number, reason: string, retryAfterSeconds?: number): void {\n const body = JSON.stringify({ error: reason })\n const statusText: Record<number, string> = {\n 401: 'Unauthorized',\n 429: 'Too Many Requests',\n 500: 'Internal Server Error',\n 503: 'Service Unavailable',\n }\n const retryAfter = status === 429 && retryAfterSeconds !== undefined\n ? `Retry-After: ${Math.max(1, Math.ceil(retryAfterSeconds))}\\r\\n`\n : ''\n socket.end(\n 'HTTP/1.1 ' + status + ' ' + (statusText[status] ?? 'Error') + '\\r\\n' +\n retryAfter +\n 'Content-Type: application/json\\r\\n' +\n 'Content-Length: ' + Buffer.byteLength(body) + '\\r\\n' +\n 'Connection: close\\r\\n' +\n '\\r\\n' +\n body,\n )\n}\n\nfunction normalizedAddress(value: string | undefined): string | null {\n if (!value) return null\n const normalized = value.startsWith('::ffff:') ? value.slice(7) : value\n return isIP(normalized) === 0 ? null : normalized\n}\n\nfunction isLoopback(value: string | undefined): boolean {\n const address = normalizedAddress(value)\n return address === '127.0.0.1' || address === '::1'\n}\n\n/**\n * Resolve a stable rate-limit key. The helper-supplied address is trusted only\n * on the private loopback hop; direct clients cannot spoof it.\n */\nexport function requestClientIdentity(\n req: Pick<IncomingMessage, 'headers' | 'socket'>,\n): string {\n if (isLoopback(req.socket.remoteAddress)) {\n const forwarded = req.headers[CLIENT_IP_HEADER]\n const value = Array.isArray(forwarded) ? forwarded[0] : forwarded\n const address = normalizedAddress(value)\n if (address !== null) return address\n }\n return normalizedAddress(req.socket.remoteAddress) ?? 'unknown'\n}\n","import { createHash, createPrivateKey, createPublicKey, X509Certificate } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { generate } from 'selfsigned'\nimport { formatLogLine } from './diagnostics.ts'\n\nexport { LAN_TLS_FINGERPRINT_PATTERN, isLanTlsFingerprint } from './pairing-qr.ts'\n\nexport const LAN_TLS_COMMON_NAME = 'dsh-deeppilot'\nexport const LAN_TLS_KEY_FILE = 'key.pem'\nexport const LAN_TLS_CERT_FILE = 'cert.pem'\n\nconst CERT_VALIDITY_MS = 10 * 365 * 24 * 60 * 60_000\n/** Re-sign this far ahead of expiry so a long-running host never serves a stale leaf. */\nconst CERT_RENEW_LEAD_MS = 30 * 24 * 60 * 60_000\n\nexport interface LanTlsIdentity {\n /** PKCS#8 PEM private key. Never logged or reported. */\n key: string\n /** Self-signed X.509 PEM leaf certificate. */\n cert: string\n /** Pinned by paired devices; changes only when the private key changes. */\n fingerprint: string\n /** Unix ms when the current leaf certificate expires. */\n notAfter: number\n /**\n * The private key was newly created. Devices that pinned the previous\n * identity can no longer connect and must pair again.\n */\n regenerated: boolean\n /** A fresh leaf was signed with the existing key; the fingerprint is unchanged. */\n resigned: boolean\n}\n\n/** Compute the pinned public-key fingerprint of a PEM certificate. */\nexport function spkiFingerprint(certPem: string): string {\n const der = createPublicKey(certPem).export({ type: 'spki', format: 'der' })\n return 'sha256:' + createHash('sha256').update(der).digest('base64url')\n}\n\n/**\n * Load the LAN listener's TLS identity from `dir`, creating or repairing it\n * as needed. The private key is the durable identity: it is created once and\n * reused for every subsequent certificate, so pinned devices survive\n * certificate renewals. Only a missing or unreadable key forces a new one.\n */\nexport async function loadOrCreateLanTlsIdentity(dir: string, now: number = Date.now()): Promise<LanTlsIdentity> {\n await mkdir(dir, { recursive: true, mode: 0o700 })\n const keyPath = join(dir, LAN_TLS_KEY_FILE)\n const certPath = join(dir, LAN_TLS_CERT_FILE)\n\n let key = await readPem(keyPath, (error) => {\n // 读失败(非 ENOENT)会被下面当成「密钥缺失」直接重建:已配对设备的\n // 固定指纹随即失效、必须重新配对。这个后果必须可见,否则用户只会看到\n // 手机突然不认证书,而电脑端毫无线索。\n console.log(formatLogLine('warn', 'LAN TLS private key unreadable, regenerating identity: ' + describeFileError(error)))\n })\n let regenerated = false\n if (key === undefined || !validPrivateKey(key)) {\n regenerated = true\n key = undefined\n }\n\n let cert = regenerated ? undefined : await readPem(certPath, (error) => {\n // 证书读失败可以只重签(密钥不变、指纹不变),仍是需要留痕的异常。\n console.log(formatLogLine('warn', 'LAN TLS certificate unreadable, re-signing with the existing key: ' + describeFileError(error)))\n })\n let resigned = false\n const existing = key !== undefined && cert !== undefined ? inspectCertificate(cert, key, now) : undefined\n if (existing === undefined) {\n const issued = await issueCertificate(key, now)\n if (key === undefined) {\n key = pemFile(issued.key)\n await writePrivate(keyPath, key)\n } else {\n resigned = true\n }\n cert = pemFile(issued.cert)\n await writePrivate(certPath, cert)\n }\n if (key === undefined || cert === undefined) throw new Error('LAN TLS identity unavailable')\n\n return {\n key,\n cert,\n fingerprint: spkiFingerprint(cert),\n notAfter: existing ?? new X509Certificate(cert).validToDate.getTime(),\n regenerated,\n resigned,\n }\n}\n\nasync function readPem(path: string, onError?: (error: unknown) => void): Promise<string | undefined> {\n try {\n const text = await readFile(path, 'utf8')\n return text.trim().length > 0 ? text : undefined\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n // 非 ENOENT 的读取失败交给调用方留痕;随后仍按「文件缺失」重建身份,\n // 保证插件能起来。错误消息只含路径与 errno,不含密钥内容。\n onError?.(error)\n return undefined\n }\n}\n\n/** 读失败的安全描述:文件名 + errno,不带文件内容。 */\nfunction describeFileError(error: unknown): string {\n const code = (error as NodeJS.ErrnoException | undefined)?.code\n const message = error instanceof Error ? error.message : String(error)\n return (typeof code === 'string' ? 'code=' + code + ' ' : '') + message.slice(0, 140)\n}\n\nfunction validPrivateKey(pem: string): boolean {\n try {\n const key = createPrivateKey(pem)\n return key.asymmetricKeyType === 'ec'\n } catch {\n return false\n }\n}\n\n/**\n * Return the certificate's expiry when it is usable with `keyPem`, or\n * `undefined` when it must be re-issued (malformed, wrong key, or expiring).\n */\nfunction inspectCertificate(certPem: string, keyPem: string, now: number): number | undefined {\n try {\n const cert = new X509Certificate(certPem)\n const certSpki = cert.publicKey.export({ type: 'spki', format: 'der' })\n const keySpki = createPublicKey(createPrivateKey(keyPem)).export({ type: 'spki', format: 'der' })\n if (!certSpki.equals(keySpki)) return undefined\n const notAfter = cert.validToDate.getTime()\n const notBefore = cert.validFromDate.getTime()\n if (!(notBefore <= now && notAfter - CERT_RENEW_LEAD_MS > now)) return undefined\n return notAfter\n } catch {\n return undefined\n }\n}\n\nasync function issueCertificate(existingKey: string | undefined, now: number): Promise<{ key: string; cert: string }> {\n // Back-date slightly so a phone whose clock trails the computer still accepts a\n // certificate issued moments ago.\n const notBeforeDate = new Date(now - 5 * 60_000)\n const notAfterDate = new Date(now + CERT_VALIDITY_MS)\n const keyPair = existingKey === undefined\n ? undefined\n : {\n privateKey: createPrivateKey(existingKey).export({ type: 'pkcs8', format: 'pem' }) as string,\n publicKey: createPublicKey(existingKey).export({ type: 'spki', format: 'pem' }) as string,\n }\n const result = await generate(\n [{ name: 'commonName', value: LAN_TLS_COMMON_NAME }],\n {\n keyType: 'ec',\n curve: 'P-256',\n algorithm: 'sha256',\n notBeforeDate,\n notAfterDate,\n ...(keyPair ? { keyPair } : {}),\n extensions: [\n { name: 'basicConstraints', cA: false, critical: true },\n { name: 'keyUsage', digitalSignature: true, keyAgreement: true, critical: true },\n { name: 'extKeyUsage', serverAuth: true },\n { name: 'subjectAltName', altNames: [{ type: 2, value: LAN_TLS_COMMON_NAME }] },\n ],\n },\n )\n return { key: existingKey ?? result.private, cert: result.cert }\n}\n\n/** Exact on-disk form, so a freshly issued value equals its later reload. */\nfunction pemFile(pem: string): string {\n return pem.replace(/\\r\\n/g, '\\n').trimEnd() + '\\n'\n}\n\nasync function writePrivate(path: string, contents: string): Promise<void> {\n const tmp = `${path}.${process.pid}.tmp`\n try {\n await writeFile(tmp, contents, { mode: 0o600 })\n await rename(tmp, path)\n } catch (error) {\n await unlink(tmp).catch(() => {})\n throw error\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createHash, randomBytes } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport type { Duplex } from 'node:stream'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { WebSocketServer } from 'ws'\nimport { BridgeConnection } from './connection.ts'\nimport { PushGateway } from './push-gateway.ts'\nimport { createLocalTransport, createRemoteTransport } from './transport-reconciler.ts'\nimport { ConnectionGate } from './connection-gate.ts'\nimport { HostBridge } from './host-bridge.ts'\nimport { DshApiProxy, type DshInteractionKind } from './dsh-api-proxy.ts'\nimport { applyReportRemote } from './report-remote.ts'\nimport type { DeepPilotReport } from './report-wire.ts'\nimport { DeviceStore, MAX_DEVICES, bridgeDataDir, deviceDisplayName, ensurePrivateBridgeDataDir, expandHome, migrateLegacyBridgeDataDir } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport {\n PairingCodeManager,\n deviceIdForPublicKey,\n loadOrCreateHostAudience,\n normalizeDeviceScopes,\n} from './device-auth.ts'\nimport type { RemoteStatus } from './remote-supervisor.ts'\nimport { localLANIPv4Addresses } from './local-address.ts'\nimport { UpdateChecker, type UpdateInfo } from './update-check.ts'\nimport { PluginUpdater, supportsPluginUpdate } from './plugin-update.ts'\nimport { setPluginUpdater } from './wire-plugin-update.ts'\nimport { setPluginUpdateProbe } from './host-capabilities.ts'\nimport { normalizeOptions } from './config.ts'\nimport type { Config } from './config.ts'\nimport { rejectUpgrade, requestClientIdentity } from './phone-http.ts'\nimport { AuthRateLimiter } from './auth-rate-limit.ts'\nimport { MAX_APP_VERSION_CHARS, MAX_DEVICE_NAME_CHARS, sanitizeDeviceField } from './connection-policy.ts'\nimport { localEndpointURLs } from './local-policy.ts'\nimport { closeServer, createPhoneServer, listen, type PhoneServerTransportError } from './phone-server.ts'\nimport { loadOrCreateLanTlsIdentity, type LanTlsIdentity } from './lan-tls.ts'\nimport {\n attemptIdFromRequest,\n buildHealthPayload,\n createLogger,\n DiagnosticsStore,\n} from './diagnostics.ts'\n\n/**\n * dsh-deeppilot — data bridge between the DSH host and DeepPilot\n * clients. Owns independent, narrowly routed LAN (TLS-only, pinned by paired\n * devices) and loopback Funnel-origin listeners. The web UI and the rest of\n * DSH's API are never exposed by these listeners.\n *\n * Data plane: an in-process HostBridge consumes a local adapter over DSH\n * Session/Workspace controllers, mirrors session summaries,\n * tracks pending approvals/questions, and fans projected protocol-v2 pushes\n * out to every connected device.\n *\n * Protocol: PROTOCOL.md is normative; src/protocol.ts and the private app's\n * Swift models mirror that v2 contract.\n */\n\nexport const name = 'deeppilot'\n\nexport { HostBridge } from './host-bridge.ts'\nexport { Config } from './config.ts'\nexport { mayReceivePush, shouldPrunePushToken, shouldReEnrollRelayToken } from './push-policy.ts'\n\n/** No web-service requirement: the plugin owns its own transport listeners. */\nexport const inject: string[] = []\n\ntype SubContext = {\n effect: (setup: () => unknown, name?: string) => unknown\n}\n\nconst SERVER_VERSION = readOwnPackageVersion()\nconst MAX_CLIENT_CONNECTIONS = 16\n/**\n * Single-frame bound. Covers the protocol maximum (4 × 8 MB base64 images\n * plus prompt text) with headroom while keeping an unauthenticated client's\n * pre-hello buffering far below ws's 100 MiB default.\n */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024\n\n/**\n * Resolve the host plugin's own version from the installed package.json.\n * Sourced at boot so the wire / UI always agrees with what npm published.\n * `createRequire(import.meta.url)` is the tsdown-bundled ESM equivalent of\n * CommonJS's `require`; the package.json sits next to lib/index.js after\n * the build, so `../package.json` resolves to the published manifest.\n */\nfunction readOwnPackageVersion(): string {\n try {\n const require_ = createRequire(import.meta.url)\n const pkg = require_('../package.json') as { version?: unknown }\n if (typeof pkg.version === 'string' && pkg.version.length > 0) return pkg.version\n } catch {\n // fall through to env / hardcoded default below\n }\n // `npm` injects this for `npm run` / `npm exec` / `npm start` invocations.\n // `process.env.npm_package_version` is unset when DSH loads the plugin\n // directly, so we keep it as a secondary source rather than the truth.\n const envVersion = process.env.npm_package_version\n if (typeof envVersion === 'string' && envVersion.length > 0) return envVersion\n return '0.0.0+unknown'\n}\n\nexport function apply(ctx: Context, options: unknown): void {\n const cfg = normalizeOptions(options)\n\n // 日志唯一出口:统一时间戳 + 级别。失败路径显式传 'warn';其余 info。\n // 这是 P0 可观测性基线——用户贴回来的每一行都能对应到具体时刻。\n const log = createLogger()\n const auditSalt = randomBytes(32)\n const auditLabel = (value: string): string => createHash('sha256')\n .update(auditSalt)\n .update(value)\n .digest('hex')\n .slice(0, 12)\n\n let scheduleRemoteReconcile: (() => void) | undefined\n let scheduleLocalReconcile: (() => void) | undefined\n const currentConfig = (): Config => normalizeOptions(options)\n const enabledNow = (): boolean => currentConfig().enabled === true\n\n // rc.2 commits volatile config edits into live refs and emits on the owning\n // fiber. Reconcile transport listeners after the new values are published.\n ;(ctx as unknown as { on: (name: string, listener: () => void) => void }).on(\n 'loader/volatile-update',\n () => queueMicrotask(() => {\n scheduleLocalReconcile?.()\n scheduleRemoteReconcile?.()\n }),\n )\n\n // Master switch, resolved against the current profile entry.\n // Individual injected services also read currentConfig() when they activate.\n if (currentConfig().enabled !== true) {\n log('disabled via settings; bridge stays inactive (rumors of /phone below are skipped)')\n }\n\n // Resolved asynchronously; route handlers await readiness. Never rejects:\n // a storage failure degrades the bridge instead of killing the host.\n const dataDir = bridgeDataDir()\n\n /** 插件启动时刻:`/phone/health` 与设置页报告都读它。 */\n const startedAt = Date.now()\n\n /**\n * 连接诊断事件:内存环形 256 条 + 按天落盘(插件数据目录 diagnostics/,\n * 保留 7 天、目录上限 5MB)。与 `diagnostics.debug` 完全解耦——失败事件\n * 无条件记录,否则用户不开调试开关就永远看不到「为什么连不上」。\n */\n const diagnostics = new DiagnosticsStore({\n dir: join(dataDir, 'diagnostics'),\n write: (line) => { console.log(line) },\n })\n\n /**\n * 传输层错误(TLS 握手失败 / HTTP 报文解析失败)的统一入口。来源先经\n * auditLabel 哈希再记录;原始 IP 绝不进入事件或日志。\n */\n const recordTransportError = (event: PhoneServerTransportError): void => {\n const code = (event.error as NodeJS.ErrnoException | undefined)?.code\n diagnostics.record('connection-error', event.kind, {\n ...(event.remoteAddress !== undefined ? { source: auditLabel(event.remoteAddress) } : {}),\n detail: typeof code === 'string'\n ? code\n : (event.error instanceof Error ? event.error.message : String(event.error)),\n })\n }\n\n // ---------- offline push (F-9) ----------\n\n // 推送的全部行为住在 PushGateway:enrollment 与持久化、中继注册、sender\n // 缓存与退避、两个 scheduler、PushOutlet 四方法、两个自测。这里只声明它并\n // 注入依赖——apply() 不再持有任何推送状态。详见 src/push-gateway.ts 文件头。\n const pushGateway = new PushGateway({\n config: currentConfig,\n dataDir,\n devices: () => auth.devices,\n audience: () => auth.audience,\n connections: () => connections,\n enabledNow,\n log,\n })\n\n const pairingCodes = new PairingCodeManager()\n const auth: { audience: string | null; devices: DeviceStore | null } = {\n audience: null,\n devices: null,\n }\n const ready = (async () => {\n try {\n try {\n const migratedFrom = await migrateLegacyBridgeDataDir()\n if (migratedFrom !== null) log(`migrated legacy plugin state from ${migratedFrom} to ${dataDir}`)\n } catch (error) {\n log('legacy plugin-state migration skipped: ' + String(error), 'warn')\n }\n await ensurePrivateBridgeDataDir()\n auth.audience = await loadOrCreateHostAudience(join(dataDir, 'host-id'))\n auth.devices = await DeviceStore.load(cfg.devicesPath ?? join(dataDir, 'devices-v2.json'))\n {\n // Startup visibility: makes \"registrations vanished after restart\"\n // instantly diagnosable (0 push rows ⇒ the file itself lost data).\n const rows = auth.devices.list()\n const registered = rows.filter((row) => row.apns !== undefined).length\n log(`device registry loaded from ${expandHome(cfg.devicesPath ?? join(dataDir, 'devices-v2.json'))}: ${rows.length} device(s), ${registered} push registration(s)`)\n }\n // Restore zero-touch push enrollment state (best effort).\n await pushGateway.restore()\n } catch (error) {\n const message = String(error)\n log('auth material unavailable, bridge degraded: ' + message, 'warn')\n return { audience: null, devices: null }\n }\n return { audience: auth.audience, devices: auth.devices }\n })()\n\n const beginPairing = async (): Promise<{ code: string; expiresAt: number; audience: string }> => {\n await ready\n if (auth.audience === null || auth.devices === null) throw new Error('device authentication unavailable')\n return { ...pairingCodes.issue(), audience: auth.audience }\n }\n\n /**\n * Settings-page push self-test: force one synthetic notification down the\n * active pathway to EVERY registered device, deliberately ignoring the\n * connected-skip and category-mute filters — an explicit user action must\n * always be able to prove delivery end to end.\n */\n const connections = new Set<BridgeConnection>()\n\n const closeConnectionsForBridge = (bridge: HostBridge): void => {\n for (const connection of connections) {\n if (!connection.isAttachedTo(bridge)) continue\n connection.closeForServerStop()\n connections.delete(connection)\n }\n }\n\n const closeAllConnections = (): void => {\n for (const connection of connections) connection.closeForServerStop()\n connections.clear()\n }\n\n /**\n * Unbind one device. Single path shared by the settings page (report-service\n * revokeDevice) and the wire-level c2s.device.revoke frame: mark the registry\n * tombstone (which also drops every APNs/WidgetKit/LiveActivity token, so the\n * push fan-out can no longer select it) and hard-drop its live sockets.\n * `except` is the connection that sent the revoke frame — it closes itself\n * with 4401 after acking, so it is not terminated here.\n */\n const revokeDevice = async (deviceId: string, except?: BridgeConnection): Promise<boolean> => {\n const { devices } = await ready\n if (!devices) throw new Error('device registry unavailable')\n const revoked = devices.revoke(deviceId, Date.now())\n if (revoked) {\n for (const connection of [...connections]) {\n if (connection === except || connection.connectedDeviceId !== deviceId) continue\n connection.terminate()\n connections.delete(connection)\n }\n log(`device revoked id=${auditLabel(deviceId)}`)\n }\n return revoked\n }\n\n const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_FRAME_BYTES })\n // The LAN TLS identity is loaded once per plugin lifetime: port or enable\n // toggles reuse it so paired devices keep their pin. `tlsIdentityRegenerated`\n // stays raised for the settings page until the next restart because it\n // means every previously paired LAN device must pair again. 身份留在\n // apply()——它被 listener 启动、配对回显与 report 三处消费,协调器不懂 TLS。\n let lanTlsIdentity: Promise<LanTlsIdentity> | undefined\n let tlsIdentityRegenerated = false\n const loadLanTls = (): Promise<LanTlsIdentity> => {\n lanTlsIdentity ??= (async () => {\n await ready\n const identity = await loadOrCreateLanTlsIdentity(join(dataDir, 'lan-tls'))\n if (identity.regenerated) {\n tlsIdentityRegenerated = true\n log(`LAN TLS identity created: fingerprint=${identity.fingerprint}; previously paired LAN devices must pair again`)\n } else if (identity.resigned) {\n log(`LAN TLS certificate renewed (fingerprint unchanged: ${identity.fingerprint})`)\n }\n return identity\n })()\n // A failed load (disk error) must not poison later reconciles.\n lanTlsIdentity.catch(() => { lanTlsIdentity = undefined })\n return lanTlsIdentity\n }\n\n const updateChecker = new UpdateChecker({ log, currentVersion: SERVER_VERSION })\n updateChecker.scheduleInitial()\n const updateInfo = (): UpdateInfo => updateChecker.get()\n\n // Typert Remote for the web settings page (deeppilot/report).\n applyReportRemote(ctx, async () => {\n let pairingReady = false\n let devices: Array<{\n deviceId: string; deviceName: string; customName?: string; appVersion: string; firstSeenTs: number; lastSeenTs: number\n fingerprint: string; scopes: ReturnType<typeof normalizeDeviceScopes>; revokedAt?: number\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n }> = []\n try {\n await ready\n pairingReady = auth.audience !== null && auth.devices !== null\n // Strip the raw APNs token at the source; the report carries only the\n // registration fact (environment + freshness).\n devices = (auth.devices?.list() ?? [])\n .filter((device) => device.publicKey !== undefined && device.fingerprint !== undefined)\n .map(({ deviceId, deviceName, customName, appVersion, firstSeenTs, lastSeenTs, fingerprint, scopes, revokedAt, apns }) => ({\n deviceId,\n deviceName: deviceDisplayName({ deviceName, customName }),\n ...(customName ? { customName } : {}),\n appVersion,\n firstSeenTs,\n lastSeenTs,\n fingerprint: fingerprint!,\n scopes: normalizeDeviceScopes(scopes),\n ...(revokedAt !== undefined ? { revokedAt } : {}),\n ...(apns ? { apns: { environment: apns.environment, updatedAt: apns.updatedAt } } : {}),\n }))\n } catch {\n // degraded: report the minimum without token facts\n }\n const update = updateInfo()\n const lanAddresses = localLANIPv4Addresses()\n return {\n protocolVersion: 2,\n serverVersion: SERVER_VERSION,\n pluginVersion: update.currentVersion,\n ...(update.available ? { updateAvailable: true } : {}),\n ...(update.releaseUrl !== null ? { releaseUrl: update.releaseUrl } : {}),\n enabled: currentConfig().enabled === true,\n identityPath: expandHome(currentConfig().devicesPath ?? join(bridgeDataDir(), 'devices-v2.json')),\n pairingReady,\n activeConnections: connections.size,\n historyBufferMax: currentConfig().historyBufferMax ?? 2000,\n lanAddresses,\n local: localStatus(lanAddresses),\n remote: remoteStatus(),\n devices,\n // 设置页「连接诊断」区块的数据源。只带结构化事实(类型 / 分因 /\n // 时间 / 来源哈希),不携带 debug 开关状态,也不含任何凭据。\n diagnostics: {\n startedAt,\n recentFailures: diagnostics.recentFailures(20).map((event) => ({\n ts: event.ts,\n kind: event.kind,\n reason: event.reason,\n ...(event.attemptId !== undefined ? { attemptId: event.attemptId } : {}),\n ...(event.source !== undefined ? { source: event.source } : {}),\n ...(event.closeCode !== undefined ? { closeCode: event.closeCode } : {}),\n ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),\n })),\n },\n }\n }, beginPairing, async (deviceId) => {\n // Settings-page revocation shares the exact path used by the wire-level\n // c2s.device.revoke frame (index.ts revokeDevice helper).\n return await revokeDevice(deviceId)\n }, async (deviceId, customName) => {\n const { devices } = await ready\n if (!devices) throw new Error('device registry unavailable')\n const effectiveName = await devices.setCustomName(deviceId, customName)\n if (effectiveName === null) throw new Error('active device not found')\n const normalized = customName === null ? '' : sanitizeDeviceField(customName, MAX_DEVICE_NAME_CHARS)\n log(`device custom name updated id=${auditLabel(deviceId)} custom=${String(normalized.length > 0)}`)\n return normalized === '' ? null : normalized\n }, async (deviceId, scopes) => {\n const { devices } = await ready\n if (!devices) throw new Error('device registry unavailable')\n const updated = devices.setScopes(deviceId, scopes)\n if (updated === null) throw new Error('active device not found')\n // Scope changes take effect on the next signed connection; disconnect the\n // current socket so stale in-memory authority cannot survive the update.\n for (const connection of [...connections]) {\n if (connection.connectedDeviceId !== deviceId) continue\n connection.terminate()\n connections.delete(connection)\n }\n log(`device scopes updated id=${auditLabel(deviceId)} scopes=${updated.join(',')}`)\n return updated\n }, () => pushGateway.relayTest(), () => pushGateway.selfTest())\n const state: { bridge?: HostBridge } = {}\n let pluginUpdater: PluginUpdater | undefined\n // Optional injection preserves old hosts. Capture a host-owned manager; the\n // running package operation deliberately outlives this plugin's HMR unload.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['pluginManager'],\n (sub) => {\n const manager = (sub as { pluginManager?: unknown }).pluginManager\n if (!supportsPluginUpdate(manager)) return\n const updater = new PluginUpdater({ manager, currentVersion: SERVER_VERSION, journalPath: join(dataDir, 'update-journal.json') })\n pluginUpdater = updater\n if (state.bridge) setPluginUpdater(state.bridge, updater)\n ;(sub as SubContext).effect(() => () => {\n if (pluginUpdater !== updater) return\n pluginUpdater = undefined\n if (state.bridge) setPluginUpdater(state.bridge, undefined)\n }, 'deeppilot: optional plugin updater')\n },\n )\n let pendingUpgrades = 0\n const authRateLimiter = new AuthRateLimiter()\n\n const readJSONBody = async (req: IncomingMessage, maxBytes = 16 * 1024): Promise<unknown> => {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buffer.length\n if (size > maxBytes) throw new Error('request body too large')\n chunks.push(buffer)\n }\n return JSON.parse(Buffer.concat(chunks).toString('utf8')) as unknown\n }\n\n const handlePair = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n res.setHeader('Content-Type', 'application/json')\n // 来源与 attemptId 在最早处解析:之后的每个拒绝分支都要带上它们。\n const source = requestClientIdentity(req)\n const attemptId = attemptIdFromRequest(req)\n const reportPairRejected = (reason: string): void => {\n diagnostics.record('pair-rejected', reason, {\n source: auditLabel(source),\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n }\n if (!enabledNow()) {\n reportPairRejected('bridge-disabled')\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, error: 'bridge disabled' }))\n return\n }\n if (req.method !== 'POST') {\n reportPairRejected('method-not-allowed')\n res.statusCode = 405\n res.setHeader('Allow', 'POST')\n res.end(JSON.stringify({ ok: false, error: 'POST required' }))\n return\n }\n const admission = authRateLimiter.admit(source)\n if (!admission.ok) {\n reportPairRejected('rate-limited')\n res.statusCode = 429\n res.setHeader('Retry-After', String(Math.max(1, Math.ceil(admission.retryAfterMs / 1_000))))\n res.end(JSON.stringify({ ok: false, error: 'pairing rate limited' }))\n return\n }\n try {\n const { devices, audience } = await ready\n if (!devices || !audience) throw new Error('device authentication unavailable')\n const raw = await readJSONBody(req) as Record<string, unknown>\n if (raw === null || typeof raw !== 'object' || raw.v !== 2) throw new TypeError('protocol v2 required')\n const code = typeof raw.code === 'string' ? raw.code : ''\n const publicKey = typeof raw.publicKey === 'string' ? raw.publicKey : ''\n const deviceName = sanitizeDeviceField(raw.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(raw.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n const deviceId = deviceIdForPublicKey(publicKey)\n if (devices.list().length >= MAX_DEVICES && devices.authorized(deviceId) === undefined) {\n reportPairRejected('registry-full')\n res.statusCode = 409\n res.end(JSON.stringify({ ok: false, error: 'device registry is full' }))\n return\n }\n if (!pairingCodes.consume(code)) {\n const failure = authRateLimiter.recordFailure(source)\n reportPairRejected('invalid-code')\n res.statusCode = failure.blocked ? 429 : 401\n if (failure.retryAfterMs > 0) res.setHeader('Retry-After', String(Math.max(1, Math.ceil(failure.retryAfterMs / 1_000))))\n res.end(JSON.stringify({ ok: false, error: failure.blocked ? 'pairing rate limited' : 'pairing code invalid or expired' }))\n return\n }\n const record = devices.register({\n publicKey,\n deviceName,\n appVersion,\n scopes: normalizeDeviceScopes(raw.scopes),\n }, Date.now())\n authRateLimiter.recordSuccess(source)\n log(`device paired id=${auditLabel(record.deviceId)} source=${auditLabel(source)}`)\n // Only a request that arrived over the LAN TLS listener echoes the pin;\n // Funnel pairings terminate TLS at tailscaled with a public CA cert.\n const overLanTls = (req.socket as { encrypted?: boolean }).encrypted === true\n const tlsFingerprint = overLanTls ? (await lanTlsIdentity)?.fingerprint : undefined\n res.statusCode = 201\n res.end(JSON.stringify({\n ok: true,\n v: 2,\n deviceId: record.deviceId,\n audience,\n scopes: record.scopes ?? [],\n ...(tlsFingerprint ? { tlsFingerprint } : {}),\n }))\n } catch (error) {\n const badRequest = error instanceof SyntaxError || error instanceof TypeError\n reportPairRejected(badRequest ? 'bad-request' : 'internal-error')\n res.statusCode = badRequest ? 400 : 503\n res.end(JSON.stringify({ ok: false, error: error instanceof Error ? error.message : 'pairing failed' }))\n } finally {\n admission.release()\n }\n }\n\n const handleUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {\n // 来源与 attemptId 在 socket 建立之前就确定:任何拒绝分支都必须能回答\n // 「谁、在什么时候、带着哪次尝试」连进来。来源只以哈希出现。\n const source = requestClientIdentity(req)\n const sourceLabel = auditLabel(source)\n const attemptId = attemptIdFromRequest(req)\n const reject = (status: number, reason: string): void => {\n diagnostics.record('upgrade-rejected', reason, {\n source: sourceLabel,\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n rejectUpgrade(socket, status, reason)\n }\n void (async () => {\n try {\n if (!enabledNow()) {\n reject(503, 'bridge disabled')\n return\n }\n if (connections.size + pendingUpgrades >= MAX_CLIENT_CONNECTIONS) {\n reject(429, 'too many connections')\n return\n }\n pendingUpgrades += 1\n // 声明在 try 之外:finally 需要归还一个可能未创建成功的门的名额。\n let gate: ConnectionGate | undefined\n try {\n const { devices, audience } = await ready\n if (!audience || !devices) {\n reject(503, 'bridge degraded')\n return\n }\n const bridge = state.bridge\n if (!bridge) {\n reject(503, 'bridge not ready')\n return\n }\n // The gate is created before the ws exists, so the admission slot is\n // held even when the upgrade callback never runs (socket gone\n // mid-upgrade). Its attach timeout covers that case; once the socket\n // is live, the gate's hello deadline takes over.\n gate = new ConnectionGate({\n source,\n devices,\n audience,\n limiter: authRateLimiter,\n log,\n auditLabel,\n diagnostics,\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n if (!gate.admitted) {\n reject(429, 'authentication rate limited')\n return\n }\n const live = gate\n wss.handleUpgrade(req, socket, head, (ws) => {\n if (auth.audience !== audience || state.bridge !== bridge) {\n live.markDead()\n // 认证期间 bridge 被替换:这个 socket 已不可能服务,记录后关。\n diagnostics.record('upgrade-rejected', 'bridge-changed', {\n source: sourceLabel,\n ...(attemptId !== undefined ? { attemptId } : {}),\n })\n ws.close(1012, 'bridge changed')\n return\n }\n try {\n const connection = new BridgeConnection(ws, {\n bridge,\n devices,\n serverVersion: SERVER_VERSION,\n audience,\n log,\n debug: currentConfig().diagnostics?.debug === true,\n source: live.source,\n rateLimiter: authRateLimiter,\n auditLabel,\n diagnostics,\n ...(attemptId !== undefined ? { attemptId } : {}),\n onClosed: (closed) => connections.delete(closed),\n onDeviceAuthenticated: (deviceId) => {\n log(`device authenticated id=${auditLabel(deviceId)} source=${auditLabel(live.source)}`)\n },\n onPushEnrollKey: (enrollKey) => pushGateway.enrollKey(enrollKey),\n // A device that unbinds itself from the app must not leave a\n // second socket (e.g. an older install) still live: the\n // registry tombstone is already written by the handler, so\n // this only drops the sibling sockets.\n onDeviceRevoke: (deviceId, except) => revokeDevice(deviceId, except),\n }, live)\n connections.add(connection)\n } catch (error) {\n live.markDead()\n ws.close(1011, 'connection setup failed')\n throw error\n }\n })\n } finally {\n // The attach window ends here: if no socket was produced (the upgrade\n // callback never ran), the slot goes back immediately instead of\n // waiting for the attach timeout. An attached gate keeps its slot\n // until a settle path or its hello deadline.\n gate?.releaseIfNeverAttached()\n pendingUpgrades -= 1\n }\n } catch (error) {\n log('upgrade failed: ' + String(error), 'warn')\n diagnostics.record('upgrade-rejected', 'internal-error', {\n source: sourceLabel,\n ...(attemptId !== undefined ? { attemptId } : {}),\n detail: error instanceof Error ? error.message : String(error),\n })\n rejectUpgrade(socket, 500, 'internal error')\n }\n })()\n }\n\n const handleHealth = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n await ready\n res.setHeader('Content-Type', 'application/json')\n if (!auth.audience || !auth.devices) {\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, degraded: true }))\n return\n }\n // 只加不删:原有字段(ok/enabled/protocolVersion/serverVersion/\n // dataPlane)语义不变;新增字段描述「此刻为什么连不上」:\n // LAN 相位与端口、数据面相位、TLS 是否重建、启动时间、最近一次失败。\n // 刻意不返回 debug 开关状态与地址候选(health 不该成为扫描器的字典)。\n const local = localTransport.status()\n res.statusCode = 200\n res.end(JSON.stringify(buildHealthPayload({\n startedAt,\n enabled: enabledNow(),\n protocolVersion: 2,\n serverVersion: SERVER_VERSION,\n dataPlaneActive: Boolean(state.bridge),\n localPhase: local.phase,\n localPort: local.port,\n tlsIdentityRegenerated,\n lastFailure: diagnostics.lastFailureSummary(),\n })))\n } catch {\n res.statusCode = 500\n res.end(JSON.stringify({ ok: false }))\n }\n }\n\n const phoneHandlers = {\n health: handleHealth,\n pair: handlePair,\n upgrade: handleUpgrade,\n }\n\n // Independent transport listeners. LAN owns a stable configurable port;\n // Funnel keeps its loopback-only ephemeral origin. They share only the\n // narrow handlers above, so a LAN bind failure cannot take remote access\n // down and neither listener exposes DSH's wider web/API surface.\n //\n // 差分、串行、拆除与状态发布的公共协议住在 TransportReconciler;两个传输\n // 各自的差异(skipWhenDisabled / appliedKey 时机 / 错误分支)在\n // createLocalTransport 与 createRemoteTransport 里显式声明并原样保留——\n // 统一它们会改变一条当前稳定路径上的并发语义,属于单独一轮的决策。\n const localTransport = createLocalTransport({\n handlers: phoneHandlers,\n config: currentConfig,\n tls: loadLanTls,\n log,\n onTransportError: recordTransportError,\n })\n const remoteTransport = createRemoteTransport({\n handlers: phoneHandlers,\n config: currentConfig,\n originURL: () => originURL,\n dataDir,\n log,\n })\n const localStatus = (addresses: readonly string[]): DeepPilotReport['local'] => {\n const state = localTransport.status()\n return {\n ...state,\n endpoints: state.phase === 'online' ? localEndpointURLs(addresses, state.port) : [],\n ...(tlsIdentityRegenerated ? { tlsIdentityRegenerated: true } : {}),\n }\n }\n const remoteStatus = (): RemoteStatus => remoteTransport.status()\n\n const originServer = createPhoneServer(phoneHandlers, undefined, {\n onTransportError: recordTransportError,\n })\n let originURL: string | undefined\n scheduleLocalReconcile = () => localTransport.scheduleReconcile()\n scheduleRemoteReconcile = () => remoteTransport.scheduleReconcile()\n\n ;(ctx as unknown as SubContext).effect(() => {\n scheduleLocalReconcile?.()\n void listen(originServer, 0, '127.0.0.1').then(() => {\n const address = originServer.address()\n if (address && typeof address === 'object') {\n originURL = `http://127.0.0.1:${address.port}`\n scheduleRemoteReconcile?.()\n }\n }, (error: unknown) => log('remote origin failed: ' + String(error), 'warn'))\n return async () => {\n scheduleLocalReconcile = undefined\n scheduleRemoteReconcile = undefined\n await Promise.allSettled([\n closeServer(originServer),\n localTransport.dispose(),\n remoteTransport.dispose(),\n ])\n }\n }, 'deeppilot: independent transports')\n\n const sweep = setInterval(() => {\n const now = Date.now()\n for (const connection of connections) {\n if (connection.isStale(now, 60_000)) {\n log('dropping stale connection')\n connection.closeIdle()\n connections.delete(connection)\n }\n }\n }, 30_000)\n ;(ctx as unknown as SubContext).effect(() => () => clearInterval(sweep), 'deeppilot: stale sweep')\n\n // Build the bridge's stable protocol-facing adapter from the public\n // Session/Workspace controllers.\n\n /**\n * Whether the resident Gateway Client represents a real phone surface. A\n * paired device remains answerable even while offline: HostBridge retains an\n * authoritative pending snapshot that the app pulls on reconnect, while\n * APNs is only a best-effort wakeup. With no paired device this Client calls\n * `next()`; Gateway's independent official Web delivery is unaffected.\n */\n const hasPairedPhoneSurface = (_kind: DshInteractionKind): boolean => {\n const devices = auth.devices?.list() ?? []\n return devices.some((device) => device.revokedAt === undefined)\n }\n\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['sessionController', 'connection', 'typertGateway'],\n (sub) => {\n if (currentConfig().enabled !== true) {\n log('bridge disabled; data plane stays inactive')\n return\n }\n const apiCtx = sub as unknown as Context & SubContext\n let proxy: DshApiProxy\n try {\n proxy = new DshApiProxy(apiCtx, { shouldSurfaceInteraction: hasPairedPhoneSurface })\n } catch (error) {\n log('DSH session bridge unavailable: ' + String(error), 'warn')\n return\n }\n setPluginUpdateProbe(proxy, () => pluginUpdater !== undefined)\n const bridge = new HostBridge(\n proxy,\n cfg.historyBufferMax,\n join(dataDir, 'prompt-deliveries-v1.json'),\n join(dataDir, 'schedule-mutations-v1.json'),\n join(dataDir, 'fork-mutations-v1.json'),\n log,\n )\n bridge.setPushOutlet(pushGateway)\n setPluginUpdater(bridge, pluginUpdater)\n state.bridge = bridge\n bridge.start()\n log('data plane active (mux + host streams)')\n apiCtx.effect(() => () => {\n closeConnectionsForBridge(bridge)\n if (state.bridge === bridge) state.bridge = undefined\n bridge.dispose()\n }, 'deeppilot: host streams')\n },\n )\n\n ;(ctx as unknown as SubContext).effect(() => async () => {\n closeAllConnections()\n const bridge = state.bridge\n state.bridge = undefined\n bridge?.dispose()\n updateChecker.dispose()\n await pushGateway.dispose()\n const wssClosed = new Promise<void>((resolve) => wss.close(() => resolve()))\n await Promise.allSettled([wssClosed])\n // 最后收尾诊断落盘:关闭前那次连接失败必须已经在磁盘上。\n await diagnostics.dispose()\n }, 'deeppilot: process resources')\n}\n"],"mappings":"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"}