@jingyi0605/codingns 0.9.5 → 0.9.7

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 (206) hide show
  1. package/bin/codingns.mjs +75 -0
  2. package/dist/public/assets/{AdaptiveButlerPage-kkJDsnCO.js → AdaptiveButlerPage-DclGPzEx.js} +2 -2
  3. package/dist/public/assets/{App-DrNI9lWA.js → App-BxX5mm9o.js} +6 -6
  4. package/dist/public/assets/{BootstrapPage-QgVH5Mps.js → BootstrapPage-Bl21SsuW.js} +1 -1
  5. package/dist/public/assets/{ConversationPage-DVk8VfIj.js → ConversationPage-CmiVCV0q.js} +7 -7
  6. package/dist/public/assets/{DesktopDetachPreviewPage-BhfP0TpH.js → DesktopDetachPreviewPage-uaOHVsjV.js} +1 -1
  7. package/dist/public/assets/{DesktopModal-DRmDrv0S.js → DesktopModal-BxsogpLf.js} +1 -1
  8. package/dist/public/assets/{DesktopWindowPage-DNbJXnSs.js → DesktopWindowPage-Bubfw1nC.js} +1 -1
  9. package/dist/public/assets/{FileContextPanel---fLO4ve.js → FileContextPanel-DrYWcTkp.js} +1 -1
  10. package/dist/public/assets/{GitSidebar-sXUE0TqT.js → GitSidebar-CQsfJqun.js} +1 -1
  11. package/dist/public/assets/{MobileCreateSessionSheet-BftZ5pvb.js → MobileCreateSessionSheet-CqtmfVNo.js} +1 -1
  12. package/dist/public/assets/{MobileSheet-nw5SCa3N.js → MobileSheet-C5IVmUsO.js} +1 -1
  13. package/dist/public/assets/{MobileTopHeaderFrame-DH_D02Wy.js → MobileTopHeaderFrame-EoBm3-X0.js} +1 -1
  14. package/dist/public/assets/{MobileWorkspaceSwitcherHeader-2K406G5p.js → MobileWorkspaceSwitcherHeader-BC8MMQs_.js} +1 -1
  15. package/dist/public/assets/{PluginAccessOverview-BVJihw3D.js → PluginAccessOverview-CrQiQxxZ.js} +1 -1
  16. package/dist/public/assets/{PluginContainerPage-CR4vStvr.js → PluginContainerPage-_2u-9thM.js} +1 -1
  17. package/dist/public/assets/{PluginDetailPage-CrMX0Mnm.js → PluginDetailPage-F9cKjSCp.js} +1 -1
  18. package/dist/public/assets/{PluginsListPage-FtIL71Yg.js → PluginsListPage-BOhcua_4.js} +1 -1
  19. package/dist/public/assets/{RelayConnectEntryPage-Bt1apX53.js → RelayConnectEntryPage-Ck_uZLzj.js} +1 -1
  20. package/dist/public/assets/{ServerSettingsModal-D-guzPrI.js → ServerSettingsModal-ICd82a_x.js} +1 -1
  21. package/dist/public/assets/SessionIndexPage-Cox6P6dw.js +1 -0
  22. package/dist/public/assets/SettingsPage-BI5cM3j5.js +2 -0
  23. package/dist/public/assets/{TerminalManagerPanel-B5MKGPy-.js → TerminalManagerPanel-DiVhBQhf.js} +1 -1
  24. package/dist/public/assets/{TerminalPage-C2dTNGHK.js → TerminalPage-DUMUO7Ng.js} +1 -1
  25. package/dist/public/assets/{TerminalRuntimeFallbackModal-DAqOxFD8.js → TerminalRuntimeFallbackModal-DGPR_CMh.js} +1 -1
  26. package/dist/public/assets/{ToolFilesPage-IsNwyE6T.js → ToolFilesPage-m88CAp-r.js} +1 -1
  27. package/dist/public/assets/{ToolGitPage-BK1JBERN.js → ToolGitPage-DCYpfTsb.js} +1 -1
  28. package/dist/public/assets/{ToolProcessesPage-DwTYUQCK.js → ToolProcessesPage-Bk5ulsyq.js} +1 -1
  29. package/dist/public/assets/{ToolsHomePage-BLOy7lPg.js → ToolsHomePage-BrTwfjI7.js} +1 -1
  30. package/dist/public/assets/{WorkbenchLandingPage-CqZKR6EA.js → WorkbenchLandingPage-q4AAmdMV.js} +1 -1
  31. package/dist/public/assets/WorkbenchLayout-DD8b-m2G.js +1027 -0
  32. package/dist/public/assets/{WorkbenchModal-BM-OeW-b.js → WorkbenchModal-CsZeLArg.js} +1 -1
  33. package/dist/public/assets/WorkbenchShellRoute-BaiW_vfb.js +1 -0
  34. package/dist/public/assets/WorkbenchShellRoute-CxKYZ6uF.css +1 -0
  35. package/dist/public/assets/{WorkspaceDebugDetailPage-BMsEN5iG.js → WorkspaceDebugDetailPage-BcUUDEyw.js} +1 -1
  36. package/dist/public/assets/WorkspaceDetailPage-zEZ1VARA.js +1 -0
  37. package/dist/public/assets/{WorkspaceHomePage-DQiXKgiP.js → WorkspaceHomePage-CR0rqS4e.js} +1 -1
  38. package/dist/public/assets/{client-runtime-manager-CgPJq21V.js → client-runtime-manager-BgGugw8T.js} +1 -1
  39. package/dist/public/assets/index-CFyk1rgJ.js +50 -0
  40. package/dist/public/assets/index-CrU73EIV.css +1 -0
  41. package/dist/public/assets/{login-direct-candidate-resolver-CGaxAXV8.js → login-direct-candidate-resolver-ty2uOY5y.js} +1 -1
  42. package/dist/public/assets/{plugin-permission-copy-BR9gWy8b.js → plugin-permission-copy-iK3faI3n.js} +1 -1
  43. package/dist/public/assets/{plugins-api-CdCsrG2e.js → plugins-api-CmV7aDGA.js} +1 -1
  44. package/dist/public/assets/{preferences-service-lOhnlxzP.js → preferences-service-BoSmT_ny.js} +1 -1
  45. package/dist/public/assets/{relay-entry-CQpxTS8y.js → relay-entry-C8k5qsl5.js} +1 -1
  46. package/dist/public/assets/styles-BhKoKfQ_.css +1 -0
  47. package/dist/public/assets/{terminal-runtime-meta-oteTx66X.js → terminal-runtime-meta-okQIDzfA.js} +1 -1
  48. package/dist/public/assets/{useRegisteredDebugTemplates-Bu2ykZ6s.js → useRegisteredDebugTemplates-B_vXUtSe.js} +1 -1
  49. package/dist/public/assets/{workbench-navigation-DlgXuFW2.js → workbench-navigation-DoDaQR4z.js} +1 -1
  50. package/dist/public/index.html +2 -2
  51. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.d.ts +1 -0
  52. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.js +31 -0
  53. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.js.map +1 -1
  54. package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.d.ts +1 -0
  55. package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.js +1 -0
  56. package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.js.map +1 -1
  57. package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.js +4 -1
  58. package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.js.map +1 -1
  59. package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.js +140 -16
  60. package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.js.map +1 -1
  61. package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +1 -1
  62. package/dist/server/modules/assistant-capability/assistant-capability-controller.js +8 -7
  63. package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
  64. package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +14 -13
  65. package/dist/server/modules/assistant-capability/assistant-capability-service.js +27 -24
  66. package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
  67. package/dist/server/modules/auth/auth-controller.d.ts +27 -1
  68. package/dist/server/modules/auth/auth-controller.js +20 -0
  69. package/dist/server/modules/auth/auth-controller.js.map +1 -1
  70. package/dist/server/modules/auth/auth-service.d.ts +32 -1
  71. package/dist/server/modules/auth/auth-service.js +217 -2
  72. package/dist/server/modules/auth/auth-service.js.map +1 -1
  73. package/dist/server/modules/bootstrap/bootstrap-service.js +1 -0
  74. package/dist/server/modules/bootstrap/bootstrap-service.js.map +1 -1
  75. package/dist/server/modules/butler/assistant-automation-service.d.ts +1 -1
  76. package/dist/server/modules/butler/assistant-automation-service.js +9 -10
  77. package/dist/server/modules/butler/assistant-automation-service.js.map +1 -1
  78. package/dist/server/modules/butler/butler-action-context-service.js +2 -2
  79. package/dist/server/modules/butler/butler-action-context-service.js.map +1 -1
  80. package/dist/server/modules/butler/butler-control-action-service.d.ts +5 -5
  81. package/dist/server/modules/butler/butler-control-action-service.js +19 -19
  82. package/dist/server/modules/butler/butler-control-action-service.js.map +1 -1
  83. package/dist/server/modules/butler/butler-control-session-service.d.ts +1 -1
  84. package/dist/server/modules/butler/butler-control-session-service.js +27 -26
  85. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  86. package/dist/server/modules/butler/butler-control-timer-service.js +4 -5
  87. package/dist/server/modules/butler/butler-control-timer-service.js.map +1 -1
  88. package/dist/server/modules/butler/butler-controller.d.ts +2 -2
  89. package/dist/server/modules/butler/butler-controller.js +19 -17
  90. package/dist/server/modules/butler/butler-controller.js.map +1 -1
  91. package/dist/server/modules/butler/butler-profile-service.d.ts +5 -5
  92. package/dist/server/modules/butler/butler-profile-service.js +17 -16
  93. package/dist/server/modules/butler/butler-profile-service.js.map +1 -1
  94. package/dist/server/modules/butler/butler-project-service.d.ts +7 -6
  95. package/dist/server/modules/butler/butler-project-service.js +35 -21
  96. package/dist/server/modules/butler/butler-project-service.js.map +1 -1
  97. package/dist/server/modules/butler/butler-session-service.d.ts +2 -2
  98. package/dist/server/modules/butler/butler-session-service.js +33 -27
  99. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  100. package/dist/server/modules/butler/butler-session-summary-service.d.ts +2 -2
  101. package/dist/server/modules/butler/butler-session-summary-service.js +23 -9
  102. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
  103. package/dist/server/modules/butler/context-aggregator.js +9 -9
  104. package/dist/server/modules/butler/context-aggregator.js.map +1 -1
  105. package/dist/server/modules/butler/patrol-execution-service.d.ts +0 -1
  106. package/dist/server/modules/butler/patrol-execution-service.js +6 -12
  107. package/dist/server/modules/butler/patrol-execution-service.js.map +1 -1
  108. package/dist/server/modules/provider/provider-discovery-runtime.js +16 -1
  109. package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
  110. package/dist/server/modules/sessions/codex-app-server-helper-client.js +14 -0
  111. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  112. package/dist/server/modules/sessions/codex-app-server-helper-process.js +59 -0
  113. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  114. package/dist/server/modules/sessions/codex-session-title-generator.d.ts +19 -0
  115. package/dist/server/modules/sessions/codex-session-title-generator.js +295 -0
  116. package/dist/server/modules/sessions/codex-session-title-generator.js.map +1 -0
  117. package/dist/server/modules/sessions/session-controller.js +3 -3
  118. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  119. package/dist/server/modules/sessions/session-history-service.d.ts +13 -3
  120. package/dist/server/modules/sessions/session-history-service.js +309 -11
  121. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  122. package/dist/server/modules/sessions/session-live-runtime-service.js +25 -15
  123. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  124. package/dist/server/modules/sessions/session-permission-request-service.js +28 -15
  125. package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
  126. package/dist/server/modules/tasks/task-types.d.ts +1 -0
  127. package/dist/server/modules/tasks/task-types.js +1 -0
  128. package/dist/server/modules/tasks/task-types.js.map +1 -1
  129. package/dist/server/modules/workbench/workbench-service.js +9 -9
  130. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  131. package/dist/server/modules/workspace/affairs-library-service.d.ts +3 -1
  132. package/dist/server/modules/workspace/affairs-library-service.js +112 -54
  133. package/dist/server/modules/workspace/affairs-library-service.js.map +1 -1
  134. package/dist/server/modules/workspace/affairs-lightweight-session-service.js +13 -9
  135. package/dist/server/modules/workspace/affairs-lightweight-session-service.js.map +1 -1
  136. package/dist/server/modules/workspace/affairs-tag-service.d.ts +12 -0
  137. package/dist/server/modules/workspace/affairs-tag-service.js +527 -2
  138. package/dist/server/modules/workspace/affairs-tag-service.js.map +1 -1
  139. package/dist/server/modules/workspace/workspace-controller.d.ts +1 -1
  140. package/dist/server/modules/workspace/workspace-controller.js +7 -7
  141. package/dist/server/modules/workspace/workspace-controller.js.map +1 -1
  142. package/dist/server/modules/workspace/workspace-service.d.ts +7 -0
  143. package/dist/server/modules/workspace/workspace-service.js +151 -10
  144. package/dist/server/modules/workspace/workspace-service.js.map +1 -1
  145. package/dist/server/routes/auth.js +6 -0
  146. package/dist/server/routes/auth.js.map +1 -1
  147. package/dist/server/server/create-server.d.ts +4 -0
  148. package/dist/server/server/create-server.js +26 -0
  149. package/dist/server/server/create-server.js.map +1 -1
  150. package/dist/server/server/start-host.js +20 -0
  151. package/dist/server/server/start-host.js.map +1 -1
  152. package/dist/server/shared/http/request-diagnostics.d.ts +56 -0
  153. package/dist/server/shared/http/request-diagnostics.js +256 -0
  154. package/dist/server/shared/http/request-diagnostics.js.map +1 -0
  155. package/dist/server/storage/repositories/auth-token-repository.d.ts +1 -0
  156. package/dist/server/storage/repositories/auth-token-repository.js +8 -0
  157. package/dist/server/storage/repositories/auth-token-repository.js.map +1 -1
  158. package/dist/server/storage/repositories/auth-user-repository.d.ts +50 -0
  159. package/dist/server/storage/repositories/auth-user-repository.js +198 -27
  160. package/dist/server/storage/repositories/auth-user-repository.js.map +1 -1
  161. package/dist/server/storage/repositories/butler-control-session-repository.d.ts +4 -1
  162. package/dist/server/storage/repositories/butler-control-session-repository.js +55 -8
  163. package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -1
  164. package/dist/server/storage/repositories/butler-profile-repository.d.ts +2 -1
  165. package/dist/server/storage/repositories/butler-profile-repository.js +35 -6
  166. package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -1
  167. package/dist/server/storage/repositories/butler-project-repository.d.ts +2 -0
  168. package/dist/server/storage/repositories/butler-project-repository.js +38 -4
  169. package/dist/server/storage/repositories/butler-project-repository.js.map +1 -1
  170. package/dist/server/storage/repositories/butler-session-repository.d.ts +2 -1
  171. package/dist/server/storage/repositories/butler-session-repository.js +35 -6
  172. package/dist/server/storage/repositories/butler-session-repository.js.map +1 -1
  173. package/dist/server/storage/repositories/session-binding-repository.d.ts +3 -0
  174. package/dist/server/storage/repositories/session-binding-repository.js +70 -2
  175. package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
  176. package/dist/server/storage/repositories/session-index-repository.js +7 -5
  177. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  178. package/dist/server/storage/repositories/workspace-repository.d.ts +3 -1
  179. package/dist/server/storage/repositories/workspace-repository.js +24 -10
  180. package/dist/server/storage/repositories/workspace-repository.js.map +1 -1
  181. package/dist/server/storage/sqlite/client.js +288 -0
  182. package/dist/server/storage/sqlite/client.js.map +1 -1
  183. package/dist/server/storage/sqlite/schema.sql +33 -9
  184. package/dist/server/types/domain.d.ts +8 -1
  185. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +34 -7
  186. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +1 -1
  187. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +7 -0
  188. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +596 -6
  189. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  190. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +3 -7
  191. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
  192. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +1 -0
  193. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +249 -6
  194. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  195. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +11 -0
  196. package/package.json +1 -1
  197. package/dist/public/assets/SessionIndexPage-CX2FppcJ.js +0 -1
  198. package/dist/public/assets/SettingsPage-BI2Olcvr.js +0 -2
  199. package/dist/public/assets/WorkbenchLayout-CJHQtwuL.js +0 -1022
  200. package/dist/public/assets/WorkbenchShellRoute-2bKI6Q9k.js +0 -1
  201. package/dist/public/assets/WorkbenchShellRoute-BjuZD101.css +0 -1
  202. package/dist/public/assets/WorkspaceDetailPage-5H9Gosx2.js +0 -1
  203. package/dist/public/assets/index-BARqMVSw.css +0 -1
  204. package/dist/public/assets/index-BUoNjVrY.js +0 -50
  205. package/dist/public/assets/styles-DkbkRgWw.css +0 -1
  206. /package/dist/public/assets/{styles-JKFlsYFv.js → styles-DwSuZo1w.js} +0 -0
@@ -1,4 +1,5 @@
1
- import { existsSync, readFileSync, statSync } from "node:fs";
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
2
3
  import { CapabilityService, ClaudeCodeAdapter, CodexAdapter, GeminiAdapter, KimiAdapter, LegnaCodeAdapter, OpenCodeAdapter, ProviderRegistry, SessionSyncService } from "@codingns/session-sync-core";
3
4
  import { AppError } from "../../shared/errors/app-error.js";
4
5
  import { hashContent } from "../../shared/utils/hash.js";
@@ -21,6 +22,7 @@ import { ProviderRuntimeStateService } from "../provider/provider-runtime-state-
21
22
  import { createTaskManager } from "../tasks/task-manager.js";
22
23
  import { HOST_TASK_TYPES } from "../tasks/task-types.js";
23
24
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
25
+ import { CodexSessionTitleGenerator } from "./codex-session-title-generator.js";
24
26
  import { CodingnsProviderSessionDeleteCli } from "./provider-session-delete-cli.js";
25
27
  const RECONSTRUCTED_FORK_TARGET_PROVIDERS = new Set(["codex", "claude-code", "opencode"]);
26
28
  const FORK_RECONSTRUCTION_PAGE_SIZE = 200;
@@ -79,6 +81,7 @@ export class SessionHistoryService {
79
81
  providerDiscoveryHelperClient = getSharedProviderDiscoveryHelperClient();
80
82
  providerSessionDiscoveryConfig;
81
83
  providerRuntimeStateService;
84
+ codexSessionTitleGenerator;
82
85
  sessionProviderConfigService;
83
86
  providerControlRepository;
84
87
  taskManager;
@@ -112,6 +115,10 @@ export class SessionHistoryService {
112
115
  this.sessionProviderConfigService = sessionProviderConfigService;
113
116
  this.providerRuntimeStateService = providerRuntimeStateService
114
117
  ?? new ProviderRuntimeStateService(config);
118
+ this.codexSessionTitleGenerator = new CodexSessionTitleGenerator({
119
+ hostDataRootDir: dirname(config.databasePath),
120
+ codexHomeDir: config.codexHomeDir
121
+ });
115
122
  this.providerControlRepository = providerControlRepository ?? {
116
123
  get: (providerId) => ({
117
124
  providerId: providerId.trim(),
@@ -227,6 +234,14 @@ export class SessionHistoryService {
227
234
  }
228
235
  });
229
236
  }
237
+ if (!this.taskManager.has(HOST_TASK_TYPES.sessionCodexTitleGenerate)) {
238
+ this.taskManager.register({
239
+ taskType: HOST_TASK_TYPES.sessionCodexTitleGenerate,
240
+ executionLane: "external_process",
241
+ timeoutMs: 45_000,
242
+ run: async ({ sessionId }, context) => this.runCodexSessionTitleGeneration(sessionId, context.signal)
243
+ });
244
+ }
230
245
  }
231
246
  async discoverWorkspaceSessions(workspaceId, userId, options) {
232
247
  const maxAgeMs = options?.maxAgeMs ?? 0;
@@ -314,6 +329,7 @@ export class SessionHistoryService {
314
329
  binding = await this.repairCodexDirtyBindingBeforeHistoryRead(resolvedSessionId, userId, binding);
315
330
  repairBindingMs = Date.now() - repairStartedAt;
316
331
  }
332
+ binding = this.repairClaudeEmptyBindingBeforeHistoryRead(resolvedSessionId, binding);
317
333
  const current = this.sessionStatusSnapshotRepository.findBySessionId(resolvedSessionId);
318
334
  const safeLimit = clampLimit(limit);
319
335
  const knownTotalLookupStartedAt = Date.now();
@@ -445,6 +461,7 @@ export class SessionHistoryService {
445
461
  async syncSessionTitle(sessionId, signal) {
446
462
  const binding = this.getBindingOrThrow(sessionId);
447
463
  await this.syncSessionTitleFromProvider(sessionId, binding, signal);
464
+ this.requestCodexTitleGenerationIfNeeded(sessionId, "session_history.sync_session_title");
448
465
  }
449
466
  async syncWorkspaceSessionTitles(workspaceId, userId, concurrency = 1, signal) {
450
467
  const sessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
@@ -495,8 +512,8 @@ export class SessionHistoryService {
495
512
  throw mapSessionProviderError(error);
496
513
  }
497
514
  }
498
- async getSessionCapabilities(sessionId) {
499
- const binding = this.getBindingOrThrow(sessionId);
515
+ async getSessionCapabilities(sessionId, userId) {
516
+ const binding = this.getBindingForUserOrThrow(sessionId, userId);
500
517
  const workspace = this.getWorkspaceOrThrow(binding.workspaceId);
501
518
  const workspacePath = workspace.path;
502
519
  return this.capabilityService
@@ -632,8 +649,8 @@ export class SessionHistoryService {
632
649
  throw mapSessionProviderError(error);
633
650
  }
634
651
  }
635
- async resumeSession(sessionId) {
636
- const binding = this.getBindingOrThrow(sessionId);
652
+ async resumeSession(sessionId, userId) {
653
+ const binding = this.getBindingForUserOrThrow(sessionId, userId);
637
654
  this.assertProviderCapabilityEnabled(binding.provider, "canResumeSession", "当前 provider 不支持继续会话");
638
655
  try {
639
656
  const result = await this.sessionSyncService.resumeSession(binding.provider, binding.providerSessionId, binding.rawStoreRef);
@@ -670,7 +687,7 @@ export class SessionHistoryService {
670
687
  return this.startSessionDirect(input);
671
688
  }
672
689
  async startSessionDirect(input) {
673
- const workspace = this.getWorkspaceOrThrow(input.workspaceId);
690
+ const workspace = this.getWorkspaceForUserOrThrow(input.workspaceId, input.userId);
674
691
  this.assertProviderCapabilityEnabled(input.provider, "canStartSession", "当前 provider 不支持创建会话");
675
692
  const sessionId = createId();
676
693
  const providerBinding = this.prepareDirectSessionBinding({
@@ -687,6 +704,7 @@ export class SessionHistoryService {
687
704
  const persist = this.db.transaction(() => {
688
705
  this.sessionBindingRepository.upsert({
689
706
  sessionId,
707
+ userId: input.userId,
690
708
  workspaceId: workspace.id,
691
709
  provider: result.session.provider,
692
710
  providerSessionId: result.session.providerSessionId,
@@ -745,9 +763,9 @@ export class SessionHistoryService {
745
763
  }
746
764
  }
747
765
  async forkSession(input) {
748
- const binding = this.getBindingOrThrow(input.sessionId);
766
+ const binding = this.getBindingForUserOrThrow(input.sessionId, input.userId);
749
767
  const targetWorkspaceId = input.targetWorkspaceId?.trim() || binding.workspaceId;
750
- const workspace = this.getWorkspaceOrThrow(targetWorkspaceId);
768
+ const workspace = this.getWorkspaceForUserOrThrow(targetWorkspaceId, input.userId);
751
769
  const targetProvider = input.targetProvider?.trim() || binding.provider;
752
770
  this.assertProviderCapabilityEnabled(targetProvider, "canStartSession", "当前 provider 不支持 fork 创建会话");
753
771
  const sourceMessageId = input.sourceType === "message"
@@ -789,6 +807,7 @@ export class SessionHistoryService {
789
807
  this.db.transaction(() => {
790
808
  this.sessionBindingRepository.upsert({
791
809
  sessionId,
810
+ userId: input.userId,
792
811
  workspaceId: workspace.id,
793
812
  provider: result.session.provider,
794
813
  providerSessionId: result.session.providerSessionId,
@@ -1037,8 +1056,8 @@ export class SessionHistoryService {
1037
1056
  }
1038
1057
  return depth;
1039
1058
  }
1040
- async sendMessage(sessionId, content, clientRequestId, permissionMode = null) {
1041
- const binding = this.getBindingOrThrow(sessionId);
1059
+ async sendMessage(sessionId, userId, content, clientRequestId, permissionMode = null) {
1060
+ const binding = this.getBindingForUserOrThrow(sessionId, userId);
1042
1061
  this.assertProviderSendEnabled(binding.provider, "sessionId", "当前 provider 不支持继续发送消息");
1043
1062
  const result = await this.sessionSyncService
1044
1063
  .sendMessage(binding.provider, binding.providerSessionId, binding.rawStoreRef, content, clientRequestId, permissionMode)
@@ -1148,6 +1167,7 @@ export class SessionHistoryService {
1148
1167
  return null;
1149
1168
  }
1150
1169
  await this.syncSessionTitleFromProvider(sessionId, binding);
1170
+ this.requestCodexTitleGenerationIfNeeded(sessionId, "session_history.read_recent_history");
1151
1171
  const snapshot = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
1152
1172
  this.upsertSnapshot(sessionId, {
1153
1173
  syncStatus: "idle",
@@ -1235,6 +1255,7 @@ export class SessionHistoryService {
1235
1255
  });
1236
1256
  this.sessionBindingRepository.upsert({
1237
1257
  ...binding,
1258
+ userId: input.userId,
1238
1259
  rawStoreRef: result.rawStoreRef,
1239
1260
  updatedAt: timestamp
1240
1261
  });
@@ -1365,6 +1386,10 @@ export class SessionHistoryService {
1365
1386
  const currentIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
1366
1387
  this.sessionBindingRepository.upsert({
1367
1388
  sessionId,
1389
+ userId: snapshot.userId
1390
+ ?? currentBinding?.userId
1391
+ ?? duplicateBinding?.userId
1392
+ ?? this.resolveWorkspaceOwnerUserId(workspaceId),
1368
1393
  workspaceId,
1369
1394
  provider: resolvedSnapshot.provider,
1370
1395
  providerSessionId: resolvedSnapshot.providerSessionId,
@@ -1402,7 +1427,7 @@ export class SessionHistoryService {
1402
1427
  async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline", signal) {
1403
1428
  const startedAt = Date.now();
1404
1429
  const debugStartedAtMs = terminalDebugNowMs();
1405
- const workspace = this.getWorkspaceOrThrow(workspaceId);
1430
+ const workspace = this.getWorkspaceForUserOrThrow(workspaceId, userId);
1406
1431
  let discoverDurationMs = 0;
1407
1432
  let persistDurationMs = 0;
1408
1433
  let persistPass1DurationMs = 0;
@@ -1470,6 +1495,7 @@ export class SessionHistoryService {
1470
1495
  : null;
1471
1496
  const nextBinding = {
1472
1497
  sessionId,
1498
+ userId,
1473
1499
  workspaceId: workspace.id,
1474
1500
  provider: session.provider,
1475
1501
  providerSessionId: session.providerSessionId,
@@ -1614,6 +1640,10 @@ export class SessionHistoryService {
1614
1640
  cleanupDurationMs = Date.now() - cleanupStartedAt;
1615
1641
  }
1616
1642
  this.workspaceSessionRelations.set(workspaceId, relationMap);
1643
+ this.observeDiscoveredProviderActivity(sessions, discoveredSessionIds, userId, timestamp);
1644
+ for (const persisted of persistedSessions) {
1645
+ this.requestCodexTitleGenerationIfNeeded(persisted.sessionId, "session_history.discover_workspace_sessions.codex_title");
1646
+ }
1617
1647
  const listItemsStartedAt = Date.now();
1618
1648
  const items = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
1619
1649
  listItemsDurationMs = Date.now() - listItemsStartedAt;
@@ -1901,6 +1931,72 @@ export class SessionHistoryService {
1901
1931
  }
1902
1932
  return relationMap;
1903
1933
  }
1934
+ observeDiscoveredProviderActivity(sessions, discoveredSessionIds, userId, timestamp) {
1935
+ for (const session of sessions) {
1936
+ const providerObservation = session.activityObservation ?? null;
1937
+ if (!providerObservation) {
1938
+ continue;
1939
+ }
1940
+ const sessionId = discoveredSessionIds.get(buildProviderSessionKey(session.provider, session.providerSessionId));
1941
+ if (!sessionId) {
1942
+ continue;
1943
+ }
1944
+ const observedAt = providerObservation.observedAt ?? timestamp;
1945
+ const resolution = this.sessionActivityAuthorityService.observe({
1946
+ sessionId,
1947
+ runId: providerObservation.runId ?? null,
1948
+ runningState: providerObservation.runningState,
1949
+ source: "authoritative_provider_event",
1950
+ confidence: providerObservation.confidence,
1951
+ detail: providerObservation.detail ?? null,
1952
+ interruptSource: providerObservation.runningState === "interrupted" ? "runtime" : null,
1953
+ errorCode: providerObservation.errorCode ?? null,
1954
+ observedAt
1955
+ });
1956
+ const current = this.sessionStateRepository.findBySessionAndUser(sessionId, userId);
1957
+ this.sessionStateRepository.upsert({
1958
+ sessionId,
1959
+ userId,
1960
+ runningState: mapResolvedRunningStateToStored(resolution.runningState, current),
1961
+ activitySource: "runtime",
1962
+ favorite: current?.favorite ?? false,
1963
+ lastEventAt: resolution.lastObservedAt ?? current?.lastEventAt ?? null,
1964
+ completedAt: isTerminalResolvedRunningState(resolution.runningState)
1965
+ ? resolution.terminalAt ?? current?.completedAt ?? observedAt
1966
+ : null,
1967
+ lastSeenAt: current?.lastSeenAt ?? null,
1968
+ updatedAt: timestamp
1969
+ });
1970
+ const currentSnapshot = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
1971
+ const shouldClearFailure = currentSnapshot?.syncStatus === "error"
1972
+ && providerObservation.runningState !== "failed";
1973
+ this.sessionStatusSnapshotRepository.upsert({
1974
+ sessionId,
1975
+ syncStatus: providerObservation.runningState === "failed"
1976
+ ? "error"
1977
+ : shouldClearFailure
1978
+ ? "idle"
1979
+ : currentSnapshot?.syncStatus ?? "idle",
1980
+ syncCursor: currentSnapshot?.syncCursor ?? null,
1981
+ lastSyncAt: resolution.lastObservedAt
1982
+ ?? resolution.terminalAt
1983
+ ?? currentSnapshot?.lastSyncAt
1984
+ ?? null,
1985
+ lastErrorCode: providerObservation.runningState === "failed"
1986
+ ? providerObservation.errorCode ?? "CODEX_PROVIDER_EVENT_FAILED"
1987
+ : shouldClearFailure
1988
+ ? null
1989
+ : currentSnapshot?.lastErrorCode ?? null,
1990
+ lastErrorDetail: providerObservation.runningState === "failed"
1991
+ ? providerObservation.detail ?? "Provider reported session failure"
1992
+ : shouldClearFailure
1993
+ ? null
1994
+ : currentSnapshot?.lastErrorDetail ?? null,
1995
+ resumedAt: currentSnapshot?.resumedAt ?? null,
1996
+ updatedAt: timestamp
1997
+ });
1998
+ }
1999
+ }
1904
2000
  enrichSessionItems(workspaceId, items) {
1905
2001
  const relationMap = this.workspaceSessionRelations.get(workspaceId);
1906
2002
  const projectionBySessionId = this.buildParallelProjectionBySessionId(items);
@@ -2125,6 +2221,7 @@ export class SessionHistoryService {
2125
2221
  return;
2126
2222
  }
2127
2223
  await this.syncSessionTitleFromProvider(sessionId, binding);
2224
+ this.requestCodexTitleGenerationIfNeeded(sessionId, "session_history.publish_history_envelope");
2128
2225
  const snapshot = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
2129
2226
  this.upsertSnapshot(sessionId, {
2130
2227
  syncStatus: "idle",
@@ -2177,6 +2274,83 @@ export class SessionHistoryService {
2177
2274
  updatedAt: nowIso()
2178
2275
  });
2179
2276
  }
2277
+ requestCodexTitleGenerationIfNeeded(sessionId, source) {
2278
+ if (!this.shouldRequestCodexTitleGeneration(sessionId)) {
2279
+ return;
2280
+ }
2281
+ const handle = this.taskManager.enqueue(HOST_TASK_TYPES.sessionCodexTitleGenerate, {
2282
+ key: sessionId,
2283
+ source,
2284
+ input: {
2285
+ sessionId
2286
+ }
2287
+ });
2288
+ if (handle.deduped) {
2289
+ return;
2290
+ }
2291
+ void handle.promise.catch((error) => {
2292
+ logPerformance("session.codex_title_generate.background_failed", 0, {
2293
+ sessionId,
2294
+ error: error instanceof Error ? error.message : "unknown"
2295
+ }, {
2296
+ thresholdMs: 0,
2297
+ force: true
2298
+ });
2299
+ });
2300
+ }
2301
+ shouldRequestCodexTitleGeneration(sessionId) {
2302
+ const binding = this.sessionBindingRepository.findBySessionId(sessionId);
2303
+ const index = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
2304
+ if (!binding || !index || binding.provider !== "codex") {
2305
+ return false;
2306
+ }
2307
+ if (isPendingBindingValue(binding.providerSessionId) || isPendingBindingValue(binding.rawStoreRef)) {
2308
+ return false;
2309
+ }
2310
+ if (index.messageCount <= 0) {
2311
+ return false;
2312
+ }
2313
+ return true;
2314
+ }
2315
+ async runCodexSessionTitleGeneration(sessionId, signal) {
2316
+ const binding = this.sessionBindingRepository.findBySessionId(sessionId);
2317
+ const index = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
2318
+ if (!binding || !index || binding.provider !== "codex") {
2319
+ return { title: null };
2320
+ }
2321
+ if (isPendingBindingValue(binding.providerSessionId) || isPendingBindingValue(binding.rawStoreRef)) {
2322
+ return { title: null };
2323
+ }
2324
+ const page = await this.sessionSyncService.readHistory(binding.provider, binding.providerSessionId, binding.rawStoreRef, null, 16, "forward").catch(() => null);
2325
+ if (!page) {
2326
+ return { title: null };
2327
+ }
2328
+ const firstUserMessage = page.messages.find((message) => message.role === "user")?.content ?? null;
2329
+ if (!shouldGenerateCodexSessionTitle(index.title, firstUserMessage)) {
2330
+ return { title: null };
2331
+ }
2332
+ const title = await this.codexSessionTitleGenerator.generate({
2333
+ currentTitle: index.title,
2334
+ messages: page.messages.map((message) => ({
2335
+ role: message.role,
2336
+ content: message.content
2337
+ })),
2338
+ signal
2339
+ });
2340
+ if (!title || !shouldApplyGeneratedCodexSessionTitle(title, firstUserMessage)) {
2341
+ return { title: null };
2342
+ }
2343
+ await this.sessionSyncService.renameSessionTitle(binding.provider, binding.providerSessionId, binding.rawStoreRef, title);
2344
+ const latestIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
2345
+ if (latestIndex && shouldGenerateCodexSessionTitle(latestIndex.title, firstUserMessage)) {
2346
+ this.sessionIndexRepository.upsert({
2347
+ ...latestIndex,
2348
+ title,
2349
+ updatedAt: nowIso()
2350
+ });
2351
+ }
2352
+ return { title };
2353
+ }
2180
2354
  async readFirstUserMessageTitleForSync(binding) {
2181
2355
  const pageSize = 20;
2182
2356
  const maxPages = 3;
@@ -2241,6 +2415,31 @@ export class SessionHistoryService {
2241
2415
  }
2242
2416
  return workspace;
2243
2417
  }
2418
+ getWorkspaceForUserOrThrow(workspaceId, userId) {
2419
+ const workspace = this.getWorkspaceOrThrow(workspaceId);
2420
+ if (workspace.ownerUserId !== userId) {
2421
+ throw new AppError({
2422
+ statusCode: 404,
2423
+ errorCode: "WORKSPACE_NOT_FOUND",
2424
+ detail: "工作区不存在"
2425
+ });
2426
+ }
2427
+ return workspace;
2428
+ }
2429
+ getBindingForUserOrThrow(sessionId, userId) {
2430
+ const binding = this.sessionBindingRepository.findBySessionIdForUser(sessionId, userId);
2431
+ if (!binding) {
2432
+ throw new AppError({
2433
+ statusCode: 404,
2434
+ errorCode: "SESSION_NOT_FOUND",
2435
+ detail: "session 不存在"
2436
+ });
2437
+ }
2438
+ return this.resolvePendingSessionAliasBinding(binding) ?? binding;
2439
+ }
2440
+ resolveWorkspaceOwnerUserId(workspaceId) {
2441
+ return this.workspaceRepository.findById(workspaceId)?.ownerUserId ?? null;
2442
+ }
2244
2443
  getSessionListItemOrThrow(sessionId, userId) {
2245
2444
  const canonicalSessionId = this.resolveCanonicalSessionId(sessionId, userId);
2246
2445
  const item = this.findSessionListItem(canonicalSessionId, sessionId, userId)
@@ -2614,6 +2813,7 @@ export class SessionHistoryService {
2614
2813
  if (!targetBinding) {
2615
2814
  this.sessionBindingRepository.upsert({
2616
2815
  sessionId: input.targetSessionId,
2816
+ userId: sourceBinding.userId ?? this.resolveWorkspaceOwnerUserId(input.workspaceId),
2617
2817
  workspaceId: input.workspaceId,
2618
2818
  provider: input.provider,
2619
2819
  providerSessionId: buildPendingBindingValue(input.provider, input.targetSessionId),
@@ -2698,6 +2898,7 @@ export class SessionHistoryService {
2698
2898
  // 保留旧 session_id 作为 alias,避免前端或 Butler 还拿着旧 id 时直接炸成 SESSION_NOT_FOUND。
2699
2899
  this.sessionBindingRepository.upsert({
2700
2900
  sessionId: input.sourceSessionId,
2901
+ userId: sourceBinding.userId ?? this.resolveWorkspaceOwnerUserId(sourceBinding.workspaceId),
2701
2902
  workspaceId: sourceBinding.workspaceId,
2702
2903
  provider: sourceBinding.provider,
2703
2904
  providerSessionId: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
@@ -3101,6 +3302,21 @@ export class SessionHistoryService {
3101
3302
  this.codexDirtyBindingRepairStates.set(sessionId, currentState);
3102
3303
  });
3103
3304
  }
3305
+ repairClaudeEmptyBindingBeforeHistoryRead(sessionId, binding) {
3306
+ if (!shouldRepairClaudeEmptyHistoryBinding(binding)) {
3307
+ return binding;
3308
+ }
3309
+ const repairedRawStoreRef = findBestClaudeHistoryFileNearBinding(binding);
3310
+ if (!repairedRawStoreRef || repairedRawStoreRef === binding.rawStoreRef) {
3311
+ return binding;
3312
+ }
3313
+ this.persistSessionBinding(sessionId, binding.workspaceId, {
3314
+ provider: binding.provider,
3315
+ providerSessionId: binding.providerSessionId,
3316
+ rawStoreRef: repairedRawStoreRef
3317
+ });
3318
+ return this.getBindingOrThrow(sessionId);
3319
+ }
3104
3320
  resolveLiveActivityObservation(sessionId) {
3105
3321
  for (const resolver of this.liveActivityObservationResolvers) {
3106
3322
  const observation = resolver(sessionId);
@@ -3596,6 +3812,69 @@ function shouldSkipClaudePendingBinding(binding) {
3596
3812
  function isPendingBindingValue(value) {
3597
3813
  return value.trim().toLowerCase().startsWith("pending://");
3598
3814
  }
3815
+ function shouldRepairClaudeEmptyHistoryBinding(binding) {
3816
+ if (binding.provider !== "claude-code") {
3817
+ return false;
3818
+ }
3819
+ if (isPendingBindingValue(binding.providerSessionId) || isPendingBindingValue(binding.rawStoreRef)) {
3820
+ return false;
3821
+ }
3822
+ const currentStats = safeStat(binding.rawStoreRef);
3823
+ return !currentStats || currentStats.size === 0;
3824
+ }
3825
+ function findBestClaudeHistoryFileNearBinding(binding) {
3826
+ const providerSessionId = binding.providerSessionId.trim();
3827
+ if (!providerSessionId || isPendingBindingValue(providerSessionId)) {
3828
+ return null;
3829
+ }
3830
+ const candidates = new Set();
3831
+ for (const projectsRoot of collectClaudeProjectsRootsNearBinding(binding)) {
3832
+ for (const candidate of listClaudeSessionCandidates(projectsRoot, providerSessionId)) {
3833
+ candidates.add(candidate);
3834
+ }
3835
+ }
3836
+ const nonEmptyCandidates = Array.from(candidates)
3837
+ .map((filePath) => ({
3838
+ filePath,
3839
+ stats: safeStat(filePath)
3840
+ }))
3841
+ .filter((candidate) => Boolean(candidate.stats && candidate.stats.size > 0))
3842
+ .sort((left, right) => {
3843
+ if (right.stats.size !== left.stats.size) {
3844
+ return right.stats.size - left.stats.size;
3845
+ }
3846
+ return right.stats.mtimeMs - left.stats.mtimeMs;
3847
+ });
3848
+ return nonEmptyCandidates[0]?.filePath ?? null;
3849
+ }
3850
+ function collectClaudeProjectsRootsNearBinding(binding) {
3851
+ const roots = new Set();
3852
+ const rawStoreRef = binding.rawStoreRef.trim();
3853
+ if (rawStoreRef && !isPendingBindingValue(rawStoreRef)) {
3854
+ // rawStoreRef 通常是 <home>/projects/<workspace-slug>/<session>.jsonl。
3855
+ // 即使 <workspace-slug> 算错了,向上两级仍然能拿到正确的 projects 根。
3856
+ roots.add(dirname(dirname(rawStoreRef)));
3857
+ }
3858
+ const runtimeHomeDir = binding.runtimeHomeDir?.trim() ?? "";
3859
+ if (runtimeHomeDir) {
3860
+ roots.add(join(runtimeHomeDir, "projects"));
3861
+ }
3862
+ return Array.from(roots);
3863
+ }
3864
+ function listClaudeSessionCandidates(projectsRoot, providerSessionId) {
3865
+ if (!projectsRoot || !existsSync(projectsRoot)) {
3866
+ return [];
3867
+ }
3868
+ try {
3869
+ return readdirSync(projectsRoot, { withFileTypes: true })
3870
+ .filter((entry) => entry.isDirectory())
3871
+ .map((entry) => join(projectsRoot, entry.name, `${providerSessionId}.jsonl`))
3872
+ .filter((filePath) => existsSync(filePath));
3873
+ }
3874
+ catch {
3875
+ return [];
3876
+ }
3877
+ }
3599
3878
  function isSessionBindingProviderUniqueConflict(error) {
3600
3879
  if (!(error instanceof Error)) {
3601
3880
  return false;
@@ -4080,6 +4359,25 @@ function shouldSyncSessionTitleFromProvider(provider, currentTitle, firstUserMes
4080
4359
  }
4081
4360
  return false;
4082
4361
  }
4362
+ function shouldGenerateCodexSessionTitle(currentTitle, firstUserMessage) {
4363
+ const normalizedTitle = currentTitle?.trim() ?? "";
4364
+ if (!normalizedTitle) {
4365
+ return true;
4366
+ }
4367
+ if (looksLikeGeneratedSessionTitle(normalizedTitle) || isSyntheticCodexSessionTitle(normalizedTitle)) {
4368
+ return true;
4369
+ }
4370
+ const firstUserTitle = normalizeRuntimePromptTitle(firstUserMessage);
4371
+ return Boolean(firstUserTitle && normalizedTitle === firstUserTitle);
4372
+ }
4373
+ function shouldApplyGeneratedCodexSessionTitle(generatedTitle, firstUserMessage) {
4374
+ const normalizedTitle = generatedTitle.trim();
4375
+ const firstUserTitle = normalizeRuntimePromptTitle(firstUserMessage);
4376
+ if (!normalizedTitle) {
4377
+ return false;
4378
+ }
4379
+ return !firstUserTitle || normalizedTitle !== firstUserTitle;
4380
+ }
4083
4381
  function looksLikeSyntheticTitleSourceMessage(provider, content) {
4084
4382
  const normalizedProvider = provider.trim().toLowerCase();
4085
4383
  if (normalizedProvider === "codex") {