@jingyi0605/codingns 0.3.0 → 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/dist/public/assets/{TerminalPage-Dfw1QUqW.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 +2 -0
- package/dist/server/config/env.js +35 -0
- 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 +13 -1
- package/dist/server/modules/butler/butler-control-session-service.js +55 -231
- 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 +8 -0
- package/dist/server/modules/butler/butler-session-service.js +205 -53
- 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/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 +3 -0
- 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 +43 -16
- 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 +3 -0
- package/dist/server/modules/sessions/session-history-service.js +259 -39
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +7 -3
- package/dist/server/modules/sessions/session-live-runtime-service.js +47 -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/tailscale/tailscale-manager.js +5 -1
- package/dist/server/modules/tailscale/tailscale-manager.js.map +1 -1
- 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/sessions.js +1 -1
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/routes/system.d.ts +2 -1
- package/dist/server/routes/system.js +3 -1
- package/dist/server/routes/system.js.map +1 -1
- package/dist/server/server/create-server.d.ts +4 -0
- package/dist/server/server/create-server.js +51 -7
- 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/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/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 +127 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +19 -1
- package/dist/server/types/domain.d.ts +37 -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-DR2rPNi7.css +0 -1
- package/dist/public/assets/index-DTOruahn.js +0 -114
|
@@ -1090,39 +1090,52 @@ export class SessionHistoryService {
|
|
|
1090
1090
|
providerSessionId: snapshot.providerSessionId,
|
|
1091
1091
|
rawStoreRef: snapshot.rawStoreRef
|
|
1092
1092
|
});
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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;
|
|
1107
1131
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
providerSessionId: resolvedSnapshot.providerSessionId,
|
|
1114
|
-
rawStoreRef: resolvedSnapshot.rawStoreRef,
|
|
1115
|
-
createdAt: pickEarlierIso(currentBinding?.createdAt ?? null, duplicateBinding?.createdAt ?? null)
|
|
1116
|
-
?? timestamp,
|
|
1117
|
-
updatedAt: timestamp
|
|
1118
|
-
});
|
|
1119
|
-
if (currentIndex) {
|
|
1120
|
-
this.sessionIndexRepository.upsert({
|
|
1121
|
-
...currentIndex,
|
|
1122
|
-
updatedAt: timestamp
|
|
1123
|
-
});
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
if (attempt === 0 && isSessionBindingProviderUniqueConflict(error)) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
throw error;
|
|
1124
1137
|
}
|
|
1125
|
-
}
|
|
1138
|
+
}
|
|
1126
1139
|
}
|
|
1127
1140
|
async runDiscoverWorkspaceSessions(workspaceId, userId, refreshStateMode = "inline") {
|
|
1128
1141
|
const startedAt = Date.now();
|
|
@@ -1162,7 +1175,9 @@ export class SessionHistoryService {
|
|
|
1162
1175
|
const claimedPendingSessionIds = new Set();
|
|
1163
1176
|
const persistPass1Transaction = this.db.transaction((batch) => {
|
|
1164
1177
|
for (const session of batch) {
|
|
1165
|
-
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);
|
|
1166
1181
|
if (exactExisting && exactExisting.workspaceId !== workspaceId) {
|
|
1167
1182
|
continue;
|
|
1168
1183
|
}
|
|
@@ -1405,12 +1420,13 @@ export class SessionHistoryService {
|
|
|
1405
1420
|
? this.sessionSyncService.readRecentHistory(provider, providerSessionId, rawStoreRef, knownTotalMessageCount, limit)
|
|
1406
1421
|
: this.sessionSyncService.readHistory(provider, providerSessionId, rawStoreRef, cursor, limit, direction);
|
|
1407
1422
|
return historyTask
|
|
1408
|
-
.then((page) => {
|
|
1409
|
-
const
|
|
1423
|
+
.then(async (page) => {
|
|
1424
|
+
const sanitizedPage = await this.sanitizeForkHistoryPage(sessionId, page, cursor, direction);
|
|
1425
|
+
const messagesWithAttachments = this.sessionMessageAttachmentService.enrichMessages(sessionId, sanitizedPage.messages);
|
|
1410
1426
|
const messages = this.enrichMessagesWithOrigin(sessionId, messagesWithAttachments);
|
|
1411
1427
|
this.persistSessionChangedFiles(sessionId, messages);
|
|
1412
1428
|
return {
|
|
1413
|
-
...
|
|
1429
|
+
...sanitizedPage,
|
|
1414
1430
|
messages
|
|
1415
1431
|
};
|
|
1416
1432
|
})
|
|
@@ -1429,6 +1445,56 @@ export class SessionHistoryService {
|
|
|
1429
1445
|
enrichMessagesWithOrigin(sessionId, messages) {
|
|
1430
1446
|
return this.resolveMessageOrigins(sessionId, messages);
|
|
1431
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
|
+
}
|
|
1432
1498
|
resolveMessageOrigins(sessionId, messages) {
|
|
1433
1499
|
const originRepository = this.sessionMessageOriginRepository;
|
|
1434
1500
|
if (!originRepository || messages.length === 0) {
|
|
@@ -1683,7 +1749,13 @@ export class SessionHistoryService {
|
|
|
1683
1749
|
return workspace;
|
|
1684
1750
|
}
|
|
1685
1751
|
getSessionListItemOrThrow(sessionId, userId) {
|
|
1686
|
-
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);
|
|
1687
1759
|
if (!item) {
|
|
1688
1760
|
throw new AppError({
|
|
1689
1761
|
statusCode: 500,
|
|
@@ -1697,6 +1769,75 @@ export class SessionHistoryService {
|
|
|
1697
1769
|
}
|
|
1698
1770
|
return this.sessionIndexRepository.findBySessionId(aliasTargetSessionId, userId) ?? item;
|
|
1699
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
|
+
}
|
|
1700
1841
|
resolveCanonicalSessionId(sessionId, userId) {
|
|
1701
1842
|
if (userId) {
|
|
1702
1843
|
const item = this.sessionIndexRepository.findBySessionId(sessionId, userId);
|
|
@@ -1812,7 +1953,9 @@ export class SessionHistoryService {
|
|
|
1812
1953
|
if (isPendingBindingValue(snapshot.providerSessionId)) {
|
|
1813
1954
|
return null;
|
|
1814
1955
|
}
|
|
1815
|
-
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);
|
|
1816
1959
|
if (!existing || existing.sessionId === sessionId) {
|
|
1817
1960
|
return null;
|
|
1818
1961
|
}
|
|
@@ -1825,11 +1968,22 @@ export class SessionHistoryService {
|
|
|
1825
1968
|
if (input.targetSessionId === input.sourceSessionId) {
|
|
1826
1969
|
return;
|
|
1827
1970
|
}
|
|
1828
|
-
const targetBinding = this.sessionBindingRepository.findBySessionId(input.targetSessionId);
|
|
1829
1971
|
const sourceBinding = this.sessionBindingRepository.findBySessionId(input.sourceSessionId);
|
|
1830
|
-
if (!
|
|
1972
|
+
if (!sourceBinding) {
|
|
1831
1973
|
return;
|
|
1832
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
|
+
}
|
|
1833
1987
|
const targetIndex = this.sessionIndexRepository.findIndexRecordBySessionId(input.targetSessionId);
|
|
1834
1988
|
const sourceIndex = this.sessionIndexRepository.findIndexRecordBySessionId(input.sourceSessionId);
|
|
1835
1989
|
const targetSnapshot = this.sessionStatusSnapshotRepository.findBySessionId(input.targetSessionId);
|
|
@@ -2215,6 +2369,7 @@ function buildInspectionActivityObservation(sessionId, inspection, observedAt) {
|
|
|
2215
2369
|
source: hasInspectionEvidence(inspection) ? "inferred_log" : "unknown",
|
|
2216
2370
|
confidence: "weak",
|
|
2217
2371
|
detail: inspection.errorDetail,
|
|
2372
|
+
interruptSource: null,
|
|
2218
2373
|
errorCode: inspection.errorCode,
|
|
2219
2374
|
observedAt: inspection.completedAtCandidate ?? inspection.lastEventAt ?? observedAt
|
|
2220
2375
|
};
|
|
@@ -2448,6 +2603,35 @@ function normalizeSessionBindingSnapshot(sessionId, snapshot) {
|
|
|
2448
2603
|
rawStoreRef: buildPendingBindingValue("claude-code", sessionId)
|
|
2449
2604
|
};
|
|
2450
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
|
+
}
|
|
2451
2635
|
function shouldSkipClaudePendingBinding(binding) {
|
|
2452
2636
|
if (binding.provider !== "claude-code") {
|
|
2453
2637
|
return false;
|
|
@@ -2460,9 +2644,24 @@ function shouldSkipClaudePendingBinding(binding) {
|
|
|
2460
2644
|
function isPendingBindingValue(value) {
|
|
2461
2645
|
return value.trim().toLowerCase().startsWith("pending://");
|
|
2462
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
|
+
}
|
|
2463
2653
|
function buildPendingBindingValue(provider, sessionId) {
|
|
2464
2654
|
return `pending://${provider}/${sessionId}`;
|
|
2465
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
|
+
}
|
|
2466
2665
|
function buildAliasBindingValue(provider, targetSessionId, sourceSessionId) {
|
|
2467
2666
|
return `alias://${provider}/${targetSessionId}/${sourceSessionId}`;
|
|
2468
2667
|
}
|
|
@@ -2746,6 +2945,9 @@ function isLegacyCodingNsRolloutSession(providerSessionId, rawStoreRef) {
|
|
|
2746
2945
|
function shouldRemoveMissingSyntheticCodexSession(rawStoreRef) {
|
|
2747
2946
|
return isSyntheticCodexRawStoreRef(rawStoreRef) && !existsSync(rawStoreRef);
|
|
2748
2947
|
}
|
|
2948
|
+
function shouldMatchSessionBindingByRawStoreRef(provider) {
|
|
2949
|
+
return provider !== "codex";
|
|
2950
|
+
}
|
|
2749
2951
|
function resolveSessionListTitle(provider, existingTitle, fallbackContent, parentTitle = null) {
|
|
2750
2952
|
const normalizedExistingTitle = existingTitle?.trim() ?? "";
|
|
2751
2953
|
const normalizedParentTitle = parentTitle?.trim() ?? "";
|
|
@@ -2768,6 +2970,24 @@ function buildUserMessageTitle(content, fallbackTitle) {
|
|
|
2768
2970
|
const title = content.trim().replace(/\s+/g, " ");
|
|
2769
2971
|
return title.slice(0, 48) || fallbackTitle;
|
|
2770
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
|
+
}
|
|
2771
2991
|
function resolvePersistedSessionTitle(provider, discoveredTitle, existingTitle, parentTitle = null) {
|
|
2772
2992
|
const nextTitle = discoveredTitle.trim();
|
|
2773
2993
|
const currentTitle = existingTitle?.trim() ?? "";
|