@toddzheng024/dscode-desktop 0.0.0-stage → 0.7.34

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 (485) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -2
  3. package/THIRD_PARTY_NOTICES.md +30 -0
  4. package/bin/apply_patch +9 -0
  5. package/cordis.patch.yml +433 -0
  6. package/docs/CHANGELOG.md +649 -0
  7. package/docs/account-login.md +44 -0
  8. package/docs/auto-review.md +128 -0
  9. package/docs/browser-use.md +784 -0
  10. package/docs/computer-use.md +81 -0
  11. package/docs/custom-providers.md +88 -0
  12. package/docs/demo.md +158 -0
  13. package/docs/distribution.md +58 -0
  14. package/docs/dscode-ultra.md +75 -0
  15. package/docs/email.md +296 -0
  16. package/docs/exec.md +40 -0
  17. package/docs/hub-distribution.md +212 -0
  18. package/docs/memory.md +121 -0
  19. package/docs/opencode-go.md +61 -0
  20. package/docs/plugin-hub.md +68 -0
  21. package/docs/releases/0.2.0.md +28 -0
  22. package/docs/releases/0.3.0.md +21 -0
  23. package/docs/releases/0.4.0.md +20 -0
  24. package/docs/releases/0.5.0.md +19 -0
  25. package/docs/releases/0.6.0.md +20 -0
  26. package/docs/releases/0.7.0.md +22 -0
  27. package/docs/releases/0.7.1.md +20 -0
  28. package/docs/releases/0.7.10.md +10 -0
  29. package/docs/releases/0.7.11.md +17 -0
  30. package/docs/releases/0.7.12.md +11 -0
  31. package/docs/releases/0.7.13.md +12 -0
  32. package/docs/releases/0.7.14.md +11 -0
  33. package/docs/releases/0.7.15.md +11 -0
  34. package/docs/releases/0.7.16.md +10 -0
  35. package/docs/releases/0.7.17.md +12 -0
  36. package/docs/releases/0.7.18.md +13 -0
  37. package/docs/releases/0.7.19.md +8 -0
  38. package/docs/releases/0.7.2.md +19 -0
  39. package/docs/releases/0.7.20.md +10 -0
  40. package/docs/releases/0.7.21.md +9 -0
  41. package/docs/releases/0.7.22.md +11 -0
  42. package/docs/releases/0.7.23.md +14 -0
  43. package/docs/releases/0.7.24.md +11 -0
  44. package/docs/releases/0.7.25.md +8 -0
  45. package/docs/releases/0.7.26.md +18 -0
  46. package/docs/releases/0.7.27.md +8 -0
  47. package/docs/releases/0.7.28.md +22 -0
  48. package/docs/releases/0.7.29.md +12 -0
  49. package/docs/releases/0.7.3.md +23 -0
  50. package/docs/releases/0.7.30.md +19 -0
  51. package/docs/releases/0.7.31.md +14 -0
  52. package/docs/releases/0.7.32.md +14 -0
  53. package/docs/releases/0.7.33.md +37 -0
  54. package/docs/releases/0.7.34.md +24 -0
  55. package/docs/releases/0.7.4.md +16 -0
  56. package/docs/releases/0.7.5.md +18 -0
  57. package/docs/releases/0.7.6.md +29 -0
  58. package/docs/releases/0.7.7.md +36 -0
  59. package/docs/releases/0.7.8.md +14 -0
  60. package/docs/releases/0.7.9.md +15 -0
  61. package/docs/session-bridge.md +80 -0
  62. package/docs/session-cards.md +53 -0
  63. package/docs/session-communication.md +186 -0
  64. package/docs/session-metrics.md +41 -0
  65. package/docs/skills.md +81 -0
  66. package/docs/triggers.md +338 -0
  67. package/docs/tui-commands.md +204 -0
  68. package/extensions/browser/manifest.json +22 -0
  69. package/extensions/browser/popup.css +6 -0
  70. package/extensions/browser/popup.html +11 -0
  71. package/extensions/browser/popup.mjs +47 -0
  72. package/extensions/browser/protocol.mjs +168 -0
  73. package/extensions/browser/worker.mjs +94 -0
  74. package/icon.svg +7 -0
  75. package/locale/en.json +6 -0
  76. package/locale/zh-cn.json +6 -0
  77. package/node_modules/@deepseek-ai/node-addon-system/LICENSE +21 -0
  78. package/node_modules/@deepseek-ai/node-addon-system/README.i18n.yaml +6 -0
  79. package/node_modules/@deepseek-ai/node-addon-system/README.md +15 -0
  80. package/node_modules/@deepseek-ai/node-addon-system/README.zh.md +15 -0
  81. package/node_modules/@deepseek-ai/node-addon-system/lib/flock.d.ts +13 -0
  82. package/node_modules/@deepseek-ai/node-addon-system/lib/flock.js +51 -0
  83. package/node_modules/@deepseek-ai/node-addon-system/lib/index.d.ts +73 -0
  84. package/node_modules/@deepseek-ai/node-addon-system/lib/index.js +98 -0
  85. package/node_modules/@deepseek-ai/node-addon-system/package.json +45 -0
  86. package/node_modules/@deepseek-ai/node-addon-system/src/flock.c +164 -0
  87. package/node_modules/@deepseek-ai/node-addon-system/src/main.c +298 -0
  88. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/LICENSE +28 -0
  89. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.i18n.yaml +6 -0
  90. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.md +9 -0
  91. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.zh.md +9 -0
  92. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/bin/system.node +0 -0
  93. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/package.json +31 -0
  94. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/prebuilds.json +6 -0
  95. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/LICENSE +28 -0
  96. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.i18n.yaml +6 -0
  97. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.md +9 -0
  98. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.zh.md +9 -0
  99. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/bin/system.node +0 -0
  100. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/package.json +31 -0
  101. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/prebuilds.json +6 -0
  102. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/LICENSE +28 -0
  103. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.i18n.yaml +6 -0
  104. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.md +11 -0
  105. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.zh.md +11 -0
  106. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/glibc/system.node +0 -0
  107. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/landlock-run +0 -0
  108. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/musl/system.node +0 -0
  109. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/package.json +31 -0
  110. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/prebuilds.json +24 -0
  111. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/LICENSE +28 -0
  112. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.i18n.yaml +6 -0
  113. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.md +11 -0
  114. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.zh.md +11 -0
  115. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/glibc/system.node +0 -0
  116. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/landlock-run +0 -0
  117. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/musl/system.node +0 -0
  118. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/package.json +31 -0
  119. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/prebuilds.json +24 -0
  120. package/package.json +156 -4
  121. package/plugins/account/index.mjs +178 -0
  122. package/plugins/account/state.mjs +123 -0
  123. package/plugins/auto-review/audit.mjs +24 -0
  124. package/plugins/auto-review/index.mjs +267 -0
  125. package/plugins/auto-review/policy.mjs +92 -0
  126. package/plugins/browser/SKILL.md +158 -0
  127. package/plugins/browser/access.mjs +143 -0
  128. package/plugins/browser/config.mjs +60 -0
  129. package/plugins/browser/connection.mjs +385 -0
  130. package/plugins/browser/desktop-client.mjs +282 -0
  131. package/plugins/browser/desktop-host.mjs +74 -0
  132. package/plugins/browser/document-identity.mjs +57 -0
  133. package/plugins/browser/extension-relay.mjs +66 -0
  134. package/plugins/browser/files.mjs +32 -0
  135. package/plugins/browser/index.mjs +235 -0
  136. package/plugins/browser/mcp-entry.mjs +18 -0
  137. package/plugins/browser/mcp-tools.mjs +21 -0
  138. package/plugins/browser/presentation.mjs +45 -0
  139. package/plugins/browser/preview.mjs +77 -0
  140. package/plugins/browser/review.mjs +20 -0
  141. package/plugins/browser/screenshots.mjs +35 -0
  142. package/plugins/clipboard-image/clipboard-image.swift +56 -0
  143. package/plugins/clipboard-image/index.mjs +70 -0
  144. package/plugins/code-review/baseline.mjs +143 -0
  145. package/plugins/code-review/git.mjs +163 -0
  146. package/plugins/code-review/index.mjs +236 -0
  147. package/plugins/compaction/engine.mjs +304 -0
  148. package/plugins/compaction/tetris.mjs +65 -0
  149. package/plugins/compaction/threshold.mjs +99 -0
  150. package/plugins/computer-use/DESKTOP-SKILL.md +64 -0
  151. package/plugins/computer-use/desktop-host.mjs +75 -0
  152. package/plugins/computer-use/desktop-skill.mjs +28 -0
  153. package/plugins/computer-use/desktop-upstream.mjs +3 -0
  154. package/plugins/computer-use/index.mjs +37 -0
  155. package/plugins/cordis-config/plain.mjs +31 -0
  156. package/plugins/credentials/index.mjs +73 -0
  157. package/plugins/custom/adapter.mjs +110 -0
  158. package/plugins/custom/config.mjs +85 -0
  159. package/plugins/custom/desktop-client.mjs +150 -0
  160. package/plugins/custom/desktop-host.mjs +78 -0
  161. package/plugins/custom/images.mjs +32 -0
  162. package/plugins/custom/index.mjs +145 -0
  163. package/plugins/custom/wire.mjs +179 -0
  164. package/plugins/desktop/client.mjs +1041 -0
  165. package/plugins/desktop/index.mjs +10 -0
  166. package/plugins/desktop/shell-environment.mjs +9 -0
  167. package/plugins/desktop-workspace/hooks.mjs +29 -0
  168. package/plugins/desktop-workspace/index.mjs +37 -0
  169. package/plugins/dscode/board.mjs +260 -0
  170. package/plugins/dscode/child-target.mjs +11 -0
  171. package/plugins/dscode/control.mjs +17 -0
  172. package/plugins/dscode/desktop-client.mjs +55 -0
  173. package/plugins/dscode/desktop-host.mjs +37 -0
  174. package/plugins/dscode/index.mjs +270 -0
  175. package/plugins/email/cli.mjs +32 -0
  176. package/plugins/email/contacts.mjs +36 -0
  177. package/plugins/email/desktop-client.mjs +117 -0
  178. package/plugins/email/desktop-host.mjs +54 -0
  179. package/plugins/email/desktop-service.mjs +106 -0
  180. package/plugins/email/gmail-oauth.mjs +69 -0
  181. package/plugins/email/gmail-store.mjs +2 -0
  182. package/plugins/email/gmail.mjs +194 -0
  183. package/plugins/email/imap.mjs +136 -0
  184. package/plugins/email/inbox.d.mts +25 -0
  185. package/plugins/email/inbox.mjs +76 -0
  186. package/plugins/email/smtp.mjs +70 -0
  187. package/plugins/email/store.mjs +32 -0
  188. package/plugins/email-tools/index.mjs +46 -0
  189. package/plugins/exec/cli.mjs +64 -0
  190. package/plugins/exec/index.mjs +112 -0
  191. package/plugins/grok/adapter.mjs +114 -0
  192. package/plugins/grok/auth.mjs +76 -0
  193. package/plugins/grok/billing.mjs +147 -0
  194. package/plugins/grok/index.mjs +123 -0
  195. package/plugins/grok/models.mjs +129 -0
  196. package/plugins/grok/status.mjs +29 -0
  197. package/plugins/grok/wire.mjs +268 -0
  198. package/plugins/hub/catalog.mjs +82 -0
  199. package/plugins/hub/desktop-client.mjs +114 -0
  200. package/plugins/hub/desktop-host.mjs +129 -0
  201. package/plugins/i18n/messages.d.mts +21 -0
  202. package/plugins/i18n/messages.mjs +537 -0
  203. package/plugins/jev/approval.mjs +131 -0
  204. package/plugins/jev/client.mjs +74 -0
  205. package/plugins/jev/index.mjs +100 -0
  206. package/plugins/memory/content.mjs +57 -0
  207. package/plugins/memory/desktop-host.mjs +3 -0
  208. package/plugins/memory/index.mjs +168 -0
  209. package/plugins/memory/pipeline.mjs +79 -0
  210. package/plugins/memory/store.mjs +93 -0
  211. package/plugins/message-source/kind.mjs +20 -0
  212. package/plugins/opencode-go/adapter.mjs +96 -0
  213. package/plugins/opencode-go/command.mjs +128 -0
  214. package/plugins/opencode-go/index.mjs +121 -0
  215. package/plugins/opencode-go/models.mjs +58 -0
  216. package/plugins/opencode-go/oauth.mjs +194 -0
  217. package/plugins/opencode-go/search.mjs +86 -0
  218. package/plugins/opencode-go/usage.mjs +120 -0
  219. package/plugins/opencode-go/version.mjs +10 -0
  220. package/plugins/opencode-go/wire.mjs +73 -0
  221. package/plugins/openrouter/adapter.mjs +232 -0
  222. package/plugins/openrouter/index.mjs +124 -0
  223. package/plugins/openrouter/models.mjs +157 -0
  224. package/plugins/openrouter/search.mjs +113 -0
  225. package/plugins/openrouter/wire.mjs +101 -0
  226. package/plugins/providers/catalog.mjs +160 -0
  227. package/plugins/providers/chat-messages.mjs +120 -0
  228. package/plugins/providers/chat-stream.mjs +174 -0
  229. package/plugins/providers/desktop-client.mjs +84 -0
  230. package/plugins/providers/desktop-host.mjs +63 -0
  231. package/plugins/providers/effort.mjs +35 -0
  232. package/plugins/providers/http-errors.mjs +50 -0
  233. package/plugins/providers/openrouter-account.mjs +171 -0
  234. package/plugins/session-bridge/client.mjs +106 -0
  235. package/plugins/session-bridge/communication.mjs +260 -0
  236. package/plugins/session-bridge/index.mjs +67 -0
  237. package/plugins/session-bridge/mailbox.mjs +267 -0
  238. package/plugins/session-bridge/paths.mjs +21 -0
  239. package/plugins/session-bridge/server.mjs +172 -0
  240. package/plugins/session-bridge/tasks.mjs +229 -0
  241. package/plugins/session-cards/content.mjs +64 -0
  242. package/plugins/session-cards/index.mjs +38 -0
  243. package/plugins/session-cards/manager.mjs +140 -0
  244. package/plugins/session-metrics/attribution.mjs +30 -0
  245. package/plugins/session-metrics/balance.mjs +81 -0
  246. package/plugins/session-metrics/desktop-client.mjs +62 -0
  247. package/plugins/session-metrics/desktop-host.mjs +46 -0
  248. package/plugins/session-metrics/index.mjs +97 -0
  249. package/plugins/session-metrics/pricing.mjs +84 -0
  250. package/plugins/session-metrics/rate.mjs +59 -0
  251. package/plugins/session-metrics/store.mjs +75 -0
  252. package/plugins/session-metrics/turns.mjs +134 -0
  253. package/plugins/session-metrics/view.mjs +272 -0
  254. package/plugins/time-marks/desktop-host.mjs +2 -0
  255. package/plugins/time-marks/index.mjs +116 -0
  256. package/plugins/time-marks/marks.mjs +152 -0
  257. package/plugins/triggers/cli.mjs +558 -0
  258. package/plugins/triggers/commands.mjs +143 -0
  259. package/plugins/triggers/config.mjs +264 -0
  260. package/plugins/triggers/desktop-client.mjs +155 -0
  261. package/plugins/triggers/desktop-host.mjs +88 -0
  262. package/plugins/triggers/desktop-management.mjs +97 -0
  263. package/plugins/triggers/desktop-scheduler.mjs +125 -0
  264. package/plugins/triggers/host.mjs +24 -0
  265. package/plugins/triggers/index.mjs +27 -0
  266. package/plugins/triggers/job-cli.mjs +110 -0
  267. package/plugins/triggers/jobs.mjs +156 -0
  268. package/plugins/triggers/launchd.mjs +101 -0
  269. package/plugins/triggers/lease.mjs +24 -0
  270. package/plugins/triggers/log.mjs +104 -0
  271. package/plugins/triggers/management.mjs +181 -0
  272. package/plugins/triggers/options.mjs +117 -0
  273. package/plugins/triggers/overlay.mjs +7 -0
  274. package/plugins/triggers/poll.mjs +65 -0
  275. package/plugins/triggers/run.mjs +157 -0
  276. package/plugins/triggers/schedule.mjs +40 -0
  277. package/plugins/triggers/scheduler-service.mjs +46 -0
  278. package/plugins/triggers/scheduler.mjs +92 -0
  279. package/plugins/triggers/session-run.mjs +125 -0
  280. package/plugins/triggers/session.mjs +48 -0
  281. package/plugins/triggers/source-emit.mjs +19 -0
  282. package/plugins/triggers/source-host.mjs +78 -0
  283. package/plugins/triggers/source-ingress.mjs +70 -0
  284. package/plugins/triggers/source-sandbox.mjs +35 -0
  285. package/plugins/triggers/sources.mjs +45 -0
  286. package/plugins/triggers/spool.mjs +145 -0
  287. package/plugins/triggers/tools.mjs +67 -0
  288. package/plugins/triggers/unattended.mjs +18 -0
  289. package/plugins/tui-tools/composition.mjs +28 -0
  290. package/plugins/tui-tools/desktop-host.mjs +2 -0
  291. package/plugins/tui-tools/doctor-cli.mjs +22 -0
  292. package/plugins/tui-tools/doctor.mjs +149 -0
  293. package/plugins/tui-tools/hook-sources.mjs +113 -0
  294. package/plugins/tui-tools/hooks.mjs +23 -0
  295. package/plugins/tui-tools/index.mjs +269 -0
  296. package/plugins/tui-tools/sandbox-runner.mjs +123 -0
  297. package/plugins/tui-tools/shell.mjs +27 -0
  298. package/plugins/tui-tools/update.mjs +111 -0
  299. package/plugins/tui-tools/workspace-discovery.mjs +129 -0
  300. package/plugins/ultra/policy.mjs +42 -0
  301. package/plugins/worktree-subagent/worktree.mjs +56 -0
  302. package/runtime-sources.json +94 -0
  303. package/vendor/bash/LICENSE +21 -0
  304. package/vendor/bash/index.js +717 -0
  305. package/vendor/bash/types/background.d.ts +50 -0
  306. package/vendor/bash/types/index.d.ts +38 -0
  307. package/vendor/bash/types/render.d.ts +57 -0
  308. package/vendor/command-goal/LICENSE +21 -0
  309. package/vendor/command-goal/index.js +210 -0
  310. package/vendor/command-goal/types/index.d.ts +10 -0
  311. package/vendor/computer-use/LICENSE +21 -0
  312. package/vendor/computer-use/lib/approval-policy.js +12 -0
  313. package/vendor/computer-use/lib/approval-policy.js.map +1 -0
  314. package/vendor/computer-use/lib/artifacts.js +79 -0
  315. package/vendor/computer-use/lib/artifacts.js.map +1 -0
  316. package/vendor/computer-use/lib/backend.js +3 -0
  317. package/vendor/computer-use/lib/backend.js.map +1 -0
  318. package/vendor/computer-use/lib/client.js +369 -0
  319. package/vendor/computer-use/lib/client.js.map +1 -0
  320. package/vendor/computer-use/lib/config.js +159 -0
  321. package/vendor/computer-use/lib/config.js.map +1 -0
  322. package/vendor/computer-use/lib/confirmations.js +99 -0
  323. package/vendor/computer-use/lib/confirmations.js.map +1 -0
  324. package/vendor/computer-use/lib/diff.js +58 -0
  325. package/vendor/computer-use/lib/diff.js.map +1 -0
  326. package/vendor/computer-use/lib/errors.js +22 -0
  327. package/vendor/computer-use/lib/errors.js.map +1 -0
  328. package/vendor/computer-use/lib/exposure.js +244 -0
  329. package/vendor/computer-use/lib/exposure.js.map +1 -0
  330. package/vendor/computer-use/lib/index.js +66 -0
  331. package/vendor/computer-use/lib/index.js.map +1 -0
  332. package/vendor/computer-use/lib/leases.js +262 -0
  333. package/vendor/computer-use/lib/leases.js.map +1 -0
  334. package/vendor/computer-use/lib/providers/macos.js +243 -0
  335. package/vendor/computer-use/lib/providers/macos.js.map +1 -0
  336. package/vendor/computer-use/lib/providers/native-helper.js +726 -0
  337. package/vendor/computer-use/lib/providers/native-helper.js.map +1 -0
  338. package/vendor/computer-use/lib/providers/unsupported.js +51 -0
  339. package/vendor/computer-use/lib/providers/unsupported.js.map +1 -0
  340. package/vendor/computer-use/lib/service.js +608 -0
  341. package/vendor/computer-use/lib/service.js.map +1 -0
  342. package/vendor/computer-use/lib/skill.js +136 -0
  343. package/vendor/computer-use/lib/skill.js.map +1 -0
  344. package/vendor/computer-use/lib/target-resolver.js +180 -0
  345. package/vendor/computer-use/lib/target-resolver.js.map +1 -0
  346. package/vendor/computer-use/lib/tools.js +511 -0
  347. package/vendor/computer-use/lib/tools.js.map +1 -0
  348. package/vendor/computer-use/lib/types/approval-policy.d.ts +13 -0
  349. package/vendor/computer-use/lib/types/approval-policy.d.ts.map +1 -0
  350. package/vendor/computer-use/lib/types/artifacts.d.ts +10 -0
  351. package/vendor/computer-use/lib/types/artifacts.d.ts.map +1 -0
  352. package/vendor/computer-use/lib/types/backend.d.ts +124 -0
  353. package/vendor/computer-use/lib/types/backend.d.ts.map +1 -0
  354. package/vendor/computer-use/lib/types/client/index.d.ts +83 -0
  355. package/vendor/computer-use/lib/types/client/index.d.ts.map +1 -0
  356. package/vendor/computer-use/lib/types/config.d.ts +94 -0
  357. package/vendor/computer-use/lib/types/config.d.ts.map +1 -0
  358. package/vendor/computer-use/lib/types/confirmations.d.ts +23 -0
  359. package/vendor/computer-use/lib/types/confirmations.d.ts.map +1 -0
  360. package/vendor/computer-use/lib/types/diff.d.ts +5 -0
  361. package/vendor/computer-use/lib/types/diff.d.ts.map +1 -0
  362. package/vendor/computer-use/lib/types/errors.d.ts +15 -0
  363. package/vendor/computer-use/lib/types/errors.d.ts.map +1 -0
  364. package/vendor/computer-use/lib/types/exposure.d.ts +30 -0
  365. package/vendor/computer-use/lib/types/exposure.d.ts.map +1 -0
  366. package/vendor/computer-use/lib/types/index.d.ts +21 -0
  367. package/vendor/computer-use/lib/types/index.d.ts.map +1 -0
  368. package/vendor/computer-use/lib/types/leases.d.ts +81 -0
  369. package/vendor/computer-use/lib/types/leases.d.ts.map +1 -0
  370. package/vendor/computer-use/lib/types/providers/macos.d.ts +39 -0
  371. package/vendor/computer-use/lib/types/providers/macos.d.ts.map +1 -0
  372. package/vendor/computer-use/lib/types/providers/native-helper.d.ts +48 -0
  373. package/vendor/computer-use/lib/types/providers/native-helper.d.ts.map +1 -0
  374. package/vendor/computer-use/lib/types/providers/unsupported.d.ts +23 -0
  375. package/vendor/computer-use/lib/types/providers/unsupported.d.ts.map +1 -0
  376. package/vendor/computer-use/lib/types/service.d.ts +58 -0
  377. package/vendor/computer-use/lib/types/service.d.ts.map +1 -0
  378. package/vendor/computer-use/lib/types/skill.d.ts +14 -0
  379. package/vendor/computer-use/lib/types/skill.d.ts.map +1 -0
  380. package/vendor/computer-use/lib/types/target-resolver.d.ts +38 -0
  381. package/vendor/computer-use/lib/types/target-resolver.d.ts.map +1 -0
  382. package/vendor/computer-use/lib/types/tools.d.ts +6 -0
  383. package/vendor/computer-use/lib/types/tools.d.ts.map +1 -0
  384. package/vendor/computer-use/lib/types/types.d.ts +270 -0
  385. package/vendor/computer-use/lib/types/types.d.ts.map +1 -0
  386. package/vendor/computer-use/lib/types/web.d.ts +33 -0
  387. package/vendor/computer-use/lib/types/web.d.ts.map +1 -0
  388. package/vendor/computer-use/lib/types.js +9 -0
  389. package/vendor/computer-use/lib/types.js.map +1 -0
  390. package/vendor/computer-use/lib/web.js +188 -0
  391. package/vendor/computer-use/lib/web.js.map +1 -0
  392. package/vendor/computer-use/native/macos/Sources/Fixture/main.swift +412 -0
  393. package/vendor/computer-use/native/macos/Sources/Helper/CursorImage.swift +11 -0
  394. package/vendor/computer-use/native/macos/Sources/Helper/CursorMotion.swift +67 -0
  395. package/vendor/computer-use/native/macos/Sources/Helper/CursorOverlay.swift +840 -0
  396. package/vendor/computer-use/native/macos/Sources/Helper/TargetedPointer.swift +327 -0
  397. package/vendor/computer-use/native/macos/Sources/Helper/WindowNumberMatcher.swift +29 -0
  398. package/vendor/computer-use/native/macos/Sources/Helper/main.swift +1244 -0
  399. package/vendor/computer-use/native/macos/Sources/Monitor/main.swift +153 -0
  400. package/vendor/computer-use/native/macos/bin/dsh-computer-use-helper +0 -0
  401. package/vendor/computer-use/native/macos/manifest.json +14 -0
  402. package/vendor/persistent/LICENSE +21 -0
  403. package/vendor/persistent/index.js +411 -0
  404. package/vendor/persistent/types/index.d.ts +24 -0
  405. package/vendor/subagent/LICENSE +21 -0
  406. package/vendor/subagent/index.js +694 -0
  407. package/vendor/subagent/invariant.js +52 -0
  408. package/vendor/subagent/model-selection-settings.js +68 -0
  409. package/vendor/subagent/types/index.d.ts +82 -0
  410. package/vendor/subagent/types/invariant.d.ts +16 -0
  411. package/vendor/subagent/types/list-models.d.ts +10 -0
  412. package/vendor/subagent/types/model-selection-settings.d.ts +45 -0
  413. package/vendor/subagent/types/model-selection-state.d.ts +48 -0
  414. package/vendor/subagent/types/model-selection.d.ts +81 -0
  415. package/vendor/subagent-core/LICENSE +21 -0
  416. package/vendor/subagent-core/index.js +3202 -0
  417. package/vendor/subagent-core/invariant.js +76 -0
  418. package/vendor/subagent-core/typert.host.d.ts +3 -0
  419. package/vendor/subagent-core/typert.host.js +978 -0
  420. package/vendor/subagent-core/typert.remote-client.d.ts +25 -0
  421. package/vendor/subagent-core/typert.remote-client.js +114 -0
  422. package/vendor/subagent-core/types/archive-admission.d.ts +16 -0
  423. package/vendor/subagent-core/types/archive-admission.js +151 -0
  424. package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
  425. package/vendor/subagent-core/types/assistant-output.js +73 -0
  426. package/vendor/subagent-core/types/catalog.d.ts +84 -0
  427. package/vendor/subagent-core/types/catalog.js +109 -0
  428. package/vendor/subagent-core/types/child-agent.d.ts +156 -0
  429. package/vendor/subagent-core/types/child-agent.js +213 -0
  430. package/vendor/subagent-core/types/client.d.ts +8 -0
  431. package/vendor/subagent-core/types/client.js +7 -0
  432. package/vendor/subagent-core/types/continuation-activation.d.ts +267 -0
  433. package/vendor/subagent-core/types/continuation-activation.js +700 -0
  434. package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
  435. package/vendor/subagent-core/types/continuation-messages.js +106 -0
  436. package/vendor/subagent-core/types/continuation.d.ts +124 -0
  437. package/vendor/subagent-core/types/continuation.js +455 -0
  438. package/vendor/subagent-core/types/control-types.d.ts +135 -0
  439. package/vendor/subagent-core/types/control-types.js +8 -0
  440. package/vendor/subagent-core/types/control.d.ts +45 -0
  441. package/vendor/subagent-core/types/control.js +79 -0
  442. package/vendor/subagent-core/types/depth.d.ts +31 -0
  443. package/vendor/subagent-core/types/depth.js +39 -0
  444. package/vendor/subagent-core/types/descriptor.d.ts +144 -0
  445. package/vendor/subagent-core/types/descriptor.js +193 -0
  446. package/vendor/subagent-core/types/error.d.ts +11 -0
  447. package/vendor/subagent-core/types/error.js +14 -0
  448. package/vendor/subagent-core/types/inbox.d.ts +43 -0
  449. package/vendor/subagent-core/types/inbox.js +61 -0
  450. package/vendor/subagent-core/types/index.d.ts +320 -0
  451. package/vendor/subagent-core/types/index.js +475 -0
  452. package/vendor/subagent-core/types/internal.d.ts +58 -0
  453. package/vendor/subagent-core/types/internal.js +58 -0
  454. package/vendor/subagent-core/types/invariant.d.ts +13 -0
  455. package/vendor/subagent-core/types/invariant.js +91 -0
  456. package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
  457. package/vendor/subagent-core/types/lifecycle.js +180 -0
  458. package/vendor/subagent-core/types/list-children.d.ts +36 -0
  459. package/vendor/subagent-core/types/list-children.js +166 -0
  460. package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
  461. package/vendor/subagent-core/types/out-of-process.js +219 -0
  462. package/vendor/subagent-core/types/projection-types.d.ts +82 -0
  463. package/vendor/subagent-core/types/projection-types.js +7 -0
  464. package/vendor/subagent-core/types/projection.d.ts +100 -0
  465. package/vendor/subagent-core/types/projection.js +157 -0
  466. package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
  467. package/vendor/subagent-core/types/run-settlement.js +70 -0
  468. package/vendor/subagent-core/types/types.d.ts +377 -0
  469. package/vendor/subagent-core/types/types.js +19 -0
  470. package/vendor/subagent-driver/LICENSE +21 -0
  471. package/vendor/subagent-driver/index.js +255 -0
  472. package/vendor/subagent-driver/types/index.d.ts +33 -0
  473. package/vendor/subagent-driver/types/structured.d.ts +42 -0
  474. package/vendor/subagent-fork/LICENSE +21 -0
  475. package/vendor/subagent-fork/index.js +61 -0
  476. package/vendor/subagent-fork/types/index.d.ts +20 -0
  477. package/vendor/subagent-spawn/LICENSE +21 -0
  478. package/vendor/subagent-spawn/index.js +45 -0
  479. package/vendor/subagent-spawn/types/index.d.ts +19 -0
  480. package/vendor/terminal/LICENSE +21 -0
  481. package/vendor/terminal/index.js +1093 -0
  482. package/vendor/terminal/types/config.d.ts +82 -0
  483. package/vendor/terminal/types/index.d.ts +36 -0
  484. package/vendor/terminal/types/sanitize.d.ts +47 -0
  485. package/vendor/terminal/types/session.d.ts +87 -0
@@ -0,0 +1,30 @@
1
+ // Charge model calls a plugin makes for a session (review, memory, session cards,
2
+ // /doctor) to that session's cost ledger without putting the session on the wire:
3
+ // a `sessionId` request option also drives session-log delivery, provider cache
4
+ // affinity and agent-only prompt shaping, which these calls must not trigger. The
5
+ // ledger middleware runs when the caller iterates the stream, so it reads the charge
6
+ // from the caller's async context.
7
+ import { AsyncLocalStorage } from 'node:async_hooks';
8
+
9
+ const charges = new AsyncLocalStorage();
10
+
11
+ /** Run `fn` with its model calls charged to `sessionId` under `purpose`; no session runs it uncharged. */
12
+ export function chargeTo(sessionId, purpose, fn) {
13
+ return sessionId ? charges.run({ sessionId, purpose }, fn) : fn();
14
+ }
15
+
16
+ /** The charge of the model call being iterated, if any. */
17
+ export function currentCharge() {
18
+ return charges.getStore();
19
+ }
20
+
21
+ /** Charge a live child request to its ancestors once, with the original session first. */
22
+ export function metricRecipients(agents, sessionId) {
23
+ const recipients = new Set([sessionId]);
24
+ let child = agents?.get(sessionId);
25
+ while (child?.session.header.origin === 'subagent' && child.session.header.parentSession && !recipients.has(child.session.header.parentSession)) {
26
+ recipients.add(child.session.header.parentSession);
27
+ child = agents.get(child.session.header.parentSession);
28
+ }
29
+ return recipients;
30
+ }
@@ -0,0 +1,81 @@
1
+ import { OPENROUTER_API, parseOpenRouterCredits, parseOpenRouterKeyRemaining } from '../providers/openrouter-account.mjs';
2
+
3
+ export { parseOpenRouterCredits, parseOpenRouterKeyRemaining };
4
+
5
+ // Remaining provider balance, refreshed on a long cache. DeepSeek's response
6
+ // also carries the trusted clock: its `Date` header anchors peak/off-peak pricing
7
+ // without a second network call. OpenRouter has no peak window. Its account
8
+ // credits (`/credits`) are documented as management-only but are served to
9
+ // inference keys too; a key refused them falls back to its own remaining limit (`/key`).
10
+ const SOURCES = {
11
+ 'deepseek-official': { env: 'DEEPSEEK_API_KEY', clock: true, requests: ({ key }) => [{ url: 'https://api.deepseek.com/user/balance', key, parse: body => parseBalance(body) }] },
12
+ openrouter: { env: 'OPENROUTER_API_KEY', managementEnv: 'OPENROUTER_MANAGEMENT_KEY', clock: false, requests: ({ key, managementKey }) => [
13
+ { url: `${OPENROUTER_API}/credits`, key: managementKey ?? key, parse: parseOpenRouterCredits },
14
+ { url: `${OPENROUTER_API}/key`, key, parse: parseOpenRouterKeyRemaining },
15
+ ] },
16
+ };
17
+ export const BALANCE_PROVIDERS = Object.freeze(Object.keys(SOURCES));
18
+ const CACHE_MS = 5 * 60 * 1000;
19
+ const RETRY_MS = 60 * 1000;
20
+ const snapshots = new Map();
21
+ let clock = { anchor: null, skewMs: 0 };
22
+ const snapshotOf = provider => snapshots.get(provider) ?? { balance: null, fetchedAt: 0, pending: false };
23
+
24
+ /** Best-effort positive USD balance from a `/user/balance` body. */
25
+ export function parseBalance(body) {
26
+ const infos = Array.isArray(body?.balance_infos) ? body.balance_infos : [];
27
+ const usd = infos.find(info => String(info?.currency ?? '').toUpperCase() === 'USD') ?? infos[0];
28
+ const total = Number(usd?.total_balance);
29
+ if (!Number.isFinite(total) || total < 0) return null;
30
+ return body?.is_available === false ? null : total;
31
+ }
32
+
33
+ export function balanceNow(provider = 'deepseek-official') { return snapshotOf(provider).balance; }
34
+ /** Clock anchored to the last DeepSeek balance response's `Date` header, else the local one. */
35
+ export function trustedNow() {
36
+ const local = Date.now();
37
+ return clock.anchor === null ? local : local + clock.skewMs;
38
+ }
39
+
40
+ /** Refresh one provider at most once per cache window; never throws into the render path. */
41
+ export async function refreshBalance(options = {}) {
42
+ const provider = options.provider ?? 'deepseek-official';
43
+ const source = SOURCES[provider];
44
+ if (!source) return null;
45
+ const snapshot = snapshotOf(provider);
46
+ const key = options.key ?? process.env[source.env];
47
+ const managementKey = options.managementKey ?? (source.managementEnv ? process.env[source.managementEnv] : undefined);
48
+ const requests = source.requests({ key, managementKey }).filter(request => request.key);
49
+ const mode = managementKey ? 'management' : 'key';
50
+ const now = Date.now();
51
+ // A newly added (or removed) management key changes the source; it is read at once.
52
+ if (snapshot.pending || snapshot.mode === mode && now - snapshot.fetchedAt < CACHE_MS) return snapshot.balance;
53
+ const retrySoon = () => Date.now() - (CACHE_MS - RETRY_MS);
54
+ if (requests.length === 0) { snapshots.set(provider, { ...snapshot, mode, fetchedAt: retrySoon(), pending: false }); return snapshot.balance; }
55
+ const fetchImpl = options.fetch ?? globalThis.fetch;
56
+ if (typeof fetchImpl !== 'function') return snapshot.balance;
57
+ snapshots.set(provider, { ...snapshot, pending: true });
58
+ try {
59
+ let response, body, request;
60
+ for (request of requests) {
61
+ response = await fetchImpl(request.url, { headers: { Authorization: `Bearer ${request.key}`, Accept: 'application/json' } });
62
+ body = await response.json();
63
+ // A key refused this reading falls through to the next source.
64
+ if (response.ok || (response.status !== 401 && response.status !== 403)) break;
65
+ }
66
+ const header = source.clock ? response.headers?.get?.('date') : null;
67
+ const anchor = header ? Date.parse(header) : NaN;
68
+ const parsed = response.ok ? request.parse(body) : null;
69
+ if (Number.isFinite(anchor)) clock = { anchor, skewMs: anchor - Date.now() };
70
+ snapshots.set(provider, {
71
+ mode,
72
+ balance: parsed === null && response.ok ? null : parsed ?? snapshot.balance,
73
+ fetchedAt: parsed === null ? retrySoon() : Date.now(),
74
+ pending: false,
75
+ });
76
+ } catch {
77
+ // A transient failure keeps the last known balance and retries sooner.
78
+ snapshots.set(provider, { ...snapshot, mode, fetchedAt: retrySoon(), pending: false });
79
+ }
80
+ return snapshotOf(provider).balance;
81
+ }
@@ -0,0 +1,62 @@
1
+ globalThis.__ModuleLoader__.load({
2
+ id: '@toddzheng024/dscode-desktop',
3
+ factory: require => {
4
+ const { createElement: h, useState, useEffect, useRef } = require('react');
5
+ const money = (cost, partial = false) => cost === null || partial && cost === 0 ? 'Unknown' : `$${cost.toFixed(4)}${partial ? '+' : ''}`;
6
+ const percent = value => value === null ? 'Unknown' : `${value.toFixed(1)}%`;
7
+ const speed = value => value === null ? 'Unknown' : `${value.toFixed(1)} tokens/s`;
8
+ const control = { padding: '7px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit' };
9
+ function Usage({ execute }) {
10
+ const [state, setState] = useState(null), [error, setError] = useState('');
11
+ const active = useRef(false), alive = useRef(true);
12
+ const refresh = async () => {
13
+ if (active.current) return; active.current = true;
14
+ try { const next = await execute(); if (alive.current) { setState(next); setError(''); } }
15
+ catch (error) { if (alive.current) { setState(null); setError(error.message); } }
16
+ finally { active.current = false; }
17
+ };
18
+ useEffect(() => { alive.current = true; void refresh(); const timer = setInterval(refresh, 1000);
19
+ return () => { alive.current = false; clearInterval(timer); }; }, []);
20
+ const fact = (label, value) => h('div', { key: label, style: { padding: 10, border: '1px solid #8884', borderRadius: 6 } },
21
+ h('dt', { style: { fontSize: 12, marginBottom: 5 } }, label), h('dd', { style: { margin: 0, fontVariantNumeric: 'tabular-nums' } }, value));
22
+ return h('section', { 'aria-label': 'DSCODE session usage', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
23
+ h('h2', { style: { marginTop: 0 } }, 'Session usage'),
24
+ h('p', { style: { fontSize: 12 } }, 'Recorded model usage for this DSCODE session, including attributed child and background calls. Costs are estimates, not an invoice.'),
25
+ h('button', { type: 'button', style: control, onClick: refresh }, 'Refresh usage'),
26
+ error && h('p', { role: 'alert' }, error),
27
+ !state && !error && h('p', { role: 'status' }, 'Loading usage…'),
28
+ state && h('div', null,
29
+ h('p', { style: { overflowWrap: 'anywhere', fontSize: 12 } }, `${state.provider} / ${state.model}`),
30
+ h('p', { role: 'status' }, state.requestActive ? 'Model request in progress' : 'No active model request'),
31
+ h('dl', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: 8 } },
32
+ fact('Recorded cost', money(state.cost, state.partial)), fact('Model calls', state.calls ?? 'Unknown'),
33
+ fact('Pending calls', state.pending ?? 'Unknown'), fact('Context estimate', percent(state.contextPercent)),
34
+ fact('Input cache share', percent(state.cachePercent)), fact('Smoothed request speed', speed(state.currentTps)),
35
+ fact('Session average speed', speed(state.averageTps)),
36
+ state.balance !== null && fact('Cached provider balance', money(state.balance)),
37
+ state.subscription && fact('Cached subscription usage', state.subscription),
38
+ state.budget && fact('Configured cost budget', `${money(state.budget.spent)} / ${money(state.budget.limit)} · ${state.budget.state}`)),
39
+ h('p', { style: { fontSize: 12 } }, 'Unknown means a reading or price is unavailable. + marks a partial cost. Pending calls have not settled. Cached account figures may lag the provider.'),
40
+ h('h3', null, 'Cost by turn'),
41
+ state.turns.length ? h('table', { style: { width: '100%', borderCollapse: 'collapse', textAlign: 'left', fontVariantNumeric: 'tabular-nums' } },
42
+ h('thead', null, h('tr', null, ...['Turn', 'Calls', 'Cost'].map(label => h('th', { key: label, style: { padding: 6 } }, label)))),
43
+ h('tbody', null, ...state.turns.map(row => h('tr', { key: row.turn }, h('td', { style: { padding: 6 } }, row.turn), h('td', { style: { padding: 6 } }, row.calls), h('td', { style: { padding: 6 } }, money(row.cost, row.partial))))))
44
+ : h('p', null, 'No turn costs recorded yet.'),
45
+ state.totalTurns > 50 && h('p', { style: { fontSize: 12 } }, `Showing the latest 50 of ${state.totalTurns} turns.`),
46
+ h('p', { style: { fontSize: 12 } }, `Cost outside recorded turn windows: ${money(state.unattributed, state.unattributedPartial)}`),
47
+ h('p', { style: { fontSize: 12 } }, 'Refreshes once per second while visible. Reading usage does not start the agent or request new account data.')));
48
+ }
49
+ function UsagePane({ sessionId, execute, useTabInfo }) { const { tab } = useTabInfo(); return tab.visible ? h(Usage, { key: sessionId, execute }) : null; }
50
+ const id = '@toddzheng024/dscode-metrics-desktop';
51
+ return { inject: ['slots', 'sidebarRightTabs', 'connection'], apply(ctx) {
52
+ ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-metrics', multiple: false, title: () => 'Session usage',
53
+ guide: [{ id: 'open', order: 33, title: () => 'Session usage', description: () => 'Inspect DSCODE cost, context, cache and request speed.' }] }), 'dscode-metrics.type');
54
+ ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
55
+ inject: sessionId => ({ sessionId, execute: async () => {
56
+ const response = await ctx.connection.rpc.call('/api', 'dscode-metrics', { action: 'status', sessionId });
57
+ if (!response.ok) throw Error(response.error.message); return response.value;
58
+ } }),
59
+ }, UsagePane)), 'dscode-metrics.body');
60
+ } };
61
+ },
62
+ });
@@ -0,0 +1,46 @@
1
+ import { clientRequestSchema } from '@deepseek-ai/dsh-client-connection';
2
+ import { footerFor, goFooterFact, grokFooterFact } from './view.mjs';
3
+ import { balanceNow } from './balance.mjs';
4
+ import { canonicalProvider } from '../providers/catalog.mjs';
5
+ import { grokSubscriptionNow } from '../grok/billing.mjs';
6
+ import { goUsageNow } from '../opencode-go/usage.mjs';
7
+
8
+ const finite = value => Number.isFinite(value) ? value : null;
9
+ /** Explicit metadata projection: no raw ledger, history, prompts or credentials. */
10
+ export function desktopMetricsRequest(ctx, payload) {
11
+ if (payload?.action !== 'status' || typeof payload.sessionId !== 'string') throw Error('Choose a DSCODE session to view usage.');
12
+ const agent = ctx.agents.get(payload.sessionId);
13
+ if (!agent || ctx.agentPresets.composedPreset(agent.ctx) !== 'dscode') throw Error('Usage is available for an open DSCODE session.');
14
+ const route = agent.session.requestHeader()?.config ?? agent.options;
15
+ const provider = canonicalProvider(route.provider);
16
+ return footerFor(agent.id, {}, 0, provider, 'en', (summary, context, _columns, rates) => ({
17
+ sessionId: agent.id, provider: route.provider, model: route.model,
18
+ cost: finite(summary.cost), partial: summary.unknown === true, calls: finite(summary.calls), pending: finite(summary.pending),
19
+ contextPercent: finite(context), cachePercent: finite(summary.cache),
20
+ currentTps: finite(rates.current), averageTps: finite(rates.average), requestActive: rates.active === true,
21
+ balance: finite(balanceNow(provider)),
22
+ subscription: provider === 'grok' ? grokFooterFact(grokSubscriptionNow()) : provider === 'opencode-go' ? goFooterFact(goUsageNow()) : null,
23
+ turns: (summary.turns ?? []).slice(-50).reverse().map(turn => ({ turn: turn.turn, cost: finite(turn.cost), calls: turn.calls, partial: turn.unknown })),
24
+ totalTurns: summary.turns?.length ?? 0, unattributed: finite(summary.unattributed),
25
+ unattributedPartial: summary.unattributedUnknown === true,
26
+ budget: summary.budget ? { limit: finite(summary.budget.limit), spent: finite(summary.budget.spent), state: summary.budget.state } : null,
27
+ updatedAt: new Date().toISOString(),
28
+ }));
29
+ }
30
+
31
+ export const inject = ['agents', 'agentPresets', 'connection'];
32
+ export function apply(ctx) {
33
+ ctx.effect(() => ctx.connection.fetch.register({ path: '/api/dscode-metrics', methods: ['POST'], requestBody: 'buffered',
34
+ fetch: async request => {
35
+ let body;
36
+ try { body = await request.json(); } catch { return new Response('Invalid JSON', { status: 400 }); }
37
+ const parsed = clientRequestSchema.safeParse(body);
38
+ if (!parsed.success || parsed.data.method !== 'dscode-metrics') return new Response('Invalid usage request', { status: 400 });
39
+ const { rpcId, payload } = parsed.data;
40
+ let result;
41
+ try { result = { ok: true, value: desktopMetricsRequest(ctx, payload) }; }
42
+ catch (error) { result = { ok: false, error: { code: 'dscode-metrics', message: error.message, details: {} } }; }
43
+ return Response.json({ type: 'server-response', rpcId, result }, { headers: { 'Cache-Control': 'no-store' } });
44
+ },
45
+ }), 'dscode metrics rpc');
46
+ }
@@ -0,0 +1,97 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { appendMetric } from './store.mjs';
4
+ import { estimateCost, priceVersionFor } from './pricing.mjs';
5
+ import { setMetricSource } from './view.mjs';
6
+ import { BALANCE_PROVIDERS, refreshBalance } from './balance.mjs';
7
+ import { refreshOpenRouterModels } from '../openrouter/models.mjs';
8
+ import { REPLAY_KIND } from '../openrouter/wire.mjs';
9
+ import { providerSpec } from '../providers/catalog.mjs';
10
+ import { createSmoothedRate } from './rate.mjs';
11
+ import { currentCharge, metricRecipients } from './attribution.mjs';
12
+
13
+ // Chunks that carry generated output; the first one marks time to first token.
14
+ const OUTPUT_CHUNKS = new Set(['text-delta', 'reasoning-delta', 'tool-call-delta']);
15
+ export const name = 'dscode-session-metrics';
16
+ export const inject = ['llm', 'agents', 'tokenMeter', 'sessionProjections'];
17
+ export function apply(ctx) {
18
+ const snapshots = new WeakMap();
19
+ const smoothedRate = createSmoothedRate();
20
+ const activeRequests = new WeakMap();
21
+ ctx.effect(() => setMetricSource(id => {
22
+ const agent = ctx.agents.get(id);
23
+ if (!agent) return undefined;
24
+ const session = agent.session;
25
+ const cached = snapshots.get(session);
26
+ const requestActive = (activeRequests.get(session) ?? 0) > 0;
27
+ if (cached?.seq === session.seq) return { ...cached.value, currentTps: smoothedRate.get(session), requestActive };
28
+ const state = ctx.sessionProjections.stateOf(session, 'contextPressure');
29
+ const measurement = ctx.tokenMeter.measure(session);
30
+ const value = { events: session.snapshotEvents(), used: measurement.totalTokens, capacity: state?.contextWindow };
31
+ snapshots.set(session, { seq: session.seq, value });
32
+ return { ...value, currentTps: smoothedRate.get(session), requestActive };
33
+ }));
34
+ const home = resolveDshHome();
35
+ // Remaining balance is best-effort decoration: resolve the key lazily (never
36
+ // inject the credentials service, so a missing one cannot fail startup), keep
37
+ // the request on a five-minute cache, and never let it reach the render path.
38
+ const credentials = ctx.get?.('credentials');
39
+ const refresh = () => Promise.all(BALANCE_PROVIDERS.map(async provider => {
40
+ try {
41
+ const spec = providerSpec(provider);
42
+ const secret = async ref => {
43
+ if (!ref) return undefined;
44
+ const resolved = await credentials?.resolve?.(ref);
45
+ return (typeof resolved === 'string' ? resolved : resolved?.value) || process.env[ref] || undefined;
46
+ };
47
+ // An OpenRouter management key, when stored, turns the balance into the account's credits.
48
+ const [key, managementKey] = await Promise.all([secret(spec.credentialRef), secret(spec.managementRef)]);
49
+ await refreshBalance({ provider, key, managementKey });
50
+ // The listing needs no key, but only a user with an OpenRouter key uses it; the adapter waits for it before its first call.
51
+ if (provider === 'openrouter' && key) await refreshOpenRouterModels({ home });
52
+ } catch {
53
+ /* balance stays unknown */
54
+ }
55
+ }));
56
+ refresh();
57
+ const balanceTimer = setInterval(refresh, 5 * 60 * 1000);
58
+ if (typeof balanceTimer.unref === 'function') balanceTimer.unref();
59
+ ctx.effect(() => () => clearInterval(balanceTimer));
60
+ const record = (id, entry) => { try { appendMetric(home, id, entry); } catch { ctx.logger.warn('Session cost telemetry could not be saved.'); } };
61
+ ctx.on('llm/stream', async function* (options, next) {
62
+ // Plugin calls made for a session carry no sessionId on the wire; they are charged through the async context.
63
+ const charge = options.sessionId ? undefined : currentCharge();
64
+ const sessionId = options.sessionId ?? charge?.sessionId;
65
+ if (!sessionId || !home) { yield* next(); return; }
66
+ const id = randomUUID(), time = Date.now();
67
+ const purpose = options.purpose ?? charge?.purpose ?? 'agent';
68
+ const recipients = metricRecipients(ctx.agents, sessionId);
69
+ const save = entry => { for (const recipient of recipients) record(recipient, { ...entry, sessionId }); };
70
+ save({ kind: 'start', id, time, provider: options.provider, model: options.model, purpose });
71
+ let usage, firstTokenTime, billed;
72
+ const liveSession = purpose === 'agent' ? ctx.agents.get(sessionId)?.session : undefined;
73
+ const settleRate = liveSession
74
+ ? smoothedRate.begin(liveSession, JSON.stringify([options.provider, options.model, options.reasoningEffort ?? null]))
75
+ : undefined;
76
+ if (liveSession) activeRequests.set(liveSession, (activeRequests.get(liveSession) ?? 0) + 1);
77
+ let completed = false;
78
+ try {
79
+ for await (const chunk of next()) {
80
+ if (chunk.type === 'usage') usage = chunk.usage;
81
+ else if (firstTokenTime === undefined && OUTPUT_CHUNKS.has(chunk.type)) firstTokenTime = Date.now();
82
+ // OpenRouter reports what it charged; the finish of its response carries it.
83
+ if (chunk.type === 'finish' && chunk.replayState?.response?.kind === REPLAY_KIND && Number.isFinite(chunk.replayState.response.cost)) billed = chunk.replayState.response.cost;
84
+ yield chunk;
85
+ }
86
+ completed = true;
87
+ } finally {
88
+ const endTime = Date.now();
89
+ if (liveSession) activeRequests.set(liveSession, (activeRequests.get(liveSession) ?? 1) - 1);
90
+ if (completed) settleRate?.({ start: time, end: endTime, outputTokens: usage?.outputTokens });
91
+ // `time` stays the start (it prices the call); `endTime` and `firstTokenTime` time it.
92
+ save({ kind: 'end', id, time, endTime, ...(firstTokenTime === undefined ? {} : { firstTokenTime }), usage: usage ?? null, ...(billed === undefined
93
+ ? { cost: estimateCost(options.provider, options.model, usage, time), priceVersion: priceVersionFor(options.provider, options.model) }
94
+ : { cost: billed, priceVersion: 'openrouter-billed' }) });
95
+ }
96
+ });
97
+ }
@@ -0,0 +1,84 @@
1
+ import { openRouterPriceVersion, openRouterRates } from '../openrouter/models.mjs';
2
+ import { isOpenRouterRoute } from '../providers/catalog.mjs';
3
+
4
+ // USD per million tokens. Snapshot of the official page opened 2026-09-11.
5
+ // https://api-docs.deepseek.com/quick_start/pricing/
6
+ export const PRICE_SOURCE = 'https://api-docs.deepseek.com/quick_start/pricing/';
7
+ export const PRICE_VERSION = 'deepseek-2026-09-11';
8
+ // OpenRouter calls carry their billed cost; an unbilled one is estimated from the live model listing (plugins/openrouter/models.mjs).
9
+ // Until that table loads, the DeepSeek models keep these list prices from the pinned
10
+ // pi-ai 0.85.1 catalog. OpenRouter bills no peak window. [cache read, input, output].
11
+ export const OPENROUTER_PRICE_VERSION = 'openrouter-pi-ai-0.85.1';
12
+ const OPENROUTER_PRICES = {
13
+ 'deepseek/deepseek-v4-flash': [0.017052, 0.08526, 0.17052],
14
+ 'deepseek/deepseek-v4-pro': [0.074196, 0.890358, 1.780716],
15
+ 'deepseek/deepseek-v4-flash-vision-exp': [0.007, 0.22, 0.66],
16
+ };
17
+
18
+ /** The price table a ledger entry for this route is estimated with. */
19
+ export function priceVersionFor(provider, model) {
20
+ if (!isOpenRouterRoute(provider)) return PRICE_VERSION;
21
+ return model !== undefined && openRouterRates(model) !== undefined ? openRouterPriceVersion() : OPENROUTER_PRICE_VERSION;
22
+ }
23
+
24
+ const promptTokens = usage => (usage.inputTokens ?? 0) + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
25
+
26
+ export function estimateCost(provider, model, usage, time) {
27
+ if (!usage || !Number.isFinite(time)) return null;
28
+ if (isOpenRouterRoute(provider)) {
29
+ // A model that lists no cache-read or cache-write price bills that input at the input rate.
30
+ const live = openRouterRates(model, promptTokens(usage));
31
+ if (live) return charge(usage, [live.cacheRead ?? live.input, live.input, live.output], live.cacheWrite ?? live.input);
32
+ return OPENROUTER_PRICES[model] ? charge(usage, OPENROUTER_PRICES[model]) : null;
33
+ }
34
+ if (provider !== 'deepseek-official') return null;
35
+ const rates = deepSeekRates(model, time);
36
+ if (!rates) return null;
37
+ const cost = charge(usage, rates);
38
+ return cost === null ? null : cost * (isPeak(time) ? 2 : 1);
39
+ }
40
+
41
+ /** DeepSeek list prices [cache read, input, output] at `time`, before the peak multiplier. */
42
+ function deepSeekRates(model, time) {
43
+ // Earlier requests require an older price table; never back-price them at today's rate.
44
+ if (time < Date.UTC(2026, 8, 11)) return undefined;
45
+ const flash = ['deepseek-flash', 'deepseek-v4-flash', 'deepseek-v4-flash-vision-exp'].includes(model)
46
+ || model === 'deepseek-v4-pro' && time >= Date.UTC(2026, 8, 14, 4);
47
+ if (!flash && model !== 'deepseek-v4-pro') return undefined;
48
+ return flash ? [0.003, 0.15, 0.6] : [0.022, 0.66, 1.98];
49
+ }
50
+
51
+ /**
52
+ * Cache-read price over input price for a route, or undefined when the route is unpriced.
53
+ * A model that lists no cache-read price bills cached input at the input rate (1).
54
+ */
55
+ export function cacheReadRatio(provider, model, time = Date.now()) {
56
+ let rates;
57
+ if (isOpenRouterRoute(provider)) {
58
+ const live = openRouterRates(model);
59
+ rates = live ? [live.cacheRead ?? live.input, live.input] : OPENROUTER_PRICES[model];
60
+ } else if (provider === 'deepseek-official') rates = deepSeekRates(model, time);
61
+ return rates && rates[1] > 0 ? rates[0] / rates[1] : undefined;
62
+ }
63
+
64
+ /** Cost in USD; cache writes need a write price, or the call stays unpriced. */
65
+ function charge(usage, [read, input, output], write) {
66
+ const values = [usage.inputTokens, usage.outputTokens, usage.cacheReadTokens ?? 0, usage.cacheWriteTokens ?? 0];
67
+ if (!values.every(n => Number.isFinite(n) && n >= 0) || values[3] !== 0 && !Number.isFinite(write)) return null;
68
+ return (values[0] * input + values[1] * output + values[2] * read + values[3] * (write ?? 0)) / 1e6;
69
+ }
70
+
71
+ /**
72
+ * Peak/off-peak window in UTC: weekdays 01:00-04:00 and 06:00-10:00.
73
+ * @param time - Unix ms timestamp.
74
+ * @returns Whether that instant bills at the peak rate.
75
+ */
76
+ export function isPeak(time) {
77
+ const date = new Date(time), day = date.getUTCDay(), hour = date.getUTCHours();
78
+ return day >= 1 && day <= 5 && (hour >= 1 && hour < 4 || hour >= 6 && hour < 10);
79
+ }
80
+
81
+ /** Footer marker for the billing window: fire for peak, snowflake for off-peak. */
82
+ export function peakEmoji(time) {
83
+ return isPeak(time) ? '🔥' : '❄️';
84
+ }
@@ -0,0 +1,59 @@
1
+ // Each valid completed request halves every older sample's weight.
2
+ const DECAY = 0.5;
3
+
4
+ /**
5
+ * Normalized exponential average of per-request TPS, from final API usage.
6
+ * New samples have weight 1; existing weights multiply by DECAY. Normalizing
7
+ * the startup weights keeps newer requests more influential from sample two.
8
+ * Idle time and unusable samples do not age the history.
9
+ */
10
+ export function createSmoothedRate() {
11
+ const sessions = new WeakMap();
12
+ return {
13
+ begin(session, route) {
14
+ let state = sessions.get(session);
15
+ if (!state || state.route !== route) {
16
+ state = { route, weightedRate: 0, weight: 0 };
17
+ sessions.set(session, state);
18
+ }
19
+ // A late completion from an older model must not contaminate the new one.
20
+ return ({ start, end, outputTokens }) => {
21
+ if (sessions.get(session) !== state || !Number.isFinite(start) || !Number.isFinite(end)
22
+ || end <= start || !Number.isFinite(outputTokens) || outputTokens < 0) return;
23
+ const rate = outputTokens / ((end - start) / 1000);
24
+ if (!Number.isFinite(rate)) return;
25
+ state.weightedRate = state.weightedRate * DECAY + rate;
26
+ state.weight = state.weight * DECAY + 1;
27
+ };
28
+ },
29
+ get(session) {
30
+ const state = sessions.get(session);
31
+ return state?.weight > 0 ? state.weightedRate / state.weight : null;
32
+ },
33
+ };
34
+ }
35
+
36
+ // Output tokens per second of LLM call time: every settled assistant message's
37
+ // exact output tokens over the time from its step's request start to its
38
+ // settlement (first-token latency included, tool execution and user idle time
39
+ // excluded). Only the root agent's own messages count, so parallel children do
40
+ // not inflate the rate; an in-flight call contributes nothing until it settles.
41
+ export function sessionAverageTps(events) {
42
+ const starts = new Map();
43
+ let callMs = 0, outputTokens = 0, known = 0, unknown = false;
44
+ for (const event of events) {
45
+ // Build the key only for the two event types that use it: the array carries every event.
46
+ if (event.type === 'step/start') starts.set(`${event.data?.turn}:${event.data?.step}`, event.time);
47
+ else if (event.type === 'assistant/message') {
48
+ const key = `${event.data?.turn}:${event.data?.step}`;
49
+ const start = starts.get(key);
50
+ starts.delete(key);
51
+ const output = event.data?.usage?.outputTokens;
52
+ if (start === undefined || !Number.isFinite(output) || output < 0) { unknown = true; continue; }
53
+ callMs += Math.max(0, event.time - start);
54
+ outputTokens += output;
55
+ known++;
56
+ }
57
+ }
58
+ return callMs > 0 && known > 0 && !unknown ? outputTokens / (callMs / 1000) : null;
59
+ }
@@ -0,0 +1,75 @@
1
+ import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ // The render path reads this once a second per session; keep the last few ledgers and
5
+ // append to the parsed rows instead of re-reading and re-parsing the whole jsonl.
6
+ const MAX_SESSIONS = 16;
7
+ const cache = new Map();
8
+ const parse = (line, onCorrupt) => { try { return [JSON.parse(line)]; } catch { onCorrupt(); return []; } };
9
+ const readsAsJson = text => { try { JSON.parse(text); return true; } catch { return false; } };
10
+ export const ledgerPath = (home, id) => join(home, 'session-metrics', createHash('sha256').update(id).digest('hex') + '.jsonl');
11
+ export function appendMetric(home, id, entry) {
12
+ const path = ledgerPath(home, id);
13
+ mkdirSync(join(home, 'session-metrics'), { recursive: true, mode: 0o700 });
14
+ appendFileSync(path, JSON.stringify(entry) + '\n', { mode: 0o600, flush: true });
15
+ }
16
+ export function readMetrics(home, id) {
17
+ const path = ledgerPath(home, id);
18
+ try {
19
+ const st = statSync(path), key = `${st.mtimeMs}:${st.size}`;
20
+ const entry = cache.get(path);
21
+ if (entry?.key === key) return entry.value;
22
+ // Touching a key refreshes its recency; the oldest ledger is dropped past the cap.
23
+ if (entry) cache.delete(path);
24
+ let corrupt = false;
25
+ const onCorrupt = () => { corrupt = true; };
26
+ let value, offset = 0, pending = false;
27
+ if (entry && st.ino === entry.ino && st.dev === entry.dev && st.size > entry.size) {
28
+ // Append-only ledger: read just the bytes written since the last read (offset is a newline boundary).
29
+ const fd = openSync(path, 'r');
30
+ try {
31
+ const buffer = Buffer.allocUnsafe(st.size - entry.offset);
32
+ let filled = 0;
33
+ while (filled < buffer.length) {
34
+ const read = readSync(fd, buffer, filled, buffer.length - filled, entry.offset + filled);
35
+ if (read <= 0) break;
36
+ filled += read;
37
+ }
38
+ // A short read must never read as "the writer stopped here": fall back to the full
39
+ // parse below rather than caching an offset that would drop the unread bytes forever.
40
+ if (filled < buffer.length) throw Object.assign(new Error('short ledger read'), { code: 'ESHORTREAD' });
41
+ const tail = buffer.toString('utf8');
42
+ const lastNewline = tail.lastIndexOf('\n') + 1;
43
+ let complete = tail.slice(0, lastNewline);
44
+ const pendingLine = tail.slice(lastNewline);
45
+ // A fragment without a newline is either a torn write or a complete row this reader is
46
+ // simply early for; keep it only when it parses, and re-read it next time either way.
47
+ pending = pendingLine !== '' && readsAsJson(pendingLine);
48
+ if (pending) complete += pendingLine + '\n';
49
+ offset = entry.offset + Buffer.byteLength(tail.slice(0, lastNewline));
50
+ value = { rows: [...(entry.pending ? entry.value.rows.slice(0, -1) : entry.value.rows), ...complete.split('\n').filter(Boolean).flatMap(line => parse(line, onCorrupt))], corrupt: entry.value.corrupt || corrupt };
51
+ } finally { closeSync(fd); }
52
+ } else {
53
+ const raw = readFileSync(path);
54
+ const lastNewline = raw.lastIndexOf(0x0a) + 1;
55
+ let complete = raw.subarray(0, lastNewline);
56
+ const pendingLine = raw.subarray(lastNewline).toString('utf8');
57
+ pending = pendingLine !== '' && readsAsJson(pendingLine);
58
+ if (pending) complete = Buffer.concat([complete, Buffer.from(pendingLine + '\n')]);
59
+ offset = lastNewline;
60
+ value = { rows: complete.toString('utf8').split('\n').filter(Boolean).flatMap(line => parse(line, onCorrupt)), corrupt };
61
+ }
62
+ cache.set(path, { key, offset, value, pending, size: st.size, ino: st.ino, dev: st.dev });
63
+ while (cache.size > MAX_SESSIONS) cache.delete(cache.keys().next().value);
64
+ return value;
65
+ } catch (e) {
66
+ cache.delete(path);
67
+ if (e.code === 'ESHORTREAD') {
68
+ // Read it the simple way once; the next call starts from a fresh offset.
69
+ let corrupt = false;
70
+ const rows = readFileSync(path, 'utf8').split('\n').filter(Boolean).flatMap(line => parse(line, () => { corrupt = true; }));
71
+ return { rows, corrupt };
72
+ }
73
+ return { rows: [], corrupt: e.code !== 'ENOENT' };
74
+ }
75
+ }