@jingyi0605/codingns 0.2.0 → 0.2.5

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 (257) hide show
  1. package/README.md +44 -0
  2. package/bin/codingns.mjs +640 -53
  3. package/dist/public/assets/{TerminalPage-BlbQuWi1.js → TerminalPage-BkjqU9NG.js} +19 -19
  4. package/dist/public/assets/index-C6U8-9jg.css +1 -0
  5. package/dist/public/assets/index-CKSumuV2.js +109 -0
  6. package/dist/public/index.html +2 -2
  7. package/dist/server/config/env.d.ts +1 -0
  8. package/dist/server/config/env.js +3 -0
  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.js +69 -30
  17. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  18. package/dist/server/modules/butler/butler-follow-up-scheduler.d.ts +9 -0
  19. package/dist/server/modules/butler/butler-follow-up-scheduler.js +47 -11
  20. package/dist/server/modules/butler/butler-follow-up-scheduler.js.map +1 -1
  21. package/dist/server/modules/butler/butler-follow-up-service.d.ts +7 -1
  22. package/dist/server/modules/butler/butler-follow-up-service.js +10 -0
  23. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
  24. package/dist/server/modules/butler/butler-session-service.d.ts +2 -1
  25. package/dist/server/modules/butler/butler-session-service.js +10 -1
  26. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  27. package/dist/server/modules/butler/butler-session-summary-service.d.ts +8 -1
  28. package/dist/server/modules/butler/butler-session-summary-service.js +34 -7
  29. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
  30. package/dist/server/modules/butler/context-aggregator.js +44 -13
  31. package/dist/server/modules/butler/context-aggregator.js.map +1 -1
  32. package/dist/server/modules/butler/patrol-scheduler.d.ts +9 -0
  33. package/dist/server/modules/butler/patrol-scheduler.js +63 -9
  34. package/dist/server/modules/butler/patrol-scheduler.js.map +1 -1
  35. package/dist/server/modules/butler/session-summary-scheduler.d.ts +9 -0
  36. package/dist/server/modules/butler/session-summary-scheduler.js +47 -11
  37. package/dist/server/modules/butler/session-summary-scheduler.js.map +1 -1
  38. package/dist/server/modules/debug-target/debug-runtime-reconciliation-scheduler.d.ts +38 -0
  39. package/dist/server/modules/debug-target/debug-runtime-reconciliation-scheduler.js +99 -0
  40. package/dist/server/modules/debug-target/debug-runtime-reconciliation-scheduler.js.map +1 -0
  41. package/dist/server/modules/debug-target/debug-target-controller.d.ts +70 -0
  42. package/dist/server/modules/debug-target/debug-target-controller.js +113 -0
  43. package/dist/server/modules/debug-target/debug-target-controller.js.map +1 -0
  44. package/dist/server/modules/debug-target/debug-target-service.d.ts +102 -0
  45. package/dist/server/modules/debug-target/debug-target-service.js +1484 -0
  46. package/dist/server/modules/debug-target/debug-target-service.js.map +1 -0
  47. package/dist/server/modules/debug-target/framework-compatibility-matrix.d.ts +4 -0
  48. package/dist/server/modules/debug-target/framework-compatibility-matrix.js +45 -0
  49. package/dist/server/modules/debug-target/framework-compatibility-matrix.js.map +1 -0
  50. package/dist/server/modules/debug-target/launch-adapter-registry.d.ts +25 -0
  51. package/dist/server/modules/debug-target/launch-adapter-registry.js +445 -0
  52. package/dist/server/modules/debug-target/launch-adapter-registry.js.map +1 -0
  53. package/dist/server/modules/file/file-content-service.d.ts +2 -1
  54. package/dist/server/modules/file/file-content-service.js +53 -0
  55. package/dist/server/modules/file/file-content-service.js.map +1 -1
  56. package/dist/server/modules/git/commit-orchestrator.d.ts +4 -1
  57. package/dist/server/modules/git/commit-orchestrator.js +18 -1
  58. package/dist/server/modules/git/commit-orchestrator.js.map +1 -1
  59. package/dist/server/modules/git/git-auth.d.ts +25 -0
  60. package/dist/server/modules/git/git-auth.js +88 -0
  61. package/dist/server/modules/git/git-auth.js.map +1 -0
  62. package/dist/server/modules/git/git-controller.d.ts +6 -0
  63. package/dist/server/modules/git/git-controller.js +5 -1
  64. package/dist/server/modules/git/git-controller.js.map +1 -1
  65. package/dist/server/modules/git/git-read-service.d.ts +2 -1
  66. package/dist/server/modules/git/git-read-service.js +19 -2
  67. package/dist/server/modules/git/git-read-service.js.map +1 -1
  68. package/dist/server/modules/git/git-remote-credential-service.d.ts +9 -0
  69. package/dist/server/modules/git/git-remote-credential-service.js +76 -0
  70. package/dist/server/modules/git/git-remote-credential-service.js.map +1 -0
  71. package/dist/server/modules/git/git-write-service.d.ts +5 -2
  72. package/dist/server/modules/git/git-write-service.js +33 -17
  73. package/dist/server/modules/git/git-write-service.js.map +1 -1
  74. package/dist/server/modules/git/types.d.ts +3 -0
  75. package/dist/server/modules/git/workspace-repo-guard.js +3 -2
  76. package/dist/server/modules/git/workspace-repo-guard.js.map +1 -1
  77. package/dist/server/modules/provider/codex-model-options.d.ts +3 -1
  78. package/dist/server/modules/provider/codex-model-options.js +4 -1
  79. package/dist/server/modules/provider/codex-model-options.js.map +1 -1
  80. package/dist/server/modules/provider/opencode-model-options.d.ts +3 -1
  81. package/dist/server/modules/provider/opencode-model-options.js +5 -1
  82. package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
  83. package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +24 -0
  84. package/dist/server/modules/provider/provider-discovery-helper-client.js +14 -0
  85. package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
  86. package/dist/server/modules/provider/provider-discovery-helper-process.js +54 -0
  87. package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
  88. package/dist/server/modules/sessions/session-controller.d.ts +5 -0
  89. package/dist/server/modules/sessions/session-controller.js +16 -0
  90. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  91. package/dist/server/modules/sessions/session-history-service.d.ts +19 -5
  92. package/dist/server/modules/sessions/session-history-service.js +322 -39
  93. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  94. package/dist/server/modules/tasks/event-loop-monitor.d.ts +21 -0
  95. package/dist/server/modules/tasks/event-loop-monitor.js +64 -0
  96. package/dist/server/modules/tasks/event-loop-monitor.js.map +1 -0
  97. package/dist/server/modules/tasks/observability-controller.d.ts +30 -0
  98. package/dist/server/modules/tasks/observability-controller.js +44 -0
  99. package/dist/server/modules/tasks/observability-controller.js.map +1 -0
  100. package/dist/server/modules/tasks/observability-service.d.ts +32 -0
  101. package/dist/server/modules/tasks/observability-service.js +104 -0
  102. package/dist/server/modules/tasks/observability-service.js.map +1 -0
  103. package/dist/server/modules/tasks/scheduler-metrics.d.ts +41 -0
  104. package/dist/server/modules/tasks/scheduler-metrics.js +92 -0
  105. package/dist/server/modules/tasks/scheduler-metrics.js.map +1 -0
  106. package/dist/server/modules/tasks/task-activity-log.d.ts +39 -0
  107. package/dist/server/modules/tasks/task-activity-log.js +43 -0
  108. package/dist/server/modules/tasks/task-activity-log.js.map +1 -0
  109. package/dist/server/modules/tasks/task-helper-client.d.ts +11 -0
  110. package/dist/server/modules/tasks/task-helper-client.js +132 -0
  111. package/dist/server/modules/tasks/task-helper-client.js.map +1 -0
  112. package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +16 -0
  113. package/dist/server/modules/tasks/task-helper-process-handlers.js +14 -0
  114. package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -0
  115. package/dist/server/modules/tasks/task-helper-process.d.ts +1 -0
  116. package/dist/server/modules/tasks/task-helper-process.js +49 -0
  117. package/dist/server/modules/tasks/task-helper-process.js.map +1 -0
  118. package/dist/server/modules/tasks/task-lane-executors.d.ts +2 -0
  119. package/dist/server/modules/tasks/task-lane-executors.js +15 -0
  120. package/dist/server/modules/tasks/task-lane-executors.js.map +1 -0
  121. package/dist/server/modules/tasks/task-manager.d.ts +15 -0
  122. package/dist/server/modules/tasks/task-manager.js +36 -0
  123. package/dist/server/modules/tasks/task-manager.js.map +1 -0
  124. package/dist/server/modules/tasks/task-metrics.d.ts +9 -0
  125. package/dist/server/modules/tasks/task-metrics.js +81 -0
  126. package/dist/server/modules/tasks/task-metrics.js.map +1 -0
  127. package/dist/server/modules/tasks/task-registry.d.ts +7 -0
  128. package/dist/server/modules/tasks/task-registry.js +21 -0
  129. package/dist/server/modules/tasks/task-registry.js.map +1 -0
  130. package/dist/server/modules/tasks/task-scheduler.d.ts +31 -0
  131. package/dist/server/modules/tasks/task-scheduler.js +473 -0
  132. package/dist/server/modules/tasks/task-scheduler.js.map +1 -0
  133. package/dist/server/modules/tasks/task-types.d.ts +106 -0
  134. package/dist/server/modules/tasks/task-types.js +23 -0
  135. package/dist/server/modules/tasks/task-types.js.map +1 -0
  136. package/dist/server/modules/terminal/command-template-service.d.ts +4 -0
  137. package/dist/server/modules/terminal/command-template-service.js +5 -3
  138. package/dist/server/modules/terminal/command-template-service.js.map +1 -1
  139. package/dist/server/modules/terminal/runtime/terminal-log-spooler.d.ts +7 -3
  140. package/dist/server/modules/terminal/runtime/terminal-log-spooler.js +95 -15
  141. package/dist/server/modules/terminal/runtime/terminal-log-spooler.js.map +1 -1
  142. package/dist/server/modules/terminal/runtime/terminal-log-writer-client.d.ts +21 -0
  143. package/dist/server/modules/terminal/runtime/terminal-log-writer-client.js +144 -0
  144. package/dist/server/modules/terminal/runtime/terminal-log-writer-client.js.map +1 -0
  145. package/dist/server/modules/terminal/runtime/terminal-log-writer-process.d.ts +1 -0
  146. package/dist/server/modules/terminal/runtime/terminal-log-writer-process.js +187 -0
  147. package/dist/server/modules/terminal/runtime/terminal-log-writer-process.js.map +1 -0
  148. package/dist/server/modules/terminal/terminal-service.d.ts +12 -0
  149. package/dist/server/modules/terminal/terminal-service.js +34 -17
  150. package/dist/server/modules/terminal/terminal-service.js.map +1 -1
  151. package/dist/server/modules/workbench/workbench-service.d.ts +23 -2
  152. package/dist/server/modules/workbench/workbench-service.js +126 -15
  153. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  154. package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +5 -1
  155. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +88 -19
  156. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
  157. package/dist/server/modules/workspace/workspace-code-composition.d.ts +2 -0
  158. package/dist/server/modules/workspace/workspace-code-composition.js +154 -0
  159. package/dist/server/modules/workspace/workspace-code-composition.js.map +1 -0
  160. package/dist/server/modules/workspace/workspace-controller.d.ts +14 -0
  161. package/dist/server/modules/workspace/workspace-controller.js +19 -0
  162. package/dist/server/modules/workspace/workspace-controller.js.map +1 -1
  163. package/dist/server/modules/workspace/workspace-service.d.ts +21 -14
  164. package/dist/server/modules/workspace/workspace-service.js +183 -234
  165. package/dist/server/modules/workspace/workspace-service.js.map +1 -1
  166. package/dist/server/modules/worktree/worktree-cleanup-service.d.ts +35 -0
  167. package/dist/server/modules/worktree/worktree-cleanup-service.js +210 -0
  168. package/dist/server/modules/worktree/worktree-cleanup-service.js.map +1 -0
  169. package/dist/server/modules/worktree/worktree-controller.d.ts +44 -0
  170. package/dist/server/modules/worktree/worktree-controller.js +40 -0
  171. package/dist/server/modules/worktree/worktree-controller.js.map +1 -0
  172. package/dist/server/modules/worktree/worktree-manager.d.ts +34 -0
  173. package/dist/server/modules/worktree/worktree-manager.js +292 -0
  174. package/dist/server/modules/worktree/worktree-manager.js.map +1 -0
  175. package/dist/server/modules/worktree/worktree-merge-service.d.ts +52 -0
  176. package/dist/server/modules/worktree/worktree-merge-service.js +293 -0
  177. package/dist/server/modules/worktree/worktree-merge-service.js.map +1 -0
  178. package/dist/server/modules/worktree/worktree-sync-service.d.ts +23 -0
  179. package/dist/server/modules/worktree/worktree-sync-service.js +166 -0
  180. package/dist/server/modules/worktree/worktree-sync-service.js.map +1 -0
  181. package/dist/server/routes/assistant.d.ts +3 -0
  182. package/dist/server/routes/assistant.js +15 -0
  183. package/dist/server/routes/assistant.js.map +1 -0
  184. package/dist/server/routes/debug-targets.d.ts +3 -0
  185. package/dist/server/routes/debug-targets.js +15 -0
  186. package/dist/server/routes/debug-targets.js.map +1 -0
  187. package/dist/server/routes/git.js +1 -0
  188. package/dist/server/routes/git.js.map +1 -1
  189. package/dist/server/routes/observability.d.ts +3 -0
  190. package/dist/server/routes/observability.js +7 -0
  191. package/dist/server/routes/observability.js.map +1 -0
  192. package/dist/server/routes/workspaces.js +2 -0
  193. package/dist/server/routes/workspaces.js.map +1 -1
  194. package/dist/server/routes/worktrees.d.ts +3 -0
  195. package/dist/server/routes/worktrees.js +8 -0
  196. package/dist/server/routes/worktrees.js.map +1 -0
  197. package/dist/server/server/create-server.d.ts +34 -0
  198. package/dist/server/server/create-server.js +100 -10
  199. package/dist/server/server/create-server.js.map +1 -1
  200. package/dist/server/shared/utils/secret-box.d.ts +2 -0
  201. package/dist/server/shared/utils/secret-box.js +29 -0
  202. package/dist/server/shared/utils/secret-box.js.map +1 -0
  203. package/dist/server/shared/utils/terminal-debug-log.js +5 -3
  204. package/dist/server/shared/utils/terminal-debug-log.js.map +1 -1
  205. package/dist/server/storage/repositories/ai-fallback-edit-repository.d.ts +11 -0
  206. package/dist/server/storage/repositories/ai-fallback-edit-repository.js +118 -0
  207. package/dist/server/storage/repositories/ai-fallback-edit-repository.js.map +1 -0
  208. package/dist/server/storage/repositories/debug-runtime-session-repository.d.ts +11 -0
  209. package/dist/server/storage/repositories/debug-runtime-session-repository.js +100 -0
  210. package/dist/server/storage/repositories/debug-runtime-session-repository.js.map +1 -0
  211. package/dist/server/storage/repositories/debug-service-repository.d.ts +9 -0
  212. package/dist/server/storage/repositories/debug-service-repository.js +99 -0
  213. package/dist/server/storage/repositories/debug-service-repository.js.map +1 -0
  214. package/dist/server/storage/repositories/debug-target-repository.d.ts +11 -0
  215. package/dist/server/storage/repositories/debug-target-repository.js +100 -0
  216. package/dist/server/storage/repositories/debug-target-repository.js.map +1 -0
  217. package/dist/server/storage/repositories/framework-analysis-result-repository.d.ts +9 -0
  218. package/dist/server/storage/repositories/framework-analysis-result-repository.js +98 -0
  219. package/dist/server/storage/repositories/framework-analysis-result-repository.js.map +1 -0
  220. package/dist/server/storage/repositories/git-remote-credential-repository.d.ts +9 -0
  221. package/dist/server/storage/repositories/git-remote-credential-repository.js +51 -0
  222. package/dist/server/storage/repositories/git-remote-credential-repository.js.map +1 -0
  223. package/dist/server/storage/repositories/port-lease-repository.d.ts +12 -0
  224. package/dist/server/storage/repositories/port-lease-repository.js +124 -0
  225. package/dist/server/storage/repositories/port-lease-repository.js.map +1 -0
  226. package/dist/server/storage/repositories/runtime-binding-repository.d.ts +10 -0
  227. package/dist/server/storage/repositories/runtime-binding-repository.js +89 -0
  228. package/dist/server/storage/repositories/runtime-binding-repository.js.map +1 -0
  229. package/dist/server/storage/repositories/terminal-command-template-repository.js +77 -4
  230. package/dist/server/storage/repositories/terminal-command-template-repository.js.map +1 -1
  231. package/dist/server/storage/repositories/terminal-instance-repository.js +89 -7
  232. package/dist/server/storage/repositories/terminal-instance-repository.js.map +1 -1
  233. package/dist/server/storage/repositories/workspace-navigation-state-repository.d.ts +9 -0
  234. package/dist/server/storage/repositories/workspace-navigation-state-repository.js +49 -0
  235. package/dist/server/storage/repositories/workspace-navigation-state-repository.js.map +1 -0
  236. package/dist/server/storage/repositories/workspace-repository.d.ts +7 -1
  237. package/dist/server/storage/repositories/workspace-repository.js +32 -8
  238. package/dist/server/storage/repositories/workspace-repository.js.map +1 -1
  239. package/dist/server/storage/repositories/workspace-worktree-repository.d.ts +13 -0
  240. package/dist/server/storage/repositories/workspace-worktree-repository.js +158 -0
  241. package/dist/server/storage/repositories/workspace-worktree-repository.js.map +1 -0
  242. package/dist/server/storage/sqlite/client.js +301 -0
  243. package/dist/server/storage/sqlite/client.js.map +1 -1
  244. package/dist/server/storage/sqlite/schema.sql +238 -0
  245. package/dist/server/types/domain.d.ts +231 -0
  246. package/dist/server/ws/workbench-ws-hub.js +33 -9
  247. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  248. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +18 -6
  249. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  250. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +14 -2
  251. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  252. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +25 -1
  253. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  254. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +6 -0
  255. package/package.json +1 -1
  256. package/dist/public/assets/index-1VIm8lVL.css +0 -1
  257. 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,14 @@ 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();
51
61
  workspaceSessionRelations = new Map();
52
- constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}) {
62
+ constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}, taskManager = createTaskManager()) {
53
63
  this.db = db;
54
64
  this.workspaceRepository = workspaceRepository;
55
65
  this.sessionBindingRepository = sessionBindingRepository;
@@ -61,6 +71,7 @@ export class SessionHistoryService {
61
71
  this.sessionMessageOriginRepository = sessionMessageOriginRepository;
62
72
  this.sessionActivityAuthorityService = sessionActivityAuthorityService;
63
73
  this.sessionForkRepository = sessionForkRepository ?? new SessionForkRepository(db);
74
+ this.taskManager = taskManager;
64
75
  this.claudeCodeHomeDir = config.claudeCodeHomeDir;
65
76
  this.providerCliCommandPaths = {
66
77
  "claude-code": process.platform === "win32" ? "claude.cmd" : "claude",
@@ -70,6 +81,18 @@ export class SessionHistoryService {
70
81
  };
71
82
  // CLI 是否可用只在 Host 启动时探测一次;后续统一读缓存,更新 CLI 后重启 Host 生效。
72
83
  this.providerCliAvailability = buildProviderCliAvailabilitySnapshot(this.providerCliCommandPaths);
84
+ this.providerSessionDiscoveryConfig = {
85
+ claudeCodeHomeDir: config.claudeCodeHomeDir,
86
+ codexCliPath: config.codexCliPath,
87
+ codexHomeDir: config.codexHomeDir,
88
+ geminiCliPath: config.geminiCliPath,
89
+ geminiHomeDir: config.geminiHomeDir,
90
+ kimiDefaultModel: config.kimiDefaultModel,
91
+ kimiHomeDir: config.kimiHomeDir,
92
+ opencodeBaseUrl: config.opencodeBaseUrl,
93
+ opencodeDataDir: config.opencodeDataDir,
94
+ opencodeDbPath: config.opencodeDbPath
95
+ };
73
96
  this.providerRegistry = new ProviderRegistry([
74
97
  new ClaudeCodeAdapter({ homeDir: config.claudeCodeHomeDir }),
75
98
  new CodexAdapter({
@@ -102,6 +125,32 @@ export class SessionHistoryService {
102
125
  baseUrlResolver: config.opencodeBaseUrlResolver?.resolve.bind(config.opencodeBaseUrlResolver),
103
126
  commandPath: config.opencodeCliPath
104
127
  });
128
+ this.registerBackgroundTasks();
129
+ }
130
+ observeBackgroundTaskMetrics() {
131
+ return this.taskManager.observe();
132
+ }
133
+ registerBackgroundTasks() {
134
+ if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscovery)) {
135
+ this.taskManager.register({
136
+ taskType: HOST_TASK_TYPES.workspaceDiscovery,
137
+ executionLane: "helper_process",
138
+ run: async ({ workspaceId, userId, refreshStateMode }) => this.runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode)
139
+ });
140
+ }
141
+ if (!this.taskManager.has(HOST_TASK_TYPES.providerCapabilityRefresh)) {
142
+ this.taskManager.register({
143
+ taskType: HOST_TASK_TYPES.providerCapabilityRefresh,
144
+ executionLane: "external_process",
145
+ run: async ({ capabilities, workspacePath }) => {
146
+ const value = await this.enrichProviderCapabilities(capabilities, workspacePath);
147
+ this.providerCapabilityCache.set(buildProviderCapabilityCacheKey(capabilities.provider, workspacePath), {
148
+ refreshedAt: Date.now(),
149
+ value
150
+ });
151
+ }
152
+ });
153
+ }
105
154
  }
106
155
  async discoverWorkspaceSessions(workspaceId, userId, options) {
107
156
  const maxAgeMs = options?.maxAgeMs ?? 0;
@@ -112,17 +161,47 @@ export class SessionHistoryService {
112
161
  discoveryStatus?.isComplete === true &&
113
162
  maxAgeMs > 0 &&
114
163
  Date.now() - lastRefreshedAt <= maxAgeMs) {
164
+ this.taskManager.recordCacheHit(HOST_TASK_TYPES.workspaceDiscovery, workspaceId);
115
165
  return this.listWorkspaceSessions(workspaceId, userId);
116
166
  }
117
- const inflight = this.workspaceDiscoveryInflight.get(workspaceId);
118
- if (inflight) {
119
- return inflight;
167
+ return this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscovery, {
168
+ key: workspaceId,
169
+ source: "session_history.discover_workspace_sessions",
170
+ input: {
171
+ workspaceId,
172
+ userId,
173
+ refreshStateMode: options?.refreshStateMode ?? "inline"
174
+ }
175
+ }).promise;
176
+ }
177
+ requestWorkspaceDiscovery(workspaceId, userId, options) {
178
+ const maxAgeMs = options?.maxAgeMs ?? WORKSPACE_DISCOVERY_BACKGROUND_MAX_AGE_MS;
179
+ const force = options?.force ?? false;
180
+ if (!force && !this.needsWorkspaceDiscovery(workspaceId, maxAgeMs)) {
181
+ return;
182
+ }
183
+ const task = this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscovery, {
184
+ key: workspaceId,
185
+ source: "session_history.request_workspace_discovery",
186
+ input: {
187
+ workspaceId,
188
+ userId,
189
+ refreshStateMode: options?.refreshStateMode ?? "deferred"
190
+ }
191
+ });
192
+ if (task.deduped) {
193
+ return;
120
194
  }
121
- const task = this.runDiscoverWorkspaceSessions(workspaceId, userId, options?.refreshStateMode ?? "inline").finally(() => {
122
- this.workspaceDiscoveryInflight.delete(workspaceId);
195
+ void task.promise.catch((error) => {
196
+ logPerformance("workspace.discover_sessions.background_failed", 0, {
197
+ workspaceId,
198
+ error: error instanceof Error ? error.message : "unknown"
199
+ }, {
200
+ thresholdMs: 0,
201
+ force: true
202
+ });
203
+ return this.listWorkspaceSessions(workspaceId, userId);
123
204
  });
124
- this.workspaceDiscoveryInflight.set(workspaceId, task);
125
- return task;
126
205
  }
127
206
  needsWorkspaceDiscovery(workspaceId, maxAgeMs) {
128
207
  if (maxAgeMs <= 0) {
@@ -246,13 +325,13 @@ export class SessionHistoryService {
246
325
  const binding = this.getBindingOrThrow(sessionId);
247
326
  await this.syncSessionTitleFromProvider(sessionId, binding);
248
327
  }
249
- async syncWorkspaceSessionTitles(workspaceId, userId) {
328
+ async syncWorkspaceSessionTitles(workspaceId, userId, concurrency = 1) {
250
329
  const sessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
251
- for (const session of sessions) {
330
+ await runWithConcurrency(sessions, concurrency, async (session) => {
252
331
  await this.syncSessionTitle(session.sessionId).catch(() => {
253
332
  return;
254
333
  });
255
- }
334
+ });
256
335
  }
257
336
  async listSessionChangedFiles(sessionId, userId) {
258
337
  this.getSession(sessionId, userId);
@@ -266,7 +345,7 @@ export class SessionHistoryService {
266
345
  }
267
346
  getProviderCapabilitiesSnapshot(provider) {
268
347
  try {
269
- return this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider));
348
+ return this.resolveProviderCapabilitiesImmediate(this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider)), null);
270
349
  }
271
350
  catch (error) {
272
351
  throw mapSessionProviderError(error);
@@ -275,7 +354,9 @@ export class SessionHistoryService {
275
354
  async getProviderCapabilities(provider, workspaceId) {
276
355
  try {
277
356
  const workspacePath = workspaceId ? this.getWorkspaceOrThrow(workspaceId).path : null;
278
- return await this.enrichProviderCapabilities(this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider)), workspacePath);
357
+ const baseCapabilities = this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider));
358
+ this.scheduleProviderCapabilityRefresh(baseCapabilities, workspacePath);
359
+ return this.resolveProviderCapabilitiesImmediate(baseCapabilities, workspacePath);
279
360
  }
280
361
  catch (error) {
281
362
  throw mapSessionProviderError(error);
@@ -284,9 +365,14 @@ export class SessionHistoryService {
284
365
  async getSessionCapabilities(sessionId) {
285
366
  const binding = this.getBindingOrThrow(sessionId);
286
367
  const workspace = this.getWorkspaceOrThrow(binding.workspaceId);
368
+ const workspacePath = workspace.path;
287
369
  return this.capabilityService
288
370
  .getSessionCapabilities(binding.provider, binding.providerSessionId)
289
- .then((capabilities) => this.enrichProviderCapabilities(this.applyProviderCliAvailability(capabilities), workspace.path))
371
+ .then((capabilities) => {
372
+ const normalizedCapabilities = this.applyProviderCliAvailability(capabilities);
373
+ this.scheduleProviderCapabilityRefresh(normalizedCapabilities, workspacePath);
374
+ return this.resolveProviderCapabilitiesImmediate(normalizedCapabilities, workspacePath);
375
+ })
290
376
  .catch((error) => {
291
377
  throw mapSessionProviderError(error);
292
378
  });
@@ -299,6 +385,48 @@ export class SessionHistoryService {
299
385
  const codexEnriched = await enrichCodexCapabilities(claudeEnriched, this.codexModelOptionsService);
300
386
  return enrichOpenCodeCapabilities(codexEnriched, this.openCodeModelOptionsService, workspacePath);
301
387
  }
388
+ resolveProviderCapabilitiesImmediate(capabilities, workspacePath) {
389
+ const cacheKey = buildProviderCapabilityCacheKey(capabilities.provider, workspacePath);
390
+ const cached = this.providerCapabilityCache.get(cacheKey);
391
+ if (cached) {
392
+ this.taskManager.recordCacheHit(HOST_TASK_TYPES.providerCapabilityRefresh, cacheKey);
393
+ return cached.value;
394
+ }
395
+ const claudeEnriched = enrichClaudeCapabilities(capabilities, {
396
+ claudeHomeDir: this.claudeCodeHomeDir,
397
+ workspacePath
398
+ });
399
+ return applyImmediateModelOptionFallbacks(claudeEnriched, this.codexModelOptionsService.peekSnapshot(), this.openCodeModelOptionsService.peekSnapshot(workspacePath));
400
+ }
401
+ scheduleProviderCapabilityRefresh(capabilities, workspacePath) {
402
+ const cacheKey = buildProviderCapabilityCacheKey(capabilities.provider, workspacePath);
403
+ const cached = this.providerCapabilityCache.get(cacheKey);
404
+ if (cached &&
405
+ Date.now() - cached.refreshedAt <= PROVIDER_CAPABILITY_CACHE_MAX_AGE_MS) {
406
+ return;
407
+ }
408
+ const task = this.taskManager.enqueue(HOST_TASK_TYPES.providerCapabilityRefresh, {
409
+ key: cacheKey,
410
+ source: "session_history.provider_capability_refresh",
411
+ input: {
412
+ capabilities,
413
+ workspacePath
414
+ }
415
+ });
416
+ if (task.deduped) {
417
+ return;
418
+ }
419
+ void task.promise.catch((error) => {
420
+ logPerformance("provider.capabilities.background_failed", 0, {
421
+ provider: capabilities.provider,
422
+ workspacePath,
423
+ error: error instanceof Error ? error.message : "unknown"
424
+ }, {
425
+ thresholdMs: 0,
426
+ force: true
427
+ });
428
+ });
429
+ }
302
430
  applyProviderCliAvailability(capabilities) {
303
431
  if (!isProviderCliBacked(capabilities.provider)) {
304
432
  return capabilities;
@@ -472,6 +600,7 @@ export class SessionHistoryService {
472
600
  rawStoreRef: binding.rawStoreRef,
473
601
  sourceType: input.sourceType,
474
602
  sourceMessageId,
603
+ sourceMessageSnapshot: input.sourceMessageSnapshot ?? null,
475
604
  strategy: input.strategy ?? "auto"
476
605
  });
477
606
  const sessionId = createId();
@@ -561,7 +690,7 @@ export class SessionHistoryService {
561
690
  throw mapSessionProviderError(new Error("FORK_TARGET_PROVIDER_NOT_SUPPORTED"));
562
691
  }
563
692
  const sourceIndex = this.sessionIndexRepository.findIndexRecordBySessionId(input.sessionId);
564
- const inheritedMessages = await this.readForkSourceMessages(input.sessionId, sourceBinding, input.sourceType, sourceMessageId);
693
+ const inheritedMessages = await this.readForkSourceMessages(input.sessionId, sourceBinding, input.sourceType, sourceMessageId, input.sourceMessageSnapshot ?? null);
565
694
  const reconstructedMessages = inheritedMessages.filter((message) => (message.role === "user" || message.role === "assistant")
566
695
  && message.kind === "text"
567
696
  && message.content.trim().length > 0);
@@ -624,7 +753,7 @@ export class SessionHistoryService {
624
753
  this.workspaceSessionRelations.set(sourceBinding.workspaceId, relationMap);
625
754
  return this.getSessionListItemOrThrow(startedSession.sessionId, input.userId);
626
755
  }
627
- async readForkSourceMessages(sessionId, binding, sourceType, sourceMessageId) {
756
+ async readForkSourceMessages(sessionId, binding, sourceType, sourceMessageId, sourceMessageSnapshot = null) {
628
757
  const messages = [];
629
758
  let cursor = null;
630
759
  while (true) {
@@ -642,7 +771,21 @@ export class SessionHistoryService {
642
771
  if (targetIndex < 0) {
643
772
  throw mapSessionProviderError(new Error("FORK_SOURCE_MESSAGE_NOT_FOUND"));
644
773
  }
645
- return messages.slice(0, targetIndex + 1);
774
+ const inheritedMessages = messages.slice(0, targetIndex + 1);
775
+ if (!sourceMessageSnapshot) {
776
+ return inheritedMessages;
777
+ }
778
+ const targetMessage = inheritedMessages[targetIndex];
779
+ if (!targetMessage) {
780
+ return inheritedMessages;
781
+ }
782
+ inheritedMessages[targetIndex] = {
783
+ ...targetMessage,
784
+ role: sourceMessageSnapshot.role,
785
+ kind: sourceMessageSnapshot.kind,
786
+ content: sourceMessageSnapshot.content
787
+ };
788
+ return inheritedMessages;
646
789
  }
647
790
  assertForkDepthWithinLimit(parentSessionId) {
648
791
  const nextDepth = this.getSessionForkDepth(parentSessionId) + 1;
@@ -969,16 +1112,29 @@ export class SessionHistoryService {
969
1112
  }
970
1113
  async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline") {
971
1114
  const startedAt = Date.now();
1115
+ const debugStartedAtMs = terminalDebugNowMs();
972
1116
  const workspace = this.getWorkspaceOrThrow(workspaceId);
973
1117
  let discoverDurationMs = 0;
974
1118
  let persistDurationMs = 0;
1119
+ let persistPass1DurationMs = 0;
1120
+ let persistPass1BatchCount = 0;
1121
+ let persistPass1MaxBatchMs = 0;
1122
+ let relationMapDurationMs = 0;
1123
+ let persistPass2DurationMs = 0;
1124
+ let persistPass2BatchCount = 0;
1125
+ let persistPass2MaxBatchMs = 0;
1126
+ let cleanupDurationMs = 0;
1127
+ let listItemsDurationMs = 0;
1128
+ let refreshStateDurationMs = 0;
975
1129
  const refreshStateCount = 10;
976
1130
  try {
977
1131
  const discoverStartedAt = Date.now();
978
1132
  const existingWorkspaceSessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
979
1133
  const knownSessions = this.buildKnownSessionSummaries(existingWorkspaceSessions, workspace.path);
980
- const discovery = await this.sessionSyncService
981
- .discoverWorkspaceSessions(workspace.path, {
1134
+ const discovery = await this.providerDiscoveryHelperClient
1135
+ .discoverWorkspaceSessions({
1136
+ config: this.providerSessionDiscoveryConfig,
1137
+ workspacePath: workspace.path,
982
1138
  knownSessions
983
1139
  })
984
1140
  .catch((error) => {
@@ -990,10 +1146,9 @@ export class SessionHistoryService {
990
1146
  const discoveredSessionIds = new Map();
991
1147
  const persistedSessions = [];
992
1148
  const claimedPendingSessionIds = new Set();
993
- const persist = this.db.transaction(() => {
994
- for (const session of sessions) {
1149
+ const persistPass1Transaction = this.db.transaction((batch) => {
1150
+ for (const session of batch) {
995
1151
  const exactExisting = this.sessionBindingRepository.findByProviderSession(session.provider, session.providerSessionId) ?? this.sessionBindingRepository.findByRawStoreRef(session.provider, session.rawStoreRef);
996
- // discover 只能补全当前工作区,不能把别的工作区已有会话偷过来重绑。
997
1152
  if (exactExisting && exactExisting.workspaceId !== workspaceId) {
998
1153
  continue;
999
1154
  }
@@ -1065,8 +1220,17 @@ export class SessionHistoryService {
1065
1220
  existingIndex
1066
1221
  });
1067
1222
  }
1068
- const relationMap = this.buildWorkspaceSessionRelationMap(sessions, discoveredSessionIds);
1069
- for (const persistedSession of persistedSessions) {
1223
+ });
1224
+ const persistPass1StartedAt = Date.now();
1225
+ const persistPass1Stats = await runBatchedTransactions(sessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass1Transaction);
1226
+ persistPass1DurationMs = Date.now() - persistPass1StartedAt;
1227
+ persistPass1BatchCount = persistPass1Stats.batchCount;
1228
+ persistPass1MaxBatchMs = persistPass1Stats.maxBatchMs;
1229
+ const relationMapStartedAt = Date.now();
1230
+ const relationMap = this.buildWorkspaceSessionRelationMap(sessions, discoveredSessionIds);
1231
+ relationMapDurationMs = Date.now() - relationMapStartedAt;
1232
+ const persistPass2Transaction = this.db.transaction((batch) => {
1233
+ for (const persistedSession of batch) {
1070
1234
  const relation = relationMap.get(persistedSession.sessionId);
1071
1235
  const resolvedParentSessionId = relation?.parentSessionId
1072
1236
  ?? persistedSession.existingIndex?.parentSessionId
@@ -1104,26 +1268,56 @@ export class SessionHistoryService {
1104
1268
  });
1105
1269
  }
1106
1270
  });
1107
- const persistStartedAt = Date.now();
1108
- persist();
1109
- persistDurationMs = Date.now() - persistStartedAt;
1271
+ const persistPass2StartedAt = Date.now();
1272
+ const persistPass2Stats = await runBatchedTransactions(persistedSessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass2Transaction);
1273
+ persistPass2DurationMs = Date.now() - persistPass2StartedAt;
1274
+ persistPass2BatchCount = persistPass2Stats.batchCount;
1275
+ persistPass2MaxBatchMs = persistPass2Stats.maxBatchMs;
1276
+ persistDurationMs = persistPass1DurationMs + relationMapDurationMs + persistPass2DurationMs;
1110
1277
  if (discovery.isComplete) {
1111
- this.cleanupStaleHiddenSessions(workspaceId, userId, sessions);
1278
+ const cleanupStartedAt = Date.now();
1279
+ await this.cleanupStaleHiddenSessions(workspaceId, userId, sessions);
1280
+ cleanupDurationMs = Date.now() - cleanupStartedAt;
1112
1281
  }
1113
- this.workspaceSessionRelations.set(workspaceId, this.buildWorkspaceSessionRelationMap(sessions, discoveredSessionIds));
1282
+ this.workspaceSessionRelations.set(workspaceId, relationMap);
1283
+ const listItemsStartedAt = Date.now();
1114
1284
  const items = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
1285
+ listItemsDurationMs = Date.now() - listItemsStartedAt;
1115
1286
  const refreshCandidates = buildSessionStateRefreshCandidates(items, refreshStateCount);
1116
1287
  this.workspaceDiscoveryStatuses.set(workspaceId, {
1117
1288
  refreshedAt: Date.now(),
1118
1289
  isComplete: discovery.isComplete
1119
1290
  });
1291
+ const refreshStateStartedAt = Date.now();
1120
1292
  if (refreshStateMode === "inline") {
1121
1293
  await this.refreshRecentSessionStates(refreshCandidates, userId);
1122
1294
  }
1123
1295
  else {
1124
1296
  this.scheduleWorkspaceStateRefresh(workspaceId, userId, refreshCandidates);
1125
1297
  }
1298
+ refreshStateDurationMs = Date.now() - refreshStateStartedAt;
1126
1299
  const nextItems = this.listWorkspaceSessions(workspaceId, userId);
1300
+ if (isTerminalDebugEnabled()) {
1301
+ logTerminalDebug("workspace.discovery.completed", {
1302
+ workspaceId,
1303
+ sessionCount: sessions.length,
1304
+ returnedSessionCount: nextItems.length,
1305
+ discoverMs: discoverDurationMs,
1306
+ persistMs: persistDurationMs,
1307
+ persistPass1Ms: persistPass1DurationMs,
1308
+ persistPass1BatchCount,
1309
+ persistPass1MaxBatchMs,
1310
+ relationMapMs: relationMapDurationMs,
1311
+ persistPass2Ms: persistPass2DurationMs,
1312
+ persistPass2BatchCount,
1313
+ persistPass2MaxBatchMs,
1314
+ cleanupMs: cleanupDurationMs,
1315
+ listItemsMs: listItemsDurationMs,
1316
+ refreshStateMs: refreshStateDurationMs,
1317
+ refreshStateDeferred: refreshStateMode !== "inline",
1318
+ durationMs: terminalDebugNowMs() - debugStartedAtMs
1319
+ });
1320
+ }
1127
1321
  logPerformance("workspace.discover_sessions", Date.now() - startedAt, {
1128
1322
  workspaceId,
1129
1323
  workspacePath: workspace.path,
@@ -1135,6 +1329,16 @@ export class SessionHistoryService {
1135
1329
  refreshedStates: refreshCandidates.length,
1136
1330
  discoverMs: discoverDurationMs,
1137
1331
  persistMs: persistDurationMs,
1332
+ persistPass1Ms: persistPass1DurationMs,
1333
+ persistPass1BatchCount,
1334
+ persistPass1MaxBatchMs,
1335
+ relationMapMs: relationMapDurationMs,
1336
+ persistPass2Ms: persistPass2DurationMs,
1337
+ persistPass2BatchCount,
1338
+ persistPass2MaxBatchMs,
1339
+ cleanupMs: cleanupDurationMs,
1340
+ listItemsMs: listItemsDurationMs,
1341
+ refreshStateMs: refreshStateDurationMs,
1138
1342
  refreshStateDeferred: refreshStateMode !== "inline"
1139
1343
  }, {
1140
1344
  thresholdMs: 500
@@ -1147,6 +1351,16 @@ export class SessionHistoryService {
1147
1351
  workspacePath: workspace.path,
1148
1352
  discoverMs: discoverDurationMs,
1149
1353
  persistMs: persistDurationMs,
1354
+ persistPass1Ms: persistPass1DurationMs,
1355
+ persistPass1BatchCount,
1356
+ persistPass1MaxBatchMs,
1357
+ relationMapMs: relationMapDurationMs,
1358
+ persistPass2Ms: persistPass2DurationMs,
1359
+ persistPass2BatchCount,
1360
+ persistPass2MaxBatchMs,
1361
+ cleanupMs: cleanupDurationMs,
1362
+ listItemsMs: listItemsDurationMs,
1363
+ refreshStateMs: refreshStateDurationMs,
1150
1364
  refreshStateDeferred: refreshStateMode !== "inline",
1151
1365
  error: error instanceof Error ? error.message : "unknown"
1152
1366
  }, {
@@ -1391,7 +1605,12 @@ export class SessionHistoryService {
1391
1605
  if (shouldSkipClaudePendingBinding(binding)) {
1392
1606
  return;
1393
1607
  }
1394
- const nextTitle = (await this.sessionSyncService.readSessionTitle(binding.provider, binding.providerSessionId, binding.rawStoreRef)).trim();
1608
+ const nextTitle = (await this.providerDiscoveryHelperClient.readSessionTitle({
1609
+ config: this.providerSessionDiscoveryConfig,
1610
+ provider: binding.provider,
1611
+ providerSessionId: binding.providerSessionId,
1612
+ rawStoreRef: binding.rawStoreRef
1613
+ })).trim();
1395
1614
  const resolvedTitle = resolvePersistedSessionTitle(binding.provider, nextTitle, currentIndex.title);
1396
1615
  if (resolvedTitle.length === 0 || resolvedTitle === currentIndex.title) {
1397
1616
  return;
@@ -1548,7 +1767,7 @@ export class SessionHistoryService {
1548
1767
  });
1549
1768
  this.workspaceStateRefreshInflight.set(inflightKey, task);
1550
1769
  }
1551
- cleanupStaleHiddenSessions(workspaceId, userId, sessions) {
1770
+ async cleanupStaleHiddenSessions(workspaceId, userId, sessions) {
1552
1771
  const discoveredProviderSessionIds = new Set(sessions.map((session) => buildProviderSessionKey(session.provider, session.providerSessionId)));
1553
1772
  const discoveredRawStoreRefs = new Set(sessions.map((session) => session.rawStoreRef));
1554
1773
  const staleHiddenSessions = this.sessionIndexRepository
@@ -1568,15 +1787,12 @@ export class SessionHistoryService {
1568
1787
  if (staleHiddenSessions.length === 0) {
1569
1788
  return;
1570
1789
  }
1571
- this.deleteSessionsByIds(staleHiddenSessions.map((session) => session.sessionId));
1572
- }
1573
- deleteSessionsByIds(sessionIds) {
1574
- const remove = this.db.transaction((ids) => {
1790
+ const deleteTransaction = this.db.transaction((ids) => {
1575
1791
  for (const sessionId of ids) {
1576
1792
  this.deleteSessionById(sessionId);
1577
1793
  }
1578
1794
  });
1579
- remove(sessionIds);
1795
+ await runBatchedTransactions(staleHiddenSessions.map((session) => session.sessionId), WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, deleteTransaction);
1580
1796
  }
1581
1797
  findPendingBindingDuplicate(sessionId, workspaceId, currentBinding, snapshot) {
1582
1798
  if (!currentBinding || !isPendingBindingValue(currentBinding.providerSessionId)) {
@@ -2620,4 +2836,71 @@ function buildReconstructedForkPrompt(input) {
2620
2836
  }
2621
2837
  return lines.join("\n").trim();
2622
2838
  }
2839
+ function buildProviderCapabilityCacheKey(provider, workspacePath) {
2840
+ return `${provider}::${workspacePath ?? ""}`;
2841
+ }
2842
+ async function runWithConcurrency(items, concurrency, worker) {
2843
+ const normalizedConcurrency = Math.max(1, Math.floor(concurrency) || 1);
2844
+ const queue = [...items];
2845
+ const runners = Array.from({
2846
+ length: Math.min(normalizedConcurrency, queue.length || 1)
2847
+ }, async () => {
2848
+ while (queue.length > 0) {
2849
+ const current = queue.shift();
2850
+ if (current === undefined) {
2851
+ return;
2852
+ }
2853
+ await worker(current);
2854
+ }
2855
+ });
2856
+ await Promise.all(runners);
2857
+ }
2858
+ async function runBatchedTransactions(items, batchSize, transaction) {
2859
+ const normalizedBatchSize = Math.max(1, Math.floor(batchSize) || 1);
2860
+ let batchCount = 0;
2861
+ let maxBatchMs = 0;
2862
+ for (let index = 0; index < items.length; index += normalizedBatchSize) {
2863
+ const batch = items.slice(index, index + normalizedBatchSize);
2864
+ const batchStartedAt = Date.now();
2865
+ transaction(batch);
2866
+ const batchDurationMs = Date.now() - batchStartedAt;
2867
+ batchCount += 1;
2868
+ maxBatchMs = Math.max(maxBatchMs, batchDurationMs);
2869
+ if (index + normalizedBatchSize < items.length) {
2870
+ await delay(0);
2871
+ }
2872
+ }
2873
+ return {
2874
+ batchCount,
2875
+ maxBatchMs
2876
+ };
2877
+ }
2878
+ function applyImmediateModelOptionFallbacks(capabilities, codexSnapshot, openCodeSnapshot) {
2879
+ if (capabilities.provider === "codex") {
2880
+ return {
2881
+ ...capabilities,
2882
+ modelOptions: codexSnapshot?.modelOptions ?? createFallbackCodexModelOptions(null),
2883
+ defaultReasoningLevel: codexSnapshot?.defaultReasoningLevel ?? null,
2884
+ limitations: codexSnapshot
2885
+ ? capabilities.limitations
2886
+ : Array.from(new Set([
2887
+ ...capabilities.limitations,
2888
+ "当前暂时使用缓存或兜底模型列表,后台会继续刷新 Codex 能力。"
2889
+ ]))
2890
+ };
2891
+ }
2892
+ if (capabilities.provider === "opencode") {
2893
+ return {
2894
+ ...capabilities,
2895
+ modelOptions: openCodeSnapshot?.modelOptions ?? createFallbackOpenCodeModelOptions(null),
2896
+ limitations: openCodeSnapshot
2897
+ ? capabilities.limitations
2898
+ : Array.from(new Set([
2899
+ ...capabilities.limitations,
2900
+ "当前暂时使用缓存或兜底模型列表,后台会继续刷新 OpenCode 能力。"
2901
+ ]))
2902
+ };
2903
+ }
2904
+ return capabilities;
2905
+ }
2623
2906
  //# sourceMappingURL=session-history-service.js.map