@jingyi0605/codingns 0.6.0 → 0.6.5
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/bin/codingns.mjs +240 -2
- package/dist/public/assets/AdaptiveButlerPage-CfsUVZKl.js +3 -0
- package/dist/public/assets/{App-BZvapsi8.js → App-B9HcTkMT.js} +3 -3
- package/dist/public/assets/{BootstrapPage-gHSoa4JN.js → BootstrapPage-BgLdZEKQ.js} +1 -1
- package/dist/public/assets/ConversationPage-Cj8go7L0.js +4 -0
- package/dist/public/assets/{DesktopDetachPreviewPage-4eMRxiBW.js → DesktopDetachPreviewPage-YCBnyC58.js} +1 -1
- package/dist/public/assets/DesktopWindowPage-CrvndE23.js +2 -0
- package/dist/public/assets/FileContextPanel-CdC7GGw5.js +1 -0
- package/dist/public/assets/GitSidebar-z2SBinQh.js +6 -0
- package/dist/public/assets/MobileCreateSessionSheet-Cb2HM-y3.js +1 -0
- package/dist/public/assets/{MobileTopHeaderFrame-Bwv8Ovm_.js → MobileTopHeaderFrame-etF2HKlm.js} +1 -1
- package/dist/public/assets/MobileWorkspaceSwitcherHeader-CuGJ31kb.js +1 -0
- package/dist/public/assets/{RelayConnectEntryPage-D_4YL-YH.js → RelayConnectEntryPage-BB6DbGtP.js} +1 -1
- package/dist/public/assets/{ServerSettingsModal-CMSm3BZU.js → ServerSettingsModal-Bl1sacZg.js} +1 -1
- package/dist/public/assets/SessionIndexPage-NbF9gJnp.js +1 -0
- package/dist/public/assets/SettingsPage-DGsmQpLv.js +1 -0
- package/dist/public/assets/TerminalManagerPanel-BOm8Hi_v.js +1 -0
- package/dist/public/assets/{TerminalPage-DaooFaJ4.js → TerminalPage-B5JNFU6w.js} +19 -19
- package/dist/public/assets/TerminalRuntimeFallbackModal-CM3LRKOJ.js +1 -0
- package/dist/public/assets/{ToolFilesPage-CGxBvYG0.js → ToolFilesPage-GSqKQsj_.js} +1 -1
- package/dist/public/assets/ToolGitPage-BVFWMMQp.js +1 -0
- package/dist/public/assets/ToolProcessesPage-DZ456fYz.js +1 -0
- package/dist/public/assets/ToolsHomePage-DsJp0y8A.js +1 -0
- package/dist/public/assets/WorkbenchLandingPage-DyPei0e-.js +1 -0
- package/dist/public/assets/WorkbenchLayout-DlbgBT3n.js +4 -0
- package/dist/public/assets/WorkbenchModal-LNfB69qx.js +1 -0
- package/dist/public/assets/WorkbenchShellRoute-BsxumYx5.js +1 -0
- package/dist/public/assets/WorkbenchShellRoute-DhQo_0vu.css +1 -0
- package/dist/public/assets/WorkspaceDebugDetailPage-CaXj5zVI.js +1 -0
- package/dist/public/assets/WorkspaceDetailPage-DOexuuaw.js +1 -0
- package/dist/public/assets/WorkspaceHomePage-DkCHNjKD.js +1 -0
- package/dist/public/assets/{client-runtime-manager-BZpL17fc.js → client-runtime-manager-6OoYHXGd.js} +1 -1
- package/dist/public/assets/{file-tree-icon-Db5LXC8h.js → file-tree-icon-9pt1OStn.js} +1 -1
- package/dist/public/assets/index-BwlbvwaA.css +1 -0
- package/dist/public/assets/index-DSw-TkQL.js +42 -0
- package/dist/public/assets/legna-code-6TqgZ4Ls.png +0 -0
- package/dist/public/assets/{login-direct-candidate-resolver-1mxe_Oh8.js → login-direct-candidate-resolver-CLlYtBRq.js} +1 -1
- package/dist/public/assets/model-switch-api-C-l8-E1S.js +1 -0
- package/dist/public/assets/{preferences-service-DWnzl5a0.js → preferences-service-BCcfYP_d.js} +1 -1
- package/dist/public/assets/{relay-entry-C5_Iay0I.js → relay-entry-BmLkMKuq.js} +1 -1
- package/dist/public/assets/session-runtime-machine-DgtvREca.js +21 -0
- package/dist/public/assets/{terminal-runtime-meta-cdtWVfCm.js → terminal-runtime-meta-0h-75uRy.js} +1 -1
- package/dist/public/assets/useRegisteredDebugTemplates-rBVmAqh3.js +1 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.d.ts +2 -0
- package/dist/server/config/env.js +7 -0
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +4 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.js +12 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
- package/dist/server/modules/butler/butler-control-session-service.d.ts +4 -1
- package/dist/server/modules/butler/butler-control-session-service.js +17 -1
- package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-profile-service.d.ts +3 -1
- package/dist/server/modules/butler/butler-profile-service.js +16 -4
- package/dist/server/modules/butler/butler-profile-service.js.map +1 -1
- package/dist/server/modules/model-switch/cc-switch-adapter.d.ts +7 -0
- package/dist/server/modules/model-switch/cc-switch-adapter.js +17 -0
- package/dist/server/modules/model-switch/cc-switch-adapter.js.map +1 -1
- package/dist/server/modules/opencli/opencli-bridge-skill-service.d.ts +12 -0
- package/dist/server/modules/opencli/opencli-bridge-skill-service.js +124 -0
- package/dist/server/modules/opencli/opencli-bridge-skill-service.js.map +1 -0
- package/dist/server/modules/opencli/opencli-catalog-service.d.ts +50 -0
- package/dist/server/modules/opencli/opencli-catalog-service.js +345 -0
- package/dist/server/modules/opencli/opencli-catalog-service.js.map +1 -0
- package/dist/server/modules/opencli/opencli-controller.d.ts +13 -0
- package/dist/server/modules/opencli/opencli-controller.js +30 -0
- package/dist/server/modules/opencli/opencli-controller.js.map +1 -0
- package/dist/server/modules/opencli/opencli-health-service.d.ts +28 -0
- package/dist/server/modules/opencli/opencli-health-service.js +106 -0
- package/dist/server/modules/opencli/opencli-health-service.js.map +1 -0
- package/dist/server/modules/opencli/opencli-install-discovery.d.ts +23 -0
- package/dist/server/modules/opencli/opencli-install-discovery.js +130 -0
- package/dist/server/modules/opencli/opencli-install-discovery.js.map +1 -0
- package/dist/server/modules/opencli/opencli-management-service.d.ts +59 -0
- package/dist/server/modules/opencli/opencli-management-service.js +152 -0
- package/dist/server/modules/opencli/opencli-management-service.js.map +1 -0
- package/dist/server/modules/opencli/opencli-runtime-builder.d.ts +11 -0
- package/dist/server/modules/opencli/opencli-runtime-builder.js +214 -0
- package/dist/server/modules/opencli/opencli-runtime-builder.js.map +1 -0
- package/dist/server/modules/opencli/opencli-runtime-layout.d.ts +3 -0
- package/dist/server/modules/opencli/opencli-runtime-layout.js +11 -0
- package/dist/server/modules/opencli/opencli-runtime-layout.js.map +1 -0
- package/dist/server/modules/opencli/opencli-runtime-profile-service.d.ts +29 -0
- package/dist/server/modules/opencli/opencli-runtime-profile-service.js +104 -0
- package/dist/server/modules/opencli/opencli-runtime-profile-service.js.map +1 -0
- package/dist/server/modules/opencli/opencli-runtime-resolver.d.ts +29 -0
- package/dist/server/modules/opencli/opencli-runtime-resolver.js +110 -0
- package/dist/server/modules/opencli/opencli-runtime-resolver.js.map +1 -0
- package/dist/server/modules/opencli/opencli-session-prompt-service.d.ts +11 -0
- package/dist/server/modules/opencli/opencli-session-prompt-service.js +66 -0
- package/dist/server/modules/opencli/opencli-session-prompt-service.js.map +1 -0
- package/dist/server/modules/parallel-sessions/parallel-session-controller.d.ts +4 -0
- package/dist/server/modules/parallel-sessions/parallel-session-controller.js +7 -0
- package/dist/server/modules/parallel-sessions/parallel-session-controller.js.map +1 -1
- package/dist/server/modules/parallel-sessions/parallel-session-group-service.d.ts +6 -1
- package/dist/server/modules/parallel-sessions/parallel-session-group-service.js +36 -2
- package/dist/server/modules/parallel-sessions/parallel-session-group-service.js.map +1 -1
- package/dist/server/modules/provider/opencode-model-options.d.ts +1 -0
- package/dist/server/modules/provider/opencode-model-options.js +54 -12
- package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
- package/dist/server/modules/provider/provider-catalog-service.d.ts +46 -0
- package/dist/server/modules/provider/provider-catalog-service.js +249 -0
- package/dist/server/modules/provider/provider-catalog-service.js.map +1 -0
- package/dist/server/modules/provider/provider-controller.d.ts +20 -2
- package/dist/server/modules/provider/provider-controller.js +65 -5
- package/dist/server/modules/provider/provider-controller.js.map +1 -1
- package/dist/server/modules/provider/provider-disabled.d.ts +8 -0
- package/dist/server/modules/provider/provider-disabled.js +45 -0
- package/dist/server/modules/provider/provider-disabled.js.map +1 -0
- package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +3 -0
- package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-helper-process.js +4 -4
- package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-runtime.d.ts +1 -1
- package/dist/server/modules/provider/provider-discovery-runtime.js +17 -9
- package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
- package/dist/server/modules/sessions/claude-compatible-provider-registry.d.ts +16 -0
- package/dist/server/modules/sessions/claude-compatible-provider-registry.js +91 -0
- package/dist/server/modules/sessions/claude-compatible-provider-registry.js.map +1 -0
- package/dist/server/modules/sessions/claude-runtime-helper-client.d.ts +1 -0
- package/dist/server/modules/sessions/claude-runtime-helper-client.js +14 -0
- package/dist/server/modules/sessions/claude-runtime-helper-client.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +1 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.js +10 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
- package/dist/server/modules/sessions/provider-session-delete-cli.js +2 -0
- package/dist/server/modules/sessions/provider-session-delete-cli.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +7 -0
- package/dist/server/modules/sessions/session-controller.js +22 -0
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +18 -2
- package/dist/server/modules/sessions/session-history-service.js +330 -29
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-router-service.js +9 -4
- package/dist/server/modules/sessions/session-live-runtime-router-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +22 -5
- package/dist/server/modules/sessions/session-live-runtime-service.js +350 -122
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-message-attachment-service.js +2 -2
- package/dist/server/modules/sessions/session-message-attachment-service.js.map +1 -1
- package/dist/server/modules/sessions/session-permission-request-service.d.ts +5 -2
- package/dist/server/modules/sessions/session-permission-request-service.js +26 -27
- package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
- package/dist/server/modules/sessions/session-provider-config-service.d.ts +82 -0
- package/dist/server/modules/sessions/session-provider-config-service.js +925 -0
- package/dist/server/modules/sessions/session-provider-config-service.js.map +1 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.d.ts +2 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js +42 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
- package/dist/server/modules/skills/skill-manager-service.d.ts +5 -0
- package/dist/server/modules/skills/skill-manager-service.js +26 -0
- package/dist/server/modules/skills/skill-manager-service.js.map +1 -1
- package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +1 -0
- package/dist/server/modules/tasks/task-helper-process-handlers.js +1 -1
- package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -1
- package/dist/server/routes/opencli.d.ts +3 -0
- package/dist/server/routes/opencli.js +7 -0
- package/dist/server/routes/opencli.js.map +1 -0
- package/dist/server/routes/providers.js +4 -2
- package/dist/server/routes/providers.js.map +1 -1
- package/dist/server/server/create-server.d.ts +8 -0
- package/dist/server/server/create-server.js +48 -12
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/storage/repositories/opencli-catalog-entry-repository.d.ts +9 -0
- package/dist/server/storage/repositories/opencli-catalog-entry-repository.js +85 -0
- package/dist/server/storage/repositories/opencli-catalog-entry-repository.js.map +1 -0
- package/dist/server/storage/repositories/opencli-provider-repository.d.ts +8 -0
- package/dist/server/storage/repositories/opencli-provider-repository.js +88 -0
- package/dist/server/storage/repositories/opencli-provider-repository.js.map +1 -0
- package/dist/server/storage/repositories/opencli-runtime-profile-repository.d.ts +11 -0
- package/dist/server/storage/repositories/opencli-runtime-profile-repository.js +127 -0
- package/dist/server/storage/repositories/opencli-runtime-profile-repository.js.map +1 -0
- package/dist/server/storage/repositories/provider-control-repository.d.ts +9 -0
- package/dist/server/storage/repositories/provider-control-repository.js +51 -0
- package/dist/server/storage/repositories/provider-control-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-binding-repository.js +44 -5
- package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-index-repository.js +6 -0
- package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
- package/dist/server/storage/sqlite/client.js +72 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +71 -0
- package/dist/server/types/domain.d.ts +56 -0
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.d.ts +5 -2
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js +40 -8
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/index.d.ts +2 -0
- package/node_modules/@codingns/session-sync-core/dist/index.js +2 -0
- package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +10 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +110 -35
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.d.ts +11 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +105 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +131 -39
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.d.ts +9 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js +17 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.d.ts +8 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js +19 -6
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +13 -8
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +5 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +103 -51
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +2 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +41 -21
- 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.js +32 -8
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.d.ts +15 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js +16 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +167 -10
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +2 -0
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +1 -1
- package/node_modules/@codingns/session-sync-core/dist/types.js +1 -1
- package/node_modules/@codingns/session-sync-core/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/public/assets/AdaptiveButlerPage-uFwDdN-F.js +0 -3
- package/dist/public/assets/ConversationPage-z3sXtKZ7.js +0 -4
- package/dist/public/assets/DesktopWindowPage-CZcoGApB.js +0 -2
- package/dist/public/assets/FileContextPanel-C3qex8bb.js +0 -1
- package/dist/public/assets/GitSidebar-BK6H16XU.js +0 -6
- package/dist/public/assets/MobileCreateSessionSheet-BYfbvK8o.js +0 -1
- package/dist/public/assets/MobileSheet-Ckug8hTb.js +0 -1
- package/dist/public/assets/MobileWorkspaceSwitcherHeader-RqWrBdn2.js +0 -1
- package/dist/public/assets/SessionIndexPage-DuK10DL5.js +0 -1
- package/dist/public/assets/SettingsPage-fyD-xaHL.js +0 -1
- package/dist/public/assets/TerminalManagerPanel-CCLr1Ypk.js +0 -1
- package/dist/public/assets/TerminalRuntimeFallbackModal-aUzjEBwP.js +0 -1
- package/dist/public/assets/ToolGitPage-C264yjS9.js +0 -1
- package/dist/public/assets/ToolProcessesPage-BOP4A1cb.js +0 -1
- package/dist/public/assets/ToolsHomePage-CQxGiKQA.js +0 -1
- package/dist/public/assets/WorkbenchLandingPage-CvAY68ca.js +0 -1
- package/dist/public/assets/WorkbenchLayout-DGm8Tc5M.js +0 -3
- package/dist/public/assets/WorkbenchModal-0tPIIhca.js +0 -1
- package/dist/public/assets/WorkbenchShellRoute-BF0nHWOk.css +0 -1
- package/dist/public/assets/WorkbenchShellRoute-DBBOsJo9.js +0 -1
- package/dist/public/assets/WorkspaceDebugDetailPage-CDerFYd2.js +0 -1
- package/dist/public/assets/WorkspaceDetailPage-BlJc1CHE.js +0 -1
- package/dist/public/assets/WorkspaceHomePage-BUsKJ3lv.js +0 -1
- package/dist/public/assets/default-session-permission-mode-DT4SGiwp.js +0 -1
- package/dist/public/assets/index-BZLcEHW3.js +0 -42
- package/dist/public/assets/index-BbspQPC2.css +0 -1
- package/dist/public/assets/session-runtime-machine-DdLeDqQr.js +0 -17
- package/dist/public/assets/useRegisteredDebugTemplates-oFAQNIqh.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { CapabilityService, ClaudeCodeAdapter, CodexAdapter, GeminiAdapter, KimiAdapter, OpenCodeAdapter, ProviderRegistry, SessionSyncService } from "@codingns/session-sync-core";
|
|
2
|
+
import { CapabilityService, ClaudeCodeAdapter, CodexAdapter, GeminiAdapter, KimiAdapter, LegnaCodeAdapter, OpenCodeAdapter, ProviderRegistry, SessionSyncService } from "@codingns/session-sync-core";
|
|
3
3
|
import { AppError } from "../../shared/errors/app-error.js";
|
|
4
4
|
import { hashContent } from "../../shared/utils/hash.js";
|
|
5
5
|
import { createId } from "../../shared/utils/id.js";
|
|
@@ -17,6 +17,7 @@ import { CodexModelOptionsService, createFallbackCodexModelOptions, enrichCodexC
|
|
|
17
17
|
import { OpenCodeModelOptionsService, createFallbackOpenCodeModelOptions, enrichOpenCodeCapabilities } from "../provider/opencode-model-options.js";
|
|
18
18
|
import { getSharedProviderDiscoveryHelperClient } from "../provider/provider-discovery-helper-client.js";
|
|
19
19
|
import { discoverWorkspaceSessionsInRuntime } from "../provider/provider-discovery-runtime.js";
|
|
20
|
+
import { applyProviderDisabledState, createProviderCapabilityBlockedError } from "../provider/provider-disabled.js";
|
|
20
21
|
import { createTaskManager } from "../tasks/task-manager.js";
|
|
21
22
|
import { HOST_TASK_TYPES } from "../tasks/task-types.js";
|
|
22
23
|
import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
|
|
@@ -25,15 +26,18 @@ const RECONSTRUCTED_FORK_TARGET_PROVIDERS = new Set(["codex", "claude-code", "op
|
|
|
25
26
|
const FORK_RECONSTRUCTION_PAGE_SIZE = 200;
|
|
26
27
|
const MAX_FORK_DEPTH = 4;
|
|
27
28
|
const SYNTHETIC_CODEX_SESSION_CLEANUP_GRACE_MS = 120_000;
|
|
29
|
+
const GEMINI_RUNTIME_CHAT_DISCOVERY_GRACE_MS = 30_000;
|
|
28
30
|
const SESSION_START_DEFERRED_PROVIDERS = new Set([
|
|
29
31
|
"codex",
|
|
30
32
|
"claude-code",
|
|
33
|
+
"legna-code",
|
|
31
34
|
"opencode",
|
|
32
35
|
"gemini",
|
|
33
36
|
"kimi"
|
|
34
37
|
]);
|
|
35
38
|
const MUTABLE_HISTORY_TAIL_PROVIDERS = new Set([
|
|
36
39
|
"claude-code",
|
|
40
|
+
"legna-code",
|
|
37
41
|
"codex",
|
|
38
42
|
"gemini",
|
|
39
43
|
"kimi",
|
|
@@ -75,6 +79,8 @@ export class SessionHistoryService {
|
|
|
75
79
|
sessionIsolatedWorkspaceRepository;
|
|
76
80
|
providerDiscoveryHelperClient = getSharedProviderDiscoveryHelperClient();
|
|
77
81
|
providerSessionDiscoveryConfig;
|
|
82
|
+
sessionProviderConfigService;
|
|
83
|
+
providerControlRepository;
|
|
78
84
|
taskManager;
|
|
79
85
|
workspaceDiscoveryStatuses = new Map();
|
|
80
86
|
workspaceStateRefreshStatuses = new Map();
|
|
@@ -84,7 +90,7 @@ export class SessionHistoryService {
|
|
|
84
90
|
sessionDeletedObservers = new Set();
|
|
85
91
|
workspaceSessionRelations = new Map();
|
|
86
92
|
workspaceStateRefreshTaskSequence = 0;
|
|
87
|
-
constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}, taskManager = createTaskManager(), parallelSessionGroupRepository = null, parallelSessionMemberRepository = null, sessionIsolatedWorkspaceRepository = null) {
|
|
93
|
+
constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}, taskManager = createTaskManager(), parallelSessionGroupRepository = null, parallelSessionMemberRepository = null, sessionIsolatedWorkspaceRepository = null, sessionProviderConfigService = null, providerControlRepository = null) {
|
|
88
94
|
this.db = db;
|
|
89
95
|
this.workspaceRepository = workspaceRepository;
|
|
90
96
|
this.sessionBindingRepository = sessionBindingRepository;
|
|
@@ -102,9 +108,18 @@ export class SessionHistoryService {
|
|
|
102
108
|
this.parallelSessionGroupRepository = parallelSessionGroupRepository;
|
|
103
109
|
this.parallelSessionMemberRepository = parallelSessionMemberRepository;
|
|
104
110
|
this.sessionIsolatedWorkspaceRepository = sessionIsolatedWorkspaceRepository;
|
|
111
|
+
this.sessionProviderConfigService = sessionProviderConfigService;
|
|
112
|
+
this.providerControlRepository = providerControlRepository ?? {
|
|
113
|
+
get: (providerId) => ({
|
|
114
|
+
providerId: providerId.trim(),
|
|
115
|
+
enabled: true,
|
|
116
|
+
updatedAt: ""
|
|
117
|
+
})
|
|
118
|
+
};
|
|
105
119
|
this.claudeCodeHomeDir = config.claudeCodeHomeDir;
|
|
106
120
|
this.providerCliCommandPaths = {
|
|
107
121
|
"claude-code": process.platform === "win32" ? "claude.cmd" : "claude",
|
|
122
|
+
"legna-code": config.legnaCodeCliPath,
|
|
108
123
|
codex: config.codexCliPath,
|
|
109
124
|
gemini: config.geminiCliPath,
|
|
110
125
|
kimi: config.kimiCliPath
|
|
@@ -113,8 +128,10 @@ export class SessionHistoryService {
|
|
|
113
128
|
this.providerCliAvailability = buildProviderCliAvailabilitySnapshot(this.providerCliCommandPaths);
|
|
114
129
|
this.providerSessionDiscoveryConfig = {
|
|
115
130
|
claudeCodeHomeDir: config.claudeCodeHomeDir,
|
|
131
|
+
legnaCodeHomeDir: config.legnaCodeHomeDir,
|
|
116
132
|
codexCliPath: config.codexCliPath,
|
|
117
133
|
codexHomeDir: config.codexHomeDir,
|
|
134
|
+
legnaCodeCliPath: config.legnaCodeCliPath,
|
|
118
135
|
geminiCliPath: config.geminiCliPath,
|
|
119
136
|
geminiHomeDir: config.geminiHomeDir,
|
|
120
137
|
kimiDefaultModel: config.kimiDefaultModel,
|
|
@@ -125,6 +142,10 @@ export class SessionHistoryService {
|
|
|
125
142
|
};
|
|
126
143
|
this.providerRegistry = new ProviderRegistry([
|
|
127
144
|
new ClaudeCodeAdapter({ homeDir: config.claudeCodeHomeDir }),
|
|
145
|
+
new LegnaCodeAdapter({
|
|
146
|
+
homeDir: config.legnaCodeHomeDir,
|
|
147
|
+
legacyClaudeHomeDir: config.claudeCodeHomeDir
|
|
148
|
+
}),
|
|
128
149
|
new CodexAdapter({
|
|
129
150
|
homeDir: config.codexHomeDir,
|
|
130
151
|
forkTransportFactory: adapterOverrides.codexForkTransportFactory
|
|
@@ -196,7 +217,7 @@ export class SessionHistoryService {
|
|
|
196
217
|
executionLane: "helper_process",
|
|
197
218
|
concurrency: WORKSPACE_DISCOVERY_SCAN_CONCURRENCY,
|
|
198
219
|
helperProcessHandler: "session.workspace_discovery",
|
|
199
|
-
run: async ({ config, workspacePath, knownSessions }, context) => await discoverWorkspaceSessionsInRuntime(config, workspacePath, knownSessions, context.signal)
|
|
220
|
+
run: async ({ config, workspacePath, knownSessions, enabledProviders }, context) => await discoverWorkspaceSessionsInRuntime(config, workspacePath, knownSessions, enabledProviders, context.signal)
|
|
200
221
|
});
|
|
201
222
|
}
|
|
202
223
|
if (!this.taskManager.has(HOST_TASK_TYPES.providerCapabilityRefresh)) {
|
|
@@ -323,8 +344,8 @@ export class SessionHistoryService {
|
|
|
323
344
|
? current?.syncCursor ?? page.cursor
|
|
324
345
|
: page.cursor,
|
|
325
346
|
lastSyncAt: nowIso(),
|
|
326
|
-
lastErrorCode: current?.lastErrorCode ?? null,
|
|
327
|
-
lastErrorDetail: current?.lastErrorDetail ?? null,
|
|
347
|
+
lastErrorCode: clearSuccessfulProviderReadErrorCode(current?.lastErrorCode ?? null),
|
|
348
|
+
lastErrorDetail: clearSuccessfulProviderReadErrorDetail(current?.lastErrorCode ?? null, current?.lastErrorDetail ?? null),
|
|
328
349
|
resumedAt: current?.resumedAt ?? null
|
|
329
350
|
});
|
|
330
351
|
snapshotIdleMs = Date.now() - snapshotIdleStartedAt;
|
|
@@ -441,11 +462,11 @@ export class SessionHistoryService {
|
|
|
441
462
|
.listByWorkspace(workspaceId, userId)
|
|
442
463
|
.filter((item) => !this.isPendingSessionAlias(item));
|
|
443
464
|
const projectedItems = this.listProjectedIsolatedWorkspaceSessions(workspaceId, userId);
|
|
444
|
-
return this.enrichSessionItems(workspaceId, sortSessionListItemsByRecentActivity(mergeSessionListItemsBySessionId([...directItems, ...projectedItems])));
|
|
465
|
+
return this.filterDisabledProviderSessions(this.enrichSessionItems(workspaceId, sortSessionListItemsByRecentActivity(mergeSessionListItemsBySessionId([...directItems, ...projectedItems]))));
|
|
445
466
|
}
|
|
446
467
|
getProviderCapabilitiesSnapshot(provider) {
|
|
447
468
|
try {
|
|
448
|
-
return this.resolveProviderCapabilitiesImmediate(this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider)), null);
|
|
469
|
+
return this.applyProviderEnabledState(this.resolveProviderCapabilitiesImmediate(this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider)), null));
|
|
449
470
|
}
|
|
450
471
|
catch (error) {
|
|
451
472
|
throw mapSessionProviderError(error);
|
|
@@ -455,8 +476,17 @@ export class SessionHistoryService {
|
|
|
455
476
|
try {
|
|
456
477
|
const workspacePath = workspaceId ? this.getWorkspaceOrThrow(workspaceId).path : null;
|
|
457
478
|
const baseCapabilities = this.applyProviderCliAvailability(this.capabilityService.getProviderCapabilities(provider));
|
|
479
|
+
if (baseCapabilities.provider === "opencode" && workspacePath) {
|
|
480
|
+
const refreshed = await this.enrichProviderCapabilities(baseCapabilities, workspacePath);
|
|
481
|
+
const cacheKey = buildProviderCapabilityCacheKey(baseCapabilities.provider, workspacePath);
|
|
482
|
+
this.providerCapabilityCache.set(cacheKey, {
|
|
483
|
+
refreshedAt: Date.now(),
|
|
484
|
+
value: refreshed
|
|
485
|
+
});
|
|
486
|
+
return this.applyProviderEnabledState(refreshed);
|
|
487
|
+
}
|
|
458
488
|
this.scheduleProviderCapabilityRefresh(baseCapabilities, workspacePath);
|
|
459
|
-
return this.resolveProviderCapabilitiesImmediate(baseCapabilities, workspacePath);
|
|
489
|
+
return this.applyProviderEnabledState(this.resolveProviderCapabilitiesImmediate(baseCapabilities, workspacePath));
|
|
460
490
|
}
|
|
461
491
|
catch (error) {
|
|
462
492
|
throw mapSessionProviderError(error);
|
|
@@ -470,8 +500,19 @@ export class SessionHistoryService {
|
|
|
470
500
|
.getSessionCapabilities(binding.provider, binding.providerSessionId)
|
|
471
501
|
.then((capabilities) => {
|
|
472
502
|
const normalizedCapabilities = this.applyProviderCliAvailability(capabilities);
|
|
503
|
+
if (normalizedCapabilities.provider === "opencode") {
|
|
504
|
+
return this.enrichProviderCapabilities(normalizedCapabilities, workspacePath)
|
|
505
|
+
.then((refreshed) => {
|
|
506
|
+
const cacheKey = buildProviderCapabilityCacheKey(normalizedCapabilities.provider, workspacePath);
|
|
507
|
+
this.providerCapabilityCache.set(cacheKey, {
|
|
508
|
+
refreshedAt: Date.now(),
|
|
509
|
+
value: refreshed
|
|
510
|
+
});
|
|
511
|
+
return this.applyProviderEnabledState(refreshed);
|
|
512
|
+
});
|
|
513
|
+
}
|
|
473
514
|
this.scheduleProviderCapabilityRefresh(normalizedCapabilities, workspacePath);
|
|
474
|
-
return this.resolveProviderCapabilitiesImmediate(normalizedCapabilities, workspacePath);
|
|
515
|
+
return this.applyProviderEnabledState(this.resolveProviderCapabilitiesImmediate(normalizedCapabilities, workspacePath));
|
|
475
516
|
})
|
|
476
517
|
.catch((error) => {
|
|
477
518
|
throw mapSessionProviderError(error);
|
|
@@ -499,6 +540,9 @@ export class SessionHistoryService {
|
|
|
499
540
|
return applyImmediateModelOptionFallbacks(claudeEnriched, this.codexModelOptionsService.peekSnapshot(), this.openCodeModelOptionsService.peekSnapshot(workspacePath));
|
|
500
541
|
}
|
|
501
542
|
scheduleProviderCapabilityRefresh(capabilities, workspacePath) {
|
|
543
|
+
if (!this.isProviderEnabled(capabilities.provider)) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
502
546
|
const cacheKey = buildProviderCapabilityCacheKey(capabilities.provider, workspacePath);
|
|
503
547
|
const cached = this.providerCapabilityCache.get(cacheKey);
|
|
504
548
|
if (cached &&
|
|
@@ -550,17 +594,31 @@ export class SessionHistoryService {
|
|
|
550
594
|
limitations
|
|
551
595
|
};
|
|
552
596
|
}
|
|
597
|
+
applyProviderEnabledState(capabilities) {
|
|
598
|
+
if (this.isProviderEnabled(capabilities.provider)) {
|
|
599
|
+
return capabilities;
|
|
600
|
+
}
|
|
601
|
+
return applyProviderDisabledState(capabilities);
|
|
602
|
+
}
|
|
603
|
+
isProviderEnabled(provider) {
|
|
604
|
+
return this.providerControlRepository.get(provider.trim()).enabled;
|
|
605
|
+
}
|
|
606
|
+
filterDisabledProviderSessions(items) {
|
|
607
|
+
return items.filter((item) => this.isProviderEnabled(item.provider));
|
|
608
|
+
}
|
|
553
609
|
assertProviderCapabilityEnabled(provider, capability, fallbackDetail) {
|
|
554
610
|
const capabilities = this.getProviderCapabilitiesSnapshot(provider);
|
|
555
611
|
if (capabilities[capability]) {
|
|
556
612
|
return;
|
|
557
613
|
}
|
|
558
|
-
throw
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
614
|
+
throw createProviderCapabilityBlockedError(capabilities, "provider", fallbackDetail);
|
|
615
|
+
}
|
|
616
|
+
assertProviderSendEnabled(provider, field, fallbackDetail) {
|
|
617
|
+
const capabilities = this.getProviderCapabilitiesSnapshot(provider);
|
|
618
|
+
if (capabilities.canSendMessage) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
throw createProviderCapabilityBlockedError(capabilities, field, fallbackDetail);
|
|
564
622
|
}
|
|
565
623
|
async getSessionContextUsage(sessionId) {
|
|
566
624
|
const binding = this.getBindingOrThrow(sessionId);
|
|
@@ -597,6 +655,7 @@ export class SessionHistoryService {
|
|
|
597
655
|
}
|
|
598
656
|
}
|
|
599
657
|
async startSession(input) {
|
|
658
|
+
this.assertProviderCapabilityEnabled(input.provider, "canStartSession", "当前 provider 不支持创建会话");
|
|
600
659
|
if (SESSION_START_DEFERRED_PROVIDERS.has(input.provider)) {
|
|
601
660
|
throw new AppError({
|
|
602
661
|
statusCode: 409,
|
|
@@ -610,11 +669,17 @@ export class SessionHistoryService {
|
|
|
610
669
|
async startSessionDirect(input) {
|
|
611
670
|
const workspace = this.getWorkspaceOrThrow(input.workspaceId);
|
|
612
671
|
this.assertProviderCapabilityEnabled(input.provider, "canStartSession", "当前 provider 不支持创建会话");
|
|
672
|
+
const sessionId = createId();
|
|
673
|
+
const providerBinding = this.prepareDirectSessionBinding({
|
|
674
|
+
sessionId,
|
|
675
|
+
provider: input.provider,
|
|
676
|
+
providerConfigMode: input.providerConfigMode ?? null,
|
|
677
|
+
providerPresetId: input.providerPresetId ?? null
|
|
678
|
+
});
|
|
613
679
|
try {
|
|
614
|
-
const result = await this.
|
|
680
|
+
const result = await this.startProviderSessionWithBinding(input.provider, workspace.path, providerBinding.runtimeHomeDir, {
|
|
615
681
|
initialPrompt: input.initialPrompt
|
|
616
682
|
});
|
|
617
|
-
const sessionId = createId();
|
|
618
683
|
const timestamp = nowIso();
|
|
619
684
|
const persist = this.db.transaction(() => {
|
|
620
685
|
this.sessionBindingRepository.upsert({
|
|
@@ -623,6 +688,9 @@ export class SessionHistoryService {
|
|
|
623
688
|
provider: result.session.provider,
|
|
624
689
|
providerSessionId: result.session.providerSessionId,
|
|
625
690
|
rawStoreRef: result.session.rawStoreRef,
|
|
691
|
+
providerConfigMode: providerBinding.providerConfigMode,
|
|
692
|
+
providerPresetId: providerBinding.providerPresetId,
|
|
693
|
+
runtimeHomeDir: providerBinding.runtimeHomeDir,
|
|
626
694
|
createdAt: timestamp,
|
|
627
695
|
updatedAt: timestamp
|
|
628
696
|
});
|
|
@@ -690,10 +758,18 @@ export class SessionHistoryService {
|
|
|
690
758
|
});
|
|
691
759
|
}
|
|
692
760
|
this.assertForkDepthWithinLimit(input.sessionId);
|
|
693
|
-
|
|
761
|
+
const requestedTargetSelection = resolveRequestedProviderSelection({
|
|
762
|
+
existingBinding: targetProvider === binding.provider ? binding : null,
|
|
763
|
+
providerConfigMode: input.providerConfigMode ?? undefined,
|
|
764
|
+
providerPresetId: input.providerPresetId ?? undefined
|
|
765
|
+
});
|
|
766
|
+
if (targetProvider !== binding.provider
|
|
767
|
+
|| !areEquivalentProviderBindingSelection(binding, requestedTargetSelection)) {
|
|
694
768
|
return this.forkSessionAcrossProviders({
|
|
695
769
|
...input,
|
|
696
|
-
targetProvider
|
|
770
|
+
targetProvider,
|
|
771
|
+
providerConfigMode: requestedTargetSelection.providerConfigMode,
|
|
772
|
+
providerPresetId: requestedTargetSelection.providerPresetId
|
|
697
773
|
}, binding, sourceMessageId);
|
|
698
774
|
}
|
|
699
775
|
try {
|
|
@@ -713,6 +789,9 @@ export class SessionHistoryService {
|
|
|
713
789
|
provider: result.session.provider,
|
|
714
790
|
providerSessionId: result.session.providerSessionId,
|
|
715
791
|
rawStoreRef: result.session.rawStoreRef,
|
|
792
|
+
providerConfigMode: binding.providerConfigMode,
|
|
793
|
+
providerPresetId: binding.providerPresetId,
|
|
794
|
+
runtimeHomeDir: binding.runtimeHomeDir,
|
|
716
795
|
createdAt: timestamp,
|
|
717
796
|
updatedAt: timestamp
|
|
718
797
|
});
|
|
@@ -807,6 +886,8 @@ export class SessionHistoryService {
|
|
|
807
886
|
userId: input.userId,
|
|
808
887
|
provider: input.targetProvider,
|
|
809
888
|
initialPrompt: inheritedPrompt,
|
|
889
|
+
providerConfigMode: input.providerConfigMode ?? null,
|
|
890
|
+
providerPresetId: input.providerPresetId ?? null,
|
|
810
891
|
parentSessionId: input.sessionId,
|
|
811
892
|
sessionKind: input.sessionKind ?? "default",
|
|
812
893
|
annotationSourceMessageId: input.annotationSourceMessageId ?? null,
|
|
@@ -854,6 +935,40 @@ export class SessionHistoryService {
|
|
|
854
935
|
this.workspaceSessionRelations.set(input.targetWorkspaceId?.trim() || sourceBinding.workspaceId, relationMap);
|
|
855
936
|
return this.getSessionListItemOrThrow(startedSession.sessionId, input.userId);
|
|
856
937
|
}
|
|
938
|
+
prepareDirectSessionBinding(input) {
|
|
939
|
+
if (!this.sessionProviderConfigService) {
|
|
940
|
+
return {
|
|
941
|
+
providerConfigMode: "global-default",
|
|
942
|
+
providerPresetId: null,
|
|
943
|
+
runtimeHomeDir: null
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
return this.sessionProviderConfigService.prepareSessionBinding({
|
|
947
|
+
sessionId: input.sessionId,
|
|
948
|
+
provider: input.provider,
|
|
949
|
+
providerConfigMode: input.providerConfigMode ?? undefined,
|
|
950
|
+
providerPresetId: input.providerPresetId ?? null
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
startProviderSessionWithBinding(provider, workspacePath, runtimeHomeDir, options) {
|
|
954
|
+
const scopedRuntimeHomeDir = runtimeHomeDir?.trim() || null;
|
|
955
|
+
if (!scopedRuntimeHomeDir) {
|
|
956
|
+
return this.sessionSyncService.startSession(provider, workspacePath, options);
|
|
957
|
+
}
|
|
958
|
+
switch (provider) {
|
|
959
|
+
case "claude-code":
|
|
960
|
+
return new ClaudeCodeAdapter({ homeDir: scopedRuntimeHomeDir }).startSession(workspacePath, options);
|
|
961
|
+
case "codex":
|
|
962
|
+
return new CodexAdapter({ homeDir: scopedRuntimeHomeDir }).startSession(workspacePath, options);
|
|
963
|
+
case "gemini":
|
|
964
|
+
return new GeminiAdapter({
|
|
965
|
+
homeDir: scopedRuntimeHomeDir,
|
|
966
|
+
commandPath: this.providerSessionDiscoveryConfig.geminiCliPath
|
|
967
|
+
}).startSession(workspacePath, options);
|
|
968
|
+
default:
|
|
969
|
+
return this.sessionSyncService.startSession(provider, workspacePath, options);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
857
972
|
async readForkSourceMessages(sessionId, binding, sourceType, sourceMessageId, sourceMessageSnapshot = null) {
|
|
858
973
|
const messages = [];
|
|
859
974
|
let cursor = null;
|
|
@@ -920,6 +1035,7 @@ export class SessionHistoryService {
|
|
|
920
1035
|
}
|
|
921
1036
|
async sendMessage(sessionId, content, clientRequestId, permissionMode = null) {
|
|
922
1037
|
const binding = this.getBindingOrThrow(sessionId);
|
|
1038
|
+
this.assertProviderSendEnabled(binding.provider, "sessionId", "当前 provider 不支持继续发送消息");
|
|
923
1039
|
const result = await this.sessionSyncService
|
|
924
1040
|
.sendMessage(binding.provider, binding.providerSessionId, binding.rawStoreRef, content, clientRequestId, permissionMode)
|
|
925
1041
|
.catch((error) => {
|
|
@@ -1024,13 +1140,14 @@ export class SessionHistoryService {
|
|
|
1024
1140
|
return null;
|
|
1025
1141
|
}
|
|
1026
1142
|
await this.syncSessionTitleFromProvider(sessionId, binding);
|
|
1143
|
+
const snapshot = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
|
|
1027
1144
|
this.upsertSnapshot(sessionId, {
|
|
1028
1145
|
syncStatus: "idle",
|
|
1029
1146
|
syncCursor: page.cursor,
|
|
1030
1147
|
lastSyncAt: nowIso(),
|
|
1031
|
-
lastErrorCode:
|
|
1032
|
-
lastErrorDetail:
|
|
1033
|
-
resumedAt:
|
|
1148
|
+
lastErrorCode: clearSuccessfulProviderReadErrorCode(snapshot?.lastErrorCode ?? null),
|
|
1149
|
+
lastErrorDetail: clearSuccessfulProviderReadErrorDetail(snapshot?.lastErrorCode ?? null, snapshot?.lastErrorDetail ?? null),
|
|
1150
|
+
resumedAt: snapshot?.resumedAt ?? null
|
|
1034
1151
|
});
|
|
1035
1152
|
return {
|
|
1036
1153
|
type: "session.delta",
|
|
@@ -1244,6 +1361,15 @@ export class SessionHistoryService {
|
|
|
1244
1361
|
provider: resolvedSnapshot.provider,
|
|
1245
1362
|
providerSessionId: resolvedSnapshot.providerSessionId,
|
|
1246
1363
|
rawStoreRef: resolvedSnapshot.rawStoreRef,
|
|
1364
|
+
providerConfigMode: currentBinding?.providerConfigMode
|
|
1365
|
+
?? duplicateBinding?.providerConfigMode
|
|
1366
|
+
?? "global-default",
|
|
1367
|
+
providerPresetId: currentBinding?.providerPresetId
|
|
1368
|
+
?? duplicateBinding?.providerPresetId
|
|
1369
|
+
?? null,
|
|
1370
|
+
runtimeHomeDir: currentBinding?.runtimeHomeDir
|
|
1371
|
+
?? duplicateBinding?.runtimeHomeDir
|
|
1372
|
+
?? null,
|
|
1247
1373
|
createdAt: pickEarlierIso(currentBinding?.createdAt ?? null, duplicateBinding?.createdAt ?? null)
|
|
1248
1374
|
?? timestamp,
|
|
1249
1375
|
updatedAt: timestamp
|
|
@@ -1285,14 +1411,19 @@ export class SessionHistoryService {
|
|
|
1285
1411
|
try {
|
|
1286
1412
|
const discoverStartedAt = Date.now();
|
|
1287
1413
|
const existingWorkspaceSessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
|
|
1288
|
-
const
|
|
1414
|
+
const enabledProviders = this.providerRegistry
|
|
1415
|
+
.list()
|
|
1416
|
+
.map((adapter) => adapter.providerId)
|
|
1417
|
+
.filter((providerId) => this.isProviderEnabled(providerId));
|
|
1418
|
+
const knownSessions = this.buildKnownSessionSummaries(existingWorkspaceSessions.filter((session) => enabledProviders.includes(session.provider)), workspace.path);
|
|
1289
1419
|
const discoveryHandle = this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscoveryScan, {
|
|
1290
1420
|
key: workspaceId,
|
|
1291
1421
|
source: "session_history.workspace_discovery.scan",
|
|
1292
1422
|
input: {
|
|
1293
1423
|
config: this.providerSessionDiscoveryConfig,
|
|
1294
1424
|
workspacePath: workspace.path,
|
|
1295
|
-
knownSessions
|
|
1425
|
+
knownSessions,
|
|
1426
|
+
enabledProviders
|
|
1296
1427
|
}
|
|
1297
1428
|
});
|
|
1298
1429
|
const discovery = await awaitTaskHandleWithSignal(discoveryHandle, signal).catch((error) => {
|
|
@@ -1335,6 +1466,9 @@ export class SessionHistoryService {
|
|
|
1335
1466
|
provider: session.provider,
|
|
1336
1467
|
providerSessionId: session.providerSessionId,
|
|
1337
1468
|
rawStoreRef: session.rawStoreRef,
|
|
1469
|
+
providerConfigMode: existing?.providerConfigMode ?? "global-default",
|
|
1470
|
+
providerPresetId: existing?.providerPresetId ?? null,
|
|
1471
|
+
runtimeHomeDir: existing?.runtimeHomeDir ?? null,
|
|
1338
1472
|
createdAt,
|
|
1339
1473
|
updatedAt: timestamp
|
|
1340
1474
|
};
|
|
@@ -1614,9 +1748,24 @@ export class SessionHistoryService {
|
|
|
1614
1748
|
total: 0
|
|
1615
1749
|
};
|
|
1616
1750
|
}
|
|
1751
|
+
if (this.shouldTreatMissingGeminiRuntimeHistoryAsEmpty(sessionId, provider, error)) {
|
|
1752
|
+
return {
|
|
1753
|
+
messages: [],
|
|
1754
|
+
cursor,
|
|
1755
|
+
nextCursor: null,
|
|
1756
|
+
total: 0
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1617
1759
|
throw mapSessionProviderError(error);
|
|
1618
1760
|
});
|
|
1619
1761
|
}
|
|
1762
|
+
shouldTreatMissingGeminiRuntimeHistoryAsEmpty(sessionId, provider, error) {
|
|
1763
|
+
if (provider !== "gemini" || !isGeminiChatNotFoundError(error)) {
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
const sessionIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
|
|
1767
|
+
return this.listSessionStatesBySessionId(sessionId).some((state) => shouldTreatMissingGeminiRuntimeHistoryStateAsTransient(state, sessionIndex));
|
|
1768
|
+
}
|
|
1620
1769
|
enrichMessagesWithOrigin(sessionId, messages) {
|
|
1621
1770
|
return this.resolveMessageOrigins(sessionId, messages);
|
|
1622
1771
|
}
|
|
@@ -1909,8 +2058,8 @@ export class SessionHistoryService {
|
|
|
1909
2058
|
syncStatus: "idle",
|
|
1910
2059
|
syncCursor: page.cursor,
|
|
1911
2060
|
lastSyncAt: nowIso(),
|
|
1912
|
-
lastErrorCode: snapshot?.lastErrorCode ?? null,
|
|
1913
|
-
lastErrorDetail: snapshot?.lastErrorDetail ?? null,
|
|
2061
|
+
lastErrorCode: clearSuccessfulProviderReadErrorCode(snapshot?.lastErrorCode ?? null),
|
|
2062
|
+
lastErrorDetail: clearSuccessfulProviderReadErrorDetail(snapshot?.lastErrorCode ?? null, snapshot?.lastErrorDetail ?? null),
|
|
1914
2063
|
resumedAt: snapshot?.resumedAt ?? null
|
|
1915
2064
|
});
|
|
1916
2065
|
await onEnvelope({
|
|
@@ -1936,7 +2085,12 @@ export class SessionHistoryService {
|
|
|
1936
2085
|
provider: binding.provider,
|
|
1937
2086
|
providerSessionId: binding.providerSessionId,
|
|
1938
2087
|
rawStoreRef: binding.rawStoreRef
|
|
1939
|
-
}, signal)
|
|
2088
|
+
}, signal).catch((error) => {
|
|
2089
|
+
if (this.shouldTreatMissingGeminiRuntimeHistoryAsEmpty(sessionId, binding.provider, error)) {
|
|
2090
|
+
return "";
|
|
2091
|
+
}
|
|
2092
|
+
throw error;
|
|
2093
|
+
})).trim();
|
|
1940
2094
|
const resolvedTitle = resolvePersistedSessionTitle(binding.provider, nextTitle, currentIndex.title);
|
|
1941
2095
|
if (resolvedTitle.length === 0 || resolvedTitle === currentIndex.title) {
|
|
1942
2096
|
return;
|
|
@@ -2371,6 +2525,9 @@ export class SessionHistoryService {
|
|
|
2371
2525
|
provider: input.provider,
|
|
2372
2526
|
providerSessionId: buildPendingBindingValue(input.provider, input.targetSessionId),
|
|
2373
2527
|
rawStoreRef: buildPendingBindingValue(input.provider, input.targetSessionId),
|
|
2528
|
+
providerConfigMode: sourceBinding.providerConfigMode,
|
|
2529
|
+
providerPresetId: sourceBinding.providerPresetId,
|
|
2530
|
+
runtimeHomeDir: sourceBinding.runtimeHomeDir,
|
|
2374
2531
|
createdAt: sourceBinding.createdAt,
|
|
2375
2532
|
updatedAt: input.timestamp
|
|
2376
2533
|
});
|
|
@@ -2452,6 +2609,9 @@ export class SessionHistoryService {
|
|
|
2452
2609
|
provider: sourceBinding.provider,
|
|
2453
2610
|
providerSessionId: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
|
|
2454
2611
|
rawStoreRef: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
|
|
2612
|
+
providerConfigMode: sourceBinding.providerConfigMode,
|
|
2613
|
+
providerPresetId: sourceBinding.providerPresetId,
|
|
2614
|
+
runtimeHomeDir: sourceBinding.runtimeHomeDir,
|
|
2455
2615
|
createdAt: sourceBinding.createdAt,
|
|
2456
2616
|
updatedAt: input.timestamp
|
|
2457
2617
|
});
|
|
@@ -2690,7 +2850,9 @@ export class SessionHistoryService {
|
|
|
2690
2850
|
const liveObservation = this.resolveLiveActivityObservation(sessionId);
|
|
2691
2851
|
const inspection = liveObservation
|
|
2692
2852
|
? null
|
|
2693
|
-
:
|
|
2853
|
+
: binding.provider === "gemini"
|
|
2854
|
+
? await this.inspectGeminiHistoryActivity(sessionId, binding)
|
|
2855
|
+
: inspectSessionActivity(binding.provider, binding.rawStoreRef);
|
|
2694
2856
|
if (inspection) {
|
|
2695
2857
|
const nowMs = Date.parse(timestamp);
|
|
2696
2858
|
if (shouldClearStaleRuntimeWithoutInspection(current, inspection, nowMs)) {
|
|
@@ -2753,6 +2915,15 @@ export class SessionHistoryService {
|
|
|
2753
2915
|
});
|
|
2754
2916
|
return nextRecord;
|
|
2755
2917
|
}
|
|
2918
|
+
async inspectGeminiHistoryActivity(sessionId, binding) {
|
|
2919
|
+
try {
|
|
2920
|
+
const page = await this.readPage(sessionId, binding.provider, binding.providerSessionId, binding.rawStoreRef, null, GEMINI_ACTIVITY_INFERENCE_HISTORY_LIMIT, "backward", this.sessionIndexRepository.findIndexRecordBySessionId(sessionId)?.messageCount ?? null);
|
|
2921
|
+
return inferGeminiInspectionFromHistory(page.messages);
|
|
2922
|
+
}
|
|
2923
|
+
catch {
|
|
2924
|
+
return inspectSessionActivity(binding.provider, binding.rawStoreRef);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2756
2927
|
async repairCodexDirtyBindingBeforeHistoryRead(sessionId, userId, binding) {
|
|
2757
2928
|
if (!shouldRepairCodexDirtyBinding(binding)) {
|
|
2758
2929
|
this.codexDirtyBindingRepairStates.delete(sessionId);
|
|
@@ -2829,7 +3000,11 @@ export class SessionHistoryService {
|
|
|
2829
3000
|
}
|
|
2830
3001
|
}
|
|
2831
3002
|
function isProviderCliBacked(provider) {
|
|
2832
|
-
return provider === "claude-code"
|
|
3003
|
+
return provider === "claude-code"
|
|
3004
|
+
|| provider === "legna-code"
|
|
3005
|
+
|| provider === "codex"
|
|
3006
|
+
|| provider === "gemini"
|
|
3007
|
+
|| provider === "kimi";
|
|
2833
3008
|
}
|
|
2834
3009
|
function buildProviderCliAvailabilitySnapshot(commandPaths) {
|
|
2835
3010
|
return Object.freeze(Object.fromEntries(Object.entries(commandPaths).map(([provider, commandPath]) => [
|
|
@@ -2841,6 +3016,8 @@ function buildProviderCliUnavailableMessage(provider) {
|
|
|
2841
3016
|
switch (provider) {
|
|
2842
3017
|
case "claude-code":
|
|
2843
3018
|
return "未检测到 Claude CLI";
|
|
3019
|
+
case "legna-code":
|
|
3020
|
+
return "未检测到 Legna CLI";
|
|
2844
3021
|
case "codex":
|
|
2845
3022
|
return "未检测到 Codex CLI";
|
|
2846
3023
|
case "gemini":
|
|
@@ -2900,6 +3077,46 @@ function hasInspectionEvidence(inspection) {
|
|
|
2900
3077
|
|| !!inspection.lastEventAt
|
|
2901
3078
|
|| !!inspection.completedAtCandidate;
|
|
2902
3079
|
}
|
|
3080
|
+
function inferGeminiInspectionFromHistory(messages) {
|
|
3081
|
+
let lastEventAt = null;
|
|
3082
|
+
let lastUserAt = null;
|
|
3083
|
+
let latestNonUserMessage = null;
|
|
3084
|
+
const pendingToolCallIds = new Set();
|
|
3085
|
+
for (const message of messages) {
|
|
3086
|
+
lastEventAt = pickLaterIso(lastEventAt, message.timestamp);
|
|
3087
|
+
if (message.role === "user") {
|
|
3088
|
+
lastUserAt = pickLaterIso(lastUserAt, message.timestamp);
|
|
3089
|
+
continue;
|
|
3090
|
+
}
|
|
3091
|
+
latestNonUserMessage = message;
|
|
3092
|
+
if (message.kind === "tool_call" && message.toolCall?.callId) {
|
|
3093
|
+
pendingToolCallIds.add(message.toolCall.callId);
|
|
3094
|
+
continue;
|
|
3095
|
+
}
|
|
3096
|
+
if (message.kind === "tool_result" && message.toolCall?.callId) {
|
|
3097
|
+
pendingToolCallIds.delete(message.toolCall.callId);
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
const latestNonUserAt = latestNonUserMessage?.timestamp ?? null;
|
|
3101
|
+
const hasReplyAfterLatestUser = !!latestNonUserAt && (!lastUserAt || latestNonUserAt.localeCompare(lastUserAt) >= 0);
|
|
3102
|
+
const isTerminalReplyKind = latestNonUserMessage?.kind === "text" || latestNonUserMessage?.kind === "tool_result";
|
|
3103
|
+
const completedAtCandidate = hasReplyAfterLatestUser && isTerminalReplyKind && pendingToolCallIds.size === 0
|
|
3104
|
+
? latestNonUserAt
|
|
3105
|
+
: null;
|
|
3106
|
+
const hasOpenTurn = !completedAtCandidate
|
|
3107
|
+
&& hasReplyAfterLatestUser
|
|
3108
|
+
&& (pendingToolCallIds.size > 0
|
|
3109
|
+
|| latestNonUserMessage?.kind === "thinking"
|
|
3110
|
+
|| latestNonUserMessage?.kind === "tool_call");
|
|
3111
|
+
return {
|
|
3112
|
+
runningState: hasOpenTurn ? "running" : "idle",
|
|
3113
|
+
hasPendingTools: pendingToolCallIds.size > 0,
|
|
3114
|
+
lastEventAt,
|
|
3115
|
+
completedAtCandidate,
|
|
3116
|
+
errorCode: null,
|
|
3117
|
+
errorDetail: null
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
2903
3120
|
function applySessionActivityResolution(item, resolution) {
|
|
2904
3121
|
const rawResolvedRunningState = resolution.runningState === "unknown" && item.runningState === null
|
|
2905
3122
|
? null
|
|
@@ -3453,6 +3670,40 @@ function shouldTreatMissingSyntheticHistoryAsEmpty(provider, rawStoreRef, error)
|
|
|
3453
3670
|
const detail = error instanceof Error ? error.message : String(error);
|
|
3454
3671
|
return detail.includes("ENOENT");
|
|
3455
3672
|
}
|
|
3673
|
+
function shouldTreatMissingGeminiRuntimeHistoryStateAsTransient(state, sessionIndex) {
|
|
3674
|
+
if (state.activitySource !== "runtime") {
|
|
3675
|
+
return false;
|
|
3676
|
+
}
|
|
3677
|
+
if (state.runningState === "starting" || state.runningState === "running") {
|
|
3678
|
+
return true;
|
|
3679
|
+
}
|
|
3680
|
+
if ((sessionIndex?.messageCount ?? 0) !== 0) {
|
|
3681
|
+
return false;
|
|
3682
|
+
}
|
|
3683
|
+
return isWithinGeminiRuntimeChatDiscoveryGraceWindow(state.lastEventAt ?? state.updatedAt ?? sessionIndex?.updatedAt ?? sessionIndex?.createdAt ?? null);
|
|
3684
|
+
}
|
|
3685
|
+
function isWithinGeminiRuntimeChatDiscoveryGraceWindow(timestamp) {
|
|
3686
|
+
if (!timestamp) {
|
|
3687
|
+
return false;
|
|
3688
|
+
}
|
|
3689
|
+
const timestampMs = Date.parse(timestamp);
|
|
3690
|
+
if (!Number.isFinite(timestampMs)) {
|
|
3691
|
+
return false;
|
|
3692
|
+
}
|
|
3693
|
+
return Date.now() - timestampMs <= GEMINI_RUNTIME_CHAT_DISCOVERY_GRACE_MS;
|
|
3694
|
+
}
|
|
3695
|
+
function isGeminiChatNotFoundError(error) {
|
|
3696
|
+
if (error instanceof AppError) {
|
|
3697
|
+
return error.errorCode === "GEMINI_CHAT_NOT_FOUND";
|
|
3698
|
+
}
|
|
3699
|
+
return error instanceof Error && error.message === "GEMINI_CHAT_NOT_FOUND";
|
|
3700
|
+
}
|
|
3701
|
+
function clearSuccessfulProviderReadErrorCode(errorCode) {
|
|
3702
|
+
return errorCode === "PROVIDER_READ_FAILED" ? null : errorCode;
|
|
3703
|
+
}
|
|
3704
|
+
function clearSuccessfulProviderReadErrorDetail(errorCode, errorDetail) {
|
|
3705
|
+
return errorCode === "PROVIDER_READ_FAILED" ? null : errorDetail;
|
|
3706
|
+
}
|
|
3456
3707
|
function shouldShortCircuitMissingSyntheticCodexHistory(provider, rawStoreRef) {
|
|
3457
3708
|
return provider === "codex" && isSyntheticCodexRawStoreRef(rawStoreRef) && !existsSync(rawStoreRef);
|
|
3458
3709
|
}
|
|
@@ -3599,6 +3850,9 @@ function isSyntheticCodexSessionTitle(title) {
|
|
|
3599
3850
|
return (/^rollout-\d{4}-\d{2}-\d{2}t/i.test(title) ||
|
|
3600
3851
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(title));
|
|
3601
3852
|
}
|
|
3853
|
+
function isSyntheticGeminiSessionTitle(title) {
|
|
3854
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(title);
|
|
3855
|
+
}
|
|
3602
3856
|
function shouldSyncSessionTitleFromProvider(provider, currentTitle) {
|
|
3603
3857
|
const normalizedTitle = currentTitle?.trim() ?? "";
|
|
3604
3858
|
if (normalizedTitle.length === 0) {
|
|
@@ -3607,6 +3861,9 @@ function shouldSyncSessionTitleFromProvider(provider, currentTitle) {
|
|
|
3607
3861
|
if (provider === "codex" && isSyntheticCodexSessionTitle(normalizedTitle)) {
|
|
3608
3862
|
return true;
|
|
3609
3863
|
}
|
|
3864
|
+
if (provider === "gemini" && isSyntheticGeminiSessionTitle(normalizedTitle)) {
|
|
3865
|
+
return true;
|
|
3866
|
+
}
|
|
3610
3867
|
return false;
|
|
3611
3868
|
}
|
|
3612
3869
|
function shouldRemoveHiddenClaudeDebugSession(session) {
|
|
@@ -3618,6 +3875,7 @@ function shouldRemoveHiddenClaudeDebugSession(session) {
|
|
|
3618
3875
|
/\/agent-[^/]+\.jsonl$/i.test(normalizedRawStoreRef));
|
|
3619
3876
|
}
|
|
3620
3877
|
const STALE_RUNTIME_WITHOUT_INSPECTION_GRACE_MS = 120_000;
|
|
3878
|
+
const GEMINI_ACTIVITY_INFERENCE_HISTORY_LIMIT = 80;
|
|
3621
3879
|
function shouldClearStaleRuntimeWithoutInspection(current, inspection, nowMs) {
|
|
3622
3880
|
if (!current || current.activitySource !== "runtime") {
|
|
3623
3881
|
return false;
|
|
@@ -3795,8 +4053,51 @@ function areEquivalentSessionBindings(current, next) {
|
|
|
3795
4053
|
current.provider === next.provider &&
|
|
3796
4054
|
current.providerSessionId === next.providerSessionId &&
|
|
3797
4055
|
current.rawStoreRef === next.rawStoreRef &&
|
|
4056
|
+
current.providerConfigMode === next.providerConfigMode &&
|
|
4057
|
+
current.providerPresetId === next.providerPresetId &&
|
|
4058
|
+
current.runtimeHomeDir === next.runtimeHomeDir &&
|
|
3798
4059
|
current.createdAt === next.createdAt);
|
|
3799
4060
|
}
|
|
4061
|
+
function resolveRequestedProviderSelection(input) {
|
|
4062
|
+
const existingSelection = input.existingBinding
|
|
4063
|
+
? {
|
|
4064
|
+
providerConfigMode: input.existingBinding.providerConfigMode,
|
|
4065
|
+
providerPresetId: input.existingBinding.providerPresetId
|
|
4066
|
+
}
|
|
4067
|
+
: null;
|
|
4068
|
+
const normalizedPresetId = input.providerPresetId?.trim() || null;
|
|
4069
|
+
if (input.providerConfigMode === undefined && input.providerPresetId === undefined) {
|
|
4070
|
+
return existingSelection ?? {
|
|
4071
|
+
providerConfigMode: "global-default",
|
|
4072
|
+
providerPresetId: null
|
|
4073
|
+
};
|
|
4074
|
+
}
|
|
4075
|
+
const providerConfigMode = input.providerConfigMode
|
|
4076
|
+
?? (normalizedPresetId ? "cc-switch-preset" : "global-default");
|
|
4077
|
+
if (providerConfigMode === "global-default") {
|
|
4078
|
+
return {
|
|
4079
|
+
providerConfigMode,
|
|
4080
|
+
providerPresetId: null
|
|
4081
|
+
};
|
|
4082
|
+
}
|
|
4083
|
+
const providerPresetId = normalizedPresetId ?? existingSelection?.providerPresetId ?? null;
|
|
4084
|
+
if (!providerPresetId) {
|
|
4085
|
+
throw new AppError({
|
|
4086
|
+
statusCode: 400,
|
|
4087
|
+
errorCode: "INVALID_INPUT",
|
|
4088
|
+
detail: "使用 cc-switch preset 时必须提供 providerPresetId",
|
|
4089
|
+
field: "providerPresetId"
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
return {
|
|
4093
|
+
providerConfigMode,
|
|
4094
|
+
providerPresetId
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
function areEquivalentProviderBindingSelection(binding, selection) {
|
|
4098
|
+
return (binding.providerConfigMode === selection.providerConfigMode
|
|
4099
|
+
&& binding.providerPresetId === selection.providerPresetId);
|
|
4100
|
+
}
|
|
3800
4101
|
function areEquivalentSessionIndexRecords(current, next) {
|
|
3801
4102
|
if (!current) {
|
|
3802
4103
|
return false;
|