codex-overleaf-link 2.3.0 → 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 (33) hide show
  1. package/README.md +23 -23
  2. package/extension/bootstrap/manifest.template.json +1 -1
  3. package/extension/src/background.js +20 -0
  4. package/extension/src/content/composerPanel.js +0 -2
  5. package/extension/src/content/contentRuntime.js +108 -110
  6. package/extension/src/content/generated/content.bundle.js +566 -331
  7. package/extension/src/content/generated/content.bundle.meta.json +4 -3
  8. package/extension/src/content/markdownText.js +12 -10
  9. package/extension/src/content/mathText.js +155 -9
  10. package/extension/src/content/nativeCompatibilityController.js +2 -2
  11. package/extension/src/content/otWarmMirror.js +1 -1
  12. package/extension/src/content/runTimelineView.js +6 -0
  13. package/extension/src/content/writebackOrchestrator.js +2 -49
  14. package/extension/src/page/trackedChangesLifecycle.js +10 -4
  15. package/extension/src/page/writebackRouter.js +6 -2
  16. package/extension/src/shared/agentTranscript.js +10 -3
  17. package/extension/src/shared/compatibility.js +1 -1
  18. package/extension/src/shared/i18n.js +2 -4
  19. package/extension/src/shared/pageRpcContract.js +2 -2
  20. package/extension/src/shared/runExecutionSnapshot.js +14 -4
  21. package/extension/src/shared/runExecutionSnapshotCodec.js +5 -2
  22. package/extension/src/shared/scopedPersistenceQueuePolicy.js +20 -1
  23. package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
  24. package/extension/src/shared/scopedPersistenceTransaction.js +50 -47
  25. package/extension/src/shared/sessionState.js +46 -11
  26. package/extension/src/shared/storageDb.js +6 -5
  27. package/extension/src/shared/writebackOperationEvidence.js +16 -6
  28. package/extension/src/shared/writebackSettlement.js +28 -6
  29. package/extension/styles/panel.css +18 -8
  30. package/native-host/src/codexPromptAssembly.js +1 -1
  31. package/native-host/src/taskRunnerRuntime.js +11 -81
  32. package/native-host/src/updateManager.js +36 -1
  33. package/package.json +1 -1
@@ -3,7 +3,6 @@
3
3
  const { buildCodexRuntimeEvent } = require('./codexRuntimeIdentity');
4
4
 
5
5
  const { spawn } = require('node:child_process');
6
- const crypto = require('node:crypto');
7
6
  const { buildOperationSummary, splitDeletePlan } = require('../../extension/src/shared/summary');
8
7
  const {
9
8
  MIN_COMPATIBLE_EXTENSION_VERSION,
@@ -40,8 +39,6 @@ const activeRunEntries = new Map();
40
39
  // Overleaf tab was reloaded — the requestId lived in content-side JS state
41
40
  // and is gone, but the native-host-side controller is still running).
42
41
  const activeRunByProject = new Map();
43
- const pendingPlans = new Map();
44
- const PENDING_PLAN_TTL_MS = 30 * 60 * 1000;
45
42
  const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed', 'codex_no_usable_result']);
46
43
 
47
44
  async function handleRequest(request, env = process.env, emit = () => {}) {
@@ -136,7 +133,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
136
133
  }
137
134
 
138
135
  if (request.method === 'task.confirm') {
139
- return handleTaskConfirm(request);
136
+ return errorResponse(request.id, 'suggest_mode_removed', 'Suggest mode has been removed. Reload the extension and choose Ask or Auto.');
140
137
  }
141
138
 
142
139
  return errorResponse(request.id, 'method_not_found', `Unknown method: ${request.method}`);
@@ -716,8 +713,11 @@ async function handleTaskRun(request, env, emit) {
716
713
  const params = request.params || {};
717
714
  const mode = params.mode;
718
715
 
719
- if (!['ask', 'confirm', 'auto'].includes(mode)) {
720
- return errorResponse(request.id, 'invalid_mode', 'Mode must be "ask", "confirm", or "auto"');
716
+ if (mode === 'confirm') {
717
+ return errorResponse(request.id, 'suggest_mode_removed', 'Suggest mode has been removed. Choose Ask or Auto.');
718
+ }
719
+ if (!['ask', 'auto'].includes(mode)) {
720
+ return errorResponse(request.id, 'invalid_mode', 'Mode must be "ask" or "auto"');
721
721
  }
722
722
 
723
723
  if (mode === 'auto' && !params.checkpoint?.ok && !isVerifiedReviewing(params.reviewing)) {
@@ -805,24 +805,6 @@ function isVerifiedReviewing(reviewing) {
805
805
  return reviewing?.ok === true && reviewing.status !== 'manual-override';
806
806
  }
807
807
 
808
- async function handleTaskConfirm(request) {
809
- const planId = request.params?.planId;
810
- purgeExpiredPendingPlans();
811
- if (!planId || !pendingPlans.has(planId)) {
812
- return errorResponse(request.id, 'plan_not_found', 'No pending task plan matched the supplied planId');
813
- }
814
-
815
- const plan = pendingPlans.get(planId);
816
- pendingPlans.delete(planId);
817
-
818
- return okResponse(request.id, {
819
- status: 'confirmed',
820
- notes: plan.notes || '',
821
- userReport: plan.userReport,
822
- operations: plan.operations
823
- });
824
- }
825
-
826
808
  function buildDefaultTaskResult(mode, operations, params = {}) {
827
809
  if (mode === 'ask') {
828
810
  return {
@@ -836,16 +818,6 @@ function buildDefaultTaskResult(mode, operations, params = {}) {
836
818
 
837
819
  const summary = buildOperationSummary(operations);
838
820
 
839
- if (mode === 'confirm') {
840
- return {
841
- status: 'requires_task_confirmation',
842
- summary,
843
- notes: '',
844
- userReport: buildDefaultUserReport(mode, operations, params),
845
- operations
846
- };
847
- }
848
-
849
821
  const split = splitDeletePlan(operations);
850
822
  if (split.needsConfirmation.length > 0) {
851
823
  return {
@@ -889,7 +861,7 @@ function normalizeAgentResult(mode, result, params = {}) {
889
861
  };
890
862
 
891
863
  if (!normalized.status) {
892
- normalized.status = mode === 'confirm' ? 'requires_task_confirmation' : 'completed';
864
+ normalized.status = 'completed';
893
865
  }
894
866
 
895
867
  return normalized;
@@ -911,40 +883,7 @@ function prepareResultForResponse(mode, result) {
911
883
  }
912
884
  }
913
885
 
914
- const confirmOperations = mode === 'confirm' ? collectResultOperations(result) : [];
915
- if (confirmOperations.length > 0) {
916
- result = {
917
- ...result,
918
- status: 'requires_task_confirmation',
919
- summary: buildOperationSummary(confirmOperations),
920
- operations: confirmOperations
921
- };
922
- }
923
-
924
- if (mode !== 'confirm' || result.status !== 'requires_task_confirmation') {
925
- return result;
926
- }
927
-
928
- purgeExpiredPendingPlans();
929
- const planId = `plan_${crypto.randomUUID()}`;
930
- pendingPlans.set(planId, {
931
- createdAt: Date.now(),
932
- expiresAt: Date.now() + PENDING_PLAN_TTL_MS,
933
- notes: result.notes || '',
934
- userReport: result.userReport,
935
- operations: Array.isArray(result.operations) ? result.operations : []
936
- });
937
-
938
- const {
939
- operations: _operations,
940
- pendingOperations: _pendingOperations,
941
- deletePlan: _deletePlan,
942
- ...redacted
943
- } = result;
944
- return {
945
- ...redacted,
946
- planId
947
- };
886
+ return result;
948
887
  }
949
888
 
950
889
  function validateTaskResultOperationQuotas(result = {}) {
@@ -965,7 +904,7 @@ function buildDefaultUserReport(mode, operations = [], params = {}) {
965
904
 
966
905
  return {
967
906
  conclusion: hasOperations
968
- ? reportText(locale, 'Codex prepared proposed changes and is waiting for confirmation or writeback.', 'Codex 已准备好建议修改,等待确认或写入。')
907
+ ? reportText(locale, 'Codex prepared changes for writeback.', 'Codex 已准备好写回修改。')
969
908
  : reportText(locale, 'This task completed without writing Overleaf files.', '这轮任务已完成,没有写入 Overleaf 文件。'),
970
909
  checked,
971
910
  findings: [],
@@ -975,7 +914,7 @@ function buildDefaultUserReport(mode, operations = [], params = {}) {
975
914
  ? ''
976
915
  : (mode === 'ask' ? reportText(locale, 'This run was Ask mode.', '这轮是只问不改。') : ''),
977
916
  nextStep: hasOperations
978
- ? reportText(locale, 'Review and confirm the proposed changes before writing them to Overleaf.', '请确认修改方案后写入 Overleaf。')
917
+ ? reportText(locale, 'Review the written changes in Overleaf.', '请在 Overleaf 中检查写入的修改。')
979
918
  : reportText(locale, 'Continue the conversation, or add more @context and run another check.', '可以继续追问,或加入更多 @context 后再检查。')
980
919
  };
981
920
  }
@@ -1193,14 +1132,6 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
1193
1132
  });
1194
1133
  }
1195
1134
 
1196
- function purgeExpiredPendingPlans(now = Date.now()) {
1197
- for (const [planId, plan] of pendingPlans.entries()) {
1198
- if (Number.isFinite(plan?.expiresAt) && plan.expiresAt <= now) {
1199
- pendingPlans.delete(planId);
1200
- }
1201
- }
1202
- }
1203
-
1204
1135
  function parsePositiveInteger(value, fallback) {
1205
1136
  const parsed = Number.parseInt(value, 10);
1206
1137
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -1295,6 +1226,5 @@ module.exports = {
1295
1226
  buildDefaultTaskResult,
1296
1227
  getActiveNativeWorkState,
1297
1228
  handleRequest,
1298
- parseAgentEventLines,
1299
- purgeExpiredPendingPlans
1229
+ parseAgentEventLines
1300
1230
  };
@@ -743,6 +743,8 @@ function cleanupOrphanStageRoots(updatesRoot, retainedRoots = []) {
743
743
 
744
744
  function recoverAndReadStatus(context) {
745
745
  let journal = readJournal(context);
746
+ const alignedVersion = readAlignedManagedVersion(context);
747
+ journal = supersedeObsoleteJournal(context, journal, alignedVersion);
746
748
  if (journal?.state === 'activation_pending') {
747
749
  journal = { ...journal, state: 'staged', reasonCode: 'update_activation_interrupted', updatedAt: new Date().toISOString() };
748
750
  writeJournal(context, journal);
@@ -753,7 +755,7 @@ function recoverAndReadStatus(context) {
753
755
  writeJournal(context, journal);
754
756
  settleAuthorization(context, journal.authorizationId, 'revoked');
755
757
  }
756
- if (journal?.state === 'rolled_back') {
758
+ if (journal?.state === 'rolled_back' || journal?.state === 'superseded') {
757
759
  pruneNativeVersions(context.nativeRoot, new Set([
758
760
  readVersionPointer(context.nativeRoot, 'active-version'),
759
761
  readVersionPointer(context.nativeRoot, 'previous-version')
@@ -768,6 +770,36 @@ function recoverAndReadStatus(context) {
768
770
  };
769
771
  }
770
772
 
773
+ function readAlignedManagedVersion(context) {
774
+ try {
775
+ return assertManagedLayout(context);
776
+ } catch (_error) {
777
+ return '';
778
+ }
779
+ }
780
+
781
+ function supersedeObsoleteJournal(context, journal, activeVersion) {
782
+ if (!journal || journal.state === 'superseded' || !parseSemver(activeVersion) ||
783
+ !parseSemver(journal.sourceVersion) || !parseSemver(journal.targetVersion) ||
784
+ compareSemver(activeVersion, journal.sourceVersion) <= 0 ||
785
+ compareSemver(activeVersion, journal.targetVersion) <= 0) {
786
+ return journal;
787
+ }
788
+ const timestamp = new Date().toISOString();
789
+ const superseded = {
790
+ ...journal,
791
+ state: 'superseded',
792
+ supersededFromState: journal.state,
793
+ supersededByVersion: activeVersion,
794
+ supersededAt: timestamp,
795
+ updatedAt: timestamp
796
+ };
797
+ writeJournal(context, superseded);
798
+ cleanupStage(journal.stageRoot);
799
+ cleanupOrphanStageRoots(context.updatesRoot);
800
+ return superseded;
801
+ }
802
+
771
803
  function pruneNativeVersions(nativeRoot, retainedVersions) {
772
804
  const versionsRoot = path.join(nativeRoot, 'versions');
773
805
  let entries = [];
@@ -960,6 +992,9 @@ function publicTransaction(journal) {
960
992
  appliedAt: journal.appliedAt,
961
993
  confirmedAt: journal.confirmedAt,
962
994
  rolledBackAt: journal.rolledBackAt,
995
+ supersededAt: journal.supersededAt,
996
+ supersededByVersion: journal.supersededByVersion,
997
+ supersededFromState: journal.supersededFromState,
963
998
  reasonCode: journal.reasonCode || ''
964
999
  };
965
1000
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",