@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
@@ -75,6 +75,12 @@ interface SessionHistoryAdapterOverrides {
75
75
  providerSessionDeleteCli?: ProviderSessionDeleteCli;
76
76
  }
77
77
  type LiveActivityObservationResolver = (sessionId: string) => SessionActivityObservation | null;
78
+ type SessionTitleChangedObserver = (input: {
79
+ sessionId: string;
80
+ userId: string;
81
+ workspaceId: string;
82
+ title: string;
83
+ }) => Promise<void> | void;
78
84
  type SessionDeletedObserver = (input: {
79
85
  sessionId: string;
80
86
  userId: string;
@@ -106,6 +112,7 @@ export declare class SessionHistoryService {
106
112
  private readonly providerDiscoveryHelperClient;
107
113
  private readonly providerSessionDiscoveryConfig;
108
114
  private readonly providerRuntimeStateService;
115
+ private readonly codexSessionTitleGenerator;
109
116
  private readonly sessionProviderConfigService;
110
117
  private readonly providerControlRepository;
111
118
  private readonly taskManager;
@@ -115,6 +122,7 @@ export declare class SessionHistoryService {
115
122
  private readonly codexDirtyBindingRepairStates;
116
123
  private readonly streamingDeltaSuppressionDebugState;
117
124
  private readonly liveActivityObservationResolvers;
125
+ private readonly sessionTitleChangedObservers;
118
126
  private readonly sessionDeletedObservers;
119
127
  private readonly workspaceSessionRelations;
120
128
  private workspaceStateRefreshTaskSequence;
@@ -126,6 +134,9 @@ export declare class SessionHistoryService {
126
134
  registerSessionDeletedObserver(observer: SessionDeletedObserver): {
127
135
  close(): void;
128
136
  };
137
+ registerSessionTitleChangedObserver(observer: SessionTitleChangedObserver): {
138
+ close(): void;
139
+ };
129
140
  private registerBackgroundTasks;
130
141
  discoverWorkspaceSessions(workspaceId: string, userId: string, options?: {
131
142
  maxAgeMs?: number;
@@ -207,6 +218,7 @@ export declare class SessionHistoryService {
207
218
  private sanitizeForkHistoryPage;
208
219
  private resolveMessageOrigins;
209
220
  private buildWorkspaceSessionRelationMap;
221
+ private observeDiscoveredProviderActivity;
210
222
  private enrichSessionItems;
211
223
  private enrichSessionItem;
212
224
  private buildParallelProjectionBySessionId;
@@ -216,6 +228,10 @@ export declare class SessionHistoryService {
216
228
  private shouldSuppressStreamingSessionDelta;
217
229
  private publishHistoryEnvelope;
218
230
  private syncSessionTitleFromProvider;
231
+ requestCodexTitleGenerationForNewSession(sessionId: string, firstUserMessage: string): void;
232
+ private shouldRequestCodexTitleGeneration;
233
+ private runCodexSessionTitleGeneration;
234
+ private notifySessionTitleChanged;
219
235
  private readFirstUserMessageTitleForSync;
220
236
  private resolvePersistedParentSessionId;
221
237
  private ensureSessionChangedFilesIndexed;
@@ -9,6 +9,7 @@ import { isTerminalDebugEnabled, logTerminalDebug, terminalDebugNowMs } from "..
9
9
  import { nowIso } from "../../shared/utils/time.js";
10
10
  import { inspectSessionActivity } from "./session-activity-inspector.js";
11
11
  import { SessionActivityAuthorityService } from "./session-activity-authority-service.js";
12
+ import { buildSessionTitleFromContent, normalizeRuntimePromptTitle } from "./session-title-utils.js";
12
13
  import { mapSessionProviderError } from "./session-provider-error-mapper.js";
13
14
  import { SessionForkRepository } from "../../storage/repositories/session-fork-repository.js";
14
15
  import { buildParallelGroupColorToken, resolveParallelDisplayParentSessionId } from "../parallel-sessions/parallel-session-group-service.js";
@@ -22,6 +23,7 @@ import { ProviderRuntimeStateService } from "../provider/provider-runtime-state-
22
23
  import { createTaskManager } from "../tasks/task-manager.js";
23
24
  import { HOST_TASK_TYPES } from "../tasks/task-types.js";
24
25
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
26
+ import { CodexSessionTitleGenerator } from "./codex-session-title-generator.js";
25
27
  import { CodingnsProviderSessionDeleteCli } from "./provider-session-delete-cli.js";
26
28
  const RECONSTRUCTED_FORK_TARGET_PROVIDERS = new Set(["codex", "claude-code", "opencode"]);
27
29
  const FORK_RECONSTRUCTION_PAGE_SIZE = 200;
@@ -80,6 +82,7 @@ export class SessionHistoryService {
80
82
  providerDiscoveryHelperClient = getSharedProviderDiscoveryHelperClient();
81
83
  providerSessionDiscoveryConfig;
82
84
  providerRuntimeStateService;
85
+ codexSessionTitleGenerator;
83
86
  sessionProviderConfigService;
84
87
  providerControlRepository;
85
88
  taskManager;
@@ -89,6 +92,7 @@ export class SessionHistoryService {
89
92
  codexDirtyBindingRepairStates = new Map();
90
93
  streamingDeltaSuppressionDebugState = new Map();
91
94
  liveActivityObservationResolvers = new Set();
95
+ sessionTitleChangedObservers = new Set();
92
96
  sessionDeletedObservers = new Set();
93
97
  workspaceSessionRelations = new Map();
94
98
  workspaceStateRefreshTaskSequence = 0;
@@ -113,6 +117,10 @@ export class SessionHistoryService {
113
117
  this.sessionProviderConfigService = sessionProviderConfigService;
114
118
  this.providerRuntimeStateService = providerRuntimeStateService
115
119
  ?? new ProviderRuntimeStateService(config);
120
+ this.codexSessionTitleGenerator = new CodexSessionTitleGenerator({
121
+ hostDataRootDir: dirname(config.databasePath),
122
+ codexHomeDir: config.codexHomeDir
123
+ });
116
124
  this.providerControlRepository = providerControlRepository ?? {
117
125
  get: (providerId) => ({
118
126
  providerId: providerId.trim(),
@@ -198,6 +206,14 @@ export class SessionHistoryService {
198
206
  }
199
207
  };
200
208
  }
209
+ registerSessionTitleChangedObserver(observer) {
210
+ this.sessionTitleChangedObservers.add(observer);
211
+ return {
212
+ close: () => {
213
+ this.sessionTitleChangedObservers.delete(observer);
214
+ }
215
+ };
216
+ }
201
217
  registerBackgroundTasks() {
202
218
  if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscovery)) {
203
219
  this.taskManager.register({
@@ -228,6 +244,15 @@ export class SessionHistoryService {
228
244
  }
229
245
  });
230
246
  }
247
+ if (!this.taskManager.has(HOST_TASK_TYPES.sessionCodexTitleGenerate)) {
248
+ this.taskManager.register({
249
+ taskType: HOST_TASK_TYPES.sessionCodexTitleGenerate,
250
+ executionLane: "external_process",
251
+ concurrency: 1,
252
+ timeoutMs: 45_000,
253
+ run: async ({ sessionId, firstUserMessage }, context) => this.runCodexSessionTitleGeneration(sessionId, firstUserMessage, context.signal)
254
+ });
255
+ }
231
256
  }
232
257
  async discoverWorkspaceSessions(workspaceId, userId, options) {
233
258
  const maxAgeMs = options?.maxAgeMs ?? 0;
@@ -1624,6 +1649,7 @@ export class SessionHistoryService {
1624
1649
  cleanupDurationMs = Date.now() - cleanupStartedAt;
1625
1650
  }
1626
1651
  this.workspaceSessionRelations.set(workspaceId, relationMap);
1652
+ this.observeDiscoveredProviderActivity(sessions, discoveredSessionIds, userId, timestamp);
1627
1653
  const listItemsStartedAt = Date.now();
1628
1654
  const items = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
1629
1655
  listItemsDurationMs = Date.now() - listItemsStartedAt;
@@ -1911,6 +1937,72 @@ export class SessionHistoryService {
1911
1937
  }
1912
1938
  return relationMap;
1913
1939
  }
1940
+ observeDiscoveredProviderActivity(sessions, discoveredSessionIds, userId, timestamp) {
1941
+ for (const session of sessions) {
1942
+ const providerObservation = session.activityObservation ?? null;
1943
+ if (!providerObservation) {
1944
+ continue;
1945
+ }
1946
+ const sessionId = discoveredSessionIds.get(buildProviderSessionKey(session.provider, session.providerSessionId));
1947
+ if (!sessionId) {
1948
+ continue;
1949
+ }
1950
+ const observedAt = providerObservation.observedAt ?? timestamp;
1951
+ const resolution = this.sessionActivityAuthorityService.observe({
1952
+ sessionId,
1953
+ runId: providerObservation.runId ?? null,
1954
+ runningState: providerObservation.runningState,
1955
+ source: "authoritative_provider_event",
1956
+ confidence: providerObservation.confidence,
1957
+ detail: providerObservation.detail ?? null,
1958
+ interruptSource: providerObservation.runningState === "interrupted" ? "runtime" : null,
1959
+ errorCode: providerObservation.errorCode ?? null,
1960
+ observedAt
1961
+ });
1962
+ const current = this.sessionStateRepository.findBySessionAndUser(sessionId, userId);
1963
+ this.sessionStateRepository.upsert({
1964
+ sessionId,
1965
+ userId,
1966
+ runningState: mapResolvedRunningStateToStored(resolution.runningState, current),
1967
+ activitySource: "runtime",
1968
+ favorite: current?.favorite ?? false,
1969
+ lastEventAt: resolution.lastObservedAt ?? current?.lastEventAt ?? null,
1970
+ completedAt: isTerminalResolvedRunningState(resolution.runningState)
1971
+ ? resolution.terminalAt ?? current?.completedAt ?? observedAt
1972
+ : null,
1973
+ lastSeenAt: current?.lastSeenAt ?? null,
1974
+ updatedAt: timestamp
1975
+ });
1976
+ const currentSnapshot = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
1977
+ const shouldClearFailure = currentSnapshot?.syncStatus === "error"
1978
+ && providerObservation.runningState !== "failed";
1979
+ this.sessionStatusSnapshotRepository.upsert({
1980
+ sessionId,
1981
+ syncStatus: providerObservation.runningState === "failed"
1982
+ ? "error"
1983
+ : shouldClearFailure
1984
+ ? "idle"
1985
+ : currentSnapshot?.syncStatus ?? "idle",
1986
+ syncCursor: currentSnapshot?.syncCursor ?? null,
1987
+ lastSyncAt: resolution.lastObservedAt
1988
+ ?? resolution.terminalAt
1989
+ ?? currentSnapshot?.lastSyncAt
1990
+ ?? null,
1991
+ lastErrorCode: providerObservation.runningState === "failed"
1992
+ ? providerObservation.errorCode ?? "CODEX_PROVIDER_EVENT_FAILED"
1993
+ : shouldClearFailure
1994
+ ? null
1995
+ : currentSnapshot?.lastErrorCode ?? null,
1996
+ lastErrorDetail: providerObservation.runningState === "failed"
1997
+ ? providerObservation.detail ?? "Provider reported session failure"
1998
+ : shouldClearFailure
1999
+ ? null
2000
+ : currentSnapshot?.lastErrorDetail ?? null,
2001
+ resumedAt: currentSnapshot?.resumedAt ?? null,
2002
+ updatedAt: timestamp
2003
+ });
2004
+ }
2005
+ }
1914
2006
  enrichSessionItems(workspaceId, items) {
1915
2007
  const relationMap = this.workspaceSessionRelations.get(workspaceId);
1916
2008
  const projectionBySessionId = this.buildParallelProjectionBySessionId(items);
@@ -2187,6 +2279,102 @@ export class SessionHistoryService {
2187
2279
  updatedAt: nowIso()
2188
2280
  });
2189
2281
  }
2282
+ requestCodexTitleGenerationForNewSession(sessionId, firstUserMessage) {
2283
+ const normalizedFirstUserMessage = firstUserMessage.trim();
2284
+ if (!normalizedFirstUserMessage) {
2285
+ return;
2286
+ }
2287
+ if (!this.shouldRequestCodexTitleGeneration(sessionId, normalizedFirstUserMessage)) {
2288
+ return;
2289
+ }
2290
+ const handle = this.taskManager.enqueue(HOST_TASK_TYPES.sessionCodexTitleGenerate, {
2291
+ key: sessionId,
2292
+ source: "session_history.new_codex_session_title",
2293
+ input: {
2294
+ sessionId,
2295
+ firstUserMessage: normalizedFirstUserMessage
2296
+ }
2297
+ });
2298
+ if (handle.deduped) {
2299
+ return;
2300
+ }
2301
+ void handle.promise.catch((error) => {
2302
+ logPerformance("session.codex_title_generate.background_failed", 0, {
2303
+ sessionId,
2304
+ error: error instanceof Error ? error.message : "unknown"
2305
+ }, {
2306
+ thresholdMs: 0,
2307
+ force: true
2308
+ });
2309
+ });
2310
+ }
2311
+ shouldRequestCodexTitleGeneration(sessionId, firstUserMessage) {
2312
+ const binding = this.sessionBindingRepository.findBySessionId(sessionId);
2313
+ const index = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
2314
+ if (!binding || !index || binding.provider !== "codex") {
2315
+ return false;
2316
+ }
2317
+ if (isPendingBindingValue(binding.providerSessionId) || isPendingBindingValue(binding.rawStoreRef)) {
2318
+ return false;
2319
+ }
2320
+ return shouldGenerateCodexSessionTitle(index.title, firstUserMessage);
2321
+ }
2322
+ async runCodexSessionTitleGeneration(sessionId, firstUserMessage, signal) {
2323
+ const binding = this.sessionBindingRepository.findBySessionId(sessionId);
2324
+ const index = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
2325
+ if (!binding || !index || binding.provider !== "codex") {
2326
+ return { title: null };
2327
+ }
2328
+ if (isPendingBindingValue(binding.providerSessionId) || isPendingBindingValue(binding.rawStoreRef)) {
2329
+ return { title: null };
2330
+ }
2331
+ if (!shouldGenerateCodexSessionTitle(index.title, firstUserMessage)) {
2332
+ return { title: null };
2333
+ }
2334
+ const title = await this.codexSessionTitleGenerator.generate({
2335
+ currentTitle: index.title,
2336
+ messages: [
2337
+ {
2338
+ role: "user",
2339
+ content: firstUserMessage
2340
+ }
2341
+ ],
2342
+ signal
2343
+ });
2344
+ if (!title || !shouldApplyGeneratedCodexSessionTitle(title, firstUserMessage)) {
2345
+ return { title: null };
2346
+ }
2347
+ await this.sessionSyncService.renameSessionTitle(binding.provider, binding.providerSessionId, binding.rawStoreRef, title);
2348
+ const latestIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
2349
+ if (latestIndex && shouldGenerateCodexSessionTitle(latestIndex.title, firstUserMessage)) {
2350
+ this.sessionIndexRepository.upsert({
2351
+ ...latestIndex,
2352
+ title,
2353
+ updatedAt: nowIso()
2354
+ });
2355
+ await this.notifySessionTitleChanged({
2356
+ sessionId,
2357
+ userId: binding.userId,
2358
+ workspaceId: binding.workspaceId,
2359
+ title
2360
+ });
2361
+ }
2362
+ return { title };
2363
+ }
2364
+ async notifySessionTitleChanged(input) {
2365
+ const userId = input.userId ?? this.workspaceRepository.findById(input.workspaceId)?.ownerUserId;
2366
+ if (!userId) {
2367
+ return;
2368
+ }
2369
+ await Promise.allSettled(Array.from(this.sessionTitleChangedObservers).map(async (observer) => {
2370
+ await observer({
2371
+ sessionId: input.sessionId,
2372
+ userId,
2373
+ workspaceId: input.workspaceId,
2374
+ title: input.title
2375
+ });
2376
+ }));
2377
+ }
2190
2378
  async readFirstUserMessageTitleForSync(binding) {
2191
2379
  const pageSize = 20;
2192
2380
  const maxPages = 3;
@@ -4101,7 +4289,7 @@ function shouldMatchSessionBindingByRawStoreRef(provider) {
4101
4289
  function resolveSessionListTitle(provider, existingTitle, fallbackContent, parentTitle = null) {
4102
4290
  const normalizedExistingTitle = existingTitle?.trim() ?? "";
4103
4291
  const normalizedParentTitle = parentTitle?.trim() ?? "";
4104
- const fallbackTitle = buildUserMessageTitle(fallbackContent, normalizedExistingTitle || "继续对话");
4292
+ const fallbackTitle = buildSessionTitleFromContent(fallbackContent, normalizedExistingTitle || "继续对话");
4105
4293
  if (normalizedExistingTitle.length > 0 &&
4106
4294
  !isSyntheticCodexSessionTitle(normalizedExistingTitle) &&
4107
4295
  (normalizedParentTitle.length === 0 ||
@@ -4116,17 +4304,6 @@ function resolveSessionListTitle(provider, existingTitle, fallbackContent, paren
4116
4304
  }
4117
4305
  return normalizedExistingTitle || fallbackTitle;
4118
4306
  }
4119
- function buildUserMessageTitle(content, fallbackTitle) {
4120
- const title = content.trim().replace(/\s+/g, " ");
4121
- return title.slice(0, 48) || fallbackTitle;
4122
- }
4123
- function normalizeRuntimePromptTitle(content) {
4124
- const normalized = (typeof content === "string" ? content : "").trim().replace(/\s+/g, " ");
4125
- if (normalized.length === 0) {
4126
- return null;
4127
- }
4128
- return normalized.slice(0, 48);
4129
- }
4130
4307
  function buildRecoveredSessionTitle(provider, providerSessionId) {
4131
4308
  if (isPendingBindingValue(providerSessionId)) {
4132
4309
  return "新会话";
@@ -4195,6 +4372,25 @@ function shouldSyncSessionTitleFromProvider(provider, currentTitle, firstUserMes
4195
4372
  }
4196
4373
  return false;
4197
4374
  }
4375
+ function shouldGenerateCodexSessionTitle(currentTitle, firstUserMessage) {
4376
+ const normalizedTitle = currentTitle?.trim() ?? "";
4377
+ if (!normalizedTitle) {
4378
+ return true;
4379
+ }
4380
+ if (looksLikeGeneratedSessionTitle(normalizedTitle) || isSyntheticCodexSessionTitle(normalizedTitle)) {
4381
+ return true;
4382
+ }
4383
+ const firstUserTitle = normalizeRuntimePromptTitle(firstUserMessage);
4384
+ return Boolean(firstUserTitle && normalizedTitle === firstUserTitle);
4385
+ }
4386
+ function shouldApplyGeneratedCodexSessionTitle(generatedTitle, firstUserMessage) {
4387
+ const normalizedTitle = generatedTitle.trim();
4388
+ const firstUserTitle = normalizeRuntimePromptTitle(firstUserMessage);
4389
+ if (!normalizedTitle) {
4390
+ return false;
4391
+ }
4392
+ return !firstUserTitle || normalizedTitle !== firstUserTitle;
4393
+ }
4198
4394
  function looksLikeSyntheticTitleSourceMessage(provider, content) {
4199
4395
  const normalizedProvider = provider.trim().toLowerCase();
4200
4396
  if (normalizedProvider === "codex") {