@jingyi0605/codingns 0.2.0 → 0.3.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 (328) hide show
  1. package/README.md +44 -0
  2. package/bin/codingns.mjs +918 -55
  3. package/dist/public/assets/{TerminalPage-BlbQuWi1.js → TerminalPage-Dfw1QUqW.js} +19 -19
  4. package/dist/public/assets/index-DR2rPNi7.css +1 -0
  5. package/dist/public/assets/index-DTOruahn.js +114 -0
  6. package/dist/public/index.html +2 -2
  7. package/dist/server/config/env.d.ts +3 -0
  8. package/dist/server/config/env.js +35 -3
  9. package/dist/server/config/env.js.map +1 -1
  10. package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +89 -0
  11. package/dist/server/modules/assistant-capability/assistant-capability-controller.js +138 -0
  12. package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -0
  13. package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +115 -0
  14. package/dist/server/modules/assistant-capability/assistant-capability-service.js +241 -0
  15. package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -0
  16. package/dist/server/modules/butler/butler-control-session-service.d.ts +3 -1
  17. package/dist/server/modules/butler/butler-control-session-service.js +96 -33
  18. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  19. package/dist/server/modules/butler/butler-follow-up-scheduler.d.ts +9 -0
  20. package/dist/server/modules/butler/butler-follow-up-scheduler.js +47 -11
  21. package/dist/server/modules/butler/butler-follow-up-scheduler.js.map +1 -1
  22. package/dist/server/modules/butler/butler-follow-up-service.d.ts +7 -1
  23. package/dist/server/modules/butler/butler-follow-up-service.js +10 -0
  24. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
  25. package/dist/server/modules/butler/butler-session-service.d.ts +3 -1
  26. package/dist/server/modules/butler/butler-session-service.js +82 -16
  27. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  28. package/dist/server/modules/butler/butler-session-summary-service.d.ts +8 -1
  29. package/dist/server/modules/butler/butler-session-summary-service.js +34 -7
  30. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
  31. package/dist/server/modules/butler/context-aggregator.js +44 -13
  32. package/dist/server/modules/butler/context-aggregator.js.map +1 -1
  33. package/dist/server/modules/butler/patrol-scheduler.d.ts +9 -0
  34. package/dist/server/modules/butler/patrol-scheduler.js +63 -9
  35. package/dist/server/modules/butler/patrol-scheduler.js.map +1 -1
  36. package/dist/server/modules/butler/session-summary-scheduler.d.ts +9 -0
  37. package/dist/server/modules/butler/session-summary-scheduler.js +47 -11
  38. package/dist/server/modules/butler/session-summary-scheduler.js.map +1 -1
  39. package/dist/server/modules/debug-target/debug-runtime-reconciliation-scheduler.d.ts +38 -0
  40. package/dist/server/modules/debug-target/debug-runtime-reconciliation-scheduler.js +99 -0
  41. package/dist/server/modules/debug-target/debug-runtime-reconciliation-scheduler.js.map +1 -0
  42. package/dist/server/modules/debug-target/debug-target-controller.d.ts +70 -0
  43. package/dist/server/modules/debug-target/debug-target-controller.js +113 -0
  44. package/dist/server/modules/debug-target/debug-target-controller.js.map +1 -0
  45. package/dist/server/modules/debug-target/debug-target-service.d.ts +105 -0
  46. package/dist/server/modules/debug-target/debug-target-service.js +1644 -0
  47. package/dist/server/modules/debug-target/debug-target-service.js.map +1 -0
  48. package/dist/server/modules/debug-target/framework-compatibility-matrix.d.ts +4 -0
  49. package/dist/server/modules/debug-target/framework-compatibility-matrix.js +45 -0
  50. package/dist/server/modules/debug-target/framework-compatibility-matrix.js.map +1 -0
  51. package/dist/server/modules/debug-target/launch-adapter-registry.d.ts +25 -0
  52. package/dist/server/modules/debug-target/launch-adapter-registry.js +445 -0
  53. package/dist/server/modules/debug-target/launch-adapter-registry.js.map +1 -0
  54. package/dist/server/modules/file/file-constants.d.ts +1 -0
  55. package/dist/server/modules/file/file-constants.js +1 -0
  56. package/dist/server/modules/file/file-constants.js.map +1 -1
  57. package/dist/server/modules/file/file-content-service.d.ts +2 -1
  58. package/dist/server/modules/file/file-content-service.js +53 -0
  59. package/dist/server/modules/file/file-content-service.js.map +1 -1
  60. package/dist/server/modules/file/file-controller.js +12 -3
  61. package/dist/server/modules/file/file-controller.js.map +1 -1
  62. package/dist/server/modules/file/file-preview-link-service.js +6 -37
  63. package/dist/server/modules/file/file-preview-link-service.js.map +1 -1
  64. package/dist/server/modules/file/file-preview-service.d.ts +6 -12
  65. package/dist/server/modules/file/file-preview-service.js +114 -28
  66. package/dist/server/modules/file/file-preview-service.js.map +1 -1
  67. package/dist/server/modules/file/file-preview-types.d.ts +37 -0
  68. package/dist/server/modules/file/file-preview-types.js +84 -0
  69. package/dist/server/modules/file/file-preview-types.js.map +1 -0
  70. package/dist/server/modules/git/commit-orchestrator.d.ts +4 -1
  71. package/dist/server/modules/git/commit-orchestrator.js +18 -1
  72. package/dist/server/modules/git/commit-orchestrator.js.map +1 -1
  73. package/dist/server/modules/git/git-auth.d.ts +25 -0
  74. package/dist/server/modules/git/git-auth.js +88 -0
  75. package/dist/server/modules/git/git-auth.js.map +1 -0
  76. package/dist/server/modules/git/git-controller.d.ts +12 -0
  77. package/dist/server/modules/git/git-controller.js +18 -1
  78. package/dist/server/modules/git/git-controller.js.map +1 -1
  79. package/dist/server/modules/git/git-read-service.d.ts +3 -1
  80. package/dist/server/modules/git/git-read-service.js +119 -2
  81. package/dist/server/modules/git/git-read-service.js.map +1 -1
  82. package/dist/server/modules/git/git-remote-credential-service.d.ts +9 -0
  83. package/dist/server/modules/git/git-remote-credential-service.js +76 -0
  84. package/dist/server/modules/git/git-remote-credential-service.js.map +1 -0
  85. package/dist/server/modules/git/git-write-service.d.ts +5 -2
  86. package/dist/server/modules/git/git-write-service.js +33 -17
  87. package/dist/server/modules/git/git-write-service.js.map +1 -1
  88. package/dist/server/modules/git/types.d.ts +26 -0
  89. package/dist/server/modules/git/workspace-repo-guard.js +3 -2
  90. package/dist/server/modules/git/workspace-repo-guard.js.map +1 -1
  91. package/dist/server/modules/provider/codex-model-options.d.ts +3 -1
  92. package/dist/server/modules/provider/codex-model-options.js +4 -1
  93. package/dist/server/modules/provider/codex-model-options.js.map +1 -1
  94. package/dist/server/modules/provider/opencode-model-options.d.ts +3 -1
  95. package/dist/server/modules/provider/opencode-model-options.js +5 -1
  96. package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
  97. package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +24 -0
  98. package/dist/server/modules/provider/provider-discovery-helper-client.js +14 -0
  99. package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
  100. package/dist/server/modules/provider/provider-discovery-helper-process.js +54 -0
  101. package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
  102. package/dist/server/modules/sessions/session-activity-authority-service.js +13 -1
  103. package/dist/server/modules/sessions/session-activity-authority-service.js.map +1 -1
  104. package/dist/server/modules/sessions/session-activity-inspector.js +21 -5
  105. package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
  106. package/dist/server/modules/sessions/session-controller.d.ts +5 -0
  107. package/dist/server/modules/sessions/session-controller.js +16 -0
  108. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  109. package/dist/server/modules/sessions/session-history-service.d.ts +27 -7
  110. package/dist/server/modules/sessions/session-history-service.js +439 -81
  111. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  112. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +2 -1
  113. package/dist/server/modules/sessions/session-live-runtime-service.js +12 -0
  114. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  115. package/dist/server/modules/skills/skill-controller.d.ts +23 -0
  116. package/dist/server/modules/skills/skill-controller.js +35 -0
  117. package/dist/server/modules/skills/skill-controller.js.map +1 -0
  118. package/dist/server/modules/skills/skill-manager-service.d.ts +86 -0
  119. package/dist/server/modules/skills/skill-manager-service.js +557 -0
  120. package/dist/server/modules/skills/skill-manager-service.js.map +1 -0
  121. package/dist/server/modules/skills/skill-reconciler.d.ts +21 -0
  122. package/dist/server/modules/skills/skill-reconciler.js +99 -0
  123. package/dist/server/modules/skills/skill-reconciler.js.map +1 -0
  124. package/dist/server/modules/skills/skill-sync-planner.d.ts +8 -0
  125. package/dist/server/modules/skills/skill-sync-planner.js +20 -0
  126. package/dist/server/modules/skills/skill-sync-planner.js.map +1 -0
  127. package/dist/server/modules/skills/skill-target-adapter.d.ts +34 -0
  128. package/dist/server/modules/skills/skill-target-adapter.js +65 -0
  129. package/dist/server/modules/skills/skill-target-adapter.js.map +1 -0
  130. package/dist/server/modules/tailscale/tailscale-controller.d.ts +15 -0
  131. package/dist/server/modules/tailscale/tailscale-controller.js +33 -0
  132. package/dist/server/modules/tailscale/tailscale-controller.js.map +1 -0
  133. package/dist/server/modules/tailscale/tailscale-helper-client.d.ts +41 -0
  134. package/dist/server/modules/tailscale/tailscale-helper-client.js +135 -0
  135. package/dist/server/modules/tailscale/tailscale-helper-client.js.map +1 -0
  136. package/dist/server/modules/tailscale/tailscale-helper-process.d.ts +1 -0
  137. package/dist/server/modules/tailscale/tailscale-helper-process.js +327 -0
  138. package/dist/server/modules/tailscale/tailscale-helper-process.js.map +1 -0
  139. package/dist/server/modules/tailscale/tailscale-manager.d.ts +41 -0
  140. package/dist/server/modules/tailscale/tailscale-manager.js +259 -0
  141. package/dist/server/modules/tailscale/tailscale-manager.js.map +1 -0
  142. package/dist/server/modules/tailscale/tailscale-service.d.ts +43 -0
  143. package/dist/server/modules/tailscale/tailscale-service.js +201 -0
  144. package/dist/server/modules/tailscale/tailscale-service.js.map +1 -0
  145. package/dist/server/modules/tasks/event-loop-monitor.d.ts +21 -0
  146. package/dist/server/modules/tasks/event-loop-monitor.js +64 -0
  147. package/dist/server/modules/tasks/event-loop-monitor.js.map +1 -0
  148. package/dist/server/modules/tasks/observability-controller.d.ts +30 -0
  149. package/dist/server/modules/tasks/observability-controller.js +44 -0
  150. package/dist/server/modules/tasks/observability-controller.js.map +1 -0
  151. package/dist/server/modules/tasks/observability-service.d.ts +32 -0
  152. package/dist/server/modules/tasks/observability-service.js +104 -0
  153. package/dist/server/modules/tasks/observability-service.js.map +1 -0
  154. package/dist/server/modules/tasks/scheduler-metrics.d.ts +41 -0
  155. package/dist/server/modules/tasks/scheduler-metrics.js +92 -0
  156. package/dist/server/modules/tasks/scheduler-metrics.js.map +1 -0
  157. package/dist/server/modules/tasks/task-activity-log.d.ts +39 -0
  158. package/dist/server/modules/tasks/task-activity-log.js +43 -0
  159. package/dist/server/modules/tasks/task-activity-log.js.map +1 -0
  160. package/dist/server/modules/tasks/task-helper-client.d.ts +11 -0
  161. package/dist/server/modules/tasks/task-helper-client.js +132 -0
  162. package/dist/server/modules/tasks/task-helper-client.js.map +1 -0
  163. package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +16 -0
  164. package/dist/server/modules/tasks/task-helper-process-handlers.js +14 -0
  165. package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -0
  166. package/dist/server/modules/tasks/task-helper-process.d.ts +1 -0
  167. package/dist/server/modules/tasks/task-helper-process.js +49 -0
  168. package/dist/server/modules/tasks/task-helper-process.js.map +1 -0
  169. package/dist/server/modules/tasks/task-lane-executors.d.ts +2 -0
  170. package/dist/server/modules/tasks/task-lane-executors.js +15 -0
  171. package/dist/server/modules/tasks/task-lane-executors.js.map +1 -0
  172. package/dist/server/modules/tasks/task-manager.d.ts +15 -0
  173. package/dist/server/modules/tasks/task-manager.js +36 -0
  174. package/dist/server/modules/tasks/task-manager.js.map +1 -0
  175. package/dist/server/modules/tasks/task-metrics.d.ts +9 -0
  176. package/dist/server/modules/tasks/task-metrics.js +81 -0
  177. package/dist/server/modules/tasks/task-metrics.js.map +1 -0
  178. package/dist/server/modules/tasks/task-registry.d.ts +7 -0
  179. package/dist/server/modules/tasks/task-registry.js +21 -0
  180. package/dist/server/modules/tasks/task-registry.js.map +1 -0
  181. package/dist/server/modules/tasks/task-scheduler.d.ts +31 -0
  182. package/dist/server/modules/tasks/task-scheduler.js +473 -0
  183. package/dist/server/modules/tasks/task-scheduler.js.map +1 -0
  184. package/dist/server/modules/tasks/task-types.d.ts +106 -0
  185. package/dist/server/modules/tasks/task-types.js +23 -0
  186. package/dist/server/modules/tasks/task-types.js.map +1 -0
  187. package/dist/server/modules/terminal/command-template-service.d.ts +4 -0
  188. package/dist/server/modules/terminal/command-template-service.js +5 -3
  189. package/dist/server/modules/terminal/command-template-service.js.map +1 -1
  190. package/dist/server/modules/terminal/runtime/terminal-log-spooler.d.ts +7 -3
  191. package/dist/server/modules/terminal/runtime/terminal-log-spooler.js +95 -15
  192. package/dist/server/modules/terminal/runtime/terminal-log-spooler.js.map +1 -1
  193. package/dist/server/modules/terminal/runtime/terminal-log-writer-client.d.ts +21 -0
  194. package/dist/server/modules/terminal/runtime/terminal-log-writer-client.js +144 -0
  195. package/dist/server/modules/terminal/runtime/terminal-log-writer-client.js.map +1 -0
  196. package/dist/server/modules/terminal/runtime/terminal-log-writer-process.d.ts +1 -0
  197. package/dist/server/modules/terminal/runtime/terminal-log-writer-process.js +187 -0
  198. package/dist/server/modules/terminal/runtime/terminal-log-writer-process.js.map +1 -0
  199. package/dist/server/modules/terminal/terminal-service.d.ts +12 -0
  200. package/dist/server/modules/terminal/terminal-service.js +34 -17
  201. package/dist/server/modules/terminal/terminal-service.js.map +1 -1
  202. package/dist/server/modules/workbench/workbench-service.d.ts +23 -2
  203. package/dist/server/modules/workbench/workbench-service.js +126 -15
  204. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  205. package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +5 -1
  206. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +88 -19
  207. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
  208. package/dist/server/modules/workspace/workspace-code-composition.d.ts +2 -0
  209. package/dist/server/modules/workspace/workspace-code-composition.js +154 -0
  210. package/dist/server/modules/workspace/workspace-code-composition.js.map +1 -0
  211. package/dist/server/modules/workspace/workspace-controller.d.ts +14 -0
  212. package/dist/server/modules/workspace/workspace-controller.js +19 -0
  213. package/dist/server/modules/workspace/workspace-controller.js.map +1 -1
  214. package/dist/server/modules/workspace/workspace-service.d.ts +21 -14
  215. package/dist/server/modules/workspace/workspace-service.js +183 -234
  216. package/dist/server/modules/workspace/workspace-service.js.map +1 -1
  217. package/dist/server/modules/worktree/worktree-cleanup-service.d.ts +35 -0
  218. package/dist/server/modules/worktree/worktree-cleanup-service.js +210 -0
  219. package/dist/server/modules/worktree/worktree-cleanup-service.js.map +1 -0
  220. package/dist/server/modules/worktree/worktree-controller.d.ts +44 -0
  221. package/dist/server/modules/worktree/worktree-controller.js +40 -0
  222. package/dist/server/modules/worktree/worktree-controller.js.map +1 -0
  223. package/dist/server/modules/worktree/worktree-manager.d.ts +34 -0
  224. package/dist/server/modules/worktree/worktree-manager.js +292 -0
  225. package/dist/server/modules/worktree/worktree-manager.js.map +1 -0
  226. package/dist/server/modules/worktree/worktree-merge-service.d.ts +52 -0
  227. package/dist/server/modules/worktree/worktree-merge-service.js +293 -0
  228. package/dist/server/modules/worktree/worktree-merge-service.js.map +1 -0
  229. package/dist/server/modules/worktree/worktree-sync-service.d.ts +23 -0
  230. package/dist/server/modules/worktree/worktree-sync-service.js +166 -0
  231. package/dist/server/modules/worktree/worktree-sync-service.js.map +1 -0
  232. package/dist/server/routes/assistant.d.ts +3 -0
  233. package/dist/server/routes/assistant.js +15 -0
  234. package/dist/server/routes/assistant.js.map +1 -0
  235. package/dist/server/routes/debug-targets.d.ts +3 -0
  236. package/dist/server/routes/debug-targets.js +15 -0
  237. package/dist/server/routes/debug-targets.js.map +1 -0
  238. package/dist/server/routes/git.js +2 -0
  239. package/dist/server/routes/git.js.map +1 -1
  240. package/dist/server/routes/observability.d.ts +3 -0
  241. package/dist/server/routes/observability.js +7 -0
  242. package/dist/server/routes/observability.js.map +1 -0
  243. package/dist/server/routes/skills.d.ts +3 -0
  244. package/dist/server/routes/skills.js +7 -0
  245. package/dist/server/routes/skills.js.map +1 -0
  246. package/dist/server/routes/system.d.ts +3 -0
  247. package/dist/server/routes/system.js +9 -0
  248. package/dist/server/routes/system.js.map +1 -0
  249. package/dist/server/routes/workspaces.js +2 -0
  250. package/dist/server/routes/workspaces.js.map +1 -1
  251. package/dist/server/routes/worktrees.d.ts +3 -0
  252. package/dist/server/routes/worktrees.js +8 -0
  253. package/dist/server/routes/worktrees.js.map +1 -0
  254. package/dist/server/server/create-server.d.ts +46 -0
  255. package/dist/server/server/create-server.js +141 -12
  256. package/dist/server/server/create-server.js.map +1 -1
  257. package/dist/server/shared/utils/command-availability.d.ts +1 -0
  258. package/dist/server/shared/utils/command-availability.js +26 -3
  259. package/dist/server/shared/utils/command-availability.js.map +1 -1
  260. package/dist/server/shared/utils/secret-box.d.ts +2 -0
  261. package/dist/server/shared/utils/secret-box.js +29 -0
  262. package/dist/server/shared/utils/secret-box.js.map +1 -0
  263. package/dist/server/shared/utils/terminal-debug-log.js +5 -3
  264. package/dist/server/shared/utils/terminal-debug-log.js.map +1 -1
  265. package/dist/server/storage/repositories/ai-fallback-edit-repository.d.ts +11 -0
  266. package/dist/server/storage/repositories/ai-fallback-edit-repository.js +118 -0
  267. package/dist/server/storage/repositories/ai-fallback-edit-repository.js.map +1 -0
  268. package/dist/server/storage/repositories/debug-runtime-session-repository.d.ts +11 -0
  269. package/dist/server/storage/repositories/debug-runtime-session-repository.js +100 -0
  270. package/dist/server/storage/repositories/debug-runtime-session-repository.js.map +1 -0
  271. package/dist/server/storage/repositories/debug-service-repository.d.ts +9 -0
  272. package/dist/server/storage/repositories/debug-service-repository.js +99 -0
  273. package/dist/server/storage/repositories/debug-service-repository.js.map +1 -0
  274. package/dist/server/storage/repositories/debug-target-repository.d.ts +11 -0
  275. package/dist/server/storage/repositories/debug-target-repository.js +100 -0
  276. package/dist/server/storage/repositories/debug-target-repository.js.map +1 -0
  277. package/dist/server/storage/repositories/framework-analysis-result-repository.d.ts +9 -0
  278. package/dist/server/storage/repositories/framework-analysis-result-repository.js +98 -0
  279. package/dist/server/storage/repositories/framework-analysis-result-repository.js.map +1 -0
  280. package/dist/server/storage/repositories/git-remote-credential-repository.d.ts +9 -0
  281. package/dist/server/storage/repositories/git-remote-credential-repository.js +51 -0
  282. package/dist/server/storage/repositories/git-remote-credential-repository.js.map +1 -0
  283. package/dist/server/storage/repositories/instance-tailscale-repository.d.ts +10 -0
  284. package/dist/server/storage/repositories/instance-tailscale-repository.js +112 -0
  285. package/dist/server/storage/repositories/instance-tailscale-repository.js.map +1 -0
  286. package/dist/server/storage/repositories/managed-skill-repository.d.ts +11 -0
  287. package/dist/server/storage/repositories/managed-skill-repository.js +102 -0
  288. package/dist/server/storage/repositories/managed-skill-repository.js.map +1 -0
  289. package/dist/server/storage/repositories/port-lease-repository.d.ts +12 -0
  290. package/dist/server/storage/repositories/port-lease-repository.js +124 -0
  291. package/dist/server/storage/repositories/port-lease-repository.js.map +1 -0
  292. package/dist/server/storage/repositories/runtime-binding-repository.d.ts +10 -0
  293. package/dist/server/storage/repositories/runtime-binding-repository.js +89 -0
  294. package/dist/server/storage/repositories/runtime-binding-repository.js.map +1 -0
  295. package/dist/server/storage/repositories/skill-target-binding-repository.d.ts +10 -0
  296. package/dist/server/storage/repositories/skill-target-binding-repository.js +77 -0
  297. package/dist/server/storage/repositories/skill-target-binding-repository.js.map +1 -0
  298. package/dist/server/storage/repositories/terminal-command-template-repository.js +77 -4
  299. package/dist/server/storage/repositories/terminal-command-template-repository.js.map +1 -1
  300. package/dist/server/storage/repositories/terminal-instance-repository.js +89 -7
  301. package/dist/server/storage/repositories/terminal-instance-repository.js.map +1 -1
  302. package/dist/server/storage/repositories/workspace-navigation-state-repository.d.ts +9 -0
  303. package/dist/server/storage/repositories/workspace-navigation-state-repository.js +49 -0
  304. package/dist/server/storage/repositories/workspace-navigation-state-repository.js.map +1 -0
  305. package/dist/server/storage/repositories/workspace-repository.d.ts +7 -1
  306. package/dist/server/storage/repositories/workspace-repository.js +32 -8
  307. package/dist/server/storage/repositories/workspace-repository.js.map +1 -1
  308. package/dist/server/storage/repositories/workspace-worktree-repository.d.ts +13 -0
  309. package/dist/server/storage/repositories/workspace-worktree-repository.js +158 -0
  310. package/dist/server/storage/repositories/workspace-worktree-repository.js.map +1 -0
  311. package/dist/server/storage/sqlite/client.js +311 -0
  312. package/dist/server/storage/sqlite/client.js.map +1 -1
  313. package/dist/server/storage/sqlite/schema.sql +303 -0
  314. package/dist/server/types/domain.d.ts +303 -0
  315. package/dist/server/ws/workbench-ws-hub.js +33 -9
  316. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  317. package/dist/server/ws/ws-server.js +4 -4
  318. package/dist/server/ws/ws-server.js.map +1 -1
  319. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +18 -6
  320. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  321. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +14 -2
  322. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  323. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +25 -1
  324. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  325. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +6 -0
  326. package/package.json +1 -1
  327. package/dist/public/assets/index-1VIm8lVL.css +0 -1
  328. package/dist/public/assets/index-Dti93O2S.js +0 -109
@@ -4,6 +4,7 @@ import { AppError } from "../../shared/errors/app-error.js";
4
4
  import { hashContent } from "../../shared/utils/hash.js";
5
5
  import { createId } from "../../shared/utils/id.js";
6
6
  import { logPerformance } from "../../shared/utils/perf-log.js";
7
+ import { isTerminalDebugEnabled, logTerminalDebug, terminalDebugNowMs } from "../../shared/utils/terminal-debug-log.js";
7
8
  import { nowIso } from "../../shared/utils/time.js";
8
9
  import { isCommandAvailable } from "../../shared/utils/command-availability.js";
9
10
  import { inspectSessionActivity } from "./session-activity-inspector.js";
@@ -11,8 +12,11 @@ import { SessionActivityAuthorityService } from "./session-activity-authority-se
11
12
  import { mapSessionProviderError } from "./session-provider-error-mapper.js";
12
13
  import { SessionForkRepository } from "../../storage/repositories/session-fork-repository.js";
13
14
  import { enrichClaudeCapabilities } from "../provider/claude-model-options.js";
14
- import { CodexModelOptionsService, enrichCodexCapabilities } from "../provider/codex-model-options.js";
15
- import { OpenCodeModelOptionsService, enrichOpenCodeCapabilities } from "../provider/opencode-model-options.js";
15
+ import { CodexModelOptionsService, createFallbackCodexModelOptions, enrichCodexCapabilities } from "../provider/codex-model-options.js";
16
+ import { OpenCodeModelOptionsService, createFallbackOpenCodeModelOptions, enrichOpenCodeCapabilities } from "../provider/opencode-model-options.js";
17
+ import { ProviderDiscoveryHelperClient } from "../provider/provider-discovery-helper-client.js";
18
+ import { createTaskManager } from "../tasks/task-manager.js";
19
+ import { HOST_TASK_TYPES } from "../tasks/task-types.js";
16
20
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
17
21
  const RECONSTRUCTED_FORK_TARGET_PROVIDERS = new Set(["codex", "claude-code", "opencode"]);
18
22
  const FORK_RECONSTRUCTION_PAGE_SIZE = 200;
@@ -25,6 +29,9 @@ const SESSION_START_DEFERRED_PROVIDERS = new Set([
25
29
  "kimi"
26
30
  ]);
27
31
  const MUTABLE_HISTORY_TAIL_REFRESH_INTERVAL_MS = 1_200;
32
+ const WORKSPACE_DISCOVERY_BACKGROUND_MAX_AGE_MS = 15_000;
33
+ const PROVIDER_CAPABILITY_CACHE_MAX_AGE_MS = 5_000;
34
+ const WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE = 25;
28
35
  export class SessionHistoryService {
29
36
  db;
30
37
  workspaceRepository;
@@ -45,11 +52,15 @@ export class SessionHistoryService {
45
52
  openCodeModelOptionsService;
46
53
  providerCliCommandPaths;
47
54
  providerCliAvailability;
55
+ providerDiscoveryHelperClient = new ProviderDiscoveryHelperClient();
56
+ providerSessionDiscoveryConfig;
57
+ taskManager;
48
58
  workspaceDiscoveryStatuses = new Map();
49
- workspaceDiscoveryInflight = new Map();
50
59
  workspaceStateRefreshInflight = new Map();
60
+ providerCapabilityCache = new Map();
61
+ liveActivityObservationResolvers = new Set();
51
62
  workspaceSessionRelations = new Map();
52
- constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}) {
63
+ constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}, taskManager = createTaskManager()) {
53
64
  this.db = db;
54
65
  this.workspaceRepository = workspaceRepository;
55
66
  this.sessionBindingRepository = sessionBindingRepository;
@@ -61,6 +72,7 @@ export class SessionHistoryService {
61
72
  this.sessionMessageOriginRepository = sessionMessageOriginRepository;
62
73
  this.sessionActivityAuthorityService = sessionActivityAuthorityService;
63
74
  this.sessionForkRepository = sessionForkRepository ?? new SessionForkRepository(db);
75
+ this.taskManager = taskManager;
64
76
  this.claudeCodeHomeDir = config.claudeCodeHomeDir;
65
77
  this.providerCliCommandPaths = {
66
78
  "claude-code": process.platform === "win32" ? "claude.cmd" : "claude",
@@ -70,6 +82,18 @@ export class SessionHistoryService {
70
82
  };
71
83
  // CLI 是否可用只在 Host 启动时探测一次;后续统一读缓存,更新 CLI 后重启 Host 生效。
72
84
  this.providerCliAvailability = buildProviderCliAvailabilitySnapshot(this.providerCliCommandPaths);
85
+ this.providerSessionDiscoveryConfig = {
86
+ claudeCodeHomeDir: config.claudeCodeHomeDir,
87
+ codexCliPath: config.codexCliPath,
88
+ codexHomeDir: config.codexHomeDir,
89
+ geminiCliPath: config.geminiCliPath,
90
+ geminiHomeDir: config.geminiHomeDir,
91
+ kimiDefaultModel: config.kimiDefaultModel,
92
+ kimiHomeDir: config.kimiHomeDir,
93
+ opencodeBaseUrl: config.opencodeBaseUrl,
94
+ opencodeDataDir: config.opencodeDataDir,
95
+ opencodeDbPath: config.opencodeDbPath
96
+ };
73
97
  this.providerRegistry = new ProviderRegistry([
74
98
  new ClaudeCodeAdapter({ homeDir: config.claudeCodeHomeDir }),
75
99
  new CodexAdapter({
@@ -102,6 +126,45 @@ export class SessionHistoryService {
102
126
  baseUrlResolver: config.opencodeBaseUrlResolver?.resolve.bind(config.opencodeBaseUrlResolver),
103
127
  commandPath: config.opencodeCliPath
104
128
  });
129
+ this.registerBackgroundTasks();
130
+ }
131
+ observeBackgroundTaskMetrics() {
132
+ return this.taskManager.observe();
133
+ }
134
+ registerLiveActivityObservationResolver(resolver) {
135
+ this.liveActivityObservationResolvers.add(resolver);
136
+ let closed = false;
137
+ return {
138
+ close: () => {
139
+ if (closed) {
140
+ return;
141
+ }
142
+ closed = true;
143
+ this.liveActivityObservationResolvers.delete(resolver);
144
+ }
145
+ };
146
+ }
147
+ registerBackgroundTasks() {
148
+ if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscovery)) {
149
+ this.taskManager.register({
150
+ taskType: HOST_TASK_TYPES.workspaceDiscovery,
151
+ executionLane: "helper_process",
152
+ run: async ({ workspaceId, userId, refreshStateMode }) => this.runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode)
153
+ });
154
+ }
155
+ if (!this.taskManager.has(HOST_TASK_TYPES.providerCapabilityRefresh)) {
156
+ this.taskManager.register({
157
+ taskType: HOST_TASK_TYPES.providerCapabilityRefresh,
158
+ executionLane: "external_process",
159
+ run: async ({ capabilities, workspacePath }) => {
160
+ const value = await this.enrichProviderCapabilities(capabilities, workspacePath);
161
+ this.providerCapabilityCache.set(buildProviderCapabilityCacheKey(capabilities.provider, workspacePath), {
162
+ refreshedAt: Date.now(),
163
+ value
164
+ });
165
+ }
166
+ });
167
+ }
105
168
  }
106
169
  async discoverWorkspaceSessions(workspaceId, userId, options) {
107
170
  const maxAgeMs = options?.maxAgeMs ?? 0;
@@ -112,17 +175,47 @@ export class SessionHistoryService {
112
175
  discoveryStatus?.isComplete === true &&
113
176
  maxAgeMs > 0 &&
114
177
  Date.now() - lastRefreshedAt <= maxAgeMs) {
178
+ this.taskManager.recordCacheHit(HOST_TASK_TYPES.workspaceDiscovery, workspaceId);
115
179
  return this.listWorkspaceSessions(workspaceId, userId);
116
180
  }
117
- const inflight = this.workspaceDiscoveryInflight.get(workspaceId);
118
- if (inflight) {
119
- return inflight;
181
+ return this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscovery, {
182
+ key: workspaceId,
183
+ source: "session_history.discover_workspace_sessions",
184
+ input: {
185
+ workspaceId,
186
+ userId,
187
+ refreshStateMode: options?.refreshStateMode ?? "inline"
188
+ }
189
+ }).promise;
190
+ }
191
+ requestWorkspaceDiscovery(workspaceId, userId, options) {
192
+ const maxAgeMs = options?.maxAgeMs ?? WORKSPACE_DISCOVERY_BACKGROUND_MAX_AGE_MS;
193
+ const force = options?.force ?? false;
194
+ if (!force && !this.needsWorkspaceDiscovery(workspaceId, maxAgeMs)) {
195
+ return;
196
+ }
197
+ const task = this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscovery, {
198
+ key: workspaceId,
199
+ source: "session_history.request_workspace_discovery",
200
+ input: {
201
+ workspaceId,
202
+ userId,
203
+ refreshStateMode: options?.refreshStateMode ?? "deferred"
204
+ }
205
+ });
206
+ if (task.deduped) {
207
+ return;
120
208
  }
121
- const task = this.runDiscoverWorkspaceSessions(workspaceId, userId, options?.refreshStateMode ?? "inline").finally(() => {
122
- this.workspaceDiscoveryInflight.delete(workspaceId);
209
+ void task.promise.catch((error) => {
210
+ logPerformance("workspace.discover_sessions.background_failed", 0, {
211
+ workspaceId,
212
+ error: error instanceof Error ? error.message : "unknown"
213
+ }, {
214
+ thresholdMs: 0,
215
+ force: true
216
+ });
217
+ return this.listWorkspaceSessions(workspaceId, userId);
123
218
  });
124
- this.workspaceDiscoveryInflight.set(workspaceId, task);
125
- return task;
126
219
  }
127
220
  needsWorkspaceDiscovery(workspaceId, maxAgeMs) {
128
221
  if (maxAgeMs <= 0) {
@@ -246,13 +339,13 @@ export class SessionHistoryService {
246
339
  const binding = this.getBindingOrThrow(sessionId);
247
340
  await this.syncSessionTitleFromProvider(sessionId, binding);
248
341
  }
249
- async syncWorkspaceSessionTitles(workspaceId, userId) {
342
+ async syncWorkspaceSessionTitles(workspaceId, userId, concurrency = 1) {
250
343
  const sessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
251
- for (const session of sessions) {
344
+ await runWithConcurrency(sessions, concurrency, async (session) => {
252
345
  await this.syncSessionTitle(session.sessionId).catch(() => {
253
346
  return;
254
347
  });
255
- }
348
+ });
256
349
  }
257
350
  async listSessionChangedFiles(sessionId, userId) {
258
351
  this.getSession(sessionId, userId);
@@ -266,7 +359,7 @@ export class SessionHistoryService {
266
359
  }
267
360
  getProviderCapabilitiesSnapshot(provider) {
268
361
  try {
269
- return this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider));
362
+ return this.resolveProviderCapabilitiesImmediate(this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider)), null);
270
363
  }
271
364
  catch (error) {
272
365
  throw mapSessionProviderError(error);
@@ -275,7 +368,9 @@ export class SessionHistoryService {
275
368
  async getProviderCapabilities(provider, workspaceId) {
276
369
  try {
277
370
  const workspacePath = workspaceId ? this.getWorkspaceOrThrow(workspaceId).path : null;
278
- return await this.enrichProviderCapabilities(this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider)), workspacePath);
371
+ const baseCapabilities = this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider));
372
+ this.scheduleProviderCapabilityRefresh(baseCapabilities, workspacePath);
373
+ return this.resolveProviderCapabilitiesImmediate(baseCapabilities, workspacePath);
279
374
  }
280
375
  catch (error) {
281
376
  throw mapSessionProviderError(error);
@@ -284,9 +379,14 @@ export class SessionHistoryService {
284
379
  async getSessionCapabilities(sessionId) {
285
380
  const binding = this.getBindingOrThrow(sessionId);
286
381
  const workspace = this.getWorkspaceOrThrow(binding.workspaceId);
382
+ const workspacePath = workspace.path;
287
383
  return this.capabilityService
288
384
  .getSessionCapabilities(binding.provider, binding.providerSessionId)
289
- .then((capabilities) => this.enrichProviderCapabilities(this.applyProviderCliAvailability(capabilities), workspace.path))
385
+ .then((capabilities) => {
386
+ const normalizedCapabilities = this.applyProviderCliAvailability(capabilities);
387
+ this.scheduleProviderCapabilityRefresh(normalizedCapabilities, workspacePath);
388
+ return this.resolveProviderCapabilitiesImmediate(normalizedCapabilities, workspacePath);
389
+ })
290
390
  .catch((error) => {
291
391
  throw mapSessionProviderError(error);
292
392
  });
@@ -299,6 +399,48 @@ export class SessionHistoryService {
299
399
  const codexEnriched = await enrichCodexCapabilities(claudeEnriched, this.codexModelOptionsService);
300
400
  return enrichOpenCodeCapabilities(codexEnriched, this.openCodeModelOptionsService, workspacePath);
301
401
  }
402
+ resolveProviderCapabilitiesImmediate(capabilities, workspacePath) {
403
+ const cacheKey = buildProviderCapabilityCacheKey(capabilities.provider, workspacePath);
404
+ const cached = this.providerCapabilityCache.get(cacheKey);
405
+ if (cached) {
406
+ this.taskManager.recordCacheHit(HOST_TASK_TYPES.providerCapabilityRefresh, cacheKey);
407
+ return cached.value;
408
+ }
409
+ const claudeEnriched = enrichClaudeCapabilities(capabilities, {
410
+ claudeHomeDir: this.claudeCodeHomeDir,
411
+ workspacePath
412
+ });
413
+ return applyImmediateModelOptionFallbacks(claudeEnriched, this.codexModelOptionsService.peekSnapshot(), this.openCodeModelOptionsService.peekSnapshot(workspacePath));
414
+ }
415
+ scheduleProviderCapabilityRefresh(capabilities, workspacePath) {
416
+ const cacheKey = buildProviderCapabilityCacheKey(capabilities.provider, workspacePath);
417
+ const cached = this.providerCapabilityCache.get(cacheKey);
418
+ if (cached &&
419
+ Date.now() - cached.refreshedAt <= PROVIDER_CAPABILITY_CACHE_MAX_AGE_MS) {
420
+ return;
421
+ }
422
+ const task = this.taskManager.enqueue(HOST_TASK_TYPES.providerCapabilityRefresh, {
423
+ key: cacheKey,
424
+ source: "session_history.provider_capability_refresh",
425
+ input: {
426
+ capabilities,
427
+ workspacePath
428
+ }
429
+ });
430
+ if (task.deduped) {
431
+ return;
432
+ }
433
+ void task.promise.catch((error) => {
434
+ logPerformance("provider.capabilities.background_failed", 0, {
435
+ provider: capabilities.provider,
436
+ workspacePath,
437
+ error: error instanceof Error ? error.message : "unknown"
438
+ }, {
439
+ thresholdMs: 0,
440
+ force: true
441
+ });
442
+ });
443
+ }
302
444
  applyProviderCliAvailability(capabilities) {
303
445
  if (!isProviderCliBacked(capabilities.provider)) {
304
446
  return capabilities;
@@ -472,6 +614,7 @@ export class SessionHistoryService {
472
614
  rawStoreRef: binding.rawStoreRef,
473
615
  sourceType: input.sourceType,
474
616
  sourceMessageId,
617
+ sourceMessageSnapshot: input.sourceMessageSnapshot ?? null,
475
618
  strategy: input.strategy ?? "auto"
476
619
  });
477
620
  const sessionId = createId();
@@ -561,7 +704,7 @@ export class SessionHistoryService {
561
704
  throw mapSessionProviderError(new Error("FORK_TARGET_PROVIDER_NOT_SUPPORTED"));
562
705
  }
563
706
  const sourceIndex = this.sessionIndexRepository.findIndexRecordBySessionId(input.sessionId);
564
- const inheritedMessages = await this.readForkSourceMessages(input.sessionId, sourceBinding, input.sourceType, sourceMessageId);
707
+ const inheritedMessages = await this.readForkSourceMessages(input.sessionId, sourceBinding, input.sourceType, sourceMessageId, input.sourceMessageSnapshot ?? null);
565
708
  const reconstructedMessages = inheritedMessages.filter((message) => (message.role === "user" || message.role === "assistant")
566
709
  && message.kind === "text"
567
710
  && message.content.trim().length > 0);
@@ -624,7 +767,7 @@ export class SessionHistoryService {
624
767
  this.workspaceSessionRelations.set(sourceBinding.workspaceId, relationMap);
625
768
  return this.getSessionListItemOrThrow(startedSession.sessionId, input.userId);
626
769
  }
627
- async readForkSourceMessages(sessionId, binding, sourceType, sourceMessageId) {
770
+ async readForkSourceMessages(sessionId, binding, sourceType, sourceMessageId, sourceMessageSnapshot = null) {
628
771
  const messages = [];
629
772
  let cursor = null;
630
773
  while (true) {
@@ -642,7 +785,21 @@ export class SessionHistoryService {
642
785
  if (targetIndex < 0) {
643
786
  throw mapSessionProviderError(new Error("FORK_SOURCE_MESSAGE_NOT_FOUND"));
644
787
  }
645
- return messages.slice(0, targetIndex + 1);
788
+ const inheritedMessages = messages.slice(0, targetIndex + 1);
789
+ if (!sourceMessageSnapshot) {
790
+ return inheritedMessages;
791
+ }
792
+ const targetMessage = inheritedMessages[targetIndex];
793
+ if (!targetMessage) {
794
+ return inheritedMessages;
795
+ }
796
+ inheritedMessages[targetIndex] = {
797
+ ...targetMessage,
798
+ role: sourceMessageSnapshot.role,
799
+ kind: sourceMessageSnapshot.kind,
800
+ content: sourceMessageSnapshot.content
801
+ };
802
+ return inheritedMessages;
646
803
  }
647
804
  assertForkDepthWithinLimit(parentSessionId) {
648
805
  const nextDepth = this.getSessionForkDepth(parentSessionId) + 1;
@@ -935,11 +1092,11 @@ export class SessionHistoryService {
935
1092
  });
936
1093
  const currentBinding = this.sessionBindingRepository.findBySessionId(sessionId);
937
1094
  const timestamp = nowIso();
938
- const duplicateBinding = this.findPendingBindingDuplicate(sessionId, workspaceId, currentBinding, resolvedSnapshot);
1095
+ const duplicateBinding = this.findSameWorkspaceBindingDuplicate(sessionId, workspaceId, resolvedSnapshot);
939
1096
  this.db.transaction(() => {
940
1097
  if (duplicateBinding) {
941
- // 新建运行时会话会先写入 pending 绑定,后台发现链路可能在真 ID 回填前先落一条重复记录。
942
- // 这里保留当前 runtime session,把扫描出的重复会话并回当前会话,避免 provider_session_id 撞唯一键。
1098
+ // 运行时链路显式指定了当前 sessionId,就应该由当前会话接管同工作区里的重复底层会话。
1099
+ // 否则后续事件重放或后台发现补录都会持续撞 UNIQUE(provider, provider_session_id)。
943
1100
  this.mergeSessionIntoTarget({
944
1101
  workspaceId,
945
1102
  targetSessionId: sessionId,
@@ -969,16 +1126,29 @@ export class SessionHistoryService {
969
1126
  }
970
1127
  async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline") {
971
1128
  const startedAt = Date.now();
1129
+ const debugStartedAtMs = terminalDebugNowMs();
972
1130
  const workspace = this.getWorkspaceOrThrow(workspaceId);
973
1131
  let discoverDurationMs = 0;
974
1132
  let persistDurationMs = 0;
1133
+ let persistPass1DurationMs = 0;
1134
+ let persistPass1BatchCount = 0;
1135
+ let persistPass1MaxBatchMs = 0;
1136
+ let relationMapDurationMs = 0;
1137
+ let persistPass2DurationMs = 0;
1138
+ let persistPass2BatchCount = 0;
1139
+ let persistPass2MaxBatchMs = 0;
1140
+ let cleanupDurationMs = 0;
1141
+ let listItemsDurationMs = 0;
1142
+ let refreshStateDurationMs = 0;
975
1143
  const refreshStateCount = 10;
976
1144
  try {
977
1145
  const discoverStartedAt = Date.now();
978
1146
  const existingWorkspaceSessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
979
1147
  const knownSessions = this.buildKnownSessionSummaries(existingWorkspaceSessions, workspace.path);
980
- const discovery = await this.sessionSyncService
981
- .discoverWorkspaceSessions(workspace.path, {
1148
+ const discovery = await this.providerDiscoveryHelperClient
1149
+ .discoverWorkspaceSessions({
1150
+ config: this.providerSessionDiscoveryConfig,
1151
+ workspacePath: workspace.path,
982
1152
  knownSessions
983
1153
  })
984
1154
  .catch((error) => {
@@ -990,10 +1160,9 @@ export class SessionHistoryService {
990
1160
  const discoveredSessionIds = new Map();
991
1161
  const persistedSessions = [];
992
1162
  const claimedPendingSessionIds = new Set();
993
- const persist = this.db.transaction(() => {
994
- for (const session of sessions) {
1163
+ const persistPass1Transaction = this.db.transaction((batch) => {
1164
+ for (const session of batch) {
995
1165
  const exactExisting = this.sessionBindingRepository.findByProviderSession(session.provider, session.providerSessionId) ?? this.sessionBindingRepository.findByRawStoreRef(session.provider, session.rawStoreRef);
996
- // discover 只能补全当前工作区,不能把别的工作区已有会话偷过来重绑。
997
1166
  if (exactExisting && exactExisting.workspaceId !== workspaceId) {
998
1167
  continue;
999
1168
  }
@@ -1065,8 +1234,17 @@ export class SessionHistoryService {
1065
1234
  existingIndex
1066
1235
  });
1067
1236
  }
1068
- const relationMap = this.buildWorkspaceSessionRelationMap(sessions, discoveredSessionIds);
1069
- for (const persistedSession of persistedSessions) {
1237
+ });
1238
+ const persistPass1StartedAt = Date.now();
1239
+ const persistPass1Stats = await runBatchedTransactions(sessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass1Transaction);
1240
+ persistPass1DurationMs = Date.now() - persistPass1StartedAt;
1241
+ persistPass1BatchCount = persistPass1Stats.batchCount;
1242
+ persistPass1MaxBatchMs = persistPass1Stats.maxBatchMs;
1243
+ const relationMapStartedAt = Date.now();
1244
+ const relationMap = this.buildWorkspaceSessionRelationMap(sessions, discoveredSessionIds);
1245
+ relationMapDurationMs = Date.now() - relationMapStartedAt;
1246
+ const persistPass2Transaction = this.db.transaction((batch) => {
1247
+ for (const persistedSession of batch) {
1070
1248
  const relation = relationMap.get(persistedSession.sessionId);
1071
1249
  const resolvedParentSessionId = relation?.parentSessionId
1072
1250
  ?? persistedSession.existingIndex?.parentSessionId
@@ -1104,26 +1282,56 @@ export class SessionHistoryService {
1104
1282
  });
1105
1283
  }
1106
1284
  });
1107
- const persistStartedAt = Date.now();
1108
- persist();
1109
- persistDurationMs = Date.now() - persistStartedAt;
1285
+ const persistPass2StartedAt = Date.now();
1286
+ const persistPass2Stats = await runBatchedTransactions(persistedSessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass2Transaction);
1287
+ persistPass2DurationMs = Date.now() - persistPass2StartedAt;
1288
+ persistPass2BatchCount = persistPass2Stats.batchCount;
1289
+ persistPass2MaxBatchMs = persistPass2Stats.maxBatchMs;
1290
+ persistDurationMs = persistPass1DurationMs + relationMapDurationMs + persistPass2DurationMs;
1110
1291
  if (discovery.isComplete) {
1111
- this.cleanupStaleHiddenSessions(workspaceId, userId, sessions);
1292
+ const cleanupStartedAt = Date.now();
1293
+ await this.cleanupStaleHiddenSessions(workspaceId, userId, sessions);
1294
+ cleanupDurationMs = Date.now() - cleanupStartedAt;
1112
1295
  }
1113
- this.workspaceSessionRelations.set(workspaceId, this.buildWorkspaceSessionRelationMap(sessions, discoveredSessionIds));
1296
+ this.workspaceSessionRelations.set(workspaceId, relationMap);
1297
+ const listItemsStartedAt = Date.now();
1114
1298
  const items = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
1299
+ listItemsDurationMs = Date.now() - listItemsStartedAt;
1115
1300
  const refreshCandidates = buildSessionStateRefreshCandidates(items, refreshStateCount);
1116
1301
  this.workspaceDiscoveryStatuses.set(workspaceId, {
1117
1302
  refreshedAt: Date.now(),
1118
1303
  isComplete: discovery.isComplete
1119
1304
  });
1305
+ const refreshStateStartedAt = Date.now();
1120
1306
  if (refreshStateMode === "inline") {
1121
1307
  await this.refreshRecentSessionStates(refreshCandidates, userId);
1122
1308
  }
1123
1309
  else {
1124
1310
  this.scheduleWorkspaceStateRefresh(workspaceId, userId, refreshCandidates);
1125
1311
  }
1312
+ refreshStateDurationMs = Date.now() - refreshStateStartedAt;
1126
1313
  const nextItems = this.listWorkspaceSessions(workspaceId, userId);
1314
+ if (isTerminalDebugEnabled()) {
1315
+ logTerminalDebug("workspace.discovery.completed", {
1316
+ workspaceId,
1317
+ sessionCount: sessions.length,
1318
+ returnedSessionCount: nextItems.length,
1319
+ discoverMs: discoverDurationMs,
1320
+ persistMs: persistDurationMs,
1321
+ persistPass1Ms: persistPass1DurationMs,
1322
+ persistPass1BatchCount,
1323
+ persistPass1MaxBatchMs,
1324
+ relationMapMs: relationMapDurationMs,
1325
+ persistPass2Ms: persistPass2DurationMs,
1326
+ persistPass2BatchCount,
1327
+ persistPass2MaxBatchMs,
1328
+ cleanupMs: cleanupDurationMs,
1329
+ listItemsMs: listItemsDurationMs,
1330
+ refreshStateMs: refreshStateDurationMs,
1331
+ refreshStateDeferred: refreshStateMode !== "inline",
1332
+ durationMs: terminalDebugNowMs() - debugStartedAtMs
1333
+ });
1334
+ }
1127
1335
  logPerformance("workspace.discover_sessions", Date.now() - startedAt, {
1128
1336
  workspaceId,
1129
1337
  workspacePath: workspace.path,
@@ -1135,6 +1343,16 @@ export class SessionHistoryService {
1135
1343
  refreshedStates: refreshCandidates.length,
1136
1344
  discoverMs: discoverDurationMs,
1137
1345
  persistMs: persistDurationMs,
1346
+ persistPass1Ms: persistPass1DurationMs,
1347
+ persistPass1BatchCount,
1348
+ persistPass1MaxBatchMs,
1349
+ relationMapMs: relationMapDurationMs,
1350
+ persistPass2Ms: persistPass2DurationMs,
1351
+ persistPass2BatchCount,
1352
+ persistPass2MaxBatchMs,
1353
+ cleanupMs: cleanupDurationMs,
1354
+ listItemsMs: listItemsDurationMs,
1355
+ refreshStateMs: refreshStateDurationMs,
1138
1356
  refreshStateDeferred: refreshStateMode !== "inline"
1139
1357
  }, {
1140
1358
  thresholdMs: 500
@@ -1147,6 +1365,16 @@ export class SessionHistoryService {
1147
1365
  workspacePath: workspace.path,
1148
1366
  discoverMs: discoverDurationMs,
1149
1367
  persistMs: persistDurationMs,
1368
+ persistPass1Ms: persistPass1DurationMs,
1369
+ persistPass1BatchCount,
1370
+ persistPass1MaxBatchMs,
1371
+ relationMapMs: relationMapDurationMs,
1372
+ persistPass2Ms: persistPass2DurationMs,
1373
+ persistPass2BatchCount,
1374
+ persistPass2MaxBatchMs,
1375
+ cleanupMs: cleanupDurationMs,
1376
+ listItemsMs: listItemsDurationMs,
1377
+ refreshStateMs: refreshStateDurationMs,
1150
1378
  refreshStateDeferred: refreshStateMode !== "inline",
1151
1379
  error: error instanceof Error ? error.message : "unknown"
1152
1380
  }, {
@@ -1391,7 +1619,12 @@ export class SessionHistoryService {
1391
1619
  if (shouldSkipClaudePendingBinding(binding)) {
1392
1620
  return;
1393
1621
  }
1394
- const nextTitle = (await this.sessionSyncService.readSessionTitle(binding.provider, binding.providerSessionId, binding.rawStoreRef)).trim();
1622
+ const nextTitle = (await this.providerDiscoveryHelperClient.readSessionTitle({
1623
+ config: this.providerSessionDiscoveryConfig,
1624
+ provider: binding.provider,
1625
+ providerSessionId: binding.providerSessionId,
1626
+ rawStoreRef: binding.rawStoreRef
1627
+ })).trim();
1395
1628
  const resolvedTitle = resolvePersistedSessionTitle(binding.provider, nextTitle, currentIndex.title);
1396
1629
  if (resolvedTitle.length === 0 || resolvedTitle === currentIndex.title) {
1397
1630
  return;
@@ -1486,11 +1719,11 @@ export class SessionHistoryService {
1486
1719
  return this.sessionBindingRepository.findBySessionId(aliasTargetSessionId);
1487
1720
  }
1488
1721
  findPendingSessionAliasTargetSessionId(descriptor) {
1489
- if (!descriptor || descriptor.provider !== "gemini") {
1722
+ if (!descriptor) {
1490
1723
  return null;
1491
1724
  }
1492
- const aliasTargetSessionId = extractPendingBindingTargetSessionId(descriptor.providerSessionId)
1493
- ?? extractPendingBindingTargetSessionId(descriptor.rawStoreRef);
1725
+ const aliasTargetSessionId = extractSessionAliasTargetSessionId(descriptor.providerSessionId)
1726
+ ?? extractSessionAliasTargetSessionId(descriptor.rawStoreRef);
1494
1727
  if (!aliasTargetSessionId || aliasTargetSessionId === descriptor.sessionId) {
1495
1728
  return null;
1496
1729
  }
@@ -1548,7 +1781,7 @@ export class SessionHistoryService {
1548
1781
  });
1549
1782
  this.workspaceStateRefreshInflight.set(inflightKey, task);
1550
1783
  }
1551
- cleanupStaleHiddenSessions(workspaceId, userId, sessions) {
1784
+ async cleanupStaleHiddenSessions(workspaceId, userId, sessions) {
1552
1785
  const discoveredProviderSessionIds = new Set(sessions.map((session) => buildProviderSessionKey(session.provider, session.providerSessionId)));
1553
1786
  const discoveredRawStoreRefs = new Set(sessions.map((session) => session.rawStoreRef));
1554
1787
  const staleHiddenSessions = this.sessionIndexRepository
@@ -1568,20 +1801,14 @@ export class SessionHistoryService {
1568
1801
  if (staleHiddenSessions.length === 0) {
1569
1802
  return;
1570
1803
  }
1571
- this.deleteSessionsByIds(staleHiddenSessions.map((session) => session.sessionId));
1572
- }
1573
- deleteSessionsByIds(sessionIds) {
1574
- const remove = this.db.transaction((ids) => {
1804
+ const deleteTransaction = this.db.transaction((ids) => {
1575
1805
  for (const sessionId of ids) {
1576
1806
  this.deleteSessionById(sessionId);
1577
1807
  }
1578
1808
  });
1579
- remove(sessionIds);
1809
+ await runBatchedTransactions(staleHiddenSessions.map((session) => session.sessionId), WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, deleteTransaction);
1580
1810
  }
1581
- findPendingBindingDuplicate(sessionId, workspaceId, currentBinding, snapshot) {
1582
- if (!currentBinding || !isPendingBindingValue(currentBinding.providerSessionId)) {
1583
- return null;
1584
- }
1811
+ findSameWorkspaceBindingDuplicate(sessionId, workspaceId, snapshot) {
1585
1812
  if (isPendingBindingValue(snapshot.providerSessionId)) {
1586
1813
  return null;
1587
1814
  }
@@ -1673,12 +1900,22 @@ export class SessionHistoryService {
1673
1900
  this.db
1674
1901
  .prepare("DELETE FROM session_forks WHERE session_id = ?")
1675
1902
  .run(input.sourceSessionId);
1676
- this.db
1677
- .prepare("DELETE FROM session_indices WHERE session_id = ?")
1678
- .run(input.sourceSessionId);
1679
- this.db
1680
- .prepare("DELETE FROM session_bindings WHERE session_id = ?")
1681
- .run(input.sourceSessionId);
1903
+ // 保留旧 session_id 作为 alias,避免前端或 Butler 还拿着旧 id 时直接炸成 SESSION_NOT_FOUND。
1904
+ this.sessionBindingRepository.upsert({
1905
+ sessionId: input.sourceSessionId,
1906
+ workspaceId: sourceBinding.workspaceId,
1907
+ provider: sourceBinding.provider,
1908
+ providerSessionId: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
1909
+ rawStoreRef: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
1910
+ createdAt: sourceBinding.createdAt,
1911
+ updatedAt: input.timestamp
1912
+ });
1913
+ if (sourceIndex) {
1914
+ this.sessionIndexRepository.upsert({
1915
+ ...sourceIndex,
1916
+ updatedAt: input.timestamp
1917
+ });
1918
+ }
1682
1919
  this.rewriteWorkspaceSessionRelations(input.workspaceId, input.targetSessionId, input.sourceSessionId, targetIndex, sourceIndex);
1683
1920
  }
1684
1921
  listSessionStatesBySessionId(sessionId) {
@@ -1804,6 +2041,7 @@ export class SessionHistoryService {
1804
2041
  }
1805
2042
  buildKnownSessionSummaries(sessions, workspacePath) {
1806
2043
  return sessions
2044
+ .filter((session) => !this.isPendingSessionAlias(session))
1807
2045
  .filter((session) => !shouldSkipClaudePendingBinding(session))
1808
2046
  .map((session) => {
1809
2047
  const stats = safeStat(session.rawStoreRef);
@@ -1823,19 +2061,28 @@ export class SessionHistoryService {
1823
2061
  async refreshSessionState(sessionId, userId) {
1824
2062
  const binding = this.getBindingOrThrow(sessionId);
1825
2063
  const current = this.sessionStateRepository.findBySessionAndUser(sessionId, userId);
1826
- const inspection = inspectSessionActivity(binding.provider, binding.rawStoreRef);
1827
2064
  const timestamp = nowIso();
1828
- const nowMs = Date.parse(timestamp);
1829
- if (shouldClearStaleRuntimeWithoutInspection(current, inspection, nowMs)) {
1830
- this.sessionActivityAuthorityService.clearSession(sessionId);
1831
- }
1832
- if (shouldPreserveRuntimeTerminalState(current, inspection)) {
1833
- return current;
2065
+ const liveObservation = this.resolveLiveActivityObservation(sessionId);
2066
+ const inspection = liveObservation
2067
+ ? null
2068
+ : inspectSessionActivity(binding.provider, binding.rawStoreRef);
2069
+ if (inspection) {
2070
+ const nowMs = Date.parse(timestamp);
2071
+ if (shouldClearStaleRuntimeWithoutInspection(current, inspection, nowMs)) {
2072
+ this.sessionActivityAuthorityService.clearSession(sessionId);
2073
+ }
2074
+ if (shouldPreserveRuntimeTerminalState(current, inspection)) {
2075
+ return current;
2076
+ }
1834
2077
  }
1835
- const resolution = this.sessionActivityAuthorityService.observe(buildInspectionActivityObservation(sessionId, inspection, timestamp));
1836
- const resolvedLastEventAt = hasInspectionEvidence(inspection)
1837
- ? resolution.lastObservedAt ?? inspection.lastEventAt ?? current?.lastEventAt ?? null
1838
- : current?.lastEventAt ?? null;
2078
+ const resolution = liveObservation
2079
+ ? this.sessionActivityAuthorityService.observe(liveObservation)
2080
+ : this.sessionActivityAuthorityService.observe(buildInspectionActivityObservation(sessionId, inspection, timestamp));
2081
+ const resolvedLastEventAt = liveObservation
2082
+ ? resolution.lastObservedAt ?? current?.lastEventAt ?? null
2083
+ : inspection && hasInspectionEvidence(inspection)
2084
+ ? resolution.lastObservedAt ?? inspection.lastEventAt ?? current?.lastEventAt ?? null
2085
+ : current?.lastEventAt ?? null;
1839
2086
  const nextRecord = {
1840
2087
  sessionId,
1841
2088
  userId,
@@ -1844,7 +2091,7 @@ export class SessionHistoryService {
1844
2091
  favorite: current?.favorite ?? false,
1845
2092
  lastEventAt: resolvedLastEventAt,
1846
2093
  completedAt: isTerminalResolvedRunningState(resolution.runningState)
1847
- ? resolution.terminalAt ?? inspection.completedAtCandidate ?? current?.completedAt ?? null
2094
+ ? resolution.terminalAt ?? inspection?.completedAtCandidate ?? current?.completedAt ?? null
1848
2095
  : null,
1849
2096
  lastSeenAt: current?.lastSeenAt ?? null,
1850
2097
  updatedAt: timestamp
@@ -1862,8 +2109,8 @@ export class SessionHistoryService {
1862
2109
  syncCursor: currentSnapshot?.syncCursor ?? null,
1863
2110
  lastSyncAt: resolution.lastObservedAt
1864
2111
  ?? resolution.terminalAt
1865
- ?? inspection.lastEventAt
1866
- ?? inspection.completedAtCandidate
2112
+ ?? inspection?.lastEventAt
2113
+ ?? inspection?.completedAtCandidate
1867
2114
  ?? currentSnapshot?.lastSyncAt
1868
2115
  ?? null,
1869
2116
  lastErrorCode: resolution.runningState === "failed"
@@ -1881,17 +2128,34 @@ export class SessionHistoryService {
1881
2128
  });
1882
2129
  return nextRecord;
1883
2130
  }
2131
+ resolveLiveActivityObservation(sessionId) {
2132
+ for (const resolver of this.liveActivityObservationResolvers) {
2133
+ const observation = resolver(sessionId);
2134
+ if (observation) {
2135
+ return observation;
2136
+ }
2137
+ }
2138
+ return null;
2139
+ }
1884
2140
  upsertSnapshot(sessionId, input) {
2141
+ const resolvedSessionId = this.resolveCanonicalSessionId(sessionId);
2142
+ if (!this.sessionBindingRepository.findBySessionId(resolvedSessionId)) {
2143
+ return;
2144
+ }
1885
2145
  this.sessionStatusSnapshotRepository.upsert({
1886
- sessionId,
2146
+ sessionId: resolvedSessionId,
1887
2147
  ...input,
1888
2148
  updatedAt: nowIso()
1889
2149
  });
1890
2150
  }
1891
2151
  markSessionError(sessionId, errorCode, error) {
1892
- const current = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
2152
+ const resolvedSessionId = this.resolveCanonicalSessionId(sessionId);
2153
+ if (!this.sessionBindingRepository.findBySessionId(resolvedSessionId)) {
2154
+ return;
2155
+ }
2156
+ const current = this.sessionStatusSnapshotRepository.findBySessionId(resolvedSessionId);
1893
2157
  this.sessionStatusSnapshotRepository.upsert({
1894
- sessionId,
2158
+ sessionId: resolvedSessionId,
1895
2159
  syncStatus: "error",
1896
2160
  syncCursor: current?.syncCursor ?? null,
1897
2161
  lastSyncAt: current?.lastSyncAt ?? null,
@@ -1961,23 +2225,28 @@ function hasInspectionEvidence(inspection) {
1961
2225
  || !!inspection.completedAtCandidate;
1962
2226
  }
1963
2227
  function applySessionActivityResolution(item, resolution) {
1964
- const runningState = resolution.runningState;
1965
- const shouldClearResolvedFailure = runningState !== "failed" && item.runningState === "failed";
2228
+ const rawResolvedRunningState = resolution.runningState === "unknown" && item.runningState === null
2229
+ ? null
2230
+ : resolution.runningState;
2231
+ const resolvedRunningState = resolution.activityResolutionSource === "inferred_log" && rawResolvedRunningState === "completed"
2232
+ ? "idle"
2233
+ : rawResolvedRunningState;
2234
+ const shouldClearResolvedFailure = resolvedRunningState !== "failed" && item.runningState === "failed";
1966
2235
  const lastEventAt = resolution.lastObservedAt ?? item.lastEventAt;
1967
- const completedAt = isTerminalResolvedRunningState(runningState)
2236
+ const completedAt = rawResolvedRunningState && isTerminalResolvedRunningState(rawResolvedRunningState)
1968
2237
  ? resolution.terminalAt ?? item.completedAt
1969
2238
  : null;
1970
- const lastErrorCode = runningState === "failed"
2239
+ const lastErrorCode = resolvedRunningState === "failed"
1971
2240
  ? resolution.errorCode ?? item.lastErrorCode
1972
2241
  : shouldClearResolvedFailure
1973
2242
  ? null
1974
2243
  : item.lastErrorCode;
1975
- const lastErrorDetail = runningState === "failed"
2244
+ const lastErrorDetail = resolvedRunningState === "failed"
1976
2245
  ? resolution.detail ?? item.lastErrorDetail
1977
2246
  : shouldClearResolvedFailure
1978
2247
  ? null
1979
2248
  : item.lastErrorDetail;
1980
- const syncStatus = runningState === "failed"
2249
+ const syncStatus = resolvedRunningState === "failed"
1981
2250
  ? "error"
1982
2251
  : shouldClearResolvedFailure && item.syncStatus === "error"
1983
2252
  ? "idle"
@@ -1985,7 +2254,7 @@ function applySessionActivityResolution(item, resolution) {
1985
2254
  return {
1986
2255
  ...item,
1987
2256
  syncStatus,
1988
- runningState,
2257
+ runningState: resolvedRunningState,
1989
2258
  activitySource: mapResolutionSourceToCompatibilitySource(resolution.activityResolutionSource),
1990
2259
  activityResolutionSource: resolution.activityResolutionSource,
1991
2260
  activityConfidence: resolution.activityConfidence,
@@ -1995,7 +2264,7 @@ function applySessionActivityResolution(item, resolution) {
1995
2264
  lastErrorCode,
1996
2265
  lastErrorDetail,
1997
2266
  watchdogTriggeredAt: resolution.watchdogTriggeredAt,
1998
- activityState: resolveActivityState(runningState, completedAt, item.lastSeenAt)
2267
+ activityState: resolveActivityState(resolvedRunningState, completedAt, item.lastSeenAt)
1999
2268
  };
2000
2269
  }
2001
2270
  function clampLimit(limit) {
@@ -2194,6 +2463,9 @@ function isPendingBindingValue(value) {
2194
2463
  function buildPendingBindingValue(provider, sessionId) {
2195
2464
  return `pending://${provider}/${sessionId}`;
2196
2465
  }
2466
+ function buildAliasBindingValue(provider, targetSessionId, sourceSessionId) {
2467
+ return `alias://${provider}/${targetSessionId}/${sourceSessionId}`;
2468
+ }
2197
2469
  function extractPendingBindingTargetSessionId(value) {
2198
2470
  if (!isPendingBindingValue(value)) {
2199
2471
  return null;
@@ -2202,6 +2474,25 @@ function extractPendingBindingTargetSessionId(value) {
2202
2474
  const targetSessionId = normalizedValue.slice(normalizedValue.indexOf("/", "pending://".length) + 1).trim();
2203
2475
  return targetSessionId || null;
2204
2476
  }
2477
+ function extractAliasBindingTargetSessionId(value) {
2478
+ const normalizedValue = value.trim();
2479
+ if (!normalizedValue.toLowerCase().startsWith("alias://")) {
2480
+ return null;
2481
+ }
2482
+ const pathStart = normalizedValue.indexOf("/", "alias://".length);
2483
+ if (pathStart < 0) {
2484
+ return null;
2485
+ }
2486
+ const targetAndSource = normalizedValue.slice(pathStart + 1).trim();
2487
+ if (targetAndSource.length === 0) {
2488
+ return null;
2489
+ }
2490
+ const [targetSessionId] = targetAndSource.split("/", 1);
2491
+ return targetSessionId?.trim() || null;
2492
+ }
2493
+ function extractSessionAliasTargetSessionId(value) {
2494
+ return extractAliasBindingTargetSessionId(value) ?? extractPendingBindingTargetSessionId(value);
2495
+ }
2205
2496
  function isClaudePendingRuntimeRawStoreRef(rawStoreRef) {
2206
2497
  const normalizedRawStoreRef = rawStoreRef.replaceAll("\\", "/").toLowerCase();
2207
2498
  return normalizedRawStoreRef.includes("/.pending-");
@@ -2570,7 +2861,7 @@ function mapResolutionSourceToLegacyActivitySource(source, inspection) {
2570
2861
  if (source === "authoritative_runtime" || source === "authoritative_provider_event") {
2571
2862
  return "runtime";
2572
2863
  }
2573
- if (inspection.lastEventAt || inspection.completedAtCandidate) {
2864
+ if (inspection && (inspection.lastEventAt || inspection.completedAtCandidate)) {
2574
2865
  return "inferred";
2575
2866
  }
2576
2867
  return "none";
@@ -2620,4 +2911,71 @@ function buildReconstructedForkPrompt(input) {
2620
2911
  }
2621
2912
  return lines.join("\n").trim();
2622
2913
  }
2914
+ function buildProviderCapabilityCacheKey(provider, workspacePath) {
2915
+ return `${provider}::${workspacePath ?? ""}`;
2916
+ }
2917
+ async function runWithConcurrency(items, concurrency, worker) {
2918
+ const normalizedConcurrency = Math.max(1, Math.floor(concurrency) || 1);
2919
+ const queue = [...items];
2920
+ const runners = Array.from({
2921
+ length: Math.min(normalizedConcurrency, queue.length || 1)
2922
+ }, async () => {
2923
+ while (queue.length > 0) {
2924
+ const current = queue.shift();
2925
+ if (current === undefined) {
2926
+ return;
2927
+ }
2928
+ await worker(current);
2929
+ }
2930
+ });
2931
+ await Promise.all(runners);
2932
+ }
2933
+ async function runBatchedTransactions(items, batchSize, transaction) {
2934
+ const normalizedBatchSize = Math.max(1, Math.floor(batchSize) || 1);
2935
+ let batchCount = 0;
2936
+ let maxBatchMs = 0;
2937
+ for (let index = 0; index < items.length; index += normalizedBatchSize) {
2938
+ const batch = items.slice(index, index + normalizedBatchSize);
2939
+ const batchStartedAt = Date.now();
2940
+ transaction(batch);
2941
+ const batchDurationMs = Date.now() - batchStartedAt;
2942
+ batchCount += 1;
2943
+ maxBatchMs = Math.max(maxBatchMs, batchDurationMs);
2944
+ if (index + normalizedBatchSize < items.length) {
2945
+ await delay(0);
2946
+ }
2947
+ }
2948
+ return {
2949
+ batchCount,
2950
+ maxBatchMs
2951
+ };
2952
+ }
2953
+ function applyImmediateModelOptionFallbacks(capabilities, codexSnapshot, openCodeSnapshot) {
2954
+ if (capabilities.provider === "codex") {
2955
+ return {
2956
+ ...capabilities,
2957
+ modelOptions: codexSnapshot?.modelOptions ?? createFallbackCodexModelOptions(null),
2958
+ defaultReasoningLevel: codexSnapshot?.defaultReasoningLevel ?? null,
2959
+ limitations: codexSnapshot
2960
+ ? capabilities.limitations
2961
+ : Array.from(new Set([
2962
+ ...capabilities.limitations,
2963
+ "当前暂时使用缓存或兜底模型列表,后台会继续刷新 Codex 能力。"
2964
+ ]))
2965
+ };
2966
+ }
2967
+ if (capabilities.provider === "opencode") {
2968
+ return {
2969
+ ...capabilities,
2970
+ modelOptions: openCodeSnapshot?.modelOptions ?? createFallbackOpenCodeModelOptions(null),
2971
+ limitations: openCodeSnapshot
2972
+ ? capabilities.limitations
2973
+ : Array.from(new Set([
2974
+ ...capabilities.limitations,
2975
+ "当前暂时使用缓存或兜底模型列表,后台会继续刷新 OpenCode 能力。"
2976
+ ]))
2977
+ };
2978
+ }
2979
+ return capabilities;
2980
+ }
2623
2981
  //# sourceMappingURL=session-history-service.js.map