@jingyi0605/codingns 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/codingns.mjs +489 -1
- package/dist/public/assets/{TerminalPage-D00S4KM6.js → TerminalPage-6jHZV9Mh.js} +17 -17
- package/dist/public/assets/index-CSVhg7I8.js +123 -0
- package/dist/public/assets/index-Ce1VX19m.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +173 -0
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js +307 -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 +199 -2
- package/dist/server/modules/assistant-capability/assistant-capability-service.js +565 -3
- package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
- package/dist/server/modules/butler/assistant-automation-service.d.ts +110 -0
- package/dist/server/modules/butler/assistant-automation-service.js +786 -0
- package/dist/server/modules/butler/assistant-automation-service.js.map +1 -0
- package/dist/server/modules/butler/assistant-automation-trigger.d.ts +94 -0
- package/dist/server/modules/butler/assistant-automation-trigger.js +400 -0
- package/dist/server/modules/butler/assistant-automation-trigger.js.map +1 -0
- package/dist/server/modules/butler/assistant-sandbox-service.d.ts +55 -0
- package/dist/server/modules/butler/assistant-sandbox-service.js +266 -0
- package/dist/server/modules/butler/assistant-sandbox-service.js.map +1 -0
- package/dist/server/modules/butler/butler-action-context-service.d.ts +4 -1
- package/dist/server/modules/butler/butler-action-context-service.js +8 -2
- package/dist/server/modules/butler/butler-action-context-service.js.map +1 -1
- package/dist/server/modules/butler/butler-control-session-service.d.ts +8 -1
- package/dist/server/modules/butler/butler-control-session-service.js +154 -40
- package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-control-timer-scheduler.d.ts +32 -0
- package/dist/server/modules/butler/butler-control-timer-scheduler.js +93 -0
- package/dist/server/modules/butler/butler-control-timer-scheduler.js.map +1 -0
- package/dist/server/modules/butler/butler-control-timer-service.d.ts +42 -0
- package/dist/server/modules/butler/butler-control-timer-service.js +132 -0
- package/dist/server/modules/butler/butler-control-timer-service.js.map +1 -0
- package/dist/server/modules/butler/butler-controller.d.ts +42 -2
- package/dist/server/modules/butler/butler-controller.js +79 -12
- package/dist/server/modules/butler/butler-controller.js.map +1 -1
- package/dist/server/modules/butler/butler-follow-up-service.d.ts +9 -1
- package/dist/server/modules/butler/butler-follow-up-service.js +273 -181
- package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
- package/dist/server/modules/butler/butler-inbox-analysis-service.d.ts +4 -1
- package/dist/server/modules/butler/butler-inbox-analysis-service.js +18 -4
- package/dist/server/modules/butler/butler-inbox-analysis-service.js.map +1 -1
- package/dist/server/modules/butler/butler-profile-service.js +2 -5
- package/dist/server/modules/butler/butler-profile-service.js.map +1 -1
- package/dist/server/modules/butler/butler-project-service.d.ts +3 -1
- package/dist/server/modules/butler/butler-project-service.js +7 -1
- package/dist/server/modules/butler/butler-project-service.js.map +1 -1
- package/dist/server/modules/butler/butler-session-service.d.ts +3 -1
- package/dist/server/modules/butler/butler-session-service.js +12 -1
- package/dist/server/modules/butler/butler-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-session-summary-service.js +2 -1
- package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
- package/dist/server/modules/butler/butler-workspace-context.d.ts +3 -0
- package/dist/server/modules/butler/butler-workspace-context.js +164 -44
- package/dist/server/modules/butler/butler-workspace-context.js.map +1 -1
- package/dist/server/modules/butler/patrol-execution-service.js +2 -1
- package/dist/server/modules/butler/patrol-execution-service.js.map +1 -1
- package/dist/server/modules/butler/provider-adapter-registry.d.ts +3 -0
- package/dist/server/modules/butler/provider-adapter-registry.js +18 -1
- package/dist/server/modules/butler/provider-adapter-registry.js.map +1 -1
- package/dist/server/modules/butler/verification-run-service.d.ts +9 -2
- package/dist/server/modules/butler/verification-run-service.js +188 -34
- package/dist/server/modules/butler/verification-run-service.js.map +1 -1
- package/dist/server/modules/debug-target/debug-target-controller.js +1 -1
- package/dist/server/modules/debug-target/debug-target-controller.js.map +1 -1
- package/dist/server/modules/debug-target/debug-target-service.d.ts +7 -2
- package/dist/server/modules/debug-target/debug-target-service.js +563 -100
- package/dist/server/modules/debug-target/debug-target-service.js.map +1 -1
- package/dist/server/modules/git/git-command-helper-client.d.ts +1 -0
- package/dist/server/modules/git/git-command-helper-client.js +19 -26
- package/dist/server/modules/git/git-command-helper-client.js.map +1 -1
- package/dist/server/modules/git/git-command-runner.js +19 -1
- package/dist/server/modules/git/git-command-runner.js.map +1 -1
- package/dist/server/modules/preferences/profile-service.d.ts +3 -1
- package/dist/server/modules/preferences/profile-service.js +74 -3
- package/dist/server/modules/preferences/profile-service.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +5 -3
- package/dist/server/modules/provider/provider-discovery-helper-client.js +129 -43
- package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-helper-process.js +44 -0
- package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-runtime.js +83 -3
- package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
- package/dist/server/modules/sessions/claude-runtime-helper-client.js +23 -1
- package/dist/server/modules/sessions/claude-runtime-helper-client.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +7 -1
- package/dist/server/modules/sessions/session-history-service.js +251 -41
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +6 -0
- package/dist/server/modules/sessions/session-live-runtime-service.js +97 -11
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-message-origin-utils.d.ts +12 -0
- package/dist/server/modules/sessions/session-message-origin-utils.js +45 -0
- package/dist/server/modules/sessions/session-message-origin-utils.js.map +1 -0
- package/dist/server/modules/sessions/session-permission-request-service.js +167 -0
- package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
- package/dist/server/modules/skills/builtin-skill-service.js +1 -1
- package/dist/server/modules/skills/builtin-skill-service.js.map +1 -1
- package/dist/server/modules/skills/builtin-skills/codingns-assistant/SKILL.md +19 -12
- package/dist/server/modules/skills/builtin-skills/codingns-assistant/references/cli-workflow.md +9 -3
- package/dist/server/modules/tasks/task-helper-client.d.ts +5 -2
- package/dist/server/modules/tasks/task-helper-client.js +118 -38
- package/dist/server/modules/tasks/task-helper-client.js.map +1 -1
- package/dist/server/modules/tasks/task-helper-process.js +94 -3
- package/dist/server/modules/tasks/task-helper-process.js.map +1 -1
- package/dist/server/modules/tasks/task-types.d.ts +3 -0
- package/dist/server/modules/tasks/task-types.js +4 -1
- package/dist/server/modules/tasks/task-types.js.map +1 -1
- package/dist/server/modules/terminal/command-template-service.d.ts +9 -0
- package/dist/server/modules/terminal/command-template-service.js +87 -5
- package/dist/server/modules/terminal/command-template-service.js.map +1 -1
- package/dist/server/modules/terminal/terminal-controller.d.ts +3 -0
- package/dist/server/modules/terminal/terminal-controller.js +41 -0
- package/dist/server/modules/terminal/terminal-controller.js.map +1 -1
- package/dist/server/modules/workbench/workbench-service.d.ts +3 -0
- package/dist/server/modules/workbench/workbench-service.js +4 -3
- package/dist/server/modules/workbench/workbench-service.js.map +1 -1
- package/dist/server/modules/workbench/workspace-file-watcher.d.ts +14 -6
- package/dist/server/modules/workbench/workspace-file-watcher.js +267 -57
- package/dist/server/modules/workbench/workspace-file-watcher.js.map +1 -1
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +2 -0
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +32 -3
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
- package/dist/server/modules/worktree/worktree-manager.d.ts +9 -1
- package/dist/server/modules/worktree/worktree-manager.js +9 -1
- package/dist/server/modules/worktree/worktree-manager.js.map +1 -1
- package/dist/server/routes/assistant.js +19 -0
- package/dist/server/routes/assistant.js.map +1 -1
- package/dist/server/routes/butler.js +5 -0
- package/dist/server/routes/butler.js.map +1 -1
- package/dist/server/server/create-server.d.ts +8 -0
- package/dist/server/server/create-server.js +36 -13
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/storage/repositories/assistant-automation-run-repository.d.ts +12 -0
- package/dist/server/storage/repositories/assistant-automation-run-repository.js +139 -0
- package/dist/server/storage/repositories/assistant-automation-run-repository.js.map +1 -0
- package/dist/server/storage/repositories/assistant-automation-task-repository.d.ts +15 -0
- package/dist/server/storage/repositories/assistant-automation-task-repository.js +173 -0
- package/dist/server/storage/repositories/assistant-automation-task-repository.js.map +1 -0
- package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.d.ts +17 -0
- package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js +164 -0
- package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-control-session-repository.js +27 -3
- package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -1
- package/dist/server/storage/repositories/butler-control-timer-repository.d.ts +15 -0
- package/dist/server/storage/repositories/butler-control-timer-repository.js +157 -0
- package/dist/server/storage/repositories/butler-control-timer-repository.js.map +1 -0
- package/dist/server/storage/repositories/user-preference-profile-repository.js +6 -3
- package/dist/server/storage/repositories/user-preference-profile-repository.js.map +1 -1
- package/dist/server/storage/sqlite/client.js +239 -2
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +107 -1
- package/dist/server/types/domain.d.ts +89 -2
- package/dist/server/ws/workbench-ws-hub.d.ts +14 -8
- package/dist/server/ws/workbench-ws-hub.js +299 -163
- package/dist/server/ws/workbench-ws-hub.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +4 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +111 -3
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +6 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +306 -31
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +5 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +187 -26
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +4 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +98 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +2 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +71 -8
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +4 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +44 -0
- 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.js +9 -3
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/services.js +17 -8
- package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +4 -0
- package/package.json +1 -1
- package/dist/public/assets/index-BlOinYqR.js +0 -122
- package/dist/public/assets/index-Dg_7g6lA.css +0 -1
|
@@ -32,9 +32,13 @@ const SESSION_START_DEFERRED_PROVIDERS = new Set([
|
|
|
32
32
|
]);
|
|
33
33
|
const MUTABLE_HISTORY_TAIL_REFRESH_INTERVAL_MS = 1_200;
|
|
34
34
|
const WORKSPACE_DISCOVERY_BACKGROUND_MAX_AGE_MS = 15_000;
|
|
35
|
+
const WORKSPACE_DISCOVERY_SCAN_CONCURRENCY = 2;
|
|
35
36
|
const PROVIDER_CAPABILITY_CACHE_MAX_AGE_MS = 5_000;
|
|
36
37
|
const WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE = 25;
|
|
37
38
|
const SESSION_TRANSACTION_HOTSPOT_THRESHOLD_MS = 150;
|
|
39
|
+
const WORKSPACE_STATE_REFRESH_COOLDOWN_MS = 1_500;
|
|
40
|
+
const SQLITE_BUSY_RETRY_LIMIT = 3;
|
|
41
|
+
const SQLITE_BUSY_RETRY_DELAY_MS = 100;
|
|
38
42
|
export class SessionHistoryService {
|
|
39
43
|
db;
|
|
40
44
|
workspaceRepository;
|
|
@@ -59,10 +63,11 @@ export class SessionHistoryService {
|
|
|
59
63
|
providerSessionDiscoveryConfig;
|
|
60
64
|
taskManager;
|
|
61
65
|
workspaceDiscoveryStatuses = new Map();
|
|
62
|
-
|
|
66
|
+
workspaceStateRefreshStatuses = new Map();
|
|
63
67
|
providerCapabilityCache = new Map();
|
|
64
68
|
liveActivityObservationResolvers = new Set();
|
|
65
69
|
workspaceSessionRelations = new Map();
|
|
70
|
+
workspaceStateRefreshTaskSequence = 0;
|
|
66
71
|
constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}, taskManager = createTaskManager()) {
|
|
67
72
|
this.db = db;
|
|
68
73
|
this.workspaceRepository = workspaceRepository;
|
|
@@ -159,6 +164,7 @@ export class SessionHistoryService {
|
|
|
159
164
|
this.taskManager.register({
|
|
160
165
|
taskType: HOST_TASK_TYPES.workspaceDiscoveryScan,
|
|
161
166
|
executionLane: "helper_process",
|
|
167
|
+
concurrency: WORKSPACE_DISCOVERY_SCAN_CONCURRENCY,
|
|
162
168
|
helperProcessHandler: "session.workspace_discovery",
|
|
163
169
|
run: async ({ config, workspacePath, knownSessions }, context) => await discoverWorkspaceSessionsInRuntime(config, workspacePath, knownSessions, context.signal)
|
|
164
170
|
});
|
|
@@ -319,6 +325,12 @@ export class SessionHistoryService {
|
|
|
319
325
|
originRef: null
|
|
320
326
|
};
|
|
321
327
|
}
|
|
328
|
+
resolveMessageOriginByClientRequestId(sessionId, clientRequestId, messageId, updatedAt) {
|
|
329
|
+
if (!this.sessionMessageOriginRepository || !clientRequestId || !messageId) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this.sessionMessageOriginRepository.resolveMessageId(sessionId, clientRequestId, messageId, updatedAt);
|
|
333
|
+
}
|
|
322
334
|
async findLatestUserMessage(sessionId, content, maxAttempts = 12, minTimestamp = null) {
|
|
323
335
|
const binding = this.getBindingOrThrow(sessionId);
|
|
324
336
|
const acceptedContents = new Set((Array.isArray(content) ? content : [content]).filter((value) => value.trim().length > 0));
|
|
@@ -1215,7 +1227,7 @@ export class SessionHistoryService {
|
|
|
1215
1227
|
const existingIndex = existing
|
|
1216
1228
|
? this.sessionIndexRepository.findIndexRecordBySessionId(existing.sessionId)
|
|
1217
1229
|
: null;
|
|
1218
|
-
|
|
1230
|
+
const nextBinding = {
|
|
1219
1231
|
sessionId,
|
|
1220
1232
|
workspaceId: workspace.id,
|
|
1221
1233
|
provider: session.provider,
|
|
@@ -1223,7 +1235,10 @@ export class SessionHistoryService {
|
|
|
1223
1235
|
rawStoreRef: session.rawStoreRef,
|
|
1224
1236
|
createdAt,
|
|
1225
1237
|
updatedAt: timestamp
|
|
1226
|
-
}
|
|
1238
|
+
};
|
|
1239
|
+
if (!areEquivalentSessionBindings(existing, nextBinding)) {
|
|
1240
|
+
this.sessionBindingRepository.upsert(nextBinding);
|
|
1241
|
+
}
|
|
1227
1242
|
const preservedParentSessionId = existingIndex?.parentSessionId
|
|
1228
1243
|
?? this.sessionForkRepository.findBySessionId(sessionId)?.parentSessionId
|
|
1229
1244
|
?? null;
|
|
@@ -1231,7 +1246,7 @@ export class SessionHistoryService {
|
|
|
1231
1246
|
? this.sessionIndexRepository.findIndexRecordBySessionId(preservedParentSessionId)?.title ?? null
|
|
1232
1247
|
: null;
|
|
1233
1248
|
const preservedTitle = resolvePersistedSessionTitle(session.provider, session.title, existingIndex?.title ?? null, preservedParentTitle);
|
|
1234
|
-
|
|
1249
|
+
const nextIndex = {
|
|
1235
1250
|
sessionId,
|
|
1236
1251
|
workspaceId: workspace.id,
|
|
1237
1252
|
provider: session.provider,
|
|
@@ -1243,12 +1258,15 @@ export class SessionHistoryService {
|
|
|
1243
1258
|
subagentLabel: existingIndex?.subagentLabel ?? null,
|
|
1244
1259
|
title: preservedTitle,
|
|
1245
1260
|
messageCount: session.messageCount,
|
|
1246
|
-
isArchived: resolveDiscoveredArchiveState(existingIndex?.isArchived ?? false, session.isArchived),
|
|
1261
|
+
isArchived: resolveDiscoveredArchiveState(session.provider, existingIndex?.isArchived ?? false, session.isArchived),
|
|
1247
1262
|
lastMessageAt: session.lastMessageAt,
|
|
1248
1263
|
createdAt,
|
|
1249
1264
|
updatedAt: timestamp
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1265
|
+
};
|
|
1266
|
+
if (!areEquivalentSessionIndexRecords(existingIndex, nextIndex)) {
|
|
1267
|
+
this.sessionIndexRepository.upsert(nextIndex);
|
|
1268
|
+
}
|
|
1269
|
+
const nextSnapshot = {
|
|
1252
1270
|
sessionId,
|
|
1253
1271
|
syncStatus: currentSnapshot?.syncStatus ?? "idle",
|
|
1254
1272
|
syncCursor: currentSnapshot?.syncCursor ?? null,
|
|
@@ -1257,13 +1275,17 @@ export class SessionHistoryService {
|
|
|
1257
1275
|
lastErrorDetail: currentSnapshot?.lastErrorDetail ?? null,
|
|
1258
1276
|
resumedAt: currentSnapshot?.resumedAt ?? null,
|
|
1259
1277
|
updatedAt: timestamp
|
|
1260
|
-
}
|
|
1278
|
+
};
|
|
1279
|
+
if (!areEquivalentSessionStatusSnapshots(currentSnapshot, nextSnapshot)) {
|
|
1280
|
+
this.sessionStatusSnapshotRepository.upsert(nextSnapshot);
|
|
1281
|
+
}
|
|
1261
1282
|
discoveredSessionIds.set(buildProviderSessionKey(session.provider, session.providerSessionId), sessionId);
|
|
1262
1283
|
persistedSessions.push({
|
|
1263
1284
|
session,
|
|
1264
1285
|
sessionId,
|
|
1265
1286
|
createdAt,
|
|
1266
|
-
existingIndex
|
|
1287
|
+
existingIndex,
|
|
1288
|
+
pass1Index: nextIndex
|
|
1267
1289
|
});
|
|
1268
1290
|
}
|
|
1269
1291
|
});
|
|
@@ -1293,7 +1315,7 @@ export class SessionHistoryService {
|
|
|
1293
1315
|
const resolvedParentTitle = resolvedParentSessionId
|
|
1294
1316
|
? this.sessionIndexRepository.findIndexRecordBySessionId(resolvedParentSessionId)?.title ?? null
|
|
1295
1317
|
: null;
|
|
1296
|
-
|
|
1318
|
+
const nextIndex = {
|
|
1297
1319
|
sessionId: persistedSession.sessionId,
|
|
1298
1320
|
workspaceId: workspace.id,
|
|
1299
1321
|
provider: persistedSession.session.provider,
|
|
@@ -1315,11 +1337,14 @@ export class SessionHistoryService {
|
|
|
1315
1337
|
?? null,
|
|
1316
1338
|
title: resolvePersistedSessionTitle(persistedSession.session.provider, persistedSession.session.title, persistedSession.existingIndex?.title ?? null, resolvedParentTitle),
|
|
1317
1339
|
messageCount: persistedSession.session.messageCount,
|
|
1318
|
-
isArchived: resolveDiscoveredArchiveState(persistedSession.existingIndex?.isArchived ?? false, persistedSession.session.isArchived),
|
|
1340
|
+
isArchived: resolveDiscoveredArchiveState(persistedSession.session.provider, persistedSession.existingIndex?.isArchived ?? false, persistedSession.session.isArchived),
|
|
1319
1341
|
lastMessageAt: persistedSession.session.lastMessageAt,
|
|
1320
1342
|
createdAt: persistedSession.createdAt,
|
|
1321
1343
|
updatedAt: timestamp
|
|
1322
|
-
}
|
|
1344
|
+
};
|
|
1345
|
+
if (!areEquivalentSessionIndexRecords(persistedSession.pass1Index, nextIndex)) {
|
|
1346
|
+
this.sessionIndexRepository.upsert(nextIndex);
|
|
1347
|
+
}
|
|
1323
1348
|
}
|
|
1324
1349
|
});
|
|
1325
1350
|
const persistPass2StartedAt = Date.now();
|
|
@@ -1387,7 +1412,22 @@ export class SessionHistoryService {
|
|
|
1387
1412
|
discoveredSessions: sessions.length,
|
|
1388
1413
|
returnedSessions: nextItems.length,
|
|
1389
1414
|
discoveryComplete: discovery.isComplete,
|
|
1390
|
-
providerDiagnostics: (discovery.providerDiagnostics ?? []).map((entry) =>
|
|
1415
|
+
providerDiagnostics: (discovery.providerDiagnostics ?? []).map((entry) => {
|
|
1416
|
+
const scannedFiles = entry.scannedFiles ?? 0;
|
|
1417
|
+
const skippedByMtimeSize = entry.skippedByMtimeSize ?? 0;
|
|
1418
|
+
const parsedFiles = entry.parsedFiles ?? 0;
|
|
1419
|
+
const bytesRead = entry.bytesRead ?? 0;
|
|
1420
|
+
return [
|
|
1421
|
+
entry.provider,
|
|
1422
|
+
entry.status,
|
|
1423
|
+
`${Math.round(entry.durationMs)}ms`,
|
|
1424
|
+
`sessions=${entry.sessionCount}`,
|
|
1425
|
+
`scanned=${scannedFiles}`,
|
|
1426
|
+
`skipped=${skippedByMtimeSize}`,
|
|
1427
|
+
`parsed=${parsedFiles}`,
|
|
1428
|
+
`bytes=${bytesRead}`
|
|
1429
|
+
].join(":");
|
|
1430
|
+
}),
|
|
1391
1431
|
refreshedStates: refreshCandidates.length,
|
|
1392
1432
|
discoverMs: discoverDurationMs,
|
|
1393
1433
|
persistMs: persistDurationMs,
|
|
@@ -1542,13 +1582,6 @@ export class SessionHistoryService {
|
|
|
1542
1582
|
const originByMessageId = new Map(originRows
|
|
1543
1583
|
.filter((row) => row.messageId)
|
|
1544
1584
|
.map((row) => [row.messageId, row]));
|
|
1545
|
-
const unresolvedRows = originRepository.listUnresolvedBySessionAndContents(sessionId, [...new Set(messages.map((message) => message.content).filter((content) => content.trim().length > 0))]);
|
|
1546
|
-
const unresolvedByContent = new Map();
|
|
1547
|
-
for (const row of unresolvedRows) {
|
|
1548
|
-
const current = unresolvedByContent.get(row.content) ?? [];
|
|
1549
|
-
current.push(row);
|
|
1550
|
-
unresolvedByContent.set(row.content, current);
|
|
1551
|
-
}
|
|
1552
1585
|
return messages.map((message) => {
|
|
1553
1586
|
const resolved = originByMessageId.get(message.messageId) ?? null;
|
|
1554
1587
|
if (resolved) {
|
|
@@ -1565,21 +1598,10 @@ export class SessionHistoryService {
|
|
|
1565
1598
|
originRef: null
|
|
1566
1599
|
};
|
|
1567
1600
|
}
|
|
1568
|
-
const candidates = unresolvedByContent.get(message.content) ?? [];
|
|
1569
|
-
const matched = candidates.find((row) => isMessageAtOrAfter(message.timestamp, row.createdAt)) ?? null;
|
|
1570
|
-
if (!matched) {
|
|
1571
|
-
return {
|
|
1572
|
-
...message,
|
|
1573
|
-
origin: null,
|
|
1574
|
-
originRef: null
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
originRepository.resolveMessageId(sessionId, matched.clientRequestId, message.messageId, message.timestamp);
|
|
1578
|
-
unresolvedByContent.set(message.content, candidates.filter((candidate) => candidate.clientRequestId !== matched.clientRequestId));
|
|
1579
1601
|
return {
|
|
1580
1602
|
...message,
|
|
1581
|
-
origin:
|
|
1582
|
-
originRef:
|
|
1603
|
+
origin: null,
|
|
1604
|
+
originRef: null
|
|
1583
1605
|
};
|
|
1584
1606
|
});
|
|
1585
1607
|
}
|
|
@@ -1718,6 +1740,9 @@ export class SessionHistoryService {
|
|
|
1718
1740
|
if (shouldSkipClaudePendingBinding(binding)) {
|
|
1719
1741
|
return;
|
|
1720
1742
|
}
|
|
1743
|
+
if (!shouldSyncSessionTitleFromProvider(binding.provider, currentIndex.title)) {
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1721
1746
|
const nextTitle = (await this.providerDiscoveryHelperClient.readSessionTitle({
|
|
1722
1747
|
config: this.providerSessionDiscoveryConfig,
|
|
1723
1748
|
provider: binding.provider,
|
|
@@ -1926,13 +1951,71 @@ export class SessionHistoryService {
|
|
|
1926
1951
|
return;
|
|
1927
1952
|
}
|
|
1928
1953
|
const inflightKey = `${workspaceId}:${userId}`;
|
|
1929
|
-
|
|
1954
|
+
const refreshState = this.getOrCreateWorkspaceStateRefreshStatus(inflightKey);
|
|
1955
|
+
const now = Date.now();
|
|
1956
|
+
refreshState.lastRequestedAt = now;
|
|
1957
|
+
refreshState.phase = refreshState.phase === "fresh" ? "stale" : refreshState.phase;
|
|
1958
|
+
refreshState.dirtyReasons.add("workspace.discovery.deferred_state_refresh");
|
|
1959
|
+
mergeWorkspaceStateRefreshSessions(refreshState.pendingSessions, sessions);
|
|
1960
|
+
if (refreshState.phase === "running" && refreshState.runningPromise) {
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
if (this.isWorkspaceStateRefreshCoolingDown(refreshState, now)) {
|
|
1964
|
+
refreshState.phase = "stale";
|
|
1965
|
+
this.ensureWorkspaceStateRefreshCooldownTimer(inflightKey, workspaceId, userId, refreshState);
|
|
1930
1966
|
return;
|
|
1931
1967
|
}
|
|
1968
|
+
this.startWorkspaceStateRefreshTask(inflightKey, workspaceId, userId, refreshState);
|
|
1969
|
+
}
|
|
1970
|
+
getOrCreateWorkspaceStateRefreshStatus(key) {
|
|
1971
|
+
const existing = this.workspaceStateRefreshStatuses.get(key);
|
|
1972
|
+
if (existing) {
|
|
1973
|
+
return existing;
|
|
1974
|
+
}
|
|
1975
|
+
const created = {
|
|
1976
|
+
phase: "fresh",
|
|
1977
|
+
dirtyReasons: new Set(),
|
|
1978
|
+
pendingSessions: new Map(),
|
|
1979
|
+
runningPromise: null,
|
|
1980
|
+
cooldownTimer: null,
|
|
1981
|
+
lastRequestedAt: null,
|
|
1982
|
+
lastStartedAt: null,
|
|
1983
|
+
lastCompletedAt: null,
|
|
1984
|
+
lastFailedAt: null,
|
|
1985
|
+
nextAllowedAt: null,
|
|
1986
|
+
runningTaskId: null
|
|
1987
|
+
};
|
|
1988
|
+
this.workspaceStateRefreshStatuses.set(key, created);
|
|
1989
|
+
return created;
|
|
1990
|
+
}
|
|
1991
|
+
isWorkspaceStateRefreshCoolingDown(state, now) {
|
|
1992
|
+
if (state.nextAllowedAt === null || now >= state.nextAllowedAt) {
|
|
1993
|
+
return false;
|
|
1994
|
+
}
|
|
1995
|
+
return state.phase === "cooldown" || state.phase === "failed";
|
|
1996
|
+
}
|
|
1997
|
+
startWorkspaceStateRefreshTask(key, workspaceId, userId, state) {
|
|
1998
|
+
if (state.runningPromise) {
|
|
1999
|
+
return;
|
|
2000
|
+
}
|
|
2001
|
+
const sessions = [...state.pendingSessions.values()];
|
|
2002
|
+
if (sessions.length === 0) {
|
|
2003
|
+
state.phase = "fresh";
|
|
2004
|
+
state.dirtyReasons.clear();
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
state.pendingSessions.clear();
|
|
2008
|
+
state.phase = "running";
|
|
2009
|
+
state.lastStartedAt = Date.now();
|
|
2010
|
+
state.runningTaskId = `${key}:${++this.workspaceStateRefreshTaskSequence}`;
|
|
1932
2011
|
const startedAt = Date.now();
|
|
1933
2012
|
const task = delay(0)
|
|
1934
2013
|
.then(() => this.refreshRecentSessionStates(sessions, userId))
|
|
1935
2014
|
.then(() => {
|
|
2015
|
+
state.lastCompletedAt = Date.now();
|
|
2016
|
+
state.phase = "cooldown";
|
|
2017
|
+
state.nextAllowedAt = state.lastCompletedAt + WORKSPACE_STATE_REFRESH_COOLDOWN_MS;
|
|
2018
|
+
state.dirtyReasons.clear();
|
|
1936
2019
|
logPerformance("workspace.refresh_recent_session_states", Date.now() - startedAt, {
|
|
1937
2020
|
workspaceId,
|
|
1938
2021
|
refreshedStates: sessions.length
|
|
@@ -1941,6 +2024,9 @@ export class SessionHistoryService {
|
|
|
1941
2024
|
});
|
|
1942
2025
|
})
|
|
1943
2026
|
.catch((error) => {
|
|
2027
|
+
state.lastFailedAt = Date.now();
|
|
2028
|
+
state.phase = "failed";
|
|
2029
|
+
state.nextAllowedAt = state.lastFailedAt + WORKSPACE_STATE_REFRESH_COOLDOWN_MS;
|
|
1944
2030
|
logPerformance("workspace.refresh_recent_session_states.failed", Date.now() - startedAt, {
|
|
1945
2031
|
workspaceId,
|
|
1946
2032
|
refreshedStates: sessions.length,
|
|
@@ -1951,9 +2037,42 @@ export class SessionHistoryService {
|
|
|
1951
2037
|
});
|
|
1952
2038
|
})
|
|
1953
2039
|
.finally(() => {
|
|
1954
|
-
|
|
2040
|
+
state.runningPromise = null;
|
|
2041
|
+
state.runningTaskId = null;
|
|
2042
|
+
if (state.pendingSessions.size === 0) {
|
|
2043
|
+
if (state.phase === "cooldown") {
|
|
2044
|
+
this.ensureWorkspaceStateRefreshCooldownTimer(key, workspaceId, userId, state);
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
if (state.phase === "failed") {
|
|
2048
|
+
this.ensureWorkspaceStateRefreshCooldownTimer(key, workspaceId, userId, state);
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
state.phase = "fresh";
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
state.phase = "stale";
|
|
2055
|
+
this.ensureWorkspaceStateRefreshCooldownTimer(key, workspaceId, userId, state);
|
|
1955
2056
|
});
|
|
1956
|
-
|
|
2057
|
+
state.runningPromise = task;
|
|
2058
|
+
}
|
|
2059
|
+
ensureWorkspaceStateRefreshCooldownTimer(key, workspaceId, userId, state) {
|
|
2060
|
+
if (state.cooldownTimer) {
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
const now = Date.now();
|
|
2064
|
+
const delayMs = Math.max(0, (state.nextAllowedAt ?? now) - now);
|
|
2065
|
+
state.cooldownTimer = setTimeout(() => {
|
|
2066
|
+
state.cooldownTimer = null;
|
|
2067
|
+
if (state.pendingSessions.size === 0) {
|
|
2068
|
+
state.phase = "fresh";
|
|
2069
|
+
state.dirtyReasons.clear();
|
|
2070
|
+
state.nextAllowedAt = null;
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
state.phase = "stale";
|
|
2074
|
+
this.startWorkspaceStateRefreshTask(key, workspaceId, userId, state);
|
|
2075
|
+
}, delayMs);
|
|
1957
2076
|
}
|
|
1958
2077
|
async cleanupStaleHiddenSessions(workspaceId, userId, sessions) {
|
|
1959
2078
|
const discoveredProviderSessionIds = new Set(sessions.map((session) => buildProviderSessionKey(session.provider, session.providerSessionId)));
|
|
@@ -2532,6 +2651,11 @@ function buildSessionStateRefreshCandidates(items, recentCount) {
|
|
|
2532
2651
|
}
|
|
2533
2652
|
return Array.from(deduped.values());
|
|
2534
2653
|
}
|
|
2654
|
+
function mergeWorkspaceStateRefreshSessions(target, sessions) {
|
|
2655
|
+
for (const session of sessions) {
|
|
2656
|
+
target.set(session.sessionId, session);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2535
2659
|
function isSessionStateRefreshCandidate(item) {
|
|
2536
2660
|
return item.activityState === "running"
|
|
2537
2661
|
|| item.runningState === "starting"
|
|
@@ -2609,12 +2733,20 @@ function mergeSessionIndexRecord(input) {
|
|
|
2609
2733
|
subagentLabel: input.target?.subagentLabel ?? input.source?.subagentLabel ?? null,
|
|
2610
2734
|
title: pickPreferredSessionTitle(input.target?.title ?? null, input.source?.title ?? null),
|
|
2611
2735
|
messageCount: Math.max(input.target?.messageCount ?? 0, input.source?.messageCount ?? 0),
|
|
2612
|
-
isArchived:
|
|
2736
|
+
isArchived: mergePersistedArchiveState(input.provider, input.target?.isArchived, input.source?.isArchived),
|
|
2613
2737
|
lastMessageAt: pickLaterIso(input.target?.lastMessageAt ?? null, input.source?.lastMessageAt ?? null),
|
|
2614
2738
|
createdAt: pickEarlierIso(input.target?.createdAt ?? null, input.source?.createdAt ?? null) ?? input.timestamp,
|
|
2615
2739
|
updatedAt: input.timestamp
|
|
2616
2740
|
};
|
|
2617
2741
|
}
|
|
2742
|
+
function mergePersistedArchiveState(provider, targetArchived, sourceArchived) {
|
|
2743
|
+
// 只有 Codex 这类真实支持归档的 provider 才认底层归档真相;
|
|
2744
|
+
// 其他 provider 的归档完全由 Host 本地索引维护,不能让旧副本把恢复状态再刷回去。
|
|
2745
|
+
if (shouldUseProviderDiscoveredArchiveState(provider)) {
|
|
2746
|
+
return Boolean(targetArchived || sourceArchived);
|
|
2747
|
+
}
|
|
2748
|
+
return targetArchived ?? sourceArchived ?? false;
|
|
2749
|
+
}
|
|
2618
2750
|
function mergeSessionStatusSnapshot(input) {
|
|
2619
2751
|
if (!input.target && !input.source) {
|
|
2620
2752
|
return null;
|
|
@@ -2915,12 +3047,16 @@ function isCloseClaudeSessionTimestamp(left, right, maxGapMs = 2 * 60 * 1000) {
|
|
|
2915
3047
|
}
|
|
2916
3048
|
return Math.abs(leftAt - rightAt) <= maxGapMs;
|
|
2917
3049
|
}
|
|
2918
|
-
function resolveDiscoveredArchiveState(existingArchived, discoveredArchived) {
|
|
2919
|
-
if (
|
|
2920
|
-
return
|
|
3050
|
+
function resolveDiscoveredArchiveState(provider, existingArchived, discoveredArchived) {
|
|
3051
|
+
if (!shouldUseProviderDiscoveredArchiveState(provider)) {
|
|
3052
|
+
return existingArchived;
|
|
2921
3053
|
}
|
|
2922
3054
|
return discoveredArchived === true;
|
|
2923
3055
|
}
|
|
3056
|
+
function shouldUseProviderDiscoveredArchiveState(provider) {
|
|
3057
|
+
// 当前只有 Codex 的归档能稳定映射到底层文件位置;其余 provider 一律信本地 session_indices。
|
|
3058
|
+
return provider === "codex";
|
|
3059
|
+
}
|
|
2924
3060
|
function isMessageAtOrAfter(timestamp, minTimestamp) {
|
|
2925
3061
|
if (!minTimestamp) {
|
|
2926
3062
|
return true;
|
|
@@ -3153,6 +3289,16 @@ function isSyntheticCodexSessionTitle(title) {
|
|
|
3153
3289
|
return (/^rollout-\d{4}-\d{2}-\d{2}t/i.test(title) ||
|
|
3154
3290
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(title));
|
|
3155
3291
|
}
|
|
3292
|
+
function shouldSyncSessionTitleFromProvider(provider, currentTitle) {
|
|
3293
|
+
const normalizedTitle = currentTitle?.trim() ?? "";
|
|
3294
|
+
if (normalizedTitle.length === 0) {
|
|
3295
|
+
return true;
|
|
3296
|
+
}
|
|
3297
|
+
if (provider === "codex" && isSyntheticCodexSessionTitle(normalizedTitle)) {
|
|
3298
|
+
return true;
|
|
3299
|
+
}
|
|
3300
|
+
return false;
|
|
3301
|
+
}
|
|
3156
3302
|
function shouldRemoveHiddenClaudeDebugSession(session) {
|
|
3157
3303
|
const normalizedRawStoreRef = session.rawStoreRef.replaceAll("\\", "/");
|
|
3158
3304
|
if (normalizedRawStoreRef.includes("/subagents/")) {
|
|
@@ -3325,6 +3471,56 @@ function getAbortMessage(reason) {
|
|
|
3325
3471
|
}
|
|
3326
3472
|
return "任务已取消";
|
|
3327
3473
|
}
|
|
3474
|
+
function areEquivalentSessionBindings(current, next) {
|
|
3475
|
+
if (!current) {
|
|
3476
|
+
return false;
|
|
3477
|
+
}
|
|
3478
|
+
return (current.sessionId === next.sessionId &&
|
|
3479
|
+
current.workspaceId === next.workspaceId &&
|
|
3480
|
+
current.provider === next.provider &&
|
|
3481
|
+
current.providerSessionId === next.providerSessionId &&
|
|
3482
|
+
current.rawStoreRef === next.rawStoreRef &&
|
|
3483
|
+
current.createdAt === next.createdAt);
|
|
3484
|
+
}
|
|
3485
|
+
function areEquivalentSessionIndexRecords(current, next) {
|
|
3486
|
+
if (!current) {
|
|
3487
|
+
return false;
|
|
3488
|
+
}
|
|
3489
|
+
return (current.sessionId === next.sessionId &&
|
|
3490
|
+
current.workspaceId === next.workspaceId &&
|
|
3491
|
+
current.provider === next.provider &&
|
|
3492
|
+
(current.parentSessionId ?? null) === (next.parentSessionId ?? null) &&
|
|
3493
|
+
(current.sessionKind ?? "default") === (next.sessionKind ?? "default") &&
|
|
3494
|
+
(current.annotationSourceMessageId ?? null) === (next.annotationSourceMessageId ?? null) &&
|
|
3495
|
+
(current.annotationSourceText ?? null) === (next.annotationSourceText ?? null) &&
|
|
3496
|
+
(current.isSubagent ?? false) === (next.isSubagent ?? false) &&
|
|
3497
|
+
(current.subagentLabel ?? null) === (next.subagentLabel ?? null) &&
|
|
3498
|
+
current.title === next.title &&
|
|
3499
|
+
current.messageCount === next.messageCount &&
|
|
3500
|
+
current.isArchived === next.isArchived &&
|
|
3501
|
+
(current.lastMessageAt ?? null) === (next.lastMessageAt ?? null) &&
|
|
3502
|
+
current.createdAt === next.createdAt);
|
|
3503
|
+
}
|
|
3504
|
+
function areEquivalentSessionStatusSnapshots(current, next) {
|
|
3505
|
+
if (!current) {
|
|
3506
|
+
return false;
|
|
3507
|
+
}
|
|
3508
|
+
return (current.sessionId === next.sessionId &&
|
|
3509
|
+
current.syncStatus === next.syncStatus &&
|
|
3510
|
+
(current.syncCursor ?? null) === (next.syncCursor ?? null) &&
|
|
3511
|
+
(current.lastSyncAt ?? null) === (next.lastSyncAt ?? null) &&
|
|
3512
|
+
(current.lastErrorCode ?? null) === (next.lastErrorCode ?? null) &&
|
|
3513
|
+
(current.lastErrorDetail ?? null) === (next.lastErrorDetail ?? null) &&
|
|
3514
|
+
(current.resumedAt ?? null) === (next.resumedAt ?? null));
|
|
3515
|
+
}
|
|
3516
|
+
function isSqliteBusyError(error) {
|
|
3517
|
+
if (!error || typeof error !== "object") {
|
|
3518
|
+
return false;
|
|
3519
|
+
}
|
|
3520
|
+
const sqliteCode = "code" in error ? error.code : null;
|
|
3521
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3522
|
+
return sqliteCode === "SQLITE_BUSY" || message.includes("database is locked");
|
|
3523
|
+
}
|
|
3328
3524
|
async function runBatchedTransactions(items, batchSize, transaction, logOptions) {
|
|
3329
3525
|
const normalizedBatchSize = Math.max(1, Math.floor(batchSize) || 1);
|
|
3330
3526
|
let batchCount = 0;
|
|
@@ -3332,7 +3528,20 @@ async function runBatchedTransactions(items, batchSize, transaction, logOptions)
|
|
|
3332
3528
|
for (let index = 0; index < items.length; index += normalizedBatchSize) {
|
|
3333
3529
|
const batch = items.slice(index, index + normalizedBatchSize);
|
|
3334
3530
|
const batchStartedAt = Date.now();
|
|
3335
|
-
|
|
3531
|
+
let retryCount = 0;
|
|
3532
|
+
while (true) {
|
|
3533
|
+
try {
|
|
3534
|
+
transaction(batch);
|
|
3535
|
+
break;
|
|
3536
|
+
}
|
|
3537
|
+
catch (error) {
|
|
3538
|
+
if (!isSqliteBusyError(error) || retryCount >= SQLITE_BUSY_RETRY_LIMIT) {
|
|
3539
|
+
throw error;
|
|
3540
|
+
}
|
|
3541
|
+
retryCount += 1;
|
|
3542
|
+
await delay(SQLITE_BUSY_RETRY_DELAY_MS * retryCount);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3336
3545
|
const batchDurationMs = Date.now() - batchStartedAt;
|
|
3337
3546
|
const nextBatchIndex = batchCount + 1;
|
|
3338
3547
|
if (logOptions) {
|
|
@@ -3341,6 +3550,7 @@ async function runBatchedTransactions(items, batchSize, transaction, logOptions)
|
|
|
3341
3550
|
batchIndex: nextBatchIndex,
|
|
3342
3551
|
batchSize: batch.length,
|
|
3343
3552
|
batchStartIndex: index,
|
|
3553
|
+
retryCount,
|
|
3344
3554
|
totalItems: items.length,
|
|
3345
3555
|
configuredBatchSize: normalizedBatchSize
|
|
3346
3556
|
}, {
|