@senguoyun/dsh-arkme 0.1.67 → 0.1.68

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.
Files changed (167) hide show
  1. package/docs/chat-add-long-article.md +39 -0
  2. package/docs/desktop-direct-recording.md +40 -0
  3. package/docs/desktop-membership-handoff.md +36 -0
  4. package/docs/desktop-regression-2026-09-19.md +77 -0
  5. package/docs/desktop-screenshot.md +59 -0
  6. package/docs/private-interaction-directory-handoff.md +95 -0
  7. package/docs/recording-calendar-coverage.md +41 -0
  8. package/docs/recording-presence-handoff.md +155 -0
  9. package/lib/client.js +49834 -45144
  10. package/lib/client.js.map +1 -1
  11. package/lib/harness-model-client.js +8 -0
  12. package/lib/harness-native-selection-client.js +72 -5
  13. package/lib/harness-sidebar-client.js +46 -2
  14. package/lib/index.js +27 -3
  15. package/lib/persistent-extension.js +1 -1
  16. package/lib/{persistent-runtime-C_Geaa4B.js → persistent-runtime-DlJzDZlE.js} +816 -94
  17. package/lib/sdk.js +8 -0
  18. package/lib/types/arkme-service.d.ts +18 -1
  19. package/lib/types/arkme-service.d.ts.map +1 -1
  20. package/lib/types/call-record-presentation.d.ts +2 -0
  21. package/lib/types/call-record-presentation.d.ts.map +1 -1
  22. package/lib/types/client/ArkmeArticlePicker.d.ts +10 -0
  23. package/lib/types/client/ArkmeArticlePicker.d.ts.map +1 -0
  24. package/lib/types/client/ArkmeCallDetailContent.d.ts.map +1 -1
  25. package/lib/types/client/ArkmeCallRecordContent.d.ts.map +1 -1
  26. package/lib/types/client/ArkmeCallSurface.d.ts +3 -1
  27. package/lib/types/client/ArkmeCallSurface.d.ts.map +1 -1
  28. package/lib/types/client/ArkmeChatMemberActions.d.ts +2 -0
  29. package/lib/types/client/ArkmeChatMemberActions.d.ts.map +1 -1
  30. package/lib/types/client/ArkmeComposerScreenshotButton.d.ts +13 -0
  31. package/lib/types/client/ArkmeComposerScreenshotButton.d.ts.map +1 -0
  32. package/lib/types/client/ArkmeComposerSendButton.d.ts.map +1 -1
  33. package/lib/types/client/ArkmeDetailShell.d.ts +2 -1
  34. package/lib/types/client/ArkmeDetailShell.d.ts.map +1 -1
  35. package/lib/types/client/ArkmeDshMenu.d.ts +2 -0
  36. package/lib/types/client/ArkmeDshMenu.d.ts.map +1 -1
  37. package/lib/types/client/ArkmeEmojiPicker.d.ts.map +1 -1
  38. package/lib/types/client/ArkmeFileViewer.d.ts.map +1 -1
  39. package/lib/types/client/ArkmeGroupChatControls.d.ts +32 -0
  40. package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
  41. package/lib/types/client/ArkmeLinkQuickView.d.ts +8 -0
  42. package/lib/types/client/ArkmeLinkQuickView.d.ts.map +1 -0
  43. package/lib/types/client/ArkmeLongArticleDialog.d.ts +6 -3
  44. package/lib/types/client/ArkmeLongArticleDialog.d.ts.map +1 -1
  45. package/lib/types/client/ArkmeLongArticleQuickView.d.ts +6 -0
  46. package/lib/types/client/ArkmeLongArticleQuickView.d.ts.map +1 -0
  47. package/lib/types/client/ArkmeMarkdownBody.d.ts +4 -1
  48. package/lib/types/client/ArkmeMarkdownBody.d.ts.map +1 -1
  49. package/lib/types/client/ArkmeMembershipDialog.d.ts +14 -0
  50. package/lib/types/client/ArkmeMembershipDialog.d.ts.map +1 -0
  51. package/lib/types/client/ArkmeMessageReadReceipt.d.ts.map +1 -1
  52. package/lib/types/client/ArkmeNoteDetails.d.ts +1 -1
  53. package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
  54. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  55. package/lib/types/client/ArkmeQuickAdd.d.ts +2 -1
  56. package/lib/types/client/ArkmeQuickAdd.d.ts.map +1 -1
  57. package/lib/types/client/ArkmeReadReceiptIcon.d.ts +7 -0
  58. package/lib/types/client/ArkmeReadReceiptIcon.d.ts.map +1 -0
  59. package/lib/types/client/ArkmeRecordingSurface.d.ts +3 -1
  60. package/lib/types/client/ArkmeRecordingSurface.d.ts.map +1 -1
  61. package/lib/types/client/ArkmeRichContent.d.ts.map +1 -1
  62. package/lib/types/client/ArkmeRichText.d.ts +6 -2
  63. package/lib/types/client/ArkmeRichText.d.ts.map +1 -1
  64. package/lib/types/client/ArkmeRightPanelHeader.d.ts +3 -1
  65. package/lib/types/client/ArkmeRightPanelHeader.d.ts.map +1 -1
  66. package/lib/types/client/ArkmeScreenshotCropDialog.d.ts +9 -0
  67. package/lib/types/client/ArkmeScreenshotCropDialog.d.ts.map +1 -0
  68. package/lib/types/client/ArkmeSearchSurface.d.ts.map +1 -1
  69. package/lib/types/client/ArkmeSidebar.d.ts +9 -0
  70. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  71. package/lib/types/client/ArkmeSourceBreadcrumb.d.ts.map +1 -1
  72. package/lib/types/client/ArkmeVoiceprintSurface.d.ts.map +1 -1
  73. package/lib/types/client/ArkmeWideConversation.d.ts.map +1 -1
  74. package/lib/types/client/api.d.ts +1 -1
  75. package/lib/types/client/api.d.ts.map +1 -1
  76. package/lib/types/client/arkme-membership.d.ts +15 -0
  77. package/lib/types/client/arkme-membership.d.ts.map +1 -0
  78. package/lib/types/client/browser-screenshot.d.ts +27 -0
  79. package/lib/types/client/browser-screenshot.d.ts.map +1 -0
  80. package/lib/types/client/chat-directory-store.d.ts.map +1 -1
  81. package/lib/types/client/composer-article-store.d.ts +35 -0
  82. package/lib/types/client/composer-article-store.d.ts.map +1 -0
  83. package/lib/types/client/composer-paste-focus.d.ts +21 -0
  84. package/lib/types/client/composer-paste-focus.d.ts.map +1 -0
  85. package/lib/types/client/composer-selection-request.d.ts +2 -0
  86. package/lib/types/client/composer-selection-request.d.ts.map +1 -1
  87. package/lib/types/client/conversation-menu-layer.d.ts +9 -2
  88. package/lib/types/client/conversation-menu-layer.d.ts.map +1 -1
  89. package/lib/types/client/conversation-selector-style.d.ts +2 -2
  90. package/lib/types/client/conversation-selector-style.d.ts.map +1 -1
  91. package/lib/types/client/directory-search-layout.d.ts +1 -1
  92. package/lib/types/client/directory-search-layout.d.ts.map +1 -1
  93. package/lib/types/client/file-send-tasks.d.ts +1 -1
  94. package/lib/types/client/file-send-tasks.d.ts.map +1 -1
  95. package/lib/types/client/harness-session-hover.d.ts.map +1 -1
  96. package/lib/types/client/mention-read-status.d.ts +10 -0
  97. package/lib/types/client/mention-read-status.d.ts.map +1 -0
  98. package/lib/types/client/menu-hover-region.d.ts +5 -0
  99. package/lib/types/client/menu-hover-region.d.ts.map +1 -0
  100. package/lib/types/client/recordings/ArkmeDirectRecording.d.ts +10 -0
  101. package/lib/types/client/recordings/ArkmeDirectRecording.d.ts.map +1 -0
  102. package/lib/types/client/recordings/ArkmeRecordingNavigationHint.d.ts +9 -0
  103. package/lib/types/client/recordings/ArkmeRecordingNavigationHint.d.ts.map +1 -0
  104. package/lib/types/client/recordings/ArkmeRecordingTimeline.d.ts +4 -2
  105. package/lib/types/client/recordings/ArkmeRecordingTimeline.d.ts.map +1 -1
  106. package/lib/types/client/recordings/direct-recording-capture.d.ts +14 -0
  107. package/lib/types/client/recordings/direct-recording-capture.d.ts.map +1 -0
  108. package/lib/types/client/recordings/direct-recording-journal.d.ts +33 -0
  109. package/lib/types/client/recordings/direct-recording-journal.d.ts.map +1 -0
  110. package/lib/types/client/recordings/direct-recording-store.d.ts +68 -0
  111. package/lib/types/client/recordings/direct-recording-store.d.ts.map +1 -0
  112. package/lib/types/client/recordings/recording-breath.d.ts +6 -0
  113. package/lib/types/client/recordings/recording-breath.d.ts.map +1 -0
  114. package/lib/types/client/recordings/recording-coverage.d.ts +6 -0
  115. package/lib/types/client/recordings/recording-coverage.d.ts.map +1 -0
  116. package/lib/types/client/redesign/styles.d.ts.map +1 -1
  117. package/lib/types/client/search-source-rows.d.ts +9 -0
  118. package/lib/types/client/search-source-rows.d.ts.map +1 -0
  119. package/lib/types/client/self-topic-menu-bridge.d.ts +2 -6
  120. package/lib/types/client/self-topic-menu-bridge.d.ts.map +1 -1
  121. package/lib/types/client/self-topic-menu-hover.d.ts.map +1 -1
  122. package/lib/types/client/text-link-parser.d.ts +1 -9
  123. package/lib/types/client/text-link-parser.d.ts.map +1 -1
  124. package/lib/types/client/text-link-tlds.d.ts +1 -1
  125. package/lib/types/client/text-link-tlds.d.ts.map +1 -1
  126. package/lib/types/client/use-conversation-name-search.d.ts +9 -0
  127. package/lib/types/client/use-conversation-name-search.d.ts.map +1 -0
  128. package/lib/types/client/use-resizable-note-detail.d.ts +3 -3
  129. package/lib/types/client/use-resizable-note-detail.d.ts.map +1 -1
  130. package/lib/types/desktop-screenshot-contract.d.ts +14 -0
  131. package/lib/types/desktop-screenshot-contract.d.ts.map +1 -0
  132. package/lib/types/desktop-screenshot.d.ts +22 -0
  133. package/lib/types/desktop-screenshot.d.ts.map +1 -0
  134. package/lib/types/host-api.d.ts.map +1 -1
  135. package/lib/types/markdown.d.ts +6 -1
  136. package/lib/types/markdown.d.ts.map +1 -1
  137. package/lib/types/recording-coverage.d.ts +12 -0
  138. package/lib/types/recording-coverage.d.ts.map +1 -0
  139. package/lib/types/sdk/index.d.ts +6 -0
  140. package/lib/types/sdk/index.d.ts.map +1 -1
  141. package/lib/types/search-record-links.d.ts +3 -0
  142. package/lib/types/search-record-links.d.ts.map +1 -0
  143. package/lib/types/services/background-sound-membership-service.d.ts +5 -0
  144. package/lib/types/services/background-sound-membership-service.d.ts.map +1 -1
  145. package/lib/types/services/call-history-service.d.ts +1 -1
  146. package/lib/types/services/call-history-service.d.ts.map +1 -1
  147. package/lib/types/services/chat-service.d.ts +8 -1
  148. package/lib/types/services/chat-service.d.ts.map +1 -1
  149. package/lib/types/services/membership-service.d.ts +13 -0
  150. package/lib/types/services/membership-service.d.ts.map +1 -0
  151. package/lib/types/services/record-service.d.ts +1 -1
  152. package/lib/types/services/record-service.d.ts.map +1 -1
  153. package/lib/types/services/recording-service.d.ts.map +1 -1
  154. package/lib/types/services/search-service.d.ts.map +1 -1
  155. package/lib/types/services/service.d.ts +1 -1
  156. package/lib/types/services/service.d.ts.map +1 -1
  157. package/lib/types/services/source-service.d.ts +7 -1
  158. package/lib/types/services/source-service.d.ts.map +1 -1
  159. package/lib/types/state-store.d.ts +1 -1
  160. package/lib/types/state-store.d.ts.map +1 -1
  161. package/lib/types/text-link-parser.d.ts +10 -0
  162. package/lib/types/text-link-parser.d.ts.map +1 -0
  163. package/lib/types/text-link-tlds.d.ts +2 -0
  164. package/lib/types/text-link-tlds.d.ts.map +1 -0
  165. package/lib/types/types.d.ts +53 -3
  166. package/lib/types/types.d.ts.map +1 -1
  167. package/package.json +5 -5
@@ -0,0 +1,39 @@
1
+ # 私聊、群聊:添加长文
2
+
3
+ ## 本轮范围
4
+
5
+ - 输入框“+ → 写长文”改为“添加长文”,先打开选择器,不创建云端记录。
6
+ - 列出自己创建的长文,支持搜索、分页、只读全文预览、单选确认。
7
+ - “新建长文”复用现有编辑器,完成按钮为“添加到待发送”,仅保存本地草稿。
8
+ - 选中或新建后在输入框显示待发送卡片;只有点击发送(或输入框中 Enter)才发送。
9
+ - 长文独立发送;原输入框中的文字、提及、附件不消费、不清空。卡片明确提示此行为。
10
+ - 待发送状态按环境、账号和会话隔离;发送失败保留内容与稳定提交标识,重复点击不重复发起。
11
+ - 离开或切换会话时关闭长文选择器及其子编辑器;返回原会话不会自动重开。已添加的待发送长文、原文字及附件保留,只有再次点击才打开选择器。
12
+ - “发给自己”和主题原有的“写长文”流程保持不变。
13
+
14
+ ## 复用的能力
15
+
16
+ 选择器使用既有 `search.scene(long_article)` / `search.records` 分页,按 owner/creator 过滤;没有新增后端 HTTP 接口。
17
+
18
+ 插件新增只读操作 `source.long-article.own`:核对当前账号,使用记录详情验证所有者与创建者,返回完整正文和现有格式的签名转发引用。搜索摘要不能作为发送正文。
19
+
20
+ 已有文章经 `source.forward-messages` 发送,保留既有转发语义和原文引用。新增可选 `expectedUserId`、`sendAtMillis` 用于账号隔离与稳定重试。新文章经现有 `source.long-article.publish` 发送,复用稳定 record/relation 标识和图片处理。
21
+
22
+ 发送后清理编辑器草稿使用可选 `expectedRecordUid`,由本地状态存储原子检查,避免删除另一个窗口后续保存的草稿。选中卡片保存在浏览器本地;浏览器禁止存储时退化为当前页面内保留。
23
+
24
+ ## 验证与边界(2026-09-19)
25
+
26
+ - 构建、类型检查通过。
27
+ - 14 个相关测试文件共 540 项通过。
28
+ - 真实私聊/群聊组件新增流程测试 2 项通过,验证选中不发送,显式发送后文字和附件仍保留。
29
+ - 隔离浏览器验证通过:明暗模式、360/640 像素窗口、搜索、分页、预览、创建后仅待发送;全部网络请求使用合成响应,没有给真实联系人发送消息。
30
+ - 完整对话回归:327 项通过、2 项失败。两项失败均来自此前已有的 `mention-read-status.tsx` 空 scope 读取 `sourceRef`,已与修改前基线对照,本轮未修改该逻辑。
31
+ - 固定 Web 预览仍为 `http://127.0.0.1:3098/`,只更新 `/tmp/arkme-membership-0918-ru7UEP` 隔离插件运行环境。未改动原生 Arkme 客户端、未占用其端口。
32
+ - 上述初次验证时预览未登录。09-19 补测已恢复现有登录,真实本人长文列表、分页、标题搜索、详情核验、选择待发送及移除通过;没有触发真实转发/发布。见 [集中回归记录](desktop-regression-2026-09-19.md)。
33
+
34
+ ### master 整合后的退出回归
35
+
36
+ - 2026-09-19 补回长文弹窗的消息鉴权引用、格式和内容块类型字段,保留 master 收到长文的鉴权与 Markdown 图片显示。
37
+ - 更新旧长文入口测试后,发现新增选择器退出会话时只隐藏、未清空打开状态,返回时会自动重开。经确认在会话退出及会话身份切换两处清理选择器状态,不删除草稿或待发送内容。
38
+ - 6 个相关测试文件、357 项通过,包括私聊/群聊退出与返回、子编辑器关闭、待发送长文及文字/附件保留、显式发送,以及上游收到长文读取。类型检查和完整构建通过。
39
+ - 本轮仅本地源码验证,未更新或重启固定 3098 预览及已安装 Arkme 客户端;不将组件测试标为最新登录浏览器或真实发送验收。
@@ -0,0 +1,40 @@
1
+ # 桌面录音页:直接录音
2
+
3
+ ## 入口与行为
4
+
5
+ - 日历下方新增“开始录音”;保留“导入历史音频”和“全天候录音”(手机功能介绍)。
6
+ - 仅用户点击后申请麦克风权限并开始;不采集系统音频、摄像头或屏幕。
7
+ - 录音开始时间取实际时间,日历切换至今天。切换到其他插件页面不停止录音,显示浮动状态和“结束并保存”。
8
+ - 第一栏录音入口在实际采集期间显示 7px 红点及“录音中”,不改变导航尺寸。悬浮或键盘聚焦显示“本机正在录音”、实际采集时长和查看提示;移开、失焦、Escape、点击或停止录音立即关闭提示。点击仍进入录音页,不会停止录音。
9
+ - 录音入口红点和淡红底晕、开始按钮的麦克风图标、页面状态红点、浮动状态红点及导航悬浮提示采用同一 2 秒呼吸节拍。仅图形亮度/缩放变化,文字、时长和布局不参与动画;后打开的提示按本次录音开始时间对齐节拍。系统“减少动态效果”开启时保留静态标识,关闭后重新同步。
10
+ - 导航提示只读取当前账号、当前环境、当前 Web 窗口的真实录音状态。准备、保存、上传阶段不显示录音红点;账号切换、隐藏或锁定导航时不泄漏旧状态。尚未接入手机或其他设备的录音状态,详见 `recording-presence-handoff.md`。
11
+ - 免费版 5 分钟、VIP 1 小时、SVIP 2 小时,与 Flutter `AudioLongRecordingManager` 一致。会员信息无法确认时不录制;达到上限自动停止并保留待提交录音。
12
+ - 结束后自动提交到现有录音导入队列,后续上传、声纹识别、转写沿用现有 Audio 服务。接收成功不代表转写完成,可通过“查看处理进度”进入任务列表。
13
+
14
+ ## 数据与故障边界
15
+
16
+ - AudioWorklet 采集单声道 PCM;每秒保存到当前 origin 的 IndexedDB,结束时补齐最后不足一秒的数据,生成 PCM16 WAV。未使用不受现有导入合同支持的 WebM,也未把短背景音录音器的内存缓存当作长录音。
17
+ - 本机记录按环境和账号分区。录音开始不写入云端;结束提交使用实际用户 ID,禁止使用含糊的 `0` 归属。
18
+ - 上传失败保留本机记录,可重试上传或下载备份。确认插件主机已接收并持久保存上传任务后才删除浏览器副本。重试使用稳定的文件名和开始时间,复用导入队列的恢复/去重逻辑。
19
+ - 麦克风断开、处理器故障、系统中断或本地写入积压会停止录制;已有内容保留待用户处理。磁盘配额失败时暂存未写入的尾段,提示不要关闭页面,并提供下载备份。
20
+ - 同一 origin 的多个预览窗口通过 Web Locks 互斥,防止同时录制或提交另一窗口尚在写入的录音。
21
+ - 切换账号/环境或退出登录会停止旧录音并中止未完成上传;旧记录仍归旧账号,不能在新账号界面读取/提交。
22
+
23
+ ## Web 与原生录音的区别
24
+
25
+ - 仅在当前浏览器/预览 origin 保存。更换浏览器、清理网站数据或浏览器存储被回收会影响恢复;重要录音应下载备份。
26
+ - 录音期间申请屏幕唤醒锁(浏览器允许时),并设置关闭/刷新提醒;不保证系统休眠、浏览器崩溃或强制退出后继续录音。意外退出后恢复的是已提交的本地片段,最近不足一秒的尾段可能无法保留。
27
+ - 桌面本轮复用既有 WAV 导入链路,未改移动端、原生 Arkme 客户端或 Audio 后端。当前提交在导入任务中呈现,不冒充移动端原生录音会话来源。
28
+ - 固定预览端口为 3098,不更换 Keychain 服务或使用客户端的运行目录。
29
+
30
+ ## 验证
31
+
32
+ - `tests/direct-recording.test.ts`:权限/会员失败、单次启停、限时、本地恢复、上传失败重试、账号切换、尾段保留、WAV 格式及 AudioWorklet 分段。
33
+ - Chromium 隔离验证使用 `--use-fake-device-for-media-stream` 模拟音源;真实 AudioWorklet + IndexedDB + Web Locks,覆盖跨页面、双窗口、刷新、下载、权限拒绝和明暗/窄屏。
34
+ - 模拟音源及拦截上传不等于真实账号云端转写验收;实际账号上传、转写及长时间稳定性需要单独验收。
35
+
36
+ 本轮结果(2026-09-19):构建和类型检查通过;录音相关 661 项通过、1 项既有跳过。固定 3098 的真实登录页面已验证入口位置、真实会员状态读取、模拟麦克风录制、切换到 DSH 对话后操作浮动状态条、停止保存和失败后保留下载;测试音频上传全部拦截,没有写入用户的云端音频。扩大聊天回归 327 项通过,另外 2 项既有 `mention-read-status.tsx` 空上下文失败与修改前基线一致,未在本轮改动。
37
+
38
+ 后续导航状态验收(2026-09-19 09:35):新增导航状态、原有导航入口、录音和主题配色共 7 个测试文件 96 项通过;构建、类型检查和 diff 检查通过。固定 3098 已登录页面通过真实采集链路+模拟麦克风验证红点/文案、悬浮时长、明暗模式、导航尺寸不变、跨页面保持、键盘聚焦/Escape、停止后清除及上传失败本机保留。测试音频上传全部拦截,没有真实麦克风采集或云端音频写入。浏览器仍复现既有 DSH 页面切换的空 `defaultView.Event` 异常,已记录在桌面回归文档;不属于本次录音状态修改,未宣称全站无异常。
39
+
40
+ 呼吸动效验收(2026-09-19):8 个相关测试文件 101 项通过,类型检查、构建和 diff 检查通过。固定 3098 的已登录页面在明暗主题各采样 6 帧,确认五处可见视觉层按 2 秒周期变化、文字不动、导航尺寸不变;切换页面后的浮动提示同频,动态切换“减少动态效果”后正确静止/恢复同步,停止后全部动效移除。模拟音源和上传拦截边界不变,未采集真实麦克风或写入云端音频。既有 DSH 切页 `defaultView.Event` 异常仍存在,不属于本次动画验证通过的范围。
@@ -0,0 +1,36 @@
1
+ # 桌面会员入口与支付对接边界
2
+
3
+ ## 本轮范围
4
+
5
+ - 第一栏头像下方显示免费版 / VIP / SVIP;读取中、失败不会当作免费。
6
+ - 个人卡片显示会员等级、有效期、赠送 / 永久状态,进入桌面会员窗口。
7
+ - 窗口展示 VIP / SVIP 权益和实际在售套餐,支持选择与刷新状态。
8
+ - 按产品确认,支付按钮点击后仅提示「桌面端支付开发中」,**没有订单创建、扣款或模拟成功**。永久会员不展示重复开通。
9
+ - 会员与托管 AI 余额是不同业务;不得复用余额充值订单当作会员支付。
10
+
11
+ ## 已复用的合同
12
+
13
+ | 数据 | 当前后端接口 | 桌面入口 |
14
+ | --- | --- | --- |
15
+ | 当前账号会员 | POST /api/v1/premium/get/member | membership.current |
16
+ | 在售套餐 | POST /api/v1/payment/get/sale-product | membership.catalog |
17
+ | 完整权益 | 手机端同一 H5 /app/membership/rights | 窗口「查看完整权益对比」 |
18
+
19
+ 会员时间为 Unix 微秒;金额 money 为人民币分。赠送会员按当前权益显示;永久会员识别沿用 Flutter 约定。在售周期、金额及是否自动续费来自接口,不捏造终身套餐或促销。
20
+
21
+ 主要实现:`src/services/membership-service.ts`、`src/client/ArkmeMembershipDialog.tsx`、`src/client/arkme-membership.ts`。状态查询与背景音会员校验共用账号绑定的读取逻辑。所有查询携带预期账号并在返回时校验账号;切换账号丢弃旧数据。
22
+
23
+ ## 留给后续支付开发
24
+
25
+ 现有支付宝会员接口返回 TradeAppPay 参数,微信返回 App SDK 预支付参数;这些不是可直接用于桌面的支付链接或二维码。本轮不改后端、不接原生 SDK。
26
+
27
+ 需要后端与支付同事确认满足以下能力的最终方案,不限定新增接口名称或内部存储实现:
28
+
29
+ 1. 当前账号和实际可购 SKU 的桌面支付能力、金额、币种、周期及续费条款。桌面与渠道价格有区别时必须明确展示。
30
+ 2. 用户确认后才创建订单,幂等防止重复点击;返回可在桌面使用的扫码 / 网页支付动作及有效期。
31
+ 3. 可查询订单权威状态,覆盖处理中、成功、取消、过期、失败;不能仅凭关闭窗口或跳转成功判定付款成功。
32
+ 4. 支付成功后重新读取会员权益;有延迟时展示「权益更新中」,可重试,不重复扣款。
33
+ 5. 订单查询和支付结果按账号隔离;退出登录、切换账号、关闭窗口及断网后恢复均有明确行为。
34
+ 6. 续费 / 升级 / 已有订阅 / 赠送或永久会员的可购规则由业务确认;前端不自行推算抵扣或叠加。
35
+
36
+ 验收同时覆盖读数据与支付动作;可以将相同业务能力按权限提供给 DSH,但不把「DSH 可以查到」等同于支付流程验收。不得自动提交支付或泄露凭据。
@@ -0,0 +1,77 @@
1
+ # 桌面历史任务补测记录(2026-09-19)
2
+
3
+ ## 范围和环境
4
+
5
+ - 只在 GitHub 插件工作区 `arkme-dsh-plugin` 及 `/tmp/arkme-membership-0918-ru7UEP` 隔离预览工作;不修改已安装 Arkme 客户端。
6
+ - 预览固定 `http://127.0.0.1:3098/`,使用现有登录状态;浏览器测试使用独立临时上下文,不清理用户浏览器数据或钥匙串。
7
+ - 原有已修改文件和新文件已备份:`/tmp/arkme-regression-0919-c5m1BL/before-source.tar.gz`、`before.patch`。部署前隔离预览产物备份 `preview-lib-before.tar.gz`。
8
+ - 对真实账号做读取和界面操作;测试中的消息发送、已读上报、邀请、付款等写入拦截。未向真实联系人发送消息、未生成邀请、未创建订单或扣款。
9
+ - 测试日志和截图位于 `/tmp/arkme-regression-0919-c5m1BL`,部分复用脚本的截图位于对应历史测试目录。日志可能含账号内容,不提交到仓库。
10
+
11
+ ## 本轮已修复
12
+
13
+ ### 临时消息触发 @ 已读组件空值异常
14
+
15
+ 用户已确认修复。没有已读上下文时,旧表达式 `scope?.itemUid === itemUid` 会在临时消息标识也缺失时为真,随后访问 `scope.sourceRef` 抛异常,影响连续发送的后续草稿。
16
+
17
+ 现在先确认上下文、消息标识和来源有效,再比较身份;无有效上下文返回空已读集合。不改消息发送和已读判断口径。增加两项无上下文回归;已读组件和发送目录共 351 项通过。
18
+
19
+ ## 本轮真实登录浏览器补测
20
+
21
+ | 项目 | 本轮结果与证据 |
22
+ | --- | --- |
23
+ | 搜索备注名、昵称、错误恢复 | 搜索“周鹏”“狗才”均找到同一私聊;备注优先、去重、名称匹配可打开;模拟单会话失败后重试恢复。明暗均无列表溢出。`search-live.log` |
24
+ | 快速查找外部链接 | 首批 30 条、分页 60 条;明暗展示、链接跳转、回到来源通过。目标外站响应拦截,未访问实际外站。首次等待超时,独立重测和完整重跑成功,未稳定复现产品缺陷。`links-diagnostic.log`、`links-live-retry.log` |
25
+ | 快速查找长文 | 首批 9 条、分页 23 条;真实全文接口成功,明暗预览、Escape/关闭后保留列表位置通过。`long-article-live.log` |
26
+ | 添加已有长文 | 真实本人文章列表、分页、标题搜索、详情权限核验、选入待发送和移除通过;选择没有触发转发/发布。`live-suite.log` |
27
+ | 群成员侧栏 | “群成员”标题、拖宽、成员左侧悬浮菜单、移开关闭通过;添加/邀请弹窗暗色可读,邀请生成被拦截。`live-suite.log` |
28
+ | 会员 | 头像状态及个人卡片入口、真实权益/套餐读取、购买按钮显示“桌面端支付开发中”、窄窗和关闭通过;没有订单请求。`live-suite.log` |
29
+ | 声纹 | 真实声音/识别人/授权数据读取成功;标题、卡片和按钮明暗均可读。切换页面捕获到下述 DSH 生命周期异常,不能把这一项写成无异常验收。`voiceprint-live.log` |
30
+ | 输入框布局 | 发给自己/私聊/群聊,明暗及 1600/760/660 宽度共 12 组;统计在输入框上方,快捷键提示在发送左侧且仅聚焦显示,窄栏自动压缩。`composer-live.log` |
31
+ | 图片粘贴 | 合成图片暂存响应;暂存结束自动恢复原光标,无需再点击即可输入,原文保留。未上传真实图片。`paste-live.log` |
32
+ | 图文发送状态 | 在真实页面中注入测试任务:三类会话上传状态均位于气泡外下方、右对齐;模拟失败重试进入发送中。没有真实发送。`send-status-live.log` |
33
+ | DSH/发给自己悬浮 | 鼠标离开立即关闭;卡片到菜单的间隙可通行,菜单操作仍可用。两者本次测得关闭延迟 0ms。`hover-live.log` |
34
+ | @ 已读小勾 | 真实已读明细驱动,明暗均 11×11px,小勾身份正确,未做已读写入。`mention-live.log` |
35
+ | 通话详情 | 49 段真实转写,1600/1100 宽度和明暗:无昵称元信息行、时间右下角、没有正文重叠或溢出;可绑定身份使用备注名。`call-live.log` |
36
+ | 文件/语音排列 | 真实群消息在 1600/900 宽度和明暗:文件名一行且未截断;语音播放/时长在转写前、图片上方。`rich-live.log` |
37
+ | 日历、Markdown 文件 | 发给自己日历加载真实日期条数,暗色背景;真实 Markdown 预览标题、正文、表格及操作按钮明暗可读。`calendar-file-live.log` |
38
+ | 截屏入口 | 已登录账号下能力接口成功,`available: true`。这不是 macOS 框选实测。`screenshot-capability.log` |
39
+
40
+ 长文新建/发送、头像悬浮、详情紧凑标题、语音排序及账号切换等边界同时由既有组件/服务测试覆盖。不能把模拟网络测试当作真实发布、真实消息送达或多端同步验收。
41
+
42
+ ## 初次补测发现的回归项(后续状态见下)
43
+
44
+ 1. **DSH 页面卸载后的窗口事件。** 浏览器异常为 `ResizeObserver.position` 读取空 `defaultView.Event`。直接触发点是 `harness-surface-viewport.ts` 的布局回调;`harness-session-hover.ts` 同样存在对失效文档的强制访问。拟在页面失效/监听已释放后停止事件派发并清理引用,补卸载和迟到回调测试。已向用户提出确认,尚未修改。
45
+ 2. **七项测试与已确认的新行为不一致。** 四项仍寻找私聊/群聊“写长文”并期待直接创建;一项表情复制模拟对所有选择器都返回图片节点;一项发送源码断言误取新增长文分支的停止状态调用;一项旧品牌扫描未涵盖中文录音文件名。已在后续 master 整合中获确认并完成精确同步,没有删除测试或降低验证口径,最终结果见末尾。
46
+
47
+ ## 仍需外部支持或人工实测
48
+
49
+ - **群 @ 联动私聊列表摘要/红点:尚未实现。** 09:10 刷新 Chat 远程 master 仍为 `60336c85c8c7a4e4d0bdd3153525c0fef06aff76`,和前次核对一致。所缺权威目录摘要及原群已读同步合同详见 [对接需求](private-interaction-directory-handoff.md)。不能用历史互动数量冒充未读。
50
+ - **桌面支付:按用户确认仅保留占位提示。** 不是本轮遗漏;真实支付交同事对接,见 [会员对接说明](desktop-membership-handoff.md)。
51
+ - **macOS 截屏:** 系统权限、真实框选/取消仍需桌面实测;合成截图回传、焦点恢复、权限失败和超时由测试覆盖。
52
+ - **直接长录音:** 已有真实登录页面 + 模拟麦克风的开始/跨页/停止/本地恢复验证,上传全部拦截;真实麦克风音质、真实云端转写、多小时稳定性未验收,见 [录音说明](desktop-direct-recording.md)。
53
+ - **通话摘要未知说话人:** 前端只替换可可靠绑定的账号身份;同一端多人的 A/B 标签不能凭昵称猜测,需后端说话人映射或用户标注。
54
+ - **真实发送/邀请/支付和跨设备联动:** 本轮未做外部业务写入,不能声称端到端上线验收完成。
55
+
56
+ ## 自动化结果
57
+
58
+ - 第一轮全量:665 个文件;7,836 通过、12 失败、12 跳过。失败中两项来自本轮已修复的空值错误,三项来自缺少 zstd 的测试环境,另外七项为上述过时测试。
59
+ - 使用现有工具链 zstd 后,打包套件 6 项通过。
60
+ - 空值修复后已读/发送相关 351 项通过;类型检查、构建、`git diff --check` 通过。
61
+ - 最终全量复跑(09:13–09:16):665 个文件;**7,843 通过、7 失败、12 跳过**,无未处理异常。剩余四个测试文件的七项失败均为上述待同步项;不能把这一结果标为全绿。日志 `full-tests-final.log`。
62
+
63
+ ## 交付
64
+
65
+ 确认修复已构建并只更新固定 3098 隔离插件,源码和预览客户端产物 SHA-256 一致(`f7bea17a006b543e1aa63a3900abc43b86ca4f078aa17578c68aa01f369e31c4`)。更新后浏览器确认仍已登录,截屏能力接口正常。未提交、推送或发布;未更改已安装 Arkme 客户端。
66
+
67
+ ## 后续 master 整合与最终回归(18:45–18:55)
68
+
69
+ 以上浏览器记录为当天较早版本的验证,不等同本节整合版本的真实浏览器验收。
70
+
71
+ - 已基于最新 `master` 的 `12cee7a75cf5868894e92f259750efe6aaae3bb2`(v0.1.67)整合;保留上游收到长文的鉴权、Markdown/图片回退与本地自己的长文读取能力。
72
+ - 获确认后修复长文弹窗缺少上游三个消息字段的类型冲突,并同步长文流程、添加菜单、日历选中态、表情复制、发送状态及精确品牌白名单的旧测试。
73
+ - 回归继续发现真实问题:长文选择器离开会话仅隐藏,旧打开状态在返回后再次生效。用户确认后补齐两处清理,不清空待发送长文、文字或附件。
74
+ - 定向 6 文件、357 项通过;覆盖私聊/群聊、离开/切换、两种刷新顺序及子编辑器关闭、草稿保留;类型检查与完整构建通过。
75
+ - 最终全量:671 文件,663 通过、0 失败、8 跳过;**7,938 项通过、0 失败、12 跳过**(18:52:20 开始,151.72 秒)。原有跳过数量未增加,未删除失败用例。
76
+ - 本节没有更新或重启固定 3098 预览,未更换端口、迁移登录、访问钥匙串或修改已安装 Arkme 客户端。此时预览仍是之前部署版本。
77
+ - 既有 DSH 页面卸载生命周期问题未在本节修复,真实截屏授权、麦克风、多小时录音、真实发送及跨端状态等验收限制也仍有效。
@@ -0,0 +1,59 @@
1
+ # 对话输入框截屏(桌面原生与浏览器)
2
+
3
+ ## 范围
4
+
5
+ - 发给自己、私聊、群聊(含自己的主题)工具栏,表情右侧新增“截屏”。
6
+ - Arkme 桌面运行环境保留 macOS 原生区域框选;普通浏览器改为“浏览器选择屏幕/窗口 → 静态截图裁剪 → 加入草稿”。
7
+ - 不自动发送、不覆盖文字和既有附件,恢复原光标位置。
8
+ - Esc 取消静默处理。切换会话、退出输入场景、切换账号或关闭页面时取消,不向新会话添加截图。
9
+ - 无标注、马赛克、全局快捷键、自动隐藏应用;浏览器不提供跨桌面的原生框选覆盖层。Windows 原生截屏暂不支持,但支持选屏的浏览器可使用网页流程。
10
+ - 未改已安装的 Arkme 客户端、宿主原生代码或权限配置。macOS 屏幕录制授权由系统决定,不绕过授权。
11
+
12
+ ## 浏览器流程与边界
13
+
14
+ 1. 只有用户点击剪刀后才调用 `getDisplayMedia({ video: true, audio: false })`;同一点击调用栈内发起,不在前面等待宿主能力请求。
15
+ 2. 用户在浏览器中选择屏幕、窗口或标签页。得到第一帧后立即停止所有轨道,再显示裁剪弹窗;不会在编辑时持续共享,也不采集音频。
16
+ 3. 截图弹窗支持拖动选区(含反向拖动)、取消、使用整张、完成裁剪;同时适配明暗主题、窄窗口和键盘关闭。
17
+ 4. 只有确认后才复用附件暂存。截图不会自动发送,不替换原文字和已有附件,完成或取消后恢复原输入位置。
18
+ 5. 会话、账号、页面可见状态变化或组件卸载时取消;浏览器选屏窗口无法由网页强制关闭,但后续即使授权返回,也立即停止流,不添加到新会话。
19
+ 6. 等待选屏超过 120 秒取消本次操作,读取画面超过 10 秒释放共享;裁剪静态画面不设操作限时。
20
+ 7. 不安全地址、缺少选屏能力、拒绝授权、系统权限或读取失败均显示提示,并提供“兼容浏览器/系统截屏后粘贴”的替代路径。
21
+
22
+ 原实现只有本机 `desktop.screenshot.capture` 路径,并不能证明普通浏览器拥有原生框选能力。现在以现有 `arkmeDesktop.appVersion / harnessVersion` 桥接标识识别 Arkme 客户端;不把所有 Electron 内嵌浏览器都当成 Arkme 原生客户端。
23
+
24
+ 浏览器安全限制不能绕过:每次都需要用户主动操作并选择共享来源;某些内嵌浏览器即使暴露 API,也需要宿主实现选屏支持。参见 [W3C Screen Capture](https://www.w3.org/TR/screen-capture/) 和 [Electron desktopCapturer](https://www.electronjs.org/docs/latest/api/desktop-capturer)。本次不改 Arkme 或其他应用的宿主权限配置。
25
+
26
+ ## macOS 原生路径约束
27
+
28
+ 只在明确点击按钮后启动 `/usr/sbin/screencapture -i -s -x -t png`,不执行 shell、不接受调用方路径、不读取剪贴板。接口要求本机连接、当前同源页面和有效登录账号,截屏前后核对账号;不注册为 MCP 工具。默认 120 秒超时,仅允许一个截屏进程;页面取消和账号退出会终止进程。
29
+
30
+ PNG 先写入独立的私有临时目录,校验大小及签名后只返回图片内容,不返回本机路径。成功、取消、出错均清理该临时目录。图片加入草稿时沿用已有文件数量和大小限制,只有用户点发送才进入消息发送流程。
31
+
32
+ ## 原生路径历史验证记录(2026-09-19)
33
+
34
+ - 类型检查、插件构建通过;325 项相关接口/输入框测试、158 项服务及登录测试通过。
35
+ - 本机接口和截图控制器测试覆盖显式点击、重复点击、正常返回、Esc、权限错误、超时、断连、账号切换、PNG 校验和临时目录清理。
36
+ - 实际浏览器使用真实输入框与附件组件、合成截图响应验证:明暗主题、表情右侧按钮、光标位置恢复、保留文字、不自动发送、取消、错误提示、迟到结果丢弃,360/640 像素无横向溢出。
37
+ - 隔离 Web 预览固定为 `http://127.0.0.1:3098/`。未新增端口或钥匙串记录。
38
+ - 初次验证时隔离预览未登录。09-19 补测已使用现有登录,截屏能力接口成功且 `available: true`;macOS 系统权限和真实框选仍未端到端验收,浏览器模拟成功不能替代。
39
+ - 扩大回归发现两项已有发送测试在 `mention-read-status.tsx:12` 触发空值访问;09-19 集中回归经用户确认已修复并新增用例,相关 351 项通过。见 [集中回归记录](desktop-regression-2026-09-19.md)。
40
+
41
+ ## 浏览器路径验证记录(2026-09-19)
42
+
43
+ - 类型检查、插件构建通过,15 个测试文件共 357 项截图/输入框/附件相关回归通过。
44
+ - 使用已登录的隔离 3098 预览与真实输入框、裁剪和附件组件测试;选屏 API 返回合成画布流,附件暂存响应由测试拦截,不截取真实桌面,不发送消息。
45
+ - 已验证:发给自己/私聊/群聊确认后加图,1600×900 整图及缩放预览上的反向裁剪,抓帧后立即停流,取消、Esc、拒绝授权、待授权时切换会话与迟到结果停流。
46
+ - 原文字及既有附件保留;从文字中间打开截图后恢复到原光标位置。截图按钮鼠标按下不抢走编辑区选择,裁剪弹窗内部交互不会被误判为外部焦点变更。
47
+ - 明暗主题截图检查通过;360 像素视口下弹窗无横向溢出;浏览器未捕获到页面运行异常。
48
+ - 原生路径仅保留既有实现,未修改或重启已安装 Arkme 客户端。本次没有新开端口或迁移登录信息,只更新隔离预览的前端产物。
49
+ - **尚未验收:真实浏览器/内嵌宿主的系统选屏授权,以及 macOS 原生框选权限。** 合成流测试不能代替这些宿主能力;不支持时应按界面提示使用兼容浏览器或系统截屏后粘贴。
50
+
51
+ ## 真实环境验收
52
+
53
+ 1. 已登录 3098 后,分别进入发给自己、私聊、群聊;保留文字及一个已有附件。
54
+ 2. 将光标置于文字中间,点击表情右侧的剪刀。浏览器先选择共享来源,再在网页弹窗框选并确认;Arkme 桌面原生路径直接框选。确认新增缩略图、文字未改、光标恢复,消息未自动发送。
55
+ 3. 再次截屏按 Esc,确认原草稿不变。重复点击只能打开一个截屏流程。
56
+ 4. 拒绝系统屏幕录制权限时应出现明确提示,不生成空附件。允许权限后重试。
57
+ 5. 切换会话或账号后,迟到的图片不得出现在新会话;已完成的草稿附件仍由原有草稿规则保留。
58
+
59
+ 系统权限通常属于实际启动插件的运行环境;请以 macOS 权限面板显示的应用名称为准,不修改或清空其他客户端权限。
@@ -0,0 +1,95 @@
1
+ # 群聊 @ 联动私聊列表:能力需求与联调验收
2
+
3
+ 状态:产品交互已确认;本轮完成最新后端代码核对,尚未实现或部署本功能。本文不代表已上线,不替代现有私聊群聊日历合同。
4
+
5
+ ## 1. 要解决的问题
6
+
7
+ 双方在群聊中的 @ 互动已经显示在私聊内容区,但第二栏私聊卡片仍只消费直接消息。因此,没有直接消息的联系人卡片只有名称,新的群互动也不能在对应私聊卡片上提醒;在群里读过之后更没有对应的联动消除。
8
+
9
+ 目标不是在私聊中复制一条消息,而是让同一条群消息在联系人的列表卡片上有正确的展示和提醒。
10
+
11
+ ## 2. 已确认的产品规则
12
+
13
+ | 场景 | 私聊卡片表现 |
14
+ | --- | --- |
15
+ | 对方在群里 @ 我,原消息尚未读 | 显示群互动摘要、时间和未读提示 |
16
+ | 我在群里 @ 对方 | 更新摘要、时间和活动排序;我的发送不产生自己的未读提示 |
17
+ | 没有直接私聊消息,但存在双方群互动 | 仍显示互动摘要,例如“群名 · 对方:消息内容” |
18
+ | 较新的直接私聊消息到达 | 摘要切换到较新的直接消息 |
19
+ | 在群里读到原消息 | 对应互动提示消除;其他未读互动和直接私聊未读保留 |
20
+ | 有历史互动 | 补齐摘要;不能因首次加载、升级或重新登录而把历史全部标成新消息 |
21
+
22
+ 摘要和活动时间比较的是消息/互动发生的时间,而不是接口请求时间或缓存更新时间;原有置顶、隐藏、免打扰等策略继续生效。
23
+
24
+ 同一原群消息在群卡片和私聊卡片中出现,不能给全局未读总数额外增加一份。现有直接消息未读与新增互动提醒须能区分,不能把它们无条件相加后覆盖私聊游标。
25
+
26
+ 本轮要求“群里阅读 → 私聊互动提示消除”。不要求为了消除私聊提示而批量把整个群标记已读;单纯打开私聊也不能清掉尚未看到的群消息。
27
+
28
+ ## 3. 最新代码核对结果
29
+
30
+ 核对时间:2026-09-19(北京时间)。
31
+
32
+ - Chat:远程 `master`,`60336c85c8c7a4e4d0bdd3153525c0fef06aff76`,本地独立只读检出。没有修改或部署后端。
33
+ - World:已刷新 `origin/master`,`06f3b56`;本次对比确认互动模块相对本地旧检出无变化。
34
+ - 桌面插件:`src/services/source-service.ts` 的 root directory 投影只从当前会话 `latest_preview`、`unread_snapshot` 生成卡片;`ArkmeSidebar.tsx` 的互动请求在激活某个私聊后才运行,不提供目录级互动摘要。
35
+
36
+ | 已有能力 | 代码证据及限制 |
37
+ | --- | --- |
38
+ | 私聊/群聊目录、会话已读游标、@ 已确认状态 | Chat 已有 `/chats/list`、`/chats/cursor/update`、`/chats/mentions/*`;这些底层事实可复用,并非需要重建一套消息系统 |
39
+ | 私聊群互动日历统计、范围明细、定位 | 已在上述 Chat master 注册 `/chats/interwoven/statistics/query`、`items/query`、`items/around`;合同明确只覆盖 Chat-owned @ 关系,不覆盖旧 World 历史,合入不等于生产部署和联调完成 |
40
+ | Chat 旧互动 summary/bootstrap | `gin/api/chat_private_interwoven.go:147` 的 summary 直接将 `HasNew` 设为 `len(items)>0`,`NewCount` 设为条目数;不是未读口径,不能当红点 |
41
+ | World 互动 summary | `internal/interwovenmoments/service.go` 根据互动列表 last-viewed 时间判断更新;不等价于原群消息已读 |
42
+ | 当前用户的 @ 列表 | `gin/api/chat_mentions.go:158` 只返回有上限的首批条目,没有下一页游标;不能用这一批推断所有联系人的完整未读,也不含“我 @ 对方”的完整双向摘要 |
43
+ | 目录排序与展示 | `gin/api/chat_api_helpers.go:70` 的会话 bundle 没有按对端聚合的群互动状态;`resolveChatConversationSortActiveAt` 按该会话活动确定排序,没有将其他群中的双方 @ 纳入私聊列表排序 |
44
+
45
+ 结论:缺的不是群互动明细本身,而是**供会话列表使用的、可及时更新的双向互动摘要与单向未读投影**。现有零散接口足以做部分展示,但不能仅凭其首批结果承诺完整提醒和全目录排序。
46
+
47
+ ## 4. 后端需提供的能力(不指定路由、字段或内部实现)
48
+
49
+ 请后端评估复用现有 owner 查询、目录投影及失效通知的最佳组合。可以扩展列表,也可以增加有界批量读取;不要求新建集合、不强制做合并时间轴,不要求前端直接使用大量原子调用。
50
+
51
+ ### 列表摘要与发现
52
+
53
+ - 能确定当前用户与每个相关联系人的最新可见群 @ 互动,包括双向 @、原群名称、发送方向、摘要、发生时间和稳定来源身份。
54
+ - 与直接消息比较后获得列表所需的最新展示和活动排序依据。新互动发生在尚未加载的目录后续页时,也应能把该联系人正确带到前面,不能只重排客户端当前已加载的一页。
55
+ - 原本已有私聊入口但零直接消息的联系人必须支持。尚无私聊入口、已主动移除/隐藏入口是否重新出现,应沿用现有会话创建和隐藏策略;若要改变须另确认,不能由本功能擅自恢复。
56
+ - 旧 World 互动若暂不纳入权威摘要,需要说明范围和迁移/兼容安排;不能声称覆盖全部历史,也不能用旧 World 的“新互动数”补作未读数。
57
+
58
+ ### 互动未读与阅读联动
59
+
60
+ - 区分“对方 @ 我且尚未读”和“我发出的 @”;前者才是本轮新增提醒来源。
61
+ - 原群消息已读游标及已有 @ 确认事实应复用同一业务判断。能把已读变化关联到受影响的联系人,且不清掉无关互动。
62
+ - 支持同一账号另一设备阅读、重新登录和断线重连后的收敛。前端内存或 localStorage 不能成为唯一已读依据。
63
+ - 同一原群消息重复通知、重复拉取不能重复记数;消息编辑移除 @、删除/撤回、失去群权限或关闭互动能力后,摘要和提醒需要移除或回退到下一条有效内容。
64
+ - 保留已有私聊消息读游标含义。不能把群消息序号写进私聊游标,也不能为满足列表展示伪造私聊消息。
65
+
66
+ ### 更新、成本与安全
67
+
68
+ - 新消息、双方 @ 变化、原群已读变化及权限变化可触发相关目录项刷新;通知可只用于失效提示,展示内容由鉴权后的查询确定。
69
+ - 登录和打开列表不应逐个联系人扫描共同群历史;请求规模应有界,支持现有目录分页和新活动发现。由后端给出实际负载下的查询/刷新成本及建议调用频率,不在需求侧指定索引或存储结构。
70
+ - 不把依赖失败、部分覆盖或权限未知当作“没有互动/没有未读”;说明缓存可用范围和重试方式。账号切换清理缓存,迟到响应不得串号或覆盖较新的已读结果。
71
+ - 原群私密、隐藏、免打扰等策略与联系人列表策略由后端明确组合规则,客户端沿用统一判断,不绕过权限显示正文。
72
+
73
+ ## 5. 前端和 DSH 的分工
74
+
75
+ - 桌面/Flutter 消费同一业务口径,实现卡片摘要、红点和实时刷新;不要在两个端各自拼一套完整性不明的未读算法。
76
+ - DSH 可以同时使用原子查询和为列表场景优化的业务查询。工具应明确来源范围、摘要时间、未读含义和失败状态,沿用用户身份与权限。
77
+ - DSH 应能回答“我和某人的最新群互动是什么”“哪些群互动还没读”。通过对话得到正确答案是功能验收之一,但不能替代列表时延、分页排序、多端同步和去重验收。
78
+ - 统计、排序和去重应由确定性代码完成,不由模型从部分历史文本猜测。
79
+ - 后端给出实际合同后再接入;本轮不预写假接口、假字段或用 `has_new` 临时替代未读。
80
+
81
+ ## 6. 验收清单
82
+
83
+ 1. 零私聊消息的联系人有历史群互动:卡片能显示摘要,已读历史不因首次加载而变成未读。
84
+ 2. 我在群里 @ 对方:对方卡片活动更新,我的卡片没有新增未读。
85
+ 3. 对方在群里 @ 我:无需先打开私聊,列表即可显示摘要和提示。
86
+ 4. 读到该条原群消息:私聊中这条互动提示消失;较晚尚未读的 @ 与直接私聊未读不受影响。
87
+ 5. 两个群都有互动,只读其中一个:另一个的未读保留;同一群不同发送人的提示只按实际覆盖的阅读范围清除。
88
+ 6. 原群已读后又收到新的 @,同时有旧请求迟到:新提示不被旧响应覆盖,旧提示不会复活。
89
+ 7. 另一台设备阅读、断线重连、退出再登录:状态收敛,历史不重复提醒,全局未读不重复累计。
90
+ 8. 最新互动被删、@ 被移除或权限收回:卡片回退到下一个有效摘要,无敏感信息残留。
91
+ 9. 被 @ 联系人在目录后续页:新活动能被发现并正确排序,置顶和主动隐藏规则不变。
92
+ 10. 互动量超过现有首批上限:摘要、未读不因截断而错误;未知/部分覆盖有明确处理。
93
+ 11. DSH 与 UI 查询同一账号、同一联系人、同一状态,业务结果一致;分别验证权限和失败场景。
94
+
95
+ 后端回复请包含:可复用的现有能力、建议的最小扩展、实际对接合同与范围限制、通知/重连机制,以及可联调版本和测试环境。字段、路由、查询与缓存策略由后端结合实现评估,两端再共同确认。
@@ -0,0 +1,41 @@
1
+ # 录音日历与三层覆盖时间轴
2
+
3
+ 更新日期:2026-09-19。范围:桌面插件源码与独立 Web 预览,不修改手机端、后端或已安装 Arkme 客户端。
4
+
5
+ ## 呈现口径
6
+
7
+ - 日历去除外层卡片、内层网格的双重边框和星期分隔线,使用留白与柔和选中底色;日期、农历、时长及新录音提示保持。
8
+ - 时间轴底色表示无已知录音,淡蓝色覆盖表示实际音频范围,人声片段叠加在覆盖层上。详细轴保留不同说话人的颜色。
9
+ - 录音覆盖来自原始音频分片的起点、时长,不再由首尾转写推算;分片之间的真实空档不能补满。
10
+ - 云端覆盖必须有 `session.belong_usr === 当前登录用户 ID` 的明确归属。仅有上传账号 `user_id` 相同,或说话人识别为本人,均不能替代这个条件;`belong_usr=0`、归属他人不纳入,缺失归属标记为范围待确认。
11
+ - 时间轴人声层和说话人统计使用相同的录音归属范围。每日转写列表及其播放/编辑能力保持原有可访问范围,不删除或修改用户录音。日历月度总时长仍沿用已有后端统计口径,本轮不改统计接口。
12
+ - 08:00–20:00 的完整录音即使没有转写,也显示这 12 小时覆盖。没有转写不等于确认静音,界面使用“暂无已识别人声”。
13
+ - 读取失败或数据不完整时明确提示,不把未知区间断言为无录音。
14
+
15
+ ## 数据与兼容
16
+
17
+ `recordings.day` 新增可选 `coverage`。继续使用现有每日音频读取合同,无新增后端接口:
18
+
19
+ - `child_ls` 的 `start_at + duration` 提供物理音频区间,兼容历史相对 session 起点的偏移;按所选日期裁剪。
20
+ - 每日接口查询当前账号的 session 及其全部 children,不以 `has_asr` 为查询条件;插件再筛选明确归属于本人的 session。归属判断使用服务端认证用户,不接受前端传入任意用户 ID。
21
+ - 浏览器转写条目新增可选 `recordingBelongsToViewer` 标志,仅用于时间轴范围;它与已有的 `isSelf`(说话人身份)互不替代,不暴露源 session ID。
22
+ - 缺少 children 时,只有完成且时长严格等于起止跨度的连续 session 才可回退;其余标记范围待确认。
23
+ - “已识别人声”目前使用已有非背景音转写片段,不声称存在独立、完整的 VAD 人声结果。
24
+ - 手机长录音、手机 Web 控制的全天候录音及导入文件,只要已经形成云端音频分片,均走相同覆盖逻辑。
25
+ - 手机当前仍在录制、尚未同步的范围不能由桌面猜测;跨端实时录制状态仍依赖同事提供的统一状态合同。
26
+
27
+ ## 本机直接录音
28
+
29
+ - 用已采集 PCM 时长扩展覆盖,不把申请权限/启动中当作有录音。
30
+ - 覆盖按环境及账号隔离,并按日期裁剪;录制中右端点呼吸,保存/停止后不继续呼吸。
31
+ - 待上传保留本机覆盖;上传接受后保留不含音频的临时覆盖元数据,直到云端相同范围确认后移交。
32
+ - 多个相邻或重叠云端分片可联合确认完整范围,但不能跨越真实缺口。上传衔接期按现有退避刷新方式拉取每日数据,离开录音页停止轮询。
33
+ - 临时“已提交”元数据仅本次运行保留;已完成云端录音仍由每日数据恢复,不伪造持久跨端状态。
34
+
35
+ ## 验证记录
36
+
37
+ - 初版 9 个相关测试文件、167 项自动测试通过。补齐明确归属口径后,11 个相关测试文件、217 项自动测试通过;类型检查、构建、差异格式检查通过。
38
+ - 自动测试覆盖:完全无转写的 12 小时录音、分片空档、跨午夜、历史相对起点、识别中、错误/未知状态、账号隔离、本机录音与上传交接、覆盖缩放、停止呼吸及原有时间轴交互。
39
+ - 在固定、已登录的 `http://127.0.0.1:3098/` 实测:9 月 17 日有录音且有人声、9 月 18 日无录音、9 月 19 日有录音无转写。确认 30 分钟到 15 分钟缩放、明暗配色与页面无横向溢出。
40
+ - 真实界面验证未开启麦克风、未上传音频、未修改或删除用户录音。直接录音生命周期使用隔离测试数据验证,不等同于新增真人长录音端到端验收。
41
+ - 保持原深色偏好;预览不换端口、不重新读取钥匙串,不触碰正式客户端。
@@ -0,0 +1,155 @@
1
+ # 跨端录音状态同步:需求与接口交接
2
+
3
+ 更新:2026-09-19。本文是供后端、手机端、手机 Web 和桌面端共同对接的需求与接口草案,不代表接口已经实现或上线。
4
+
5
+ ## 一、核心需求:任何一端开始录音,其他端都能及时知道
6
+
7
+ 同一账号在任意一台设备上开启录音后,其他已登录的端无需手动刷新,就能看到:
8
+
9
+ 1. **是否正在录音**,而不是仅知道“录音开关已打开”或“有文件正在上传”。
10
+ 2. **哪台设备、哪个端正在录音**,例如“我的 iPhone · 手机端”或“MacBook · 桌面端”;多台设备同时录音时分别展示。
11
+ 3. **这次已经录了多久**,例如 `12:36`,显示期间持续走时。
12
+ 4. 录音停止、暂停或中断后,其他端同步更新,不能继续显示为正在录音。
13
+
14
+ 典型场景:在手机开始全天候录音 → 电脑第一栏出现录音中的呼吸提示 → 查看可见“我的 iPhone · 全天候录音中 · 12:36”;手机停止采集 → 电脑及时取消该设备的录音中提示。电脑开始录音、手机查看,同样成立。
15
+
16
+ ### 范围与显示口径
17
+
18
+ | 场景 | 要求 |
19
+ | --- | --- |
20
+ | 手机手动长录音 | 真实采集开始后同步,不等停止、不等音频上传 |
21
+ | 手机 Web 入口的全天候录音 | 必须覆盖;实际由手机原生运行时采集,由它上报,不把 Web 页面和手机算成两台录音设备 |
22
+ | 桌面直接录音、实际具备采集能力的 Web 端 | 使用同一套后端合同上报和接收状态 |
23
+ | 另一端新打开、重新登录或恢复前台 | 立即查询当前状态,不能只等下一次开始事件 |
24
+ | 页面切换、手机关闭录音控制网页 | 只要实际采集还在继续,录音状态就应继续;不能依赖某个页面一直打开 |
25
+ | 同账号多设备、多窗口或不同 Web 端口 | 都从同一账号的云端状态获取信息;没有采集的窗口只消费状态,不重复上报 |
26
+ | 静音录音 | 麦克风正在采集即属于录音,与是否检测到人声、是否完成转写无关 |
27
+
28
+ “立即”的建议验收目标:正常联网且接收端在线可运行时,从实际开始/停止到其他端更新 **不超过 2 秒**。这是待各端共同确认并实测的目标,不是现有能力承诺;不能只靠十几秒一次的轮询实现。
29
+
30
+ 断网、系统挂起或进程被终止时无法承诺即时同步,必须明确显示“状态未同步/状态待确认”,不能把失联直接当作停止,也不能永远保留确定正在录音的提示。
31
+
32
+ 本需求只同步录音状态元信息,不要求实时上传声音,不调整现有音频保存、上传或转写流程,也不包含远程启停、远程接管或全账号录音互斥。
33
+
34
+ ## 二、当前缺什么:后端需要提供统一的录音状态接口与通知
35
+
36
+ **现在桌面知道本窗口是否录音,但缺少供所有端共同上报、查询和订阅的完整后端合同。** 其他端不能仅靠本地红点、手机 Web 桥接或已上传音频列表知道谁正在录音。
37
+
38
+ | 必需能力 | 当前缺口 | 后端需交付的内容 |
39
+ | --- | --- | --- |
40
+ | 开始录音登记 | 缺少独立于音频上传会话的录音实例登记 | 登记账号下的采集设备、端类型、录音模式、开始时间与实例标识 |
41
+ | 持续录音与状态更新 | 现有短期保活没有完整的设备、状态和时长合同 | 心跳续期、累计采集时长更新、暂停/中断/恢复上报及失联判断 |
42
+ | 停止录音 | 缺少明确的结束与其他端清除链路 | 停止登记、最终时长、幂等重试,拒绝迟到消息重新激活已结束录音 |
43
+ | 当前录音列表查询 | 缺少同账号所有录音设备的统一快照 | 返回各设备状态、已录时长、更新时间、有效期与服务端时间 |
44
+ | 实时变更通知 | 缺少已确认的跨端开始/停止/中断/失联通知合同 | 按账号推送状态变化,支持断线后的状态校准 |
45
+
46
+ 已核验的 `/api/v1/audio/active-session` 只接收 `session_id`,按账号与 session 写入有效期 60 秒、值为 `1` 的缓存。它没有上述设备列表、时长、结束和实时通知合同,**不能直接作为完整实现交给各端接入**。可以由后端评估扩展复用,但必须补齐表中能力。
47
+
48
+ `get-session-ls`、`one-day-trans` 等已有接口服务于音频历史与转写,不能用“最近有上传”推断“当前还在录音”。录音时间轴的历史覆盖范围是另一类需求,也不能替代实时状态。
49
+
50
+ 以上缺口依据目前已核验的源码,非生产接口全量审计;如已有未纳入核验的服务,请后端提供合同后对照补齐。
51
+
52
+ ## 三、建议接口合同:各端按同一份定义实现
53
+
54
+ 以下路由和事件名称均为**建议名称,待后端确认,不是当前可调用接口**。后端可以合并写接口或复用已有实时通道,但需要完整保留这些能力,并交付正式请求、响应、错误码与通知定义。
55
+
56
+ ### 3.1 接口清单
57
+
58
+ 统一建议前缀:`/api/v1/audio/recording-presence`;所有接口必须鉴权。
59
+
60
+ | 建议接口/事件 | 何时调用 | 关键输入与结果 |
61
+ | --- | --- | --- |
62
+ | `POST /start` | 麦克风实际开始采集成功 | 上报录音 ID、采集设备/运行实例、端类型、模式、真实开始时间、累计时长、递增序号;返回服务端确认状态与有效期 |
63
+ | `POST /update` | 采集期间心跳,以及暂停、中断、恢复等变化时立即调用 | 上报录音 ID、序号、真实状态、累计采集时长;续期并返回确认状态 |
64
+ | `POST /stop` | 实际停止采集后立即调用,不等保存/上传完成 | 上报录音 ID、序号、最终采集时长、结束原因;确认结束并通知其他端 |
65
+ | `POST /list` | 登录、首次打开、恢复前台、重连或状态校准 | 不传任意用户 ID;查询当前登录账号的设备录音列表、服务端时间及快照版本 |
66
+ | `recording.presence.changed` | 开始、停止、状态变化或服务端判定失联 | 服务端主动通知该账号的其他端;携带变更条目、账号级版本及服务端时间 |
67
+
68
+ 写接口由真正采集音频的运行时调用;查询和订阅由所有需要展示状态的端接入。仅提供 HTTP 写接口而没有查询和通知,不能完成本需求。
69
+
70
+ ### 3.2 统一数据字段
71
+
72
+ | 字段 | 含义与约束 |
73
+ | --- | --- |
74
+ | `recording_id` | 客户端生成的单次录音唯一 ID;重试、暂停恢复和全天候自动切片不更换,明确停止后重新录音才更换;不依赖最终音频 session ID |
75
+ | `device_id` / `device_name` | 采集设备标识及可读名称,用来区分两台手机或两台电脑;设备归属由服务端校验,名称仅用于展示 |
76
+ | `runtime_id` | 本次采集运行实例标识,用于区分同设备多个运行环境;观看状态的页面不能冒充另一个采集实例 |
77
+ | `client_type` / `platform` | 如 `mobile`、`desktop`、`web` 与 `ios`、`android`、`macos`、`windows`;手机内嵌 Web 控制的录音仍归属手机 |
78
+ | `recording_mode` | `manual` 或 `all_day`,展示直接录音/全天候录音的区别 |
79
+ | `state` | `recording`、`paused`、`interrupted`、`error`、`stopped`;表示真实采集阶段,不是用户开关或上传阶段;不要求没有暂停能力的端新增暂停功能 |
80
+ | `elapsed_ms` | 采集运行时累计的有效录音时长;静音采集计入,暂停、中断、等待上传不计入 |
81
+ | `started_at` | 本次实际开始时间,UTC 毫秒;不是本次切片开始时间,也不能单独用于计算累计有效时长 |
82
+ | `seq` | 单次录音内递增的上报序号,重试沿用原序号,避免乱序覆盖新状态 |
83
+ | `revision` | 服务端按账号、环境维护的状态版本,用于快照与事件校准;与客户端 `seq` 区分 |
84
+ | `last_confirmed_at` / `expires_at` | 服务端最后确认时间与本条状态有效期,UTC 毫秒;由服务端计算,不由客户端任意延长 |
85
+ | `duration_anchor_at` | 服务端时间坐标上的时长参考点,与响应中的 `elapsed_ms` 配对,供其他端平滑走时 |
86
+ | `freshness` | `fresh` 或 `stale`;区分“确认在录音”和“上次在录音但现在失联”,不能把 stale 自动改成 stopped |
87
+ | `server_now` | 响应/事件生成时的服务端时间,UTC 毫秒,用于接收端校准本机时钟偏差 |
88
+
89
+ 账号从认证上下文取得,禁止信任任意 `user_id`。设备身份必须与该登录会话绑定;若现有认证不能区分采集设备,后端需一并补充设备登记/绑定约定,不能只信任前端填写的设备 ID。只有原采集设备/实例可以修改自己的录音记录。
90
+
91
+ `list` 返回当前未结束的录音,包括仍保留的失联条目;成功且为空才表示当前没有可展示条目。服务异常不能伪装成空列表。停止事件需带已结束条目,让接收端移除活动提示。失联条目的保留时限需写入正式合同。
92
+
93
+ ### 3.3 时长、心跳与事件规则
94
+
95
+ - 开始、停止、暂停、中断和恢复立即上报并推送,不能等下一个心跳。建议初始心跳间隔 10 秒、有效期 30 秒,最终以手机后台及桌面休眠实测共同确定;这两个间隔不是开始/停止通知的延迟。
96
+ - 服务端确认时把累计采集时长锚定到服务端时间,并返回参考点。其他端仅在 `recording + fresh` 时随时间递增,收到新样本后校正;暂停、中断或有效期届满即停止外推。
97
+ - 时长以采集运行时的单调计时累计值为准,不用“当前时间-开关开启时间”。网络传输会产生小幅展示误差,心跳用于校准;设备墙上时钟不准不能导致负数或异常跳时。
98
+ - 服务端负责续期和失效判定,并为过期状态提供查询结果与通知。接收端同时检查有效期,即使通知断开也不能无限显示“确认正在录音”。
99
+ - 开始和结束重试必须幂等;过时序号不得覆盖新状态。结束是终态,结束先到、开始后到或迟到心跳均不能复活同一录音 ID。
100
+ - 断网时本地继续录音并显示“状态未同步”;恢复后上报当前真实状态,不逐条重放过时的“正在录音”心跳。离线期间已结束的录音补报结束。
101
+ - 首次进入和重连采用“订阅 + 带版本快照/补偿”的一致性流程,避免查询与订阅之间漏事件。实时通道可复用现有账号通道;若不支持事件重放,发现版本缺口应重新查询快照。
102
+ - 实时通道不可用时可有界轮询兜底,但必须标明同步延迟,不能把降级轮询当作已经满足 2 秒目标。
103
+
104
+ ## 四、各端分别需要做什么
105
+
106
+ ```text
107
+ 手机手动录音 ──────┐
108
+ 手机 Web → 原生采集 ├─ 开始 / 更新 / 停止 → 后端账号录音状态
109
+ 桌面或 Web 实际采集 ┘ │
110
+ 列表查询 + 实时通知
111
+ ↓
112
+ 手机 / 桌面 / Web 统一展示
113
+ 哪台设备 · 录音状态 · 已录多久
114
+ ```
115
+
116
+ | 负责方 | 交付内容 |
117
+ | --- | --- |
118
+ | 后端 | 提供写入、查询、通知合同与可联调环境;实现账号/设备鉴权、版本与幂等、有效期、失联通知及服务端时间;明确错误码和重连同步方式 |
119
+ | 手机原生/Flutter | 手动长录音、全天候录音都从真实采集生命周期上报;后台录音时保持可靠状态续期,停止采集即报结束;同时接入其他设备状态的查询和展示 |
120
+ | 手机 Web | 全天候控制页继续通过原生桥接控制采集;不重复登记录音,不拿 `enabled=true` 当正在采集;涉及跨设备展示时消费同一后端合同,原生续期不能依赖 WebView 存活 |
121
+ | 桌面插件/Web | 本机采集状态即时展示并上报;订阅同账号其他设备状态,在全局录音入口显示活动提示,可查看设备、模式、时长;新窗口/新端口同样查询云端状态 |
122
+
123
+ 共同规则:本机采集事实不等后端成功才显示;自己的本机状态与云端回传按 `recording_id` 去重;多个录音实例分别展示。账号退出、切换或环境变化后取消旧订阅并清空旧账号状态,不提供跨设备远程停止按钮。
124
+
125
+ 手机全天候录音的分片、封段、合并、上传与下一段采集可能并行。**自动切片不等于用户停止录音,上传中也不等于正在录音。** 全程 ID、计时和续期应由手机采集运行时维护,不由音频上传队列或控制网页维护。
126
+
127
+ ## 五、联调验收与交付顺序
128
+
129
+ | 验收场景 | 通过标准 |
130
+ | --- | --- |
131
+ | 手机开始/停止,桌面已在线;反向再测 | 对端按约定时延更新,设备、端类型和时长正确;不等上传完成 |
132
+ | 手机 Web 开启全天候录音 | 桌面显示手机这一条录音;关闭 WebView、切片或封段不产生重复记录或错误停止 |
133
+ | 录音中另一个端登录/打开 | 快照立即给出已有设备状态及累计时长,不从零计时 |
134
+ | 两台设备同时录音,再停止一台 | 各自独立展示;另一台不被误清除 |
135
+ | 整段没有人声 | 仍显示录音中且时长增长,与转写结果无关 |
136
+ | 暂停、中断、恢复、权限拒绝或启动失败 | 与实际采集一致;非采集状态不显示录音呼吸,时长不误增 |
137
+ | 断网、系统挂起、进程退出或服务不可用 | 未获新确认时转为待确认;不得假报已停止或持续无限增长;本地音频保存不受影响 |
138
+ | 结束后到达旧心跳、重复请求、设备时间不准 | 不复活已结束录音、不重复登记,时长无异常跳变 |
139
+ | 账号切换或越权请求 | 不能查看或修改其他账号状态;不能由其他设备冒充原采集者写入 |
140
+
141
+ 建议交付顺序:**先确认统一接口合同 → 后端提供可联调环境 → 手机(含全天候)与桌面按合同接入 → 双向、多设备及断网场景验收。** 不能只做桌面红点后将整个需求视为完成。
142
+
143
+ 后端请优先反馈:上述能力是否复用已有接口;最终路由、字段、错误码与实时事件格式;设备身份绑定方式;心跳/失效/失联保留规则;是否可满足建议时延及联调环境。端侧据此实施,不自行猜测生产接口。
144
+
145
+ ## 附录:现有实现与核验依据
146
+
147
+ 以下是截至 2026-09-19 核验的源码情况,不代表手机安装包或生产部署已经具备相应能力。
148
+
149
+ - 后端 `jotmo-audio`,`origin/master: 5d2a14b`:`gin/api/audio.go` 中 `audioActiveSessionHandler` 仅写 60 秒 session/user 缓存;现有历史音频/转写接口不构成完整的跨端录音状态合同。
150
+ - 手机 `jotmo-frontend`,`origin/master: 32734114d`:`AudioLongRecordingManager` 先本地录音,远端音频 session 延后到停止后创建;因此录音状态 ID 必须独立于云端音频 session。
151
+ - 同版手机 `AudioAllDayRecordingRuntime` 负责全天候真实采集、约 2 分钟分片与最长 1 小时逻辑段轮换;桥接快照包括 `enabled`、`state`、`startedAt`、`elapsedMs` 等。iOS Live Activity 本机心跳不是云端状态心跳。
152
+ - 手机 Web `jotmo-frontend-web`,`origin/master: a53807c5`:`apps/jotmo-web-pages/src/app/app/audio/long-recording` 通过 `app.audio.allDayRecording.snapshot` / `setEnabled` 控制原生;收到 `web.audio.allDayRecording.state.changed` 后重新查询本机快照。页面目前有以 `enabled` 判断 recording 的路径,不能照搬为可信的跨端采集事实。
153
+ - 桌面 `DirectRecordingStore` 当前提供本窗口的真实录音状态、本地保存与停止后上传;第一栏提示尚不等于跨设备同步。Web Locks 只提供同源窗口约束,不承担跨设备状态分发。桌面“全天候录音”原入口是手机操作引导,不是远程采集开关。
154
+
155
+ 本次仅重写交接文档;手机、后端与跨端状态接入仍待同事按确认后的合同实现,不修改已安装的 Arkme 客户端。