codex-overleaf-link 2.3.6 → 2.4.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 (52) hide show
  1. package/README.md +62 -34
  2. package/README.zh-CN.md +546 -0
  3. package/extension/bootstrap/manifest.template.json +1 -1
  4. package/extension/src/background.js +3 -0
  5. package/extension/src/content/activeTurnControl.js +1 -0
  6. package/extension/src/content/assetTransferBroker.js +2 -0
  7. package/extension/src/content/contentRuntime.js +122 -118
  8. package/extension/src/content/contextTray.js +4 -0
  9. package/extension/src/content/diagnosticsController.js +4 -0
  10. package/extension/src/content/diagnosticsPanel.js +128 -4
  11. package/extension/src/content/generated/content.bundle.js +1233 -550
  12. package/extension/src/content/generated/content.bundle.meta.json +4 -3
  13. package/extension/src/content/legacyGlobalRegistry.js +1 -0
  14. package/extension/src/content/modelPicker.js +21 -9
  15. package/extension/src/content/modelPickerSupport.js +38 -1
  16. package/extension/src/content/nativeCompatibilityController.js +4 -1
  17. package/extension/src/content/otWarmMirror.js +34 -30
  18. package/extension/src/content/otWarmMirrorController.js +20 -0
  19. package/extension/src/content/pageBridgeClient.js +2 -0
  20. package/extension/src/content/panelMaintenance.js +8 -4
  21. package/extension/src/content/recentProjects.js +354 -355
  22. package/extension/src/content/runResultActions.js +16 -1
  23. package/extension/src/content/runTimelineView.js +9 -11
  24. package/extension/src/content/scopedPersistenceCoordinator.js +2 -1
  25. package/extension/src/content/scopedPersistencePanelState.js +45 -2
  26. package/extension/src/content/sessionForkController.js +20 -11
  27. package/extension/src/content/sessionPersistence.js +4 -1
  28. package/extension/src/content/settingsPanel.js +16 -2
  29. package/extension/src/content/trackedChangeCaptureController.js +153 -0
  30. package/extension/src/content/writebackOrchestrator.js +31 -0
  31. package/extension/src/page/binaryAssetUploader.js +101 -29
  32. package/extension/src/page/overleafRealtimeObserver.js +4 -1
  33. package/extension/src/page/saveState.js +44 -1
  34. package/extension/src/page/textFileCreator.js +493 -0
  35. package/extension/src/page/trackedChangeCapture.js +342 -0
  36. package/extension/src/page/trackedChangesLifecycle.js +149 -150
  37. package/extension/src/page/treeOperations.js +40 -29
  38. package/extension/src/page/writebackRouter.js +95 -49
  39. package/extension/src/pageBridge.js +65 -25
  40. package/extension/src/shared/compatibility.js +1 -1
  41. package/extension/src/shared/i18n.js +94 -40
  42. package/extension/src/shared/pageRpcContract.js +2 -1
  43. package/extension/src/shared/sessionState.js +6 -2
  44. package/extension/src/shared/storageDb.js +11 -12
  45. package/extension/src/shared/storageRunActions.js +73 -1
  46. package/extension/src/shared/writebackSettlement.js +19 -5
  47. package/extension/styles/panel.css +254 -405
  48. package/native-host/src/codexSessionRunner.js +41 -38
  49. package/native-host/src/codexSessionTiming.js +68 -0
  50. package/native-host/src/readProgressGuard.js +84 -17
  51. package/native-host/src/taskRunnerRuntime.js +25 -0
  52. package/package.json +5 -1
@@ -636,7 +636,7 @@
636
636
  undoExpectedFiles: normalizeRunFiles(run.undoExpectedFiles),
637
637
  undoStatus: sanitizeAssistantVisibleText(run.undoStatus),
638
638
  nativeRequestId: sanitizeAssistantVisibleText(run.nativeRequestId),
639
- codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
639
+ codexThreadId: sanitizeAssistantVisibleText(run.codexThreadId), codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
640
640
  forkSnapshot: run.forkSnapshot === true,
641
641
  forkSourceRunId: sanitizeAssistantVisibleText(run.forkSourceRunId),
642
642
  nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
@@ -656,6 +656,9 @@
656
656
  if (run.changedDocument === true) {
657
657
  normalized.changedDocument = true;
658
658
  }
659
+ if (Array.isArray(run.trackedChangeCaptures) && run.trackedChangeCaptures.length && !run.forkSnapshot) {
660
+ normalized.trackedChangeCaptures = sanitizeAssistantVisibleValue(run.trackedChangeCaptures);
661
+ }
659
662
  applyTrackedChangeStatus(normalized, run.trackedChangeStatus);
660
663
 
661
664
  return normalized;
@@ -1215,7 +1218,7 @@
1215
1218
  undoExpectedFiles: undoPayload.undoExpectedFiles,
1216
1219
  undoStatus: summarizeTextForStorage(run.undoStatus, 'undo status'),
1217
1220
  nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
1218
- codexTurnId: normalizeTextField(run.codexTurnId, 160),
1221
+ codexThreadId: normalizeTextField(run.codexThreadId, 160), codexTurnId: normalizeTextField(run.codexTurnId, 160),
1219
1222
  forkSnapshot: run.forkSnapshot === true,
1220
1223
  forkSourceRunId: normalizeTextField(run.forkSourceRunId, 160),
1221
1224
  nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
@@ -1627,6 +1630,7 @@
1627
1630
  setActiveSession,
1628
1631
  updateActiveSession,
1629
1632
  normalizeRuns,
1633
+ normalizeProjectReferenceFiles,
1630
1634
  prepareStateForStorage,
1631
1635
  estimateJsonBytes,
1632
1636
  computeSafeTaskSummary
@@ -1,18 +1,19 @@
1
1
  (function initStorageDb(root, factory) {
2
2
  if (typeof module === 'object' && module.exports) {
3
3
  module.exports = factory(require('./storageRunActions'), require('./runInputQueue'),
4
- require('./settlementFacts'), require('./storageSessionClaims'));
4
+ require('./settlementFacts'), require('./storageSessionClaims'), require('./sessionState'));
5
5
  } else {
6
6
  root.CodexOverleafModuleRegistry.define('StorageDb', [
7
7
  'StorageRunActions',
8
8
  'RunInputQueue',
9
9
  'SettlementFacts',
10
- 'StorageSessionClaims'
10
+ 'StorageSessionClaims',
11
+ 'SessionState'
11
12
  ], factory);
12
13
  }
13
- })(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims) {
14
+ })(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims, SessionState) {
14
15
  'use strict';
15
- if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
16
+ if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactRunExecutionSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
16
17
  throw new Error('Codex Overleaf storage run-action helpers are unavailable.');
17
18
  }
18
19
  // IndexedDB versions are monotonic; v2.0 RC profiles have already opened v3.
@@ -279,6 +280,7 @@
279
280
  codexThreadId: typeof input.codexThreadId === 'string' ? input.codexThreadId : '',
280
281
  status: typeof input.status === 'string' && input.status ? input.status : 'active',
281
282
  focusFiles: normalizePathList(input.focusFiles),
283
+ projectReferenceFiles: SessionState.normalizeProjectReferenceFiles(input.projectReferenceFiles),
282
284
  pendingInputs: RunInputQueue.compactForStorage(input.pendingInputs, {
283
285
  normalizeField: normalizeTextField,
284
286
  normalizeDisplay: normalizeDisplayTextForStorage,
@@ -650,20 +652,15 @@
650
652
  }
651
653
 
652
654
  function compactRunsForStorage(runs, options) {
653
- return StorageRunActions.compactRunsForStorage(
654
- runs,
655
- options,
656
- SESSION_STORAGE_LIMITS.maxRunsPerSession,
657
- compactRunForStorage
658
- );
655
+ return StorageRunActions.compactRunsForStorage(runs, options, SESSION_STORAGE_LIMITS.maxRunsPerSession, compactRunForStorage);
659
656
  }
660
-
661
657
  function compactRunForStorage(run, keepActionPayload) {
662
658
  var actionPayload = StorageRunActions.compactRunActionPayload(run, keepActionPayload);
663
659
  var compact = {
664
660
  id: run.id,
665
661
  task: normalizeDisplayTextForStorage(run.task || 'untitled task', SESSION_STORAGE_LIMITS.taskChars),
666
662
  mode: typeof run.mode === 'string' ? redactSecretLikeText(run.mode) : '', ...StorageRunActions.compactProviderSnapshot(run),
663
+ ...StorageRunActions.compactRunExecutionSnapshot(run, function (text) { return normalizeTextField(text, 4096); }),
667
664
  model: normalizeTextField(run.model, 80),
668
665
  reasoningEffort: typeof run.reasoningEffort === 'string' ? redactSecretLikeText(run.reasoningEffort) : '',
669
666
  speedTier: typeof run.speedTier === 'string' ? redactSecretLikeText(run.speedTier) : '',
@@ -679,9 +676,10 @@
679
676
  undoBaseFiles: actionPayload.undoBaseFiles,
680
677
  undoTrackedChanges: actionPayload.undoTrackedChanges,
681
678
  undoExpectedFiles: actionPayload.undoExpectedFiles,
679
+ ...(actionPayload.trackedChangeCaptures ? { trackedChangeCaptures: actionPayload.trackedChangeCaptures } : {}),
682
680
  undoStatus: normalizeDisplayTextForStorage(run.undoStatus, SESSION_STORAGE_LIMITS.statusTextChars),
683
681
  nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
684
- codexTurnId: normalizeTextField(run.codexTurnId, 160),
682
+ codexThreadId: normalizeTextField(run.codexThreadId, 160), codexTurnId: normalizeTextField(run.codexTurnId, 160),
685
683
  nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
686
684
  queueItemId: normalizeTextField(run.queueItemId, 160)
687
685
  };
@@ -1345,6 +1343,7 @@
1345
1343
  clearStore: clearStore,
1346
1344
  clearAllStores: clearAllStores,
1347
1345
  buildSessionRecord: buildSessionRecord,
1346
+ mergeSessionReviewState: StorageRunActions.mergeSessionReviewState,
1348
1347
  buildTurnRecord: buildTurnRecord,
1349
1348
  buildEventRecord: buildEventRecord,
1350
1349
  buildArtifactRecord: buildArtifactRecord,
@@ -70,7 +70,13 @@
70
70
  } catch (_error) {
71
71
  return empty;
72
72
  }
73
- return getUtf8ByteLength(serialized) <= MAX_PERSISTED_ACTION_BYTES_PER_RUN ? payload : empty;
73
+ if (getUtf8ByteLength(serialized) > MAX_PERSISTED_ACTION_BYTES_PER_RUN) return empty;
74
+ const captures = cloneSerializableArray(run.trackedChangeCaptures);
75
+ if (captures.length) {
76
+ const extended = { ...payload, trackedChangeCaptures: captures };
77
+ if (getUtf8ByteLength(JSON.stringify(extended)) <= MAX_PERSISTED_ACTION_BYTES_PER_RUN) return extended;
78
+ }
79
+ return payload; // Optional capture evidence must never evict a mature Undo payload.
74
80
  }
75
81
 
76
82
  function emptyActionPayload() {
@@ -102,6 +108,27 @@
102
108
  return value.length * 2;
103
109
  }
104
110
 
111
+ // Persist only execution metadata; never infer a missing historical setting.
112
+ function compactRunExecutionSnapshot(run, normalizeString) {
113
+ var snapshot = run && run.executionSnapshot;
114
+ if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return {};
115
+ var normalize = typeof normalizeString === 'function' ? normalizeString : String;
116
+ var compact = {};
117
+ var fields = ['schemaVersion', 'mode', 'providerId', 'providerRevision', 'model',
118
+ 'reasoningEffort', 'speedTier', 'autoRecompile', 'requireReviewing', 'capturedAt', 'source'];
119
+ for (var key of fields) {
120
+ if (!Object.prototype.hasOwnProperty.call(snapshot, key)) continue;
121
+ var value = snapshot[key];
122
+ if (typeof value === 'string') compact[key] = normalize(value);
123
+ else if (value === null || typeof value === 'number' || typeof value === 'boolean') compact[key] = value;
124
+ }
125
+ if (Array.isArray(snapshot.focusFiles)) {
126
+ compact.focusFiles = snapshot.focusFiles.slice(0, 100)
127
+ .filter(function (value) { return typeof value === 'string'; }).map(normalize);
128
+ }
129
+ return { executionSnapshot: compact };
130
+ }
131
+
105
132
  function compactProviderSnapshot(value) {
106
133
  return {
107
134
  providerId: typeof value.providerId === 'string' && value.providerId ? value.providerId : 'builtin',
@@ -132,6 +159,49 @@
132
159
  return out;
133
160
  }
134
161
 
162
+ // Review terminal states are monotonic even when a later session snapshot is stale.
163
+ function mergeSessionReviewState(incoming, existing, reviewRunIds) {
164
+ if (!existing || incoming.id !== existing.id || incoming.projectId !== existing.projectId
165
+ || !incoming.accountScopeId || incoming.accountScopeId !== existing.accountScopeId) return incoming;
166
+ var terminal = run => ['accepted', 'rejected'].includes(run?.trackedChangeStatus);
167
+ var incomingRuns = new Map((incoming.runs || []).map(run => [run.id, run]));
168
+ var existingRuns = new Map((existing.runs || []).map(run => [run.id, run]));
169
+ var requestedIds = new Set(reviewRunIds || []);
170
+ var explicitReview = Array.from(requestedIds).some(id =>
171
+ ['accepted', 'rejected', 'needs_review'].includes(incomingRuns.get(id)?.trackedChangeStatus));
172
+ var newer = (Date.parse(incoming.updatedAt) || 0) >= (Date.parse(existing.updatedAt) || 0);
173
+ if (!newer && !explicitReview) return incoming;
174
+ var base = newer ? incoming : existing;
175
+ var fields = ['trackedChangeStatus', 'undoStatus', 'undoTrackedChanges', 'undoExpectedFiles',
176
+ 'undoOperations', 'undoBaseFiles', 'appliedOperations', 'trackedChangeCaptures', 'settlement', 'settlementFacts'];
177
+ var changed = false;
178
+ var runs = (base.runs || []).map(function (run) {
179
+ var previous = existingRuns.get(run.id), candidate = incomingRuns.get(run.id);
180
+ // Never revive a removed run or change the first durably recorded review decision.
181
+ var source = terminal(previous) ? previous
182
+ : explicitReview && requestedIds.has(run.id) ? candidate : null;
183
+ if (!source || source === run) return run;
184
+ if (source.runProjectId && source.runProjectId !== incoming.projectId) return run;
185
+ var merged = { ...run };
186
+ for (var field of fields) {
187
+ if (Object.prototype.hasOwnProperty.call(source, field)) {
188
+ merged[field] = JSON.parse(JSON.stringify(source[field]));
189
+ } else {
190
+ delete merged[field];
191
+ }
192
+ }
193
+ changed = true;
194
+ return merged;
195
+ });
196
+ if (!changed) return base;
197
+ var merged = { ...base, runs };
198
+ if (explicitReview && !newer) {
199
+ merged.updatedAt = new Date(Math.max(Date.now(), Date.parse(existing.updatedAt) || 0,
200
+ Date.parse(incoming.updatedAt) || 0) + 1).toISOString();
201
+ }
202
+ return merged;
203
+ }
204
+
135
205
  function hashString(value) {
136
206
  var hash = 2166136261;
137
207
  var text = String(value || '');
@@ -144,9 +214,11 @@
144
214
 
145
215
  return {
146
216
  compactStructuredEventValue: compactStructuredEventValue,
217
+ mergeSessionReviewState: mergeSessionReviewState,
147
218
  compactRunsForStorage: compactRunsForStorage,
148
219
  compactRunActionPayload: compactRunActionPayload,
149
220
  compactProviderSnapshot: compactProviderSnapshot,
221
+ compactRunExecutionSnapshot: compactRunExecutionSnapshot,
150
222
  hashString: hashString
151
223
  };
152
224
  });
@@ -397,7 +397,7 @@
397
397
  if (!result || typeof result !== 'object' || result.ok === false) return result;
398
398
  if (Array.isArray(result.skipped) && result.skipped.length > 0) return result;
399
399
  const expectedFiles = Array.isArray(run?.undoExpectedFiles) ? run.undoExpectedFiles : [];
400
- if (!expectedFiles.length || isUndoVerifiedContentMatching(run, result)) return result;
400
+ if (!expectedFiles.length || isUndoVerifiedContentMatching(run, result, options)) return result;
401
401
  const path = expectedFiles.find(entry => typeof entry?.path === 'string')?.path || '';
402
402
  appendSyntheticFailure(result, options.buildFailure?.('undo_not_verified', { path, type: 'undo' }, {
403
403
  changedDocument: true,
@@ -407,15 +407,18 @@
407
407
  return result;
408
408
  }
409
409
 
410
- function isUndoVerifiedContentMatching(run, result) {
410
+ function isUndoVerifiedContentMatching(run, result, options = {}) {
411
411
  const expectedByPath = new Map((run?.undoExpectedFiles || [])
412
412
  .filter(file => typeof file?.path === 'string' && typeof file?.content === 'string')
413
413
  .map(file => [normalizeSettlementPath(file.path), file.content])
414
414
  .filter(([path]) => path));
415
415
  if (!expectedByPath.size) return true;
416
+ const postByPath = new Map((Array.isArray(options?.postFiles) ? options.postFiles : [])
417
+ .filter(file => typeof file?.path === 'string' && typeof file?.content === 'string')
418
+ .map(file => [normalizeSettlementPath(file.path), file.content]));
416
419
  const applied = Array.isArray(result?.applied) ? result.applied : [];
417
420
  return Array.from(expectedByPath).every(([path, expected]) =>
418
- applied.some(entry => isVerifiedUndoEntryForPath(entry, path, expected))
421
+ applied.some(entry => isVerifiedUndoEntryForPath(entry, path, expected, postByPath.get(path)))
419
422
  );
420
423
  }
421
424
 
@@ -438,7 +441,7 @@
438
441
  ));
439
442
  }
440
443
 
441
- function isVerifiedUndoEntryForPath(entry, path, expectedContent) {
444
+ function isVerifiedUndoEntryForPath(entry, path, expectedContent, postContent) {
442
445
  const inner = entry?.result || {};
443
446
  if (inner.ok === false) return false;
444
447
  const normalizedPath = normalizeSettlementPath(path);
@@ -456,7 +459,18 @@
456
459
  && inner.verifiedContent === expectedContent;
457
460
  }
458
461
  if (key === `snapshot-undo:${normalizedPath}`) {
459
- return inner.verifiedContent === expectedContent;
462
+ if (inner.verifiedContent === expectedContent) return true;
463
+ const proof = inner.undoRebaseProof, before = proof?.beforeUndoContent;
464
+ if (inner.verified !== true || proof?.version !== 1
465
+ || normalizeSettlementPath(proof.path) !== normalizedPath
466
+ || typeof before !== 'string' || typeof postContent !== 'string' || !postContent
467
+ || typeof inner.verifiedContent !== 'string') return false;
468
+ // The page observed prefix + post + suffix before Undo. Prove that only
469
+ // this run's post-image became its pre-image, preserving both envelopes.
470
+ const start = before.indexOf(postContent);
471
+ if (start < 0 || before.indexOf(postContent, start + 1) >= 0) return false;
472
+ return inner.verifiedContent === before.slice(0, start) + expectedContent
473
+ + before.slice(start + postContent.length);
460
474
  }
461
475
  return false;
462
476
  }