@jingyi0605/codingns 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/dist/public/assets/{TerminalPage-4ulgBhv9.js → TerminalPage-BlbQuWi1.js} +1 -1
  2. package/dist/public/assets/gemini-D4G1NbrE.png +0 -0
  3. package/dist/public/assets/index-1VIm8lVL.css +1 -0
  4. package/dist/public/assets/index-Dti93O2S.js +109 -0
  5. package/dist/public/assets/kimi-BWNNSh7e.png +0 -0
  6. package/dist/public/index.html +2 -2
  7. package/dist/server/config/env.d.ts +7 -0
  8. package/dist/server/config/env.js +150 -1
  9. package/dist/server/config/env.js.map +1 -1
  10. package/dist/server/config/opencode-system-probe-helper-process.d.ts +24 -0
  11. package/dist/server/config/opencode-system-probe-helper-process.js +70 -5
  12. package/dist/server/config/opencode-system-probe-helper-process.js.map +1 -1
  13. package/dist/server/modules/butler/butler-action-context-service.d.ts +30 -0
  14. package/dist/server/modules/butler/butler-action-context-service.js +108 -0
  15. package/dist/server/modules/butler/butler-action-context-service.js.map +1 -0
  16. package/dist/server/modules/butler/butler-auth-service.d.ts +17 -0
  17. package/dist/server/modules/butler/butler-auth-service.js +91 -0
  18. package/dist/server/modules/butler/butler-auth-service.js.map +1 -0
  19. package/dist/server/modules/butler/butler-control-action-service.d.ts +65 -0
  20. package/dist/server/modules/butler/butler-control-action-service.js +296 -0
  21. package/dist/server/modules/butler/butler-control-action-service.js.map +1 -0
  22. package/dist/server/modules/butler/butler-control-session-service.d.ts +55 -0
  23. package/dist/server/modules/butler/butler-control-session-service.js +367 -0
  24. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -0
  25. package/dist/server/modules/butler/butler-controller.d.ts +367 -0
  26. package/dist/server/modules/butler/butler-controller.js +475 -0
  27. package/dist/server/modules/butler/butler-controller.js.map +1 -0
  28. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.d.ts +34 -0
  29. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js +77 -0
  30. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js.map +1 -0
  31. package/dist/server/modules/butler/butler-follow-up-scheduler.d.ts +23 -0
  32. package/dist/server/modules/butler/butler-follow-up-scheduler.js +57 -0
  33. package/dist/server/modules/butler/butler-follow-up-scheduler.js.map +1 -0
  34. package/dist/server/modules/butler/butler-follow-up-service.d.ts +86 -0
  35. package/dist/server/modules/butler/butler-follow-up-service.js +948 -0
  36. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -0
  37. package/dist/server/modules/butler/butler-inbox-service.d.ts +35 -0
  38. package/dist/server/modules/butler/butler-inbox-service.js +136 -0
  39. package/dist/server/modules/butler/butler-inbox-service.js.map +1 -0
  40. package/dist/server/modules/butler/butler-notification-service.d.ts +12 -0
  41. package/dist/server/modules/butler/butler-notification-service.js +45 -0
  42. package/dist/server/modules/butler/butler-notification-service.js.map +1 -0
  43. package/dist/server/modules/butler/butler-profile-service.d.ts +26 -0
  44. package/dist/server/modules/butler/butler-profile-service.js +529 -0
  45. package/dist/server/modules/butler/butler-profile-service.js.map +1 -0
  46. package/dist/server/modules/butler/butler-project-service.d.ts +48 -0
  47. package/dist/server/modules/butler/butler-project-service.js +253 -0
  48. package/dist/server/modules/butler/butler-project-service.js.map +1 -0
  49. package/dist/server/modules/butler/butler-session-service.d.ts +79 -0
  50. package/dist/server/modules/butler/butler-session-service.js +503 -0
  51. package/dist/server/modules/butler/butler-session-service.js.map +1 -0
  52. package/dist/server/modules/butler/butler-session-summary-service.d.ts +55 -0
  53. package/dist/server/modules/butler/butler-session-summary-service.js +382 -0
  54. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -0
  55. package/dist/server/modules/butler/context-aggregator.d.ts +187 -0
  56. package/dist/server/modules/butler/context-aggregator.js +807 -0
  57. package/dist/server/modules/butler/context-aggregator.js.map +1 -0
  58. package/dist/server/modules/butler/instruction-adapter.d.ts +28 -0
  59. package/dist/server/modules/butler/instruction-adapter.js +101 -0
  60. package/dist/server/modules/butler/instruction-adapter.js.map +1 -0
  61. package/dist/server/modules/butler/patrol-execution-service.d.ts +47 -0
  62. package/dist/server/modules/butler/patrol-execution-service.js +347 -0
  63. package/dist/server/modules/butler/patrol-execution-service.js.map +1 -0
  64. package/dist/server/modules/butler/patrol-plan-service.d.ts +54 -0
  65. package/dist/server/modules/butler/patrol-plan-service.js +272 -0
  66. package/dist/server/modules/butler/patrol-plan-service.js.map +1 -0
  67. package/dist/server/modules/butler/patrol-run-service.d.ts +60 -0
  68. package/dist/server/modules/butler/patrol-run-service.js +185 -0
  69. package/dist/server/modules/butler/patrol-run-service.js.map +1 -0
  70. package/dist/server/modules/butler/patrol-scheduler.d.ts +36 -0
  71. package/dist/server/modules/butler/patrol-scheduler.js +99 -0
  72. package/dist/server/modules/butler/patrol-scheduler.js.map +1 -0
  73. package/dist/server/modules/butler/project-memory-service.d.ts +30 -0
  74. package/dist/server/modules/butler/project-memory-service.js +103 -0
  75. package/dist/server/modules/butler/project-memory-service.js.map +1 -0
  76. package/dist/server/modules/butler/provider-adapter-registry.d.ts +61 -0
  77. package/dist/server/modules/butler/provider-adapter-registry.js +430 -0
  78. package/dist/server/modules/butler/provider-adapter-registry.js.map +1 -0
  79. package/dist/server/modules/butler/session-summary-instruction-adapter.d.ts +28 -0
  80. package/dist/server/modules/butler/session-summary-instruction-adapter.js +79 -0
  81. package/dist/server/modules/butler/session-summary-instruction-adapter.js.map +1 -0
  82. package/dist/server/modules/butler/session-summary-scheduler.d.ts +23 -0
  83. package/dist/server/modules/butler/session-summary-scheduler.js +57 -0
  84. package/dist/server/modules/butler/session-summary-scheduler.js.map +1 -0
  85. package/dist/server/modules/butler/verification-run-service.d.ts +73 -0
  86. package/dist/server/modules/butler/verification-run-service.js +633 -0
  87. package/dist/server/modules/butler/verification-run-service.js.map +1 -0
  88. package/dist/server/modules/file/file-controller.d.ts +12 -1
  89. package/dist/server/modules/file/file-controller.js +72 -1
  90. package/dist/server/modules/file/file-controller.js.map +1 -1
  91. package/dist/server/modules/file/file-preview-link-service.d.ts +22 -0
  92. package/dist/server/modules/file/file-preview-link-service.js +160 -0
  93. package/dist/server/modules/file/file-preview-link-service.js.map +1 -0
  94. package/dist/server/modules/preferences/profile-service.js +8 -2
  95. package/dist/server/modules/preferences/profile-service.js.map +1 -1
  96. package/dist/server/modules/sessions/claude-runtime-helper-process.js +1 -1
  97. package/dist/server/modules/sessions/claude-runtime-helper-process.js.map +1 -1
  98. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +7 -2
  99. package/dist/server/modules/sessions/codex-app-server-helper-client.js +113 -2
  100. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  101. package/dist/server/modules/sessions/codex-app-server-helper-process.js +106 -1
  102. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  103. package/dist/server/modules/sessions/session-controller.d.ts +24 -1
  104. package/dist/server/modules/sessions/session-controller.js +34 -3
  105. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  106. package/dist/server/modules/sessions/session-history-service.d.ts +47 -2
  107. package/dist/server/modules/sessions/session-history-service.js +881 -56
  108. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  109. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +30 -2
  110. package/dist/server/modules/sessions/session-live-runtime-service.js +584 -159
  111. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  112. package/dist/server/modules/sessions/session-provider-error-mapper.js +94 -0
  113. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  114. package/dist/server/modules/workbench/workbench-service.d.ts +7 -1
  115. package/dist/server/modules/workbench/workbench-service.js +31 -7
  116. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  117. package/dist/server/routes/butler.d.ts +3 -0
  118. package/dist/server/routes/butler.js +54 -0
  119. package/dist/server/routes/butler.js.map +1 -0
  120. package/dist/server/routes/files.js +2 -0
  121. package/dist/server/routes/files.js.map +1 -1
  122. package/dist/server/routes/sessions.js +1 -0
  123. package/dist/server/routes/sessions.js.map +1 -1
  124. package/dist/server/server/create-server.d.ts +65 -0
  125. package/dist/server/server/create-server.js +154 -5
  126. package/dist/server/server/create-server.js.map +1 -1
  127. package/dist/server/shared/utils/command-availability.d.ts +1 -0
  128. package/dist/server/shared/utils/command-availability.js +83 -0
  129. package/dist/server/shared/utils/command-availability.js.map +1 -0
  130. package/dist/server/storage/repositories/butler-control-event-repository.d.ts +8 -0
  131. package/dist/server/storage/repositories/butler-control-event-repository.js +78 -0
  132. package/dist/server/storage/repositories/butler-control-event-repository.js.map +1 -0
  133. package/dist/server/storage/repositories/butler-control-session-repository.d.ts +11 -0
  134. package/dist/server/storage/repositories/butler-control-session-repository.js +86 -0
  135. package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -0
  136. package/dist/server/storage/repositories/butler-follow-up-task-repository.d.ts +16 -0
  137. package/dist/server/storage/repositories/butler-follow-up-task-repository.js +252 -0
  138. package/dist/server/storage/repositories/butler-follow-up-task-repository.js.map +1 -0
  139. package/dist/server/storage/repositories/butler-inbox-item-repository.d.ts +15 -0
  140. package/dist/server/storage/repositories/butler-inbox-item-repository.js +111 -0
  141. package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -0
  142. package/dist/server/storage/repositories/butler-notification-archive-repository.d.ts +9 -0
  143. package/dist/server/storage/repositories/butler-notification-archive-repository.js +48 -0
  144. package/dist/server/storage/repositories/butler-notification-archive-repository.js.map +1 -0
  145. package/dist/server/storage/repositories/butler-profile-repository.d.ts +9 -0
  146. package/dist/server/storage/repositories/butler-profile-repository.js +86 -0
  147. package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -0
  148. package/dist/server/storage/repositories/butler-project-repository.d.ts +14 -0
  149. package/dist/server/storage/repositories/butler-project-repository.js +140 -0
  150. package/dist/server/storage/repositories/butler-project-repository.js.map +1 -0
  151. package/dist/server/storage/repositories/butler-session-repository.d.ts +11 -0
  152. package/dist/server/storage/repositories/butler-session-repository.js +106 -0
  153. package/dist/server/storage/repositories/butler-session-repository.js.map +1 -0
  154. package/dist/server/storage/repositories/butler-session-summary-state-repository.d.ts +8 -0
  155. package/dist/server/storage/repositories/butler-session-summary-state-repository.js +62 -0
  156. package/dist/server/storage/repositories/butler-session-summary-state-repository.js.map +1 -0
  157. package/dist/server/storage/repositories/patrol-plan-repository.d.ts +27 -0
  158. package/dist/server/storage/repositories/patrol-plan-repository.js +119 -0
  159. package/dist/server/storage/repositories/patrol-plan-repository.js.map +1 -0
  160. package/dist/server/storage/repositories/patrol-run-repository.d.ts +28 -0
  161. package/dist/server/storage/repositories/patrol-run-repository.js +121 -0
  162. package/dist/server/storage/repositories/patrol-run-repository.js.map +1 -0
  163. package/dist/server/storage/repositories/project-memory-repository.d.ts +15 -0
  164. package/dist/server/storage/repositories/project-memory-repository.js +150 -0
  165. package/dist/server/storage/repositories/project-memory-repository.js.map +1 -0
  166. package/dist/server/storage/repositories/session-checkpoint-repository.d.ts +9 -0
  167. package/dist/server/storage/repositories/session-checkpoint-repository.js +72 -0
  168. package/dist/server/storage/repositories/session-checkpoint-repository.js.map +1 -0
  169. package/dist/server/storage/repositories/session-fork-repository.d.ts +8 -0
  170. package/dist/server/storage/repositories/session-fork-repository.js +69 -0
  171. package/dist/server/storage/repositories/session-fork-repository.js.map +1 -0
  172. package/dist/server/storage/repositories/session-index-repository.js +40 -2
  173. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  174. package/dist/server/storage/repositories/session-message-origin-repository.d.ts +10 -0
  175. package/dist/server/storage/repositories/session-message-origin-repository.js +93 -0
  176. package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -0
  177. package/dist/server/storage/repositories/verification-run-repository.d.ts +29 -0
  178. package/dist/server/storage/repositories/verification-run-repository.js +125 -0
  179. package/dist/server/storage/repositories/verification-run-repository.js.map +1 -0
  180. package/dist/server/storage/sqlite/client.js +146 -0
  181. package/dist/server/storage/sqlite/client.js.map +1 -1
  182. package/dist/server/storage/sqlite/schema.sql +354 -0
  183. package/dist/server/types/domain.d.ts +286 -2
  184. package/dist/server/ws/ws-server.d.ts +2 -1
  185. package/dist/server/ws/ws-server.js +2 -1
  186. package/dist/server/ws/ws-server.js.map +1 -1
  187. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +4 -0
  188. package/node_modules/@codingns/session-sync-core/dist/index.js +4 -0
  189. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  190. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +18 -0
  191. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +659 -0
  192. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +1 -0
  193. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +11 -0
  194. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +72 -0
  195. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +1 -0
  196. package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +8 -0
  197. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +89 -0
  198. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -1
  199. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +6 -1
  200. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +228 -7
  201. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  202. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +26 -1
  203. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +499 -3
  204. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  205. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +41 -0
  206. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +1175 -0
  207. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -0
  208. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +29 -0
  209. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +578 -0
  210. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -0
  211. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +5 -1
  212. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +271 -4
  213. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  214. package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +1 -0
  215. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +147 -19
  216. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
  217. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +2 -0
  218. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +43 -5
  219. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  220. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +12 -0
  221. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +442 -71
  222. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  223. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +21 -0
  224. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +537 -0
  225. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -0
  226. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +38 -0
  227. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +911 -0
  228. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +1 -0
  229. package/node_modules/@codingns/session-sync-core/dist/services.d.ts +2 -1
  230. package/node_modules/@codingns/session-sync-core/dist/services.js +55 -8
  231. package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
  232. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +6 -0
  233. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +9 -0
  234. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +1 -0
  235. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +27 -0
  236. package/node_modules/@codingns/session-sync-core/package.json +8 -0
  237. package/package.json +1 -1
  238. package/dist/public/assets/index-C5lu52cQ.css +0 -1
  239. package/dist/public/assets/index-WpdUo_Vs.js +0 -108
@@ -1,8 +1,10 @@
1
1
  import { existsSync, readdirSync } from "node:fs";
2
2
  import path from "node:path";
3
- import { ClaudeRuntimeAdapter, CodexRuntimeAdapter, OpenCodeRuntimeAdapter, ProviderRuntimeService } from "@codingns/session-sync-core";
3
+ import { performance } from "node:perf_hooks";
4
+ import { ClaudeRuntimeAdapter, CodexRuntimeAdapter, GeminiRuntimeAdapter, KimiRuntimeAdapter, OpenCodeRuntimeAdapter, ProviderRuntimeService } from "@codingns/session-sync-core";
4
5
  import { AppError } from "../../shared/errors/app-error.js";
5
6
  import { createId } from "../../shared/utils/id.js";
7
+ import { isPerfDebugEnabled, logPerformance } from "../../shared/utils/perf-log.js";
6
8
  import { logPermissionDebug } from "../../shared/utils/permission-debug-log.js";
7
9
  import { nowIso } from "../../shared/utils/time.js";
8
10
  import { SessionActivityAuthorityService } from "./session-activity-authority-service.js";
@@ -10,6 +12,8 @@ import { SessionPermissionRequestService } from "./session-permission-request-se
10
12
  import { mapSessionProviderError } from "./session-provider-error-mapper.js";
11
13
  import { ClaudeRuntimeHelperAdapter } from "./claude-runtime-helper-client.js";
12
14
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
15
+ const RUNTIME_START_BINDING_WAIT_TIMEOUT_MS = 10_000;
16
+ const START_BINDING_POLL_INTERVAL_MS = 50;
13
17
  export class SessionLiveRuntimeService {
14
18
  sessionHistoryService;
15
19
  sessionMessageAttachmentService;
@@ -28,10 +32,12 @@ export class SessionLiveRuntimeService {
28
32
  runtimeAdapterDisposables;
29
33
  externalRuntimeSnapshots = new Map();
30
34
  runtimeListeners = new Map();
35
+ terminalStateListeners = new Set();
31
36
  runtimeMessageSeenSessions = new Set();
32
37
  runtimeHistoryFallbackSentSessions = new Set();
33
38
  queueDispatchSessions = new Set();
34
39
  queueRetryTimers = new Map();
40
+ pendingSendDebugTracesBySessionId = new Map();
35
41
  constructor(sessionHistoryService, sessionMessageAttachmentService, workspaceService, sessionChangedFileService, sessionBindingRepository, authUserRepository, sessionSendQueueRepository, sessionIndexRepository, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService()) {
36
42
  this.sessionHistoryService = sessionHistoryService;
37
43
  this.sessionMessageAttachmentService = sessionMessageAttachmentService;
@@ -58,63 +64,113 @@ export class SessionLiveRuntimeService {
58
64
  }
59
65
  async startLiveSession(input) {
60
66
  const requestStartedAt = nowIso();
61
- const capabilities = this.sessionHistoryService.getProviderCapabilitiesSnapshot(input.provider);
62
- const workspace = this.workspaceService.getWorkspaceOrThrow(input.workspaceId);
63
67
  const sessionId = createId();
64
- this.ensurePendingSessionBinding(sessionId, workspace.id, input.provider);
65
- const persistedAttachments = this.persistMessageAttachments(sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
66
- const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(input.provider, input.content, persistedAttachments.runtimeAttachments);
67
- this.ensureCapability(capabilities.canStartSession, "provider", "provider 不支持 start-live");
68
- this.ensureCapability(capabilities.canSendMessage, "provider", "provider 不支持实时对话");
69
- const handle = await this.launchRuntimeRun({
70
- sessionId,
71
- workspaceId: workspace.id,
72
- workspacePath: workspace.path,
73
- provider: input.provider,
74
- providerSessionId: null,
75
- rawStoreRef: null,
76
- sequenceBase: 1,
77
- options: {
78
- content: input.content,
79
- clientRequestId: input.clientRequestId,
80
- model: input.runtimeOptions?.model ?? null,
81
- reasoningLevel: input.runtimeOptions?.reasoningLevel ?? null,
82
- permissionMode: input.runtimeOptions?.permissionMode ?? null,
83
- providerPrompt,
84
- attachments: persistedAttachments.runtimeAttachments
85
- }
86
- }, "start");
87
- const snapshot = handle.getSnapshot();
88
- this.attachRuntimePersistence(handle, sessionId, workspace.id, input.userId);
89
- this.createRuntimeBackedSession({
68
+ const workspace = this.workspaceService.getWorkspaceOrThrow(input.workspaceId);
69
+ const debugTrace = this.beginPendingSendDebugTrace({
70
+ mode: "start_live",
90
71
  sessionId,
91
72
  workspaceId: workspace.id,
92
- userId: input.userId,
93
73
  provider: input.provider,
94
- initialContent: input.content,
95
- snapshot
74
+ clientRequestId: input.clientRequestId
96
75
  });
97
- const binding = this.sessionHistoryService.getBindingOrThrow(sessionId);
98
- const acceptedMessage = await this.findAcceptedUserMessage(sessionId, this.sessionMessageAttachmentService.buildAcceptedContentCandidates(input.content, providerPrompt), requestStartedAt);
99
- const acceptedAt = acceptedMessage?.timestamp ?? nowIso();
100
- const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
101
- return {
102
- sessionId,
103
- provider: input.provider,
104
- providerSessionId: binding.providerSessionId,
105
- acceptedAt,
106
- clientRequestId: input.clientRequestId,
107
- message: (acceptedMessage
108
- ? {
109
- ...acceptedMessage,
110
- attachments: boundAttachments
76
+ try {
77
+ const capabilities = this.sessionHistoryService.getProviderCapabilitiesSnapshot(input.provider);
78
+ this.ensurePendingSessionBinding(sessionId, workspace.id, input.provider);
79
+ const persistedAttachments = this.persistMessageAttachments(sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
80
+ const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(input.provider, input.content, persistedAttachments.runtimeAttachments);
81
+ this.ensureCapability(capabilities.canStartSession, "provider", "provider 不支持 start-live");
82
+ this.ensureCapability(capabilities.canSendMessage, "provider", "provider 不支持实时对话");
83
+ const launchRuntimeStartedAtMs = performance.now();
84
+ const handle = await this.launchRuntimeRun({
85
+ sessionId,
86
+ workspaceId: workspace.id,
87
+ workspacePath: workspace.path,
88
+ provider: input.provider,
89
+ providerSessionId: null,
90
+ rawStoreRef: null,
91
+ sequenceBase: 1,
92
+ options: {
93
+ content: input.content,
94
+ clientRequestId: input.clientRequestId,
95
+ model: input.runtimeOptions?.model ?? null,
96
+ reasoningLevel: input.runtimeOptions?.reasoningLevel ?? null,
97
+ permissionMode: input.runtimeOptions?.permissionMode ?? null,
98
+ providerPrompt,
99
+ attachments: persistedAttachments.runtimeAttachments
111
100
  }
112
- : null) ??
113
- createSyntheticUserMessage(input.provider, binding.providerSessionId, input.content, acceptedAt, 1, boundAttachments.length > 0
114
- ? boundAttachments
115
- : persistedAttachments.messageAttachments),
116
- session: this.sessionHistoryService.getSession(sessionId, input.userId)
117
- };
101
+ }, "start");
102
+ this.logSendDebugStep(debugTrace, "launch_runtime", launchRuntimeStartedAtMs, {
103
+ userId: input.userId
104
+ });
105
+ const snapshot = handle.getSnapshot();
106
+ this.attachRuntimePersistence(handle, sessionId, workspace.id, input.userId);
107
+ this.createRuntimeBackedSession({
108
+ sessionId,
109
+ workspaceId: workspace.id,
110
+ userId: input.userId,
111
+ provider: input.provider,
112
+ parentSessionId: input.parentSessionId ?? null,
113
+ sessionKind: input.sessionKind ?? "default",
114
+ annotationSourceMessageId: input.annotationSourceMessageId ?? null,
115
+ annotationSourceText: input.annotationSourceText ?? null,
116
+ initialContent: input.content,
117
+ snapshot
118
+ });
119
+ const startBindingTask = this.waitForResolvedStartBinding(sessionId, workspace.id, input.provider, handle).catch(() => {
120
+ return;
121
+ });
122
+ if (shouldAwaitStartBindingBeforeAcceptedUserLookup(input.provider)) {
123
+ const bindingWaitStartedAtMs = performance.now();
124
+ await Promise.race([
125
+ startBindingTask,
126
+ waitForAcceptedUserLookupWindow()
127
+ ]);
128
+ this.logSendDebugStep(debugTrace, "binding_wait", bindingWaitStartedAtMs, {
129
+ provider: input.provider
130
+ });
131
+ }
132
+ const binding = this.sessionHistoryService.getBindingOrThrow(sessionId);
133
+ const acceptedLookupStartedAtMs = performance.now();
134
+ const acceptedMessage = shouldAwaitAcceptedUserMessage(input.provider)
135
+ ? await this.findAcceptedUserMessage(sessionId, this.sessionMessageAttachmentService.buildAcceptedContentCandidates(input.content, providerPrompt), requestStartedAt)
136
+ : null;
137
+ this.logSendDebugStep(debugTrace, "accepted_user_lookup", acceptedLookupStartedAtMs, {
138
+ awaited: shouldAwaitAcceptedUserMessage(input.provider),
139
+ matched: Boolean(acceptedMessage)
140
+ });
141
+ if (!shouldAwaitStartBindingBeforeAcceptedUserLookup(input.provider)) {
142
+ void startBindingTask;
143
+ }
144
+ const acceptedAt = acceptedMessage?.timestamp ?? nowIso();
145
+ const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
146
+ const session = this.sessionHistoryService.getSession(sessionId, input.userId);
147
+ this.markSendDebugResponseReady(debugTrace, {
148
+ returnedAcceptedMessage: Boolean(acceptedMessage),
149
+ returnedSyntheticUser: !acceptedMessage,
150
+ providerSessionId: binding.providerSessionId
151
+ });
152
+ return {
153
+ sessionId: session.sessionId,
154
+ provider: input.provider,
155
+ providerSessionId: binding.providerSessionId,
156
+ acceptedAt,
157
+ clientRequestId: input.clientRequestId,
158
+ message: (acceptedMessage
159
+ ? {
160
+ ...acceptedMessage,
161
+ attachments: boundAttachments
162
+ }
163
+ : null) ??
164
+ createSyntheticUserMessage(input.provider, binding.providerSessionId, input.content, acceptedAt, 1, boundAttachments.length > 0
165
+ ? boundAttachments
166
+ : persistedAttachments.messageAttachments),
167
+ session
168
+ };
169
+ }
170
+ catch (error) {
171
+ this.failPendingSendDebugTrace(debugTrace, error);
172
+ throw error;
173
+ }
118
174
  }
119
175
  async sendLiveMessage(input) {
120
176
  return this.sendLiveMessageDirect(input);
@@ -169,7 +225,8 @@ export class SessionLiveRuntimeService {
169
225
  field: "queueItemId"
170
226
  });
171
227
  }
172
- const runtimeSnapshot = this.providerRuntimeService.getSnapshot(sessionId);
228
+ const runtimeSessionId = this.resolveRuntimeSessionId(sessionId);
229
+ const runtimeSnapshot = this.providerRuntimeService.getSnapshot(runtimeSessionId);
173
230
  if (!runtimeSnapshot || !isActiveRuntimeState(runtimeSnapshot.runningState)) {
174
231
  throw new AppError({
175
232
  statusCode: 409,
@@ -225,7 +282,7 @@ export class SessionLiveRuntimeService {
225
282
  };
226
283
  }
227
284
  catch (error) {
228
- if (isQueueDispatchDeferredError(error)) {
285
+ if (isQueueDispatchRetryableError(error)) {
229
286
  this.sessionSendQueueRepository.markQueued(queueItem.id, nowIso());
230
287
  this.scheduleQueueRetry(sessionId);
231
288
  }
@@ -368,8 +425,9 @@ export class SessionLiveRuntimeService {
368
425
  };
369
426
  }
370
427
  async getSessionRuntime(sessionId, userId) {
371
- const runtimeSnapshot = this.providerRuntimeService.getSnapshot(sessionId);
372
- const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(sessionId) ?? null;
428
+ const runtimeSessionId = this.resolveRuntimeSessionId(sessionId);
429
+ const runtimeSnapshot = this.providerRuntimeService.getSnapshot(runtimeSessionId);
430
+ const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(runtimeSessionId) ?? null;
373
431
  const session = runtimeSnapshot || externalRuntimeSnapshot
374
432
  ? this.sessionHistoryService.getSession(sessionId, userId)
375
433
  : await this.sessionHistoryService.refreshRuntimeFallbackSession(sessionId, userId);
@@ -377,9 +435,9 @@ export class SessionLiveRuntimeService {
377
435
  const capabilities = await this.sessionHistoryService.getSessionCapabilities(sessionId);
378
436
  const contextUsage = await this.sessionHistoryService.getSessionContextUsage(sessionId).catch(() => null);
379
437
  const resolution = runtimeSnapshot
380
- ? this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(sessionId, runtimeSnapshot))
438
+ ? this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(runtimeSessionId, runtimeSnapshot))
381
439
  : externalRuntimeSnapshot
382
- ? this.sessionActivityAuthorityService.observe(createExternalRuntimeActivityObservation(sessionId, externalRuntimeSnapshot))
440
+ ? this.sessionActivityAuthorityService.observe(createExternalRuntimeActivityObservation(runtimeSessionId, externalRuntimeSnapshot))
383
441
  : this.sessionActivityAuthorityService.resolvePersistedSession(session);
384
442
  if (runtimeSnapshot) {
385
443
  return {
@@ -451,7 +509,8 @@ export class SessionLiveRuntimeService {
451
509
  }
452
510
  async interruptSession(sessionId, userId) {
453
511
  this.sessionHistoryService.getSession(sessionId, userId);
454
- const runtime = this.providerRuntimeService.getSnapshot(sessionId);
512
+ const runtimeSessionId = this.resolveRuntimeSessionId(sessionId);
513
+ const runtime = this.providerRuntimeService.getSnapshot(runtimeSessionId);
455
514
  if (!runtime || (runtime.runningState !== "running" && runtime.runningState !== "starting")) {
456
515
  throw new AppError({
457
516
  statusCode: 409,
@@ -460,7 +519,7 @@ export class SessionLiveRuntimeService {
460
519
  field: "sessionId"
461
520
  });
462
521
  }
463
- const interrupted = await this.providerRuntimeService.interrupt(sessionId).catch((error) => {
522
+ const interrupted = await this.providerRuntimeService.interrupt(runtimeSessionId).catch((error) => {
464
523
  if (error instanceof Error && error.message === "INTERRUPT_NOT_SUPPORTED") {
465
524
  throw new AppError({
466
525
  statusCode: 400,
@@ -477,6 +536,14 @@ export class SessionLiveRuntimeService {
477
536
  detail: interrupted.detail ?? "interrupt requested"
478
537
  };
479
538
  }
539
+ registerTerminalStateListener(listener) {
540
+ this.terminalStateListeners.add(listener);
541
+ return {
542
+ close: () => {
543
+ this.terminalStateListeners.delete(listener);
544
+ }
545
+ };
546
+ }
480
547
  async listPermissionRequests(sessionId, userId) {
481
548
  return this.sessionPermissionRequestService.listSessionPermissionRequests(sessionId, userId);
482
549
  }
@@ -484,9 +551,10 @@ export class SessionLiveRuntimeService {
484
551
  return this.sessionPermissionRequestService.replyToSessionPermissionRequest(sessionId, userId, requestId, input);
485
552
  }
486
553
  subscribeRuntime(sessionId, onEnvelope) {
487
- const runtimeSnapshot = this.providerRuntimeService.getSnapshot(sessionId);
488
- const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(sessionId) ?? null;
489
- const initialActivityEnvelope = this.buildSessionActivityEnvelope(sessionId);
554
+ const runtimeSessionId = this.resolveRuntimeSessionId(sessionId);
555
+ const runtimeSnapshot = this.providerRuntimeService.getSnapshot(runtimeSessionId);
556
+ const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(runtimeSessionId) ?? null;
557
+ const initialActivityEnvelope = this.buildSessionActivityEnvelope(sessionId, runtimeSessionId);
490
558
  if (runtimeSnapshot) {
491
559
  void onEnvelope({
492
560
  type: "session.runtime_status",
@@ -508,16 +576,21 @@ export class SessionLiveRuntimeService {
508
576
  if (initialActivityEnvelope) {
509
577
  void onEnvelope(initialActivityEnvelope);
510
578
  }
511
- const runtimeSubscription = this.providerRuntimeService.subscribe(sessionId, async (event) => {
512
- const envelope = this.mapRuntimeEventToEnvelope(sessionId, event);
579
+ const runtimeSubscription = this.providerRuntimeService.subscribe(runtimeSessionId, async (event) => {
580
+ const envelope = this.mapRuntimeEventToEnvelope(sessionId, event, runtimeSessionId);
513
581
  if (!envelope) {
514
582
  return;
515
583
  }
516
584
  await onEnvelope(envelope);
517
585
  });
518
- const externalSubscription = this.subscribeExternalRuntime(sessionId, onEnvelope);
519
- const activitySubscription = this.sessionActivityAuthorityService.subscribe(sessionId, async () => {
520
- const envelope = this.buildSessionActivityEnvelope(sessionId);
586
+ const externalSubscription = this.subscribeExternalRuntime(runtimeSessionId, async (envelope) => {
587
+ await onEnvelope({
588
+ ...envelope,
589
+ sessionId
590
+ });
591
+ });
592
+ const activitySubscription = this.sessionActivityAuthorityService.subscribe(runtimeSessionId, async () => {
593
+ const envelope = this.buildSessionActivityEnvelope(sessionId, runtimeSessionId);
521
594
  if (!envelope) {
522
595
  return;
523
596
  }
@@ -578,31 +651,67 @@ export class SessionLiveRuntimeService {
578
651
  await listener(envelope);
579
652
  }));
580
653
  }
581
- buildSessionActivityEnvelope(sessionId) {
582
- const runtimeSnapshot = this.providerRuntimeService.getSnapshot(sessionId);
654
+ buildSessionActivityEnvelope(sessionId, runtimeSessionId = sessionId) {
655
+ const runtimeSnapshot = this.providerRuntimeService.getSnapshot(runtimeSessionId);
583
656
  if (runtimeSnapshot) {
584
- const resolution = this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(sessionId, runtimeSnapshot));
585
- return this.mapResolutionToActivityEnvelope(resolution, {
586
- hasActiveRun: true,
587
- canInterrupt: runtimeSnapshot.supportsInterrupt
588
- });
657
+ const resolution = this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(runtimeSessionId, runtimeSnapshot));
658
+ return {
659
+ ...this.mapResolutionToActivityEnvelope(resolution, {
660
+ hasActiveRun: true,
661
+ canInterrupt: runtimeSnapshot.supportsInterrupt
662
+ }),
663
+ sessionId
664
+ };
589
665
  }
590
- const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(sessionId) ?? null;
666
+ const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(runtimeSessionId) ?? null;
591
667
  if (externalRuntimeSnapshot) {
592
- const resolution = this.sessionActivityAuthorityService.observe(createExternalRuntimeActivityObservation(sessionId, externalRuntimeSnapshot));
593
- return this.mapResolutionToActivityEnvelope(resolution, {
594
- hasActiveRun: true,
595
- canInterrupt: false
596
- });
668
+ const resolution = this.sessionActivityAuthorityService.observe(createExternalRuntimeActivityObservation(runtimeSessionId, externalRuntimeSnapshot));
669
+ return {
670
+ ...this.mapResolutionToActivityEnvelope(resolution, {
671
+ hasActiveRun: true,
672
+ canInterrupt: false
673
+ }),
674
+ sessionId
675
+ };
597
676
  }
598
- const resolution = this.sessionActivityAuthorityService.getResolution(sessionId);
677
+ const resolution = this.sessionActivityAuthorityService.getResolution(runtimeSessionId);
599
678
  if (!resolution) {
600
679
  return null;
601
680
  }
602
- return this.mapResolutionToActivityEnvelope(resolution, {
603
- hasActiveRun: resolution.runningState === "stale" || resolution.runningState === "unknown",
604
- canInterrupt: false
605
- });
681
+ return {
682
+ ...this.mapResolutionToActivityEnvelope(resolution, {
683
+ hasActiveRun: resolution.runningState === "stale" || resolution.runningState === "unknown",
684
+ canInterrupt: false
685
+ }),
686
+ sessionId
687
+ };
688
+ }
689
+ resolveRuntimeSessionId(sessionId) {
690
+ if (this.providerRuntimeService.getSnapshot(sessionId)
691
+ || this.externalRuntimeSnapshots.has(sessionId)) {
692
+ return sessionId;
693
+ }
694
+ const listSnapshots = "listSnapshots" in this.providerRuntimeService
695
+ && typeof this.providerRuntimeService.listSnapshots === "function"
696
+ ? this.providerRuntimeService.listSnapshots.bind(this.providerRuntimeService)
697
+ : null;
698
+ if (!listSnapshots) {
699
+ return sessionId;
700
+ }
701
+ const linkedSnapshot = listSnapshots()
702
+ .find((snapshot) => this.isLinkedGeminiRuntimeSession(snapshot.sessionId, sessionId));
703
+ return linkedSnapshot?.sessionId ?? sessionId;
704
+ }
705
+ isLinkedGeminiRuntimeSession(candidateSessionId, targetSessionId) {
706
+ if (candidateSessionId === targetSessionId) {
707
+ return true;
708
+ }
709
+ const binding = this.sessionBindingRepository.findBySessionId(candidateSessionId);
710
+ if (!binding || binding.provider !== "gemini") {
711
+ return false;
712
+ }
713
+ return isGeminiPendingRuntimeAliasBinding(binding.providerSessionId, targetSessionId)
714
+ || isGeminiPendingRuntimeAliasBinding(binding.rawStoreRef, targetSessionId);
606
715
  }
607
716
  mapResolutionToActivityEnvelope(resolution, options) {
608
717
  return {
@@ -660,6 +769,7 @@ export class SessionLiveRuntimeService {
660
769
  workspaceId: input.workspaceId,
661
770
  provider: "claude-code",
662
771
  parentSessionId: null,
772
+ sessionKind: "default",
663
773
  isSubagent: false,
664
774
  subagentLabel: null,
665
775
  title: `Claude 会话 ${input.providerSessionId.slice(0, 8)}`,
@@ -764,6 +874,13 @@ export class SessionLiveRuntimeService {
764
874
  };
765
875
  await this.emitExternalRuntimeEnvelope(envelope);
766
876
  if (isTerminalSessionRunningState(input.runningState)) {
877
+ await this.emitTerminalStateEvent({
878
+ sessionId: input.sessionId,
879
+ status: input.runningState,
880
+ timestamp: input.timestamp,
881
+ detail: input.detail,
882
+ source: "external_runtime"
883
+ });
767
884
  void this.dispatchNextQueuedMessage(input.sessionId);
768
885
  }
769
886
  }
@@ -794,80 +911,118 @@ export class SessionLiveRuntimeService {
794
911
  async sendLiveMessageDirect(input, persistedAttachments) {
795
912
  const requestStartedAt = nowIso();
796
913
  const session = this.sessionHistoryService.getSession(input.sessionId, input.userId);
797
- const capabilities = await this.sessionHistoryService.getSessionCapabilities(input.sessionId);
798
- const workspace = this.workspaceService.getWorkspaceOrThrow(session.workspaceId);
799
- const runtimeMode = shouldStartNativeSessionOnFirstMessage(session);
800
- const resolvedAttachments = persistedAttachments
801
- ?? this.persistMessageAttachments(input.sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
802
- const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(session.provider, input.content, resolvedAttachments.runtimeAttachments);
803
- this.ensureCapability(capabilities.canSendMessage, "sessionId", "provider 不支持实时对话");
804
- const runtimeRequest = {
914
+ const debugTrace = this.beginPendingSendDebugTrace({
915
+ mode: "send_live",
805
916
  sessionId: input.sessionId,
806
917
  workspaceId: session.workspaceId,
807
- workspacePath: workspace.path,
808
918
  provider: session.provider,
809
- providerSessionId: runtimeMode === "start" ? null : session.providerSessionId,
810
- rawStoreRef: runtimeMode === "start" ? null : session.rawStoreRef,
811
- sequenceBase: runtimeMode === "start"
919
+ clientRequestId: input.clientRequestId
920
+ });
921
+ try {
922
+ const capabilities = await this.sessionHistoryService.getSessionCapabilities(input.sessionId);
923
+ const workspace = this.workspaceService.getWorkspaceOrThrow(session.workspaceId);
924
+ const runtimeMode = shouldStartNativeSessionOnFirstMessage(session);
925
+ const syntheticForkRawStoreRef = runtimeMode === "start" && shouldResumeCodexSyntheticForkSession(session)
926
+ ? session.rawStoreRef
927
+ : null;
928
+ const nextUserSequence = runtimeMode === "start"
812
929
  ? 1
813
- : Math.max(session.messageCount + 1, 1),
814
- options: {
815
- content: input.content,
816
- clientRequestId: input.clientRequestId,
817
- model: input.runtimeOptions?.model ?? null,
818
- reasoningLevel: input.runtimeOptions?.reasoningLevel ?? null,
819
- permissionMode: input.runtimeOptions?.permissionMode ?? null,
820
- providerPrompt,
821
- attachments: resolvedAttachments.runtimeAttachments
930
+ : await this.resolveNextUserSequence(input.sessionId, session.messageCount);
931
+ const resolvedAttachments = persistedAttachments
932
+ ?? this.persistMessageAttachments(input.sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
933
+ const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(session.provider, input.content, resolvedAttachments.runtimeAttachments);
934
+ this.ensureCapability(capabilities.canSendMessage, "sessionId", "provider 不支持实时对话");
935
+ const runtimeRequest = {
936
+ sessionId: input.sessionId,
937
+ workspaceId: session.workspaceId,
938
+ workspacePath: workspace.path,
939
+ provider: session.provider,
940
+ providerSessionId: runtimeMode === "start" ? null : session.providerSessionId,
941
+ rawStoreRef: runtimeMode === "start" ? syntheticForkRawStoreRef : session.rawStoreRef,
942
+ sequenceBase: nextUserSequence,
943
+ options: {
944
+ content: input.content,
945
+ clientRequestId: input.clientRequestId,
946
+ model: input.runtimeOptions?.model ?? null,
947
+ reasoningLevel: input.runtimeOptions?.reasoningLevel ?? null,
948
+ permissionMode: input.runtimeOptions?.permissionMode ?? null,
949
+ providerPrompt,
950
+ attachments: resolvedAttachments.runtimeAttachments
951
+ }
952
+ };
953
+ const runtimeSessionId = this.resolveRuntimeSessionId(input.sessionId);
954
+ const activeRun = this.providerRuntimeService.getSnapshot(runtimeSessionId);
955
+ const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(runtimeSessionId);
956
+ if (activeRun &&
957
+ activeRun.provider === "claude-code" &&
958
+ isActiveRuntimeState(activeRun.runningState)) {
959
+ this.clearExternalRuntimeSnapshot(runtimeSessionId);
822
960
  }
823
- };
824
- const activeRun = this.providerRuntimeService.getSnapshot(input.sessionId);
825
- const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(input.sessionId);
826
- if (activeRun &&
827
- activeRun.provider === "claude-code" &&
828
- isActiveRuntimeState(activeRun.runningState)) {
829
- this.clearExternalRuntimeSnapshot(input.sessionId);
830
- }
831
- if (!activeRun &&
832
- session.provider === "claude-code" &&
833
- externalRuntimeSnapshot &&
834
- isActiveRuntimeState(externalRuntimeSnapshot.runningState)) {
835
- throw new AppError({
836
- statusCode: 409,
837
- errorCode: "SESSION_EXTERNAL_RUN_ACTIVE",
838
- detail: "当前 Claude 外部会话仍在运行,不能直接追加;请加入队列或等待当前轮结束",
839
- field: "sessionId"
961
+ if (!activeRun &&
962
+ session.provider === "claude-code" &&
963
+ externalRuntimeSnapshot &&
964
+ isActiveRuntimeState(externalRuntimeSnapshot.runningState)) {
965
+ throw new AppError({
966
+ statusCode: 409,
967
+ errorCode: "SESSION_EXTERNAL_RUN_ACTIVE",
968
+ detail: "当前 Claude 外部会话仍在运行,不能直接追加;请加入队列或等待当前轮结束",
969
+ field: "sessionId"
970
+ });
971
+ }
972
+ if (activeRun && isActiveRuntimeState(activeRun.runningState)) {
973
+ const submitStartedAtMs = performance.now();
974
+ await this.providerRuntimeService.submitToActiveRun(runtimeSessionId, runtimeRequest.options)
975
+ .catch((error) => {
976
+ throw mapSessionProviderError(error);
977
+ });
978
+ this.logSendDebugStep(debugTrace, "submit_to_active_run", submitStartedAtMs, {
979
+ runtimeMode,
980
+ activeRunState: activeRun.runningState
981
+ });
982
+ }
983
+ else {
984
+ const startRuntimeStartedAtMs = performance.now();
985
+ await this.startRuntimeRun(runtimeRequest, input.userId, runtimeMode);
986
+ this.logSendDebugStep(debugTrace, "start_runtime_run", startRuntimeStartedAtMs, {
987
+ runtimeMode
988
+ });
989
+ }
990
+ const binding = this.sessionHistoryService.getBindingOrThrow(input.sessionId);
991
+ const acceptedLookupStartedAtMs = performance.now();
992
+ const acceptedMessage = await this.findAcceptedUserMessage(input.sessionId, this.sessionMessageAttachmentService.buildAcceptedContentCandidates(input.content, providerPrompt), requestStartedAt);
993
+ this.logSendDebugStep(debugTrace, "accepted_user_lookup", acceptedLookupStartedAtMs, {
994
+ matched: Boolean(acceptedMessage)
840
995
  });
841
- }
842
- if (activeRun && isActiveRuntimeState(activeRun.runningState)) {
843
- await this.providerRuntimeService.submitToActiveRun(input.sessionId, runtimeRequest.options)
844
- .catch((error) => {
845
- throw mapSessionProviderError(error);
996
+ const acceptedAt = acceptedMessage?.timestamp ?? nowIso();
997
+ const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(input.sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
998
+ this.refreshSyntheticSessionTitle(session, input.content, input.userId);
999
+ this.markSendDebugResponseReady(debugTrace, {
1000
+ runtimeMode,
1001
+ returnedAcceptedMessage: Boolean(acceptedMessage),
1002
+ returnedSyntheticUser: !acceptedMessage,
1003
+ providerSessionId: binding.providerSessionId
846
1004
  });
1005
+ return {
1006
+ sessionId: input.sessionId,
1007
+ provider: session.provider,
1008
+ providerSessionId: binding.providerSessionId,
1009
+ acceptedAt,
1010
+ clientRequestId: input.clientRequestId,
1011
+ message: (acceptedMessage
1012
+ ? {
1013
+ ...acceptedMessage,
1014
+ attachments: boundAttachments
1015
+ }
1016
+ : null) ??
1017
+ createSyntheticUserMessage(session.provider, binding.providerSessionId, input.content, acceptedAt, nextUserSequence, boundAttachments.length > 0
1018
+ ? boundAttachments
1019
+ : resolvedAttachments.messageAttachments)
1020
+ };
847
1021
  }
848
- else {
849
- await this.startRuntimeRun(runtimeRequest, input.userId, runtimeMode);
1022
+ catch (error) {
1023
+ this.failPendingSendDebugTrace(debugTrace, error);
1024
+ throw error;
850
1025
  }
851
- const binding = this.sessionHistoryService.getBindingOrThrow(input.sessionId);
852
- const acceptedMessage = await this.findAcceptedUserMessage(input.sessionId, this.sessionMessageAttachmentService.buildAcceptedContentCandidates(input.content, providerPrompt), requestStartedAt);
853
- const acceptedAt = acceptedMessage?.timestamp ?? nowIso();
854
- const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(input.sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
855
- return {
856
- sessionId: input.sessionId,
857
- provider: session.provider,
858
- providerSessionId: binding.providerSessionId,
859
- acceptedAt,
860
- clientRequestId: input.clientRequestId,
861
- message: (acceptedMessage
862
- ? {
863
- ...acceptedMessage,
864
- attachments: boundAttachments
865
- }
866
- : null) ??
867
- createSyntheticUserMessage(session.provider, binding.providerSessionId, input.content, acceptedAt, Math.max(session.messageCount + 1, 1), boundAttachments.length > 0
868
- ? boundAttachments
869
- : resolvedAttachments.messageAttachments)
870
- };
871
1026
  }
872
1027
  async dispatchNextQueuedMessage(sessionId) {
873
1028
  if (this.queueDispatchSessions.has(sessionId)) {
@@ -919,7 +1074,7 @@ export class SessionLiveRuntimeService {
919
1074
  this.sessionSendQueueRepository.delete(nextQueueItem.id);
920
1075
  }
921
1076
  catch (error) {
922
- if (isQueueDispatchDeferredError(error)) {
1077
+ if (isQueueDispatchRetryableError(error)) {
923
1078
  this.sessionSendQueueRepository.markQueued(nextQueueItem.id, nowIso());
924
1079
  this.scheduleQueueRetry(sessionId);
925
1080
  return;
@@ -1010,7 +1165,10 @@ export class SessionLiveRuntimeService {
1010
1165
  sessionId: input.sessionId,
1011
1166
  workspaceId: input.workspaceId,
1012
1167
  provider: input.provider,
1013
- parentSessionId: null,
1168
+ parentSessionId: input.parentSessionId,
1169
+ sessionKind: input.sessionKind,
1170
+ annotationSourceMessageId: input.annotationSourceMessageId,
1171
+ annotationSourceText: input.annotationSourceText,
1014
1172
  isSubagent: false,
1015
1173
  subagentLabel: null,
1016
1174
  title: buildSessionTitle(input.initialContent),
@@ -1053,6 +1211,7 @@ export class SessionLiveRuntimeService {
1053
1211
  };
1054
1212
  }
1055
1213
  async persistRuntimeEvent(sessionId, workspaceId, userId, event) {
1214
+ this.observePendingSendDebugTraceEvent(sessionId, event);
1056
1215
  this.sessionHistoryService.persistSessionBinding(sessionId, workspaceId, {
1057
1216
  provider: event.provider,
1058
1217
  providerSessionId: event.providerSessionId,
@@ -1152,9 +1311,151 @@ export class SessionLiveRuntimeService {
1152
1311
  });
1153
1312
  await this.maybeEmitRuntimeHistoryFallback(sessionId, event);
1154
1313
  if (isTerminalRuntimeEventStatus(event.status)) {
1314
+ if (!isTerminalSessionRunningState(currentRunningState)) {
1315
+ await this.emitTerminalStateEvent({
1316
+ sessionId,
1317
+ status: event.status,
1318
+ timestamp: event.timestamp,
1319
+ detail: event.detail ?? null,
1320
+ source: "runtime"
1321
+ });
1322
+ }
1155
1323
  void this.dispatchNextQueuedMessage(sessionId);
1156
1324
  }
1157
1325
  }
1326
+ async emitTerminalStateEvent(event) {
1327
+ for (const listener of this.terminalStateListeners) {
1328
+ await listener(event);
1329
+ }
1330
+ }
1331
+ beginPendingSendDebugTrace(input) {
1332
+ if (!isPerfDebugEnabled()) {
1333
+ return null;
1334
+ }
1335
+ const trace = {
1336
+ ...input,
1337
+ startedAtMs: performance.now(),
1338
+ responseReadyAtMs: null,
1339
+ firstRuntimeEventAtMs: null
1340
+ };
1341
+ const queue = this.pendingSendDebugTracesBySessionId.get(input.sessionId) ?? [];
1342
+ queue.push(trace);
1343
+ this.pendingSendDebugTracesBySessionId.set(input.sessionId, queue);
1344
+ logPerformance(`session_send.${trace.mode}.begin`, 0, this.buildSendDebugDetail(trace), {
1345
+ force: true,
1346
+ thresholdMs: 0
1347
+ });
1348
+ return trace;
1349
+ }
1350
+ logSendDebugStep(trace, step, startedAtMs, detail = {}) {
1351
+ if (!trace) {
1352
+ return;
1353
+ }
1354
+ logPerformance(`session_send.${trace.mode}.${step}`, performance.now() - startedAtMs, {
1355
+ ...this.buildSendDebugDetail(trace),
1356
+ ...detail
1357
+ }, {
1358
+ force: true,
1359
+ thresholdMs: 0
1360
+ });
1361
+ }
1362
+ markSendDebugResponseReady(trace, detail = {}) {
1363
+ if (!trace || trace.responseReadyAtMs !== null) {
1364
+ return;
1365
+ }
1366
+ trace.responseReadyAtMs = performance.now();
1367
+ logPerformance(`session_send.${trace.mode}.response_ready`, trace.responseReadyAtMs - trace.startedAtMs, {
1368
+ ...this.buildSendDebugDetail(trace),
1369
+ ...detail
1370
+ }, {
1371
+ force: true,
1372
+ thresholdMs: 0
1373
+ });
1374
+ }
1375
+ failPendingSendDebugTrace(trace, error) {
1376
+ if (!trace) {
1377
+ return;
1378
+ }
1379
+ logPerformance(`session_send.${trace.mode}.error`, performance.now() - trace.startedAtMs, {
1380
+ ...this.buildSendDebugDetail(trace),
1381
+ error: error instanceof Error ? error.message : String(error)
1382
+ }, {
1383
+ force: true,
1384
+ thresholdMs: 0
1385
+ });
1386
+ this.removePendingSendDebugTrace(trace);
1387
+ }
1388
+ observePendingSendDebugTraceEvent(sessionId, event) {
1389
+ const trace = this.peekPendingSendDebugTrace(sessionId);
1390
+ if (!trace) {
1391
+ return;
1392
+ }
1393
+ const nowMs = performance.now();
1394
+ if (trace.firstRuntimeEventAtMs === null) {
1395
+ trace.firstRuntimeEventAtMs = nowMs;
1396
+ logPerformance(`session_send.${trace.mode}.first_runtime_event`, trace.firstRuntimeEventAtMs - trace.startedAtMs, {
1397
+ ...this.buildSendDebugDetail(trace),
1398
+ eventType: event.type,
1399
+ status: event.status,
1400
+ role: event.type === "message" ? event.message.role : null,
1401
+ kind: event.type === "message" ? event.message.kind : null,
1402
+ responseReady: trace.responseReadyAtMs !== null
1403
+ }, {
1404
+ force: true,
1405
+ thresholdMs: 0
1406
+ });
1407
+ }
1408
+ if (event.type === "message" && event.message.role === "assistant") {
1409
+ logPerformance(`session_send.${trace.mode}.first_assistant_message`, nowMs - trace.startedAtMs, {
1410
+ ...this.buildSendDebugDetail(trace),
1411
+ kind: event.message.kind,
1412
+ contentLength: event.message.content.length,
1413
+ responseToAssistantMs: trace.responseReadyAtMs === null ? null : nowMs - trace.responseReadyAtMs
1414
+ }, {
1415
+ force: true,
1416
+ thresholdMs: 0
1417
+ });
1418
+ this.removePendingSendDebugTrace(trace);
1419
+ return;
1420
+ }
1421
+ if (event.type === "error" ||
1422
+ (event.type !== "message" && isTerminalRuntimeEventStatus(event.status))) {
1423
+ logPerformance(`session_send.${trace.mode}.completed_without_assistant`, nowMs - trace.startedAtMs, {
1424
+ ...this.buildSendDebugDetail(trace),
1425
+ eventType: event.type,
1426
+ status: event.status,
1427
+ detail: event.detail
1428
+ }, {
1429
+ force: true,
1430
+ thresholdMs: 0
1431
+ });
1432
+ this.removePendingSendDebugTrace(trace);
1433
+ }
1434
+ }
1435
+ peekPendingSendDebugTrace(sessionId) {
1436
+ const queue = this.pendingSendDebugTracesBySessionId.get(sessionId);
1437
+ return queue && queue.length > 0 ? queue[0] : null;
1438
+ }
1439
+ removePendingSendDebugTrace(trace) {
1440
+ const queue = this.pendingSendDebugTracesBySessionId.get(trace.sessionId);
1441
+ if (!queue || queue.length === 0) {
1442
+ return;
1443
+ }
1444
+ const nextQueue = queue.filter((item) => item !== trace);
1445
+ if (nextQueue.length === 0) {
1446
+ this.pendingSendDebugTracesBySessionId.delete(trace.sessionId);
1447
+ return;
1448
+ }
1449
+ this.pendingSendDebugTracesBySessionId.set(trace.sessionId, nextQueue);
1450
+ }
1451
+ buildSendDebugDetail(trace) {
1452
+ return {
1453
+ sessionId: trace.sessionId,
1454
+ workspaceId: trace.workspaceId,
1455
+ provider: trace.provider,
1456
+ clientRequestId: trace.clientRequestId
1457
+ };
1458
+ }
1158
1459
  async findAcceptedUserMessage(sessionId, content, minTimestamp) {
1159
1460
  try {
1160
1461
  return await withTimeout(this.sessionHistoryService.findLatestUserMessage(sessionId, content, 12, minTimestamp), 1200);
@@ -1163,6 +1464,36 @@ export class SessionLiveRuntimeService {
1163
1464
  return null;
1164
1465
  }
1165
1466
  }
1467
+ async resolveNextUserSequence(sessionId, messageCount) {
1468
+ let maxSequence = Math.max(messageCount, 0);
1469
+ const envelope = await Promise.resolve(this.sessionHistoryService.readRecentHistoryEnvelope(sessionId, 10)).catch(() => {
1470
+ return null;
1471
+ });
1472
+ for (const message of envelope?.messages ?? []) {
1473
+ if (Number.isFinite(message.sequence) && message.sequence > maxSequence) {
1474
+ maxSequence = message.sequence;
1475
+ }
1476
+ }
1477
+ return Math.max(maxSequence + 1, 1);
1478
+ }
1479
+ async waitForResolvedStartBinding(sessionId, workspaceId, provider, handle) {
1480
+ if (provider !== "gemini" && provider !== "kimi") {
1481
+ return;
1482
+ }
1483
+ const startedAt = Date.now();
1484
+ while (Date.now() - startedAt < RUNTIME_START_BINDING_WAIT_TIMEOUT_MS) {
1485
+ const snapshot = handle.getSnapshot();
1486
+ if (hasResolvedRuntimeBinding(snapshot.providerSessionId, snapshot.rawStoreRef)) {
1487
+ this.sessionHistoryService.persistSessionBinding(sessionId, workspaceId, {
1488
+ provider: snapshot.provider,
1489
+ providerSessionId: snapshot.providerSessionId,
1490
+ rawStoreRef: snapshot.rawStoreRef
1491
+ });
1492
+ return;
1493
+ }
1494
+ await waitForRuntimeBindingPoll();
1495
+ }
1496
+ }
1166
1497
  persistMessageAttachments(sessionId, clientRequestId, attachments) {
1167
1498
  if (!clientRequestId || attachments.length === 0) {
1168
1499
  return {
@@ -1176,12 +1507,30 @@ export class SessionLiveRuntimeService {
1176
1507
  attachments
1177
1508
  });
1178
1509
  }
1179
- mapRuntimeEventToEnvelope(sessionId, event) {
1510
+ refreshSyntheticSessionTitle(session, content, userId) {
1511
+ const currentIndex = this.sessionIndexRepository.findIndexRecordBySessionId(session.sessionId);
1512
+ if (!currentIndex) {
1513
+ return;
1514
+ }
1515
+ const parentTitle = session.parentSessionId
1516
+ ? this.sessionHistoryService.getSession(session.parentSessionId, userId).title
1517
+ : null;
1518
+ const nextTitle = resolveRuntimeSessionTitle(currentIndex.provider, currentIndex.title, content, parentTitle, session.forkMethod, session.forkSourceType);
1519
+ if (!nextTitle || nextTitle === currentIndex.title) {
1520
+ return;
1521
+ }
1522
+ this.sessionIndexRepository.upsert({
1523
+ ...currentIndex,
1524
+ title: nextTitle,
1525
+ updatedAt: nowIso()
1526
+ });
1527
+ }
1528
+ mapRuntimeEventToEnvelope(sessionId, event, originSessionId = sessionId) {
1180
1529
  if (event.type === "message") {
1181
1530
  return {
1182
1531
  type: "session.runtime_message",
1183
1532
  sessionId,
1184
- message: event.message,
1533
+ message: this.sessionHistoryService.resolveMessageOrigin(originSessionId, event.message),
1185
1534
  source: "runtime"
1186
1535
  };
1187
1536
  }
@@ -1445,10 +1794,31 @@ function buildSessionTitle(content) {
1445
1794
  const title = content.trim().replace(/\s+/g, " ");
1446
1795
  return title.slice(0, 48) || "继续对话";
1447
1796
  }
1797
+ function resolveRuntimeSessionTitle(provider, existingTitle, content, parentTitle, forkMethod, forkSourceType) {
1798
+ const normalizedExistingTitle = existingTitle.trim();
1799
+ const normalizedParentTitle = parentTitle?.trim() ?? "";
1800
+ const isForkSession = Boolean(forkMethod || forkSourceType);
1801
+ if (normalizedExistingTitle.length > 0 &&
1802
+ !isSyntheticRuntimeSessionTitle(provider, normalizedExistingTitle) &&
1803
+ (!isForkSession || normalizedExistingTitle !== normalizedParentTitle)) {
1804
+ return null;
1805
+ }
1806
+ return buildSessionTitle(content);
1807
+ }
1808
+ function isSyntheticRuntimeSessionTitle(provider, title) {
1809
+ if (provider !== "codex") {
1810
+ return false;
1811
+ }
1812
+ return (/^rollout-\d{4}-\d{2}-\d{2}t/i.test(title) ||
1813
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(title));
1814
+ }
1448
1815
  function shouldStartNativeSessionOnFirstMessage(session) {
1449
1816
  if (session.provider !== "codex" && session.provider !== "opencode") {
1450
1817
  return "continue";
1451
1818
  }
1819
+ if (session.provider === "codex" && session.providerSessionId.startsWith("rollout-")) {
1820
+ return "start";
1821
+ }
1452
1822
  if (session.messageCount > 0) {
1453
1823
  return "continue";
1454
1824
  }
@@ -1456,6 +1826,12 @@ function shouldStartNativeSessionOnFirstMessage(session) {
1456
1826
  ? "continue"
1457
1827
  : "start";
1458
1828
  }
1829
+ function shouldResumeCodexSyntheticForkSession(session) {
1830
+ return (session.provider === "codex"
1831
+ && session.messageCount > 0
1832
+ && session.providerSessionId.startsWith("rollout-")
1833
+ && Boolean(session.rawStoreRef?.trim()));
1834
+ }
1459
1835
  function isActiveRuntimeState(state) {
1460
1836
  return state === "starting" || state === "running";
1461
1837
  }
@@ -1465,12 +1841,25 @@ function isTerminalRuntimeEventStatus(status) {
1465
1841
  function isPendingSessionRunningState(state) {
1466
1842
  return state === "starting" || state === "running";
1467
1843
  }
1468
- function isQueueDispatchDeferredError(error) {
1844
+ function isQueueDispatchRetryableError(error) {
1469
1845
  if (error instanceof AppError) {
1470
- return error.errorCode === "ACTIVE_RUN_EXISTS" || error.errorCode === "SESSION_NOT_RUNNING";
1846
+ if (error.errorCode === "ACTIVE_RUN_EXISTS"
1847
+ || error.errorCode === "SESSION_NOT_RUNNING"
1848
+ || error.errorCode === "IN_RUN_INPUT_NOT_SUPPORTED"
1849
+ || error.errorCode === "SESSION_EXTERNAL_RUN_ACTIVE"
1850
+ || error.errorCode === "PROVIDER_RUNTIME_UNAVAILABLE"
1851
+ || error.errorCode === "PROVIDER_RUNTIME_TIMEOUT") {
1852
+ return true;
1853
+ }
1854
+ return error.statusCode >= 500;
1471
1855
  }
1472
1856
  if (error instanceof Error) {
1473
- return error.message === "ACTIVE_RUN_EXISTS";
1857
+ return (error.message === "ACTIVE_RUN_EXISTS"
1858
+ || error.message === "SESSION_NOT_RUNNING"
1859
+ || error.message === "IN_RUN_INPUT_NOT_SUPPORTED"
1860
+ || error.message === "SESSION_EXTERNAL_RUN_ACTIVE"
1861
+ || error.message === "SERVER_UNAVAILABLE"
1862
+ || error.message === "SERVER_TIMEOUT");
1474
1863
  }
1475
1864
  return false;
1476
1865
  }
@@ -1493,6 +1882,32 @@ function mapQueueItemRecordToView(record) {
1493
1882
  function isTerminalSessionRunningState(state) {
1494
1883
  return state === "completed" || state === "interrupted" || state === "failed";
1495
1884
  }
1885
+ function hasResolvedRuntimeBinding(providerSessionId, rawStoreRef) {
1886
+ if (!providerSessionId?.trim() || !rawStoreRef?.trim()) {
1887
+ return false;
1888
+ }
1889
+ return !providerSessionId.trim().toLowerCase().startsWith("pending://")
1890
+ && !rawStoreRef.trim().toLowerCase().startsWith("pending://");
1891
+ }
1892
+ function waitForRuntimeBindingPoll() {
1893
+ return new Promise((resolve) => {
1894
+ setTimeout(resolve, START_BINDING_POLL_INTERVAL_MS);
1895
+ });
1896
+ }
1897
+ function isGeminiPendingRuntimeAliasBinding(value, targetSessionId) {
1898
+ return value.trim().toLowerCase() === `pending://gemini/${targetSessionId.trim().toLowerCase()}`;
1899
+ }
1900
+ function shouldAwaitAcceptedUserMessage(provider) {
1901
+ return provider !== "gemini";
1902
+ }
1903
+ function shouldAwaitStartBindingBeforeAcceptedUserLookup(provider) {
1904
+ return provider === "kimi";
1905
+ }
1906
+ function waitForAcceptedUserLookupWindow() {
1907
+ return new Promise((resolve) => {
1908
+ setTimeout(resolve, 1200);
1909
+ });
1910
+ }
1496
1911
  function createProviderRuntimeAdapters(config, options = {}) {
1497
1912
  const claudeHookBridgeConfig = buildClaudeHookBridgeConfig(config);
1498
1913
  const claudeAdapter = process.env.VITEST
@@ -1518,7 +1933,9 @@ function createProviderRuntimeAdapters(config, options = {}) {
1518
1933
  }
1519
1934
  const codexTransportHelper = process.env.VITEST
1520
1935
  ? null
1521
- : new CodexAppServerHelperClient(config.codexCliPath);
1936
+ : new CodexAppServerHelperClient(config.codexCliPath, {
1937
+ homeDir: config.codexHomeDir
1938
+ });
1522
1939
  if (codexTransportHelper) {
1523
1940
  disposables.push(codexTransportHelper);
1524
1941
  }
@@ -1531,6 +1948,14 @@ function createProviderRuntimeAdapters(config, options = {}) {
1531
1948
  transportFactory: codexTransportHelper?.createTransport.bind(codexTransportHelper),
1532
1949
  handleServerRequest: options.handleCodexServerRequest
1533
1950
  }),
1951
+ new GeminiRuntimeAdapter({
1952
+ homeDir: config.geminiHomeDir,
1953
+ commandPath: config.geminiCliPath
1954
+ }),
1955
+ new KimiRuntimeAdapter({
1956
+ homeDir: config.kimiHomeDir,
1957
+ commandPath: config.kimiCliPath
1958
+ }),
1534
1959
  new OpenCodeRuntimeAdapter({
1535
1960
  baseUrl: config.opencodeBaseUrl,
1536
1961
  baseUrlResolver: config.opencodeBaseUrlResolver?.resolve.bind(config.opencodeBaseUrlResolver)