@jingyi0605/codingns 0.9.8 → 0.9.9
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/{AdaptiveButlerPage-D-gXre7Y.js → AdaptiveButlerPage-CqBM1XiC.js} +2 -2
- package/dist/public/assets/App-Pe30N_Dy.js +30 -0
- package/dist/public/assets/{BootstrapPage-B-yMdfpQ.js → BootstrapPage-D9ai1XhC.js} +1 -1
- package/dist/public/assets/ConversationPage-C8aU99KN.js +9 -0
- package/dist/public/assets/{DesktopDetachPreviewPage-D1DMaGcy.js → DesktopDetachPreviewPage-BJVPVFf2.js} +1 -1
- package/dist/public/assets/{DesktopModal-BnfGW2gk.js → DesktopModal-DyC_e0eF.js} +1 -1
- package/dist/public/assets/DesktopWindowPage-BZleww0T.js +2 -0
- package/dist/public/assets/FileContextPanel-BklQRQXj.js +1 -0
- package/dist/public/assets/GitSidebar-D0tb3W87.js +6 -0
- package/dist/public/assets/MobileCreateSessionSheet-BS881Agk.js +1 -0
- package/dist/public/assets/{MobileSheet-rkn_CUOY.js → MobileSheet-C32hwIFd.js} +1 -1
- package/dist/public/assets/PluginAccessOverview-BDiz6fV8.js +1 -0
- package/dist/public/assets/{PluginContainerPage-D-ly3i3H.js → PluginContainerPage-BMT6J0YS.js} +1 -1
- package/dist/public/assets/{PluginDetailPage-CWAHYyyG.js → PluginDetailPage-ztNhvCWP.js} +1 -1
- package/dist/public/assets/{PluginsListPage-Cte3vBgR.js → PluginsListPage-Cuk2-U6E.js} +1 -1
- package/dist/public/assets/PureConversationPage-ShluI2En.js +1 -0
- package/dist/public/assets/{RelayConnectEntryPage-sRJlstx9.js → RelayConnectEntryPage-CMbjlD0k.js} +1 -1
- package/dist/public/assets/{ServerSettingsModal-BBft9KEC.js → ServerSettingsModal-CLSWyHu6.js} +1 -1
- package/dist/public/assets/SessionIndexPage-dqfauVAj.js +1 -0
- package/dist/public/assets/SettingsPage-PUga-wwJ.js +2 -0
- package/dist/public/assets/TerminalManagerPanel-75khWhRz.js +1 -0
- package/dist/public/assets/ToolFilesPage-S3eWxO_D.js +1 -0
- package/dist/public/assets/ToolGitPage-8RAMpope.js +1 -0
- package/dist/public/assets/ToolProcessesPage-TZACKEoH.js +1 -0
- package/dist/public/assets/{ToolsHomePage-D1n4FU1s.js → ToolsHomePage-Whgu6Tyf.js} +1 -1
- package/dist/public/assets/{WorkbenchLandingPage-BaU_dXls.js → WorkbenchLandingPage-CC24vHza.js} +1 -1
- package/dist/public/assets/{WorkbenchLayout-BksVkkFF.css → WorkbenchLayout-BZdfVest.css} +32 -1
- package/dist/public/assets/WorkbenchLayout-CoYxMzzR.js +1081 -0
- package/dist/public/assets/{WorkbenchModal-DWsNm2B2.js → WorkbenchModal-2PaCY_Gj.js} +1 -1
- package/dist/public/assets/WorkbenchShellRoute-DLVRpGzb.css +1 -0
- package/dist/public/assets/WorkbenchShellRoute-QXztW_Ny.js +1 -0
- package/dist/public/assets/WorkspaceDebugDetailPage-Ce_oMmJ8.js +1 -0
- package/dist/public/assets/WorkspaceDetailPage-aX6w4Q7S.js +1 -0
- package/dist/public/assets/WorkspaceHomePage-DQttgj2w.js +1 -0
- package/dist/public/assets/{client-runtime-manager-D9VbgJZ_.js → client-runtime-manager-CGVLChqs.js} +1 -1
- package/dist/public/assets/host-alias-CeDJeL0T.js +1 -0
- package/dist/public/assets/index-BehUkul4.css +1 -0
- package/dist/public/assets/index-Bp1FnRo_.js +50 -0
- package/dist/public/assets/{login-direct-candidate-resolver-17wEvjhh.js → login-direct-candidate-resolver-Bvs5uPix.js} +1 -1
- package/dist/public/assets/peer-host-config-sync-oelf2T0_.js +1 -0
- package/dist/public/assets/{plugin-permission-copy-apDn8EWG.js → plugin-permission-copy-Do028HzJ.js} +1 -1
- package/dist/public/assets/{plugins-api-CnZYRKoS.js → plugins-api-BOugklJ-.js} +1 -1
- package/dist/public/assets/{preferences-service-PZlLLAWH.js → preferences-service-Dys1mbBy.js} +1 -1
- package/dist/public/assets/{relay-entry-DhHwflXl.js → relay-entry-B6UDc5cD.js} +1 -1
- package/dist/public/assets/useRegisteredDebugTemplates-BafVuBvE.js +1 -0
- package/dist/public/assets/workbench-navigation-Bs4OQYD3.js +1 -0
- package/dist/public/index.html +2 -2
- package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +3 -0
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js +9 -0
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +3 -0
- package/dist/server/modules/assistant-capability/assistant-capability-service.js +4 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +10 -0
- package/dist/server/modules/sessions/session-controller.js +11 -0
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +42 -1
- package/dist/server/modules/sessions/session-history-service.js +356 -25
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/tasks/observability-controller.d.ts +2 -0
- package/dist/server/modules/tasks/observability-controller.js +16 -1
- package/dist/server/modules/tasks/observability-controller.js.map +1 -1
- package/dist/server/modules/tasks/observability-service.d.ts +12 -2
- package/dist/server/modules/tasks/observability-service.js +16 -3
- package/dist/server/modules/tasks/observability-service.js.map +1 -1
- package/dist/server/modules/workbench/workbench-service.d.ts +2 -0
- package/dist/server/modules/workbench/workbench-service.js +162 -19
- package/dist/server/modules/workbench/workbench-service.js.map +1 -1
- package/dist/server/modules/workspace/affairs-library-controller.d.ts +1 -0
- package/dist/server/modules/workspace/affairs-library-controller.js +3 -0
- package/dist/server/modules/workspace/affairs-library-controller.js.map +1 -1
- package/dist/server/modules/workspace/affairs-library-service.d.ts +4 -0
- package/dist/server/modules/workspace/affairs-library-service.js +23 -4
- package/dist/server/modules/workspace/affairs-library-service.js.map +1 -1
- package/dist/server/modules/workspace/workspace-controller.d.ts +7 -1
- package/dist/server/modules/workspace/workspace-controller.js +11 -1
- package/dist/server/modules/workspace/workspace-controller.js.map +1 -1
- package/dist/server/modules/workspace/workspace-service.d.ts +9 -2
- package/dist/server/modules/workspace/workspace-service.js +45 -10
- package/dist/server/modules/workspace/workspace-service.js.map +1 -1
- package/dist/server/routes/affairs.js +4 -0
- package/dist/server/routes/affairs.js.map +1 -1
- package/dist/server/routes/sessions.js +2 -0
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/server/create-server.js +1 -1
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/storage/repositories/session-discovery-diagnostics-repository.d.ts +10 -0
- package/dist/server/storage/repositories/session-discovery-diagnostics-repository.js +68 -0
- package/dist/server/storage/repositories/session-discovery-diagnostics-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-source-index-repository.d.ts +14 -0
- package/dist/server/storage/repositories/session-source-index-repository.js +164 -0
- package/dist/server/storage/repositories/session-source-index-repository.js.map +1 -0
- package/dist/server/storage/repositories/workspace-navigation-state-repository.js +16 -7
- package/dist/server/storage/repositories/workspace-navigation-state-repository.js.map +1 -1
- package/dist/server/storage/sqlite/client.js +26 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +57 -0
- package/dist/server/types/domain.d.ts +45 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +19 -2
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +45 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +11 -4
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
- package/package.json +1 -1
- package/dist/public/assets/App-Dl-mcdqy.js +0 -30
- package/dist/public/assets/ConversationPage-DRQ5Sg_d.js +0 -9
- package/dist/public/assets/DesktopWindowPage-2SWAi0xz.js +0 -2
- package/dist/public/assets/FileContextPanel-fbPuE9dO.js +0 -1
- package/dist/public/assets/GitSidebar-BkmesJJR.js +0 -6
- package/dist/public/assets/MobileCreateSessionSheet-CEJcDBZJ.js +0 -1
- package/dist/public/assets/MobileTopHeaderFrame-CU0wsYSS.js +0 -1
- package/dist/public/assets/MobileWorkspaceSwitcherHeader-idl8o1OB.js +0 -1
- package/dist/public/assets/PluginAccessOverview-BBgM6tb0.js +0 -1
- package/dist/public/assets/SessionIndexPage-CN7cEdl9.js +0 -1
- package/dist/public/assets/SettingsPage-BGT-YqG2.js +0 -2
- package/dist/public/assets/TerminalManagerPanel-6-ZJ8vGn.js +0 -1
- package/dist/public/assets/TerminalPage-6GBZ9nXN.css +0 -32
- package/dist/public/assets/TerminalPage-CUXXQYU2.js +0 -55
- package/dist/public/assets/TerminalRuntimeFallbackModal-zc3qqMKJ.js +0 -1
- package/dist/public/assets/ToolFilesPage-QzsZyr0F.js +0 -1
- package/dist/public/assets/ToolGitPage-CXg4ncuT.js +0 -1
- package/dist/public/assets/ToolProcessesPage-BPsOsg4w.js +0 -1
- package/dist/public/assets/WorkbenchLayout-DViAJhHz.js +0 -1027
- package/dist/public/assets/WorkbenchShellRoute-BGfRqBUa.js +0 -1
- package/dist/public/assets/WorkbenchShellRoute-f2jWjHWu.css +0 -1
- package/dist/public/assets/WorkspaceDebugDetailPage-BX0zVSsI.js +0 -1
- package/dist/public/assets/WorkspaceDetailPage-Dx6JX4jx.js +0 -1
- package/dist/public/assets/WorkspaceHomePage-DQVJ042Z.js +0 -1
- package/dist/public/assets/host-alias-0TfFnYxR.js +0 -1
- package/dist/public/assets/index-DREvg1Yu.css +0 -1
- package/dist/public/assets/index-FOhyOpGY.js +0 -50
- package/dist/public/assets/peer-host-config-sync-vYkmqzNz.js +0 -1
- package/dist/public/assets/terminal-runtime-meta-2zvacxvM.js +0 -1
- package/dist/public/assets/useRegisteredDebugTemplates-CJ-o4tFl.js +0 -1
- package/dist/public/assets/workbench-navigation-aqJ1ay4M.js +0 -1
|
@@ -7,6 +7,8 @@ import { createId } from "../../shared/utils/id.js";
|
|
|
7
7
|
import { logPerformance } from "../../shared/utils/perf-log.js";
|
|
8
8
|
import { isTerminalDebugEnabled, logTerminalDebug, terminalDebugNowMs } from "../../shared/utils/terminal-debug-log.js";
|
|
9
9
|
import { nowIso } from "../../shared/utils/time.js";
|
|
10
|
+
import { SessionDiscoveryDiagnosticsRepository } from "../../storage/repositories/session-discovery-diagnostics-repository.js";
|
|
11
|
+
import { SessionSourceIndexRepository } from "../../storage/repositories/session-source-index-repository.js";
|
|
10
12
|
import { inspectSessionActivity } from "./session-activity-inspector.js";
|
|
11
13
|
import { SessionActivityAuthorityService } from "./session-activity-authority-service.js";
|
|
12
14
|
import { buildSessionTitleFromContent, normalizeRuntimePromptTitle } from "./session-title-utils.js";
|
|
@@ -72,6 +74,8 @@ export class SessionHistoryService {
|
|
|
72
74
|
capabilityService;
|
|
73
75
|
sessionActivityAuthorityService;
|
|
74
76
|
sessionForkRepository;
|
|
77
|
+
sessionSourceIndexRepository;
|
|
78
|
+
sessionDiscoveryDiagnosticsRepository;
|
|
75
79
|
providerSessionDeleteCli;
|
|
76
80
|
claudeCodeHomeDir;
|
|
77
81
|
codexModelOptionsService;
|
|
@@ -87,6 +91,7 @@ export class SessionHistoryService {
|
|
|
87
91
|
providerControlRepository;
|
|
88
92
|
taskManager;
|
|
89
93
|
workspaceDiscoveryStatuses = new Map();
|
|
94
|
+
sessionSourceIndexRepairScopes = new Map();
|
|
90
95
|
workspaceStateRefreshStatuses = new Map();
|
|
91
96
|
providerCapabilityCache = new Map();
|
|
92
97
|
codexDirtyBindingRepairStates = new Map();
|
|
@@ -108,6 +113,8 @@ export class SessionHistoryService {
|
|
|
108
113
|
this.sessionMessageOriginRepository = sessionMessageOriginRepository;
|
|
109
114
|
this.sessionActivityAuthorityService = sessionActivityAuthorityService;
|
|
110
115
|
this.sessionForkRepository = sessionForkRepository ?? new SessionForkRepository(db);
|
|
116
|
+
this.sessionSourceIndexRepository = new SessionSourceIndexRepository(db);
|
|
117
|
+
this.sessionDiscoveryDiagnosticsRepository = new SessionDiscoveryDiagnosticsRepository(db);
|
|
111
118
|
this.providerSessionDeleteCli =
|
|
112
119
|
adapterOverrides.providerSessionDeleteCli ?? new CodingnsProviderSessionDeleteCli(config);
|
|
113
120
|
this.taskManager = taskManager;
|
|
@@ -185,6 +192,73 @@ export class SessionHistoryService {
|
|
|
185
192
|
observeBackgroundTaskMetrics() {
|
|
186
193
|
return this.taskManager.observe();
|
|
187
194
|
}
|
|
195
|
+
listWorkspaceDiscoveryDiagnostics(workspaceId, userId, limit = 20) {
|
|
196
|
+
this.getDiscoverableWorkspaceForUserOrThrow(workspaceId, userId);
|
|
197
|
+
const normalizedLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 20;
|
|
198
|
+
return this.sessionDiscoveryDiagnosticsRepository.listByWorkspaceId(workspaceId, normalizedLimit);
|
|
199
|
+
}
|
|
200
|
+
getWorkspaceDiscoveryStatusSummary(workspaceId) {
|
|
201
|
+
const status = this.workspaceDiscoveryStatuses.get(workspaceId);
|
|
202
|
+
if (!status) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
phase: status.phase,
|
|
207
|
+
dirtyReasonCount: status.dirtyReasons.size,
|
|
208
|
+
isComplete: status.isComplete,
|
|
209
|
+
lastRequestedAt: status.lastRequestedAt,
|
|
210
|
+
lastStartedAt: status.lastStartedAt,
|
|
211
|
+
lastCompletedAt: status.lastCompletedAt,
|
|
212
|
+
lastFailedAt: status.lastFailedAt,
|
|
213
|
+
nextAllowedAt: status.nextAllowedAt
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
async repairSessionSourceIndex(input) {
|
|
217
|
+
const workspaceId = input.workspaceId.trim();
|
|
218
|
+
const userId = input.userId.trim();
|
|
219
|
+
this.getDiscoverableWorkspaceForUserOrThrow(workspaceId, userId);
|
|
220
|
+
const provider = normalizeOptionalText(input.provider);
|
|
221
|
+
if (provider && !this.providerRegistry.list().some((adapter) => adapter.providerId === provider)) {
|
|
222
|
+
throw new AppError({
|
|
223
|
+
statusCode: 400,
|
|
224
|
+
errorCode: "INVALID_INPUT",
|
|
225
|
+
detail: "provider 不存在或当前不受支持",
|
|
226
|
+
field: "provider"
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const sourceKeys = normalizeDistinctTexts(input.sourceKeys);
|
|
230
|
+
const rawStoreRefs = normalizeDistinctTexts(input.rawStoreRefs);
|
|
231
|
+
const existingRecords = this.sessionSourceIndexRepository.listByWorkspaceId(workspaceId);
|
|
232
|
+
const matchedRecords = existingRecords.filter((record) => matchesSessionSourceIndexRepairScope(record, provider, sourceKeys, rawStoreRefs));
|
|
233
|
+
const deletedSourceKeys = matchedRecords.map((record) => record.sourceKey);
|
|
234
|
+
const clearedSourceCount = this.sessionSourceIndexRepository.deleteBySourceKeys(deletedSourceKeys);
|
|
235
|
+
this.sessionSourceIndexRepairScopes.set(workspaceId, {
|
|
236
|
+
provider,
|
|
237
|
+
sourceKeys: new Set(sourceKeys),
|
|
238
|
+
rawStoreRefs: new Set(rawStoreRefs)
|
|
239
|
+
});
|
|
240
|
+
const awaitDiscovery = input.awaitDiscovery === true;
|
|
241
|
+
if (awaitDiscovery) {
|
|
242
|
+
await this.discoverWorkspaceSessions(workspaceId, userId, {
|
|
243
|
+
force: true,
|
|
244
|
+
refreshStateMode: "deferred"
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
this.requestWorkspaceDiscovery(workspaceId, userId, {
|
|
249
|
+
force: true,
|
|
250
|
+
refreshStateMode: "deferred"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
workspaceId,
|
|
255
|
+
provider,
|
|
256
|
+
sourceKeyCount: sourceKeys.length,
|
|
257
|
+
rawStoreRefCount: rawStoreRefs.length,
|
|
258
|
+
clearedSourceCount,
|
|
259
|
+
awaitDiscovery
|
|
260
|
+
};
|
|
261
|
+
}
|
|
188
262
|
registerLiveActivityObservationResolver(resolver) {
|
|
189
263
|
this.liveActivityObservationResolvers.add(resolver);
|
|
190
264
|
let closed = false;
|
|
@@ -219,7 +293,7 @@ export class SessionHistoryService {
|
|
|
219
293
|
this.taskManager.register({
|
|
220
294
|
taskType: HOST_TASK_TYPES.workspaceDiscovery,
|
|
221
295
|
executionLane: "host_background",
|
|
222
|
-
run: async ({ workspaceId, userId, refreshStateMode }, context) => this.runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode, context.signal)
|
|
296
|
+
run: async ({ workspaceId, userId, refreshStateMode }, context) => this.runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode, context.signal, context.taskId)
|
|
223
297
|
});
|
|
224
298
|
}
|
|
225
299
|
if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscoveryScan)) {
|
|
@@ -255,17 +329,14 @@ export class SessionHistoryService {
|
|
|
255
329
|
}
|
|
256
330
|
}
|
|
257
331
|
async discoverWorkspaceSessions(workspaceId, userId, options) {
|
|
332
|
+
this.getDiscoverableWorkspaceForUserOrThrow(workspaceId, userId);
|
|
333
|
+
this.markWorkspaceDiscoveryRequested(workspaceId, "session_history.discover_workspace_sessions");
|
|
258
334
|
const maxAgeMs = options?.maxAgeMs ?? 0;
|
|
259
335
|
const force = options?.force ?? false;
|
|
260
336
|
const discoveryStatus = this.workspaceDiscoveryStatuses.get(workspaceId);
|
|
261
|
-
const lastRefreshedAt = discoveryStatus?.refreshedAt ?? 0;
|
|
262
337
|
const now = Date.now();
|
|
263
|
-
const isPartialCoolingDown = discoveryStatus
|
|
264
|
-
|
|
265
|
-
now < discoveryStatus.partialCooldownUntil;
|
|
266
|
-
const isCompleteAndFresh = discoveryStatus?.isComplete === true &&
|
|
267
|
-
maxAgeMs > 0 &&
|
|
268
|
-
now - lastRefreshedAt <= maxAgeMs;
|
|
338
|
+
const isPartialCoolingDown = this.isWorkspaceDiscoveryPartialCoolingDown(discoveryStatus, now);
|
|
339
|
+
const isCompleteAndFresh = this.isWorkspaceDiscoveryCompleteAndFresh(discoveryStatus, maxAgeMs, now);
|
|
269
340
|
if (!force &&
|
|
270
341
|
discoveryStatus &&
|
|
271
342
|
(isPartialCoolingDown || isCompleteAndFresh)) {
|
|
@@ -285,6 +356,13 @@ export class SessionHistoryService {
|
|
|
285
356
|
requestWorkspaceDiscovery(workspaceId, userId, options) {
|
|
286
357
|
const maxAgeMs = options?.maxAgeMs ?? WORKSPACE_DISCOVERY_BACKGROUND_MAX_AGE_MS;
|
|
287
358
|
const force = options?.force ?? false;
|
|
359
|
+
if (!this.isWorkspaceDiscoverableForUser(workspaceId, userId)) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const discoveryStatus = this.markWorkspaceDiscoveryRequested(workspaceId, "session_history.request_workspace_discovery");
|
|
363
|
+
if (!force && discoveryStatus.phase === "running") {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
288
366
|
if (!force && !this.needsWorkspaceDiscovery(workspaceId, maxAgeMs)) {
|
|
289
367
|
return;
|
|
290
368
|
}
|
|
@@ -312,19 +390,27 @@ export class SessionHistoryService {
|
|
|
312
390
|
});
|
|
313
391
|
}
|
|
314
392
|
needsWorkspaceDiscovery(workspaceId, maxAgeMs) {
|
|
315
|
-
if (maxAgeMs <= 0) {
|
|
316
|
-
return true;
|
|
317
|
-
}
|
|
318
393
|
const discoveryStatus = this.workspaceDiscoveryStatuses.get(workspaceId);
|
|
319
394
|
if (!discoveryStatus) {
|
|
320
395
|
return true;
|
|
321
396
|
}
|
|
397
|
+
if (discoveryStatus.phase === "running") {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
if (discoveryStatus.phase === "failed") {
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
if (discoveryStatus.dirtyReasons.size > 0) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
322
406
|
if (!discoveryStatus.isComplete &&
|
|
323
|
-
(discoveryStatus.
|
|
324
|
-
Date.now() >= discoveryStatus.partialCooldownUntil)) {
|
|
407
|
+
!this.isWorkspaceDiscoveryPartialCoolingDown(discoveryStatus, Date.now())) {
|
|
325
408
|
return true;
|
|
326
409
|
}
|
|
327
|
-
|
|
410
|
+
if (maxAgeMs <= 0) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
return !this.isWorkspaceDiscoveryCompleteAndFresh(discoveryStatus, maxAgeMs, Date.now());
|
|
328
414
|
}
|
|
329
415
|
async readSessionHistory(sessionId, cursor, limit, direction = "forward", userId) {
|
|
330
416
|
const startedAt = Date.now();
|
|
@@ -1433,10 +1519,14 @@ export class SessionHistoryService {
|
|
|
1433
1519
|
}
|
|
1434
1520
|
}
|
|
1435
1521
|
}
|
|
1436
|
-
async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline", signal) {
|
|
1522
|
+
async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline", signal, taskId) {
|
|
1437
1523
|
const startedAt = Date.now();
|
|
1438
1524
|
const debugStartedAtMs = terminalDebugNowMs();
|
|
1439
|
-
const workspace = this.
|
|
1525
|
+
const workspace = this.getDiscoverableWorkspaceForUserOrThrow(workspaceId, userId);
|
|
1526
|
+
const discoveryStatus = this.getOrCreateWorkspaceDiscoveryStatus(workspaceId);
|
|
1527
|
+
discoveryStatus.phase = "running";
|
|
1528
|
+
discoveryStatus.lastStartedAt = startedAt;
|
|
1529
|
+
discoveryStatus.runningTaskId = taskId ?? `workspace.discovery:${workspaceId}:${startedAt}`;
|
|
1440
1530
|
let discoverDurationMs = 0;
|
|
1441
1531
|
let persistDurationMs = 0;
|
|
1442
1532
|
let persistPass1DurationMs = 0;
|
|
@@ -1450,14 +1540,16 @@ export class SessionHistoryService {
|
|
|
1450
1540
|
let listItemsDurationMs = 0;
|
|
1451
1541
|
let refreshStateDurationMs = 0;
|
|
1452
1542
|
const refreshStateCount = 10;
|
|
1543
|
+
const activeRepairScope = this.sessionSourceIndexRepairScopes.get(workspaceId) ?? null;
|
|
1453
1544
|
try {
|
|
1454
1545
|
const discoverStartedAt = Date.now();
|
|
1455
1546
|
const existingWorkspaceSessions = this.sessionIndexRepository.listByWorkspace(workspaceId, userId);
|
|
1547
|
+
const existingWorkspaceSourceIndexes = this.sessionSourceIndexRepository.listByWorkspaceId(workspaceId);
|
|
1456
1548
|
const enabledProviders = this.providerRegistry
|
|
1457
1549
|
.list()
|
|
1458
1550
|
.map((adapter) => adapter.providerId)
|
|
1459
1551
|
.filter((providerId) => this.isProviderEnabled(providerId));
|
|
1460
|
-
const knownSessions = this.buildKnownSessionSummaries(existingWorkspaceSessions.filter((session) => enabledProviders.includes(session.provider)), workspace.path);
|
|
1552
|
+
const knownSessions = this.buildKnownSessionSummaries(existingWorkspaceSessions.filter((session) => enabledProviders.includes(session.provider)), existingWorkspaceSourceIndexes.filter((record) => enabledProviders.includes(record.provider)), workspace.path, activeRepairScope);
|
|
1461
1553
|
const discoveryHandle = this.taskManager.enqueue(HOST_TASK_TYPES.workspaceDiscoveryScan, {
|
|
1462
1554
|
key: workspaceId,
|
|
1463
1555
|
source: "session_history.workspace_discovery.scan",
|
|
@@ -1474,6 +1566,7 @@ export class SessionHistoryService {
|
|
|
1474
1566
|
const sessions = discovery.sessions;
|
|
1475
1567
|
discoverDurationMs = Date.now() - discoverStartedAt;
|
|
1476
1568
|
const timestamp = nowIso();
|
|
1569
|
+
this.persistDiscoveryDiagnostics(workspaceId, "session_history.workspace_discovery.scan", discovery, timestamp);
|
|
1477
1570
|
const discoveredSessionIds = new Map();
|
|
1478
1571
|
const persistedSessions = [];
|
|
1479
1572
|
const claimedPendingSessionIds = new Set();
|
|
@@ -1642,6 +1735,7 @@ export class SessionHistoryService {
|
|
|
1642
1735
|
persistPass2DurationMs = Date.now() - persistPass2StartedAt;
|
|
1643
1736
|
persistPass2BatchCount = persistPass2Stats.batchCount;
|
|
1644
1737
|
persistPass2MaxBatchMs = persistPass2Stats.maxBatchMs;
|
|
1738
|
+
this.persistSessionSourceIndexRecords(workspaceId, workspace.path, sessions, existingWorkspaceSourceIndexes, timestamp);
|
|
1645
1739
|
persistDurationMs = persistPass1DurationMs + relationMapDurationMs + persistPass2DurationMs;
|
|
1646
1740
|
if (discovery.isComplete) {
|
|
1647
1741
|
const cleanupStartedAt = Date.now();
|
|
@@ -1655,11 +1749,19 @@ export class SessionHistoryService {
|
|
|
1655
1749
|
listItemsDurationMs = Date.now() - listItemsStartedAt;
|
|
1656
1750
|
const refreshCandidates = buildSessionStateRefreshCandidates(items, refreshStateCount);
|
|
1657
1751
|
this.workspaceDiscoveryStatuses.set(workspaceId, {
|
|
1752
|
+
...discoveryStatus,
|
|
1753
|
+
phase: discovery.isComplete ? "fresh" : "cooldown",
|
|
1754
|
+
dirtyReasons: new Set(),
|
|
1658
1755
|
refreshedAt: Date.now(),
|
|
1659
1756
|
isComplete: discovery.isComplete,
|
|
1660
1757
|
partialCooldownUntil: discovery.isComplete
|
|
1661
1758
|
? null
|
|
1662
|
-
: Date.now() + WORKSPACE_DISCOVERY_PARTIAL_COOLDOWN_MS
|
|
1759
|
+
: Date.now() + WORKSPACE_DISCOVERY_PARTIAL_COOLDOWN_MS,
|
|
1760
|
+
lastCompletedAt: Date.now(),
|
|
1761
|
+
nextAllowedAt: discovery.isComplete
|
|
1762
|
+
? null
|
|
1763
|
+
: Date.now() + WORKSPACE_DISCOVERY_PARTIAL_COOLDOWN_MS,
|
|
1764
|
+
runningTaskId: null
|
|
1663
1765
|
});
|
|
1664
1766
|
const refreshStateStartedAt = Date.now();
|
|
1665
1767
|
if (refreshStateMode === "inline") {
|
|
@@ -1734,6 +1836,13 @@ export class SessionHistoryService {
|
|
|
1734
1836
|
return nextItems;
|
|
1735
1837
|
}
|
|
1736
1838
|
catch (error) {
|
|
1839
|
+
const failedAt = Date.now();
|
|
1840
|
+
this.workspaceDiscoveryStatuses.set(workspaceId, {
|
|
1841
|
+
...discoveryStatus,
|
|
1842
|
+
phase: "failed",
|
|
1843
|
+
lastFailedAt: failedAt,
|
|
1844
|
+
runningTaskId: null
|
|
1845
|
+
});
|
|
1737
1846
|
logPerformance("workspace.discover_sessions.failed", Date.now() - startedAt, {
|
|
1738
1847
|
workspaceId,
|
|
1739
1848
|
workspacePath: workspace.path,
|
|
@@ -1757,6 +1866,11 @@ export class SessionHistoryService {
|
|
|
1757
1866
|
});
|
|
1758
1867
|
throw error;
|
|
1759
1868
|
}
|
|
1869
|
+
finally {
|
|
1870
|
+
if (activeRepairScope) {
|
|
1871
|
+
this.sessionSourceIndexRepairScopes.delete(workspaceId);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1760
1874
|
}
|
|
1761
1875
|
async readPage(sessionId, provider, providerSessionId, rawStoreRef, cursor, limit, direction = "forward", knownTotalMessageCount = null) {
|
|
1762
1876
|
if (shouldShortCircuitClaudePendingHistory(provider, providerSessionId, rawStoreRef)) {
|
|
@@ -2450,6 +2564,65 @@ export class SessionHistoryService {
|
|
|
2450
2564
|
}
|
|
2451
2565
|
return workspace;
|
|
2452
2566
|
}
|
|
2567
|
+
getOrCreateWorkspaceDiscoveryStatus(workspaceId) {
|
|
2568
|
+
const existing = this.workspaceDiscoveryStatuses.get(workspaceId);
|
|
2569
|
+
if (existing) {
|
|
2570
|
+
return existing;
|
|
2571
|
+
}
|
|
2572
|
+
const created = {
|
|
2573
|
+
phase: "fresh",
|
|
2574
|
+
dirtyReasons: new Set(),
|
|
2575
|
+
refreshedAt: 0,
|
|
2576
|
+
isComplete: false,
|
|
2577
|
+
partialCooldownUntil: null,
|
|
2578
|
+
lastRequestedAt: null,
|
|
2579
|
+
lastStartedAt: null,
|
|
2580
|
+
lastCompletedAt: null,
|
|
2581
|
+
lastFailedAt: null,
|
|
2582
|
+
nextAllowedAt: null,
|
|
2583
|
+
runningTaskId: null
|
|
2584
|
+
};
|
|
2585
|
+
this.workspaceDiscoveryStatuses.set(workspaceId, created);
|
|
2586
|
+
return created;
|
|
2587
|
+
}
|
|
2588
|
+
markWorkspaceDiscoveryRequested(workspaceId, reason) {
|
|
2589
|
+
const status = this.getOrCreateWorkspaceDiscoveryStatus(workspaceId);
|
|
2590
|
+
status.lastRequestedAt = Date.now();
|
|
2591
|
+
if (status.phase === "running") {
|
|
2592
|
+
status.dirtyReasons.add(reason);
|
|
2593
|
+
}
|
|
2594
|
+
return status;
|
|
2595
|
+
}
|
|
2596
|
+
isWorkspaceDiscoveryPartialCoolingDown(status, now) {
|
|
2597
|
+
return Boolean(status
|
|
2598
|
+
&& status.isComplete === false
|
|
2599
|
+
&& status.partialCooldownUntil !== null
|
|
2600
|
+
&& now < status.partialCooldownUntil);
|
|
2601
|
+
}
|
|
2602
|
+
isWorkspaceDiscoveryCompleteAndFresh(status, maxAgeMs, now) {
|
|
2603
|
+
if (!status || !status.isComplete || maxAgeMs <= 0) {
|
|
2604
|
+
return false;
|
|
2605
|
+
}
|
|
2606
|
+
return now - status.refreshedAt <= maxAgeMs;
|
|
2607
|
+
}
|
|
2608
|
+
isWorkspaceDiscoverableForUser(workspaceId, userId) {
|
|
2609
|
+
const workspace = this.workspaceRepository.findById(workspaceId);
|
|
2610
|
+
if (!workspace || workspace.removedAt || workspace.ownerUserId !== userId) {
|
|
2611
|
+
return false;
|
|
2612
|
+
}
|
|
2613
|
+
return true;
|
|
2614
|
+
}
|
|
2615
|
+
getDiscoverableWorkspaceForUserOrThrow(workspaceId, userId) {
|
|
2616
|
+
const workspace = this.getWorkspaceForUserOrThrow(workspaceId, userId);
|
|
2617
|
+
if (workspace.removedAt) {
|
|
2618
|
+
throw new AppError({
|
|
2619
|
+
statusCode: 404,
|
|
2620
|
+
errorCode: "WORKSPACE_NOT_FOUND",
|
|
2621
|
+
detail: "工作区不存在"
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
return workspace;
|
|
2625
|
+
}
|
|
2453
2626
|
getBindingForUserOrThrow(sessionId, userId) {
|
|
2454
2627
|
const binding = this.sessionBindingRepository.findBySessionIdForUser(sessionId, userId);
|
|
2455
2628
|
if (!binding) {
|
|
@@ -3189,24 +3362,121 @@ export class SessionHistoryService {
|
|
|
3189
3362
|
}
|
|
3190
3363
|
}
|
|
3191
3364
|
}
|
|
3192
|
-
buildKnownSessionSummaries(sessions, workspacePath) {
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
.
|
|
3196
|
-
|
|
3365
|
+
buildKnownSessionSummaries(sessions, sourceIndexes, workspacePath, repairScope = null) {
|
|
3366
|
+
const merged = new Map();
|
|
3367
|
+
for (const session of sessions) {
|
|
3368
|
+
if (this.isPendingSessionAlias(session) || shouldSkipClaudePendingBinding(session)) {
|
|
3369
|
+
continue;
|
|
3370
|
+
}
|
|
3371
|
+
const sourceKey = buildSessionSourceKey(session.provider, session.providerSessionId, session.rawStoreRef);
|
|
3372
|
+
if (shouldExcludeKnownSessionFromRepairScope(repairScope, session.provider, sourceKey, session.rawStoreRef)) {
|
|
3373
|
+
continue;
|
|
3374
|
+
}
|
|
3197
3375
|
const stats = safeStat(session.rawStoreRef);
|
|
3198
|
-
|
|
3376
|
+
const summary = {
|
|
3199
3377
|
provider: session.provider,
|
|
3200
3378
|
providerSessionId: session.providerSessionId,
|
|
3201
3379
|
title: session.title,
|
|
3202
3380
|
workspacePath,
|
|
3203
3381
|
rawStoreRef: session.rawStoreRef,
|
|
3382
|
+
isArchived: session.isArchived,
|
|
3204
3383
|
lastMessageAt: session.lastMessageAt,
|
|
3205
3384
|
messageCount: session.messageCount,
|
|
3206
3385
|
sourceMtimeMs: stats?.mtimeMs,
|
|
3207
3386
|
sourceSizeBytes: stats?.size
|
|
3208
3387
|
};
|
|
3209
|
-
|
|
3388
|
+
merged.set(buildKnownSessionSummaryKey(summary.provider, summary.providerSessionId, summary.rawStoreRef), summary);
|
|
3389
|
+
}
|
|
3390
|
+
for (const record of sourceIndexes) {
|
|
3391
|
+
if (record.deletedAt) {
|
|
3392
|
+
continue;
|
|
3393
|
+
}
|
|
3394
|
+
if (!record.providerSessionId || !record.rawStoreRef) {
|
|
3395
|
+
continue;
|
|
3396
|
+
}
|
|
3397
|
+
if (shouldExcludeKnownSessionFromRepairScope(repairScope, record.provider, record.sourceKey, record.rawStoreRef)) {
|
|
3398
|
+
continue;
|
|
3399
|
+
}
|
|
3400
|
+
const key = buildKnownSessionSummaryKey(record.provider, record.providerSessionId, record.rawStoreRef);
|
|
3401
|
+
if (merged.has(key)) {
|
|
3402
|
+
continue;
|
|
3403
|
+
}
|
|
3404
|
+
merged.set(key, {
|
|
3405
|
+
provider: record.provider,
|
|
3406
|
+
providerSessionId: record.providerSessionId,
|
|
3407
|
+
title: record.title?.trim() || record.providerSessionId,
|
|
3408
|
+
workspacePath: record.workspacePath?.trim() || workspacePath,
|
|
3409
|
+
rawStoreRef: record.rawStoreRef,
|
|
3410
|
+
isArchived: record.isArchivedHint ?? undefined,
|
|
3411
|
+
lastMessageAt: record.lastMessageAt,
|
|
3412
|
+
messageCount: Math.max(0, record.messageCount ?? 0),
|
|
3413
|
+
sourceMtimeMs: record.fingerprintMtimeMs ?? undefined,
|
|
3414
|
+
sourceSizeBytes: record.fingerprintSizeBytes ?? undefined
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
return [...merged.values()];
|
|
3418
|
+
}
|
|
3419
|
+
persistSessionSourceIndexRecords(workspaceId, workspacePath, sessions, existingSourceIndexes, timestamp) {
|
|
3420
|
+
const existingByKey = new Map(existingSourceIndexes.map((record) => [record.sourceKey, record]));
|
|
3421
|
+
for (const session of sessions) {
|
|
3422
|
+
const sourceKind = inferSessionSourceKind(session.rawStoreRef);
|
|
3423
|
+
const sourceKey = buildSessionSourceKey(session.provider, session.providerSessionId, session.rawStoreRef);
|
|
3424
|
+
const stats = safeStat(session.rawStoreRef);
|
|
3425
|
+
const existing = existingByKey.get(sourceKey);
|
|
3426
|
+
this.sessionSourceIndexRepository.upsert({
|
|
3427
|
+
sourceKey,
|
|
3428
|
+
provider: session.provider,
|
|
3429
|
+
sourceKind,
|
|
3430
|
+
workspaceId,
|
|
3431
|
+
providerSessionId: session.providerSessionId,
|
|
3432
|
+
rawStoreRef: session.rawStoreRef,
|
|
3433
|
+
workspacePath,
|
|
3434
|
+
fingerprintMtimeMs: stats?.mtimeMs ?? existing?.fingerprintMtimeMs ?? null,
|
|
3435
|
+
fingerprintSizeBytes: stats?.size ?? existing?.fingerprintSizeBytes ?? null,
|
|
3436
|
+
fingerprintInode: existing?.fingerprintInode ?? null,
|
|
3437
|
+
fingerprintVersion: existing?.fingerprintVersion ?? null,
|
|
3438
|
+
title: session.title,
|
|
3439
|
+
messageCount: session.messageCount,
|
|
3440
|
+
lastMessageAt: session.lastMessageAt,
|
|
3441
|
+
isArchivedHint: session.isArchived ?? existing?.isArchivedHint ?? null,
|
|
3442
|
+
lastParsedAt: timestamp,
|
|
3443
|
+
lastVerifiedAt: timestamp,
|
|
3444
|
+
sampleDueAt: existing?.sampleDueAt ?? null,
|
|
3445
|
+
deletedAt: null,
|
|
3446
|
+
createdAt: existing?.createdAt ?? timestamp,
|
|
3447
|
+
updatedAt: timestamp
|
|
3448
|
+
});
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
persistDiscoveryDiagnostics(workspaceId, triggerSource, discovery, timestamp) {
|
|
3452
|
+
try {
|
|
3453
|
+
for (const entry of discovery.providerDiagnostics ?? []) {
|
|
3454
|
+
const record = {
|
|
3455
|
+
id: createId(),
|
|
3456
|
+
workspaceId,
|
|
3457
|
+
triggerSource,
|
|
3458
|
+
provider: entry.provider,
|
|
3459
|
+
isComplete: entry.isComplete,
|
|
3460
|
+
status: entry.status,
|
|
3461
|
+
durationMs: Math.max(0, Math.round(entry.durationMs)),
|
|
3462
|
+
sessionCount: Math.max(0, entry.sessionCount),
|
|
3463
|
+
scannedFiles: Math.max(0, entry.scannedFiles ?? 0),
|
|
3464
|
+
skippedByFingerprint: Math.max(0, entry.skippedByMtimeSize ?? 0),
|
|
3465
|
+
parsedFiles: Math.max(0, entry.parsedFiles ?? 0),
|
|
3466
|
+
bytesRead: Math.max(0, entry.bytesRead ?? 0),
|
|
3467
|
+
createdAt: timestamp
|
|
3468
|
+
};
|
|
3469
|
+
this.sessionDiscoveryDiagnosticsRepository.insert(record);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
catch (error) {
|
|
3473
|
+
console.warn("[session-discovery-diagnostics-persist-failed]", {
|
|
3474
|
+
workspaceId,
|
|
3475
|
+
triggerSource,
|
|
3476
|
+
providerCount: discovery.providerDiagnostics?.length ?? 0,
|
|
3477
|
+
error
|
|
3478
|
+
});
|
|
3479
|
+
}
|
|
3210
3480
|
}
|
|
3211
3481
|
async refreshSessionState(sessionId, userId) {
|
|
3212
3482
|
const binding = this.getBindingOrThrow(sessionId);
|
|
@@ -3783,6 +4053,67 @@ function pickLaterIso(left, right) {
|
|
|
3783
4053
|
function buildProviderSessionKey(provider, providerSessionId) {
|
|
3784
4054
|
return `${provider}::${providerSessionId}`;
|
|
3785
4055
|
}
|
|
4056
|
+
function normalizeOptionalText(value) {
|
|
4057
|
+
const normalized = value?.trim();
|
|
4058
|
+
return normalized ? normalized : null;
|
|
4059
|
+
}
|
|
4060
|
+
function normalizeDistinctTexts(values) {
|
|
4061
|
+
if (!Array.isArray(values)) {
|
|
4062
|
+
return [];
|
|
4063
|
+
}
|
|
4064
|
+
const unique = new Set();
|
|
4065
|
+
for (const value of values) {
|
|
4066
|
+
const normalized = value?.trim();
|
|
4067
|
+
if (normalized) {
|
|
4068
|
+
unique.add(normalized);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
return [...unique];
|
|
4072
|
+
}
|
|
4073
|
+
function matchesSessionSourceIndexRepairScope(record, provider, sourceKeys, rawStoreRefs) {
|
|
4074
|
+
if (provider && record.provider !== provider) {
|
|
4075
|
+
return false;
|
|
4076
|
+
}
|
|
4077
|
+
if (sourceKeys.length === 0 && rawStoreRefs.length === 0) {
|
|
4078
|
+
return true;
|
|
4079
|
+
}
|
|
4080
|
+
return sourceKeys.includes(record.sourceKey)
|
|
4081
|
+
|| (record.rawStoreRef ? rawStoreRefs.includes(record.rawStoreRef) : false);
|
|
4082
|
+
}
|
|
4083
|
+
function shouldExcludeKnownSessionFromRepairScope(scope, provider, sourceKey, rawStoreRef) {
|
|
4084
|
+
if (!scope) {
|
|
4085
|
+
return false;
|
|
4086
|
+
}
|
|
4087
|
+
if (scope.provider && scope.provider !== provider) {
|
|
4088
|
+
return false;
|
|
4089
|
+
}
|
|
4090
|
+
if (scope.sourceKeys.size === 0 && scope.rawStoreRefs.size === 0) {
|
|
4091
|
+
return true;
|
|
4092
|
+
}
|
|
4093
|
+
return scope.sourceKeys.has(sourceKey) || scope.rawStoreRefs.has(rawStoreRef);
|
|
4094
|
+
}
|
|
4095
|
+
function buildKnownSessionSummaryKey(provider, providerSessionId, rawStoreRef) {
|
|
4096
|
+
return `${provider}::${providerSessionId}::${rawStoreRef}`;
|
|
4097
|
+
}
|
|
4098
|
+
function buildSessionSourceKey(provider, providerSessionId, rawStoreRef) {
|
|
4099
|
+
const normalizedRawStoreRef = rawStoreRef.trim();
|
|
4100
|
+
if (normalizedRawStoreRef.length > 0) {
|
|
4101
|
+
return `${provider}:raw:${normalizedRawStoreRef}`;
|
|
4102
|
+
}
|
|
4103
|
+
return `${provider}:session:${providerSessionId}`;
|
|
4104
|
+
}
|
|
4105
|
+
function inferSessionSourceKind(rawStoreRef) {
|
|
4106
|
+
if (rawStoreRef.endsWith(".jsonl")) {
|
|
4107
|
+
return "jsonl";
|
|
4108
|
+
}
|
|
4109
|
+
if (rawStoreRef.startsWith("opencode://") || rawStoreRef.startsWith("server://")) {
|
|
4110
|
+
return "server_session";
|
|
4111
|
+
}
|
|
4112
|
+
if (rawStoreRef.endsWith(".sqlite") || rawStoreRef.includes(".sqlite:")) {
|
|
4113
|
+
return "sqlite_row";
|
|
4114
|
+
}
|
|
4115
|
+
return "index_entry";
|
|
4116
|
+
}
|
|
3786
4117
|
function normalizeSessionBindingSnapshot(sessionId, snapshot) {
|
|
3787
4118
|
if (snapshot.provider !== "claude-code" ||
|
|
3788
4119
|
!(isPendingBindingValue(snapshot.providerSessionId) ||
|