codex-overleaf-link 2.2.1 → 2.3.1

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.
Files changed (79) hide show
  1. package/README.md +25 -23
  2. package/extension/bootstrap/manifest.template.json +1 -1
  3. package/extension/runtime-manifest.json +5 -71
  4. package/extension/src/background.js +67 -63
  5. package/extension/src/backgroundUpdateCoordinator.js +128 -66
  6. package/extension/src/content/applyResultFormatters.js +2 -3
  7. package/extension/src/content/composerPanel.js +0 -2
  8. package/extension/src/content/contentRuntime.js +757 -1558
  9. package/extension/src/content/contextTray.js +4 -6
  10. package/extension/src/content/diagnosticsController.js +1 -2
  11. package/extension/src/content/diffReviewPanel.js +14 -13
  12. package/extension/src/content/generated/content.bundle.js +41605 -0
  13. package/extension/src/content/generated/content.bundle.meta.json +103 -0
  14. package/extension/src/content/legacyGlobalRegistry.js +129 -0
  15. package/extension/src/content/markdownText.js +17 -17
  16. package/extension/src/content/mathText.js +155 -9
  17. package/extension/src/content/modelPicker.js +1 -1
  18. package/extension/src/content/modelPickerSupport.js +10 -5
  19. package/extension/src/content/moduleRegistryKernel.js +125 -0
  20. package/extension/src/content/nativeCompatibilityController.js +313 -0
  21. package/extension/src/content/otWarmMirror.js +8 -6
  22. package/extension/src/content/pageBridgeClient.js +224 -0
  23. package/extension/src/content/panelMaintenance.js +8 -3
  24. package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
  25. package/extension/src/content/projectProviderSelection.js +4 -12
  26. package/extension/src/content/projectSessionCleanup.js +3 -3
  27. package/extension/src/content/providerSettingsCoordinator.js +59 -26
  28. package/extension/src/content/providerSettingsDialog.js +30 -18
  29. package/extension/src/content/recentProjects.js +15 -23
  30. package/extension/src/content/runGuidanceController.js +1 -1
  31. package/extension/src/content/runQueueScheduler.js +129 -29
  32. package/extension/src/content/runSettlementPersistence.js +51 -0
  33. package/extension/src/content/runTimelineView.js +17 -8
  34. package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
  35. package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
  36. package/extension/src/content/scopedPersistencePanelState.js +111 -0
  37. package/extension/src/content/sessionManager.js +15 -12
  38. package/extension/src/content/sessionPersistence.js +67 -8
  39. package/extension/src/content/updateNotice.js +10 -4
  40. package/extension/src/content/writebackOrchestrator.js +89 -68
  41. package/extension/src/contentScript.js +5 -1
  42. package/extension/src/page/trackedChangesLifecycle.js +16 -5
  43. package/extension/src/page/writebackRouter.js +6 -2
  44. package/extension/src/pageBridge.js +47 -98
  45. package/extension/src/shared/agentTranscript.js +18 -8
  46. package/extension/src/shared/compatibility.js +1 -1
  47. package/extension/src/shared/governanceRules.js +1 -1
  48. package/extension/src/shared/i18n.js +2 -4
  49. package/extension/src/shared/managedUpdateProjection.js +337 -0
  50. package/extension/src/shared/nativeRequestIdentity.js +60 -0
  51. package/extension/src/shared/otText.js +0 -19
  52. package/extension/src/shared/pageRpcContract.js +136 -0
  53. package/extension/src/shared/runExecutionSnapshot.js +187 -0
  54. package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
  55. package/extension/src/shared/runInputQueue.js +81 -34
  56. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
  57. package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
  58. package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
  59. package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
  60. package/extension/src/shared/sessionState.js +110 -59
  61. package/extension/src/shared/settlementFacts.js +351 -0
  62. package/extension/src/shared/storageDb.js +25 -10
  63. package/extension/src/shared/storageMigration.js +275 -61
  64. package/extension/src/shared/storageSessionClaims.js +60 -0
  65. package/extension/src/shared/updateConsent.js +4 -0
  66. package/extension/src/shared/updateRevocationIntent.js +109 -0
  67. package/extension/src/shared/writebackEvidenceProjection.js +150 -0
  68. package/extension/src/shared/writebackOperationEvidence.js +153 -0
  69. package/extension/src/shared/writebackSettlement.js +727 -0
  70. package/extension/styles/panel.css +18 -8
  71. package/native-host/src/codexPromptAssembly.js +1 -1
  72. package/native-host/src/codexRuntimeIdentity.js +1 -1
  73. package/native-host/src/codexSessionRunner.js +7 -13
  74. package/native-host/src/index.js +3 -69
  75. package/native-host/src/managedInstall.js +50 -8
  76. package/native-host/src/nativeTransportEnvelope.js +148 -0
  77. package/native-host/src/taskRunnerRuntime.js +14 -84
  78. package/native-host/src/updateManager.js +36 -1
  79. package/package.json +13 -5
@@ -1,12 +1,18 @@
1
1
  (function initStorageDb(root, factory) {
2
2
  if (typeof module === 'object' && module.exports) {
3
- module.exports = factory(require('./storageRunActions'), require('./runInputQueue'));
3
+ module.exports = factory(require('./storageRunActions'), require('./runInputQueue'),
4
+ require('./settlementFacts'), require('./storageSessionClaims'));
4
5
  } else {
5
- root.CodexOverleafStorageDb = factory(root.CodexOverleafStorageRunActions, root.CodexOverleafRunInputQueue);
6
- }
7
- })(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue) {
6
+ root.CodexOverleafModuleRegistry.define('StorageDb', [
7
+ 'StorageRunActions',
8
+ 'RunInputQueue',
9
+ 'SettlementFacts',
10
+ 'StorageSessionClaims'
11
+ ], factory);
12
+ }
13
+ })(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims) {
8
14
  'use strict';
9
- if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage) {
15
+ if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
10
16
  throw new Error('Codex Overleaf storage run-action helpers are unavailable.');
11
17
  }
12
18
  // IndexedDB versions are monotonic; v2.0 RC profiles have already opened v3.
@@ -127,6 +133,7 @@
127
133
  };
128
134
  });
129
135
  }
136
+ var claimSessionsForAccount = StorageSessionClaims.createSessionClaimer(openDb, function () { return IDBKeyRange; });
130
137
 
131
138
  function putRecord(storeName, record) {
132
139
  return openDb().then(function (db) {
@@ -280,7 +287,7 @@
280
287
  history: compactHistoryForStorage(input.history),
281
288
  runs: compactRunsForStorage(input.runs, options),
282
289
  task: normalizeDisplayTextForStorage(input.task, SESSION_STORAGE_LIMITS.taskChars),
283
- mode: typeof input.mode === 'string' ? input.mode : '', providerId: typeof input.providerId === 'string' && input.providerId ? input.providerId : 'builtin',
290
+ mode: normalizeCurrentMode(input.mode), providerId: typeof input.providerId === 'string' && input.providerId ? input.providerId : 'builtin',
284
291
  model: typeof input.model === 'string' ? input.model : '',
285
292
  reasoningEffort: typeof input.reasoningEffort === 'string' ? input.reasoningEffort : '',
286
293
  speedTier: typeof input.speedTier === 'string' ? input.speedTier : '',
@@ -429,13 +436,17 @@
429
436
 
430
437
  // --- Helpers ---
431
438
 
439
+ function normalizeCurrentMode(mode) {
440
+ return mode === 'auto' ? 'auto' : 'ask';
441
+ }
442
+
432
443
  function extractLightweightPrefs(state, projectId) {
433
444
  var prefs = {
434
445
  storageSchemaVersion: TARGET_SCHEMA_VERSION, providerId: typeof state.providerId === 'string' && state.providerId ? state.providerId : 'builtin',
435
446
  model: typeof state.model === 'string' ? state.model : '',
436
447
  reasoningEffort: typeof state.reasoningEffort === 'string' ? state.reasoningEffort : '',
437
448
  speedTier: typeof state.speedTier === 'string' ? state.speedTier : '',
438
- mode: typeof state.mode === 'string' ? state.mode : '',
449
+ mode: normalizeCurrentMode(state.mode),
439
450
  locale: typeof state.locale === 'string' ? state.locale : '',
440
451
  requireReviewing: state.requireReviewing !== false,
441
452
  autoRecompile: state.autoRecompile !== false,
@@ -688,6 +699,12 @@
688
699
  if (VALID_TRACKED_CHANGE_STATUSES[run.trackedChangeStatus] === true) {
689
700
  compact.trackedChangeStatus = run.trackedChangeStatus;
690
701
  }
702
+ var settlement = run.settlement && typeof run.settlement === 'object'
703
+ ? run.settlement
704
+ : run.settlementFacts;
705
+ if (settlement && typeof settlement === 'object') {
706
+ compact.settlement = compactStructuredEventValueForStorage(SettlementFacts.compactSettlementFacts(settlement), 0);
707
+ }
691
708
  return compact;
692
709
  }
693
710
 
@@ -1301,11 +1318,9 @@
1301
1318
  }
1302
1319
 
1303
1320
  // --- Utilities ---
1304
-
1305
1321
  function generateId(prefix) {
1306
1322
  return prefix + '_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
1307
1323
  }
1308
-
1309
1324
  function safeJsonStringify(value) {
1310
1325
  try {
1311
1326
  return JSON.stringify(value) || '';
@@ -1313,7 +1328,6 @@
1313
1328
  return '[unserializable]';
1314
1329
  }
1315
1330
  }
1316
-
1317
1331
  var hashString = StorageRunActions.hashString;
1318
1332
 
1319
1333
  return {
@@ -1325,6 +1339,7 @@
1325
1339
  putRecords: putRecords,
1326
1340
  getRecord: getRecord,
1327
1341
  getAllByIndex: getAllByIndex,
1342
+ claimSessionsForAccount: claimSessionsForAccount,
1328
1343
  deleteRecord: deleteRecord,
1329
1344
  deleteByIndex: deleteByIndex,
1330
1345
  clearStore: clearStore,
@@ -1,10 +1,10 @@
1
1
  (function initStorageMigration(root, factory) {
2
2
  if (typeof module === 'object' && module.exports) {
3
- module.exports = factory();
3
+ module.exports = factory(require('./storageDb'));
4
4
  } else {
5
- root.CodexOverleafStorageMigration = factory();
5
+ root.CodexOverleafModuleRegistry.define('StorageMigration', ['StorageDb'], factory);
6
6
  }
7
- })(typeof globalThis !== 'undefined' ? globalThis : window, function storageMigrationFactory() {
7
+ })(typeof globalThis !== 'undefined' ? globalThis : window, function storageMigrationFactory(StorageDb) {
8
8
  'use strict';
9
9
 
10
10
  var PREFS_KEY = 'codexOverleafPrefs';
@@ -13,74 +13,249 @@
13
13
  var MAX_DELETED_SESSION_IDS_PER_PROJECT = 100;
14
14
  var CUSTOM_INSTRUCTIONS_MAX_CHARS = 12000;
15
15
  var PROJECT_PREF_KEY_MAX_CHARS = 160;
16
+ var PROJECT_SCOPED_PREF_MAPS = [
17
+ 'activeSessionByProject',
18
+ 'experimentalOtByProject',
19
+ 'customInstructionsByProject',
20
+ 'governanceRulesByProject',
21
+ 'selectedLocalSkillIdsByProject'
22
+ ];
23
+ var projectPreferenceLockTails = {};
16
24
 
17
- function runMigrationIfNeeded(projectId, legacyStorageKey) {
18
- var StorageDb = (typeof window !== 'undefined' && window.CodexOverleafStorageDb)
19
- ? window.CodexOverleafStorageDb
20
- : require('./storageDb');
21
- return chrome.storage.local.get(null).then(function (stored) {
22
- var prefs = normalizePrefs(stored[PREFS_KEY] || {});
23
- var tombstones = collectSessionTombstones(stored);
24
- var deletedSessionIds = getDeletedSessionIds(tombstones, projectId);
25
- var schemaVersion = prefs.storageSchemaVersion || 0;
26
-
27
- if (schemaVersion >= StorageDb.TARGET_SCHEMA_VERSION) {
28
- var activeSessionByProject = prefs.activeSessionByProject || {};
29
- var activeSessionId = activeSessionByProject[projectId] || '';
30
- return StorageDb.getAllByIndex('sessions', 'projectId', projectId).then(function (sessions) {
31
- var visibleSessions = sessions.filter(function (session) {
32
- return deletedSessionIds.indexOf(session && session.id) === -1;
33
- });
34
- return { prefs: prefs, sessions: visibleSessions, activeSessionId: activeSessionId, migrated: false };
35
- });
25
+ function buildScopedProjectPreferenceKey(accountScopeId, projectId) {
26
+ var account = compactScopePart(accountScopeId);
27
+ var project = compactScopePart(projectId);
28
+ return account && project ? 'account:' + account + ':project:' + project : '';
29
+ }
30
+
31
+ function compactScopePart(value) {
32
+ var text = normalizeTextField(value, PROJECT_PREF_KEY_MAX_CHARS);
33
+ if (!text) {
34
+ return '';
35
+ }
36
+ var encoded = encodeURIComponent(text);
37
+ if (encoded.length <= 64) {
38
+ return encoded;
39
+ }
40
+ return encoded.slice(0, 48) + '-' + hashText(encoded);
41
+ }
42
+
43
+ function hashText(value) {
44
+ var hash = 2166136261;
45
+ for (var i = 0; i < value.length; i++) {
46
+ hash ^= value.charCodeAt(i);
47
+ hash = Math.imul(hash, 16777619);
48
+ }
49
+ return (hash >>> 0).toString(36);
50
+ }
51
+
52
+ function withProjectPreferenceLock(projectId, work) {
53
+ var lockName = 'codex-overleaf-project-prefs:' + compactScopePart(projectId);
54
+ var lockManager = typeof navigator !== 'undefined' && navigator && navigator.locks;
55
+ if (lockManager && typeof lockManager.request === 'function') {
56
+ return lockManager.request(lockName, work);
57
+ }
58
+ var previous = projectPreferenceLockTails[lockName] || Promise.resolve();
59
+ var current = previous.then(work, work);
60
+ projectPreferenceLockTails[lockName] = current;
61
+ return current.finally(function () {
62
+ if (projectPreferenceLockTails[lockName] === current) {
63
+ delete projectPreferenceLockTails[lockName];
36
64
  }
65
+ });
66
+ }
37
67
 
38
- // Migration v0 → v1
39
- var legacyBlob = stored[legacyStorageKey] || {};
40
- var legacySessions = Array.isArray(legacyBlob.sessions) ? legacyBlob.sessions : [];
41
- var migratedSessions = [];
42
-
43
- for (var i = 0; i < legacySessions.length; i++) {
44
- var legacy = legacySessions[i];
45
- if (!legacy || !legacy.id) { continue; }
46
- if (deletedSessionIds.indexOf(legacy.id) !== -1) { continue; }
47
- var record = StorageDb.buildSessionRecord(
48
- buildLegacySessionRecordInput(projectId, legacyBlob, legacy)
49
- );
50
- migratedSessions.push(record);
68
+ function copyPreferenceValue(value) {
69
+ if (!value || typeof value !== 'object') {
70
+ return value;
71
+ }
72
+ try {
73
+ return JSON.parse(JSON.stringify(value));
74
+ } catch (_error) {
75
+ return value;
76
+ }
77
+ }
78
+
79
+ function claimLegacyProjectPreferences(prefs, accountScopeId, projectId) {
80
+ var scopedKey = buildScopedProjectPreferenceKey(accountScopeId, projectId);
81
+ var next = normalizePrefs(prefs);
82
+ var changed = false;
83
+ PROJECT_SCOPED_PREF_MAPS.forEach(function (mapName) {
84
+ var source = next[mapName] && typeof next[mapName] === 'object'
85
+ ? Object.assign({}, next[mapName])
86
+ : {};
87
+ if (Object.prototype.hasOwnProperty.call(source, projectId)) {
88
+ if (!Object.prototype.hasOwnProperty.call(source, scopedKey)) {
89
+ source[scopedKey] = copyPreferenceValue(source[projectId]);
90
+ }
91
+ delete source[projectId];
92
+ changed = true;
93
+ }
94
+ next[mapName] = source;
95
+ });
96
+ return { prefs: next, changed: changed };
97
+ }
98
+
99
+ function buildProjectPreferenceView(prefs, accountScopeId, projectId) {
100
+ var source = normalizePrefs(prefs);
101
+ var scopedKey = buildScopedProjectPreferenceKey(accountScopeId, projectId);
102
+ var view = Object.assign({}, source);
103
+ PROJECT_SCOPED_PREF_MAPS.forEach(function (mapName) {
104
+ var sourceMap = source[mapName] && typeof source[mapName] === 'object'
105
+ ? source[mapName]
106
+ : {};
107
+ var projectMap = {};
108
+ if (Object.prototype.hasOwnProperty.call(sourceMap, scopedKey)) {
109
+ projectMap[projectId] = copyPreferenceValue(sourceMap[scopedKey]);
51
110
  }
111
+ view[mapName] = projectMap;
112
+ });
113
+ return normalizePrefs(view);
114
+ }
52
115
 
53
- var putPromise = migratedSessions.length
54
- ? StorageDb.putRecords('sessions', migratedSessions)
55
- : Promise.resolve([]);
116
+ function mergeScopedProjectPreferences(existingPrefs, incomingPrefs, accountScopeId, projectId) {
117
+ var existing = normalizePrefs(existingPrefs);
118
+ var incoming = normalizePrefs(incomingPrefs);
119
+ var scopedKey = buildScopedProjectPreferenceKey(accountScopeId, projectId);
120
+ var next = Object.assign({}, existing, incoming);
121
+ PROJECT_SCOPED_PREF_MAPS.forEach(function (mapName) {
122
+ var storedMap = existing[mapName] && typeof existing[mapName] === 'object'
123
+ ? Object.assign({}, existing[mapName])
124
+ : {};
125
+ var incomingMap = incoming[mapName] && typeof incoming[mapName] === 'object'
126
+ ? incoming[mapName]
127
+ : {};
128
+ if (Object.prototype.hasOwnProperty.call(incomingMap, projectId)) {
129
+ storedMap[scopedKey] = copyPreferenceValue(incomingMap[projectId]);
130
+ }
131
+ delete storedMap[projectId];
132
+ next[mapName] = storedMap;
133
+ });
134
+ return normalizePrefs(next);
135
+ }
56
136
 
57
- return putPromise.then(function () {
58
- var newPrefs = StorageDb.extractLightweightPrefs(legacyBlob, projectId);
59
- newPrefs.activeSessionByProject = StorageDb.buildActiveSessionByProject(
60
- {},
61
- projectId,
62
- legacyBlob.activeSessionId || (migratedSessions.length ? migratedSessions[migratedSessions.length - 1].id : '')
137
+ function runMigrationIfNeeded(projectId, legacyStorageKey, accountScopeId) {
138
+ projectId = normalizeProjectPrefKey(projectId);
139
+ var normalizedAccountScopeId = normalizeTextField(accountScopeId, PROJECT_PREF_KEY_MAX_CHARS);
140
+ if (!normalizedAccountScopeId || !projectId) {
141
+ return Promise.resolve({
142
+ prefs: normalizePrefs({}),
143
+ sessions: [],
144
+ activeSessionId: '',
145
+ migrated: false,
146
+ reason: 'account_scope_unavailable'
147
+ });
148
+ }
149
+ return withProjectPreferenceLock(projectId, function () {
150
+ return chrome.storage.local.get(null).then(function (stored) {
151
+ var claimed = claimLegacyProjectPreferences(
152
+ stored[PREFS_KEY] || {},
153
+ normalizedAccountScopeId,
154
+ projectId
63
155
  );
64
- newPrefs = normalizePrefs(newPrefs);
65
-
66
- return chrome.storage.local.set({ [PREFS_KEY]: newPrefs }).then(function () {
67
- return chrome.storage.local.remove(legacyStorageKey).catch(function () {});
68
- }).then(function () {
69
- return {
70
- prefs: newPrefs,
71
- sessions: migratedSessions,
72
- activeSessionId: newPrefs.activeSessionByProject[projectId] || '',
73
- migrated: true
74
- };
156
+ var storedPrefs = claimed.prefs;
157
+ var persistClaim = claimed.changed
158
+ ? chrome.storage.local.set({ [PREFS_KEY]: storedPrefs })
159
+ : Promise.resolve();
160
+ return persistClaim.then(function () {
161
+ var prefs = buildProjectPreferenceView(
162
+ storedPrefs,
163
+ normalizedAccountScopeId,
164
+ projectId
165
+ );
166
+ var tombstones = collectSessionTombstones(stored);
167
+ var deletedSessionIds = getDeletedSessionIds(tombstones, projectId);
168
+ var schemaVersion = prefs.storageSchemaVersion || 0;
169
+
170
+ if (schemaVersion >= StorageDb.TARGET_SCHEMA_VERSION) {
171
+ var activeSessionByProject = prefs.activeSessionByProject || {};
172
+ var activeSessionId = activeSessionByProject[projectId] || '';
173
+ if (typeof StorageDb.claimSessionsForAccount !== 'function') {
174
+ return Promise.reject(Object.assign(
175
+ new Error('Atomic legacy-session claiming is unavailable.'),
176
+ { code: 'scoped_claim_unavailable' }
177
+ ));
178
+ }
179
+ return StorageDb.claimSessionsForAccount(
180
+ projectId,
181
+ normalizedAccountScopeId,
182
+ deletedSessionIds
183
+ ).then(function (visibleSessions) {
184
+ var visibleActiveSessionId = visibleSessions.some(function (session) {
185
+ return session && session.id === activeSessionId;
186
+ })
187
+ ? activeSessionId
188
+ : visibleSessions.length
189
+ ? visibleSessions[visibleSessions.length - 1].id
190
+ : '';
191
+ return {
192
+ prefs: prefs,
193
+ sessions: visibleSessions,
194
+ activeSessionId: visibleActiveSessionId,
195
+ migrated: false
196
+ };
197
+ });
198
+ }
199
+
200
+ // Migration v0 → v1
201
+ var legacyBlob = stored[legacyStorageKey] || {};
202
+ var legacySessions = Array.isArray(legacyBlob.sessions) ? legacyBlob.sessions : [];
203
+ var migratedSessions = [];
204
+
205
+ for (var i = 0; i < legacySessions.length; i++) {
206
+ var legacy = legacySessions[i];
207
+ if (!legacy || !legacy.id) { continue; }
208
+ if (deletedSessionIds.indexOf(legacy.id) !== -1) { continue; }
209
+ var record = StorageDb.buildSessionRecord(
210
+ buildLegacySessionRecordInput(projectId, legacyBlob, legacy, normalizedAccountScopeId)
211
+ );
212
+ migratedSessions.push(record);
213
+ }
214
+
215
+ var putPromise = migratedSessions.length
216
+ ? StorageDb.putRecords('sessions', migratedSessions)
217
+ : Promise.resolve([]);
218
+
219
+ return putPromise.then(function () {
220
+ var newPrefs = StorageDb.extractLightweightPrefs(legacyBlob, projectId);
221
+ newPrefs.activeSessionByProject = StorageDb.buildActiveSessionByProject(
222
+ {},
223
+ projectId,
224
+ legacyBlob.activeSessionId || (migratedSessions.length ? migratedSessions[migratedSessions.length - 1].id : '')
225
+ );
226
+ var mergedPrefs = mergeScopedProjectPreferences(
227
+ storedPrefs,
228
+ newPrefs,
229
+ normalizedAccountScopeId,
230
+ projectId
231
+ );
232
+ var prefsView = buildProjectPreferenceView(
233
+ mergedPrefs,
234
+ normalizedAccountScopeId,
235
+ projectId
236
+ );
237
+
238
+ return chrome.storage.local.set({ [PREFS_KEY]: mergedPrefs }).then(function () {
239
+ return chrome.storage.local.remove(legacyStorageKey).catch(function () {});
240
+ }).then(function () {
241
+ return {
242
+ prefs: prefsView,
243
+ sessions: migratedSessions,
244
+ activeSessionId: prefsView.activeSessionByProject[projectId] || '',
245
+ migrated: true
246
+ };
247
+ });
248
+ });
75
249
  });
76
250
  });
77
251
  });
78
252
  }
79
253
 
80
- function buildLegacySessionRecordInput(projectId, legacyBlob, legacy) {
254
+ function buildLegacySessionRecordInput(projectId, legacyBlob, legacy, accountScopeId) {
81
255
  return {
82
256
  id: legacy.id,
83
257
  projectId: projectId,
258
+ accountScopeId: accountScopeId,
84
259
  title: legacy.title || '',
85
260
  titleSource: legacy.titleSource === 'manual' ? 'manual' : 'auto',
86
261
  codexThreadId: '',
@@ -100,13 +275,51 @@
100
275
  };
101
276
  }
102
277
 
103
- function savePrefs(prefs) {
104
- return chrome.storage.local.set({ [PREFS_KEY]: normalizePrefs(prefs) });
278
+ function savePrefs(prefs, accountScopeId, projectId) {
279
+ var account = normalizeTextField(accountScopeId, PROJECT_PREF_KEY_MAX_CHARS);
280
+ var project = normalizeProjectPrefKey(projectId);
281
+ if (!account && !project) {
282
+ return chrome.storage.local.set({ [PREFS_KEY]: normalizePrefs(prefs) });
283
+ }
284
+ if (!account || !project) {
285
+ return Promise.reject(Object.assign(
286
+ new Error('Scoped project preferences require account and project identity.'),
287
+ { code: 'account_scope_unavailable' }
288
+ ));
289
+ }
290
+ return withProjectPreferenceLock(project, function () {
291
+ return chrome.storage.local.get([PREFS_KEY]).then(function (stored) {
292
+ var claimed = claimLegacyProjectPreferences(stored[PREFS_KEY] || {}, account, project);
293
+ var next = mergeScopedProjectPreferences(claimed.prefs, prefs, account, project);
294
+ return chrome.storage.local.set({ [PREFS_KEY]: next });
295
+ });
296
+ });
105
297
  }
106
298
 
107
- function loadPrefs() {
108
- return chrome.storage.local.get([PREFS_KEY]).then(function (stored) {
109
- return normalizePrefs(stored[PREFS_KEY] || {});
299
+ function loadPrefs(accountScopeId, projectId) {
300
+ var account = normalizeTextField(accountScopeId, PROJECT_PREF_KEY_MAX_CHARS);
301
+ var project = normalizeProjectPrefKey(projectId);
302
+ if (!account && !project) {
303
+ return chrome.storage.local.get([PREFS_KEY]).then(function (stored) {
304
+ return normalizePrefs(stored[PREFS_KEY] || {});
305
+ });
306
+ }
307
+ if (!account || !project) {
308
+ return Promise.reject(Object.assign(
309
+ new Error('Scoped project preferences require account and project identity.'),
310
+ { code: 'account_scope_unavailable' }
311
+ ));
312
+ }
313
+ return withProjectPreferenceLock(project, function () {
314
+ return chrome.storage.local.get([PREFS_KEY]).then(function (stored) {
315
+ var claimed = claimLegacyProjectPreferences(stored[PREFS_KEY] || {}, account, project);
316
+ var persisted = claimed.changed
317
+ ? chrome.storage.local.set({ [PREFS_KEY]: claimed.prefs })
318
+ : Promise.resolve();
319
+ return persisted.then(function () {
320
+ return buildProjectPreferenceView(claimed.prefs, account, project);
321
+ });
322
+ });
110
323
  });
111
324
  }
112
325
 
@@ -278,6 +491,7 @@
278
491
  return {
279
492
  PREFS_KEY: PREFS_KEY,
280
493
  SESSION_TOMBSTONES_KEY: SESSION_TOMBSTONES_KEY,
494
+ buildScopedProjectPreferenceKey: buildScopedProjectPreferenceKey,
281
495
  runMigrationIfNeeded: runMigrationIfNeeded,
282
496
  savePrefs: savePrefs,
283
497
  loadPrefs: loadPrefs,
@@ -0,0 +1,60 @@
1
+ (function initStorageSessionClaims(root, factory) {
2
+ var api = factory();
3
+ if (typeof module === 'object' && module.exports) module.exports = api;
4
+ if (root) root.CodexOverleafStorageSessionClaims = api;
5
+ })(typeof globalThis !== 'undefined' ? globalThis : this, function storageSessionClaimsFactory() {
6
+ 'use strict';
7
+
8
+ function createSessionClaimer(openDb, getKeyRange) {
9
+ return function claimSessionsForAccount(projectId, accountScopeId, deletedSessionIds) {
10
+ var normalizedProjectId = typeof projectId === 'string' ? projectId.trim() : '';
11
+ var normalizedAccountScopeId = typeof accountScopeId === 'string' ? accountScopeId.trim() : '';
12
+ if (!normalizedProjectId || !normalizedAccountScopeId) {
13
+ return Promise.reject(Object.assign(
14
+ new Error('A project and account scope are required to claim legacy sessions.'),
15
+ { code: !normalizedAccountScopeId ? 'account_scope_unavailable' : 'project_scope_unavailable' }
16
+ ));
17
+ }
18
+ var deleted = {};
19
+ (Array.isArray(deletedSessionIds) ? deletedSessionIds : []).forEach(function (id) {
20
+ if (typeof id === 'string' && id) deleted[id] = true;
21
+ });
22
+ return openDb().then(function (db) {
23
+ return new Promise(function (resolve, reject) {
24
+ var visible = [];
25
+ var tx = db.transaction('sessions', 'readwrite');
26
+ var store = tx.objectStore('sessions');
27
+ var index = store.index('projectId');
28
+ var request = index.openCursor(getKeyRange().only(normalizedProjectId));
29
+ request.onsuccess = function (event) {
30
+ var cursor = event.target.result;
31
+ if (!cursor) return;
32
+ var record = cursor.value;
33
+ if (record && !deleted[record.id]) {
34
+ var recordScopeId = typeof record.accountScopeId === 'string'
35
+ ? record.accountScopeId.trim()
36
+ : '';
37
+ if (recordScopeId === normalizedAccountScopeId) {
38
+ visible.push(record);
39
+ } else if (!recordScopeId) {
40
+ var claimed = Object.assign({}, record, {
41
+ accountScopeId: normalizedAccountScopeId,
42
+ accountScopeUnavailable: false
43
+ });
44
+ cursor.update(claimed);
45
+ visible.push(claimed);
46
+ }
47
+ }
48
+ cursor.continue();
49
+ };
50
+ request.onerror = function (event) { reject(event.target.error); };
51
+ tx.oncomplete = function () { resolve(visible); };
52
+ tx.onerror = function (event) { reject(event.target.error || tx.error); };
53
+ tx.onabort = function (event) { reject(event.target.error || tx.error); };
54
+ });
55
+ });
56
+ };
57
+ }
58
+
59
+ return Object.freeze({ createSessionClaimer: createSessionClaimer });
60
+ });
@@ -87,6 +87,10 @@
87
87
  authorizedVersion: safeVersion(value.authorizedVersion),
88
88
  authorizationId: safeId(value.authorizationId),
89
89
  authorizedAt: finiteNumber(value.authorizedAt),
90
+ revokingAuthorizationId: safeId(value.revokingAuthorizationId),
91
+ revokingVersion: safeVersion(value.revokingVersion),
92
+ revokingTransactionId: safeId(value.revokingTransactionId),
93
+ revokingAt: finiteNumber(value.revokingAt),
90
94
  lastPromptedVersion: safeVersion(value.lastPromptedVersion),
91
95
  lastPromptedAt: finiteNumber(value.lastPromptedAt)
92
96
  };
@@ -0,0 +1,109 @@
1
+ (function initUpdateRevocationIntent(root, factory) {
2
+ const api = factory();
3
+ if (typeof module === "object" && module.exports) {
4
+ module.exports = api;
5
+ }
6
+ if (root) {
7
+ root.CodexOverleafUpdateRevocation = api;
8
+ }
9
+ })(typeof globalThis !== "undefined" ? globalThis : this, function createUpdateRevocationIntent() {
10
+ "use strict";
11
+
12
+ function safeString(value) {
13
+ return typeof value === "string" ? value.trim() : "";
14
+ }
15
+
16
+ function safeTime(value, fallback = 0) {
17
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
18
+ }
19
+
20
+ function begin(consentState, updateState, now = Date.now()) {
21
+ const consent = consentState && typeof consentState === "object" ? consentState : {};
22
+ const update = updateState && typeof updateState === "object" ? updateState : {};
23
+ return {
24
+ ...consent,
25
+ revokingAuthorizationId: safeString(consent.authorizationId),
26
+ revokingVersion:
27
+ safeString(consent.authorizedVersion) || safeString(update.latestVersion),
28
+ revokingTransactionId: safeString(update.transactionId),
29
+ revokingAt: safeTime(now)
30
+ };
31
+ }
32
+
33
+ function prepareAuthorization(consentState, authorizationId, targetVersion, now = Date.now()) {
34
+ const consent = consentState && typeof consentState === "object" ? consentState : {};
35
+ return {
36
+ ...consent,
37
+ revokingAuthorizationId: safeString(authorizationId),
38
+ revokingVersion: safeString(targetVersion),
39
+ revokingTransactionId: "",
40
+ revokingAt: safeTime(now)
41
+ };
42
+ }
43
+
44
+ function hasPending(consentState) {
45
+ const consent = consentState && typeof consentState === "object" ? consentState : {};
46
+ return Boolean(
47
+ safeString(consent.revokingAuthorizationId) &&
48
+ safeString(consent.revokingVersion)
49
+ );
50
+ }
51
+
52
+ function clear(consentState) {
53
+ const consent = consentState && typeof consentState === "object" ? consentState : {};
54
+ return {
55
+ ...consent,
56
+ revokingAuthorizationId: "",
57
+ revokingVersion: "",
58
+ revokingTransactionId: "",
59
+ revokingAt: 0
60
+ };
61
+ }
62
+
63
+ function complete(updateState, consentState, options = {}) {
64
+ const update = updateState && typeof updateState === "object" ? updateState : {};
65
+ const consent = consentState && typeof consentState === "object" ? consentState : {};
66
+ const now = safeTime(options.now, Date.now());
67
+ const snoozeMs = safeTime(options.snoozeMs);
68
+ const version =
69
+ safeString(consent.revokingVersion) ||
70
+ safeString(consent.authorizedVersion) ||
71
+ safeString(update.latestVersion);
72
+ return {
73
+ updateState: {
74
+ ...update,
75
+ state: "update_available",
76
+ transactionId: "",
77
+ stagedPath: "",
78
+ stagedAt: 0,
79
+ applyingAt: 0,
80
+ awaitingHealthAt: 0,
81
+ blocker: "",
82
+ blockers: [],
83
+ blockingReason: "",
84
+ code: "",
85
+ message: "",
86
+ error: null,
87
+ postponeUntil: safeTime(options.postponeUntil),
88
+ postponeGuardVersion: version
89
+ },
90
+ consentState: clear({
91
+ ...consent,
92
+ mode: "snoozed",
93
+ snoozedVersion: version,
94
+ snoozedUntil: now + snoozeMs,
95
+ authorizedVersion: "",
96
+ authorizationId: "",
97
+ authorizedAt: 0
98
+ })
99
+ };
100
+ }
101
+
102
+ return Object.freeze({
103
+ begin,
104
+ prepareAuthorization,
105
+ hasPending,
106
+ clear,
107
+ complete
108
+ });
109
+ });