@senguoyun/dsh-arkme 0.1.29 → 0.1.31

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 (133) hide show
  1. package/docs/consumer-plugin-contract.md +35 -1
  2. package/docs/file-flow-parity.md +213 -0
  3. package/docs/file-icon-licenses.md +54 -0
  4. package/docs/group-ai-polish-member-enablement.md +49 -0
  5. package/lib/client.js +8486 -1506
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +633 -28
  8. package/lib/persistent-extension.js +1 -1
  9. package/lib/{persistent-runtime-DLKV_LTp.js → persistent-runtime-BJ8konXI.js} +2319 -144
  10. package/lib/sdk.js +198 -0
  11. package/lib/types/arkme-service.d.ts +58 -1
  12. package/lib/types/arkme-service.d.ts.map +1 -1
  13. package/lib/types/chat-realtime.d.ts +7 -1
  14. package/lib/types/chat-realtime.d.ts.map +1 -1
  15. package/lib/types/client/ArkmeAttachmentStrip.d.ts +11 -0
  16. package/lib/types/client/ArkmeAttachmentStrip.d.ts.map +1 -0
  17. package/lib/types/client/ArkmeBillingSettings.d.ts.map +1 -1
  18. package/lib/types/client/ArkmeBotConversationSurface.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeFileIcon.d.ts +24 -0
  20. package/lib/types/client/ArkmeFileIcon.d.ts.map +1 -0
  21. package/lib/types/client/ArkmeFileQuickView.d.ts +6 -0
  22. package/lib/types/client/ArkmeFileQuickView.d.ts.map +1 -0
  23. package/lib/types/client/ArkmeFileViewer.d.ts +22 -0
  24. package/lib/types/client/ArkmeFileViewer.d.ts.map +1 -0
  25. package/lib/types/client/ArkmeFooterAction.d.ts +3 -1
  26. package/lib/types/client/ArkmeFooterAction.d.ts.map +1 -1
  27. package/lib/types/client/ArkmeFooterDropdown.d.ts.map +1 -1
  28. package/lib/types/client/ArkmeGroupChatControls.d.ts +3 -1
  29. package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
  30. package/lib/types/client/ArkmeNoteDetails.d.ts +3 -1
  31. package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
  32. package/lib/types/client/ArkmePersistentShell.d.ts +3 -0
  33. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  34. package/lib/types/client/ArkmeProductNavigation.d.ts +2 -1
  35. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  36. package/lib/types/client/ArkmeRichContent.d.ts +28 -4
  37. package/lib/types/client/ArkmeRichContent.d.ts.map +1 -1
  38. package/lib/types/client/ArkmeSearchSurface.d.ts.map +1 -1
  39. package/lib/types/client/ArkmeSettingsSurface.d.ts +5 -1
  40. package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
  41. package/lib/types/client/ArkmeSidebar.d.ts +15 -0
  42. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  43. package/lib/types/client/ArkmeUpdateSurfaces.d.ts +5 -39
  44. package/lib/types/client/ArkmeUpdateSurfaces.d.ts.map +1 -1
  45. package/lib/types/client/ArkmeVirtualWorkspace.d.ts +9 -2
  46. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  47. package/lib/types/client/ArkmeWebLoginOverlay.d.ts +6 -0
  48. package/lib/types/client/ArkmeWebLoginOverlay.d.ts.map +1 -0
  49. package/lib/types/client/DeepSeekHarnessSurface.d.ts +7 -4
  50. package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
  51. package/lib/types/client/api.d.ts +1 -1
  52. package/lib/types/client/api.d.ts.map +1 -1
  53. package/lib/types/client/arkme-auth-flow.d.ts +4 -0
  54. package/lib/types/client/arkme-auth-flow.d.ts.map +1 -1
  55. package/lib/types/client/composer-draft-store.d.ts +25 -2
  56. package/lib/types/client/composer-draft-store.d.ts.map +1 -1
  57. package/lib/types/client/file-send-tasks.d.ts +12 -0
  58. package/lib/types/client/file-send-tasks.d.ts.map +1 -0
  59. package/lib/types/client/index.d.ts +1 -0
  60. package/lib/types/client/index.d.ts.map +1 -1
  61. package/lib/types/client/plugin-update-store.d.ts +28 -1
  62. package/lib/types/client/plugin-update-store.d.ts.map +1 -1
  63. package/lib/types/client/read-retry.d.ts +7 -0
  64. package/lib/types/client/read-retry.d.ts.map +1 -0
  65. package/lib/types/client/ui-controller.d.ts +4 -0
  66. package/lib/types/client/ui-controller.d.ts.map +1 -1
  67. package/lib/types/client/update-presentation.d.ts +42 -0
  68. package/lib/types/client/update-presentation.d.ts.map +1 -0
  69. package/lib/types/extensions/manager.d.ts +4 -0
  70. package/lib/types/extensions/manager.d.ts.map +1 -1
  71. package/lib/types/extensions/persistent-bundle.d.ts +14 -0
  72. package/lib/types/extensions/persistent-bundle.d.ts.map +1 -1
  73. package/lib/types/extensions/persistent-client-bundle.d.ts +1 -1
  74. package/lib/types/extensions/persistent-client-bundle.d.ts.map +1 -1
  75. package/lib/types/extensions/persistent-runtime.d.ts.map +1 -1
  76. package/lib/types/extensions/profile-installer.d.ts.map +1 -1
  77. package/lib/types/file-transfer-contract.d.ts +63 -0
  78. package/lib/types/file-transfer-contract.d.ts.map +1 -0
  79. package/lib/types/file-transfer-owner.d.ts +17 -0
  80. package/lib/types/file-transfer-owner.d.ts.map +1 -0
  81. package/lib/types/harness-embed-contract.d.ts +2 -0
  82. package/lib/types/harness-embed-contract.d.ts.map +1 -0
  83. package/lib/types/harness-embed-route.d.ts +32 -0
  84. package/lib/types/harness-embed-route.d.ts.map +1 -0
  85. package/lib/types/host-api.d.ts.map +1 -1
  86. package/lib/types/index.d.ts.map +1 -1
  87. package/lib/types/plugin-update.d.ts.map +1 -1
  88. package/lib/types/rich-media-routes.d.ts +2 -1
  89. package/lib/types/rich-media-routes.d.ts.map +1 -1
  90. package/lib/types/runtime-instance.d.ts +8 -0
  91. package/lib/types/runtime-instance.d.ts.map +1 -0
  92. package/lib/types/sdk/index.d.ts +65 -2
  93. package/lib/types/sdk/index.d.ts.map +1 -1
  94. package/lib/types/services/bot-service.d.ts +15 -1
  95. package/lib/types/services/bot-service.d.ts.map +1 -1
  96. package/lib/types/services/chat-realtime-service.d.ts +1 -1
  97. package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
  98. package/lib/types/services/chat-service.d.ts +39 -4
  99. package/lib/types/services/chat-service.d.ts.map +1 -1
  100. package/lib/types/services/file-transfers.d.ts +65 -0
  101. package/lib/types/services/file-transfers.d.ts.map +1 -0
  102. package/lib/types/services/group-ai-polish-service.d.ts +17 -2
  103. package/lib/types/services/group-ai-polish-service.d.ts.map +1 -1
  104. package/lib/types/services/media-service.d.ts +11 -1
  105. package/lib/types/services/media-service.d.ts.map +1 -1
  106. package/lib/types/services/profile-service.d.ts +1 -0
  107. package/lib/types/services/profile-service.d.ts.map +1 -1
  108. package/lib/types/services/search-service.d.ts +6 -0
  109. package/lib/types/services/search-service.d.ts.map +1 -1
  110. package/lib/types/services/service.d.ts +1 -0
  111. package/lib/types/services/service.d.ts.map +1 -1
  112. package/lib/types/services/source-service.d.ts +2 -0
  113. package/lib/types/services/source-service.d.ts.map +1 -1
  114. package/lib/types/tools/business/conversation/copy-link-extend.d.ts +2 -0
  115. package/lib/types/tools/business/conversation/copy-link-extend.d.ts.map +1 -0
  116. package/lib/types/tools/business/conversation/group-ai-polish.d.ts.map +1 -1
  117. package/lib/types/tools/business/index.d.ts.map +1 -1
  118. package/lib/types/tools/business/media/files.d.ts +2 -0
  119. package/lib/types/tools/business/media/files.d.ts.map +1 -0
  120. package/lib/types/tools/business/media/index.d.ts.map +1 -1
  121. package/lib/types/tools/ports/bots.d.ts +1 -1
  122. package/lib/types/tools/ports/bots.d.ts.map +1 -1
  123. package/lib/types/tools/ports/conversations.d.ts +7 -1
  124. package/lib/types/tools/ports/conversations.d.ts.map +1 -1
  125. package/lib/types/tools/ports/files.d.ts +22 -0
  126. package/lib/types/tools/ports/files.d.ts.map +1 -0
  127. package/lib/types/tools/ports/index.d.ts +2 -1
  128. package/lib/types/tools/ports/index.d.ts.map +1 -1
  129. package/lib/types/tools/prompts/business.d.ts.map +1 -1
  130. package/lib/types/tools/registry/registrar.d.ts.map +1 -1
  131. package/lib/types/types.d.ts +124 -2
  132. package/lib/types/types.d.ts.map +1 -1
  133. package/package.json +5 -5
@@ -93,7 +93,41 @@ Plugin update discovery and acknowledgement are lifecycle concerns owned by the
93
93
 
94
94
  `capabilities().features.callHistory === true` advertises Browser-safe call-history access. `callHistory()` returns recent call records with opaque account-scoped `callRef` values, display metadata, result labels, summary status and summary previews. `callDetail(callRef)` accepts only an unchanged `callRef` from the Provider and returns safe metadata, participants, summary and transcript text. `retryCallSummary(callRef)` is a write-like operation and must only run from a current explicit human request to retry or regenerate that call summary. Consumers must never expect or reconstruct raw room IDs, WebRTC credentials, recording URLs, video URLs, signed media URLs or upstream tokens from this contract.
95
95
 
96
- `profile()` exposes only UI-safe fields: display name, nickname, avatar reference, Arkme ID, optional one-time Arkme ID change availability, account type, creation time, binding flags, and masked phone/email. Raw phone, raw email, real name, and credentials are intentionally excluded from contract v1. The model-facing `arkme_id_set` tool owns the one-time write workflow; the Browser SDK does not expose a profile mutation method.
96
+ `profile()` exposes only UI-safe fields: display name, nickname, avatar reference, Arkme ID, optional one-time Arkme ID change availability, account type, creation time, binding flags, optional third-party display names such as bound WeChat nickname, and masked phone/email. Raw phone, raw email, real name, OAuth code, and credentials are intentionally excluded from contract v1.
97
+
98
+ `capabilities().features.accountSettings === true` advertises the current-account settings migration from the Flutter client. Built-in UI shows the Arkme ID, generates the personal QR code from the same World share URL rule used by Flutter (`<shareWebsite>/<arkmeId>`), and can run the phone bind/rebind SMS flow through `auth.phone.send` and `auth.phone.verify`. `checkArkmeIdAvailability()` and `setArkmeIdOnce()` expose the same one-time Arkme ID owner used by the model-facing `arkme_id_set` tool; callers must obtain explicit human confirmation before `setArkmeIdOnce()`. `sendPhoneCode()` requires a Geetest captcha result from a current human browser action, and `verifyPhoneCode()` refreshes auth/profile state after success. WeChat binding remains a Flutter-native AppBridge OAuth flow: Flutter opens WeChat, receives an OAuth code, then calls `/api/v1/auth/wechat-bind`. The plugin contract currently exposes only WeChat binding status and safe nickname; Consumer SDKs and model Tools must not fabricate a WeChat bind/rebind flow without a Host-provided OAuth bridge.
99
+
100
+ Account settings capability matrix:
101
+
102
+ | Capability | UI | SDK | Tool | Host owner |
103
+ | --- | --- | --- | --- | --- |
104
+ | Show Arkme ID and bindings | `ArkmeSettingsSurface` account info rows | `profile()` | `arkme_user_profile` | `ProfileService.refreshProfileForSession()` |
105
+ | Personal QR and copy profile link | Built-in QR dialog | `profile()` + Consumer-generated URL from `shareWebsite` | N/A, display-only | Client config + safe profile projection |
106
+ | Check/set Arkme ID once | Built-in ID dialog | `checkArkmeIdAvailability()`, `setArkmeIdOnce()` | `arkme_id_set` with human confirmation | `ProfileService.checkArkmeIdAvailability()` / `setArkmeIdOnce()` |
107
+ | Phone bind/rebind | Built-in SMS dialog | `sendPhoneCode()`, `verifyPhoneCode()` | Blocked: human captcha and SMS code must not be model-driven | `AuthService.sendPhoneCode()` / `verifyPhoneCode()` |
108
+ | WeChat bind/rebind | Status only, explicit unavailable feedback | Blocked until a Host OAuth bridge exists | Blocked until a Host OAuth bridge exists | Flutter AppBridge only today |
109
+
110
+ UI structure:
111
+
112
+ ```text
113
+ 设置页
114
+ 账户概览
115
+ 头像 + 名称 + Arkme ID + Arkme ID 旁二维码图标入口
116
+ 账户
117
+ 账号信息
118
+ Arkme ID:当前 ID / 暂未获取到账号 ID -> 设置账号 ID 弹窗
119
+ 手机号:脱敏号码 / 未绑定 -> 绑定或更换手机号弹窗
120
+ 微信:绑定昵称 / 已绑定 / 未绑定 -> DSH OAuth 能力缺失反馈
121
+ ```
122
+
123
+ Interaction flow:
124
+
125
+ ```text
126
+ 点击 Arkme ID -> 本地格式校验 -> Host 可用性校验 -> 人类确认 -> Host 写入 -> 刷新 profile -> 关闭弹窗
127
+ 点击 Arkme ID 旁二维码图标 -> 用 shareWebsite + arkmeId 生成二维码 -> 打开二维码弹窗 -> 复制链接 -> 状态反馈
128
+ 点击手机号 -> 极验验证 -> 发送短信 -> 输入验证码 -> Host 验证绑定 -> 刷新 auth/profile -> 关闭弹窗
129
+ 点击微信 -> 显示 Flutter AppBridge OAuth 缺失说明,不发起伪请求
130
+ ```
97
131
 
98
132
  `readImage(avatarRef)` resolves an opaque image reference returned by `profile()` or `listSources()`. Private chats expose one optional `avatarRef`. Groups expose the preferred additive `groupAvatar` presentation plus legacy `avatarRefs`: `groupAvatar.slots` preserves the server-selected order for up to five members, including safe phone-default or generic fallbacks when a real image is absent, while legacy `avatarRefs` contains only resolvable real images. `memberCount`, `strategy`, and `computedAtMillis` describe the snapshot without exposing member or session identities. The Provider refreshes an authorized public profile image before downloading it and returns bounded PNG/JPEG/WebP/GIF base64 bytes; signed URLs, STS credentials and bearer tokens never enter the browser contract. Consumers must use `imageDataUrl()` (or decode the payload themselves) instead of concatenating OSS URLs or fetching an avatar reference directly.
99
133
 
@@ -0,0 +1,213 @@
1
+ # File lifecycle parity
2
+
3
+ Baseline: official master `3af3554949e8d47f2fcb8f3962e2da1aced0ba73`
4
+ (v0.1.28), rebased on 2026-08-27 before this correction.
5
+
6
+ Client reference (read-only): native frontend remote `pre-release`
7
+ `0fc133450`, inspected on 2026-08-27. Only the Arkme plugin is changed; no
8
+ client or DSH source is changed.
9
+
10
+ ## Product reference, not internal capability names
11
+
12
+ The client input toolbar has exactly two actions: 添加照片和文件 and 写长文
13
+ (`features/chat/presentation/input_toolbar.dart`, lines 714–731 at the reference).
14
+ Local file staging is an implementation detail, not a 本地附件 menu or library.
15
+ The client preview uses 接收文件 / 打开 in the file panel and a download icon at
16
+ the bottom; it does **not** have an 另存为 button. The icon invokes a destination
17
+ picker before receiving/copying bytes (`features/file/presentation/desktop/desktop_image_preview.dart`,
18
+ lines 999–1054, 1485–1554 and 2214–2247).
19
+
20
+ The bottom download icon is visible **before reception**, while receiving and
21
+ after reception, unless that page has already been saved successfully in the
22
+ current preview. This condition is independent of the original-file cache
23
+ (`desktop_image_preview.dart`, lines 99–112 and 2214–2247;
24
+ `test/features/file/desktop_image_preview_open_file_test.dart`, lines 357–379).
25
+ The center action changes from 接收文件 to percentage progress, then hands the
26
+ completed local original to the operating system and closes the preview. Opening
27
+ the same cached original later shows 打开. These statements describe the remote
28
+ desktop source, not a driven native-app acceptance test.
29
+
30
+ File icons use the user-selected **B: Untitled UI File Icons / Solid** set,
31
+ pinned to `@untitledui/file-icons@0.0.9`. All 12 SVGs retain upstream geometry
32
+ and default colors; see [asset provenance and license](file-icon-licenses.md).
33
+ This intentionally replaces the previous native-client assets. The shared icon
34
+ keeps MIME-first classification, adding a dedicated DMG icon; Markdown uses
35
+ the same set's code icon and unknown files its empty icon. Drafts use 32px,
36
+ message/search cards 40px, and the information panel 64px. The Solid preview
37
+ icon is intentionally smaller than the previous 120px native-style sizing,
38
+ following visual feedback; card/draft sizes and transfer actions are unchanged.
39
+ Assets are embedded
40
+ in the client bundle, with no external image requests or new dependency.
41
+ This is UI-only: Host, SDK and Tools are N/A for this icon change because no
42
+ business capability, transfer logic or button visibility rule changes.
43
+
44
+ | User step | Plugin behavior following the client |
45
+ | --- | --- |
46
+ | Add files | Existing picker, clipboard files or drop; prepare locally, spinner in add button |
47
+ | Pending attachments | Preview/remove and drag order; keyboard Alt+arrows without extra visible controls |
48
+ | Send | Accept a fixed message with attachments, clear that draft, allow the next input while uploading |
49
+ | Upload | Per-file overlay/progress; completing capped at 99%; failure removes a stale progress overlay |
50
+ | Retry | Keep the message identity and already uploaded siblings; never overwrite a newer draft |
51
+ | Open a remote generic file | 接收文件, shared percentage progress, then automatically open with the system default application |
52
+ | Open a cached generic file | Show 打开 and hand the account-bound local original to the system default application |
53
+ | Open browser media/text | Reuse original bytes; supported media and Markdown keep the existing inline renderer |
54
+ | Download | Client download icon; native browser picker first where supported, cancellation has no download side effect |
55
+ | Search | Existing file scene; reuse the same file card, receiver and viewer |
56
+
57
+ Image, video and generic-file presentation are separate after classification.
58
+ Real filename/MIME format wins over a stale upstream `file_kind=4`, so JPG,
59
+ MP4 and MP3 records do not flash or remain in a generic file card. Presentation
60
+ classification is separate from browser decoding: HEIC and SVG remain images,
61
+ and MKV remains video, even when the Web host cannot render their bytes inline.
62
+ Those formats use a media-shaped receive/download fallback instead of being
63
+ relabeled as files. Legacy visual metadata is still rejected when the real
64
+ filename has a non-media suffix, preventing old `.dmg` records from being
65
+ decoded as images. PDF, Office documents, archives, installers and unknown
66
+ types remain generic files. Recorded voice messages continue to use the audio
67
+ flow.
68
+
69
+ If an image or transient video resource fails to load, the message keeps an
70
+ image- or video-shaped failure tile with a retry action. It does not temporarily
71
+ replace that media with a generic file card. Retrying uses a fresh URL, and a
72
+ newer record version clears stale failure state. A browser-declared unsupported
73
+ image or video format instead offers 接收图片 or 接收视频 and opens the existing
74
+ receive/download flow; it does not promise that retrying the same bytes will work. This prevents
75
+ the observed `image -> file text/card -> image` flash while preserving explicit
76
+ files.
77
+
78
+ The retry/status UI is a plugin recovery adaptation, not a claim that every
79
+ client surface has an identical retry control. Unknown server acknowledgement
80
+ is reconciled against recent messages and is never blindly resent with new IDs.
81
+
82
+ ## Contract before implementation
83
+
84
+ Select/import prepares account-bound local files only. Accepting a send persists
85
+ the message and its local references before releasing the composer. Cloud upload
86
+ progress belongs to message attachments; uploaded bytes are not a sent message.
87
+ Retries retain successful assets and the original record/relation identifiers.
88
+ An uncertain message acknowledgement must not be silently retried with new IDs.
89
+
90
+ Drafts, messages and file search share a file viewer. Original-file reception is
91
+ distinct from browser download/save; a thumbnail is never an original fallback.
92
+ File search uses existing scene 4. Existing upload()/sendRich() consumers retain
93
+ their synchronous completion contract.
94
+
95
+ | Consumer | Required implementation | Verification |
96
+ | --- | --- | --- |
97
+ | Host | Single account-bound file owner: local staging, MIME normalization, durable send state, upload progress, original reception and validated local open | Owner, account-isolation, API-proxy and route failure/recovery tests |
98
+ | UI | Local attachment strip, preview/reorder/remove, optimistic files, per-file progress, retry; distinct visual/file rendering; generic receive/open state machine | Interactive and rendering tests, actual Web acceptance |
99
+ | SDK | Public typed stage/send/status/retry/receive plus `openLocalFile(fileRef)`; paths never cross the API | External consumer compile and contract tests |
100
+ | Tools | Existing `arkme_file_task` adds confirmed `open-local` for opaque current-account file refs; no arbitrary filesystem read | Formal catalog/grant registration and official DSH session invocation |
101
+
102
+ DSH public seams: WebServer.register() owns HTTP response lifecycle and returns a
103
+ disposer. Existing Arkme HTTP adapters remain responsible for Origin/auth checks.
104
+ The Host resolves an opaque Arkme file ref and calls the public `ctx.apiProxy.host.openPath`
105
+ gateway, which owns Finder / Explorer / xdg-open handoff. Before that handoff,
106
+ the owner creates an account-scoped hard-link alias with a sanitized original
107
+ file name and extension. This lets the OS recognize PDF, ZIP, Office and other
108
+ generic formats while the canonical cache remains opaque. Both paths stay
109
+ Host-side and aliases are removed with their cached originals. DSH attachment v1 supports images only, not generic files. No private
110
+ DSH imports, custom shell-open commands or new remote upload/search services are introduced.
111
+
112
+ ## Acceptance
113
+
114
+ - Selection performs no cloud upload; importing failure preserves valid siblings.
115
+ - Local acceptance releases input; each file has actual upload progress capped
116
+ below completion until complete-upload succeeds.
117
+ - Partial upload failure retains completed assets; message failure never replaces
118
+ a newer draft; source/account changes cannot rebind a pending send.
119
+ - Same record ID is idempotent; uncertain acknowledgement is shown explicitly.
120
+ - Original reception is shared, validates length, and never promotes partial data.
121
+ - Cache/staging references remain account-bound and do not expose paths or URLs.
122
+ - Generic files show 接收文件, real percentage progress, automatically open on
123
+ successful reception, and an already received file card opens the account-local
124
+ original directly without showing a second confirmation dialog.
125
+ - When an authoritative remote message replaces its local pending send row, the
126
+ current-account task rebinds each matching `fileAssetUid` to its opaque local
127
+ file ref. A file selected on this device therefore never regresses to 未下载.
128
+ - Browser download fallback reports only handoff, never unverified disk-save success.
129
+ - With a supported save picker, success is reported only after the writable file closes.
130
+ - Feature remains plugin-only; running user profiles are not replaced.
131
+
132
+ ## Verification and remaining boundaries
133
+
134
+ Typecheck, the complete media/file interaction matrix and the production build
135
+ passed on macOS. The full suite passed 1917 product tests and skipped 5; its
136
+ remaining package-list harness uses an npm flag that npm 11 no longer parses,
137
+ including the B / Solid icon replacement. File icon tests pin all 12 SVG hashes,
138
+ check inactive/self-contained assets, retain MIME precedence and verify the
139
+ shared draft/card/preview mapping, including DMG. No new Host, SDK or Tool
140
+ behavior is introduced by this visual correction.
141
+
142
+ The file viewer uses a 64px icon and places its close control inside the
143
+ top-right corner, with a 32px hit target and 12px inset. Information and content
144
+ views both reserve space above their content. Click and Escape dismissal remain
145
+ covered; the latter stops propagation so a parent detail view stays open.
146
+
147
+ The Web adaptation shows actual reception percentages on the reference client's
148
+ 220px-wide, 4px-high rounded track, with no invented percentage when total bytes
149
+ are unknown. A received browser-previewable image, video, audio or supported text
150
+ file offers Preview. A stale generic-file marker cannot override a real media
151
+ format. PDF, XML, Office documents, installers, archives and unknown formats use
152
+ the native generic-file flow: 接收文件, percentage progress, automatic system open,
153
+ then 打开 for the cached original. PDF is never placed in a blank browser iframe.
154
+ Clicking an already received generic-file card bypasses the information panel and
155
+ hands the cached original directly to the native opener. The panel remains the
156
+ receive/progress/retry surface for files that are missing, receiving or failed.
157
+ The bottom download control remains a separate optional disk-copy action and does
158
+ not replace the central receive/open state. No native-application limitation text
159
+ or central save-success state is shown.
160
+
161
+ The mounted message view now retains its last usable attachment display when
162
+ the Host explicitly reports a media lookup failure for the same record version.
163
+ It keeps the existing failure notice and replaces references on recovery. It
164
+ does not retain media across source/record changes, unknown/newer versions,
165
+ deleted records, or a successful response that removes attachments. The
166
+ regression sequence (complete, unavailable, recovered) formerly rendered
167
+ attachment counts `1 -> 0 -> 1`; it now renders `1 -> 1 -> 1`. This reproduces
168
+ one deterministic failure path, not every possible live intermittent symptom.
169
+
170
+ Native copy remains blocked: the desktop reference's `_copyImage` first calls
171
+ `Pasteboard.writeFiles`, with an image fallback (lines 956–997). It copies a
172
+ file, not its name or a URL. DSH rc.7 exposes `writeClipboard(text: string)`;
173
+ the inspected browser bridges cover app updates/notifications/calls, not native
174
+ file clipboard writes. Standard browser clipboard formats are not an
175
+ equivalent OS file-list contract. No shell-on-Host workaround, misleading
176
+ copy-name fallback, or nonfunctional copy button is added. A client-side native
177
+ file-clipboard bridge with explicit permission is needed for full parity.
178
+ Owner/SDK route tests cover account isolation, Origin, range reads, local staging,
179
+ durable identity, partial upload failure, unknown acknowledgement, cache reuse,
180
+ truncated reception, validated native opening, save cancellation and failed disk writes. UI tests also
181
+ cover original-menu-only, drag order, shared card reception, formatted Markdown
182
+ and the absence of invented file-library/Save-As controls.
183
+
184
+ An isolated official DSH rc.7 installation loaded the immutable package on a
185
+ fresh Profile. Its Host reported `canOpenPath=true`; the real Web conversation
186
+ rendered a remote PDF with 接收文件 as the central action and the separate download
187
+ icon. The file Tool owner/action and conversational confirmation are covered by
188
+ registration and dispatch tests; no automated acceptance opened a user file.
189
+ A separate external consumer compiled against the packaged public SDK and its
190
+ exported `ArkmeFileOpenResult`. These checks do not prove authenticated upstream
191
+ upload/send behavior. The interactive fixture uses real plugin components and
192
+ file owner, but simulated upstream upload/send ports; it never sends to a real
193
+ user's conversation.
194
+
195
+ Still **not full native-client parity**:
196
+
197
+ - Native opening is available only when this DSH Host reports its official native
198
+ opener capability. The plugin uses that Host seam and never invents a remote
199
+ browser-open claim. Native file clipboard copy remains unavailable: the public
200
+ clipboard primitive accepts text, not an OS file list, so no misleading copy
201
+ fallback is added.
202
+ - Browser save dialogs, clipboard file availability, video codecs and PDF viewer
203
+ support depend on the browser. Native OS dialogs and Windows/Linux behavior
204
+ have not been validated. The current receive/preview presentation is not a
205
+ pixel-identical client clone.
206
+ - File size remains bounded by Host configuration (default 100 MiB); image limit
207
+ is at most 50 MiB and attachment limit is 9. Do not promise the client's 200 MiB
208
+ non-image limit or dynamic VIP quota without an authoritative capability.
209
+ - Real-account upload, private/group/self/topic delivery, weak-network behavior
210
+ and the full native client reference flow still require end-to-end acceptance.
211
+ - Native client code was inspected at the remote reference; the Flutter client
212
+ itself was not driven through this flow. Passing plugin tests is not proof
213
+ that the two products are completely aligned.
@@ -0,0 +1,54 @@
1
+ # Third-party file icon attribution
2
+
3
+ The bundled file icons use **Untitled UI File Icons, Solid**, version 0.0.9.
4
+
5
+ - Official project: https://www.untitledui.com/react/resources/file-icons
6
+ - Package: https://www.npmjs.com/package/@untitledui/file-icons/v/0.0.9
7
+ - Upstream author, as declared in package metadata: Dilshod Turobov.
8
+ - Upstream declared license: MIT. The published package does not include a
9
+ separate LICENSE file or dated copyright notice; the attribution above
10
+ preserves its supplied author information. The MIT terms are reproduced below.
11
+ - Archive: https://registry.npmjs.org/@untitledui/file-icons/-/file-icons-0.0.9.tgz
12
+ - Verified archive SHA-512 (base64):
13
+ `wz3btNnSSv2hTujgyxEFL21oyjPtGTj3osU8ZEMe8nwdlmLQEILLe96NMg9b1ciiOC5UOWNDeYIt7IfxEM6qtg==`
14
+
15
+ Static SVGs were extracted from the package's `dist/solid/*.mjs` element trees,
16
+ using default size 40 and theme light without executing upstream modules.
17
+ Only serialization changed; paths, fills and proportions are unchanged.
18
+ The UI scales them to 32/40/64px. No remote resources or active SVG content are
19
+ used. This attribution is shipped in the plugin package alongside the bundle.
20
+
21
+ | Local asset suffix | Upstream Solid module |
22
+ | --- | --- |
23
+ | audio | audio.mjs |
24
+ | csv | csv.mjs |
25
+ | default | empty.mjs |
26
+ | dmg | dmg.mjs |
27
+ | excel | xls.mjs |
28
+ | md | code.mjs |
29
+ | pdf | pdf.mjs |
30
+ | ppt | ppt.mjs |
31
+ | txt | txt.mjs |
32
+ | video | video.mjs |
33
+ | word | doc.mjs |
34
+ | zip | zip.mjs |
35
+
36
+ ## MIT License
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining a copy
39
+ of this software and associated documentation files (the "Software"), to deal
40
+ in the Software without restriction, including without limitation the rights
41
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42
+ copies of the Software, and to permit persons to whom the Software is
43
+ furnished to do so, subject to the following conditions:
44
+
45
+ The above copyright notice and this permission notice shall be included in
46
+ all copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
54
+ THE SOFTWARE.
@@ -0,0 +1,49 @@
1
+ # Group AI polish management
2
+
3
+ Group AI polish is a shared group capability. Any active member may manage it when the chat service returns `can_manage: true`; clients must not infer permission from owner/admin roles or require a group-member-list request.
4
+
5
+ ## Capability matrix
6
+
7
+ | Surface | Contract |
8
+ | --- | --- |
9
+ | Tools | `arkme_group_ai_polish_manage` resolves an exact group name, reads status, previews a generated or saved rule, and performs an enable/disable write only after explicit confirmation. |
10
+ | SDK | External plugins discover `features.groupAiPolish` and use public methods to query settings, generate or select a rule, and prepare/confirm enable or disable operations. |
11
+ | UI | The group header shows the active state and rule. The existing three-dot menu opens a centered rules dialog and conversational rule editor. |
12
+ | Host owner | `GroupAiPolishService` owns permission checks, rule selection, confirmation expiry, concurrency control, persistence, cache invalidation, and fresh readback verification for every adapter. |
13
+
14
+ ## Permission and confirmation contract
15
+
16
+ - The server-provided `can_manage` value is authoritative for every read-modify-write flow.
17
+ - Permission is checked again immediately before a confirmed write. A revoked membership or permission cannot reuse an earlier preview.
18
+ - Confirmation references are opaque, scoped to the current account, action, and group, and expire after ten minutes.
19
+ - A natural-language requirement always generates a preview. It must not silently select an existing rule.
20
+ - Enabling a saved rule does not regenerate or upsert it. When selection is ambiguous, the caller must request an exact rule name or opaque rule reference.
21
+ - Editing an existing rule records its version at preview time and rejects a concurrent edit instead of overwriting it.
22
+ - Saving and enabling are treated as a recoverable two-step operation. If saving succeeds but enabling or verification fails, a retry reuses the saved rule rather than creating a duplicate.
23
+ - A positive write response is not sufficient. The Host invalidates cached settings and verifies the enabled state, active rule, and approved rule content with a fresh read.
24
+ - Server denial is reported as a permission failure, never as an inability to fetch group members.
25
+
26
+ ## Desktop interaction
27
+
28
+ - The enabled state and active rule are visible beneath the group title.
29
+ - The only settings entry is `AI 表达润色` inside the existing group three-dot menu.
30
+ - The centered dialog supports selecting a saved rule, disabling polish, and opening a conversational editor for a new or existing rule.
31
+ - Loading, retryable failure, read-only permission, confirmation, success, and partial-write recovery remain scoped to the selected group.
32
+ - The editor persists mobile-compatible `rule_thread_messages`, so mobile and desktop can continue the same rule conversation.
33
+ - Settings changes replace only the selected group's AI-polish snapshot; they do not reload the conversation timeline.
34
+
35
+ ## Conversation rendering reliability
36
+
37
+ - A confirmed send replaces only its optimistic message row and writes the same result into the selected conversation cache before the conversation summary advances.
38
+ - Realtime deltas merge into the latest rendered timeline instead of replacing restored history with the delta alone.
39
+ - Identical in-flight timeline reads are shared. Switching sources or requesting a different page still cancels obsolete work.
40
+ - Expected request cancellation is silent. Transient idempotent reads use bounded retries and persistent failures settle into an actionable error state.
41
+ - Outgoing message rows retain their existing right-aligned container while text wrapping remains inside the bubble.
42
+
43
+ ## Verification requirements
44
+
45
+ - Cover ordinary-member success, server denial, revoked permission, ambiguous selection, stale preview, retry after partial success, and fresh readback mismatch at the Host owner.
46
+ - Cover Tool discovery, grant, schema, preview/confirmation behavior, and the absence of member-list lookup.
47
+ - Compile and exercise the public SDK consumer contract without private imports.
48
+ - Cover menu, dialog, editor, row-only send reconciliation, retained timeline history, request single-flight, retry, and error settlement in UI tests.
49
+ - Before delivery, run related tests, typecheck, the complete test suite, production build, immutable `.tgz` inspection, and installation into an unmodified official DSH with a fresh temporary Profile.