@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.
- package/dist/public/assets/{TerminalPage-4ulgBhv9.js → TerminalPage-BlbQuWi1.js} +1 -1
- package/dist/public/assets/gemini-D4G1NbrE.png +0 -0
- package/dist/public/assets/index-1VIm8lVL.css +1 -0
- package/dist/public/assets/index-Dti93O2S.js +109 -0
- package/dist/public/assets/kimi-BWNNSh7e.png +0 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.d.ts +7 -0
- package/dist/server/config/env.js +150 -1
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/config/opencode-system-probe-helper-process.d.ts +24 -0
- package/dist/server/config/opencode-system-probe-helper-process.js +70 -5
- package/dist/server/config/opencode-system-probe-helper-process.js.map +1 -1
- package/dist/server/modules/butler/butler-action-context-service.d.ts +30 -0
- package/dist/server/modules/butler/butler-action-context-service.js +108 -0
- package/dist/server/modules/butler/butler-action-context-service.js.map +1 -0
- package/dist/server/modules/butler/butler-auth-service.d.ts +17 -0
- package/dist/server/modules/butler/butler-auth-service.js +91 -0
- package/dist/server/modules/butler/butler-auth-service.js.map +1 -0
- package/dist/server/modules/butler/butler-control-action-service.d.ts +65 -0
- package/dist/server/modules/butler/butler-control-action-service.js +296 -0
- package/dist/server/modules/butler/butler-control-action-service.js.map +1 -0
- package/dist/server/modules/butler/butler-control-session-service.d.ts +55 -0
- package/dist/server/modules/butler/butler-control-session-service.js +367 -0
- package/dist/server/modules/butler/butler-control-session-service.js.map +1 -0
- package/dist/server/modules/butler/butler-controller.d.ts +367 -0
- package/dist/server/modules/butler/butler-controller.js +475 -0
- package/dist/server/modules/butler/butler-controller.js.map +1 -0
- package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.d.ts +34 -0
- package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js +77 -0
- package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js.map +1 -0
- package/dist/server/modules/butler/butler-follow-up-scheduler.d.ts +23 -0
- package/dist/server/modules/butler/butler-follow-up-scheduler.js +57 -0
- package/dist/server/modules/butler/butler-follow-up-scheduler.js.map +1 -0
- package/dist/server/modules/butler/butler-follow-up-service.d.ts +86 -0
- package/dist/server/modules/butler/butler-follow-up-service.js +948 -0
- package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -0
- package/dist/server/modules/butler/butler-inbox-service.d.ts +35 -0
- package/dist/server/modules/butler/butler-inbox-service.js +136 -0
- package/dist/server/modules/butler/butler-inbox-service.js.map +1 -0
- package/dist/server/modules/butler/butler-notification-service.d.ts +12 -0
- package/dist/server/modules/butler/butler-notification-service.js +45 -0
- package/dist/server/modules/butler/butler-notification-service.js.map +1 -0
- package/dist/server/modules/butler/butler-profile-service.d.ts +26 -0
- package/dist/server/modules/butler/butler-profile-service.js +529 -0
- package/dist/server/modules/butler/butler-profile-service.js.map +1 -0
- package/dist/server/modules/butler/butler-project-service.d.ts +48 -0
- package/dist/server/modules/butler/butler-project-service.js +253 -0
- package/dist/server/modules/butler/butler-project-service.js.map +1 -0
- package/dist/server/modules/butler/butler-session-service.d.ts +79 -0
- package/dist/server/modules/butler/butler-session-service.js +503 -0
- package/dist/server/modules/butler/butler-session-service.js.map +1 -0
- package/dist/server/modules/butler/butler-session-summary-service.d.ts +55 -0
- package/dist/server/modules/butler/butler-session-summary-service.js +382 -0
- package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -0
- package/dist/server/modules/butler/context-aggregator.d.ts +187 -0
- package/dist/server/modules/butler/context-aggregator.js +807 -0
- package/dist/server/modules/butler/context-aggregator.js.map +1 -0
- package/dist/server/modules/butler/instruction-adapter.d.ts +28 -0
- package/dist/server/modules/butler/instruction-adapter.js +101 -0
- package/dist/server/modules/butler/instruction-adapter.js.map +1 -0
- package/dist/server/modules/butler/patrol-execution-service.d.ts +47 -0
- package/dist/server/modules/butler/patrol-execution-service.js +347 -0
- package/dist/server/modules/butler/patrol-execution-service.js.map +1 -0
- package/dist/server/modules/butler/patrol-plan-service.d.ts +54 -0
- package/dist/server/modules/butler/patrol-plan-service.js +272 -0
- package/dist/server/modules/butler/patrol-plan-service.js.map +1 -0
- package/dist/server/modules/butler/patrol-run-service.d.ts +60 -0
- package/dist/server/modules/butler/patrol-run-service.js +185 -0
- package/dist/server/modules/butler/patrol-run-service.js.map +1 -0
- package/dist/server/modules/butler/patrol-scheduler.d.ts +36 -0
- package/dist/server/modules/butler/patrol-scheduler.js +99 -0
- package/dist/server/modules/butler/patrol-scheduler.js.map +1 -0
- package/dist/server/modules/butler/project-memory-service.d.ts +30 -0
- package/dist/server/modules/butler/project-memory-service.js +103 -0
- package/dist/server/modules/butler/project-memory-service.js.map +1 -0
- package/dist/server/modules/butler/provider-adapter-registry.d.ts +61 -0
- package/dist/server/modules/butler/provider-adapter-registry.js +430 -0
- package/dist/server/modules/butler/provider-adapter-registry.js.map +1 -0
- package/dist/server/modules/butler/session-summary-instruction-adapter.d.ts +28 -0
- package/dist/server/modules/butler/session-summary-instruction-adapter.js +79 -0
- package/dist/server/modules/butler/session-summary-instruction-adapter.js.map +1 -0
- package/dist/server/modules/butler/session-summary-scheduler.d.ts +23 -0
- package/dist/server/modules/butler/session-summary-scheduler.js +57 -0
- package/dist/server/modules/butler/session-summary-scheduler.js.map +1 -0
- package/dist/server/modules/butler/verification-run-service.d.ts +73 -0
- package/dist/server/modules/butler/verification-run-service.js +633 -0
- package/dist/server/modules/butler/verification-run-service.js.map +1 -0
- package/dist/server/modules/file/file-controller.d.ts +12 -1
- package/dist/server/modules/file/file-controller.js +72 -1
- package/dist/server/modules/file/file-controller.js.map +1 -1
- package/dist/server/modules/file/file-preview-link-service.d.ts +22 -0
- package/dist/server/modules/file/file-preview-link-service.js +160 -0
- package/dist/server/modules/file/file-preview-link-service.js.map +1 -0
- package/dist/server/modules/preferences/profile-service.js +8 -2
- package/dist/server/modules/preferences/profile-service.js.map +1 -1
- package/dist/server/modules/sessions/claude-runtime-helper-process.js +1 -1
- package/dist/server/modules/sessions/claude-runtime-helper-process.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +7 -2
- package/dist/server/modules/sessions/codex-app-server-helper-client.js +113 -2
- package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js +106 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +24 -1
- package/dist/server/modules/sessions/session-controller.js +34 -3
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +47 -2
- package/dist/server/modules/sessions/session-history-service.js +881 -56
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +30 -2
- package/dist/server/modules/sessions/session-live-runtime-service.js +584 -159
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-provider-error-mapper.js +94 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
- package/dist/server/modules/workbench/workbench-service.d.ts +7 -1
- package/dist/server/modules/workbench/workbench-service.js +31 -7
- package/dist/server/modules/workbench/workbench-service.js.map +1 -1
- package/dist/server/routes/butler.d.ts +3 -0
- package/dist/server/routes/butler.js +54 -0
- package/dist/server/routes/butler.js.map +1 -0
- package/dist/server/routes/files.js +2 -0
- package/dist/server/routes/files.js.map +1 -1
- package/dist/server/routes/sessions.js +1 -0
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/server/create-server.d.ts +65 -0
- package/dist/server/server/create-server.js +154 -5
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/shared/utils/command-availability.d.ts +1 -0
- package/dist/server/shared/utils/command-availability.js +83 -0
- package/dist/server/shared/utils/command-availability.js.map +1 -0
- package/dist/server/storage/repositories/butler-control-event-repository.d.ts +8 -0
- package/dist/server/storage/repositories/butler-control-event-repository.js +78 -0
- package/dist/server/storage/repositories/butler-control-event-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-control-session-repository.d.ts +11 -0
- package/dist/server/storage/repositories/butler-control-session-repository.js +86 -0
- package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-follow-up-task-repository.d.ts +16 -0
- package/dist/server/storage/repositories/butler-follow-up-task-repository.js +252 -0
- package/dist/server/storage/repositories/butler-follow-up-task-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-inbox-item-repository.d.ts +15 -0
- package/dist/server/storage/repositories/butler-inbox-item-repository.js +111 -0
- package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-notification-archive-repository.d.ts +9 -0
- package/dist/server/storage/repositories/butler-notification-archive-repository.js +48 -0
- package/dist/server/storage/repositories/butler-notification-archive-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-profile-repository.d.ts +9 -0
- package/dist/server/storage/repositories/butler-profile-repository.js +86 -0
- package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-project-repository.d.ts +14 -0
- package/dist/server/storage/repositories/butler-project-repository.js +140 -0
- package/dist/server/storage/repositories/butler-project-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-session-repository.d.ts +11 -0
- package/dist/server/storage/repositories/butler-session-repository.js +106 -0
- package/dist/server/storage/repositories/butler-session-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-session-summary-state-repository.d.ts +8 -0
- package/dist/server/storage/repositories/butler-session-summary-state-repository.js +62 -0
- package/dist/server/storage/repositories/butler-session-summary-state-repository.js.map +1 -0
- package/dist/server/storage/repositories/patrol-plan-repository.d.ts +27 -0
- package/dist/server/storage/repositories/patrol-plan-repository.js +119 -0
- package/dist/server/storage/repositories/patrol-plan-repository.js.map +1 -0
- package/dist/server/storage/repositories/patrol-run-repository.d.ts +28 -0
- package/dist/server/storage/repositories/patrol-run-repository.js +121 -0
- package/dist/server/storage/repositories/patrol-run-repository.js.map +1 -0
- package/dist/server/storage/repositories/project-memory-repository.d.ts +15 -0
- package/dist/server/storage/repositories/project-memory-repository.js +150 -0
- package/dist/server/storage/repositories/project-memory-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-checkpoint-repository.d.ts +9 -0
- package/dist/server/storage/repositories/session-checkpoint-repository.js +72 -0
- package/dist/server/storage/repositories/session-checkpoint-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-fork-repository.d.ts +8 -0
- package/dist/server/storage/repositories/session-fork-repository.js +69 -0
- package/dist/server/storage/repositories/session-fork-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-index-repository.js +40 -2
- package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-message-origin-repository.d.ts +10 -0
- package/dist/server/storage/repositories/session-message-origin-repository.js +93 -0
- package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -0
- package/dist/server/storage/repositories/verification-run-repository.d.ts +29 -0
- package/dist/server/storage/repositories/verification-run-repository.js +125 -0
- package/dist/server/storage/repositories/verification-run-repository.js.map +1 -0
- package/dist/server/storage/sqlite/client.js +146 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +354 -0
- package/dist/server/types/domain.d.ts +286 -2
- package/dist/server/ws/ws-server.d.ts +2 -1
- package/dist/server/ws/ws-server.js +2 -1
- package/dist/server/ws/ws-server.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/index.d.ts +4 -0
- package/node_modules/@codingns/session-sync-core/dist/index.js +4 -0
- package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +18 -0
- package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +659 -0
- package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +11 -0
- package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +72 -0
- package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +8 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +89 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +6 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +228 -7
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +26 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +499 -3
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +41 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +1175 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +29 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +578 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +5 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +271 -4
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +147 -19
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +2 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +43 -5
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +12 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +442 -71
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +21 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +537 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +38 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +911 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/services.d.ts +2 -1
- package/node_modules/@codingns/session-sync-core/dist/services.js +55 -8
- package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +6 -0
- package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +9 -0
- package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +27 -0
- package/node_modules/@codingns/session-sync-core/package.json +8 -0
- package/package.json +1 -1
- package/dist/public/assets/index-C5lu52cQ.css +0 -1
- 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 {
|
|
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.
|
|
65
|
-
const
|
|
66
|
-
|
|
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
|
-
|
|
95
|
-
snapshot
|
|
74
|
+
clientRequestId: input.clientRequestId
|
|
96
75
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
provider
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
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 (
|
|
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
|
|
372
|
-
const
|
|
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(
|
|
438
|
+
? this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(runtimeSessionId, runtimeSnapshot))
|
|
381
439
|
: externalRuntimeSnapshot
|
|
382
|
-
? this.sessionActivityAuthorityService.observe(createExternalRuntimeActivityObservation(
|
|
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
|
|
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(
|
|
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
|
|
488
|
-
const
|
|
489
|
-
const
|
|
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(
|
|
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(
|
|
519
|
-
|
|
520
|
-
|
|
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(
|
|
654
|
+
buildSessionActivityEnvelope(sessionId, runtimeSessionId = sessionId) {
|
|
655
|
+
const runtimeSnapshot = this.providerRuntimeService.getSnapshot(runtimeSessionId);
|
|
583
656
|
if (runtimeSnapshot) {
|
|
584
|
-
const resolution = this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(
|
|
585
|
-
return
|
|
586
|
-
|
|
587
|
-
|
|
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(
|
|
666
|
+
const externalRuntimeSnapshot = this.externalRuntimeSnapshots.get(runtimeSessionId) ?? null;
|
|
591
667
|
if (externalRuntimeSnapshot) {
|
|
592
|
-
const resolution = this.sessionActivityAuthorityService.observe(createExternalRuntimeActivityObservation(
|
|
593
|
-
return
|
|
594
|
-
|
|
595
|
-
|
|
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(
|
|
677
|
+
const resolution = this.sessionActivityAuthorityService.getResolution(runtimeSessionId);
|
|
599
678
|
if (!resolution) {
|
|
600
679
|
return null;
|
|
601
680
|
}
|
|
602
|
-
return
|
|
603
|
-
|
|
604
|
-
|
|
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
|
|
798
|
-
|
|
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
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
-
:
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
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
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
isActiveRuntimeState(
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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
|
-
|
|
849
|
-
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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
|
|
1844
|
+
function isQueueDispatchRetryableError(error) {
|
|
1469
1845
|
if (error instanceof AppError) {
|
|
1470
|
-
|
|
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)
|