@ljwei-stak/dsh-model-route-bot 0.66.4 → 0.67.0

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 (271) hide show
  1. package/.dsh-plugin/bot-product.mjs +81 -5
  2. package/.dsh-plugin/client.js +17490 -1791
  3. package/README.i18n.yaml +4 -4
  4. package/README.md +409 -364
  5. package/README.zh.md +409 -364
  6. package/apps/integration-client/README.md +31 -7
  7. package/apps/integration-client/api.mjs +14 -3
  8. package/apps/integration-client/binding-editor.jsx +308 -109
  9. package/apps/integration-client/binding-model.mjs +262 -0
  10. package/apps/integration-client/binding.css +144 -0
  11. package/apps/integration-client/bot-model-status.mjs +18 -0
  12. package/apps/integration-client/bot-profile-identity.jsx +197 -0
  13. package/apps/integration-client/bot-profile.css +57 -0
  14. package/apps/integration-client/bot-profile.mjs +97 -0
  15. package/apps/integration-client/bot-settings-dialog.css +18 -0
  16. package/apps/integration-client/bot-settings-dialog.jsx +111 -0
  17. package/apps/integration-client/bot-thread-header-bridge.css +20 -0
  18. package/apps/integration-client/bot-thread-header.jsx +278 -0
  19. package/apps/integration-client/bot-threads-panel.jsx +378 -0
  20. package/apps/integration-client/bot-threads.css +169 -0
  21. package/apps/integration-client/chat-design.mjs +250 -0
  22. package/apps/integration-client/chat-ui.jsx +175 -0
  23. package/apps/integration-client/chat.css +477 -0
  24. package/apps/integration-client/composer-draft-binding.mjs +50 -0
  25. package/apps/integration-client/composer-draft-bound.jsx +14 -0
  26. package/apps/integration-client/composer-draft-host.jsx +142 -0
  27. package/apps/integration-client/composer-draft-session.jsx +287 -0
  28. package/apps/integration-client/composer-draft-witness.mjs +115 -0
  29. package/apps/integration-client/composer-draft.css +4 -0
  30. package/apps/integration-client/composer-draft.mjs +322 -0
  31. package/apps/integration-client/contact-attention.jsx +54 -0
  32. package/apps/integration-client/data-panel.jsx +439 -0
  33. package/apps/integration-client/data.css +69 -0
  34. package/apps/integration-client/desktop-bridge.mjs +1 -0
  35. package/apps/integration-client/desktop-http-socket.mjs +49 -0
  36. package/apps/integration-client/desktop-keyboard.mjs +32 -0
  37. package/apps/integration-client/desktop-pane.jsx +99 -30
  38. package/apps/integration-client/desktop-stream.mjs +13 -3
  39. package/apps/integration-client/desktop-tools.jsx +19 -0
  40. package/apps/integration-client/desktop-touch.mjs +27 -0
  41. package/apps/integration-client/desktop.css +41 -0
  42. package/apps/integration-client/independent-chat-controller.mjs +224 -0
  43. package/apps/integration-client/independent-chat-entry.jsx +61 -0
  44. package/apps/integration-client/independent-chat-navigation.mjs +21 -0
  45. package/apps/integration-client/independent-chat-page.jsx +148 -0
  46. package/apps/integration-client/independent-chat-rich.jsx +64 -0
  47. package/apps/integration-client/independent-chat-rich.mjs +30 -0
  48. package/apps/integration-client/independent-chat.css +73 -0
  49. package/apps/integration-client/index.jsx +24 -16
  50. package/apps/integration-client/library-panel.jsx +4 -1
  51. package/apps/integration-client/licenses/jsqr-1.4.0-LICENSE.txt +202 -0
  52. package/apps/integration-client/main-route-editor.jsx +94 -0
  53. package/apps/integration-client/main-route-model.mjs +100 -0
  54. package/apps/integration-client/model-picker.css +37 -0
  55. package/apps/integration-client/model-picker.jsx +83 -0
  56. package/apps/integration-client/native-activity.css +141 -0
  57. package/apps/integration-client/native-activity.jsx +276 -0
  58. package/apps/integration-client/native-activity.mjs +384 -0
  59. package/apps/integration-client/notifications-panel.jsx +186 -0
  60. package/apps/integration-client/notifications.css +126 -0
  61. package/apps/integration-client/pairing-qr.jsx +79 -0
  62. package/apps/integration-client/panels.jsx +126 -115
  63. package/apps/integration-client/product-slots.jsx +46 -0
  64. package/apps/integration-client/routine-recorder.mjs +23 -2
  65. package/apps/integration-client/secure-pairing.jsx +84 -0
  66. package/apps/integration-client/tasks-panel.jsx +32 -10
  67. package/apps/integration-client/ui-icons.jsx +17 -0
  68. package/apps/integration-client/usage-explorer.jsx +43 -0
  69. package/apps/integration-client/usage-report.mjs +71 -0
  70. package/apps/integration-client/usage.css +34 -0
  71. package/apps/integration-client/web-panel-model.mjs +140 -0
  72. package/apps/integration-client/web-panel-page.jsx +221 -0
  73. package/apps/integration-client/web-panel-pins-live.jsx +28 -0
  74. package/apps/integration-client/web-panel.css +139 -0
  75. package/apps/integration-client/workspace-model.mjs +67 -0
  76. package/apps/integration-client/workspace-navigation.mjs +56 -0
  77. package/apps/integration-client/workspace-ui.jsx +173 -0
  78. package/apps/integration-client/workspace.css +332 -0
  79. package/modules/bot-runtime/SOURCE.json +176 -50
  80. package/modules/bot-runtime/index.mjs +169 -13
  81. package/modules/bot-runtime/product-overlay/OVERLAY.json +128 -2
  82. package/modules/bot-runtime/product-overlay/base/src/client/header.js +244 -0
  83. package/modules/bot-runtime/product-overlay/base/src/client/learning-card.js +202 -0
  84. package/modules/bot-runtime/product-overlay/base/src/client/model-menu.js +371 -0
  85. package/modules/bot-runtime/product-overlay/base/src/client/secret-card.js +363 -0
  86. package/modules/bot-runtime/product-overlay/base/src/client/sources.js +68 -0
  87. package/modules/bot-runtime/product-overlay/base/src/client/turn-error.js +260 -0
  88. package/modules/bot-runtime/product-overlay/base/src/host/channel.js +647 -0
  89. package/modules/bot-runtime/product-overlay/base/src/host/memory.js +2050 -0
  90. package/modules/bot-runtime/product-overlay/base/src/host/models.js +314 -0
  91. package/modules/bot-runtime/product-overlay/base/src/host/parts.js +1473 -0
  92. package/modules/bot-runtime/product-overlay/base/src/host/privacy.js +368 -0
  93. package/modules/bot-runtime/product-overlay/base/src/host/roster.js +776 -0
  94. package/modules/bot-runtime/product-overlay/base/src/host/store.js +512 -0
  95. package/modules/bot-runtime/product-overlay/base/src/host/usage.js +653 -0
  96. package/modules/bot-runtime/product-overlay/patches/src/client/details-drawer.js.patch +48 -2
  97. package/modules/bot-runtime/product-overlay/patches/src/client/header.js.patch +41 -0
  98. package/modules/bot-runtime/product-overlay/patches/src/client/index.js.patch +33 -1
  99. package/modules/bot-runtime/product-overlay/patches/src/client/learning-card.js.patch +20 -0
  100. package/modules/bot-runtime/product-overlay/patches/src/client/model-menu.js.patch +24 -0
  101. package/modules/bot-runtime/product-overlay/patches/src/client/secret-card.js.patch +25 -0
  102. package/modules/bot-runtime/product-overlay/patches/src/client/sources.js.patch +52 -0
  103. package/modules/bot-runtime/product-overlay/patches/src/client/turn-error.js.patch +23 -0
  104. package/modules/bot-runtime/product-overlay/patches/src/host/channel.js.patch +46 -0
  105. package/modules/bot-runtime/product-overlay/patches/src/host/memory.js.patch +11 -0
  106. package/modules/bot-runtime/product-overlay/patches/src/host/models.js.patch +16 -0
  107. package/modules/bot-runtime/product-overlay/patches/src/host/parts.js.patch +95 -0
  108. package/modules/bot-runtime/product-overlay/patches/src/host/privacy.js.patch +11 -0
  109. package/modules/bot-runtime/product-overlay/patches/src/host/roster.js.patch +145 -0
  110. package/modules/bot-runtime/product-overlay/patches/src/host/store.js.patch +68 -0
  111. package/modules/bot-runtime/product-overlay/patches/src/host/usage.js.patch +11 -0
  112. package/modules/bot-runtime/read-model.mjs +19 -4
  113. package/modules/bot-runtime/vendor/ds-bot/src/client/details-drawer.js +15 -5
  114. package/modules/bot-runtime/vendor/ds-bot/src/client/header.js +9 -2
  115. package/modules/bot-runtime/vendor/ds-bot/src/client/index.js +9 -2
  116. package/modules/bot-runtime/vendor/ds-bot/src/client/learning-card.js +2 -2
  117. package/modules/bot-runtime/vendor/ds-bot/src/client/model-menu.js +5 -3
  118. package/modules/bot-runtime/vendor/ds-bot/src/client/secret-card.js +3 -3
  119. package/modules/bot-runtime/vendor/ds-bot/src/client/sources.js +25 -2
  120. package/modules/bot-runtime/vendor/ds-bot/src/client/turn-error.js +3 -2
  121. package/modules/bot-runtime/vendor/ds-bot/src/host/channel.js +14 -2
  122. package/modules/bot-runtime/vendor/ds-bot/src/host/memory.js +1 -1
  123. package/modules/bot-runtime/vendor/ds-bot/src/host/models.js +7 -0
  124. package/modules/bot-runtime/vendor/ds-bot/src/host/parts.js +26 -9
  125. package/modules/bot-runtime/vendor/ds-bot/src/host/privacy.js +1 -1
  126. package/modules/bot-runtime/vendor/ds-bot/src/host/roster.js +112 -3
  127. package/modules/bot-runtime/vendor/ds-bot/src/host/store.js +34 -1
  128. package/modules/bot-runtime/vendor/ds-bot/src/host/usage.js +1 -1
  129. package/modules/independent-runtime/index.mjs +152 -62
  130. package/modules/independent-runtime/remote-tools.mjs +8 -5
  131. package/modules/openmaus-protocol/NOTICE +10 -0
  132. package/modules/openmaus-protocol/README.md +15 -0
  133. package/modules/openmaus-protocol/SOURCE.json +68 -0
  134. package/modules/openmaus-protocol/dist/protocol.mjs +1203 -0
  135. package/modules/openmaus-protocol/index.mjs +1 -0
  136. package/modules/openmaus-protocol/transport.mjs +77 -0
  137. package/modules/openmaus-protocol/vendor/LICENSE +202 -0
  138. package/modules/openmaus-protocol/vendor/companion/src/e2e/README.md +405 -0
  139. package/modules/openmaus-protocol/vendor/companion/src/e2e/credentials.ts +128 -0
  140. package/modules/openmaus-protocol/vendor/companion/src/e2e/descriptor.ts +84 -0
  141. package/modules/openmaus-protocol/vendor/companion/src/e2e/noise.ts +456 -0
  142. package/modules/openmaus-protocol/vendor/companion/src/e2e/phone.ts +104 -0
  143. package/modules/openmaus-protocol/vendor/companion/src/e2e/session.ts +417 -0
  144. package/modules/openmaus-protocol/vendor/companion/src/e2e/vectors/v1.json +2565 -0
  145. package/modules/openmaus-protocol/vendor/companion/src/e2e/vectors/v1.sha256 +1 -0
  146. package/modules/openmaus-protocol/vendor/companion/src/e2e/yamux.ts +335 -0
  147. package/package.json +18 -6
  148. package/packages/bot-acp/README.md +156 -0
  149. package/packages/bot-acp/licenses/LICENSE-OpenMausBot +202 -0
  150. package/packages/bot-acp/licenses/NOTICE.md +27 -0
  151. package/packages/bot-acp/src/acp-adapter.mjs +795 -0
  152. package/packages/bot-acp/src/acp-host.mjs +148 -0
  153. package/packages/bot-acp/src/native-launch.mjs +121 -0
  154. package/packages/bot-acp/src/permission-policy.mjs +283 -0
  155. package/packages/bot-acp/src/rpc-codec.mjs +265 -0
  156. package/packages/bot-acp/src/session-core.mjs +581 -0
  157. package/packages/bot-acp/src/stdio-rpc.mjs +329 -0
  158. package/packages/bot-engines/README.md +281 -0
  159. package/packages/bot-engines/licenses/LICENSE-OpenMausBot +202 -0
  160. package/packages/bot-engines/licenses/NOTICE.md +32 -0
  161. package/packages/bot-engines/src/claude-engine.mjs +772 -0
  162. package/packages/bot-engines/src/claude-llm-adapter.mjs +797 -0
  163. package/packages/bot-engines/src/claude-llm-turn.mjs +292 -0
  164. package/packages/bot-engines/src/claude-permission-broker.mjs +130 -0
  165. package/packages/bot-engines/src/claude-permission-proxy.mjs +87 -0
  166. package/packages/bot-engines/src/claude-protocol.mjs +177 -0
  167. package/packages/bot-engines/src/driver-catalog.mjs +334 -0
  168. package/packages/bot-engines/src/host-loop-request.mjs +95 -0
  169. package/packages/bot-engines/src/host-registrations.mjs +105 -0
  170. package/packages/capability-broker/src/cli-bootstrap.mjs +86 -16
  171. package/packages/capability-broker/src/cli-bridge-response.mjs +80 -0
  172. package/packages/capability-broker/src/cli-stdio-bridge.mjs +21 -8
  173. package/packages/cli-runtime/src/codex-session-core.mjs +4 -0
  174. package/packages/cli-runtime/src/native-cli-output.mjs +57 -0
  175. package/packages/cli-runtime/src/native-peer.ps1 +51 -3
  176. package/packages/cli-runtime/src/native-process.mjs +77 -14
  177. package/packages/model-router-plugin/package.json +1 -1
  178. package/packages/orchestration/src/binding-product.mjs +19 -0
  179. package/packages/orchestration/src/bot-data-actions.mjs +45 -0
  180. package/packages/orchestration/src/bot-data-chart.mjs +109 -0
  181. package/packages/orchestration/src/bot-data-context.mjs +44 -0
  182. package/packages/orchestration/src/bot-data-engine.mjs +809 -0
  183. package/packages/orchestration/src/bot-data-io.mjs +186 -0
  184. package/packages/orchestration/src/bot-data-sql.mjs +195 -0
  185. package/packages/orchestration/src/bot-data-surface.mjs +144 -0
  186. package/packages/orchestration/src/bot-data-tools.mjs +68 -0
  187. package/packages/orchestration/src/bot-data-worker.mjs +228 -0
  188. package/packages/orchestration/src/bot-independent-execution.mjs +195 -0
  189. package/packages/orchestration/src/bot-independent-stream.mjs +74 -0
  190. package/packages/orchestration/src/bot-loop.mjs +152 -19
  191. package/packages/orchestration/src/bot-notifications.mjs +159 -0
  192. package/packages/orchestration/src/bot-policy.mjs +43 -4
  193. package/packages/orchestration/src/bot-profile.mjs +171 -0
  194. package/packages/orchestration/src/bot-routine.mjs +4 -4
  195. package/packages/orchestration/src/bot-runtime-assembly.mjs +255 -8
  196. package/packages/orchestration/src/bot-runtime-switch.mjs +13 -2
  197. package/packages/orchestration/src/bot-serving.mjs +164 -0
  198. package/packages/orchestration/src/bot-session-store.mjs +105 -9
  199. package/packages/orchestration/src/bot-settings.mjs +50 -11
  200. package/packages/orchestration/src/bot-thread-admission.mjs +84 -0
  201. package/packages/orchestration/src/bot-thread-host.mjs +187 -0
  202. package/packages/orchestration/src/bot-threads.mjs +790 -0
  203. package/packages/orchestration/src/data-product-access.mjs +37 -0
  204. package/packages/orchestration/src/data-tool-contract.mjs +291 -0
  205. package/packages/orchestration/src/desktop-http-carrier.mjs +120 -0
  206. package/packages/orchestration/src/desktop-proxy.mjs +311 -0
  207. package/packages/orchestration/src/desktop-sessions.mjs +43 -9
  208. package/packages/orchestration/src/ds-lifecycle.mjs +31 -4
  209. package/packages/orchestration/src/grant-refresh-queue.mjs +17 -0
  210. package/packages/orchestration/src/independent-chat-page.mjs +39 -0
  211. package/packages/orchestration/src/independent-chat-service.mjs +308 -0
  212. package/packages/orchestration/src/independent-context-state.mjs +44 -0
  213. package/packages/orchestration/src/independent-message-wire.mjs +84 -0
  214. package/packages/orchestration/src/independent-tool-catalog.mjs +175 -0
  215. package/packages/orchestration/src/independent-tool-families.mjs +73 -0
  216. package/packages/orchestration/src/independent-tool-host.mjs +344 -0
  217. package/packages/orchestration/src/main-bot-grant.mjs +15 -0
  218. package/packages/orchestration/src/main-model-routes.mjs +208 -0
  219. package/packages/orchestration/src/model-accounting.mjs +99 -35
  220. package/packages/orchestration/src/model-auxiliary-dispatch.mjs +187 -0
  221. package/packages/orchestration/src/model-loop-frame.mjs +179 -0
  222. package/packages/orchestration/src/model-prepared-generation.mjs +175 -0
  223. package/packages/orchestration/src/model-request-bounds.mjs +111 -0
  224. package/packages/orchestration/src/native-activity-contract.mjs +491 -0
  225. package/packages/orchestration/src/native-activity-service.mjs +102 -0
  226. package/packages/orchestration/src/native-activity-store.mjs +249 -0
  227. package/packages/orchestration/src/native-conversation-tools.mjs +78 -0
  228. package/packages/orchestration/src/native-data-tools.mjs +100 -0
  229. package/packages/orchestration/src/native-remote-actions.mjs +742 -0
  230. package/packages/orchestration/src/native-team-tools.mjs +73 -0
  231. package/packages/orchestration/src/pairing-invite.mjs +37 -0
  232. package/packages/orchestration/src/product-composition.mjs +1559 -1001
  233. package/packages/orchestration/src/remote-machines.mjs +82 -14
  234. package/packages/orchestration/src/routine-execution.mjs +5 -3
  235. package/packages/orchestration/src/routine-session.mjs +19 -13
  236. package/packages/orchestration/src/secure-pairing-host.mjs +112 -0
  237. package/packages/orchestration/src/task-journal.mjs +29 -0
  238. package/packages/orchestration/src/task-product.mjs +17 -0
  239. package/packages/orchestration/src/team-assignment-store.mjs +120 -0
  240. package/packages/orchestration/src/team-bridge.mjs +282 -0
  241. package/packages/orchestration/src/team-scheduler.mjs +83 -0
  242. package/packages/orchestration/src/team-sdk-tools.mjs +84 -0
  243. package/packages/orchestration/src/team-tool-contract.mjs +284 -0
  244. package/packages/orchestration/src/ui-host-namespace.mjs +23 -0
  245. package/packages/orchestration/src/web-panel-actions.mjs +103 -0
  246. package/packages/orchestration/src/web-panel-framing.mjs +227 -0
  247. package/packages/orchestration/src/web-panel-store.mjs +206 -0
  248. package/remote-computer-agent/README.md +39 -29
  249. package/remote-computer-agent/build.mjs +3 -0
  250. package/remote-computer-agent/dist/OPENMAUSBOT_LICENSE +202 -0
  251. package/remote-computer-agent/dist/OPENMAUSBOT_NOTICE +10 -0
  252. package/remote-computer-agent/dist/OPENMAUSBOT_SOURCE.json +68 -0
  253. package/remote-computer-agent/dist/cli-stdio-bridge.mjs +215 -25
  254. package/remote-computer-agent/dist/cli-stdio-bridge.mjs.map +4 -4
  255. package/remote-computer-agent/dist/cli.mjs +1688 -168
  256. package/remote-computer-agent/dist/cli.mjs.map +4 -4
  257. package/remote-computer-agent/dist/index.mjs +1579 -122
  258. package/remote-computer-agent/dist/index.mjs.map +4 -4
  259. package/remote-computer-agent/dist/native-peer.ps1 +51 -3
  260. package/remote-computer-agent/package-lock.json +308 -2
  261. package/remote-computer-agent/package.json +2 -1
  262. package/remote-computer-agent/pair-device.ps1 +20 -0
  263. package/remote-computer-agent/setup.ps1 +38 -16
  264. package/remote-computer-agent/src/cli.ts +24 -3
  265. package/remote-computer-agent/src/desktop-session.ts +11 -3
  266. package/remote-computer-agent/src/e2e-server.ts +81 -0
  267. package/remote-computer-agent/src/pairing-page.ts +35 -0
  268. package/remote-computer-agent/src/protocol.ts +25 -25
  269. package/remote-computer-agent/src/secure-pairing.ts +105 -0
  270. package/remote-computer-agent/src/store.ts +20 -5
  271. package/remote-computer-agent/src/transport.ts +39 -15
@@ -0,0 +1,260 @@
1
+ import { createElement as h, useEffect, useMemo, useState, useSyncExternalStore } from 'react'
2
+ import { currentPart, isHiddenTurn } from './text.js'
3
+ import { CheckIcon, CopyIcon } from './icons.js'
4
+ import { ModelKeyCard, manageModels } from './model-menu.js'
5
+ import { knownFailure } from './diagnostics.js'
6
+ import { hostText, t } from './i18n.js'
7
+
8
+ // -------------------------------------------------------------------------
9
+ // A failed turn in a Bot's chat: what went wrong and what the user can do about it,
10
+ // in place of DSH's row. The copy and the fixes go by DSH's stable failure code, never
11
+ // by the provider's message, which shows only under Technical details. The chat node
12
+ // carries no HTTP status or request id; the host keeps them, with the failing route's
13
+ // provider, on the Bot as `blocked` while a credential or balance failure stands.
14
+
15
+ const OFFICIAL = 'deepseek-official'
16
+ const ACCOUNT = 'deepseek-account'
17
+ const TOP_UP_URL = 'https://platform.deepseek.com/top_up'
18
+ // How long a sign-in started here waits for the account's models before the user picks.
19
+ const ACCOUNT_MODELS_WAIT_MS = 20_000
20
+ const SIGNED_OUT = new Set(['ACCOUNT_SIGN_IN_REQUIRED', 'ACCOUNT_SIGNED_OUT', 'ACCOUNT_TOKEN_INVALID'])
21
+ // Failures of an API key route; the account route never has them.
22
+ const KEY_CODES = new Set(['MISSING_CREDENTIAL', 'INVALID_CREDENTIAL', 'AUTH'])
23
+ // Until the credentials change the host keeps these on the Bot, and a retry would fail
24
+ // the same way, so Retry waits for the fix.
25
+ const CREDENTIAL_CODES = new Set([...KEY_CODES, ...SIGNED_OUT])
26
+
27
+ // The sentence for one failure. `status` is the HTTP status, when the host has it.
28
+ export function failureText(code, { name, provider, status }) {
29
+ switch (code) {
30
+ case 'MISSING_CREDENTIAL': return t("{name}'s model has no API key yet.", { name })
31
+ case 'INVALID_CREDENTIAL': return t('This API key is not in the right format.')
32
+ case 'AUTH': return status === undefined
33
+ ? t('{provider} did not accept the current API key.', { provider })
34
+ : t('{provider} did not accept the current API key ({status}).', { provider, status })
35
+ case 'QUOTA': return status === undefined
36
+ ? t('{provider} is out of balance or quota.', { provider })
37
+ : t('{provider} is out of balance or quota ({status}).', { provider, status })
38
+ case 'ACCOUNT_QUOTA': return t('The DeepSeek account balance is too low.')
39
+ case 'ACCOUNT_SIGN_IN_REQUIRED':
40
+ case 'ACCOUNT_SIGNED_OUT':
41
+ case 'ACCOUNT_TOKEN_INVALID': return t('The DeepSeek account sign-in has expired.')
42
+ case 'TRANSPORT':
43
+ case 'TIMEOUT': return t('Could not reach the model service.')
44
+ case 'RATE_LIMIT': return t('Too many requests. Wait a moment, then try again.')
45
+ case 'SERVER': return t('The model service failed.')
46
+ case 'INTERRUPTED': return t('DeepSeek Harness stopped before this turn finished.')
47
+ default: return knownFailure(code) ?? t('This turn failed.')
48
+ }
49
+ }
50
+
51
+ // The fixes one failure offers, the one to try first leading. Each is offered only
52
+ // where this window can carry it out. A Bot moved onto the account route since a key
53
+ // failure has nothing left to fix there.
54
+ export function fixesFor(code, { canSignIn, official, topUp, onAccount = false }) {
55
+ if (onAccount && KEY_CODES.has(code)) return []
56
+ if (SIGNED_OUT.has(code)) return canSignIn ? ['sign-in-again'] : ['model']
57
+ switch (code) {
58
+ case 'MISSING_CREDENTIAL': return ['key', canSignIn ? 'sign-in' : null, 'model'].filter(Boolean)
59
+ case 'INVALID_CREDENTIAL': return ['rekey', 'model']
60
+ case 'AUTH': return ['rekey', canSignIn ? 'use-account' : null, 'model'].filter(Boolean)
61
+ case 'QUOTA': return official ? ['top-up'] : []
62
+ case 'ACCOUNT_QUOTA': return topUp ? ['top-up'] : []
63
+ default: return []
64
+ }
65
+ }
66
+
67
+ export const retryShown = (code, blocked) => !CREDENTIAL_CODES.has(code) || blocked === undefined
68
+
69
+ const FIX_LABELS = {
70
+ key: () => t('Enter API key'),
71
+ rekey: () => t('Enter the key again'),
72
+ 'sign-in': signedIn => (signedIn ? t('Use the DeepSeek account') : t('Sign in with a DeepSeek account')),
73
+ 'use-account': () => t('Use a DeepSeek account instead'),
74
+ 'sign-in-again': () => t('Sign in again'),
75
+ model: () => t('Pick another model'),
76
+ 'top-up': () => t('Top up ↗'),
77
+ }
78
+
79
+ const NO_SUBSCRIBE = () => () => {}
80
+ const NO_VIEW = () => null
81
+
82
+ // The shell account plugin's view of the DeepSeek account: its sign-in status and links.
83
+ function useAccountView(actions) {
84
+ const entry = actions.signInEntry?.() ?? null
85
+ const ops = useMemo(() => entry?.inject?.() ?? {}, [entry])
86
+ const source = ops.hooks?.account
87
+ const read = source ? () => source.getSnapshot()?.view ?? null : NO_VIEW
88
+ return useSyncExternalStore(source?.subscribe ?? NO_SUBSCRIBE, read, read)
89
+ }
90
+
91
+ // The DeepSeek account's own top-up page.
92
+ const topUpOf = (view) => {
93
+ const url = view?.links?.topUpUrl
94
+ return typeof url === 'string' && url.startsWith('https://') ? url : undefined
95
+ }
96
+
97
+ // The account route's model for a Bot moved onto it: the DSH default when it is one,
98
+ // else the first one listed. The account lists its models only once it is signed in,
99
+ // a moment after the sign-in itself.
100
+ export function accountModel(roster) {
101
+ const models = (roster.models ?? []).filter(model => model.provider === ACCOUNT)
102
+ return (models.find(model => model.ref === roster.defaultModel) ?? models[0])?.ref
103
+ }
104
+
105
+ const providerOf = (bot, roster) => {
106
+ const ref = bot?.model ?? bot?.appliedModel
107
+ if (!ref) return undefined
108
+ return (roster.models ?? []).find(model => model.ref === ref)?.provider ?? ref.split('/')[0]
109
+ }
110
+ // A provider without a key may not be in the catalog yet, so DeepSeek's own two routes
111
+ // keep a readable name instead of their id.
112
+ const KNOWN_PROVIDERS = { 'deepseek-official': 'DeepSeek', 'deepseek-account': 'DeepSeek account' }
113
+ const providerName = (roster, provider) => (provider === undefined
114
+ ? t('The model service')
115
+ : (roster.models ?? []).find(model => model.provider === provider)?.providerName
116
+ ?? (KNOWN_PROVIDERS[provider] ? t(KNOWN_PROVIDERS[provider]) : provider))
117
+
118
+ // What Technical details lists, and copies in English for a report.
119
+ export function failureFacts(data, blocked) {
120
+ const facts = []
121
+ if (data.code) facts.push(['Code', t('Code'), data.code])
122
+ if (blocked?.status !== undefined) facts.push(['HTTP status', t('HTTP status'), String(blocked.status)])
123
+ if (blocked?.requestId) facts.push(['Request ID', t('Request ID'), blocked.requestId])
124
+ // DSH already blanks an AUTH message; it never shows here either way.
125
+ if (data.code !== 'AUTH' && data.message) facts.push(['Message', t('Provider message'), data.message])
126
+ return facts
127
+ }
128
+
129
+ function TechnicalDetails({ facts }) {
130
+ const [copied, setCopied] = useState(false)
131
+ if (facts.length === 0) return null
132
+ const copy = () => {
133
+ const text = facts.map(([name, , value]) => `${name}: ${value}`).join('\n')
134
+ void navigator.clipboard?.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1000) }).catch(() => {})
135
+ }
136
+ return h('details', { className: 'bt-fail-tech' },
137
+ h('summary', null, t('Technical details')),
138
+ h('dl', null, facts.map(([name, label, value]) => [h('dt', { key: `${name}:t` }, label), h('dd', { key: `${name}:d` }, value)])),
139
+ h('button', { type: 'button', className: 'bt-mini bt-fail-copy', onClick: copy }, copied ? h(CheckIcon) : h(CopyIcon), copied ? t('Copied') : t('Copy')))
140
+ }
141
+
142
+ export function FailureCell({ node, sessionId, useRoster, useTurnClock, actions }) {
143
+ const roster = useRoster(value => value)
144
+ const data = node?.data ?? {}
145
+ const bot = roster.byId?.[sessionId]
146
+ // A later turn, or a later part of the Bot's chat, has moved past this failure.
147
+ const later = useTurnClock(state => Object.keys(state[sessionId] ?? {}).some(turn => Number(turn) > data.turn))
148
+ const account = useAccountView(actions)
149
+ const signedIn = account?.status === 'credential-stored'
150
+ const topUp = topUpOf(account)
151
+ const [keying, setKeying] = useState(false)
152
+ const [retry, setRetry] = useState('idle')
153
+ const [error, setError] = useState('')
154
+ // A sign-in started here to leave a failing key route, with the model the Bot was on.
155
+ const [joining, setJoining] = useState(null)
156
+ const blocked = bot?.blocked?.code === data.code ? bot.blocked : undefined
157
+ const provider = blocked?.provider ?? providerOf(bot, roster)
158
+ const text = failureText(data.code, { name: bot?.name ?? t('This Bot'), provider: providerName(roster, provider), status: blocked?.status })
159
+ const facts = failureFacts(data, blocked)
160
+ const current = bot !== undefined && currentPart(bot) === sessionId && !later
161
+ // The key route never takes the account's credential, so a sign-in alone leaves the Bot
162
+ // failing; it moves onto the account route. With no account model listed yet, the
163
+ // user picks one.
164
+ const moveToAccount = () => {
165
+ const model = accountModel(roster)
166
+ if (model === undefined) { actions.showDetails?.(sessionId); return }
167
+ if (model === bot.model) return
168
+ setError('')
169
+ void Promise.resolve(actions.updateBot(bot.id, { model })).catch(failure => setError(hostText(failure?.message ?? String(failure))))
170
+ }
171
+ const listed = accountModel(roster)
172
+ useEffect(() => {
173
+ if (joining === null || !signedIn) return undefined
174
+ // A model the user picked meanwhile stands.
175
+ if (bot === undefined || bot.model !== joining.from) { setJoining(null); return undefined }
176
+ if (listed !== undefined) { setJoining(null); moveToAccount(); return undefined }
177
+ // The account's models come with the next listing; without them for a while, the
178
+ // user picks.
179
+ void actions.refreshModels?.()
180
+ const timer = setTimeout(() => { setJoining(null); actions.showDetails?.(sessionId) }, ACCOUNT_MODELS_WAIT_MS)
181
+ return () => clearTimeout(timer)
182
+ }, [joining, signedIn, listed])
183
+ const settled = () => h('div', { className: 'bt-fail', 'data-settled': '', role: 'status' },
184
+ h('div', { className: 'bt-fail-head' }, h('span', { className: 'bt-fail-dot', 'aria-hidden': true }), h('span', { className: 'bt-fail-text' }, text)),
185
+ h(TechnicalDetails, { facts }))
186
+ if (!current) return settled()
187
+ const official = provider === OFFICIAL
188
+ const canSignIn = typeof actions.signIn === 'function' && actions.canSignIn?.() === true
189
+ const keyHere = typeof actions.setProviderKey === 'function' && provider !== undefined
190
+ const keyElsewhere = official && actions.openHarnessOnboarding ? () => actions.openHarnessOnboarding(OFFICIAL) : manageModels(actions)
191
+ const viaAccount = () => {
192
+ if (signedIn) { moveToAccount(); return }
193
+ setJoining({ from: bot.model })
194
+ actions.signIn()
195
+ }
196
+ const runs = {
197
+ key: keyHere ? () => setKeying(value => !value) : keyElsewhere,
198
+ rekey: keyHere ? () => setKeying(value => !value) : keyElsewhere,
199
+ 'sign-in': viaAccount,
200
+ 'use-account': viaAccount,
201
+ 'sign-in-again': () => actions.signIn(),
202
+ model: actions.showDetails ? () => actions.showDetails(sessionId) : undefined,
203
+ }
204
+ const fixes = fixesFor(data.code, { canSignIn, official, topUp: topUp !== undefined, onAccount: provider === ACCOUNT }).filter(fix => fix === 'top-up' || runs[fix] !== undefined)
205
+ // A turn another Bot asked for answers that Bot, which was already told it failed.
206
+ // Retry would answer the user's last message instead, so it is not offered.
207
+ const asked = isHiddenTurn(roster, sessionId, data.turn)
208
+ const retrying = !asked && typeof actions.resume === 'function' && retryShown(data.code, blocked)
209
+ // Once a credential failure is fixed, going on is the next step.
210
+ const retryFirst = retrying && (fixes.length === 0 || CREDENTIAL_CODES.has(data.code))
211
+ const resume = () => {
212
+ setRetry('busy')
213
+ setError('')
214
+ void Promise.resolve(actions.resume(bot.id))
215
+ .then(() => setRetry('sent'), (failure) => { setRetry('idle'); setError(hostText(failure?.message ?? String(failure))) })
216
+ }
217
+ const button = (fix, primary) => {
218
+ const className = primary ? 'bt-send bt-fail-go' : 'bt-soft bt-soft-sm'
219
+ if (fix === 'top-up') {
220
+ return h('a', { key: fix, className: `${className} bt-fail-link`, href: data.code === 'ACCOUNT_QUOTA' ? topUp : TOP_UP_URL, target: '_blank', rel: 'noopener noreferrer' }, FIX_LABELS[fix]())
221
+ }
222
+ const toggles = keyHere && (fix === 'key' || fix === 'rekey')
223
+ return h('button', { key: fix, type: 'button', className, 'aria-expanded': toggles ? keying : undefined, onClick: () => runs[fix]() }, FIX_LABELS[fix](signedIn))
224
+ }
225
+ const retryButton = primary => h('button', { key: 'retry', type: 'button', className: primary ? 'bt-send bt-fail-go' : 'bt-soft bt-soft-sm', disabled: retry !== 'idle', onClick: resume }, retry === 'idle' ? t('Retry') : t('Retrying…'))
226
+ const row = retryFirst
227
+ ? [retryButton(true), ...fixes.map(fix => button(fix, false))]
228
+ : [...fixes.map((fix, index) => button(fix, index === 0)), retrying ? retryButton(false) : null]
229
+ // With nothing to do here the card reads as one a later turn moved past.
230
+ if (!row.some(Boolean)) return settled()
231
+ return h('div', { className: 'bt-fail', role: 'status' },
232
+ h('div', { className: 'bt-fail-head' }, h('span', { className: 'bt-fail-dot', 'aria-hidden': true }), h('span', { className: 'bt-fail-text' }, text)),
233
+ h('div', { className: 'bt-fail-actions' }, row),
234
+ keying && keyHere ? h(ModelKeyCard, { model: { provider, providerName: providerName(roster, provider) }, actions, onSaved: () => setKeying(false) }) : null,
235
+ error ? h('div', { className: 'bt-fail-error', role: 'alert' }, error) : null,
236
+ h(TechnicalDetails, { facts }))
237
+ }
238
+
239
+ export const TURN_ERROR_CSS = `
240
+ .bt-fail{display:flex;flex-direction:column;gap:10px;margin:4px 0 6px;width:max-content;max-width:min(550px,86%);padding:12px 14px;border-radius:16px;background:var(--bt-card);box-shadow:inset 0 0 0 1px var(--bt-line);box-sizing:border-box;color:var(--bt-ink);font-size:14px;line-height:20px}
241
+ .bt-fail[data-settled]{gap:4px;padding:2px 0;background:none;box-shadow:none;color:var(--bt-ink-2);font-size:13px;line-height:18px}
242
+ .bt-fail-head{display:flex;align-items:flex-start;gap:8px;min-width:0}
243
+ .bt-fail-dot{flex:none;width:7px;height:7px;margin-top:6.5px;border-radius:50%;background:var(--bt-danger)}
244
+ .bt-fail[data-settled] .bt-fail-dot{margin-top:5.5px}
245
+ .bt-fail-text{min-width:0;overflow-wrap:anywhere}
246
+ .bt-fail-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-left:15px}
247
+ .bt-fail-actions .bt-fail-go{height:28px;padding:0 12px;font:inherit;font-size:12px}
248
+ .bt-fail-link{display:inline-flex;align-items:center;box-sizing:border-box;text-decoration:none}
249
+ .bt-fail:has(.bt-model-key){width:min(480px,86%)}
250
+ .bt-fail .bt-q.bt-model-key{margin:0 0 0 15px;width:auto;padding:0;background:none;box-shadow:none;border-radius:0;animation:none}
251
+ .bt-fail-error{margin-left:15px;font-size:12px;line-height:17px;color:var(--bt-danger);overflow-wrap:anywhere}
252
+ .bt-fail-tech{margin-left:15px;font-size:12px;line-height:17px;color:var(--bt-ink-2)}
253
+ .bt-fail-tech summary{width:max-content;cursor:pointer;color:var(--bt-ink-3)}
254
+ .bt-fail-tech summary:hover{color:var(--bt-ink-2)}
255
+ .bt-fail-tech dl{display:grid;grid-template-columns:auto minmax(0,1fr);gap:2px 10px;margin:6px 0}
256
+ .bt-fail-tech dt{color:var(--bt-ink-3)}
257
+ .bt-fail-tech dd{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;overflow-wrap:anywhere;white-space:pre-wrap;user-select:text}
258
+ .bt-fail-copy{display:inline-flex;align-items:center;gap:4px}
259
+ .bt-fail-copy svg{width:13px;height:13px}
260
+ `