@jingyi0605/codingns 0.2.5 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codingns.mjs +278 -2
- package/dist/public/assets/{TerminalPage-BkjqU9NG.js → TerminalPage-CgrfstRm.js} +19 -19
- package/dist/public/assets/index-Cek6u0b9.css +1 -0
- package/dist/public/assets/index-THHY79si.js +122 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.d.ts +4 -0
- package/dist/server/config/env.js +67 -3
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/modules/auth/auth-service.d.ts +18 -1
- package/dist/server/modules/auth/auth-service.js +168 -7
- package/dist/server/modules/auth/auth-service.js.map +1 -1
- package/dist/server/modules/butler/butler-codex-model-policy.d.ts +1 -0
- package/dist/server/modules/butler/butler-codex-model-policy.js +36 -0
- package/dist/server/modules/butler/butler-codex-model-policy.js.map +1 -0
- package/dist/server/modules/butler/butler-control-session-service.d.ts +16 -2
- package/dist/server/modules/butler/butler-control-session-service.js +58 -210
- package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-controller.d.ts +17 -0
- package/dist/server/modules/butler/butler-controller.js +20 -1
- package/dist/server/modules/butler/butler-controller.js.map +1 -1
- package/dist/server/modules/butler/butler-follow-up-service.js +7 -3
- package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
- package/dist/server/modules/butler/butler-inbox-analysis-service.d.ts +36 -0
- package/dist/server/modules/butler/butler-inbox-analysis-service.js +375 -0
- package/dist/server/modules/butler/butler-inbox-analysis-service.js.map +1 -0
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.d.ts +23 -0
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.js +96 -0
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.js.map +1 -0
- package/dist/server/modules/butler/butler-inbox-service.d.ts +39 -2
- package/dist/server/modules/butler/butler-inbox-service.js +392 -2
- package/dist/server/modules/butler/butler-inbox-service.js.map +1 -1
- package/dist/server/modules/butler/butler-session-service.d.ts +9 -0
- package/dist/server/modules/butler/butler-session-service.js +277 -68
- package/dist/server/modules/butler/butler-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-session-summary-service.d.ts +1 -0
- package/dist/server/modules/butler/butler-session-summary-service.js +48 -23
- package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
- package/dist/server/modules/butler/butler-workspace-context.d.ts +13 -0
- package/dist/server/modules/butler/butler-workspace-context.js +223 -0
- package/dist/server/modules/butler/butler-workspace-context.js.map +1 -0
- package/dist/server/modules/client/client-controller.d.ts +6 -0
- package/dist/server/modules/client/client-controller.js +30 -8
- package/dist/server/modules/client/client-controller.js.map +1 -1
- package/dist/server/modules/client/client-service.d.ts +22 -10
- package/dist/server/modules/client/client-service.js +77 -100
- package/dist/server/modules/client/client-service.js.map +1 -1
- package/dist/server/modules/client/npm-global-package-service.d.ts +21 -0
- package/dist/server/modules/client/npm-global-package-service.js +210 -0
- package/dist/server/modules/client/npm-global-package-service.js.map +1 -0
- package/dist/server/modules/client/service-update-task-service.d.ts +15 -0
- package/dist/server/modules/client/service-update-task-service.js +147 -0
- package/dist/server/modules/client/service-update-task-service.js.map +1 -0
- package/dist/server/modules/client/service-update-types.d.ts +30 -0
- package/dist/server/modules/client/service-update-types.js +2 -0
- package/dist/server/modules/client/service-update-types.js.map +1 -0
- package/dist/server/modules/debug-target/debug-target-service.d.ts +5 -2
- package/dist/server/modules/debug-target/debug-target-service.js +170 -10
- package/dist/server/modules/debug-target/debug-target-service.js.map +1 -1
- package/dist/server/modules/file/file-constants.d.ts +1 -0
- package/dist/server/modules/file/file-constants.js +1 -0
- package/dist/server/modules/file/file-constants.js.map +1 -1
- package/dist/server/modules/file/file-controller.js +12 -3
- package/dist/server/modules/file/file-controller.js.map +1 -1
- package/dist/server/modules/file/file-preview-link-service.js +6 -37
- package/dist/server/modules/file/file-preview-link-service.js.map +1 -1
- package/dist/server/modules/file/file-preview-service.d.ts +6 -12
- package/dist/server/modules/file/file-preview-service.js +114 -28
- package/dist/server/modules/file/file-preview-service.js.map +1 -1
- package/dist/server/modules/file/file-preview-types.d.ts +37 -0
- package/dist/server/modules/file/file-preview-types.js +84 -0
- package/dist/server/modules/file/file-preview-types.js.map +1 -0
- package/dist/server/modules/git/git-controller.d.ts +6 -0
- package/dist/server/modules/git/git-controller.js +13 -0
- package/dist/server/modules/git/git-controller.js.map +1 -1
- package/dist/server/modules/git/git-read-service.d.ts +2 -1
- package/dist/server/modules/git/git-read-service.js +100 -0
- package/dist/server/modules/git/git-read-service.js.map +1 -1
- package/dist/server/modules/git/types.d.ts +23 -0
- package/dist/server/modules/model-switch/cc-switch-adapter.d.ts +36 -0
- package/dist/server/modules/model-switch/cc-switch-adapter.js +317 -0
- package/dist/server/modules/model-switch/cc-switch-adapter.js.map +1 -0
- package/dist/server/modules/model-switch/model-switch-controller.d.ts +11 -0
- package/dist/server/modules/model-switch/model-switch-controller.js +30 -0
- package/dist/server/modules/model-switch/model-switch-controller.js.map +1 -0
- package/dist/server/modules/model-switch/model-switch-service.d.ts +16 -0
- package/dist/server/modules/model-switch/model-switch-service.js +29 -0
- package/dist/server/modules/model-switch/model-switch-service.js.map +1 -0
- package/dist/server/modules/preferences/profile-service.d.ts +1 -0
- package/dist/server/modules/preferences/profile-service.js +9 -0
- package/dist/server/modules/preferences/profile-service.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js +3 -0
- package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
- package/dist/server/modules/sessions/session-activity-authority-service.d.ts +3 -1
- package/dist/server/modules/sessions/session-activity-authority-service.js +16 -1
- package/dist/server/modules/sessions/session-activity-authority-service.js.map +1 -1
- package/dist/server/modules/sessions/session-activity-inspector.d.ts +1 -1
- package/dist/server/modules/sessions/session-activity-inspector.js +52 -9
- package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +3 -3
- package/dist/server/modules/sessions/session-controller.js +3 -3
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +11 -2
- package/dist/server/modules/sessions/session-history-service.js +373 -78
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +9 -4
- package/dist/server/modules/sessions/session-live-runtime-service.js +59 -11
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-message-attachment-service.d.ts +8 -8
- package/dist/server/modules/sessions/session-message-attachment-service.js +25 -34
- package/dist/server/modules/sessions/session-message-attachment-service.js.map +1 -1
- package/dist/server/modules/sessions/session-provider-error-mapper.js +7 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
- package/dist/server/modules/skills/skill-controller.d.ts +23 -0
- package/dist/server/modules/skills/skill-controller.js +35 -0
- package/dist/server/modules/skills/skill-controller.js.map +1 -0
- package/dist/server/modules/skills/skill-manager-service.d.ts +86 -0
- package/dist/server/modules/skills/skill-manager-service.js +557 -0
- package/dist/server/modules/skills/skill-manager-service.js.map +1 -0
- package/dist/server/modules/skills/skill-reconciler.d.ts +21 -0
- package/dist/server/modules/skills/skill-reconciler.js +99 -0
- package/dist/server/modules/skills/skill-reconciler.js.map +1 -0
- package/dist/server/modules/skills/skill-sync-planner.d.ts +8 -0
- package/dist/server/modules/skills/skill-sync-planner.js +20 -0
- package/dist/server/modules/skills/skill-sync-planner.js.map +1 -0
- package/dist/server/modules/skills/skill-target-adapter.d.ts +34 -0
- package/dist/server/modules/skills/skill-target-adapter.js +65 -0
- package/dist/server/modules/skills/skill-target-adapter.js.map +1 -0
- package/dist/server/modules/tailscale/tailscale-controller.d.ts +15 -0
- package/dist/server/modules/tailscale/tailscale-controller.js +33 -0
- package/dist/server/modules/tailscale/tailscale-controller.js.map +1 -0
- package/dist/server/modules/tailscale/tailscale-helper-client.d.ts +41 -0
- package/dist/server/modules/tailscale/tailscale-helper-client.js +135 -0
- package/dist/server/modules/tailscale/tailscale-helper-client.js.map +1 -0
- package/dist/server/modules/tailscale/tailscale-helper-process.d.ts +1 -0
- package/dist/server/modules/tailscale/tailscale-helper-process.js +327 -0
- package/dist/server/modules/tailscale/tailscale-helper-process.js.map +1 -0
- package/dist/server/modules/tailscale/tailscale-manager.d.ts +41 -0
- package/dist/server/modules/tailscale/tailscale-manager.js +263 -0
- package/dist/server/modules/tailscale/tailscale-manager.js.map +1 -0
- package/dist/server/modules/tailscale/tailscale-service.d.ts +43 -0
- package/dist/server/modules/tailscale/tailscale-service.js +201 -0
- package/dist/server/modules/tailscale/tailscale-service.js.map +1 -0
- package/dist/server/modules/tasks/task-lane-executors.js +3 -0
- package/dist/server/modules/tasks/task-lane-executors.js.map +1 -1
- package/dist/server/modules/tasks/task-types.d.ts +2 -0
- package/dist/server/modules/tasks/task-types.js +3 -1
- package/dist/server/modules/tasks/task-types.js.map +1 -1
- package/dist/server/modules/terminal/command-template-service.js +10 -1
- package/dist/server/modules/terminal/command-template-service.js.map +1 -1
- package/dist/server/modules/terminal/template-port-runtime.d.ts +12 -1
- package/dist/server/modules/terminal/template-port-runtime.js +189 -17
- package/dist/server/modules/terminal/template-port-runtime.js.map +1 -1
- package/dist/server/modules/terminal/terminal-service.js +30 -3
- package/dist/server/modules/terminal/terminal-service.js.map +1 -1
- package/dist/server/modules/workspace/workspace-code-composition.js +95 -2
- package/dist/server/modules/workspace/workspace-code-composition.js.map +1 -1
- package/dist/server/routes/butler.js +4 -0
- package/dist/server/routes/butler.js.map +1 -1
- package/dist/server/routes/client.js +2 -0
- package/dist/server/routes/client.js.map +1 -1
- package/dist/server/routes/git.js +1 -0
- package/dist/server/routes/git.js.map +1 -1
- package/dist/server/routes/sessions.js +1 -1
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/routes/skills.d.ts +3 -0
- package/dist/server/routes/skills.js +7 -0
- package/dist/server/routes/skills.js.map +1 -0
- package/dist/server/routes/system.d.ts +4 -0
- package/dist/server/routes/system.js +11 -0
- package/dist/server/routes/system.js.map +1 -0
- package/dist/server/server/create-server.d.ts +16 -0
- package/dist/server/server/create-server.js +91 -8
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/shared/errors/app-error.d.ts +2 -0
- package/dist/server/shared/errors/app-error.js +2 -0
- package/dist/server/shared/errors/app-error.js.map +1 -1
- package/dist/server/shared/http/error-handler.d.ts +2 -1
- package/dist/server/shared/http/error-handler.js +3 -2
- package/dist/server/shared/http/error-handler.js.map +1 -1
- package/dist/server/shared/utils/command-availability.d.ts +1 -0
- package/dist/server/shared/utils/command-availability.js +26 -3
- package/dist/server/shared/utils/command-availability.js.map +1 -1
- package/dist/server/storage/repositories/auth-login-attempt-repository.d.ts +9 -0
- package/dist/server/storage/repositories/auth-login-attempt-repository.js +59 -0
- package/dist/server/storage/repositories/auth-login-attempt-repository.js.map +1 -0
- package/dist/server/storage/repositories/butler-control-session-repository.d.ts +3 -0
- package/dist/server/storage/repositories/butler-control-session-repository.js +80 -4
- package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -1
- package/dist/server/storage/repositories/butler-inbox-item-repository.js +54 -3
- package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -1
- package/dist/server/storage/repositories/instance-tailscale-repository.d.ts +10 -0
- package/dist/server/storage/repositories/instance-tailscale-repository.js +112 -0
- package/dist/server/storage/repositories/instance-tailscale-repository.js.map +1 -0
- package/dist/server/storage/repositories/managed-skill-repository.d.ts +11 -0
- package/dist/server/storage/repositories/managed-skill-repository.js +102 -0
- package/dist/server/storage/repositories/managed-skill-repository.js.map +1 -0
- package/dist/server/storage/repositories/skill-target-binding-repository.d.ts +10 -0
- package/dist/server/storage/repositories/skill-target-binding-repository.js +77 -0
- package/dist/server/storage/repositories/skill-target-binding-repository.js.map +1 -0
- package/dist/server/storage/repositories/terminal-instance-repository.js +1 -1
- package/dist/server/storage/repositories/terminal-instance-repository.js.map +1 -1
- 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 +137 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +84 -1
- package/dist/server/types/domain.d.ts +109 -1
- package/dist/server/ws/ws-server.js +4 -4
- package/dist/server/ws/ws-server.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +23 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +162 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.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 +89 -33
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +18 -2
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +3 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +238 -53
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +1 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +6 -2
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +1 -1
- package/package.json +1 -1
- package/dist/public/assets/index-C6U8-9jg.css +0 -1
- package/dist/public/assets/index-CKSumuV2.js +0 -109
|
@@ -58,6 +58,7 @@ export class SessionHistoryService {
|
|
|
58
58
|
workspaceDiscoveryStatuses = new Map();
|
|
59
59
|
workspaceStateRefreshInflight = new Map();
|
|
60
60
|
providerCapabilityCache = new Map();
|
|
61
|
+
liveActivityObservationResolvers = new Set();
|
|
61
62
|
workspaceSessionRelations = new Map();
|
|
62
63
|
constructor(db, workspaceRepository, sessionBindingRepository, sessionChangedFileService, sessionIndexRepository, sessionMessageAttachmentService, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), sessionMessageOriginRepository = null, sessionForkRepository = null, adapterOverrides = {}, taskManager = createTaskManager()) {
|
|
63
64
|
this.db = db;
|
|
@@ -130,6 +131,19 @@ export class SessionHistoryService {
|
|
|
130
131
|
observeBackgroundTaskMetrics() {
|
|
131
132
|
return this.taskManager.observe();
|
|
132
133
|
}
|
|
134
|
+
registerLiveActivityObservationResolver(resolver) {
|
|
135
|
+
this.liveActivityObservationResolvers.add(resolver);
|
|
136
|
+
let closed = false;
|
|
137
|
+
return {
|
|
138
|
+
close: () => {
|
|
139
|
+
if (closed) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
closed = true;
|
|
143
|
+
this.liveActivityObservationResolvers.delete(resolver);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
133
147
|
registerBackgroundTasks() {
|
|
134
148
|
if (!this.taskManager.has(HOST_TASK_TYPES.workspaceDiscovery)) {
|
|
135
149
|
this.taskManager.register({
|
|
@@ -1076,39 +1090,52 @@ export class SessionHistoryService {
|
|
|
1076
1090
|
providerSessionId: snapshot.providerSessionId,
|
|
1077
1091
|
rawStoreRef: snapshot.rawStoreRef
|
|
1078
1092
|
});
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
+
// discovery 和 runtime 回填会并发命中这里;如果在事务外先看重复,再事务内写入,
|
|
1094
|
+
// 中间就会留下一个竞态窗口,最后直接撞 UNIQUE(provider, provider_session_id)。
|
|
1095
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1096
|
+
try {
|
|
1097
|
+
this.db.transaction(() => {
|
|
1098
|
+
const currentBinding = this.sessionBindingRepository.findBySessionId(sessionId);
|
|
1099
|
+
const timestamp = nowIso();
|
|
1100
|
+
const duplicateBinding = this.findSameWorkspaceBindingDuplicate(sessionId, workspaceId, resolvedSnapshot);
|
|
1101
|
+
if (duplicateBinding) {
|
|
1102
|
+
// 运行时链路显式指定了当前 sessionId,就应该由当前会话接管同工作区里的重复底层会话。
|
|
1103
|
+
// 否则后续事件重放或后台发现补录都会持续撞 UNIQUE(provider, provider_session_id)。
|
|
1104
|
+
this.mergeSessionIntoTarget({
|
|
1105
|
+
workspaceId,
|
|
1106
|
+
targetSessionId: sessionId,
|
|
1107
|
+
sourceSessionId: duplicateBinding.sessionId,
|
|
1108
|
+
provider: resolvedSnapshot.provider,
|
|
1109
|
+
timestamp
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
const currentIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
|
|
1113
|
+
this.sessionBindingRepository.upsert({
|
|
1114
|
+
sessionId,
|
|
1115
|
+
workspaceId,
|
|
1116
|
+
provider: resolvedSnapshot.provider,
|
|
1117
|
+
providerSessionId: resolvedSnapshot.providerSessionId,
|
|
1118
|
+
rawStoreRef: resolvedSnapshot.rawStoreRef,
|
|
1119
|
+
createdAt: pickEarlierIso(currentBinding?.createdAt ?? null, duplicateBinding?.createdAt ?? null)
|
|
1120
|
+
?? timestamp,
|
|
1121
|
+
updatedAt: timestamp
|
|
1122
|
+
});
|
|
1123
|
+
if (currentIndex) {
|
|
1124
|
+
this.sessionIndexRepository.upsert({
|
|
1125
|
+
...currentIndex,
|
|
1126
|
+
updatedAt: timestamp
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
})();
|
|
1130
|
+
return;
|
|
1093
1131
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
providerSessionId: resolvedSnapshot.providerSessionId,
|
|
1100
|
-
rawStoreRef: resolvedSnapshot.rawStoreRef,
|
|
1101
|
-
createdAt: pickEarlierIso(currentBinding?.createdAt ?? null, duplicateBinding?.createdAt ?? null)
|
|
1102
|
-
?? timestamp,
|
|
1103
|
-
updatedAt: timestamp
|
|
1104
|
-
});
|
|
1105
|
-
if (currentIndex) {
|
|
1106
|
-
this.sessionIndexRepository.upsert({
|
|
1107
|
-
...currentIndex,
|
|
1108
|
-
updatedAt: timestamp
|
|
1109
|
-
});
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
if (attempt === 0 && isSessionBindingProviderUniqueConflict(error)) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
throw error;
|
|
1110
1137
|
}
|
|
1111
|
-
}
|
|
1138
|
+
}
|
|
1112
1139
|
}
|
|
1113
1140
|
async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline") {
|
|
1114
1141
|
const startedAt = Date.now();
|
|
@@ -1148,7 +1175,9 @@ export class SessionHistoryService {
|
|
|
1148
1175
|
const claimedPendingSessionIds = new Set();
|
|
1149
1176
|
const persistPass1Transaction = this.db.transaction((batch) => {
|
|
1150
1177
|
for (const session of batch) {
|
|
1151
|
-
const exactExisting = this.sessionBindingRepository.findByProviderSession(session.provider, session.providerSessionId) ??
|
|
1178
|
+
const exactExisting = this.sessionBindingRepository.findByProviderSession(session.provider, session.providerSessionId) ?? (shouldMatchSessionBindingByRawStoreRef(session.provider)
|
|
1179
|
+
? this.sessionBindingRepository.findByRawStoreRef(session.provider, session.rawStoreRef)
|
|
1180
|
+
: null);
|
|
1152
1181
|
if (exactExisting && exactExisting.workspaceId !== workspaceId) {
|
|
1153
1182
|
continue;
|
|
1154
1183
|
}
|
|
@@ -1391,12 +1420,13 @@ export class SessionHistoryService {
|
|
|
1391
1420
|
? this.sessionSyncService.readRecentHistory(provider, providerSessionId, rawStoreRef, knownTotalMessageCount, limit)
|
|
1392
1421
|
: this.sessionSyncService.readHistory(provider, providerSessionId, rawStoreRef, cursor, limit, direction);
|
|
1393
1422
|
return historyTask
|
|
1394
|
-
.then((page) => {
|
|
1395
|
-
const
|
|
1423
|
+
.then(async (page) => {
|
|
1424
|
+
const sanitizedPage = await this.sanitizeForkHistoryPage(sessionId, page, cursor, direction);
|
|
1425
|
+
const messagesWithAttachments = this.sessionMessageAttachmentService.enrichMessages(sessionId, sanitizedPage.messages);
|
|
1396
1426
|
const messages = this.enrichMessagesWithOrigin(sessionId, messagesWithAttachments);
|
|
1397
1427
|
this.persistSessionChangedFiles(sessionId, messages);
|
|
1398
1428
|
return {
|
|
1399
|
-
...
|
|
1429
|
+
...sanitizedPage,
|
|
1400
1430
|
messages
|
|
1401
1431
|
};
|
|
1402
1432
|
})
|
|
@@ -1415,6 +1445,56 @@ export class SessionHistoryService {
|
|
|
1415
1445
|
enrichMessagesWithOrigin(sessionId, messages) {
|
|
1416
1446
|
return this.resolveMessageOrigins(sessionId, messages);
|
|
1417
1447
|
}
|
|
1448
|
+
async sanitizeForkHistoryPage(sessionId, page, cursor, direction) {
|
|
1449
|
+
if (direction !== "forward" || cursor !== null || page.messages.length === 0) {
|
|
1450
|
+
return page;
|
|
1451
|
+
}
|
|
1452
|
+
const forkRecord = this.sessionForkRepository.findBySessionId(sessionId);
|
|
1453
|
+
if (!forkRecord
|
|
1454
|
+
|| forkRecord.forkSourceType !== "message"
|
|
1455
|
+
|| !forkRecord.forkSourceMessageId) {
|
|
1456
|
+
return page;
|
|
1457
|
+
}
|
|
1458
|
+
const childSession = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
|
|
1459
|
+
const childCreatedAt = childSession?.createdAt?.trim() || null;
|
|
1460
|
+
if (!childCreatedAt) {
|
|
1461
|
+
return page;
|
|
1462
|
+
}
|
|
1463
|
+
const parentBinding = this.getBindingOrThrow(forkRecord.forkSourceSessionId);
|
|
1464
|
+
const inheritedMessages = await this.readForkSourceMessages(forkRecord.forkSourceSessionId, parentBinding, "message", forkRecord.forkSourceMessageId, null);
|
|
1465
|
+
const expectedInheritedCount = inheritedMessages.length;
|
|
1466
|
+
if (expectedInheritedCount <= 0) {
|
|
1467
|
+
return page;
|
|
1468
|
+
}
|
|
1469
|
+
const parentMessages = await this.readForkSourceMessages(forkRecord.forkSourceSessionId, parentBinding, "session", null, null);
|
|
1470
|
+
let leakedInheritedCount = countCommonHistoryPrefixLength(page.messages.slice(expectedInheritedCount), parentMessages.slice(expectedInheritedCount));
|
|
1471
|
+
if (leakedInheritedCount <= 0) {
|
|
1472
|
+
for (let index = expectedInheritedCount; index < page.messages.length; index += 1) {
|
|
1473
|
+
const message = page.messages[index];
|
|
1474
|
+
if (!message || message.timestamp > childCreatedAt) {
|
|
1475
|
+
break;
|
|
1476
|
+
}
|
|
1477
|
+
leakedInheritedCount += 1;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
if (forkRecord.inheritedPrefixMessageCount !== expectedInheritedCount) {
|
|
1481
|
+
this.sessionForkRepository.upsert({
|
|
1482
|
+
...forkRecord,
|
|
1483
|
+
inheritedPrefixMessageCount: expectedInheritedCount
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
if (leakedInheritedCount <= 0) {
|
|
1487
|
+
return page;
|
|
1488
|
+
}
|
|
1489
|
+
return {
|
|
1490
|
+
...page,
|
|
1491
|
+
messages: [
|
|
1492
|
+
...page.messages.slice(0, expectedInheritedCount),
|
|
1493
|
+
...page.messages.slice(expectedInheritedCount + leakedInheritedCount)
|
|
1494
|
+
],
|
|
1495
|
+
total: Math.max(0, page.total - leakedInheritedCount)
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1418
1498
|
resolveMessageOrigins(sessionId, messages) {
|
|
1419
1499
|
const originRepository = this.sessionMessageOriginRepository;
|
|
1420
1500
|
if (!originRepository || messages.length === 0) {
|
|
@@ -1669,7 +1749,13 @@ export class SessionHistoryService {
|
|
|
1669
1749
|
return workspace;
|
|
1670
1750
|
}
|
|
1671
1751
|
getSessionListItemOrThrow(sessionId, userId) {
|
|
1672
|
-
const
|
|
1752
|
+
const canonicalSessionId = this.resolveCanonicalSessionId(sessionId, userId);
|
|
1753
|
+
const item = this.findSessionListItem(canonicalSessionId, sessionId, userId)
|
|
1754
|
+
?? this.repairMissingSessionListItem(canonicalSessionId, userId)
|
|
1755
|
+
?? (canonicalSessionId === sessionId
|
|
1756
|
+
? null
|
|
1757
|
+
: this.repairMissingSessionListItem(sessionId, userId))
|
|
1758
|
+
?? this.findSessionListItem(canonicalSessionId, sessionId, userId);
|
|
1673
1759
|
if (!item) {
|
|
1674
1760
|
throw new AppError({
|
|
1675
1761
|
statusCode: 500,
|
|
@@ -1683,6 +1769,75 @@ export class SessionHistoryService {
|
|
|
1683
1769
|
}
|
|
1684
1770
|
return this.sessionIndexRepository.findBySessionId(aliasTargetSessionId, userId) ?? item;
|
|
1685
1771
|
}
|
|
1772
|
+
findSessionListItem(canonicalSessionId, sessionId, userId) {
|
|
1773
|
+
return (this.sessionIndexRepository.findBySessionId(canonicalSessionId, userId)
|
|
1774
|
+
?? (canonicalSessionId === sessionId
|
|
1775
|
+
? null
|
|
1776
|
+
: this.sessionIndexRepository.findBySessionId(sessionId, userId)));
|
|
1777
|
+
}
|
|
1778
|
+
repairMissingSessionListItem(sessionId, userId) {
|
|
1779
|
+
const binding = this.sessionBindingRepository.findBySessionId(sessionId);
|
|
1780
|
+
if (!binding) {
|
|
1781
|
+
return null;
|
|
1782
|
+
}
|
|
1783
|
+
const existingIndex = this.sessionIndexRepository.findIndexRecordBySessionId(sessionId);
|
|
1784
|
+
const existingSnapshot = this.sessionStatusSnapshotRepository.findBySessionId(sessionId);
|
|
1785
|
+
const existingState = this.sessionStateRepository.findBySessionAndUser(sessionId, userId);
|
|
1786
|
+
const timestamp = nowIso();
|
|
1787
|
+
const fallbackLastMessageAt = existingIndex?.lastMessageAt
|
|
1788
|
+
?? existingState?.lastEventAt
|
|
1789
|
+
?? existingSnapshot?.lastSyncAt
|
|
1790
|
+
?? null;
|
|
1791
|
+
const fallbackCreatedAt = pickEarlierIso(binding.createdAt, existingIndex?.createdAt ?? null)
|
|
1792
|
+
?? timestamp;
|
|
1793
|
+
this.db.transaction(() => {
|
|
1794
|
+
this.sessionIndexRepository.upsert({
|
|
1795
|
+
sessionId,
|
|
1796
|
+
workspaceId: binding.workspaceId,
|
|
1797
|
+
provider: binding.provider,
|
|
1798
|
+
parentSessionId: existingIndex?.parentSessionId ?? this.sessionForkRepository.findBySessionId(sessionId)?.parentSessionId ?? null,
|
|
1799
|
+
sessionKind: existingIndex?.sessionKind ?? "default",
|
|
1800
|
+
annotationSourceMessageId: existingIndex?.annotationSourceMessageId ?? null,
|
|
1801
|
+
annotationSourceText: existingIndex?.annotationSourceText ?? null,
|
|
1802
|
+
isSubagent: existingIndex?.isSubagent ?? false,
|
|
1803
|
+
subagentLabel: existingIndex?.subagentLabel ?? null,
|
|
1804
|
+
title: existingIndex?.title?.trim()
|
|
1805
|
+
|| buildRecoveredSessionTitle(binding.provider, binding.providerSessionId),
|
|
1806
|
+
messageCount: existingIndex?.messageCount ?? 0,
|
|
1807
|
+
isArchived: existingIndex?.isArchived ?? false,
|
|
1808
|
+
lastMessageAt: fallbackLastMessageAt,
|
|
1809
|
+
createdAt: fallbackCreatedAt,
|
|
1810
|
+
updatedAt: timestamp
|
|
1811
|
+
});
|
|
1812
|
+
if (!existingSnapshot) {
|
|
1813
|
+
this.sessionStatusSnapshotRepository.upsert({
|
|
1814
|
+
sessionId,
|
|
1815
|
+
syncStatus: "idle",
|
|
1816
|
+
syncCursor: null,
|
|
1817
|
+
lastSyncAt: fallbackLastMessageAt,
|
|
1818
|
+
lastErrorCode: null,
|
|
1819
|
+
lastErrorDetail: null,
|
|
1820
|
+
resumedAt: null,
|
|
1821
|
+
updatedAt: timestamp
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
if (!existingState) {
|
|
1825
|
+
this.sessionStateRepository.upsert({
|
|
1826
|
+
sessionId,
|
|
1827
|
+
userId,
|
|
1828
|
+
runningState: inferRecoveredSessionRunningState(binding),
|
|
1829
|
+
activitySource: inferRecoveredSessionActivitySource(binding),
|
|
1830
|
+
favorite: false,
|
|
1831
|
+
lastEventAt: shouldRecoverSessionAsActive(binding) ? (binding.updatedAt || timestamp) : fallbackLastMessageAt,
|
|
1832
|
+
completedAt: null,
|
|
1833
|
+
lastSeenAt: null,
|
|
1834
|
+
updatedAt: timestamp
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
})();
|
|
1838
|
+
console.warn(`[session-history] repaired missing session index for ${sessionId} (${binding.provider})`);
|
|
1839
|
+
return this.sessionIndexRepository.findBySessionId(sessionId, userId);
|
|
1840
|
+
}
|
|
1686
1841
|
resolveCanonicalSessionId(sessionId, userId) {
|
|
1687
1842
|
if (userId) {
|
|
1688
1843
|
const item = this.sessionIndexRepository.findBySessionId(sessionId, userId);
|
|
@@ -1705,11 +1860,11 @@ export class SessionHistoryService {
|
|
|
1705
1860
|
return this.sessionBindingRepository.findBySessionId(aliasTargetSessionId);
|
|
1706
1861
|
}
|
|
1707
1862
|
findPendingSessionAliasTargetSessionId(descriptor) {
|
|
1708
|
-
if (!descriptor
|
|
1863
|
+
if (!descriptor) {
|
|
1709
1864
|
return null;
|
|
1710
1865
|
}
|
|
1711
|
-
const aliasTargetSessionId =
|
|
1712
|
-
??
|
|
1866
|
+
const aliasTargetSessionId = extractSessionAliasTargetSessionId(descriptor.providerSessionId)
|
|
1867
|
+
?? extractSessionAliasTargetSessionId(descriptor.rawStoreRef);
|
|
1713
1868
|
if (!aliasTargetSessionId || aliasTargetSessionId === descriptor.sessionId) {
|
|
1714
1869
|
return null;
|
|
1715
1870
|
}
|
|
@@ -1794,14 +1949,13 @@ export class SessionHistoryService {
|
|
|
1794
1949
|
});
|
|
1795
1950
|
await runBatchedTransactions(staleHiddenSessions.map((session) => session.sessionId), WORKSPACE_DISCOVERY_PERSIST_BATCH_SIZE, deleteTransaction);
|
|
1796
1951
|
}
|
|
1797
|
-
|
|
1798
|
-
if (!currentBinding || !isPendingBindingValue(currentBinding.providerSessionId)) {
|
|
1799
|
-
return null;
|
|
1800
|
-
}
|
|
1952
|
+
findSameWorkspaceBindingDuplicate(sessionId, workspaceId, snapshot) {
|
|
1801
1953
|
if (isPendingBindingValue(snapshot.providerSessionId)) {
|
|
1802
1954
|
return null;
|
|
1803
1955
|
}
|
|
1804
|
-
const existing = this.sessionBindingRepository.findByProviderSession(snapshot.provider, snapshot.providerSessionId) ??
|
|
1956
|
+
const existing = this.sessionBindingRepository.findByProviderSession(snapshot.provider, snapshot.providerSessionId) ?? (shouldMatchSessionBindingByRawStoreRef(snapshot.provider)
|
|
1957
|
+
? this.sessionBindingRepository.findByRawStoreRef(snapshot.provider, snapshot.rawStoreRef)
|
|
1958
|
+
: null);
|
|
1805
1959
|
if (!existing || existing.sessionId === sessionId) {
|
|
1806
1960
|
return null;
|
|
1807
1961
|
}
|
|
@@ -1814,11 +1968,22 @@ export class SessionHistoryService {
|
|
|
1814
1968
|
if (input.targetSessionId === input.sourceSessionId) {
|
|
1815
1969
|
return;
|
|
1816
1970
|
}
|
|
1817
|
-
const targetBinding = this.sessionBindingRepository.findBySessionId(input.targetSessionId);
|
|
1818
1971
|
const sourceBinding = this.sessionBindingRepository.findBySessionId(input.sourceSessionId);
|
|
1819
|
-
if (!
|
|
1972
|
+
if (!sourceBinding) {
|
|
1820
1973
|
return;
|
|
1821
1974
|
}
|
|
1975
|
+
const targetBinding = this.sessionBindingRepository.findBySessionId(input.targetSessionId);
|
|
1976
|
+
if (!targetBinding) {
|
|
1977
|
+
this.sessionBindingRepository.upsert({
|
|
1978
|
+
sessionId: input.targetSessionId,
|
|
1979
|
+
workspaceId: input.workspaceId,
|
|
1980
|
+
provider: input.provider,
|
|
1981
|
+
providerSessionId: buildPendingBindingValue(input.provider, input.targetSessionId),
|
|
1982
|
+
rawStoreRef: buildPendingBindingValue(input.provider, input.targetSessionId),
|
|
1983
|
+
createdAt: sourceBinding.createdAt,
|
|
1984
|
+
updatedAt: input.timestamp
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1822
1987
|
const targetIndex = this.sessionIndexRepository.findIndexRecordBySessionId(input.targetSessionId);
|
|
1823
1988
|
const sourceIndex = this.sessionIndexRepository.findIndexRecordBySessionId(input.sourceSessionId);
|
|
1824
1989
|
const targetSnapshot = this.sessionStatusSnapshotRepository.findBySessionId(input.targetSessionId);
|
|
@@ -1889,12 +2054,22 @@ export class SessionHistoryService {
|
|
|
1889
2054
|
this.db
|
|
1890
2055
|
.prepare("DELETE FROM session_forks WHERE session_id = ?")
|
|
1891
2056
|
.run(input.sourceSessionId);
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
.
|
|
1897
|
-
|
|
2057
|
+
// 保留旧 session_id 作为 alias,避免前端或 Butler 还拿着旧 id 时直接炸成 SESSION_NOT_FOUND。
|
|
2058
|
+
this.sessionBindingRepository.upsert({
|
|
2059
|
+
sessionId: input.sourceSessionId,
|
|
2060
|
+
workspaceId: sourceBinding.workspaceId,
|
|
2061
|
+
provider: sourceBinding.provider,
|
|
2062
|
+
providerSessionId: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
|
|
2063
|
+
rawStoreRef: buildAliasBindingValue(input.provider, input.targetSessionId, input.sourceSessionId),
|
|
2064
|
+
createdAt: sourceBinding.createdAt,
|
|
2065
|
+
updatedAt: input.timestamp
|
|
2066
|
+
});
|
|
2067
|
+
if (sourceIndex) {
|
|
2068
|
+
this.sessionIndexRepository.upsert({
|
|
2069
|
+
...sourceIndex,
|
|
2070
|
+
updatedAt: input.timestamp
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
1898
2073
|
this.rewriteWorkspaceSessionRelations(input.workspaceId, input.targetSessionId, input.sourceSessionId, targetIndex, sourceIndex);
|
|
1899
2074
|
}
|
|
1900
2075
|
listSessionStatesBySessionId(sessionId) {
|
|
@@ -2020,6 +2195,7 @@ export class SessionHistoryService {
|
|
|
2020
2195
|
}
|
|
2021
2196
|
buildKnownSessionSummaries(sessions, workspacePath) {
|
|
2022
2197
|
return sessions
|
|
2198
|
+
.filter((session) => !this.isPendingSessionAlias(session))
|
|
2023
2199
|
.filter((session) => !shouldSkipClaudePendingBinding(session))
|
|
2024
2200
|
.map((session) => {
|
|
2025
2201
|
const stats = safeStat(session.rawStoreRef);
|
|
@@ -2039,19 +2215,28 @@ export class SessionHistoryService {
|
|
|
2039
2215
|
async refreshSessionState(sessionId, userId) {
|
|
2040
2216
|
const binding = this.getBindingOrThrow(sessionId);
|
|
2041
2217
|
const current = this.sessionStateRepository.findBySessionAndUser(sessionId, userId);
|
|
2042
|
-
const inspection = inspectSessionActivity(binding.provider, binding.rawStoreRef);
|
|
2043
2218
|
const timestamp = nowIso();
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
if (
|
|
2049
|
-
|
|
2219
|
+
const liveObservation = this.resolveLiveActivityObservation(sessionId);
|
|
2220
|
+
const inspection = liveObservation
|
|
2221
|
+
? null
|
|
2222
|
+
: inspectSessionActivity(binding.provider, binding.rawStoreRef);
|
|
2223
|
+
if (inspection) {
|
|
2224
|
+
const nowMs = Date.parse(timestamp);
|
|
2225
|
+
if (shouldClearStaleRuntimeWithoutInspection(current, inspection, nowMs)) {
|
|
2226
|
+
this.sessionActivityAuthorityService.clearSession(sessionId);
|
|
2227
|
+
}
|
|
2228
|
+
if (shouldPreserveRuntimeTerminalState(current, inspection)) {
|
|
2229
|
+
return current;
|
|
2230
|
+
}
|
|
2050
2231
|
}
|
|
2051
|
-
const resolution =
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2232
|
+
const resolution = liveObservation
|
|
2233
|
+
? this.sessionActivityAuthorityService.observe(liveObservation)
|
|
2234
|
+
: this.sessionActivityAuthorityService.observe(buildInspectionActivityObservation(sessionId, inspection, timestamp));
|
|
2235
|
+
const resolvedLastEventAt = liveObservation
|
|
2236
|
+
? resolution.lastObservedAt ?? current?.lastEventAt ?? null
|
|
2237
|
+
: inspection && hasInspectionEvidence(inspection)
|
|
2238
|
+
? resolution.lastObservedAt ?? inspection.lastEventAt ?? current?.lastEventAt ?? null
|
|
2239
|
+
: current?.lastEventAt ?? null;
|
|
2055
2240
|
const nextRecord = {
|
|
2056
2241
|
sessionId,
|
|
2057
2242
|
userId,
|
|
@@ -2060,7 +2245,7 @@ export class SessionHistoryService {
|
|
|
2060
2245
|
favorite: current?.favorite ?? false,
|
|
2061
2246
|
lastEventAt: resolvedLastEventAt,
|
|
2062
2247
|
completedAt: isTerminalResolvedRunningState(resolution.runningState)
|
|
2063
|
-
? resolution.terminalAt ?? inspection
|
|
2248
|
+
? resolution.terminalAt ?? inspection?.completedAtCandidate ?? current?.completedAt ?? null
|
|
2064
2249
|
: null,
|
|
2065
2250
|
lastSeenAt: current?.lastSeenAt ?? null,
|
|
2066
2251
|
updatedAt: timestamp
|
|
@@ -2078,8 +2263,8 @@ export class SessionHistoryService {
|
|
|
2078
2263
|
syncCursor: currentSnapshot?.syncCursor ?? null,
|
|
2079
2264
|
lastSyncAt: resolution.lastObservedAt
|
|
2080
2265
|
?? resolution.terminalAt
|
|
2081
|
-
?? inspection
|
|
2082
|
-
?? inspection
|
|
2266
|
+
?? inspection?.lastEventAt
|
|
2267
|
+
?? inspection?.completedAtCandidate
|
|
2083
2268
|
?? currentSnapshot?.lastSyncAt
|
|
2084
2269
|
?? null,
|
|
2085
2270
|
lastErrorCode: resolution.runningState === "failed"
|
|
@@ -2097,17 +2282,34 @@ export class SessionHistoryService {
|
|
|
2097
2282
|
});
|
|
2098
2283
|
return nextRecord;
|
|
2099
2284
|
}
|
|
2285
|
+
resolveLiveActivityObservation(sessionId) {
|
|
2286
|
+
for (const resolver of this.liveActivityObservationResolvers) {
|
|
2287
|
+
const observation = resolver(sessionId);
|
|
2288
|
+
if (observation) {
|
|
2289
|
+
return observation;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2100
2294
|
upsertSnapshot(sessionId, input) {
|
|
2295
|
+
const resolvedSessionId = this.resolveCanonicalSessionId(sessionId);
|
|
2296
|
+
if (!this.sessionBindingRepository.findBySessionId(resolvedSessionId)) {
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2101
2299
|
this.sessionStatusSnapshotRepository.upsert({
|
|
2102
|
-
sessionId,
|
|
2300
|
+
sessionId: resolvedSessionId,
|
|
2103
2301
|
...input,
|
|
2104
2302
|
updatedAt: nowIso()
|
|
2105
2303
|
});
|
|
2106
2304
|
}
|
|
2107
2305
|
markSessionError(sessionId, errorCode, error) {
|
|
2108
|
-
const
|
|
2306
|
+
const resolvedSessionId = this.resolveCanonicalSessionId(sessionId);
|
|
2307
|
+
if (!this.sessionBindingRepository.findBySessionId(resolvedSessionId)) {
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
2310
|
+
const current = this.sessionStatusSnapshotRepository.findBySessionId(resolvedSessionId);
|
|
2109
2311
|
this.sessionStatusSnapshotRepository.upsert({
|
|
2110
|
-
sessionId,
|
|
2312
|
+
sessionId: resolvedSessionId,
|
|
2111
2313
|
syncStatus: "error",
|
|
2112
2314
|
syncCursor: current?.syncCursor ?? null,
|
|
2113
2315
|
lastSyncAt: current?.lastSyncAt ?? null,
|
|
@@ -2167,6 +2369,7 @@ function buildInspectionActivityObservation(sessionId, inspection, observedAt) {
|
|
|
2167
2369
|
source: hasInspectionEvidence(inspection) ? "inferred_log" : "unknown",
|
|
2168
2370
|
confidence: "weak",
|
|
2169
2371
|
detail: inspection.errorDetail,
|
|
2372
|
+
interruptSource: null,
|
|
2170
2373
|
errorCode: inspection.errorCode,
|
|
2171
2374
|
observedAt: inspection.completedAtCandidate ?? inspection.lastEventAt ?? observedAt
|
|
2172
2375
|
};
|
|
@@ -2177,23 +2380,28 @@ function hasInspectionEvidence(inspection) {
|
|
|
2177
2380
|
|| !!inspection.completedAtCandidate;
|
|
2178
2381
|
}
|
|
2179
2382
|
function applySessionActivityResolution(item, resolution) {
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2383
|
+
const rawResolvedRunningState = resolution.runningState === "unknown" && item.runningState === null
|
|
2384
|
+
? null
|
|
2385
|
+
: resolution.runningState;
|
|
2386
|
+
const resolvedRunningState = resolution.activityResolutionSource === "inferred_log" && rawResolvedRunningState === "completed"
|
|
2387
|
+
? "idle"
|
|
2388
|
+
: rawResolvedRunningState;
|
|
2389
|
+
const shouldClearResolvedFailure = resolvedRunningState !== "failed" && item.runningState === "failed";
|
|
2182
2390
|
const lastEventAt = resolution.lastObservedAt ?? item.lastEventAt;
|
|
2183
|
-
const completedAt = isTerminalResolvedRunningState(
|
|
2391
|
+
const completedAt = rawResolvedRunningState && isTerminalResolvedRunningState(rawResolvedRunningState)
|
|
2184
2392
|
? resolution.terminalAt ?? item.completedAt
|
|
2185
2393
|
: null;
|
|
2186
|
-
const lastErrorCode =
|
|
2394
|
+
const lastErrorCode = resolvedRunningState === "failed"
|
|
2187
2395
|
? resolution.errorCode ?? item.lastErrorCode
|
|
2188
2396
|
: shouldClearResolvedFailure
|
|
2189
2397
|
? null
|
|
2190
2398
|
: item.lastErrorCode;
|
|
2191
|
-
const lastErrorDetail =
|
|
2399
|
+
const lastErrorDetail = resolvedRunningState === "failed"
|
|
2192
2400
|
? resolution.detail ?? item.lastErrorDetail
|
|
2193
2401
|
: shouldClearResolvedFailure
|
|
2194
2402
|
? null
|
|
2195
2403
|
: item.lastErrorDetail;
|
|
2196
|
-
const syncStatus =
|
|
2404
|
+
const syncStatus = resolvedRunningState === "failed"
|
|
2197
2405
|
? "error"
|
|
2198
2406
|
: shouldClearResolvedFailure && item.syncStatus === "error"
|
|
2199
2407
|
? "idle"
|
|
@@ -2201,7 +2409,7 @@ function applySessionActivityResolution(item, resolution) {
|
|
|
2201
2409
|
return {
|
|
2202
2410
|
...item,
|
|
2203
2411
|
syncStatus,
|
|
2204
|
-
runningState,
|
|
2412
|
+
runningState: resolvedRunningState,
|
|
2205
2413
|
activitySource: mapResolutionSourceToCompatibilitySource(resolution.activityResolutionSource),
|
|
2206
2414
|
activityResolutionSource: resolution.activityResolutionSource,
|
|
2207
2415
|
activityConfidence: resolution.activityConfidence,
|
|
@@ -2211,7 +2419,7 @@ function applySessionActivityResolution(item, resolution) {
|
|
|
2211
2419
|
lastErrorCode,
|
|
2212
2420
|
lastErrorDetail,
|
|
2213
2421
|
watchdogTriggeredAt: resolution.watchdogTriggeredAt,
|
|
2214
|
-
activityState: resolveActivityState(
|
|
2422
|
+
activityState: resolveActivityState(resolvedRunningState, completedAt, item.lastSeenAt)
|
|
2215
2423
|
};
|
|
2216
2424
|
}
|
|
2217
2425
|
function clampLimit(limit) {
|
|
@@ -2395,6 +2603,35 @@ function normalizeSessionBindingSnapshot(sessionId, snapshot) {
|
|
|
2395
2603
|
rawStoreRef: buildPendingBindingValue("claude-code", sessionId)
|
|
2396
2604
|
};
|
|
2397
2605
|
}
|
|
2606
|
+
function countCommonHistoryPrefixLength(left, right) {
|
|
2607
|
+
const maxLength = Math.min(left.length, right.length);
|
|
2608
|
+
let count = 0;
|
|
2609
|
+
for (; count < maxLength; count += 1) {
|
|
2610
|
+
if (!areHistoryMessagesEquivalent(left[count], right[count])) {
|
|
2611
|
+
break;
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
return count;
|
|
2615
|
+
}
|
|
2616
|
+
function areHistoryMessagesEquivalent(left, right) {
|
|
2617
|
+
if (!left || !right) {
|
|
2618
|
+
return false;
|
|
2619
|
+
}
|
|
2620
|
+
if (left.messageId && right.messageId) {
|
|
2621
|
+
if (left.messageId === right.messageId) {
|
|
2622
|
+
return true;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
if (left.rawRef && right.rawRef) {
|
|
2626
|
+
if (left.rawRef === right.rawRef) {
|
|
2627
|
+
return true;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
return left.role === right.role
|
|
2631
|
+
&& left.kind === right.kind
|
|
2632
|
+
&& left.content === right.content
|
|
2633
|
+
&& left.timestamp === right.timestamp;
|
|
2634
|
+
}
|
|
2398
2635
|
function shouldSkipClaudePendingBinding(binding) {
|
|
2399
2636
|
if (binding.provider !== "claude-code") {
|
|
2400
2637
|
return false;
|
|
@@ -2407,9 +2644,27 @@ function shouldSkipClaudePendingBinding(binding) {
|
|
|
2407
2644
|
function isPendingBindingValue(value) {
|
|
2408
2645
|
return value.trim().toLowerCase().startsWith("pending://");
|
|
2409
2646
|
}
|
|
2647
|
+
function isSessionBindingProviderUniqueConflict(error) {
|
|
2648
|
+
if (!(error instanceof Error)) {
|
|
2649
|
+
return false;
|
|
2650
|
+
}
|
|
2651
|
+
return error.message.includes("UNIQUE constraint failed: session_bindings.provider, session_bindings.provider_session_id");
|
|
2652
|
+
}
|
|
2410
2653
|
function buildPendingBindingValue(provider, sessionId) {
|
|
2411
2654
|
return `pending://${provider}/${sessionId}`;
|
|
2412
2655
|
}
|
|
2656
|
+
function shouldRecoverSessionAsActive(binding) {
|
|
2657
|
+
return isPendingBindingValue(binding.providerSessionId) || isPendingBindingValue(binding.rawStoreRef);
|
|
2658
|
+
}
|
|
2659
|
+
function inferRecoveredSessionRunningState(binding) {
|
|
2660
|
+
return shouldRecoverSessionAsActive(binding) ? "starting" : "idle";
|
|
2661
|
+
}
|
|
2662
|
+
function inferRecoveredSessionActivitySource(binding) {
|
|
2663
|
+
return shouldRecoverSessionAsActive(binding) ? "runtime" : "none";
|
|
2664
|
+
}
|
|
2665
|
+
function buildAliasBindingValue(provider, targetSessionId, sourceSessionId) {
|
|
2666
|
+
return `alias://${provider}/${targetSessionId}/${sourceSessionId}`;
|
|
2667
|
+
}
|
|
2413
2668
|
function extractPendingBindingTargetSessionId(value) {
|
|
2414
2669
|
if (!isPendingBindingValue(value)) {
|
|
2415
2670
|
return null;
|
|
@@ -2418,6 +2673,25 @@ function extractPendingBindingTargetSessionId(value) {
|
|
|
2418
2673
|
const targetSessionId = normalizedValue.slice(normalizedValue.indexOf("/", "pending://".length) + 1).trim();
|
|
2419
2674
|
return targetSessionId || null;
|
|
2420
2675
|
}
|
|
2676
|
+
function extractAliasBindingTargetSessionId(value) {
|
|
2677
|
+
const normalizedValue = value.trim();
|
|
2678
|
+
if (!normalizedValue.toLowerCase().startsWith("alias://")) {
|
|
2679
|
+
return null;
|
|
2680
|
+
}
|
|
2681
|
+
const pathStart = normalizedValue.indexOf("/", "alias://".length);
|
|
2682
|
+
if (pathStart < 0) {
|
|
2683
|
+
return null;
|
|
2684
|
+
}
|
|
2685
|
+
const targetAndSource = normalizedValue.slice(pathStart + 1).trim();
|
|
2686
|
+
if (targetAndSource.length === 0) {
|
|
2687
|
+
return null;
|
|
2688
|
+
}
|
|
2689
|
+
const [targetSessionId] = targetAndSource.split("/", 1);
|
|
2690
|
+
return targetSessionId?.trim() || null;
|
|
2691
|
+
}
|
|
2692
|
+
function extractSessionAliasTargetSessionId(value) {
|
|
2693
|
+
return extractAliasBindingTargetSessionId(value) ?? extractPendingBindingTargetSessionId(value);
|
|
2694
|
+
}
|
|
2421
2695
|
function isClaudePendingRuntimeRawStoreRef(rawStoreRef) {
|
|
2422
2696
|
const normalizedRawStoreRef = rawStoreRef.replaceAll("\\", "/").toLowerCase();
|
|
2423
2697
|
return normalizedRawStoreRef.includes("/.pending-");
|
|
@@ -2671,6 +2945,9 @@ function isLegacyCodingNsRolloutSession(providerSessionId, rawStoreRef) {
|
|
|
2671
2945
|
function shouldRemoveMissingSyntheticCodexSession(rawStoreRef) {
|
|
2672
2946
|
return isSyntheticCodexRawStoreRef(rawStoreRef) && !existsSync(rawStoreRef);
|
|
2673
2947
|
}
|
|
2948
|
+
function shouldMatchSessionBindingByRawStoreRef(provider) {
|
|
2949
|
+
return provider !== "codex";
|
|
2950
|
+
}
|
|
2674
2951
|
function resolveSessionListTitle(provider, existingTitle, fallbackContent, parentTitle = null) {
|
|
2675
2952
|
const normalizedExistingTitle = existingTitle?.trim() ?? "";
|
|
2676
2953
|
const normalizedParentTitle = parentTitle?.trim() ?? "";
|
|
@@ -2693,6 +2970,24 @@ function buildUserMessageTitle(content, fallbackTitle) {
|
|
|
2693
2970
|
const title = content.trim().replace(/\s+/g, " ");
|
|
2694
2971
|
return title.slice(0, 48) || fallbackTitle;
|
|
2695
2972
|
}
|
|
2973
|
+
function buildRecoveredSessionTitle(provider, providerSessionId) {
|
|
2974
|
+
if (isPendingBindingValue(providerSessionId)) {
|
|
2975
|
+
return "新会话";
|
|
2976
|
+
}
|
|
2977
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
2978
|
+
const providerLabel = normalizedProvider === "claude-code"
|
|
2979
|
+
? "Claude"
|
|
2980
|
+
: normalizedProvider === "codex"
|
|
2981
|
+
? "Codex"
|
|
2982
|
+
: normalizedProvider === "gemini"
|
|
2983
|
+
? "Gemini"
|
|
2984
|
+
: normalizedProvider === "kimi"
|
|
2985
|
+
? "Kimi"
|
|
2986
|
+
: normalizedProvider === "opencode"
|
|
2987
|
+
? "OpenCode"
|
|
2988
|
+
: provider;
|
|
2989
|
+
return `${providerLabel} 会话 ${providerSessionId.slice(0, 8)}`;
|
|
2990
|
+
}
|
|
2696
2991
|
function resolvePersistedSessionTitle(provider, discoveredTitle, existingTitle, parentTitle = null) {
|
|
2697
2992
|
const nextTitle = discoveredTitle.trim();
|
|
2698
2993
|
const currentTitle = existingTitle?.trim() ?? "";
|
|
@@ -2786,7 +3081,7 @@ function mapResolutionSourceToLegacyActivitySource(source, inspection) {
|
|
|
2786
3081
|
if (source === "authoritative_runtime" || source === "authoritative_provider_event") {
|
|
2787
3082
|
return "runtime";
|
|
2788
3083
|
}
|
|
2789
|
-
if (inspection.lastEventAt || inspection.completedAtCandidate) {
|
|
3084
|
+
if (inspection && (inspection.lastEventAt || inspection.completedAtCandidate)) {
|
|
2790
3085
|
return "inferred";
|
|
2791
3086
|
}
|
|
2792
3087
|
return "none";
|