@jingyi0605/codingns 0.9.6 → 0.9.8

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 (170) hide show
  1. package/dist/public/assets/{AdaptiveButlerPage-khJQh6a_.js → AdaptiveButlerPage-D-gXre7Y.js} +2 -2
  2. package/dist/public/assets/{App-CcDXqFl1.css → App-7zrCMhE-.css} +1 -1
  3. package/dist/public/assets/App-Dl-mcdqy.js +30 -0
  4. package/dist/public/assets/{BootstrapPage-DcfYtoLC.js → BootstrapPage-B-yMdfpQ.js} +1 -1
  5. package/dist/public/assets/ConversationPage-DRQ5Sg_d.js +9 -0
  6. package/dist/public/assets/{DesktopDetachPreviewPage-CXUPMcBz.js → DesktopDetachPreviewPage-D1DMaGcy.js} +1 -1
  7. package/dist/public/assets/{DesktopModal-bMdI1jEe.js → DesktopModal-BnfGW2gk.js} +1 -1
  8. package/dist/public/assets/DesktopWindowPage-2SWAi0xz.js +2 -0
  9. package/dist/public/assets/FileContextPanel-fbPuE9dO.js +1 -0
  10. package/dist/public/assets/GitSidebar-BkmesJJR.js +6 -0
  11. package/dist/public/assets/{MobileCreateSessionSheet-DWPBsEx8.js → MobileCreateSessionSheet-CEJcDBZJ.js} +1 -1
  12. package/dist/public/assets/{MobileSheet-BXvQPkxt.js → MobileSheet-rkn_CUOY.js} +1 -1
  13. package/dist/public/assets/{MobileTopHeaderFrame-vdYOyaaB.js → MobileTopHeaderFrame-CU0wsYSS.js} +1 -1
  14. package/dist/public/assets/MobileWorkspaceSwitcherHeader-idl8o1OB.js +1 -0
  15. package/dist/public/assets/{PluginAccessOverview-C77TeZTK.js → PluginAccessOverview-BBgM6tb0.js} +1 -1
  16. package/dist/public/assets/{PluginContainerPage-DdSwOCw-.js → PluginContainerPage-D-ly3i3H.js} +1 -1
  17. package/dist/public/assets/{PluginDetailPage-BK1yTzvO.js → PluginDetailPage-CWAHYyyG.js} +1 -1
  18. package/dist/public/assets/{PluginsListPage-DAAwSc6W.js → PluginsListPage-Cte3vBgR.js} +1 -1
  19. package/dist/public/assets/{RelayConnectEntryPage-4Yyo2p8b.js → RelayConnectEntryPage-sRJlstx9.js} +1 -1
  20. package/dist/public/assets/{ServerSettingsModal-C_DEisHs.js → ServerSettingsModal-BBft9KEC.js} +1 -1
  21. package/dist/public/assets/SessionIndexPage-CN7cEdl9.js +1 -0
  22. package/dist/public/assets/SettingsPage-BGT-YqG2.js +2 -0
  23. package/dist/public/assets/TerminalManagerPanel-6-ZJ8vGn.js +1 -0
  24. package/dist/public/assets/TerminalPage-CUXXQYU2.js +55 -0
  25. package/dist/public/assets/{TerminalRuntimeFallbackModal-KvG6k4AQ.js → TerminalRuntimeFallbackModal-zc3qqMKJ.js} +1 -1
  26. package/dist/public/assets/ToolFilesPage-QzsZyr0F.js +1 -0
  27. package/dist/public/assets/ToolGitPage-CXg4ncuT.js +1 -0
  28. package/dist/public/assets/ToolProcessesPage-BPsOsg4w.js +1 -0
  29. package/dist/public/assets/ToolsHomePage-D1n4FU1s.js +1 -0
  30. package/dist/public/assets/WorkbenchLandingPage-BaU_dXls.js +1 -0
  31. package/dist/public/assets/WorkbenchLayout-DViAJhHz.js +1027 -0
  32. package/dist/public/assets/{WorkbenchModal-xbx1o6MO.js → WorkbenchModal-DWsNm2B2.js} +1 -1
  33. package/dist/public/assets/WorkbenchShellRoute-BGfRqBUa.js +1 -0
  34. package/dist/public/assets/WorkbenchShellRoute-f2jWjHWu.css +1 -0
  35. package/dist/public/assets/WorkspaceDebugDetailPage-BX0zVSsI.js +1 -0
  36. package/dist/public/assets/WorkspaceDetailPage-Dx6JX4jx.js +1 -0
  37. package/dist/public/assets/WorkspaceHomePage-DQVJ042Z.js +1 -0
  38. package/dist/public/assets/{client-runtime-manager-Bwau7p1v.js → client-runtime-manager-D9VbgJZ_.js} +1 -1
  39. package/dist/public/assets/host-alias-0TfFnYxR.js +1 -0
  40. package/dist/public/assets/index-DREvg1Yu.css +1 -0
  41. package/dist/public/assets/index-FOhyOpGY.js +50 -0
  42. package/dist/public/assets/{login-direct-candidate-resolver-CKUQ07IA.js → login-direct-candidate-resolver-17wEvjhh.js} +1 -1
  43. package/dist/public/assets/peer-host-config-sync-vYkmqzNz.js +1 -0
  44. package/dist/public/assets/{plugin-permission-copy-DIVk5jNp.js → plugin-permission-copy-apDn8EWG.js} +1 -1
  45. package/dist/public/assets/{plugins-api-DHJVvPZw.js → plugins-api-CnZYRKoS.js} +1 -1
  46. package/dist/public/assets/{preferences-service-CyxxeBmS.js → preferences-service-PZlLLAWH.js} +1 -1
  47. package/dist/public/assets/relay-entry-DhHwflXl.js +1 -0
  48. package/dist/public/assets/styles-BhKoKfQ_.css +1 -0
  49. package/dist/public/assets/terminal-runtime-meta-2zvacxvM.js +1 -0
  50. package/dist/public/assets/{useRegisteredDebugTemplates-wCGD2SLW.js → useRegisteredDebugTemplates-CJ-o4tFl.js} +1 -1
  51. package/dist/public/assets/workbench-navigation-aqJ1ay4M.js +1 -0
  52. package/dist/public/index.html +2 -2
  53. package/dist/server/middlewares/auth-guard.js +1 -0
  54. package/dist/server/middlewares/auth-guard.js.map +1 -1
  55. package/dist/server/modules/peer-host/host-api-proxy-service.d.ts +9 -0
  56. package/dist/server/modules/peer-host/host-api-proxy-service.js +174 -0
  57. package/dist/server/modules/peer-host/host-api-proxy-service.js.map +1 -0
  58. package/dist/server/modules/peer-host/host-handshake-controller.d.ts +7 -0
  59. package/dist/server/modules/peer-host/host-handshake-controller.js +10 -0
  60. package/dist/server/modules/peer-host/host-handshake-controller.js.map +1 -0
  61. package/dist/server/modules/peer-host/host-handshake.d.ts +15 -0
  62. package/dist/server/modules/peer-host/host-handshake.js +20 -0
  63. package/dist/server/modules/peer-host/host-handshake.js.map +1 -0
  64. package/dist/server/modules/peer-host/host-ws-proxy-service.d.ts +14 -0
  65. package/dist/server/modules/peer-host/host-ws-proxy-service.js +256 -0
  66. package/dist/server/modules/peer-host/host-ws-proxy-service.js.map +1 -0
  67. package/dist/server/modules/peer-host/peer-host-controller.d.ts +55 -0
  68. package/dist/server/modules/peer-host/peer-host-controller.js +62 -0
  69. package/dist/server/modules/peer-host/peer-host-controller.js.map +1 -0
  70. package/dist/server/modules/peer-host/peer-host-service.d.ts +77 -0
  71. package/dist/server/modules/peer-host/peer-host-service.js +529 -0
  72. package/dist/server/modules/peer-host/peer-host-service.js.map +1 -0
  73. package/dist/server/modules/provider/provider-discovery-runtime.js +16 -1
  74. package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
  75. package/dist/server/modules/sessions/codex-app-server-helper-client.js +14 -0
  76. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  77. package/dist/server/modules/sessions/codex-app-server-helper-process.js +59 -0
  78. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  79. package/dist/server/modules/sessions/codex-session-title-generator.d.ts +19 -0
  80. package/dist/server/modules/sessions/codex-session-title-generator.js +308 -0
  81. package/dist/server/modules/sessions/codex-session-title-generator.js.map +1 -0
  82. package/dist/server/modules/sessions/session-history-service.d.ts +16 -0
  83. package/dist/server/modules/sessions/session-history-service.js +208 -12
  84. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  85. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +6 -0
  86. package/dist/server/modules/sessions/session-live-runtime-service.js +181 -80
  87. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  88. package/dist/server/modules/sessions/session-title-utils.d.ts +3 -0
  89. package/dist/server/modules/sessions/session-title-utils.js +25 -0
  90. package/dist/server/modules/sessions/session-title-utils.js.map +1 -0
  91. package/dist/server/modules/sessions/workspace-office-mcp-config.d.ts +2 -1
  92. package/dist/server/modules/sessions/workspace-office-mcp-config.js +44 -3
  93. package/dist/server/modules/sessions/workspace-office-mcp-config.js.map +1 -1
  94. package/dist/server/modules/tasks/task-manager.d.ts +2 -1
  95. package/dist/server/modules/tasks/task-manager.js +3 -0
  96. package/dist/server/modules/tasks/task-manager.js.map +1 -1
  97. package/dist/server/modules/tasks/task-scheduler.d.ts +2 -1
  98. package/dist/server/modules/tasks/task-scheduler.js +21 -0
  99. package/dist/server/modules/tasks/task-scheduler.js.map +1 -1
  100. package/dist/server/modules/tasks/task-types.d.ts +6 -0
  101. package/dist/server/modules/tasks/task-types.js +1 -0
  102. package/dist/server/modules/tasks/task-types.js.map +1 -1
  103. package/dist/server/modules/workbench/workbench-controller.js +3 -2
  104. package/dist/server/modules/workbench/workbench-controller.js.map +1 -1
  105. package/dist/server/modules/workspace/affairs-library-service.d.ts +1 -0
  106. package/dist/server/modules/workspace/affairs-library-service.js +80 -0
  107. package/dist/server/modules/workspace/affairs-library-service.js.map +1 -1
  108. package/dist/server/modules/workspace/affairs-lightweight-session-service.js +13 -9
  109. package/dist/server/modules/workspace/affairs-lightweight-session-service.js.map +1 -1
  110. package/dist/server/routes/peer-hosts.d.ts +3 -0
  111. package/dist/server/routes/peer-hosts.js +18 -0
  112. package/dist/server/routes/peer-hosts.js.map +1 -0
  113. package/dist/server/routes/public.d.ts +2 -1
  114. package/dist/server/routes/public.js +2 -1
  115. package/dist/server/routes/public.js.map +1 -1
  116. package/dist/server/server/create-server.d.ts +4 -0
  117. package/dist/server/server/create-server.js +30 -2
  118. package/dist/server/server/create-server.js.map +1 -1
  119. package/dist/server/shared/http/error-handler.js +12 -0
  120. package/dist/server/shared/http/error-handler.js.map +1 -1
  121. package/dist/server/storage/repositories/peer-host-repository.d.ts +44 -0
  122. package/dist/server/storage/repositories/peer-host-repository.js +271 -0
  123. package/dist/server/storage/repositories/peer-host-repository.js.map +1 -0
  124. package/dist/server/storage/sqlite/client.js +81 -0
  125. package/dist/server/storage/sqlite/client.js.map +1 -1
  126. package/dist/server/storage/sqlite/schema.sql +64 -0
  127. package/dist/server/types/domain.d.ts +43 -0
  128. package/dist/server/ws/workbench-ws-hub.js +5 -14
  129. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  130. package/dist/server/ws/ws-server.d.ts +2 -1
  131. package/dist/server/ws/ws-server.js +5 -1
  132. package/dist/server/ws/ws-server.js.map +1 -1
  133. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +7 -0
  134. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +623 -9
  135. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  136. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +8 -1
  137. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  138. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +4 -0
  139. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +375 -15
  140. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  141. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +11 -0
  142. package/package.json +1 -1
  143. package/dist/public/assets/App-If9gThKM.js +0 -30
  144. package/dist/public/assets/ConversationPage-Bfb7GLTM.js +0 -9
  145. package/dist/public/assets/DesktopWindowPage-D1xwgS-7.js +0 -2
  146. package/dist/public/assets/FileContextPanel-C4syif3B.js +0 -1
  147. package/dist/public/assets/GitSidebar-DduL9aTV.js +0 -6
  148. package/dist/public/assets/MobileWorkspaceSwitcherHeader-DT330cAx.js +0 -1
  149. package/dist/public/assets/SessionIndexPage-DyMikN_x.js +0 -1
  150. package/dist/public/assets/SettingsPage-CDAVsPr3.js +0 -2
  151. package/dist/public/assets/TerminalManagerPanel-4OR47vcf.js +0 -1
  152. package/dist/public/assets/TerminalPage-Pvx396YX.js +0 -55
  153. package/dist/public/assets/ToolFilesPage-DrYHk0N-.js +0 -1
  154. package/dist/public/assets/ToolGitPage-Dz1q-Ns_.js +0 -1
  155. package/dist/public/assets/ToolProcessesPage-CRhphOmM.js +0 -1
  156. package/dist/public/assets/ToolsHomePage-BJSDLR6T.js +0 -1
  157. package/dist/public/assets/WorkbenchLandingPage-BlkxdOLC.js +0 -1
  158. package/dist/public/assets/WorkbenchLayout-D-U7ghT0.js +0 -1022
  159. package/dist/public/assets/WorkbenchShellRoute-DyWSCHz_.js +0 -1
  160. package/dist/public/assets/WorkbenchShellRoute-htbkGbtW.css +0 -1
  161. package/dist/public/assets/WorkspaceDebugDetailPage-B4ol2_a5.js +0 -1
  162. package/dist/public/assets/WorkspaceDetailPage-DMakfmHR.js +0 -1
  163. package/dist/public/assets/WorkspaceHomePage-tmCafatd.js +0 -1
  164. package/dist/public/assets/index-DmUJ8tIw.css +0 -1
  165. package/dist/public/assets/index-_OCkVmfl.js +0 -50
  166. package/dist/public/assets/relay-entry-B5GmiOrR.js +0 -1
  167. package/dist/public/assets/styles-DkbkRgWw.css +0 -1
  168. package/dist/public/assets/terminal-runtime-meta-DBsyT35T.js +0 -1
  169. package/dist/public/assets/workbench-navigation-RyUjchbD.js +0 -1
  170. /package/dist/public/assets/{styles-JKFlsYFv.js → styles-DwSuZo1w.js} +0 -0
@@ -208,6 +208,7 @@ export declare class SessionLiveRuntimeService {
208
208
  private readonly queueDispatchSessions;
209
209
  private readonly queueRetryTimers;
210
210
  private readonly pendingSendDebugTracesBySessionId;
211
+ private readonly runtimePersistenceQueues;
211
212
  constructor(sessionHistoryService: SessionHistoryService, sessionMessageAttachmentService: SessionMessageAttachmentService, workspaceService: WorkspaceService, sessionChangedFileService: SessionChangedFileService, sessionBindingRepository: SessionBindingRepository, authUserRepository: AuthUserRepository, sessionSendQueueRepository: SessionSendQueueRepository, sessionIndexRepository: SessionIndexRepository, sessionStateRepository: SessionStateRepository, sessionStatusSnapshotRepository: SessionStatusSnapshotRepository, sessionProviderConfigService: SessionProviderConfigService, config: HostConfig, sessionActivityAuthorityService?: SessionActivityAuthorityService, openCliSessionPromptService?: OpenCliSessionPromptService | null, workspaceSessionRuntimeContextService?: Pick<WorkspaceSessionRuntimeContextService, "prepareWorkspaceInstructionBundle"> | null);
212
213
  startLiveSession(input: StartLiveSessionInput): Promise<LiveMessageAcceptedResult>;
213
214
  sendLiveMessage(input: SendLiveMessageInput): Promise<LiveMessageAcceptedResult>;
@@ -253,6 +254,7 @@ export declare class SessionLiveRuntimeService {
253
254
  private resolveQueueDispatchSession;
254
255
  private launchRuntimeRun;
255
256
  private attachRuntimePersistence;
257
+ private enqueueRuntimeEventPersistence;
256
258
  private createRuntimeBackedSession;
257
259
  private prepareWorkspaceRuntimeContext;
258
260
  private resolveEffectiveRuntimeHomeDir;
@@ -268,6 +270,9 @@ export declare class SessionLiveRuntimeService {
268
270
  private resolveCodexRuntimeRequestRawStoreRef;
269
271
  private buildBindingSnapshot;
270
272
  private persistRuntimeEvent;
273
+ private runRuntimeSqliteRead;
274
+ private runRuntimeSqliteWrite;
275
+ private runRuntimeSqliteOperation;
271
276
  private emitTerminalStateEvent;
272
277
  private reconcileTerminalRuntimeSnapshot;
273
278
  private forceInterruptInactiveSession;
@@ -282,6 +287,7 @@ export declare class SessionLiveRuntimeService {
282
287
  private findAcceptedUserMessage;
283
288
  private resolveNextUserSequence;
284
289
  private waitForResolvedStartBinding;
290
+ private requestCodexTitleGenerationAfterStartBinding;
285
291
  private persistMessageAttachments;
286
292
  private refreshSyntheticSessionTitle;
287
293
  private mapRuntimeEventToEnvelope;
@@ -9,12 +9,14 @@ import { logPermissionDebug } from "../../shared/utils/permission-debug-log.js";
9
9
  import { nowIso } from "../../shared/utils/time.js";
10
10
  import { SessionActivityAuthorityService } from "./session-activity-authority-service.js";
11
11
  import { createProviderCapabilityBlockedError } from "../provider/provider-disabled.js";
12
+ import { buildSessionTitleFromContent } from "./session-title-utils.js";
12
13
  import { SessionPermissionRequestService } from "./session-permission-request-service.js";
13
14
  import { appendSessionProviderErrorContext, mapSessionProviderError } from "./session-provider-error-mapper.js";
14
15
  import { buildClaudeCompatibleHookBridgeUrl, buildClaudeCompatibleRawStoreRef, buildClaudeCompatibleSessionTitle, findClaudeCompatibleSessionFile, isClaudeCompatibleProvider } from "./claude-compatible-provider-registry.js";
15
16
  import { ClaudeRuntimeHelperAdapter } from "./claude-runtime-helper-client.js";
16
17
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
17
18
  const OPENCODE_ORDER_DEBUG_ENABLED = /^(1|true|yes)$/i.test(process.env.CODINGNS_OPENCODE_ORDER_DEBUG?.trim() ?? "");
19
+ const RUNTIME_EVENT_SQLITE_BUSY_RETRY_DELAYS_MS = [100, 250, 500, 1_000, 1_500, 2_000];
18
20
  const RUNTIME_START_BINDING_WAIT_TIMEOUT_MS = 10_000;
19
21
  const START_BINDING_POLL_INTERVAL_MS = 50;
20
22
  const EXTERNAL_RUNTIME_INTERRUPT_SUPPRESSION_MS = 30_000;
@@ -48,6 +50,7 @@ export class SessionLiveRuntimeService {
48
50
  queueDispatchSessions = new Set();
49
51
  queueRetryTimers = new Map();
50
52
  pendingSendDebugTracesBySessionId = new Map();
53
+ runtimePersistenceQueues = new Map();
51
54
  constructor(sessionHistoryService, sessionMessageAttachmentService, workspaceService, sessionChangedFileService, sessionBindingRepository, authUserRepository, sessionSendQueueRepository, sessionIndexRepository, sessionStateRepository, sessionStatusSnapshotRepository, sessionProviderConfigService, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), openCliSessionPromptService = null, workspaceSessionRuntimeContextService = null) {
52
55
  this.sessionHistoryService = sessionHistoryService;
53
56
  this.sessionMessageAttachmentService = sessionMessageAttachmentService;
@@ -178,13 +181,20 @@ export class SessionLiveRuntimeService {
178
181
  // 先把基础记录建出来,再回放 runtime 缓存事件,避免超快启动时出现
179
182
  // “事件先到、索引还没落库”的竞态窗口。
180
183
  this.attachRuntimePersistence(handle, sessionId, workspace.id, input.userId);
181
- const startBindingTask = this.waitForResolvedStartBinding(sessionId, workspace.id, input.provider, handle).catch(() => {
184
+ const startBindingTask = this.waitForResolvedStartBinding(sessionId, workspace.id, input.provider, handle);
185
+ if (input.provider === "codex") {
186
+ void this.requestCodexTitleGenerationAfterStartBinding(sessionId, input.content, startBindingTask)
187
+ .catch(() => {
188
+ return;
189
+ });
190
+ }
191
+ const safeStartBindingTask = startBindingTask.catch(() => {
182
192
  return;
183
193
  });
184
194
  if (shouldAwaitStartBindingBeforeAcceptedUserLookup(input.provider)) {
185
195
  const bindingWaitStartedAtMs = performance.now();
186
196
  await Promise.race([
187
- startBindingTask,
197
+ safeStartBindingTask,
188
198
  waitForAcceptedUserLookupWindow()
189
199
  ]);
190
200
  this.logSendDebugStep(debugTrace, "binding_wait", bindingWaitStartedAtMs, {
@@ -201,7 +211,7 @@ export class SessionLiveRuntimeService {
201
211
  matched: Boolean(acceptedMessage)
202
212
  });
203
213
  if (!shouldAwaitStartBindingBeforeAcceptedUserLookup(input.provider)) {
204
- void startBindingTask;
214
+ void safeStartBindingTask;
205
215
  }
206
216
  const acceptedAt = acceptedMessage?.timestamp ?? requestStartedAt;
207
217
  const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
@@ -1466,9 +1476,24 @@ export class SessionLiveRuntimeService {
1466
1476
  }
1467
1477
  attachRuntimePersistence(handle, sessionId, workspaceId, userId) {
1468
1478
  handle.attach(async (event) => {
1469
- await this.persistRuntimeEvent(sessionId, workspaceId, userId, event);
1479
+ await this.enqueueRuntimeEventPersistence(sessionId, workspaceId, userId, event);
1470
1480
  });
1471
1481
  }
1482
+ enqueueRuntimeEventPersistence(sessionId, workspaceId, userId, event) {
1483
+ const previous = this.runtimePersistenceQueues.get(sessionId) ?? Promise.resolve();
1484
+ const task = previous
1485
+ .catch(() => {
1486
+ return;
1487
+ })
1488
+ .then(() => this.persistRuntimeEvent(sessionId, workspaceId, userId, event));
1489
+ const queuedTask = task.finally(() => {
1490
+ if (this.runtimePersistenceQueues.get(sessionId) === queuedTask) {
1491
+ this.runtimePersistenceQueues.delete(sessionId);
1492
+ }
1493
+ });
1494
+ this.runtimePersistenceQueues.set(sessionId, queuedTask);
1495
+ return task;
1496
+ }
1472
1497
  createRuntimeBackedSession(input) {
1473
1498
  this.upsertRuntimeBackedSessionRecords(input);
1474
1499
  }
@@ -1547,7 +1572,7 @@ export class SessionLiveRuntimeService {
1547
1572
  sessionVisibility: currentIndex?.sessionVisibility ?? input.sessionVisibility,
1548
1573
  isSubagent: currentIndex?.isSubagent ?? false,
1549
1574
  subagentLabel: currentIndex?.subagentLabel ?? null,
1550
- title: currentIndex?.title?.trim() || buildSessionTitle(input.initialContent),
1575
+ title: currentIndex?.title?.trim() || buildSessionTitleFromContent(input.initialContent, "继续对话"),
1551
1576
  messageCount: currentIndex?.messageCount ?? 0,
1552
1577
  isArchived: currentIndex?.isArchived ?? false,
1553
1578
  lastMessageAt: currentIndex?.lastMessageAt ?? input.snapshot.lastEventAt,
@@ -1692,103 +1717,121 @@ export class SessionLiveRuntimeService {
1692
1717
  }
1693
1718
  async persistRuntimeEvent(sessionId, workspaceId, userId, event) {
1694
1719
  this.observePendingSendDebugTraceEvent(sessionId, event);
1695
- this.sessionHistoryService.persistSessionBinding(sessionId, workspaceId, {
1696
- provider: event.provider,
1697
- providerSessionId: event.providerSessionId,
1698
- rawStoreRef: event.rawStoreRef,
1699
- userId
1720
+ await this.runRuntimeSqliteWrite(sessionId, "persistSessionBinding", () => {
1721
+ this.sessionHistoryService.persistSessionBinding(sessionId, workspaceId, {
1722
+ provider: event.provider,
1723
+ providerSessionId: event.providerSessionId,
1724
+ rawStoreRef: event.rawStoreRef,
1725
+ userId
1726
+ });
1700
1727
  });
1701
- const currentState = this.sessionStateRepository.findBySessionAndUser(sessionId, userId);
1728
+ const currentState = await this.runRuntimeSqliteRead(sessionId, "findSessionState", () => this.sessionStateRepository.findBySessionAndUser(sessionId, userId));
1702
1729
  const currentRunningState = currentState?.runningState ?? null;
1703
- const shouldPreserveTerminalState = isTerminalSessionRunningState(currentRunningState);
1730
+ const shouldPreserveTerminalStatusEvent = isTerminalSessionRunningState(currentRunningState);
1704
1731
  if (event.type === "message") {
1705
1732
  this.runtimeMessageSeenSessions.add(sessionId);
1706
1733
  this.runtimeHistoryFallbackSentSessions.delete(sessionId);
1707
1734
  const workspace = this.workspaceService.getWorkspaceOrThrow(workspaceId);
1708
- await this.sessionHistoryService.syncSessionTitle(sessionId).catch(() => {
1709
- return;
1710
- });
1711
- const existing = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
1735
+ const existing = await this.runRuntimeSqliteRead(sessionId, "findSessionIndex", () => this.sessionIndexRepository.findIndexRecordBySessionId(sessionId));
1712
1736
  if (existing) {
1713
- this.sessionIndexRepository.upsert({
1714
- ...existing,
1715
- messageCount: existing.messageCount + 1,
1716
- lastMessageAt: event.message.timestamp,
1717
- updatedAt: event.message.timestamp
1737
+ await this.runRuntimeSqliteWrite(sessionId, "upsertSessionIndex", () => {
1738
+ this.sessionIndexRepository.upsert({
1739
+ ...existing,
1740
+ messageCount: existing.messageCount + 1,
1741
+ lastMessageAt: event.message.timestamp,
1742
+ updatedAt: event.message.timestamp
1743
+ });
1718
1744
  });
1719
1745
  }
1720
- this.sessionChangedFileService.recordMessages(sessionId, workspaceId, workspace.path, [event.message]);
1721
- this.sessionStateRepository.upsert({
1722
- sessionId,
1723
- userId,
1724
- runningState: shouldPreserveTerminalState ? currentRunningState : "running",
1725
- activitySource: "runtime",
1726
- favorite: currentState?.favorite ?? false,
1727
- lastEventAt: event.message.timestamp,
1728
- completedAt: shouldPreserveTerminalState ? currentState?.completedAt ?? null : null,
1729
- lastSeenAt: currentState?.lastSeenAt ?? null,
1730
- updatedAt: nowIso()
1746
+ await this.runRuntimeSqliteWrite(sessionId, "recordChangedFiles", () => {
1747
+ this.sessionChangedFileService.recordMessages(sessionId, workspaceId, workspace.path, [event.message]);
1731
1748
  });
1732
- this.upsertSnapshot(sessionId, {
1733
- syncStatus: "idle",
1734
- syncCursor: this.sessionStatusSnapshotRepository.findBySessionId(sessionId)?.syncCursor ?? null,
1735
- lastSyncAt: event.message.timestamp,
1736
- lastErrorCode: null,
1737
- lastErrorDetail: null,
1738
- resumedAt: this.sessionStatusSnapshotRepository.findBySessionId(sessionId)?.resumedAt ?? null
1749
+ await this.runRuntimeSqliteWrite(sessionId, "upsertSessionState", () => {
1750
+ this.sessionStateRepository.upsert({
1751
+ sessionId,
1752
+ userId,
1753
+ runningState: "running",
1754
+ activitySource: "runtime",
1755
+ favorite: currentState?.favorite ?? false,
1756
+ lastEventAt: event.message.timestamp,
1757
+ completedAt: null,
1758
+ lastSeenAt: currentState?.lastSeenAt ?? null,
1759
+ updatedAt: nowIso()
1760
+ });
1761
+ });
1762
+ const currentSnapshot = await this.runRuntimeSqliteRead(sessionId, "findStatusSnapshot", () => this.sessionStatusSnapshotRepository.findBySessionId(sessionId));
1763
+ await this.runRuntimeSqliteWrite(sessionId, "upsertStatusSnapshot", () => {
1764
+ this.upsertSnapshot(sessionId, {
1765
+ syncStatus: "idle",
1766
+ syncCursor: currentSnapshot?.syncCursor ?? null,
1767
+ lastSyncAt: event.message.timestamp,
1768
+ lastErrorCode: null,
1769
+ lastErrorDetail: null,
1770
+ resumedAt: currentSnapshot?.resumedAt ?? null
1771
+ });
1739
1772
  });
1740
1773
  return;
1741
1774
  }
1742
- if (shouldPreserveTerminalState) {
1743
- this.sessionStateRepository.upsert({
1744
- sessionId,
1745
- userId,
1746
- runningState: currentRunningState,
1747
- activitySource: "runtime",
1748
- favorite: currentState?.favorite ?? false,
1749
- lastEventAt: event.timestamp,
1750
- completedAt: currentState?.completedAt ?? null,
1751
- lastSeenAt: currentState?.lastSeenAt ?? null,
1752
- updatedAt: nowIso()
1775
+ if (shouldPreserveTerminalStatusEvent) {
1776
+ await this.runRuntimeSqliteWrite(sessionId, "upsertTerminalPreservedState", () => {
1777
+ this.sessionStateRepository.upsert({
1778
+ sessionId,
1779
+ userId,
1780
+ runningState: currentRunningState,
1781
+ activitySource: "runtime",
1782
+ favorite: currentState?.favorite ?? false,
1783
+ lastEventAt: event.timestamp,
1784
+ completedAt: currentState?.completedAt ?? null,
1785
+ lastSeenAt: currentState?.lastSeenAt ?? null,
1786
+ updatedAt: nowIso()
1787
+ });
1753
1788
  });
1754
- this.upsertSnapshot(sessionId, {
1755
- syncStatus: "idle",
1756
- syncCursor: this.sessionStatusSnapshotRepository.findBySessionId(sessionId)?.syncCursor ?? null,
1757
- lastSyncAt: event.timestamp,
1758
- lastErrorCode: null,
1759
- lastErrorDetail: null,
1760
- resumedAt: this.sessionStatusSnapshotRepository.findBySessionId(sessionId)?.resumedAt ?? null
1789
+ const currentSnapshot = await this.runRuntimeSqliteRead(sessionId, "findStatusSnapshot", () => this.sessionStatusSnapshotRepository.findBySessionId(sessionId));
1790
+ await this.runRuntimeSqliteWrite(sessionId, "upsertTerminalPreservedSnapshot", () => {
1791
+ this.upsertSnapshot(sessionId, {
1792
+ syncStatus: "idle",
1793
+ syncCursor: currentSnapshot?.syncCursor ?? null,
1794
+ lastSyncAt: event.timestamp,
1795
+ lastErrorCode: null,
1796
+ lastErrorDetail: null,
1797
+ resumedAt: currentSnapshot?.resumedAt ?? null
1798
+ });
1761
1799
  });
1762
1800
  await this.maybeEmitRuntimeHistoryFallback(sessionId, event);
1763
1801
  return;
1764
1802
  }
1765
1803
  const completedAt = event.status === "completed" || event.status === "interrupted" || event.status === "failed"
1766
1804
  ? event.timestamp
1767
- : this.sessionStateRepository.findBySessionAndUser(sessionId, userId)?.completedAt ?? null;
1805
+ : (await this.runRuntimeSqliteRead(sessionId, "findCompletedState", () => this.sessionStateRepository.findBySessionAndUser(sessionId, userId)))?.completedAt ?? null;
1768
1806
  if (completedAt) {
1769
1807
  await this.sessionHistoryService.syncSessionTitle(sessionId).catch(() => {
1770
1808
  return;
1771
1809
  });
1772
1810
  }
1773
- this.sessionStateRepository.upsert({
1774
- sessionId,
1775
- userId,
1776
- runningState: toStoredRunningState(event.status),
1777
- activitySource: "runtime",
1778
- favorite: currentState?.favorite ?? false,
1779
- lastEventAt: event.timestamp,
1780
- completedAt,
1781
- lastSeenAt: currentState?.lastSeenAt ?? null,
1782
- updatedAt: nowIso()
1811
+ await this.runRuntimeSqliteWrite(sessionId, "upsertRuntimeState", () => {
1812
+ this.sessionStateRepository.upsert({
1813
+ sessionId,
1814
+ userId,
1815
+ runningState: toStoredRunningState(event.status),
1816
+ activitySource: "runtime",
1817
+ favorite: currentState?.favorite ?? false,
1818
+ lastEventAt: event.timestamp,
1819
+ completedAt,
1820
+ lastSeenAt: currentState?.lastSeenAt ?? null,
1821
+ updatedAt: nowIso()
1822
+ });
1783
1823
  });
1784
1824
  this.sessionActivityAuthorityService.observe(createRuntimeEventObservation(sessionId, event, this.providerRuntimeService.getSnapshot(sessionId)?.startedAt ?? null));
1785
- this.upsertSnapshot(sessionId, {
1786
- syncStatus: event.type === "error" ? "error" : "idle",
1787
- syncCursor: this.sessionStatusSnapshotRepository.findBySessionId(sessionId)?.syncCursor ?? null,
1788
- lastSyncAt: event.timestamp,
1789
- lastErrorCode: event.type === "error" ? event.errorCode : null,
1790
- lastErrorDetail: event.type === "error" ? (event.detail ?? "runtime failed") : null,
1791
- resumedAt: this.sessionStatusSnapshotRepository.findBySessionId(sessionId)?.resumedAt ?? null
1825
+ const currentSnapshot = await this.runRuntimeSqliteRead(sessionId, "findStatusSnapshot", () => this.sessionStatusSnapshotRepository.findBySessionId(sessionId));
1826
+ await this.runRuntimeSqliteWrite(sessionId, "upsertRuntimeSnapshot", () => {
1827
+ this.upsertSnapshot(sessionId, {
1828
+ syncStatus: event.type === "error" ? "error" : "idle",
1829
+ syncCursor: currentSnapshot?.syncCursor ?? null,
1830
+ lastSyncAt: event.timestamp,
1831
+ lastErrorCode: event.type === "error" ? event.errorCode : null,
1832
+ lastErrorDetail: event.type === "error" ? (event.detail ?? "runtime failed") : null,
1833
+ resumedAt: currentSnapshot?.resumedAt ?? null
1834
+ });
1792
1835
  });
1793
1836
  await this.maybeEmitRuntimeHistoryFallback(sessionId, event);
1794
1837
  if (isTerminalRuntimeEventStatus(event.status)) {
@@ -1804,6 +1847,36 @@ export class SessionLiveRuntimeService {
1804
1847
  void this.dispatchNextQueuedMessage(sessionId);
1805
1848
  }
1806
1849
  }
1850
+ async runRuntimeSqliteRead(sessionId, scope, operation) {
1851
+ return await this.runRuntimeSqliteOperation(sessionId, scope, operation);
1852
+ }
1853
+ async runRuntimeSqliteWrite(sessionId, scope, operation) {
1854
+ await this.runRuntimeSqliteOperation(sessionId, scope, operation);
1855
+ }
1856
+ async runRuntimeSqliteOperation(sessionId, scope, operation) {
1857
+ let attempt = 0;
1858
+ while (true) {
1859
+ try {
1860
+ return operation();
1861
+ }
1862
+ catch (error) {
1863
+ if (!isSqliteBusyError(error) || attempt >= RUNTIME_EVENT_SQLITE_BUSY_RETRY_DELAYS_MS.length) {
1864
+ throw error;
1865
+ }
1866
+ const delayMs = RUNTIME_EVENT_SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
1867
+ attempt += 1;
1868
+ logPerformance("session.runtime_event.sqlite_busy_retry", delayMs, {
1869
+ sessionId,
1870
+ scope,
1871
+ attempt
1872
+ }, {
1873
+ thresholdMs: 0,
1874
+ force: true
1875
+ });
1876
+ await delay(delayMs);
1877
+ }
1878
+ }
1879
+ }
1807
1880
  async emitTerminalStateEvent(event) {
1808
1881
  for (const listener of this.terminalStateListeners) {
1809
1882
  await listener(event);
@@ -2085,6 +2158,25 @@ export class SessionLiveRuntimeService {
2085
2158
  await waitForRuntimeBindingPoll();
2086
2159
  }
2087
2160
  }
2161
+ async requestCodexTitleGenerationAfterStartBinding(sessionId, firstUserMessage, startBindingTask) {
2162
+ const normalizedFirstUserMessage = firstUserMessage.trim();
2163
+ if (!normalizedFirstUserMessage) {
2164
+ return;
2165
+ }
2166
+ try {
2167
+ await startBindingTask;
2168
+ }
2169
+ catch {
2170
+ return;
2171
+ }
2172
+ const binding = this.sessionBindingRepository.findBySessionId(sessionId);
2173
+ if (!binding
2174
+ || binding.provider !== "codex"
2175
+ || !hasResolvedRuntimeBinding(binding.providerSessionId, binding.rawStoreRef)) {
2176
+ return;
2177
+ }
2178
+ this.sessionHistoryService.requestCodexTitleGenerationForNewSession(sessionId, normalizedFirstUserMessage);
2179
+ }
2088
2180
  persistMessageAttachments(sessionId, clientRequestId, attachments) {
2089
2181
  if (!clientRequestId || attachments.length === 0) {
2090
2182
  return {
@@ -2422,10 +2514,6 @@ function mapClaudeHookToRuntimeUpdate(hookEventName, payload, timestamp) {
2422
2514
  }
2423
2515
  return null;
2424
2516
  }
2425
- function buildSessionTitle(content) {
2426
- const title = content.trim().replace(/\s+/g, " ");
2427
- return title.slice(0, 48) || "继续对话";
2428
- }
2429
2517
  function resolveRuntimeSessionTitle(provider, existingTitle, content, parentTitle, forkMethod, forkSourceType) {
2430
2518
  const normalizedExistingTitle = existingTitle.trim();
2431
2519
  const normalizedParentTitle = parentTitle?.trim() ?? "";
@@ -2435,7 +2523,7 @@ function resolveRuntimeSessionTitle(provider, existingTitle, content, parentTitl
2435
2523
  (!isForkSession || normalizedExistingTitle !== normalizedParentTitle)) {
2436
2524
  return null;
2437
2525
  }
2438
- return buildSessionTitle(content);
2526
+ return buildSessionTitleFromContent(content, "继续对话");
2439
2527
  }
2440
2528
  function isSyntheticRuntimeSessionTitle(provider, title) {
2441
2529
  if (provider !== "codex") {
@@ -2737,6 +2825,19 @@ function normalizeOptionalBindingValue(value) {
2737
2825
  const normalized = value?.trim();
2738
2826
  return normalized && normalized.length > 0 ? normalized : null;
2739
2827
  }
2828
+ function isSqliteBusyError(error) {
2829
+ if (!error || typeof error !== "object") {
2830
+ return false;
2831
+ }
2832
+ const sqliteCode = "code" in error ? error.code : null;
2833
+ const message = error instanceof Error ? error.message : String(error);
2834
+ return sqliteCode === "SQLITE_BUSY" || message.includes("database is locked");
2835
+ }
2836
+ async function delay(ms) {
2837
+ await new Promise((resolve) => {
2838
+ setTimeout(resolve, ms);
2839
+ });
2840
+ }
2740
2841
  function logOpenCodeOrderEnvelopeDebug(scope, detail) {
2741
2842
  if (!OPENCODE_ORDER_DEBUG_ENABLED) {
2742
2843
  return;