@jingyi0605/codingns 0.3.5 → 0.3.6
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/README.md +13 -0
- package/bin/codingns.mjs +880 -9
- package/dist/public/assets/{TerminalPage-CgrfstRm.js → TerminalPage-D00S4KM6.js} +16 -16
- package/dist/public/assets/index-BlOinYqR.js +122 -0
- package/dist/public/assets/index-Dg_7g6lA.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/opencode-base-url-resolver.d.ts +7 -0
- package/dist/server/config/opencode-base-url-resolver.js +48 -11
- package/dist/server/config/opencode-base-url-resolver.js.map +1 -1
- package/dist/server/config/opencode-system-probe-helper-client.d.ts +4 -0
- package/dist/server/config/opencode-system-probe-helper-client.js +29 -0
- package/dist/server/config/opencode-system-probe-helper-client.js.map +1 -1
- package/dist/server/config/opencode-system-probe-helper-process.d.ts +12 -0
- package/dist/server/config/opencode-system-probe-helper-process.js +34 -7
- package/dist/server/config/opencode-system-probe-helper-process.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +144 -0
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js +242 -1
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +133 -2
- package/dist/server/modules/assistant-capability/assistant-capability-service.js +395 -2
- package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.js +7 -6
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.js.map +1 -1
- package/dist/server/modules/butler/butler-workspace-context.js +24 -13
- package/dist/server/modules/butler/butler-workspace-context.js.map +1 -1
- package/dist/server/modules/debug-target/debug-target-controller.d.ts +13 -0
- package/dist/server/modules/debug-target/debug-target-controller.js +77 -2
- package/dist/server/modules/debug-target/debug-target-controller.js.map +1 -1
- package/dist/server/modules/debug-target/debug-target-service.d.ts +11 -2
- package/dist/server/modules/debug-target/debug-target-service.js +138 -3
- package/dist/server/modules/debug-target/debug-target-service.js.map +1 -1
- package/dist/server/modules/git/git-command-helper-client.d.ts +2 -0
- package/dist/server/modules/git/git-command-helper-client.js +52 -3
- package/dist/server/modules/git/git-command-helper-client.js.map +1 -1
- package/dist/server/modules/git/git-command-helper-process.js +62 -9
- package/dist/server/modules/git/git-command-helper-process.js.map +1 -1
- package/dist/server/modules/git/git-command-runner.d.ts +1 -0
- package/dist/server/modules/git/git-command-runner.js +25 -0
- package/dist/server/modules/git/git-command-runner.js.map +1 -1
- package/dist/server/modules/git/git-controller.js +8 -7
- package/dist/server/modules/git/git-controller.js.map +1 -1
- package/dist/server/modules/git/git-read-service.d.ts +7 -7
- package/dist/server/modules/git/git-read-service.js +41 -24
- package/dist/server/modules/git/git-read-service.js.map +1 -1
- package/dist/server/modules/model-switch/cc-switch-adapter.js +6 -2
- package/dist/server/modules/model-switch/cc-switch-adapter.js.map +1 -1
- package/dist/server/modules/provider/codex-model-options.js +2 -3
- package/dist/server/modules/provider/codex-model-options.js.map +1 -1
- package/dist/server/modules/provider/opencode-model-options.js +2 -3
- package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +9 -4
- package/dist/server/modules/provider/provider-discovery-helper-client.js +87 -11
- package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-helper-process.js +52 -47
- package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-runtime.d.ts +4 -0
- package/dist/server/modules/provider/provider-discovery-runtime.js +65 -0
- package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -0
- package/dist/server/modules/sessions/session-history-service.d.ts +5 -2
- package/dist/server/modules/sessions/session-history-service.js +214 -26
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +2 -0
- package/dist/server/modules/sessions/session-live-runtime-service.js +67 -23
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/skills/builtin-skill-service.d.ts +12 -0
- package/dist/server/modules/skills/builtin-skill-service.js +49 -0
- package/dist/server/modules/skills/builtin-skill-service.js.map +1 -0
- package/dist/server/modules/skills/builtin-skills/codingns-assistant/SKILL.md +75 -0
- package/dist/server/modules/skills/builtin-skills/codingns-assistant/agents/openai.yaml +4 -0
- package/dist/server/modules/skills/builtin-skills/codingns-assistant/references/cli-workflow.md +130 -0
- package/dist/server/modules/skills/skill-manager-service.d.ts +7 -0
- package/dist/server/modules/skills/skill-manager-service.js +98 -0
- package/dist/server/modules/skills/skill-manager-service.js.map +1 -1
- package/dist/server/modules/tailscale/tailscale-helper-client.d.ts +1 -0
- package/dist/server/modules/tailscale/tailscale-helper-client.js +12 -0
- package/dist/server/modules/tailscale/tailscale-helper-client.js.map +1 -1
- package/dist/server/modules/tasks/task-helper-client.d.ts +5 -0
- package/dist/server/modules/tasks/task-helper-client.js +45 -0
- package/dist/server/modules/tasks/task-helper-client.js.map +1 -1
- package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +10 -3
- package/dist/server/modules/tasks/task-helper-process-handlers.js +7 -5
- package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -1
- package/dist/server/modules/tasks/task-helper-process.js +11 -1
- package/dist/server/modules/tasks/task-helper-process.js.map +1 -1
- package/dist/server/modules/tasks/task-lane-executors.js +2 -2
- package/dist/server/modules/tasks/task-lane-executors.js.map +1 -1
- package/dist/server/modules/tasks/task-types.d.ts +1 -0
- package/dist/server/modules/tasks/task-types.js +1 -0
- package/dist/server/modules/tasks/task-types.js.map +1 -1
- package/dist/server/modules/terminal/command-template-service.d.ts +2 -2
- package/dist/server/modules/terminal/command-template-service.js +4 -4
- package/dist/server/modules/terminal/command-template-service.js.map +1 -1
- package/dist/server/modules/terminal/runtime/terminal-log-writer-client.js +1 -1
- package/dist/server/modules/terminal/runtime/terminal-log-writer-client.js.map +1 -1
- package/dist/server/modules/terminal/runtime/terminal-log-writer-process.js +160 -11
- package/dist/server/modules/terminal/runtime/terminal-log-writer-process.js.map +1 -1
- package/dist/server/modules/terminal/template-port-runtime.d.ts +1 -1
- package/dist/server/modules/terminal/template-port-runtime.js +87 -37
- package/dist/server/modules/terminal/template-port-runtime.js.map +1 -1
- package/dist/server/modules/terminal/terminal-service.d.ts +4 -0
- package/dist/server/modules/terminal/terminal-service.js +35 -1
- package/dist/server/modules/terminal/terminal-service.js.map +1 -1
- package/dist/server/modules/workbench/workbench-service.js +3 -3
- package/dist/server/modules/workbench/workbench-service.js.map +1 -1
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +1 -0
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +118 -39
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
- package/dist/server/modules/workspace/workspace-code-composition.d.ts +1 -0
- package/dist/server/modules/workspace/workspace-code-composition.js +183 -1
- package/dist/server/modules/workspace/workspace-code-composition.js.map +1 -1
- package/dist/server/modules/workspace/workspace-service.js +54 -17
- package/dist/server/modules/workspace/workspace-service.js.map +1 -1
- package/dist/server/modules/worktree/worktree-cleanup-service.d.ts +1 -1
- package/dist/server/modules/worktree/worktree-cleanup-service.js +22 -17
- package/dist/server/modules/worktree/worktree-cleanup-service.js.map +1 -1
- package/dist/server/modules/worktree/worktree-controller.js +6 -5
- package/dist/server/modules/worktree/worktree-controller.js.map +1 -1
- package/dist/server/modules/worktree/worktree-manager.d.ts +1 -1
- package/dist/server/modules/worktree/worktree-manager.js +26 -19
- package/dist/server/modules/worktree/worktree-manager.js.map +1 -1
- package/dist/server/modules/worktree/worktree-merge-service.d.ts +2 -2
- package/dist/server/modules/worktree/worktree-merge-service.js +34 -27
- package/dist/server/modules/worktree/worktree-merge-service.js.map +1 -1
- package/dist/server/modules/worktree/worktree-sync-service.d.ts +1 -1
- package/dist/server/modules/worktree/worktree-sync-service.js +5 -3
- package/dist/server/modules/worktree/worktree-sync-service.js.map +1 -1
- package/dist/server/routes/assistant.js +24 -0
- package/dist/server/routes/assistant.js.map +1 -1
- package/dist/server/server/create-server.js +16 -1
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/shared/http/request-abort.d.ts +2 -0
- package/dist/server/shared/http/request-abort.js +38 -0
- package/dist/server/shared/http/request-abort.js.map +1 -0
- package/dist/server/ws/workbench-ws-hub.d.ts +1 -0
- package/dist/server/ws/workbench-ws-hub.js +25 -3
- package/dist/server/ws/workbench-ws-hub.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +33 -0
- package/dist/public/assets/index-Cek6u0b9.css +0 -1
- package/dist/public/assets/index-THHY79si.js +0 -122
|
@@ -14,13 +14,15 @@ import { SessionForkRepository } from "../../storage/repositories/session-fork-r
|
|
|
14
14
|
import { enrichClaudeCapabilities } from "../provider/claude-model-options.js";
|
|
15
15
|
import { CodexModelOptionsService, createFallbackCodexModelOptions, enrichCodexCapabilities } from "../provider/codex-model-options.js";
|
|
16
16
|
import { OpenCodeModelOptionsService, createFallbackOpenCodeModelOptions, enrichOpenCodeCapabilities } from "../provider/opencode-model-options.js";
|
|
17
|
-
import {
|
|
17
|
+
import { getSharedProviderDiscoveryHelperClient } from "../provider/provider-discovery-helper-client.js";
|
|
18
|
+
import { discoverWorkspaceSessionsInRuntime } from "../provider/provider-discovery-runtime.js";
|
|
18
19
|
import { createTaskManager } from "../tasks/task-manager.js";
|
|
19
20
|
import { HOST_TASK_TYPES } from "../tasks/task-types.js";
|
|
20
21
|
import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
|
|
21
22
|
const RECONSTRUCTED_FORK_TARGET_PROVIDERS = new Set(["codex", "claude-code", "opencode"]);
|
|
22
23
|
const FORK_RECONSTRUCTION_PAGE_SIZE = 200;
|
|
23
24
|
const MAX_FORK_DEPTH = 4;
|
|
25
|
+
const SYNTHETIC_CODEX_SESSION_CLEANUP_GRACE_MS = 120_000;
|
|
24
26
|
const SESSION_START_DEFERRED_PROVIDERS = new Set([
|
|
25
27
|
"codex",
|
|
26
28
|
"claude-code",
|
|
@@ -32,6 +34,7 @@ const MUTABLE_HISTORY_TAIL_REFRESH_INTERVAL_MS = 1_200;
|
|
|
32
34
|
const WORKSPACE_DISCOVERY_BACKGROUND_MAX_AGE_MS = 15_000;
|
|
33
35
|
const PROVIDER_CAPABILITY_CACHE_MAX_AGE_MS = 5_000;
|
|
34
36
|
const WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE = 25;
|
|
37
|
+
const SESSION_TRANSACTION_HOTSPOT_THRESHOLD_MS = 150;
|
|
35
38
|
export class SessionHistoryService {
|
|
36
39
|
db;
|
|
37
40
|
workspaceRepository;
|
|
@@ -52,7 +55,7 @@ export class SessionHistoryService {
|
|
|
52
55
|
openCodeModelOptionsService;
|
|
53
56
|
providerCliCommandPaths;
|
|
54
57
|
providerCliAvailability;
|
|
55
|
-
providerDiscoveryHelperClient =
|
|
58
|
+
providerDiscoveryHelperClient = getSharedProviderDiscoveryHelperClient();
|
|
56
59
|
providerSessionDiscoveryConfig;
|
|
57
60
|
taskManager;
|
|
58
61
|
workspaceDiscoveryStatuses = new Map();
|
|
@@ -148,8 +151,16 @@ export class SessionHistoryService {
|
|
|
148
151
|
if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscovery)) {
|
|
149
152
|
this.taskManager.register({
|
|
150
153
|
taskType: HOST_TASK_TYPES.workspaceDiscovery,
|
|
154
|
+
executionLane: "host_background",
|
|
155
|
+
run: async ({ workspaceId, userId, refreshStateMode }, context) => this.runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode, context.signal)
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscoveryScan)) {
|
|
159
|
+
this.taskManager.register({
|
|
160
|
+
taskType: HOST_TASK_TYPES.workspaceDiscoveryScan,
|
|
151
161
|
executionLane: "helper_process",
|
|
152
|
-
|
|
162
|
+
helperProcessHandler: "session.workspace_discovery",
|
|
163
|
+
run: async ({ config, workspacePath, knownSessions }, context) => await discoverWorkspaceSessionsInRuntime(config, workspacePath, knownSessions, context.signal)
|
|
153
164
|
});
|
|
154
165
|
}
|
|
155
166
|
if (!this.taskManager.has(HOST_TASK_TYPES.providerCapabilityRefresh)) {
|
|
@@ -233,7 +244,10 @@ export class SessionHistoryService {
|
|
|
233
244
|
async readSessionHistory(sessionId, cursor, limit, direction = "forward", userId) {
|
|
234
245
|
const startedAt = Date.now();
|
|
235
246
|
const resolvedSessionId = this.resolveCanonicalSessionId(sessionId, userId);
|
|
236
|
-
|
|
247
|
+
let binding = this.getBindingOrThrow(resolvedSessionId);
|
|
248
|
+
if (userId) {
|
|
249
|
+
binding = await this.repairCodexDirtyBindingBeforeHistoryRead(resolvedSessionId, userId, binding);
|
|
250
|
+
}
|
|
237
251
|
const current = this.sessionStatusSnapshotRepository.findBySessionId(resolvedSessionId);
|
|
238
252
|
const safeLimit = clampLimit(limit);
|
|
239
253
|
const knownTotalMessageCount = direction === "backward" && cursor === null
|
|
@@ -335,17 +349,17 @@ export class SessionHistoryService {
|
|
|
335
349
|
await this.refreshSessionState(sessionId, userId);
|
|
336
350
|
return this.enrichSessionItem(this.getSessionListItemOrThrow(sessionId, userId));
|
|
337
351
|
}
|
|
338
|
-
async syncSessionTitle(sessionId) {
|
|
352
|
+
async syncSessionTitle(sessionId, signal) {
|
|
339
353
|
const binding = this.getBindingOrThrow(sessionId);
|
|
340
|
-
await this.syncSessionTitleFromProvider(sessionId, binding);
|
|
354
|
+
await this.syncSessionTitleFromProvider(sessionId, binding, signal);
|
|
341
355
|
}
|
|
342
|
-
async syncWorkspaceSessionTitles(workspaceId, userId, concurrency = 1) {
|
|
356
|
+
async syncWorkspaceSessionTitles(workspaceId, userId, concurrency = 1, signal) {
|
|
343
357
|
const sessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
|
|
344
358
|
await runWithConcurrency(sessions, concurrency, async (session) => {
|
|
345
|
-
await this.syncSessionTitle(session.sessionId).catch(() => {
|
|
359
|
+
await this.syncSessionTitle(session.sessionId, signal).catch(() => {
|
|
346
360
|
return;
|
|
347
361
|
});
|
|
348
|
-
});
|
|
362
|
+
}, signal);
|
|
349
363
|
}
|
|
350
364
|
async listSessionChangedFiles(sessionId, userId) {
|
|
351
365
|
this.getSession(sessionId, userId);
|
|
@@ -1137,7 +1151,7 @@ export class SessionHistoryService {
|
|
|
1137
1151
|
}
|
|
1138
1152
|
}
|
|
1139
1153
|
}
|
|
1140
|
-
async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline") {
|
|
1154
|
+
async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline", signal) {
|
|
1141
1155
|
const startedAt = Date.now();
|
|
1142
1156
|
const debugStartedAtMs = terminalDebugNowMs();
|
|
1143
1157
|
const workspace = this.getWorkspaceOrThrow(workspaceId);
|
|
@@ -1158,13 +1172,16 @@ export class SessionHistoryService {
|
|
|
1158
1172
|
const discoverStartedAt = Date.now();
|
|
1159
1173
|
const existingWorkspaceSessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
|
|
1160
1174
|
const knownSessions = this.buildKnownSessionSummaries(existingWorkspaceSessions, workspace.path);
|
|
1161
|
-
const
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1175
|
+
const discoveryHandle = this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscoveryScan, {
|
|
1176
|
+
key: workspaceId,
|
|
1177
|
+
source: "session_history.workspace_discovery.scan",
|
|
1178
|
+
input: {
|
|
1179
|
+
config: this.providerSessionDiscoveryConfig,
|
|
1180
|
+
workspacePath: workspace.path,
|
|
1181
|
+
knownSessions
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
const discovery = await awaitTaskHandleWithSignal(discoveryHandle, signal).catch((error) => {
|
|
1168
1185
|
throw mapSessionProviderError(error);
|
|
1169
1186
|
});
|
|
1170
1187
|
const sessions = discovery.sessions;
|
|
@@ -1251,7 +1268,15 @@ export class SessionHistoryService {
|
|
|
1251
1268
|
}
|
|
1252
1269
|
});
|
|
1253
1270
|
const persistPass1StartedAt = Date.now();
|
|
1254
|
-
const persistPass1Stats = await runBatchedTransactions(sessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass1Transaction
|
|
1271
|
+
const persistPass1Stats = await runBatchedTransactions(sessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass1Transaction, {
|
|
1272
|
+
scope: "workspace.discover_sessions.persist_pass1.batch",
|
|
1273
|
+
thresholdMs: SESSION_TRANSACTION_HOTSPOT_THRESHOLD_MS,
|
|
1274
|
+
detail: {
|
|
1275
|
+
workspaceId,
|
|
1276
|
+
workspacePath: workspace.path,
|
|
1277
|
+
phase: "pass1"
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1255
1280
|
persistPass1DurationMs = Date.now() - persistPass1StartedAt;
|
|
1256
1281
|
persistPass1BatchCount = persistPass1Stats.batchCount;
|
|
1257
1282
|
persistPass1MaxBatchMs = persistPass1Stats.maxBatchMs;
|
|
@@ -1298,7 +1323,15 @@ export class SessionHistoryService {
|
|
|
1298
1323
|
}
|
|
1299
1324
|
});
|
|
1300
1325
|
const persistPass2StartedAt = Date.now();
|
|
1301
|
-
const persistPass2Stats = await runBatchedTransactions(persistedSessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass2Transaction
|
|
1326
|
+
const persistPass2Stats = await runBatchedTransactions(persistedSessions, WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, persistPass2Transaction, {
|
|
1327
|
+
scope: "workspace.discover_sessions.persist_pass2.batch",
|
|
1328
|
+
thresholdMs: SESSION_TRANSACTION_HOTSPOT_THRESHOLD_MS,
|
|
1329
|
+
detail: {
|
|
1330
|
+
workspaceId,
|
|
1331
|
+
workspacePath: workspace.path,
|
|
1332
|
+
phase: "pass2"
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1302
1335
|
persistPass2DurationMs = Date.now() - persistPass2StartedAt;
|
|
1303
1336
|
persistPass2BatchCount = persistPass2Stats.batchCount;
|
|
1304
1337
|
persistPass2MaxBatchMs = persistPass2Stats.maxBatchMs;
|
|
@@ -1677,7 +1710,7 @@ export class SessionHistoryService {
|
|
|
1677
1710
|
messages
|
|
1678
1711
|
});
|
|
1679
1712
|
}
|
|
1680
|
-
async syncSessionTitleFromProvider(sessionId, binding) {
|
|
1713
|
+
async syncSessionTitleFromProvider(sessionId, binding, signal) {
|
|
1681
1714
|
const currentIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
|
|
1682
1715
|
if (!currentIndex) {
|
|
1683
1716
|
return;
|
|
@@ -1690,7 +1723,7 @@ export class SessionHistoryService {
|
|
|
1690
1723
|
provider: binding.provider,
|
|
1691
1724
|
providerSessionId: binding.providerSessionId,
|
|
1692
1725
|
rawStoreRef: binding.rawStoreRef
|
|
1693
|
-
})).trim();
|
|
1726
|
+
}, signal)).trim();
|
|
1694
1727
|
const resolvedTitle = resolvePersistedSessionTitle(binding.provider, nextTitle, currentIndex.title);
|
|
1695
1728
|
if (resolvedTitle.length === 0 || resolvedTitle === currentIndex.title) {
|
|
1696
1729
|
return;
|
|
@@ -1925,6 +1958,7 @@ export class SessionHistoryService {
|
|
|
1925
1958
|
async cleanupStaleHiddenSessions(workspaceId, userId, sessions) {
|
|
1926
1959
|
const discoveredProviderSessionIds = new Set(sessions.map((session) => buildProviderSessionKey(session.provider, session.providerSessionId)));
|
|
1927
1960
|
const discoveredRawStoreRefs = new Set(sessions.map((session) => session.rawStoreRef));
|
|
1961
|
+
const nowMs = Date.now();
|
|
1928
1962
|
const staleHiddenSessions = this.sessionIndexRepository
|
|
1929
1963
|
.listByWorkspace(workspaceId, userId)
|
|
1930
1964
|
.filter((session) => {
|
|
@@ -1936,10 +1970,13 @@ export class SessionHistoryService {
|
|
|
1936
1970
|
}
|
|
1937
1971
|
return ((session.provider === "codex" &&
|
|
1938
1972
|
(isLegacyCodingNsRolloutSession(session.providerSessionId, session.rawStoreRef) ||
|
|
1939
|
-
shouldRemoveMissingSyntheticCodexSession(session.rawStoreRef)
|
|
1973
|
+
(shouldRemoveMissingSyntheticCodexSession(session.rawStoreRef) &&
|
|
1974
|
+
!this.shouldPreserveSyntheticCodexSession(session, nowMs)))) ||
|
|
1940
1975
|
(session.provider === "claude-code" && shouldRemoveHiddenClaudeDebugSession(session)));
|
|
1941
1976
|
});
|
|
1942
|
-
|
|
1977
|
+
const managedButlerSessionIds = this.listManagedButlerSessionIds(staleHiddenSessions.map((session) => session.sessionId));
|
|
1978
|
+
const deletableSessions = staleHiddenSessions.filter((session) => !managedButlerSessionIds.has(session.sessionId));
|
|
1979
|
+
if (deletableSessions.length === 0) {
|
|
1943
1980
|
return;
|
|
1944
1981
|
}
|
|
1945
1982
|
const deleteTransaction = this.db.transaction((ids) => {
|
|
@@ -1947,7 +1984,53 @@ export class SessionHistoryService {
|
|
|
1947
1984
|
this.deleteSessionById(sessionId);
|
|
1948
1985
|
}
|
|
1949
1986
|
});
|
|
1950
|
-
await runBatchedTransactions(
|
|
1987
|
+
await runBatchedTransactions(deletableSessions.map((session) => session.sessionId), WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, deleteTransaction, {
|
|
1988
|
+
scope: "workspace.discover_sessions.cleanup_hidden.batch",
|
|
1989
|
+
thresholdMs: SESSION_TRANSACTION_HOTSPOT_THRESHOLD_MS,
|
|
1990
|
+
detail: {
|
|
1991
|
+
workspaceId,
|
|
1992
|
+
userId,
|
|
1993
|
+
phase: "cleanup_hidden"
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
listManagedButlerSessionIds(sessionIds) {
|
|
1998
|
+
if (sessionIds.length === 0) {
|
|
1999
|
+
return new Set();
|
|
2000
|
+
}
|
|
2001
|
+
const managedSessionIds = new Set();
|
|
2002
|
+
for (let index = 0; index < sessionIds.length; index += WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE) {
|
|
2003
|
+
const batch = sessionIds.slice(index, index + WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE);
|
|
2004
|
+
const placeholders = batch.map(() => "?").join(", ");
|
|
2005
|
+
const rows = this.db
|
|
2006
|
+
.prepare(`SELECT session_id
|
|
2007
|
+
FROM butler_sessions
|
|
2008
|
+
WHERE session_id IN (${placeholders})`)
|
|
2009
|
+
.all(...batch);
|
|
2010
|
+
for (const row of rows) {
|
|
2011
|
+
managedSessionIds.add(row.session_id);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
return managedSessionIds;
|
|
2015
|
+
}
|
|
2016
|
+
shouldPreserveSyntheticCodexSession(session, nowMs) {
|
|
2017
|
+
if (session.activitySource === "runtime"
|
|
2018
|
+
|| session.runningState === "starting"
|
|
2019
|
+
|| session.runningState === "running") {
|
|
2020
|
+
return true;
|
|
2021
|
+
}
|
|
2022
|
+
const hasActiveRuntimeState = this.listSessionStatesBySessionId(session.sessionId).some((state) => state.activitySource === "runtime"
|
|
2023
|
+
|| state.runningState === "starting"
|
|
2024
|
+
|| state.runningState === "running");
|
|
2025
|
+
if (hasActiveRuntimeState) {
|
|
2026
|
+
return true;
|
|
2027
|
+
}
|
|
2028
|
+
const latestTouchedAt = pickLaterIso(session.updatedAt, session.createdAt) ?? session.updatedAt;
|
|
2029
|
+
const latestTouchedAtMs = Date.parse(latestTouchedAt);
|
|
2030
|
+
if (!Number.isFinite(latestTouchedAtMs)) {
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
return nowMs - latestTouchedAtMs <= SYNTHETIC_CODEX_SESSION_CLEANUP_GRACE_MS;
|
|
1951
2034
|
}
|
|
1952
2035
|
findSameWorkspaceBindingDuplicate(sessionId, workspaceId, snapshot) {
|
|
1953
2036
|
if (isPendingBindingValue(snapshot.providerSessionId)) {
|
|
@@ -2282,6 +2365,18 @@ export class SessionHistoryService {
|
|
|
2282
2365
|
});
|
|
2283
2366
|
return nextRecord;
|
|
2284
2367
|
}
|
|
2368
|
+
async repairCodexDirtyBindingBeforeHistoryRead(sessionId, userId, binding) {
|
|
2369
|
+
if (!shouldRepairCodexDirtyBinding(binding)) {
|
|
2370
|
+
return binding;
|
|
2371
|
+
}
|
|
2372
|
+
await this.discoverWorkspaceSessions(binding.workspaceId, userId, {
|
|
2373
|
+
force: true,
|
|
2374
|
+
refreshStateMode: "deferred"
|
|
2375
|
+
}).catch(() => {
|
|
2376
|
+
return [];
|
|
2377
|
+
});
|
|
2378
|
+
return this.getBindingOrThrow(sessionId);
|
|
2379
|
+
}
|
|
2285
2380
|
resolveLiveActivityObservation(sessionId) {
|
|
2286
2381
|
for (const resolver of this.liveActivityObservationResolvers) {
|
|
2287
2382
|
const observation = resolver(sessionId);
|
|
@@ -2945,6 +3040,48 @@ function isLegacyCodingNsRolloutSession(providerSessionId, rawStoreRef) {
|
|
|
2945
3040
|
function shouldRemoveMissingSyntheticCodexSession(rawStoreRef) {
|
|
2946
3041
|
return isSyntheticCodexRawStoreRef(rawStoreRef) && !existsSync(rawStoreRef);
|
|
2947
3042
|
}
|
|
3043
|
+
function shouldRepairCodexDirtyBinding(binding) {
|
|
3044
|
+
if (binding.provider !== "codex") {
|
|
3045
|
+
return false;
|
|
3046
|
+
}
|
|
3047
|
+
if (isSyntheticCodexRawStoreRef(binding.rawStoreRef)) {
|
|
3048
|
+
return false;
|
|
3049
|
+
}
|
|
3050
|
+
const expectedThreadId = binding.providerSessionId.trim();
|
|
3051
|
+
if (!expectedThreadId) {
|
|
3052
|
+
return false;
|
|
3053
|
+
}
|
|
3054
|
+
const boundThreadId = readCodexThreadIdFromRawStore(binding.rawStoreRef);
|
|
3055
|
+
if (boundThreadId) {
|
|
3056
|
+
return boundThreadId !== expectedThreadId;
|
|
3057
|
+
}
|
|
3058
|
+
return !existsSync(binding.rawStoreRef);
|
|
3059
|
+
}
|
|
3060
|
+
function readCodexThreadIdFromRawStore(filePath) {
|
|
3061
|
+
if (!existsSync(filePath)) {
|
|
3062
|
+
return null;
|
|
3063
|
+
}
|
|
3064
|
+
try {
|
|
3065
|
+
const firstLine = readFileSync(filePath, "utf8")
|
|
3066
|
+
.split(/\r?\n/, 1)
|
|
3067
|
+
.at(0)
|
|
3068
|
+
?.trim();
|
|
3069
|
+
if (!firstLine) {
|
|
3070
|
+
return null;
|
|
3071
|
+
}
|
|
3072
|
+
const record = JSON.parse(firstLine);
|
|
3073
|
+
if (record.type !== "session_meta") {
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
const threadId = typeof record.payload?.id === "string"
|
|
3077
|
+
? record.payload.id.trim()
|
|
3078
|
+
: "";
|
|
3079
|
+
return threadId.length > 0 ? threadId : null;
|
|
3080
|
+
}
|
|
3081
|
+
catch {
|
|
3082
|
+
return null;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
2948
3085
|
function shouldMatchSessionBindingByRawStoreRef(provider) {
|
|
2949
3086
|
return provider !== "codex";
|
|
2950
3087
|
}
|
|
@@ -3134,13 +3271,14 @@ function buildReconstructedForkPrompt(input) {
|
|
|
3134
3271
|
function buildProviderCapabilityCacheKey(provider, workspacePath) {
|
|
3135
3272
|
return `${provider}::${workspacePath ?? ""}`;
|
|
3136
3273
|
}
|
|
3137
|
-
async function runWithConcurrency(items, concurrency, worker) {
|
|
3274
|
+
async function runWithConcurrency(items, concurrency, worker, signal) {
|
|
3138
3275
|
const normalizedConcurrency = Math.max(1, Math.floor(concurrency) || 1);
|
|
3139
3276
|
const queue = [...items];
|
|
3140
3277
|
const runners = Array.from({
|
|
3141
3278
|
length: Math.min(normalizedConcurrency, queue.length || 1)
|
|
3142
3279
|
}, async () => {
|
|
3143
3280
|
while (queue.length > 0) {
|
|
3281
|
+
throwIfAborted(signal);
|
|
3144
3282
|
const current = queue.shift();
|
|
3145
3283
|
if (current === undefined) {
|
|
3146
3284
|
return;
|
|
@@ -3150,7 +3288,44 @@ async function runWithConcurrency(items, concurrency, worker) {
|
|
|
3150
3288
|
});
|
|
3151
3289
|
await Promise.all(runners);
|
|
3152
3290
|
}
|
|
3153
|
-
|
|
3291
|
+
function throwIfAborted(signal) {
|
|
3292
|
+
if (signal?.aborted) {
|
|
3293
|
+
throw signal.reason ?? new Error("任务已取消");
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
async function awaitTaskHandleWithSignal(handle, signal) {
|
|
3297
|
+
if (!signal) {
|
|
3298
|
+
return await handle.promise;
|
|
3299
|
+
}
|
|
3300
|
+
if (signal.aborted) {
|
|
3301
|
+
handle.cancel(getAbortMessage(signal.reason));
|
|
3302
|
+
throw signal.reason ?? new Error("任务已取消");
|
|
3303
|
+
}
|
|
3304
|
+
return await new Promise((resolve, reject) => {
|
|
3305
|
+
const onAbort = () => {
|
|
3306
|
+
handle.cancel(getAbortMessage(signal.reason));
|
|
3307
|
+
reject(signal.reason ?? new Error("任务已取消"));
|
|
3308
|
+
};
|
|
3309
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3310
|
+
handle.promise.then((value) => {
|
|
3311
|
+
signal.removeEventListener("abort", onAbort);
|
|
3312
|
+
resolve(value);
|
|
3313
|
+
}, (error) => {
|
|
3314
|
+
signal.removeEventListener("abort", onAbort);
|
|
3315
|
+
reject(error);
|
|
3316
|
+
});
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
function getAbortMessage(reason) {
|
|
3320
|
+
if (reason instanceof Error && reason.message.trim().length > 0) {
|
|
3321
|
+
return reason.message;
|
|
3322
|
+
}
|
|
3323
|
+
if (typeof reason === "string" && reason.trim().length > 0) {
|
|
3324
|
+
return reason;
|
|
3325
|
+
}
|
|
3326
|
+
return "任务已取消";
|
|
3327
|
+
}
|
|
3328
|
+
async function runBatchedTransactions(items, batchSize, transaction, logOptions) {
|
|
3154
3329
|
const normalizedBatchSize = Math.max(1, Math.floor(batchSize) || 1);
|
|
3155
3330
|
let batchCount = 0;
|
|
3156
3331
|
let maxBatchMs = 0;
|
|
@@ -3159,6 +3334,19 @@ async function runBatchedTransactions(items, batchSize, transaction) {
|
|
|
3159
3334
|
const batchStartedAt = Date.now();
|
|
3160
3335
|
transaction(batch);
|
|
3161
3336
|
const batchDurationMs = Date.now() - batchStartedAt;
|
|
3337
|
+
const nextBatchIndex = batchCount + 1;
|
|
3338
|
+
if (logOptions) {
|
|
3339
|
+
logPerformance(logOptions.scope, batchDurationMs, {
|
|
3340
|
+
...logOptions.detail,
|
|
3341
|
+
batchIndex: nextBatchIndex,
|
|
3342
|
+
batchSize: batch.length,
|
|
3343
|
+
batchStartIndex: index,
|
|
3344
|
+
totalItems: items.length,
|
|
3345
|
+
configuredBatchSize: normalizedBatchSize
|
|
3346
|
+
}, {
|
|
3347
|
+
thresholdMs: logOptions.thresholdMs ?? SESSION_TRANSACTION_HOTSPOT_THRESHOLD_MS
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3162
3350
|
batchCount += 1;
|
|
3163
3351
|
maxBatchMs = Math.max(maxBatchMs, batchDurationMs);
|
|
3164
3352
|
if (index + normalizedBatchSize < items.length) {
|