codex-overleaf-link 2.3.5 → 2.4.0

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 (57) hide show
  1. package/README.md +63 -31
  2. package/README.zh-CN.md +543 -0
  3. package/extension/bootstrap/manifest.template.json +1 -1
  4. package/extension/src/background.js +7 -0
  5. package/extension/src/backgroundUpdateCoordinator.js +88 -4
  6. package/extension/src/content/activeTurnControl.js +1 -0
  7. package/extension/src/content/assetTransferBroker.js +2 -0
  8. package/extension/src/content/contentRuntime.js +77 -57
  9. package/extension/src/content/contextTray.js +4 -0
  10. package/extension/src/content/diagnosticsController.js +17 -3
  11. package/extension/src/content/diagnosticsPanel.js +129 -5
  12. package/extension/src/content/generated/content.bundle.js +1422 -348
  13. package/extension/src/content/generated/content.bundle.meta.json +7 -3
  14. package/extension/src/content/legacyGlobalRegistry.js +3 -0
  15. package/extension/src/content/modelPicker.js +21 -9
  16. package/extension/src/content/modelPickerSupport.js +38 -1
  17. package/extension/src/content/nativeCompatibilityController.js +4 -1
  18. package/extension/src/content/otWarmMirror.js +126 -92
  19. package/extension/src/content/otWarmMirrorController.js +122 -1
  20. package/extension/src/content/pageBridgeClient.js +2 -0
  21. package/extension/src/content/panelMaintenance.js +26 -15
  22. package/extension/src/content/providerSettingsCoordinator.js +1 -0
  23. package/extension/src/content/runFailureNotice.js +104 -0
  24. package/extension/src/content/runResultActions.js +16 -1
  25. package/extension/src/content/runScrollLayout.js +124 -0
  26. package/extension/src/content/runTimelineView.js +37 -11
  27. package/extension/src/content/sessionForkController.js +20 -11
  28. package/extension/src/content/settingsPanel.js +28 -11
  29. package/extension/src/content/trackedChangeCaptureController.js +153 -0
  30. package/extension/src/content/updateNotice.js +24 -4
  31. package/extension/src/content/writebackOrchestrator.js +31 -0
  32. package/extension/src/page/binaryAssetUploader.js +101 -29
  33. package/extension/src/page/overleafRealtimeObserver.js +120 -25
  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 +145 -149
  37. package/extension/src/page/treeOperations.js +30 -2
  38. package/extension/src/page/writebackRouter.js +94 -48
  39. package/extension/src/pageBridge.js +62 -25
  40. package/extension/src/shared/compatibility.js +1 -1
  41. package/extension/src/shared/i18n.js +77 -37
  42. package/extension/src/shared/managedUpdateProjection.js +1 -1
  43. package/extension/src/shared/otText.js +0 -1
  44. package/extension/src/shared/pageRpcContract.js +2 -1
  45. package/extension/src/shared/sessionState.js +23 -23
  46. package/extension/src/shared/sessionTitle.js +120 -0
  47. package/extension/src/shared/storageDb.js +10 -12
  48. package/extension/src/shared/storageRunActions.js +29 -1
  49. package/extension/src/shared/updateRuntimeIdentity.js +32 -0
  50. package/extension/src/shared/writebackSettlement.js +19 -5
  51. package/extension/styles/panel.css +128 -24
  52. package/native-host/src/mirrorWorkspace.js +20 -1
  53. package/native-host/src/releaseProxy.js +177 -0
  54. package/native-host/src/releaseTransport.js +154 -0
  55. package/native-host/src/taskRunnerRuntime.js +25 -0
  56. package/native-host/src/updateManager.js +40 -39
  57. package/package.json +5 -1
@@ -1,3 +1,4 @@
1
+ if (typeof document !== 'undefined' && document.documentElement) document.documentElement.setAttribute('data-codex-overleaf-source-digest', "f146d7e07b67706193fa5fcff26d7c9719826ac745aec9b745dd95c52413d27c");
1
2
  (() => {
2
3
  // extension/entries/content-entry.mjs
3
4
  (function runClassicContentModule(module, exports, require2) {
@@ -3895,7 +3896,7 @@ ${block}`;
3895
3896
  return sha256Hex(utf8Bytes(value));
3896
3897
  }
3897
3898
  function normalizePath(value) {
3898
- return String(value || "").replace(/\s+/g, " ").replace(/\\/g, "/").trim().replace(/^\/+/, "");
3899
+ return String(value || "").replace(/\\/g, "/").trim().replace(/^\/+/, "");
3899
3900
  }
3900
3901
  function normalizeText(value) {
3901
3902
  return String(value ?? "");
@@ -4114,20 +4115,31 @@ ${block}`;
4114
4115
  diagnosticsPageSubtitle: "Current file, write access, track changes",
4115
4116
  diagnosticsSnapshotTitle: "Check Project Read",
4116
4117
  diagnosticsSnapshotSubtitle: "Full project, assets, read source",
4117
- diagnosticsOtTitle: "Check Experimental OT Mirror",
4118
+ diagnosticsOtTitle: "Check OT Mirror",
4118
4119
  diagnosticsOtSubtitle: "Status, fresh files, fallback",
4119
4120
  diagnosticsExportTitle: "Export Diagnostics",
4120
4121
  diagnosticsExportSubtitle: "Redacted audit and environment bundle",
4121
4122
  diagnosticsExportDone: "Diagnostics bundle exported.",
4122
- diagnosticsOtSummaryEnabled: "Experimental OT is enabled. Codex will use the OT-warmed mirror only when matching files are fresh; otherwise it falls back to the normal project read path.",
4123
- diagnosticsOtSummaryDisabled: "Experimental OT is disabled, so Codex is using the normal project read path.",
4124
- diagnosticsOtNextStep: "If you expected OT warm reads, turn on OT and wait for the status to show Live before relying on it.",
4125
- experimentalOtMenuTitle: "Experimental OT Mirror",
4126
- experimentalOtMenuSubtitle: "Experimental \u2014 speeds up reading the file you are editing; Codex falls back to normal reading whenever unsure.",
4127
- experimentalOtConfirmTitle: "Experimental OT Mirror",
4128
- experimentalOtConfirmMessage: "Experimental: tracks the current Overleaf editor and warms the local mirror so Codex can read recent focused-file edits faster. Codex uses it only when file scope is clear and the mirror is fresh; otherwise it falls back to normal project reading.",
4123
+ diagnosticsOtSummaryEnabled: "OT Mirror is enabled. Codex will use the OT-warmed mirror only when matching files are fresh; otherwise it falls back to the normal project read path.",
4124
+ diagnosticsOtSummaryDisabled: "OT Mirror is disabled, so Codex is using the normal project read path.",
4125
+ diagnosticsOtNextStep: "Observer status describes warming availability. File scope and freshness are checked again before each run.",
4126
+ experimentalOtMenuTitle: "OT Mirror",
4127
+ otSettingsTitle: "Project cache",
4128
+ contextLoadingTitle: "Context loading",
4129
+ runFeedbackFailedTitle: "Needs attention",
4130
+ runFeedbackPartialTitle: "Partially completed",
4131
+ runFeedbackUnconfirmedTitle: "Write status needs review",
4132
+ runFeedbackGeneric: "Open the run details for the reported cause and the next step.",
4133
+ runFeedbackUnconfirmed: "Some changes may already be in Overleaf. Check the document before retrying.",
4134
+ runFeedbackUnconfirmedResult: "Not fully confirmed. Review Overleaf before retrying.",
4135
+ runFeedbackReviewFile: "Review {file} in Overleaf",
4136
+ runFeedbackReviewDetails: "Review run details",
4137
+ runFeedbackDetailsUnavailable: "Detailed steps are unavailable for this older run.",
4138
+ experimentalOtMenuSubtitle: "Warms recent edits to the active text file locally. File scope and freshness are checked before reuse.",
4139
+ experimentalOtConfirmTitle: "Enable OT Mirror?",
4140
+ experimentalOtConfirmMessage: "Observes the active text editor and warms its local mirror. This is read-only with respect to Overleaf and does not upload assets. Each run checks file scope and freshness before reuse; normal project reading remains the fallback.",
4129
4141
  experimentalOtConfirmEnable: "Turn on",
4130
- experimentalOtEnabledToast: "Experimental OT Mirror is on. Use @file for the cleanest warm-mirror test.",
4142
+ experimentalOtEnabledToast: "OT Mirror requested for this project. Its status shows whether warming is available.",
4131
4143
  diagnosticsSnapshotLoading: "Checking whether the extension can read the full Overleaf project.",
4132
4144
  diagnosticsSnapshotErrorSummary: "Overleaf may still be loading, or the page API did not return project files yet.",
4133
4145
  diagnosticsNativeLoading: "Checking whether local Codex, Native Host, and LaTeX tools are available.",
@@ -4400,18 +4412,27 @@ ${block}`;
4400
4412
  requireReviewingTitle: "When enabled, Codex checks or switches Overleaf Reviewing/Track Changes before writing. Deletes still require confirmation.",
4401
4413
  autoCompile: "Compile",
4402
4414
  autoCompileTitle: "After Codex writes, click Overleaf Recompile and record the compile result for this task. Ask mode will not trigger it.",
4403
- experimentalOtWarmMirror: "Experimental OT warm mirror",
4415
+ experimentalOtWarmMirror: "OT warm mirror",
4404
4416
  experimentalOtWarmMirrorShort: "OT",
4405
- experimentalOtWarmMirrorTitle: "Experimental read-only OT mirror warming. If unavailable, stale, or inconsistent, Codex falls back to the normal project read path.",
4417
+ experimentalOtWarmMirrorTitle: "Read-only local text warming. File scope and freshness are checked before reuse; normal project reading remains the fallback.",
4406
4418
  otStatus: "OT status",
4407
- otFreshFiles: "OT fresh files",
4408
- otFallback: "Fallback",
4419
+ otFreshFiles: "Files with recent OT evidence",
4420
+ otFallback: "Read fallback",
4409
4421
  otStatusOff: "Off",
4410
- otStatusStarting: "Start",
4411
- otStatusObserving: "Live",
4412
- otStatusUnavailable: "N/A",
4413
- otStatusStale: "Stale",
4414
- otStatusInconsistent: "Check",
4422
+ otStatusStarting: "Connecting",
4423
+ otStatusObserving: "Observing current editor",
4424
+ otStatusUnavailable: "Unavailable",
4425
+ otStatusStale: "Warming paused; awaiting verification",
4426
+ otStatusInconsistent: "Needs verification",
4427
+ otFailureBridge: "The page connection needs to be restored. Reload the extension and Overleaf before retrying.",
4428
+ otFailureObserver: "The editor observer is unavailable. The next run will try normal project reading.",
4429
+ otFailureEditor: "The active editor could not be read. Warming is paused; the next run will try normal project reading.",
4430
+ otFailureFileLimit: "This file exceeds the 1 MiB warming limit. The next run will try normal project reading.",
4431
+ otFailureQueueLimit: "The mirror queue reached its limit. Warming is paused; the next run will try normal project reading.",
4432
+ otFailureExpired: "The observation is too old to reuse. The next run will try normal project reading.",
4433
+ otFailureMirror: "The mirror update was not fully confirmed. The next run will try normal project reading.",
4434
+ otFailureGeneric: "Mirror warming could not continue. The next run will try normal project reading.",
4435
+ otFailureNextStep: "Resolve the reported cause, then enable OT again. If the page connection is unavailable, reload the extension and Overleaf first.",
4415
4436
  modelSourceFallback: "fallback",
4416
4437
  modelSourceDiscovered: "discovered",
4417
4438
  modelSourceFailed: "failed",
@@ -4634,20 +4655,31 @@ ${block}`;
4634
4655
  diagnosticsPageSubtitle: "\u5F53\u524D\u6587\u4EF6\u3001\u5199\u5165\u80FD\u529B\u3001\u7559\u75D5\u72B6\u6001",
4635
4656
  diagnosticsSnapshotTitle: "\u68C0\u67E5\u9879\u76EE\u8BFB\u53D6",
4636
4657
  diagnosticsSnapshotSubtitle: "\u5B8C\u6574\u9879\u76EE\u3001\u8D44\u6E90\u6587\u4EF6\u3001\u8BFB\u53D6\u6765\u6E90",
4637
- diagnosticsOtTitle: "\u68C0\u67E5\u5B9E\u9A8C\u6027 OT Mirror",
4658
+ diagnosticsOtTitle: "\u68C0\u67E5 OT Mirror",
4638
4659
  diagnosticsOtSubtitle: "\u72B6\u6001\u3001\u65B0\u9C9C\u6587\u4EF6\u3001\u56DE\u9000\u8DEF\u5F84",
4639
4660
  diagnosticsExportTitle: "\u5BFC\u51FA\u8BCA\u65AD\u4FE1\u606F",
4640
4661
  diagnosticsExportSubtitle: "\u5DF2\u8131\u654F\u7684\u5BA1\u8BA1\u548C\u73AF\u5883\u4FE1\u606F\u5305",
4641
4662
  diagnosticsExportDone: "\u8BCA\u65AD\u4FE1\u606F\u5DF2\u5BFC\u51FA\u3002",
4642
- diagnosticsOtSummaryEnabled: "\u5B9E\u9A8C\u6027 OT \u5DF2\u5F00\u542F\u3002\u53EA\u6709\u5339\u914D\u6587\u4EF6\u662F\u65B0\u9C9C\u72B6\u6001\u65F6\uFF0CCodex \u624D\u4F1A\u4F7F\u7528 OT \u9884\u70ED mirror\uFF1B\u5426\u5219\u4F1A\u56DE\u9000\u5230\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4643
- diagnosticsOtSummaryDisabled: "\u5B9E\u9A8C\u6027 OT \u5DF2\u5173\u95ED\uFF0CCodex \u4F1A\u4F7F\u7528\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4644
- diagnosticsOtNextStep: "\u5982\u679C\u4F60\u5E0C\u671B\u4F7F\u7528 OT \u9884\u70ED\u8BFB\u53D6\uFF0C\u8BF7\u5F00\u542F OT\uFF0C\u5E76\u7B49\u72B6\u6001\u663E\u793A\u4E3A\u201C\u5B9E\u65F6\u201D\u540E\u518D\u4F9D\u8D56\u5B83\u3002",
4645
- experimentalOtMenuTitle: "\u5B9E\u9A8C\u6027 OT Mirror",
4646
- experimentalOtMenuSubtitle: "\u5B9E\u9A8C\u6027\u2014\u2014\u52A0\u5FEB\u8BFB\u53D6\u4F60\u6B63\u5728\u7F16\u8F91\u7684\u6587\u4EF6\uFF1B\u62FF\u4E0D\u51C6\u65F6\u81EA\u52A8\u56DE\u9000\u5230\u666E\u901A\u8BFB\u53D6\u3002",
4647
- experimentalOtConfirmTitle: "\u5B9E\u9A8C\u6027 OT Mirror",
4648
- experimentalOtConfirmMessage: "\u5B9E\u9A8C\u6027\uFF1A\u5B9E\u65F6\u8DDF\u8E2A\u5F53\u524D Overleaf \u7F16\u8F91\u5668\uFF0C\u628A\u6539\u52A8\u9884\u70ED\u5230\u672C\u5730 mirror\uFF0C\u8BA9 Codex \u66F4\u5FEB\u8BFB\u5230\u6700\u8FD1\u7684\u5F53\u524D\u6587\u4EF6\u6539\u52A8\u3002\u53EA\u6709\u6587\u4EF6\u8303\u56F4\u660E\u786E\u4E14 mirror \u65B0\u9C9C\u65F6\u624D\u4F1A\u4F7F\u7528\uFF1B\u5426\u5219\u81EA\u52A8\u56DE\u9000\u5230\u666E\u901A\u9879\u76EE\u8BFB\u53D6\u3002",
4663
+ diagnosticsOtSummaryEnabled: "OT Mirror \u5DF2\u5F00\u542F\u3002\u53EA\u6709\u5339\u914D\u6587\u4EF6\u662F\u65B0\u9C9C\u72B6\u6001\u65F6\uFF0CCodex \u624D\u4F1A\u4F7F\u7528 OT \u9884\u70ED\u955C\u50CF\uFF1B\u5426\u5219\u4F1A\u56DE\u9000\u5230\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4664
+ diagnosticsOtSummaryDisabled: "OT Mirror \u5DF2\u5173\u95ED\uFF0CCodex \u4F1A\u4F7F\u7528\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4665
+ diagnosticsOtNextStep: "\u89C2\u6D4B\u72B6\u6001\u8BF4\u660E\u9884\u70ED\u662F\u5426\u53EF\u7528\uFF1B\u6BCF\u6B21\u4EFB\u52A1\u5F00\u59CB\u524D\u4ECD\u4F1A\u91CD\u65B0\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\u3002",
4666
+ experimentalOtMenuTitle: "OT Mirror",
4667
+ otSettingsTitle: "\u9879\u76EE\u7F13\u5B58",
4668
+ contextLoadingTitle: "\u4E0A\u4E0B\u6587\u52A0\u8F7D",
4669
+ runFeedbackFailedTitle: "\u9700\u8981\u5904\u7406",
4670
+ runFeedbackPartialTitle: "\u90E8\u5206\u5B8C\u6210",
4671
+ runFeedbackUnconfirmedTitle: "\u5199\u5165\u72B6\u6001\u5F85\u786E\u8BA4",
4672
+ runFeedbackGeneric: "\u53EF\u5728\u672C\u8F6E\u8BE6\u60C5\u4E2D\u67E5\u770B\u5177\u4F53\u539F\u56E0\u548C\u540E\u7EED\u5904\u7406\u65B9\u5F0F\u3002",
4673
+ runFeedbackUnconfirmed: "\u90E8\u5206\u6539\u52A8\u53EF\u80FD\u5DF2\u7ECF\u5199\u5165 Overleaf\uFF0C\u91CD\u8BD5\u524D\u8BF7\u5148\u68C0\u67E5\u6587\u6863\u3002",
4674
+ runFeedbackUnconfirmedResult: "\u7ED3\u679C\u5C1A\u672A\u5B8C\u5168\u786E\u8BA4\uFF0C\u8BF7\u68C0\u67E5 Overleaf \u540E\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5\u3002",
4675
+ runFeedbackReviewFile: "\u5728 Overleaf \u68C0\u67E5 {file}",
4676
+ runFeedbackReviewDetails: "\u67E5\u770B\u8FD0\u884C\u6B65\u9AA4",
4677
+ runFeedbackDetailsUnavailable: "\u8FD9\u6761\u5386\u53F2\u8BB0\u5F55\u6CA1\u6709\u53EF\u5C55\u5F00\u7684\u8BE6\u7EC6\u6B65\u9AA4\u3002",
4678
+ experimentalOtMenuSubtitle: "\u5728\u672C\u5730\u9884\u70ED\u5F53\u524D\u6587\u672C\u6587\u4EF6\u7684\u6700\u8FD1\u6539\u52A8\uFF0C\u590D\u7528\u524D\u91CD\u65B0\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\u3002",
4679
+ experimentalOtConfirmTitle: "\u5F00\u542F OT Mirror\uFF1F",
4680
+ experimentalOtConfirmMessage: "\u89C2\u6D4B\u5F53\u524D\u6587\u672C\u7F16\u8F91\u5668\u5E76\u9884\u70ED\u672C\u5730\u955C\u50CF\u3002\u6B64\u529F\u80FD\u4E0D\u4F1A\u5199\u56DE Overleaf\uFF0C\u4E5F\u4E0D\u8D1F\u8D23\u4E0A\u4F20\u8D44\u4EA7\u3002\u6BCF\u6B21\u4EFB\u52A1\u590D\u7528\u524D\u90FD\u4F1A\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\uFF0C\u4E0D\u6EE1\u8DB3\u6761\u4EF6\u65F6\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4649
4681
  experimentalOtConfirmEnable: "\u5F00\u542F",
4650
- experimentalOtEnabledToast: "\u5B9E\u9A8C\u6027 OT Mirror \u5DF2\u5F00\u542F\u3002\u4F7F\u7528 @file \u53EF\u4EE5\u6700\u6E05\u695A\u5730\u6D4B\u8BD5 warm mirror\u3002",
4682
+ experimentalOtEnabledToast: "\u5DF2\u4E3A\u5F53\u524D\u9879\u76EE\u8BF7\u6C42\u5F00\u542F OT Mirror\uFF0C\u53EF\u7528\u60C5\u51B5\u89C1\u72B6\u6001\u63D0\u793A\u3002",
4651
4683
  diagnosticsSnapshotLoading: "\u786E\u8BA4\u63D2\u4EF6\u80FD\u5426\u8BFB\u53D6\u5B8C\u6574 Overleaf \u9879\u76EE\u3002",
4652
4684
  diagnosticsSnapshotErrorSummary: "\u8FD9\u901A\u5E38\u662F Overleaf \u9875\u9762\u8FD8\u6CA1\u52A0\u8F7D\u5B8C\u6210\uFF0C\u6216\u8005\u5F53\u524D\u9875\u9762\u63A5\u53E3\u6682\u65F6\u6CA1\u6709\u8FD4\u56DE\u9879\u76EE\u6587\u4EF6\u3002",
4653
4685
  diagnosticsNativeLoading: "\u786E\u8BA4\u672C\u673A Codex\u3001Native Host \u548C LaTeX \u5DE5\u5177\u662F\u5426\u53EF\u7528\u3002",
@@ -4920,18 +4952,27 @@ ${block}`;
4920
4952
  requireReviewingTitle: "\u5F00\u542F\u540E\uFF0C\u5199\u5165\u524D\u4F1A\u786E\u8BA4\u5E76\u5C1D\u8BD5\u5207\u5230 Overleaf Reviewing/Track Changes\uFF1B\u5220\u9664\u4ECD\u9700\u786E\u8BA4\u3002",
4921
4953
  autoCompile: "\u7F16\u8BD1",
4922
4954
  autoCompileTitle: "Codex \u5199\u5165\u540E\u81EA\u52A8\u70B9\u51FB Overleaf Recompile\uFF0C\u5E76\u628A\u7F16\u8BD1\u7ED3\u679C\u8BB0\u5F55\u5230\u672C\u8F6E\u4EFB\u52A1\u3002\u53EA\u95EE\u4E0D\u6539\u65F6\u4E0D\u4F1A\u89E6\u53D1\u3002",
4923
- experimentalOtWarmMirror: "\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF",
4955
+ experimentalOtWarmMirror: "OT \u9884\u70ED\u955C\u50CF",
4924
4956
  experimentalOtWarmMirrorShort: "OT",
4925
- experimentalOtWarmMirrorTitle: "\u5B9E\u9A8C\u6027\u53EA\u8BFB OT \u955C\u50CF\u9884\u70ED\uFF1B\u4E0D\u53EF\u7528\u3001\u8FC7\u671F\u6216\u4E0D\u4E00\u81F4\u65F6\u4F1A\u56DE\u9000\u5230\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4957
+ experimentalOtWarmMirrorTitle: "\u53EA\u8BFB\u672C\u5730\u6587\u672C\u9884\u70ED\uFF1B\u590D\u7528\u524D\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\uFF0C\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u4F5C\u4E3A\u56DE\u9000\u8DEF\u5F84\u3002",
4926
4958
  otStatus: "OT \u72B6\u6001",
4927
- otFreshFiles: "OT \u65B0\u9C9C\u6587\u4EF6",
4928
- otFallback: "\u56DE\u9000",
4929
- otStatusOff: "\u5173",
4930
- otStatusStarting: "\u542F\u52A8",
4931
- otStatusObserving: "\u5B9E\u65F6",
4932
- otStatusUnavailable: "\u4E0D\u53EF\u7528",
4933
- otStatusStale: "\u8FC7\u671F",
4934
- otStatusInconsistent: "\u9700\u68C0\u67E5",
4959
+ otFreshFiles: "\u8FD1\u671F\u6709 OT \u6821\u9A8C\u8BC1\u636E\u7684\u6587\u4EF6",
4960
+ otFallback: "\u8BFB\u53D6\u56DE\u9000",
4961
+ otStatusOff: "\u672A\u5F00\u542F",
4962
+ otStatusStarting: "\u6B63\u5728\u8FDE\u63A5",
4963
+ otStatusObserving: "\u6B63\u5728\u89C2\u6D4B\u5F53\u524D\u7F16\u8F91\u5668",
4964
+ otStatusUnavailable: "\u6682\u4E0D\u53EF\u7528",
4965
+ otStatusStale: "\u9884\u70ED\u6682\u7F13\uFF0C\u7B49\u5F85\u91CD\u65B0\u6821\u9A8C",
4966
+ otStatusInconsistent: "\u9700\u91CD\u65B0\u6821\u9A8C",
4967
+ otFailureBridge: "\u9875\u9762\u8FDE\u63A5\u9700\u8981\u6062\u590D\uFF0C\u8BF7\u5148\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u5E76\u5237\u65B0 Overleaf\uFF0C\u518D\u91CD\u8BD5\u3002",
4968
+ otFailureObserver: "\u7F16\u8F91\u5668\u89C2\u5BDF\u5668\u6682\u4E0D\u53EF\u7528\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4969
+ otFailureEditor: "\u5F53\u524D\u7F16\u8F91\u5668\u5185\u5BB9\u5C1A\u4E0D\u53EF\u8BFB\uFF0C\u9884\u70ED\u5DF2\u6682\u505C\uFF1B\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4970
+ otFailureFileLimit: "\u6587\u4EF6\u8D85\u8FC7 1 MiB \u9884\u70ED\u4E0A\u9650\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4971
+ otFailureQueueLimit: "\u955C\u50CF\u961F\u5217\u8FBE\u5230\u5BB9\u91CF\u4E0A\u9650\uFF0C\u9884\u70ED\u5DF2\u6682\u505C\uFF1B\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4972
+ otFailureExpired: "\u89C2\u6D4B\u8BB0\u5F55\u5DF2\u8FC7\u671F\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4973
+ otFailureMirror: "\u955C\u50CF\u66F4\u65B0\u5C1A\u672A\u5B8C\u6574\u786E\u8BA4\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4974
+ otFailureGeneric: "\u955C\u50CF\u9884\u70ED\u672A\u80FD\u7EE7\u7EED\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4975
+ otFailureNextStep: "\u5904\u7406\u6240\u663E\u793A\u7684\u539F\u56E0\u540E\u91CD\u65B0\u5F00\u542F OT\uFF1B\u82E5\u9875\u9762\u8FDE\u63A5\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5148\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u5E76\u5237\u65B0 Overleaf\u3002",
4935
4976
  modelSourceFallback: "\u5907\u7528\u5217\u8868",
4936
4977
  modelSourceDiscovered: "\u5DF2\u53D1\u73B0",
4937
4978
  modelSourceFailed: "\u83B7\u53D6\u5931\u8D25",
@@ -7846,7 +7887,7 @@ ${block}`;
7846
7887
  if (!result || typeof result !== "object" || result.ok === false) return result;
7847
7888
  if (Array.isArray(result.skipped) && result.skipped.length > 0) return result;
7848
7889
  const expectedFiles = Array.isArray(run?.undoExpectedFiles) ? run.undoExpectedFiles : [];
7849
- if (!expectedFiles.length || isUndoVerifiedContentMatching(run, result)) return result;
7890
+ if (!expectedFiles.length || isUndoVerifiedContentMatching(run, result, options2)) return result;
7850
7891
  const path = expectedFiles.find((entry) => typeof entry?.path === "string")?.path || "";
7851
7892
  appendSyntheticFailure(result, options2.buildFailure?.("undo_not_verified", { path, type: "undo" }, {
7852
7893
  changedDocument: true,
@@ -7855,12 +7896,13 @@ ${block}`;
7855
7896
  }), path, "undo_not_verified", "undo");
7856
7897
  return result;
7857
7898
  }
7858
- function isUndoVerifiedContentMatching(run, result) {
7899
+ function isUndoVerifiedContentMatching(run, result, options2 = {}) {
7859
7900
  const expectedByPath = new Map((run?.undoExpectedFiles || []).filter((file) => typeof file?.path === "string" && typeof file?.content === "string").map((file) => [normalizeSettlementPath(file.path), file.content]).filter(([path]) => path));
7860
7901
  if (!expectedByPath.size) return true;
7902
+ const postByPath = new Map((Array.isArray(options2?.postFiles) ? options2.postFiles : []).filter((file) => typeof file?.path === "string" && typeof file?.content === "string").map((file) => [normalizeSettlementPath(file.path), file.content]));
7861
7903
  const applied = Array.isArray(result?.applied) ? result.applied : [];
7862
7904
  return Array.from(expectedByPath).every(
7863
- ([path, expected]) => applied.some((entry) => isVerifiedUndoEntryForPath(entry, path, expected))
7905
+ ([path, expected]) => applied.some((entry) => isVerifiedUndoEntryForPath(entry, path, expected, postByPath.get(path)))
7864
7906
  );
7865
7907
  }
7866
7908
  function isUndoResultEffectivelyApplied(run, result, undoOperations = []) {
@@ -7874,7 +7916,7 @@ ${block}`;
7874
7916
  (item) => isVerifiedUndoEntryForPath(item, path, expectedByPath.get(path))
7875
7917
  ));
7876
7918
  }
7877
- function isVerifiedUndoEntryForPath(entry, path, expectedContent) {
7919
+ function isVerifiedUndoEntryForPath(entry, path, expectedContent, postContent) {
7878
7920
  const inner = entry?.result || {};
7879
7921
  if (inner.ok === false) return false;
7880
7922
  const normalizedPath = normalizeSettlementPath(path);
@@ -7889,7 +7931,12 @@ ${block}`;
7889
7931
  return inner.method === "overleaf-editor-undo" && inner.verified === true && inner.verifiedContent === expectedContent;
7890
7932
  }
7891
7933
  if (key === `snapshot-undo:${normalizedPath}`) {
7892
- return inner.verifiedContent === expectedContent;
7934
+ if (inner.verifiedContent === expectedContent) return true;
7935
+ const proof = inner.undoRebaseProof, before = proof?.beforeUndoContent;
7936
+ if (inner.verified !== true || proof?.version !== 1 || normalizeSettlementPath(proof.path) !== normalizedPath || typeof before !== "string" || typeof postContent !== "string" || !postContent || typeof inner.verifiedContent !== "string") return false;
7937
+ const start = before.indexOf(postContent);
7938
+ if (start < 0 || before.indexOf(postContent, start + 1) >= 0) return false;
7939
+ return inner.verifiedContent === before.slice(0, start) + expectedContent + before.slice(start + postContent.length);
7893
7940
  }
7894
7941
  return false;
7895
7942
  }
@@ -8108,6 +8155,106 @@ ${block}`;
8108
8155
  });
8109
8156
  }).call(globalThis, void 0, void 0, void 0);
8110
8157
  (function runClassicContentModule27(module, exports, require2) {
8158
+ (function initSessionTitle(root, factory) {
8159
+ if (typeof module === "object" && module.exports) {
8160
+ module.exports = factory();
8161
+ } else {
8162
+ root.CodexOverleafModuleRegistry.define("SessionTitle", [], factory);
8163
+ }
8164
+ })(typeof globalThis !== "undefined" ? globalThis : window, function sessionTitleFactory() {
8165
+ "use strict";
8166
+ const INPUT_LIMIT = 12e3;
8167
+ const STORAGE_LIMIT = 80;
8168
+ const segmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function" ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
8169
+ const FILE_TOKEN = /@file:(?:"([^"]+)"|'([^']+)'|([^\s]+))/g;
8170
+ function derive(runs, task, options2 = {}) {
8171
+ let fallback = "";
8172
+ for (const run of Array.isArray(runs) ? runs : []) {
8173
+ const candidate = fromPrompt(run?.task, options2);
8174
+ fallback || (fallback = candidate);
8175
+ if (candidate && !isGeneric(candidate)) return candidate;
8176
+ }
8177
+ const draft = fromPrompt(task, options2);
8178
+ return draft && !isGeneric(draft) ? draft : fallback || draft;
8179
+ }
8180
+ function fromPrompt(value, { sanitize = (text) => text, maxChars = 24 } = {}) {
8181
+ if (typeof value !== "string" || !value.trim()) return "";
8182
+ const source = String(sanitize(value.slice(0, INPUT_LIMIT)) || "");
8183
+ let fence = null;
8184
+ let fallback = "";
8185
+ for (const rawLine of source.split(/\r?\n/)) {
8186
+ const line = rawLine.trim();
8187
+ const marker = /^(`{3,}|~{3,})(.*)$/.exec(line);
8188
+ if (marker) {
8189
+ if (!fence) {
8190
+ fence = marker[1];
8191
+ const language = marker[2].trim().match(/^[A-Za-z0-9_+-]{1,24}\b/)?.[0] || "code";
8192
+ fallback || (fallback = `${language} snippet`);
8193
+ } else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && !marker[2].trim()) {
8194
+ fence = null;
8195
+ }
8196
+ continue;
8197
+ }
8198
+ if (fence || !line || /^>/.test(line)) continue;
8199
+ const withoutContext = line.replace(FILE_TOKEN, " ").replace(/@(context|compile-log)\b/g, " ").trim();
8200
+ const cleaned = cleanLine(line);
8201
+ if (!withoutContext || /^https?:\/\/\S+$/i.test(line)) {
8202
+ fallback || (fallback = cleaned);
8203
+ continue;
8204
+ }
8205
+ for (const sentence of cleaned.split(/[\u3002\uff01\uff1f]+/u)) {
8206
+ const candidate = sentence.trim().replace(/[,;:\uff0c\uff1b\uff1a]+$/u, "").trim();
8207
+ if (!/[\p{L}\p{N}]/u.test(candidate) || isScaffolding(candidate)) continue;
8208
+ fallback || (fallback = candidate);
8209
+ if (!isGeneric(candidate)) return shorten(candidate, maxChars);
8210
+ }
8211
+ }
8212
+ return shorten(fallback, maxChars);
8213
+ }
8214
+ function cleanLine(value) {
8215
+ return value.replace(/^#{1,6}\s+/, "").replace(/^(?:[-*+]\s+|\d+\.\s+|\d+[)\u3001]\s*)/, "").replace(FILE_TOKEN, (_match, quoted, singleQuoted, bare) => String(quoted || singleQuoted || bare || "").replace(/\\/g, "/").split("/").pop()).replace(/@(context|compile-log)\b/g, " ").replace(/!?\[([^\]]*)\]\([^\n)]*\)/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/https?:\/\/\S+/gi, (address) => {
8216
+ try {
8217
+ return new URL(address).hostname;
8218
+ } catch (_error) {
8219
+ return "";
8220
+ }
8221
+ }).replace(/\[(?:REDACTED_SECRET|local path(?::\d+)?)\]/gi, " ").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\s+/g, " ").trim().replace(/^(?:\u8bf7(?:\u4f60)?\u5e2e\u6211|\u8bf7\u4f60|\u5e2e\u6211|\u9ebb\u70e6\u4f60(?:\u5e2e\u6211)?|\u9ebb\u70e6\u5e2e\u6211|please\s+)\s*/i, "");
8222
+ }
8223
+ function isScaffolding(value) {
8224
+ return /^(?:my request(?: for [^:]+)?|request|task|context|files(?: mentioned by [^:]+)?|attachments|\u6211\u7684\u8bf7\u6c42|\u9700\u6c42|\u4efb\u52a1|\u4e0a\u4e0b\u6587|\u9644\u4ef6|\u6587\u4ef6)$/i.test(value);
8225
+ }
8226
+ function isGeneric(value) {
8227
+ const text = String(value || "").trim().replace(/[\s.!?\u3002\uff1f\uff01\u2026]+$/u, "");
8228
+ if (!text || Array.from(text).length < 2) return true;
8229
+ return /^(?:ok(?:ay)?|yes|no|go|done|continue|proceed|thanks?|thank you|hello|hi|test|new task|untitled task|\u597d(?:\u7684)?|\u53ef\u4ee5|\u7ee7\u7eed|\u786e\u8ba4|\u786e\u5b9a|\u6536\u5230|\u8c22\u8c22|\u6d4b\u8bd5|\u6267\u884c|\u662f\u7684|\u4f60\u597d)$/i.test(text) || /^[A-Za-z0-9_+-]{1,24} snippet$/i.test(text) || /^[^\s]+\.(?:tex|bib|md|txt|pdf|svg|png|jpe?g|webp)(?::\d+)?$/i.test(text) || /^(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}$/i.test(text);
8230
+ }
8231
+ function shorten(value, maxChars) {
8232
+ const title = String(value || "").trim();
8233
+ if (!title) return "";
8234
+ const chars = Number.isFinite(maxChars) && maxChars > 0 ? Math.min(40, Math.floor(maxChars)) : 24;
8235
+ const maxUnits = chars * 2;
8236
+ const glyphs = segmenter ? Array.from(segmenter.segment(title), (item) => item.segment) : Array.from(title);
8237
+ const width = (glyph) => /[^\u0000-\u00ff]/u.test(glyph) ? 2 : 1;
8238
+ if (title.length <= STORAGE_LIMIT && glyphs.reduce((total, glyph) => total + width(glyph), 0) <= maxUnits) return title;
8239
+ let prefix = "";
8240
+ let units = 0;
8241
+ let index = 0;
8242
+ for (; index < glyphs.length; index++) {
8243
+ const glyph = glyphs[index];
8244
+ if (units + width(glyph) > maxUnits - 2 || prefix.length + glyph.length > STORAGE_LIMIT - 1) break;
8245
+ prefix += glyph;
8246
+ units += width(glyph);
8247
+ }
8248
+ const boundary = prefix.lastIndexOf(" ");
8249
+ if (/[A-Za-z0-9]$/.test(prefix) && /^[A-Za-z0-9]/.test(glyphs[index] || "") && boundary >= prefix.length * 0.6) {
8250
+ prefix = prefix.slice(0, boundary);
8251
+ }
8252
+ return prefix.trimEnd().replace(/[,;:\uff0c\uff1b\uff1a]+$/u, "") + "\u2026";
8253
+ }
8254
+ return Object.freeze({ derive, fromPrompt, isGeneric });
8255
+ });
8256
+ }).call(globalThis, void 0, void 0, void 0);
8257
+ (function runClassicContentModule28(module, exports, require2) {
8111
8258
  (function initSessionState(root, factory) {
8112
8259
  if (typeof module === "object" && module.exports) {
8113
8260
  module.exports = factory(
@@ -8115,16 +8262,17 @@ ${block}`;
8115
8262
  require2("./settlementFacts"),
8116
8263
  require2("./runInputQueue"),
8117
8264
  require2("./lineReferences"),
8118
- require2("./pathRedaction")
8265
+ require2("./pathRedaction"),
8266
+ require2("./sessionTitle")
8119
8267
  );
8120
8268
  } else {
8121
8269
  root.CodexOverleafModuleRegistry.define(
8122
8270
  "SessionState",
8123
- ["I18n", "SettlementFacts", "RunInputQueue", "LineReferences", "PathRedaction"],
8271
+ ["I18n", "SettlementFacts", "RunInputQueue", "LineReferences", "PathRedaction", "SessionTitle"],
8124
8272
  factory
8125
8273
  );
8126
8274
  }
8127
- })(typeof globalThis !== "undefined" ? globalThis : window, function sessionStateFactory(i18n, SettlementFacts, RunInputQueue, LineReferences, PathRedaction) {
8275
+ })(typeof globalThis !== "undefined" ? globalThis : window, function sessionStateFactory(i18n, SettlementFacts, RunInputQueue, LineReferences, PathRedaction, SessionTitle) {
8128
8276
  "use strict";
8129
8277
  const DEFAULT_PANEL_STATE = {
8130
8278
  mode: "ask",
@@ -8446,6 +8594,9 @@ ${block}`;
8446
8594
  if (session.titleSource === "manual" && patch.titleSource !== "manual" && Object.prototype.hasOwnProperty.call(patch, "title")) {
8447
8595
  next.title = session.title;
8448
8596
  next.titleSource = "manual";
8597
+ } else if (session.titleSource === "auto" && patch.titleSource !== "manual" && typeof patch.title === "string" && patch.title.trim() && session.runs?.length && SessionTitle.isGeneric(sanitizeAutoTitle(patch.title)) && !SessionTitle.isGeneric(sanitizeAutoTitle(session.title))) {
8598
+ next.title = session.title;
8599
+ next.titleSource = "auto";
8449
8600
  }
8450
8601
  return normalizeSession({
8451
8602
  ...next
@@ -8507,12 +8658,7 @@ ${block}`;
8507
8658
  }, sessions[0]);
8508
8659
  }
8509
8660
  function deriveSessionTitle(runs, task) {
8510
- const taskTitle = sanitizeAutoTitle(task);
8511
- if (taskTitle) {
8512
- return taskTitle;
8513
- }
8514
- const firstRunTask = Array.isArray(runs) && runs.length ? runs[0]?.task : "";
8515
- return sanitizeAutoTitle(firstRunTask);
8661
+ return SessionTitle.derive(runs, task, { sanitize: sanitizeAssistantVisibleText, maxChars: SESSION_AUTO_TITLE_CHARS });
8516
8662
  }
8517
8663
  function renameSession(state, sessionId, rawTitle, options2 = {}) {
8518
8664
  const sessions = Array.isArray(state?.sessions) ? state.sessions : [];
@@ -8542,14 +8688,13 @@ ${block}`;
8542
8688
  if (titleSource === "manual") {
8543
8689
  return normalizeTextField(rawTitle === LEGACY_DEFAULT_SESSION_TITLE ? "" : rawTitle, STORAGE_DEFAULT_LIMITS.sessionTitleChars);
8544
8690
  }
8545
- return sanitizeAutoTitle(rawTitle === LEGACY_DEFAULT_SESSION_TITLE ? "" : rawTitle) || derivedTitle || "";
8691
+ const current = sanitizeAutoTitle(rawTitle === LEGACY_DEFAULT_SESSION_TITLE ? "" : rawTitle);
8692
+ if (!current || SessionTitle.isGeneric(current)) return derivedTitle || current || "";
8693
+ if (current.endsWith("\u2026") && derivedTitle.startsWith(current.slice(0, -1))) return derivedTitle;
8694
+ return current;
8546
8695
  }
8547
8696
  function sanitizeAutoTitle(value) {
8548
- const title = sanitizeAssistantVisibleText(value).replace(/@file:[^\s]+/g, " ").replace(/@(context|compile-log)\b/g, " ").replace(/\s+/g, " ").trim();
8549
- if (!title) {
8550
- return "";
8551
- }
8552
- return title.length > SESSION_AUTO_TITLE_CHARS ? `${title.slice(0, SESSION_AUTO_TITLE_CHARS - 1)}\u2026` : title;
8697
+ return SessionTitle.fromPrompt(value, { sanitize: sanitizeAssistantVisibleText, maxChars: SESSION_AUTO_TITLE_CHARS });
8553
8698
  }
8554
8699
  function isDisplayableSession(session) {
8555
8700
  if (!session?.id) {
@@ -8637,6 +8782,7 @@ ${block}`;
8637
8782
  undoExpectedFiles: normalizeRunFiles(run.undoExpectedFiles),
8638
8783
  undoStatus: sanitizeAssistantVisibleText(run.undoStatus),
8639
8784
  nativeRequestId: sanitizeAssistantVisibleText(run.nativeRequestId),
8785
+ codexThreadId: sanitizeAssistantVisibleText(run.codexThreadId),
8640
8786
  codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
8641
8787
  forkSnapshot: run.forkSnapshot === true,
8642
8788
  forkSourceRunId: sanitizeAssistantVisibleText(run.forkSourceRunId),
@@ -8654,6 +8800,9 @@ ${block}`;
8654
8800
  if (run.changedDocument === true) {
8655
8801
  normalized.changedDocument = true;
8656
8802
  }
8803
+ if (Array.isArray(run.trackedChangeCaptures) && run.trackedChangeCaptures.length && !run.forkSnapshot) {
8804
+ normalized.trackedChangeCaptures = sanitizeAssistantVisibleValue(run.trackedChangeCaptures);
8805
+ }
8657
8806
  applyTrackedChangeStatus(normalized, run.trackedChangeStatus);
8658
8807
  return normalized;
8659
8808
  }
@@ -9088,6 +9237,7 @@ ${block}`;
9088
9237
  undoExpectedFiles: undoPayload.undoExpectedFiles,
9089
9238
  undoStatus: summarizeTextForStorage(run.undoStatus, "undo status"),
9090
9239
  nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
9240
+ codexThreadId: normalizeTextField(run.codexThreadId, 160),
9091
9241
  codexTurnId: normalizeTextField(run.codexTurnId, 160),
9092
9242
  forkSnapshot: run.forkSnapshot === true,
9093
9243
  forkSourceRunId: normalizeTextField(run.forkSourceRunId, 160),
@@ -9420,13 +9570,14 @@ ${block}`;
9420
9570
  setActiveSession,
9421
9571
  updateActiveSession,
9422
9572
  normalizeRuns,
9573
+ normalizeProjectReferenceFiles,
9423
9574
  prepareStateForStorage,
9424
9575
  estimateJsonBytes,
9425
9576
  computeSafeTaskSummary
9426
9577
  };
9427
9578
  });
9428
9579
  }).call(globalThis, void 0, void 0, void 0);
9429
- (function runClassicContentModule28(module, exports, require2) {
9580
+ (function runClassicContentModule29(module, exports, require2) {
9430
9581
  (function initStorageKeys(root, factory) {
9431
9582
  if (typeof module === "object" && module.exports) {
9432
9583
  module.exports = factory();
@@ -9454,7 +9605,7 @@ ${block}`;
9454
9605
  };
9455
9606
  });
9456
9607
  }).call(globalThis, void 0, void 0, void 0);
9457
- (function runClassicContentModule29(module, exports, require2) {
9608
+ (function runClassicContentModule30(module, exports, require2) {
9458
9609
  (function initStaleGuard(root, factory) {
9459
9610
  if (typeof module === "object" && module.exports) {
9460
9611
  module.exports = factory();
@@ -9605,7 +9756,7 @@ ${block}`;
9605
9756
  };
9606
9757
  });
9607
9758
  }).call(globalThis, void 0, void 0, void 0);
9608
- (function runClassicContentModule30(module, exports, require2) {
9759
+ (function runClassicContentModule31(module, exports, require2) {
9609
9760
  (function initStorageRunActions(root, factory) {
9610
9761
  if (typeof module === "object" && module.exports) {
9611
9762
  module.exports = factory();
@@ -9660,7 +9811,13 @@ ${block}`;
9660
9811
  } catch (_error) {
9661
9812
  return empty;
9662
9813
  }
9663
- return getUtf8ByteLength(serialized) <= MAX_PERSISTED_ACTION_BYTES_PER_RUN ? payload : empty;
9814
+ if (getUtf8ByteLength(serialized) > MAX_PERSISTED_ACTION_BYTES_PER_RUN) return empty;
9815
+ const captures = cloneSerializableArray(run.trackedChangeCaptures);
9816
+ if (captures.length) {
9817
+ const extended = { ...payload, trackedChangeCaptures: captures };
9818
+ if (getUtf8ByteLength(JSON.stringify(extended)) <= MAX_PERSISTED_ACTION_BYTES_PER_RUN) return extended;
9819
+ }
9820
+ return payload;
9664
9821
  }
9665
9822
  function emptyActionPayload() {
9666
9823
  return {
@@ -9688,6 +9845,37 @@ ${block}`;
9688
9845
  }
9689
9846
  return value.length * 2;
9690
9847
  }
9848
+ function compactRunExecutionSnapshot(run, normalizeString) {
9849
+ var snapshot = run && run.executionSnapshot;
9850
+ if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) return {};
9851
+ var normalize = typeof normalizeString === "function" ? normalizeString : String;
9852
+ var compact = {};
9853
+ var fields = [
9854
+ "schemaVersion",
9855
+ "mode",
9856
+ "providerId",
9857
+ "providerRevision",
9858
+ "model",
9859
+ "reasoningEffort",
9860
+ "speedTier",
9861
+ "autoRecompile",
9862
+ "requireReviewing",
9863
+ "capturedAt",
9864
+ "source"
9865
+ ];
9866
+ for (var key of fields) {
9867
+ if (!Object.prototype.hasOwnProperty.call(snapshot, key)) continue;
9868
+ var value = snapshot[key];
9869
+ if (typeof value === "string") compact[key] = normalize(value);
9870
+ else if (value === null || typeof value === "number" || typeof value === "boolean") compact[key] = value;
9871
+ }
9872
+ if (Array.isArray(snapshot.focusFiles)) {
9873
+ compact.focusFiles = snapshot.focusFiles.slice(0, 100).filter(function(value2) {
9874
+ return typeof value2 === "string";
9875
+ }).map(normalize);
9876
+ }
9877
+ return { executionSnapshot: compact };
9878
+ }
9691
9879
  function compactProviderSnapshot(value) {
9692
9880
  return {
9693
9881
  providerId: typeof value.providerId === "string" && value.providerId ? value.providerId : "builtin",
@@ -9730,11 +9918,12 @@ ${block}`;
9730
9918
  compactRunsForStorage,
9731
9919
  compactRunActionPayload,
9732
9920
  compactProviderSnapshot,
9921
+ compactRunExecutionSnapshot,
9733
9922
  hashString
9734
9923
  };
9735
9924
  });
9736
9925
  }).call(globalThis, void 0, void 0, void 0);
9737
- (function runClassicContentModule31(module, exports, require2) {
9926
+ (function runClassicContentModule32(module, exports, require2) {
9738
9927
  (function initStorageSessionClaims(root, factory) {
9739
9928
  var api = factory();
9740
9929
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -9800,26 +9989,28 @@ ${block}`;
9800
9989
  return Object.freeze({ createSessionClaimer });
9801
9990
  });
9802
9991
  }).call(globalThis, void 0, void 0, void 0);
9803
- (function runClassicContentModule32(module, exports, require2) {
9992
+ (function runClassicContentModule33(module, exports, require2) {
9804
9993
  (function initStorageDb(root, factory) {
9805
9994
  if (typeof module === "object" && module.exports) {
9806
9995
  module.exports = factory(
9807
9996
  require2("./storageRunActions"),
9808
9997
  require2("./runInputQueue"),
9809
9998
  require2("./settlementFacts"),
9810
- require2("./storageSessionClaims")
9999
+ require2("./storageSessionClaims"),
10000
+ require2("./sessionState")
9811
10001
  );
9812
10002
  } else {
9813
10003
  root.CodexOverleafModuleRegistry.define("StorageDb", [
9814
10004
  "StorageRunActions",
9815
10005
  "RunInputQueue",
9816
10006
  "SettlementFacts",
9817
- "StorageSessionClaims"
10007
+ "StorageSessionClaims",
10008
+ "SessionState"
9818
10009
  ], factory);
9819
10010
  }
9820
- })(typeof globalThis !== "undefined" ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims) {
10011
+ })(typeof globalThis !== "undefined" ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims, SessionState) {
9821
10012
  "use strict";
9822
- if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
10013
+ if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactRunExecutionSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
9823
10014
  throw new Error("Codex Overleaf storage run-action helpers are unavailable.");
9824
10015
  }
9825
10016
  var TARGET_SCHEMA_VERSION = 3;
@@ -10087,6 +10278,7 @@ ${block}`;
10087
10278
  codexThreadId: typeof input.codexThreadId === "string" ? input.codexThreadId : "",
10088
10279
  status: typeof input.status === "string" && input.status ? input.status : "active",
10089
10280
  focusFiles: normalizePathList(input.focusFiles),
10281
+ projectReferenceFiles: SessionState.normalizeProjectReferenceFiles(input.projectReferenceFiles),
10090
10282
  pendingInputs: RunInputQueue.compactForStorage(input.pendingInputs, {
10091
10283
  normalizeField: normalizeTextField,
10092
10284
  normalizeDisplay: normalizeDisplayTextForStorage,
@@ -10126,8 +10318,8 @@ ${block}`;
10126
10318
  }
10127
10319
  }
10128
10320
  function deriveSafeTaskSummaryFromInput(input) {
10129
- var SessionState = loadSessionState();
10130
- if (!SessionState || !(SessionState.computeSafeTaskSummary instanceof Function)) {
10321
+ var SessionState2 = loadSessionState();
10322
+ if (!SessionState2 || !(SessionState2.computeSafeTaskSummary instanceof Function)) {
10131
10323
  return "";
10132
10324
  }
10133
10325
  var task = typeof input.task === "string" && input.task ? input.task : "";
@@ -10137,7 +10329,7 @@ ${block}`;
10137
10329
  task = firstRun.task;
10138
10330
  }
10139
10331
  }
10140
- return SessionState.computeSafeTaskSummary(task);
10332
+ return SessionState2.computeSafeTaskSummary(task);
10141
10333
  }
10142
10334
  var _sessionStateCache = null;
10143
10335
  function loadSessionState() {
@@ -10422,12 +10614,7 @@ ${block}`;
10422
10614
  });
10423
10615
  }
10424
10616
  function compactRunsForStorage(runs, options2) {
10425
- return StorageRunActions.compactRunsForStorage(
10426
- runs,
10427
- options2,
10428
- SESSION_STORAGE_LIMITS.maxRunsPerSession,
10429
- compactRunForStorage
10430
- );
10617
+ return StorageRunActions.compactRunsForStorage(runs, options2, SESSION_STORAGE_LIMITS.maxRunsPerSession, compactRunForStorage);
10431
10618
  }
10432
10619
  function compactRunForStorage(run, keepActionPayload) {
10433
10620
  var actionPayload = StorageRunActions.compactRunActionPayload(run, keepActionPayload);
@@ -10436,6 +10623,9 @@ ${block}`;
10436
10623
  task: normalizeDisplayTextForStorage(run.task || "untitled task", SESSION_STORAGE_LIMITS.taskChars),
10437
10624
  mode: typeof run.mode === "string" ? redactSecretLikeText(run.mode) : "",
10438
10625
  ...StorageRunActions.compactProviderSnapshot(run),
10626
+ ...StorageRunActions.compactRunExecutionSnapshot(run, function(text) {
10627
+ return normalizeTextField(text, 4096);
10628
+ }),
10439
10629
  model: normalizeTextField(run.model, 80),
10440
10630
  reasoningEffort: typeof run.reasoningEffort === "string" ? redactSecretLikeText(run.reasoningEffort) : "",
10441
10631
  speedTier: typeof run.speedTier === "string" ? redactSecretLikeText(run.speedTier) : "",
@@ -10451,8 +10641,10 @@ ${block}`;
10451
10641
  undoBaseFiles: actionPayload.undoBaseFiles,
10452
10642
  undoTrackedChanges: actionPayload.undoTrackedChanges,
10453
10643
  undoExpectedFiles: actionPayload.undoExpectedFiles,
10644
+ ...actionPayload.trackedChangeCaptures ? { trackedChangeCaptures: actionPayload.trackedChangeCaptures } : {},
10454
10645
  undoStatus: normalizeDisplayTextForStorage(run.undoStatus, SESSION_STORAGE_LIMITS.statusTextChars),
10455
10646
  nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
10647
+ codexThreadId: normalizeTextField(run.codexThreadId, 160),
10456
10648
  codexTurnId: normalizeTextField(run.codexTurnId, 160),
10457
10649
  nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
10458
10650
  queueItemId: normalizeTextField(run.queueItemId, 160)
@@ -11025,7 +11217,7 @@ ${block}`;
11025
11217
  };
11026
11218
  });
11027
11219
  }).call(globalThis, void 0, void 0, void 0);
11028
- (function runClassicContentModule33(module, exports, require2) {
11220
+ (function runClassicContentModule34(module, exports, require2) {
11029
11221
  (function initStorageMigration(root, factory) {
11030
11222
  if (typeof module === "object" && module.exports) {
11031
11223
  module.exports = factory(require2("./storageDb"));
@@ -11480,7 +11672,7 @@ ${block}`;
11480
11672
  };
11481
11673
  });
11482
11674
  }).call(globalThis, void 0, void 0, void 0);
11483
- (function runClassicContentModule34(module, exports, require2) {
11675
+ (function runClassicContentModule35(module, exports, require2) {
11484
11676
  (function initCompileAdapter(root, factory) {
11485
11677
  if (typeof module === "object" && module.exports) {
11486
11678
  module.exports = factory();
@@ -11623,7 +11815,7 @@ ${block}`;
11623
11815
  };
11624
11816
  });
11625
11817
  }).call(globalThis, void 0, void 0, void 0);
11626
- (function runClassicContentModule35(module, exports, require2) {
11818
+ (function runClassicContentModule36(module, exports, require2) {
11627
11819
  (function initCodexOverleafCompatibility(root, factory) {
11628
11820
  if (typeof module === "object" && module.exports) {
11629
11821
  module.exports = factory();
@@ -11637,7 +11829,7 @@ ${block}`;
11637
11829
  const MIN_NATIVE_VERSION = "1.0.0";
11638
11830
  const MIN_COMPATIBLE_NATIVE_VERSION = "1.0.0";
11639
11831
  const MIN_COMPATIBLE_EXTENSION_VERSION = "1.0.0";
11640
- const BUILD_TARGET_VERSION = "2.3.5";
11832
+ const BUILD_TARGET_VERSION = "2.4.0";
11641
11833
  const DEFAULT_CHROME_EXTENSION_ID = "illdpneeeopfffmiepaejglgmhpmdhdc";
11642
11834
  const REQUIRED_CAPABILITIES = Object.freeze([
11643
11835
  "bridgePing",
@@ -11933,14 +12125,14 @@ ${block}`;
11933
12125
  };
11934
12126
  });
11935
12127
  }).call(globalThis, void 0, void 0, void 0);
11936
- (function runClassicContentModule36(module, exports, require2) {
12128
+ (function runClassicContentModule37(module, exports, require2) {
11937
12129
  (function(root, factory) {
11938
12130
  var api = factory();
11939
12131
  if (typeof module === "object" && module.exports) module.exports = api;
11940
12132
  if (root) root.CodexOverleafPageRpcContract = api;
11941
12133
  })(typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : this, function() {
11942
12134
  "use strict";
11943
- var REVISION = "2026-08-04-page-rpc-contract-v15";
12135
+ var REVISION = "2026-09-14-native-text-upload-v28";
11944
12136
  var METHODS = freezeCatalog({
11945
12137
  initializeCapability: entry("bridge.initialize", "control", "none", "default", false, "none"),
11946
12138
  probe: entry("bridge.probe", "read", "none", "default", false, "safe"),
@@ -11957,6 +12149,7 @@ ${block}`;
11957
12149
  binaryUploadCommit: entry("asset.write", "document", "required", "writeback", true, "no_retry"),
11958
12150
  binaryUploadAbort: entry("asset.stage", "cache", "none", "default", false, "idempotent"),
11959
12151
  jumpToPosition: entry("editor.navigate", "navigation", "required", "default", false, "idempotent"),
12152
+ reconcileTrackedChangeCapture: entry("tracked_changes.read", "read", "required", "default", false, "safe"),
11960
12153
  rejectTrackedChanges: entry("tracked_changes.reject", "document", "required", "lifecycle", true, "no_retry"),
11961
12154
  acceptTrackedChanges: entry("tracked_changes.accept", "document", "required", "lifecycle", true, "no_retry"),
11962
12155
  triggerCompile: entry("compile.trigger", "page_state", "required", "compile", false, "no_retry"),
@@ -12064,7 +12257,7 @@ ${block}`;
12064
12257
  });
12065
12258
  });
12066
12259
  }).call(globalThis, void 0, void 0, void 0);
12067
- (function runClassicContentModule37(module, exports, require2) {
12260
+ (function runClassicContentModule38(module, exports, require2) {
12068
12261
  (function initCodexOverleafManagedUpdateProjection(root) {
12069
12262
  "use strict";
12070
12263
  const PHASE_ALIASES = Object.freeze({
@@ -12090,7 +12283,7 @@ ${block}`;
12090
12283
  "deferred"
12091
12284
  ]);
12092
12285
  const PHASE_TIMEOUTS = Object.freeze({
12093
- checking: 30 * 1e3,
12286
+ checking: 60 * 1e3,
12094
12287
  downloading: 2 * 60 * 1e3,
12095
12288
  applying: 90 * 1e3,
12096
12289
  awaiting_health: 2 * 60 * 1e3,
@@ -12370,7 +12563,7 @@ ${block}`;
12370
12563
  });
12371
12564
  })(typeof window !== "undefined" ? window : globalThis);
12372
12565
  }).call(globalThis, void 0, void 0, void 0);
12373
- (function runClassicContentModule38(module, exports, require2) {
12566
+ (function runClassicContentModule39(module, exports, require2) {
12374
12567
  (function initGovernanceRules(root, factory) {
12375
12568
  if (typeof module === "object" && module.exports) {
12376
12569
  let projectFiles = null;
@@ -12587,7 +12780,7 @@ ${block}`;
12587
12780
  };
12588
12781
  });
12589
12782
  }).call(globalThis, void 0, void 0, void 0);
12590
- (function runClassicContentModule39(module, exports, require2) {
12783
+ (function runClassicContentModule40(module, exports, require2) {
12591
12784
  (function initSensitiveScan(root, factory) {
12592
12785
  if (typeof module === "object" && module.exports) {
12593
12786
  module.exports = factory();
@@ -12697,7 +12890,7 @@ ${block}`;
12697
12890
  };
12698
12891
  });
12699
12892
  }).call(globalThis, void 0, void 0, void 0);
12700
- (function runClassicContentModule40(module, exports, require2) {
12893
+ (function runClassicContentModule41(module, exports, require2) {
12701
12894
  (function initAuditRecords(root, factory) {
12702
12895
  if (typeof module === "object" && module.exports) {
12703
12896
  module.exports = factory();
@@ -13223,7 +13416,7 @@ ${block}`;
13223
13416
  };
13224
13417
  });
13225
13418
  }).call(globalThis, void 0, void 0, void 0);
13226
- (function runClassicContentModule41(module, exports, require2) {
13419
+ (function runClassicContentModule42(module, exports, require2) {
13227
13420
  (function initCodexOverleafNativeChannel(root, factory) {
13228
13421
  if (typeof module === "object" && module.exports) {
13229
13422
  module.exports = factory();
@@ -13302,7 +13495,7 @@ ${block}`;
13302
13495
  };
13303
13496
  });
13304
13497
  }).call(globalThis, void 0, void 0, void 0);
13305
- (function runClassicContentModule42(module, exports, require2) {
13498
+ (function runClassicContentModule43(module, exports, require2) {
13306
13499
  (function initCodexOverleafPageBridgeClient(root, factory) {
13307
13500
  const api = factory();
13308
13501
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -13325,9 +13518,11 @@ ${block}`;
13325
13518
  ["src/page/overleafProjectSnapshot.js", "codex-overleaf-project-snapshot-script"],
13326
13519
  ["src/page/pageBridgeCapability.js", "codex-overleaf-page-bridge-capability-script"],
13327
13520
  ["src/page/treeOperations.js", "codex-overleaf-tree-operations-script", true],
13521
+ ["src/page/textFileCreator.js", "codex-overleaf-text-file-creator-script", true],
13328
13522
  ["src/page/binaryAssetUploader.js", "codex-overleaf-binary-asset-uploader-script", true],
13329
13523
  ["src/page/snapshotRouter.js", "codex-overleaf-snapshot-router-script"],
13330
13524
  ["src/page/writeGuard.js", "codex-overleaf-write-guard-script", true],
13525
+ ["src/page/trackedChangeCapture.js", "codex-overleaf-tracked-change-capture-script", true],
13331
13526
  ["src/page/trackedChangesLifecycle.js", "codex-overleaf-tracked-changes-lifecycle-script", true],
13332
13527
  ["src/page/writebackRouter.js", "codex-overleaf-writeback-router-script", true],
13333
13528
  ["src/pageBridge.js", "codex-overleaf-page-bridge-script", true]
@@ -13505,7 +13700,7 @@ ${block}`;
13505
13700
  });
13506
13701
  });
13507
13702
  }).call(globalThis, void 0, void 0, void 0);
13508
- (function runClassicContentModule43(module, exports, require2) {
13703
+ (function runClassicContentModule44(module, exports, require2) {
13509
13704
  (function initCodexOverleafNativeCompatibilityController(root, factory) {
13510
13705
  const api = factory();
13511
13706
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -13527,7 +13722,9 @@ ${block}`;
13527
13722
  async function dispatch(payload, channelMethod) {
13528
13723
  const compatibilityGate = await ensureForMethod(payload?.method);
13529
13724
  if (!compatibilityGate.ok) return compatibilityGate.response;
13530
- if (gatedMethods.has(payload?.method)) options2.throwIfCancellationRequested?.();
13725
+ if (gatedMethods.has(payload?.method) && payload.method !== "mirror.invalidate") {
13726
+ options2.throwIfCancellationRequested?.();
13727
+ }
13531
13728
  return nativeChannel[channelMethod](
13532
13729
  attachEvidence(payload, compatibilityGate.compatibility)
13533
13730
  );
@@ -13784,7 +13981,7 @@ ${block}`;
13784
13981
  return Object.freeze({ create });
13785
13982
  });
13786
13983
  }).call(globalThis, void 0, void 0, void 0);
13787
- (function runClassicContentModule44(module, exports, require2) {
13984
+ (function runClassicContentModule45(module, exports, require2) {
13788
13985
  (function initCodexOverleafWritebackController(root, factory) {
13789
13986
  if (typeof module === "object" && module.exports) {
13790
13987
  module.exports = factory();
@@ -14049,7 +14246,7 @@ ${block}`;
14049
14246
  };
14050
14247
  });
14051
14248
  }).call(globalThis, void 0, void 0, void 0);
14052
- (function runClassicContentModule45(module, exports, require2) {
14249
+ (function runClassicContentModule46(module, exports, require2) {
14053
14250
  (function initCodexOverleafAssetTransferBroker(root, factory) {
14054
14251
  const api = factory();
14055
14252
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -14180,8 +14377,10 @@ ${block}`;
14180
14377
  merged.applied.push(...Array.isArray(result?.applied) ? result.applied : []);
14181
14378
  merged.skipped.push(...Array.isArray(result?.skipped) ? result.skipped : []);
14182
14379
  merged.trackedChanges.push(...Array.isArray(result?.trackedChanges) ? result.trackedChanges : []);
14380
+ if (result?.trackedChangeCaptures?.length) (merged.trackedChangeCaptures || (merged.trackedChangeCaptures = [])).push(...result.trackedChangeCaptures);
14183
14381
  if (result?.ok !== true) merged.ok = false;
14184
14382
  }
14383
+ if (merged.trackedChangeCaptures?.some((capture) => capture.state !== "observed")) merged.trackedChanges = [];
14185
14384
  if (merged.skipped.length) merged.ok = false;
14186
14385
  return merged;
14187
14386
  }
@@ -14211,7 +14410,7 @@ ${block}`;
14211
14410
  return { create, mergeApplyResults };
14212
14411
  });
14213
14412
  }).call(globalThis, void 0, void 0, void 0);
14214
- (function runClassicContentModule46(module, exports, require2) {
14413
+ (function runClassicContentModule47(module, exports, require2) {
14215
14414
  (function initCodexOverleafReviewHunks(root, factory) {
14216
14415
  if (typeof module === "object" && module.exports) {
14217
14416
  module.exports = factory();
@@ -14430,7 +14629,7 @@ ${block}`;
14430
14629
  };
14431
14630
  });
14432
14631
  }).call(globalThis, void 0, void 0, void 0);
14433
- (function runClassicContentModule47(module, exports, require2) {
14632
+ (function runClassicContentModule48(module, exports, require2) {
14434
14633
  (function initCodexOverleafDiffReviewPanel(root, factory) {
14435
14634
  if (typeof module === "object" && module.exports) {
14436
14635
  module.exports = factory(require2("./reviewHunks"));
@@ -15157,7 +15356,7 @@ ${block}`;
15157
15356
  };
15158
15357
  });
15159
15358
  }).call(globalThis, void 0, void 0, void 0);
15160
- (function runClassicContentModule48(module, exports, require2) {
15359
+ (function runClassicContentModule49(module, exports, require2) {
15161
15360
  (function initCodexOverleafContextTray(root, factory) {
15162
15361
  if (typeof module === "object" && module.exports) {
15163
15362
  module.exports = factory(require2("../shared/projectFiles"));
@@ -15177,6 +15376,8 @@ ${block}`;
15177
15376
  saveState = null,
15178
15377
  saveStateSoon = () => {
15179
15378
  },
15379
+ onProjectFilesReady = () => {
15380
+ },
15180
15381
  updateActiveSession = defaultUpdateActiveSession,
15181
15382
  callPageBridge = () => Promise.resolve({ ok: false }),
15182
15383
  getCurrentProjectId = () => "",
@@ -15340,6 +15541,7 @@ ${block}`;
15340
15541
  renderContextFiles(contextProject);
15341
15542
  if (isExactContextFileListProject(contextProject)) {
15342
15543
  setContextSyncState("exact-ready");
15544
+ onProjectFilesReady(contextProject);
15343
15545
  } else if (contextSyncState !== "partial") {
15344
15546
  setContextSyncState("exact-loading");
15345
15547
  void enhanceContextFilesFromExactSnapshot({ loadId: contextLoadId });
@@ -15361,6 +15563,7 @@ ${block}`;
15361
15563
  contextProject = project;
15362
15564
  setContextSyncState(isExactContextFileListProject(project) ? "exact-ready" : "exact-loading");
15363
15565
  renderContextFiles(project);
15566
+ if (isExactContextFileListProject(project)) onProjectFilesReady(project);
15364
15567
  if (!isExactContextFileListProject(project)) {
15365
15568
  void enhanceContextFilesFromExactSnapshot({
15366
15569
  force: Boolean(options2.force),
@@ -15418,6 +15621,7 @@ ${block}`;
15418
15621
  contextProject = project;
15419
15622
  setContextSyncState("exact-ready");
15420
15623
  renderContextFiles(project);
15624
+ onProjectFilesReady(project);
15421
15625
  } catch (_error) {
15422
15626
  if (loadId === contextLoadId) {
15423
15627
  setContextSyncState("partial");
@@ -15905,7 +16109,7 @@ ${block}`;
15905
16109
  };
15906
16110
  });
15907
16111
  }).call(globalThis, void 0, void 0, void 0);
15908
- (function runClassicContentModule49(module, exports, require2) {
16112
+ (function runClassicContentModule50(module, exports, require2) {
15909
16113
  (function initCodexOverleafLocalSkillsPanel(root, factory) {
15910
16114
  if (typeof module === "object" && module.exports) {
15911
16115
  module.exports = factory();
@@ -16136,7 +16340,7 @@ ${block}`;
16136
16340
  };
16137
16341
  });
16138
16342
  }).call(globalThis, void 0, void 0, void 0);
16139
- (function runClassicContentModule50(module, exports, require2) {
16343
+ (function runClassicContentModule51(module, exports, require2) {
16140
16344
  (function initCodexOverleafRunController(root, factory) {
16141
16345
  if (typeof module === "object" && module.exports) {
16142
16346
  module.exports = factory();
@@ -16366,7 +16570,7 @@ ${block}`;
16366
16570
  };
16367
16571
  });
16368
16572
  }).call(globalThis, void 0, void 0, void 0);
16369
- (function runClassicContentModule51(module, exports, require2) {
16573
+ (function runClassicContentModule52(module, exports, require2) {
16370
16574
  (function initCodexOverleafMirrorHealth(root, factory) {
16371
16575
  if (typeof module === "object" && module.exports) {
16372
16576
  module.exports = factory();
@@ -16450,7 +16654,7 @@ ${block}`;
16450
16654
  };
16451
16655
  });
16452
16656
  }).call(globalThis, void 0, void 0, void 0);
16453
- (function runClassicContentModule52(module, exports, require2) {
16657
+ (function runClassicContentModule53(module, exports, require2) {
16454
16658
  (function initCodexOverleafOtWarmMirrorController(root, factory) {
16455
16659
  if (typeof module === "object" && module.exports) {
16456
16660
  module.exports = factory();
@@ -16462,6 +16666,12 @@ ${block}`;
16462
16666
  const OT_POLL_INTERVAL_MS = 1e3;
16463
16667
  const OT_PATCH_DEBOUNCE_MS = 500;
16464
16668
  const OT_MAX_PATCH_BATCH = 25;
16669
+ const OT_FRESHNESS_MAX_AGE_MS = 3e4;
16670
+ const OT_MAX_FILE_BYTES = 1024 * 1024;
16671
+ const OT_MAX_PENDING_EVENTS = 64;
16672
+ const OT_MAX_PENDING_BYTES = 4 * 1024 * 1024;
16673
+ const OT_MAX_BATCH_BYTES = 2 * 1024 * 1024;
16674
+ const encoder = typeof TextEncoder === "function" ? new TextEncoder() : null;
16465
16675
  const PAUSE_STATES = /* @__PURE__ */ new Set(["running", "writing", "undoing", "compiling"]);
16466
16676
  function buildPatchFilesRequest({ projectId, events, nativeCompatibility } = {}) {
16467
16677
  const files = [];
@@ -16513,13 +16723,14 @@ ${block}`;
16513
16723
  }
16514
16724
  return { pause: false };
16515
16725
  }
16516
- function canUseOtWarmStart({ enabled, focusFiles, mirrorStatus } = {}) {
16726
+ function canUseOtWarmStart({ enabled, focusFiles, mirrorStatus, now = Date.now() } = {}) {
16517
16727
  if (!enabled) {
16518
16728
  return { ok: false, reason: "disabled" };
16519
16729
  }
16520
16730
  if (!mirrorStatus || mirrorStatus.exists !== true) {
16521
16731
  return { ok: false, reason: "mirror_missing" };
16522
16732
  }
16733
+ if (mirrorStatus.dirty === true) return { ok: false, reason: "mirror_dirty" };
16523
16734
  const normalizedFocusFiles = normalizePaths(focusFiles);
16524
16735
  if (!normalizedFocusFiles.length) {
16525
16736
  return { ok: false, reason: "no_focus_files" };
@@ -16529,6 +16740,8 @@ ${block}`;
16529
16740
  if (file?.state !== "fresh") {
16530
16741
  continue;
16531
16742
  }
16743
+ const age = now - Date.parse(file.observedAt ?? file.lastPatchAt ?? "");
16744
+ if (!Number.isFinite(age) || age < 0 || age > OT_FRESHNESS_MAX_AGE_MS) continue;
16532
16745
  const filePath = normalizePath(file.path);
16533
16746
  if (filePath) {
16534
16747
  freshFiles.add(filePath);
@@ -16544,6 +16757,102 @@ ${block}`;
16544
16757
  }
16545
16758
  return { ok: false, reason: "missing_ot_fresh_focus" };
16546
16759
  }
16760
+ function textBytes(value) {
16761
+ const text = String(value || "");
16762
+ return encoder ? encoder.encode(text).byteLength : text.length * 3;
16763
+ }
16764
+ function patchBytes(event) {
16765
+ return textBytes(event.nextContent) + textBytes(event.path) + 512;
16766
+ }
16767
+ function queuePatchEvents(queue, events, now = Date.now()) {
16768
+ if (!Array.isArray(events) || events.length > OT_MAX_PENDING_EVENTS) return { ok: false, reason: "ot_queue_limit" };
16769
+ const next = Array.isArray(queue) ? queue.slice() : [];
16770
+ let bytes = next.reduce((sum, event) => sum + patchBytes(event), 0);
16771
+ if (next.length > OT_MAX_PENDING_EVENTS || bytes > OT_MAX_PENDING_BYTES) return { ok: false, reason: "ot_queue_limit" };
16772
+ for (const input of events) {
16773
+ const path = normalizePath(input?.path);
16774
+ const time = typeof input?.observedAt === "number" ? input.observedAt : Date.parse(input?.observedAt || "");
16775
+ if (!isSafeRelativePath(path) || path.length > 1024 || typeof input?.nextContent !== "string" || typeof input.baseHash !== "string" || !input.baseHash || input.baseHash.length > 128) {
16776
+ return { ok: false, reason: "ot_event_invalid" };
16777
+ }
16778
+ if (!Number.isFinite(time) || now - time < 0 || now - time > OT_FRESHNESS_MAX_AGE_MS) return { ok: false, reason: "ot_event_expired" };
16779
+ if (textBytes(input.nextContent) > OT_MAX_FILE_BYTES) return { ok: false, reason: "ot_file_limit" };
16780
+ const event = {
16781
+ path,
16782
+ baseHash: input.baseHash,
16783
+ nextContent: input.nextContent,
16784
+ nextHash: typeof input.nextHash === "string" && input.nextHash.length <= 128 ? input.nextHash : "",
16785
+ observedAt: new Date(time).toISOString()
16786
+ };
16787
+ if (isPrimitiveMetadata(input.observedVersion) && String(input.observedVersion).length <= 128) event.observedVersion = input.observedVersion;
16788
+ const tail = next[next.length - 1];
16789
+ if (tail?.path === path && tail.nextHash && tail.nextHash === event.baseHash) {
16790
+ event.baseHash = tail.baseHash;
16791
+ bytes -= patchBytes(next.pop());
16792
+ }
16793
+ if (event.nextHash !== event.baseHash) {
16794
+ next.push(event);
16795
+ bytes += patchBytes(event);
16796
+ }
16797
+ if (next.length > OT_MAX_PENDING_EVENTS || bytes > OT_MAX_PENDING_BYTES) return { ok: false, reason: "ot_queue_limit" };
16798
+ }
16799
+ return { ok: true, queue: next, bytes };
16800
+ }
16801
+ function takePatchBatch(queue) {
16802
+ let bytes = 0;
16803
+ let count = 0;
16804
+ while (count < Math.min(queue.length, OT_MAX_PATCH_BATCH)) {
16805
+ const size = patchBytes(queue[count]);
16806
+ if (bytes + size > OT_MAX_BATCH_BYTES) break;
16807
+ bytes += size;
16808
+ count += 1;
16809
+ }
16810
+ return queue.splice(0, count);
16811
+ }
16812
+ function validatePatchReceipt(events, result = {}, now = Date.now()) {
16813
+ if (Array.isArray(result.skippedFiles) && result.skippedFiles.length || Number(result.skippedCount || 0) > 0) {
16814
+ return { ok: false, reason: "mirror_patch_skipped" };
16815
+ }
16816
+ if (!events.length || Number(result.appliedCount) !== events.length || !Array.isArray(result.appliedFiles) || result.appliedFiles.length !== events.length || !result.appliedFiles.every((file, index) => normalizePath(file?.path) === events[index].path && (!events[index].nextHash || file.hash === events[index].nextHash))) {
16817
+ return { ok: false, reason: "mirror_patch_invalid_result" };
16818
+ }
16819
+ if (events.some((event) => {
16820
+ const time = Date.parse(event.observedAt);
16821
+ return !Number.isFinite(time) || time > now || now - time > OT_FRESHNESS_MAX_AGE_MS;
16822
+ })) return { ok: false, reason: "ot_event_expired" };
16823
+ return { ok: true };
16824
+ }
16825
+ function readOtBridgeStatus(response) {
16826
+ if (!response || response.ok === false) return "unavailable";
16827
+ return response.state || response.status || response.result?.state || response.result?.status || "unavailable";
16828
+ }
16829
+ function readOtBridgeEvents(response) {
16830
+ if (Array.isArray(response)) return response;
16831
+ if (Array.isArray(response?.events)) return response.events;
16832
+ if (Array.isArray(response?.result?.events)) return response.result.events;
16833
+ return [];
16834
+ }
16835
+ function readOtBridgeErrorCode(response, fallback) {
16836
+ const value = response?.lastErrorCode || response?.reason || response?.error?.code || response?.error || fallback;
16837
+ return typeof value === "string" ? value : fallback;
16838
+ }
16839
+ function normalizeFailureCode(value, fallback = "ot_error") {
16840
+ const code = typeof value === "string" ? value : value?.code;
16841
+ if (/^[a-z][a-z0-9_]{0,79}$/.test(code || "")) return code;
16842
+ if (/page bridge|capability|bridge timed out/i.test(String(code || ""))) return "ot_bridge_unavailable";
16843
+ return fallback;
16844
+ }
16845
+ function getFailureMessageKey(value) {
16846
+ const code = normalizeFailureCode(value);
16847
+ if (/bridge|ot_status|ot_events/.test(code)) return "otFailureBridge";
16848
+ if (/missing_ot_text|observer_create|observer_unavailable/.test(code)) return "otFailureObserver";
16849
+ if (code === "ot_file_limit") return "otFailureFileLimit";
16850
+ if (code === "ot_queue_limit") return "otFailureQueueLimit";
16851
+ if (code === "ot_event_expired") return "otFailureExpired";
16852
+ if (/editor|active_path|document/.test(code)) return "otFailureEditor";
16853
+ if (/mirror|hash|baseline/.test(code)) return "otFailureMirror";
16854
+ return "otFailureGeneric";
16855
+ }
16547
16856
  function normalizePath(value) {
16548
16857
  return String(value ?? "").trim().replace(/^@file:/i, "").trim().replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/+/, "");
16549
16858
  }
@@ -16595,6 +16904,19 @@ ${block}`;
16595
16904
  OT_POLL_INTERVAL_MS,
16596
16905
  OT_PATCH_DEBOUNCE_MS,
16597
16906
  OT_MAX_PATCH_BATCH,
16907
+ OT_FRESHNESS_MAX_AGE_MS,
16908
+ OT_MAX_FILE_BYTES,
16909
+ OT_MAX_PENDING_EVENTS,
16910
+ OT_MAX_PENDING_BYTES,
16911
+ OT_MAX_BATCH_BYTES,
16912
+ queuePatchEvents,
16913
+ takePatchBatch,
16914
+ validatePatchReceipt,
16915
+ normalizeFailureCode,
16916
+ readOtBridgeStatus,
16917
+ readOtBridgeEvents,
16918
+ readOtBridgeErrorCode,
16919
+ getFailureMessageKey,
16598
16920
  buildPatchFilesRequest,
16599
16921
  canUseOtWarmStart,
16600
16922
  normalizePath,
@@ -16603,7 +16925,7 @@ ${block}`;
16603
16925
  };
16604
16926
  });
16605
16927
  }).call(globalThis, void 0, void 0, void 0);
16606
- (function runClassicContentModule53(module, exports, require2) {
16928
+ (function runClassicContentModule54(module, exports, require2) {
16607
16929
  (function initComposerAttachments(global) {
16608
16930
  "use strict";
16609
16931
  function createComposerAttachmentController(options2 = {}) {
@@ -17063,7 +17385,7 @@ ${block}`;
17063
17385
  };
17064
17386
  })(window);
17065
17387
  }).call(globalThis, void 0, void 0, void 0);
17066
- (function runClassicContentModule54(module, exports, require2) {
17388
+ (function runClassicContentModule55(module, exports, require2) {
17067
17389
  (function initCodexOverleafPanelRenderer() {
17068
17390
  "use strict";
17069
17391
  const ROOT_CLASS = "codex-overleaf-panel-mounted";
@@ -17464,7 +17786,7 @@ ${block}`;
17464
17786
  };
17465
17787
  })();
17466
17788
  }).call(globalThis, void 0, void 0, void 0);
17467
- (function runClassicContentModule55(module, exports, require2) {
17789
+ (function runClassicContentModule56(module, exports, require2) {
17468
17790
  (function initCodexOverleafSessionPanel() {
17469
17791
  "use strict";
17470
17792
  function create(options2 = {}) {
@@ -17700,7 +18022,7 @@ ${block}`;
17700
18022
  };
17701
18023
  })();
17702
18024
  }).call(globalThis, void 0, void 0, void 0);
17703
- (function runClassicContentModule56(module, exports, require2) {
18025
+ (function runClassicContentModule57(module, exports, require2) {
17704
18026
  (function initCodexOverleafSettingsPanel() {
17705
18027
  "use strict";
17706
18028
  function codexIcon(name) {
@@ -17794,18 +18116,18 @@ ${block}`;
17794
18116
  </details>
17795
18117
  <details class="codex-set-group" data-set-group="experimental">
17796
18118
  <summary class="codex-set-group-head">
17797
- <span class="codex-set-group-title">${codexSetIcon("flask")}<span data-i18n="experimentalTitle">Experimental</span></span>
18119
+ <span class="codex-set-group-title">${codexSetIcon("database")}<span data-i18n="otSettingsTitle">Project cache</span></span>
17798
18120
  <span class="codex-set-saved" data-set-saved hidden>\u2713 <span data-i18n="settingsSaved">Saved</span></span>
17799
18121
  </summary>
17800
18122
  <div class="codex-set-card">
17801
18123
  <label class="codex-project-settings-row codex-project-settings-row--switch">
17802
18124
  <span class="codex-project-settings-row-label">
17803
- <span data-i18n="experimentalOtMenuTitle">Experimental OT Mirror</span>
17804
- <small class="codex-set-row-sub" data-i18n="experimentalOtMenuSubtitle">Experimental \u2014 speeds up reading the file you are editing; Codex falls back to normal reading whenever unsure.</small>
18125
+ <span data-i18n="experimentalOtMenuTitle">OT Mirror</span>
18126
+ <small class="codex-set-row-sub" data-i18n="experimentalOtMenuSubtitle">Warms recent edits to the active text file locally. File scope and freshness are checked before reuse.</small>
17805
18127
  </span>
17806
18128
  <input type="checkbox" class="codex-switch" data-experimental-ot>
17807
18129
  </label>
17808
- <p class="codex-set-row-help" data-experimental-ot-menu-status></p>
18130
+ <p class="codex-set-row-help" data-experimental-ot-menu-status aria-live="polite"></p>
17809
18131
  </div>
17810
18132
  </details>
17811
18133
  </div>
@@ -17858,17 +18180,25 @@ ${block}`;
17858
18180
  <button type="button" class="codex-set-btn" data-provider-settings-open data-i18n="providerSettingsConfigure">Configure</button>
17859
18181
  </div>
17860
18182
  </details>
18183
+ <details class="codex-set-group" data-set-group="context-loading" open>
18184
+ <summary class="codex-set-group-head">
18185
+ <span class="codex-set-group-title">${codexSetIcon("database")}<span data-i18n="contextLoadingTitle">Context loading</span></span>
18186
+ <span class="codex-set-saved" data-set-saved hidden>\u2713 <span data-i18n="settingsSaved">Saved</span></span>
18187
+ </summary>
18188
+ <div class="codex-set-card">
18189
+ <label class="codex-project-settings-row codex-project-settings-row--switch">
18190
+ <span class="codex-project-settings-row-label" data-i18n="preloadProjectContextTitle">Preload project context</span>
18191
+ <input type="checkbox" class="codex-switch" data-preload-project-context>
18192
+ </label>
18193
+ <p class="codex-set-row-help" data-i18n="preloadProjectContextDescription">Fetch the exact project file list in the background after an Overleaf project becomes idle.</p>
18194
+ </div>
18195
+ </details>
17861
18196
  <details class="codex-set-group" data-set-group="updates" open>
17862
18197
  <summary class="codex-set-group-head">
17863
18198
  <span class="codex-set-group-title">${codexSetIcon("software")}<span data-i18n="softwareUpdatesTitle">Software updates</span></span>
17864
18199
  </summary>
17865
18200
  <div class="codex-set-card" data-update-settings>
17866
18201
  <div class="codex-set-row">
17867
- <label class="codex-project-settings-row codex-project-settings-row--switch">
17868
- <span class="codex-project-settings-row-label" data-i18n="preloadProjectContextTitle">Preload project context</span>
17869
- <input type="checkbox" class="codex-switch" data-preload-project-context>
17870
- </label>
17871
- <p class="codex-set-row-help" data-i18n="preloadProjectContextDescription">Fetch the exact project file list in the background after an Overleaf project becomes idle.</p>
17872
18202
  <p class="codex-set-row-help" data-update-settings-summary data-i18n="softwareUpdatesHelp">Automatically checks signed stable releases for the Extension and Native Host.</p>
17873
18203
  <p class="codex-set-note" data-update-settings-state aria-live="polite" hidden></p>
17874
18204
  <button type="button" class="codex-set-btn" data-check-updates data-i18n="checkForUpdates">Check for updates</button>
@@ -17933,6 +18263,9 @@ ${block}`;
17933
18263
  instance.callbacks.onHistoryOpen?.();
17934
18264
  }
17935
18265
  });
18266
+ container.querySelector("[data-storage-card]")?.addEventListener("toggle", (event) => {
18267
+ if (event.target.open) instance.callbacks.onStorageOpen?.();
18268
+ });
17936
18269
  container.querySelector("[data-history-filter]")?.addEventListener("input", () => instance.callbacks.onHistoryFilter?.());
17937
18270
  container.querySelector("[data-experimental-ot]")?.addEventListener("click", (event) => instance.callbacks.onOtToggleClick?.(event));
17938
18271
  const customInstructionsInput = container.querySelector("[data-custom-instructions-input]");
@@ -17943,7 +18276,12 @@ ${block}`;
17943
18276
  for (const selector of ["[data-governance-readonly-patterns]", "[data-governance-writable-patterns]", "[data-sensitive-check-enabled]", "[data-sensitive-confirm-allowed]", "[data-preload-project-context]", "[data-load-codex-local-skills]", "[data-load-codex-overleaf-skills]", "[data-theme-select]", "[data-language-select]"]) {
17944
18277
  const element = container.querySelector(selector);
17945
18278
  element?.addEventListener?.("change", (event) => {
17946
- Promise.resolve(instance.callbacks.onInputChange?.(event)).then(() => flashSaved(instance, event)).catch(() => {
18279
+ Promise.resolve(instance.callbacks.onInputChange?.(event)).then(() => {
18280
+ flashSaved(instance, event);
18281
+ if (selector === "[data-language-select]" && container.querySelector("[data-storage-card]")?.open) {
18282
+ instance.callbacks.onStorageOpen?.();
18283
+ }
18284
+ }).catch(() => {
17947
18285
  });
17948
18286
  });
17949
18287
  element?.addEventListener?.("input", (event) => {
@@ -17958,6 +18296,9 @@ ${block}`;
17958
18296
  }
17959
18297
  setupGroupPersistence(instance);
17960
18298
  return {
18299
+ get container() {
18300
+ return instance.container;
18301
+ },
17961
18302
  show: () => show(instance),
17962
18303
  hide: () => hide(instance),
17963
18304
  loadState: (state) => loadState(instance, state),
@@ -18218,7 +18559,7 @@ ${block}`;
18218
18559
  };
18219
18560
  })();
18220
18561
  }).call(globalThis, void 0, void 0, void 0);
18221
- (function runClassicContentModule57(module, exports, require2) {
18562
+ (function runClassicContentModule58(module, exports, require2) {
18222
18563
  (function initCodexOverleafProviderSettingsDialog() {
18223
18564
  "use strict";
18224
18565
  const CODEX_BLOSSOM_PATH = "M13.798 23.976a5.7 5.7 0 0 1-2.26-.456 6.1 6.1 0 0 1-1.903-1.27 5.7 5.7 0 0 1-1.88.311 5.75 5.75 0 0 1-2.95-.79 6.2 6.2 0 0 1-2.188-2.159q-.81-1.366-.809-3.045 0-.695.19-1.51a6.4 6.4 0 0 1-1.475-2.038A5.95 5.95 0 0 1 0 10.573Q0 9.278.547 8.08q.547-1.2 1.523-2.062a5.5 5.5 0 0 1 2.307-1.223A5.7 5.7 0 0 1 5.472 2.35 6.1 6.1 0 0 1 7.565.623 5.8 5.8 0 0 1 10.206 0q1.19 0 2.26.456a6.1 6.1 0 0 1 1.903 1.27 5.7 5.7 0 0 1 1.88-.311q1.594 0 2.95.79a6 6 0 0 1 2.165 2.159q.832 1.366.832 3.045 0 .695-.19 1.51a6.3 6.3 0 0 1 1.475 2.062q.523 1.15.523 2.422a5.9 5.9 0 0 1-.547 2.493q-.547 1.2-1.546 2.086a5.4 5.4 0 0 1-2.284 1.199 5.56 5.56 0 0 1-1.118 2.445 5.9 5.9 0 0 1-2.07 1.727 5.8 5.8 0 0 1-2.64.623m-5.876-2.997q1.19 0 2.07-.504l4.472-2.589a.53.53 0 0 0 .238-.455v-2.062L8.945 18.7a.96.96 0 0 1-1.047 0l-4.496-2.613a.7.7 0 0 1-.024.168v.287q0 1.224.571 2.254a4.24 4.24 0 0 0 1.642 1.583q1.047.6 2.331.599m.238-3.908a.6.6 0 0 0 .262.072q.118 0 .238-.072l1.784-1.031-5.734-3.357q-.522-.312-.523-.935V6.545a4.3 4.3 0 0 0-1.903 1.63 4.25 4.25 0 0 0-.714 2.398q0 1.176.595 2.254.594 1.08 1.546 1.63zm5.638 5.323q1.26 0 2.284-.576a4.3 4.3 0 0 0 1.618-1.582q.595-1.008.595-2.254v-5.179a.47.47 0 0 0-.238-.431l-1.808-1.055v6.689q0 .624-.524.935l-4.496 2.613a4.3 4.3 0 0 0 2.57.84m.904-8.776v-3.26l-2.688-1.535-2.712 1.535v3.26l2.712 1.535zM7.756 5.97q0-.623.523-.935l4.496-2.613a4.3 4.3 0 0 0-2.569-.84q-1.26 0-2.284.576A4.3 4.3 0 0 0 6.304 3.74q-.57 1.008-.57 2.254v5.155q0 .287.237.455l1.785 1.055zM19.84 17.43a4.16 4.16 0 0 0 1.88-1.63 4.33 4.33 0 0 0 .713-2.397q0-1.176-.595-2.254-.594-1.08-1.546-1.63l-4.449-2.59q-.143-.096-.261-.072a.46.46 0 0 0-.238.072L13.56 7.936l5.758 3.38a.9.9 0 0 1 .38.384q.143.216.143.528zM15.059 5.25q.524-.335 1.047 0l4.52 2.662V7.48q0-1.15-.57-2.181A4.14 4.14 0 0 0 18.46 3.62q-1.023-.623-2.379-.623-1.19 0-2.07.503L9.54 6.09a.53.53 0 0 0-.238.455v2.062z";
@@ -19152,7 +19493,7 @@ ${block}`;
19152
19493
  window.CodexOverleafProviderSettingsDialog = { create, getFooterActionState };
19153
19494
  })();
19154
19495
  }).call(globalThis, void 0, void 0, void 0);
19155
- (function runClassicContentModule58(module, exports, require2) {
19496
+ (function runClassicContentModule59(module, exports, require2) {
19156
19497
  (function initCodexOverleafProjectProviderSelection() {
19157
19498
  "use strict";
19158
19499
  function create(deps = {}) {
@@ -19209,7 +19550,7 @@ ${block}`;
19209
19550
  window.CodexOverleafProjectProviderSelection = { create };
19210
19551
  })();
19211
19552
  }).call(globalThis, void 0, void 0, void 0);
19212
- (function runClassicContentModule59(module, exports, require2) {
19553
+ (function runClassicContentModule60(module, exports, require2) {
19213
19554
  (function initCodexOverleafProviderSettingsCoordinator() {
19214
19555
  "use strict";
19215
19556
  function create(options2 = {}) {
@@ -19273,6 +19614,7 @@ ${block}`;
19273
19614
  return {
19274
19615
  open: () => open(instance),
19275
19616
  refreshSummary: () => refreshSummary(instance),
19617
+ renderSummary: () => updateSettingsSummary(instance),
19276
19618
  ensureLoaded: () => ensureLoaded(instance),
19277
19619
  getRunSelection: (providerId) => instance.loaded ? Profiles.buildRunSelection(instance.catalog, providerId || instance.getSelectedProviderId()) : null,
19278
19620
  getProviderSnapshot: (providerId) => getProviderSnapshot(instance, providerId),
@@ -19819,7 +20161,7 @@ ${block}`;
19819
20161
  window.CodexOverleafProviderSettingsCoordinator = { create };
19820
20162
  })();
19821
20163
  }).call(globalThis, void 0, void 0, void 0);
19822
- (function runClassicContentModule60(module, exports, require2) {
20164
+ (function runClassicContentModule61(module, exports, require2) {
19823
20165
  (function initCodexOverleafSessionPersistence(root, factory) {
19824
20166
  if (typeof module === "object" && module.exports) {
19825
20167
  module.exports = factory(require2("../shared/storageMigration"));
@@ -19913,7 +20255,7 @@ ${block}`;
19913
20255
  };
19914
20256
  });
19915
20257
  }).call(globalThis, void 0, void 0, void 0);
19916
- (function runClassicContentModule61(module, exports, require2) {
20258
+ (function runClassicContentModule62(module, exports, require2) {
19917
20259
  (function(root, factory) {
19918
20260
  var api = factory();
19919
20261
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -20017,7 +20359,7 @@ ${block}`;
20017
20359
  return Object.freeze({ persistPanelState });
20018
20360
  });
20019
20361
  }).call(globalThis, void 0, void 0, void 0);
20020
- (function runClassicContentModule62(module, exports, require2) {
20362
+ (function runClassicContentModule63(module, exports, require2) {
20021
20363
  (function(root, factory) {
20022
20364
  var api = factory();
20023
20365
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -20062,7 +20404,7 @@ ${block}`;
20062
20404
  return Object.freeze({ create });
20063
20405
  });
20064
20406
  }).call(globalThis, void 0, void 0, void 0);
20065
- (function runClassicContentModule63(module, exports, require2) {
20407
+ (function runClassicContentModule64(module, exports, require2) {
20066
20408
  (function(root, factory) {
20067
20409
  if (typeof module === "object" && module.exports) {
20068
20410
  module.exports = factory(
@@ -20157,7 +20499,7 @@ ${block}`;
20157
20499
  });
20158
20500
  });
20159
20501
  }).call(globalThis, void 0, void 0, void 0);
20160
- (function runClassicContentModule64(module, exports, require2) {
20502
+ (function runClassicContentModule65(module, exports, require2) {
20161
20503
  (function initCodexOverleafDiagnosticsPanel() {
20162
20504
  "use strict";
20163
20505
  function create(options2 = {}) {
@@ -20169,7 +20511,8 @@ ${block}`;
20169
20511
  container,
20170
20512
  callbacks: options2.callbacks || {},
20171
20513
  i18n: options2.i18n || {},
20172
- dismissInstalled: false
20514
+ dismissInstalled: false,
20515
+ cleanup: []
20173
20516
  };
20174
20517
  container.innerHTML = `
20175
20518
  <div class="codex-diagnostics-wrap">
@@ -20196,7 +20539,7 @@ ${block}`;
20196
20539
  <small data-i18n="diagnosticsSnapshotSubtitle">Full project, assets, read source</small>
20197
20540
  </button>
20198
20541
  <button type="button" data-diagnostics-ot>
20199
- <span data-i18n="diagnosticsOtTitle">Check Experimental OT Mirror</span>
20542
+ <span data-i18n="diagnosticsOtTitle">Check OT Mirror</span>
20200
20543
  <small data-i18n="diagnosticsOtSubtitle">Status, fresh files, fallback</small>
20201
20544
  </button>
20202
20545
  <button type="button" data-diagnostics-export>
@@ -20253,6 +20596,11 @@ ${block}`;
20253
20596
  instance.callbacks.onBeforeOpen?.();
20254
20597
  }
20255
20598
  popover.hidden = !open;
20599
+ if (open) {
20600
+ openFloating(instance, popover, 300);
20601
+ } else {
20602
+ hideFloating(popover);
20603
+ }
20256
20604
  button.dataset.active = open ? "true" : "false";
20257
20605
  button.setAttribute("aria-expanded", open ? "true" : "false");
20258
20606
  }
@@ -20263,7 +20611,7 @@ ${block}`;
20263
20611
  if (!popover || !button) {
20264
20612
  return;
20265
20613
  }
20266
- popover.hidden = true;
20614
+ hideFloating(popover);
20267
20615
  button.dataset.active = "false";
20268
20616
  button.setAttribute("aria-expanded", "false");
20269
20617
  }
@@ -20271,7 +20619,7 @@ ${block}`;
20271
20619
  const instance = target?._instance || target;
20272
20620
  const result = instance?.container?.querySelector("[data-diagnostics-result]");
20273
20621
  if (result) {
20274
- result.hidden = true;
20622
+ hideFloating(result);
20275
20623
  }
20276
20624
  }
20277
20625
  function showLoading(target, title, subtitle) {
@@ -20327,6 +20675,65 @@ ${block}`;
20327
20675
  details.open = false;
20328
20676
  details.hidden = !technicalText;
20329
20677
  technical.textContent = technicalText;
20678
+ closeMenu(instance);
20679
+ openFloating(instance, root, 560);
20680
+ }
20681
+ function openFloating(instance, element, preferredWidth) {
20682
+ if (element.isConnected === false) return;
20683
+ element.hidden = false;
20684
+ if (typeof element.showPopover === "function") {
20685
+ element.setAttribute("popover", "manual");
20686
+ try {
20687
+ if (!element.matches(":popover-open")) element.showPopover();
20688
+ } catch (_error) {
20689
+ element.removeAttribute("popover");
20690
+ }
20691
+ }
20692
+ positionFloating(instance, element, preferredWidth);
20693
+ }
20694
+ function hideFloating(element) {
20695
+ if (!element) return;
20696
+ if (typeof element.hidePopover === "function" && element.getAttribute("popover") === "manual") {
20697
+ try {
20698
+ if (element.matches(":popover-open")) element.hidePopover();
20699
+ } catch (_error) {
20700
+ element.removeAttribute("popover");
20701
+ }
20702
+ }
20703
+ element.hidden = true;
20704
+ }
20705
+ function positionFloating(instance, element, preferredWidth) {
20706
+ if (!element || element.hidden) return;
20707
+ const button = instance.container.querySelector("[data-diagnostics-menu]");
20708
+ if (!button?.getBoundingClientRect || !element.getBoundingClientRect) return;
20709
+ if (button.getClientRects && !button.getClientRects().length) {
20710
+ closeMenu(instance);
20711
+ closeResult(instance);
20712
+ return;
20713
+ }
20714
+ const doc = instance.container.ownerDocument || document;
20715
+ const view = doc.defaultView || window;
20716
+ const viewport = view.visualViewport;
20717
+ const width = viewport?.width || view.innerWidth || doc.documentElement?.clientWidth;
20718
+ const height = viewport?.height || view.innerHeight || doc.documentElement?.clientHeight;
20719
+ if (!(width > 0 && height > 0)) return;
20720
+ const margin = 12;
20721
+ const gap = 8;
20722
+ const leftEdge = (viewport?.offsetLeft || 0) + margin;
20723
+ const topEdge = (viewport?.offsetTop || 0) + margin;
20724
+ const rightEdge = leftEdge + Math.max(0, width - margin * 2);
20725
+ const bottomEdge = topEdge + Math.max(0, height - margin * 2);
20726
+ element.style.width = `${Math.min(preferredWidth, Math.max(0, width - margin * 2))}px`;
20727
+ element.style.maxHeight = `${Math.min(640, Math.max(0, height - margin * 2))}px`;
20728
+ const anchor = button.getBoundingClientRect();
20729
+ const bounds = element.getBoundingClientRect();
20730
+ const below = Math.max(0, bottomEdge - anchor.bottom - gap);
20731
+ const above = Math.max(0, anchor.top - topEdge - gap);
20732
+ const desiredTop = bounds.height > below && above > below ? anchor.top - gap - bounds.height : anchor.bottom + gap;
20733
+ element.style.left = `${Math.max(leftEdge, Math.min(anchor.right - bounds.width, rightEdge - bounds.width))}px`;
20734
+ element.style.top = `${Math.max(topEdge, Math.min(desiredTop, bottomEdge - bounds.height))}px`;
20735
+ element.style.right = "auto";
20736
+ element.style.bottom = "auto";
20330
20737
  }
20331
20738
  function renderInstallCommand(instance, container, command) {
20332
20739
  const wrap = document.createElement("div");
@@ -20401,13 +20808,60 @@ ${block}`;
20401
20808
  return;
20402
20809
  }
20403
20810
  instance.dismissInstalled = true;
20404
- document.addEventListener("click", (event) => {
20811
+ const doc = instance.container.ownerDocument || document;
20812
+ const view = doc.defaultView || window;
20813
+ const listen = (target, type, handler, options2) => {
20814
+ if (!target?.addEventListener) return;
20815
+ target.addEventListener(type, handler, options2);
20816
+ instance.cleanup.push(() => target.removeEventListener(type, handler, options2));
20817
+ };
20818
+ listen(doc, "click", (event) => {
20405
20819
  const wrap = instance.container?.querySelector(".codex-diagnostics-wrap");
20406
20820
  if (!wrap || wrap.contains(event.target)) {
20407
20821
  return;
20408
20822
  }
20409
20823
  closeMenu(instance);
20410
20824
  }, true);
20825
+ listen(doc, "keydown", (event) => {
20826
+ if (event.key !== "Escape") return;
20827
+ const menu = instance.container.querySelector("[data-diagnostics-popover]");
20828
+ const result = instance.container.querySelector("[data-diagnostics-result]");
20829
+ if (menu && !menu.hidden) closeMenu(instance);
20830
+ else if (result && !result.hidden) closeResult(instance);
20831
+ else return;
20832
+ event.preventDefault();
20833
+ event.stopPropagation();
20834
+ instance.container.querySelector("[data-diagnostics-menu]")?.focus();
20835
+ }, true);
20836
+ let frame = null;
20837
+ const reposition = () => {
20838
+ frame = null;
20839
+ positionFloating(instance, instance.container.querySelector("[data-diagnostics-popover]"), 300);
20840
+ positionFloating(instance, instance.container.querySelector("[data-diagnostics-result]"), 560);
20841
+ };
20842
+ const schedule = () => {
20843
+ if (frame !== null) return;
20844
+ if (view.requestAnimationFrame) frame = view.requestAnimationFrame(reposition);
20845
+ else reposition();
20846
+ };
20847
+ listen(view, "resize", schedule);
20848
+ listen(doc, "scroll", schedule, true);
20849
+ listen(view.visualViewport, "resize", schedule);
20850
+ listen(view.visualViewport, "scroll", schedule);
20851
+ if (typeof view.ResizeObserver === "function") {
20852
+ const observer = new view.ResizeObserver(schedule);
20853
+ for (const node of [
20854
+ instance.container.closest("#codex-overleaf-panel") || instance.container,
20855
+ instance.container.querySelector("[data-diagnostics-popover]"),
20856
+ instance.container.querySelector("[data-diagnostics-result]")
20857
+ ]) {
20858
+ if (node) observer.observe(node);
20859
+ }
20860
+ instance.cleanup.push(() => observer.disconnect());
20861
+ }
20862
+ instance.cleanup.push(() => {
20863
+ if (frame !== null) view.cancelAnimationFrame?.(frame);
20864
+ });
20411
20865
  }
20412
20866
  const HEALTH_STATES = ["ok", "warn", "fail", "unknown"];
20413
20867
  function updateStatus(target, status = {}) {
@@ -20564,6 +21018,10 @@ ${block}`;
20564
21018
  return key;
20565
21019
  }
20566
21020
  function destroy(instance) {
21021
+ closeMenu(instance);
21022
+ closeResult(instance);
21023
+ for (const cleanup of instance.cleanup || []) cleanup();
21024
+ instance.cleanup = [];
20567
21025
  instance.container.textContent = "";
20568
21026
  }
20569
21027
  window.CodexOverleafDiagnosticsPanel = {
@@ -20585,7 +21043,7 @@ ${block}`;
20585
21043
  };
20586
21044
  })();
20587
21045
  }).call(globalThis, void 0, void 0, void 0);
20588
- (function runClassicContentModule65(module, exports, require2) {
21046
+ (function runClassicContentModule66(module, exports, require2) {
20589
21047
  (function initCodexOverleafRunQueueScheduler(root, factory) {
20590
21048
  if (typeof module === "object" && module.exports) {
20591
21049
  module.exports = factory(require2("../shared/runInputQueue"));
@@ -20913,7 +21371,7 @@ ${block}`;
20913
21371
  return { create, createCoordinator };
20914
21372
  });
20915
21373
  }).call(globalThis, void 0, void 0, void 0);
20916
- (function runClassicContentModule66(module, exports, require2) {
21374
+ (function runClassicContentModule67(module, exports, require2) {
20917
21375
  (function initCodexOverleafActiveTurnControl(root, factory) {
20918
21376
  if (typeof module === "object" && module.exports) {
20919
21377
  module.exports = factory();
@@ -21031,6 +21489,7 @@ ${block}`;
21031
21489
  const raw = entry?.event || {};
21032
21490
  if (raw.type === "codex.turn.bound") {
21033
21491
  session.codexThreadId = String(raw.detail?.threadId || session.codexThreadId || "");
21492
+ record.codexThreadId = String(raw.detail?.threadId || record.codexThreadId || "");
21034
21493
  record.codexTurnId = String(raw.detail?.turnId || record.codexTurnId || "");
21035
21494
  record.nativeEventSeq = sequence;
21036
21495
  continue;
@@ -21082,7 +21541,7 @@ ${block}`;
21082
21541
  return { create };
21083
21542
  });
21084
21543
  }).call(globalThis, void 0, void 0, void 0);
21085
- (function runClassicContentModule67(module, exports, require2) {
21544
+ (function runClassicContentModule68(module, exports, require2) {
21086
21545
  (function initCodexOverleafPendingInputView(root, factory) {
21087
21546
  if (typeof module === "object" && module.exports) {
21088
21547
  module.exports = factory();
@@ -21184,7 +21643,7 @@ ${block}`;
21184
21643
  return { create };
21185
21644
  });
21186
21645
  }).call(globalThis, void 0, void 0, void 0);
21187
- (function runClassicContentModule68(module, exports, require2) {
21646
+ (function runClassicContentModule69(module, exports, require2) {
21188
21647
  (function initCodexOverleafComposerPanel() {
21189
21648
  "use strict";
21190
21649
  function create(options2 = {}) {
@@ -21443,7 +21902,7 @@ ${block}`;
21443
21902
  };
21444
21903
  })();
21445
21904
  }).call(globalThis, void 0, void 0, void 0);
21446
- (function runClassicContentModule69(module, exports, require2) {
21905
+ (function runClassicContentModule70(module, exports, require2) {
21447
21906
  (function initCodexOverleafTheme(root, factory) {
21448
21907
  if (typeof module === "object" && module.exports) {
21449
21908
  module.exports = factory();
@@ -21514,7 +21973,7 @@ ${block}`;
21514
21973
  };
21515
21974
  });
21516
21975
  }).call(globalThis, void 0, void 0, void 0);
21517
- (function runClassicContentModule70(module, exports, require2) {
21976
+ (function runClassicContentModule71(module, exports, require2) {
21518
21977
  !(function(e, t) {
21519
21978
  "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.katex = t() : e.katex = t();
21520
21979
  })("undefined" != typeof self ? self : this, function() {
@@ -24934,7 +25393,7 @@ ${block}`;
24934
25393
  })();
24935
25394
  });
24936
25395
  }).call(globalThis, void 0, void 0, void 0);
24937
- (function runClassicContentModule71(module, exports, require2) {
25396
+ (function runClassicContentModule72(module, exports, require2) {
24938
25397
  !(function(t, e) {
24939
25398
  "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define([], e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).markdownit = e();
24940
25399
  })(this, function() {
@@ -27006,7 +27465,7 @@ ${block}`;
27006
27465
  }, Ke;
27007
27466
  });
27008
27467
  }).call(globalThis, void 0, void 0, void 0);
27009
- (function runClassicContentModule72(module, exports, require2) {
27468
+ (function runClassicContentModule73(module, exports, require2) {
27010
27469
  (function initCodexOverleafMathText(root) {
27011
27470
  "use strict";
27012
27471
  const DELIMITERS = Object.freeze([
@@ -27391,7 +27850,7 @@ ${block}`;
27391
27850
  }
27392
27851
  })(typeof window !== "undefined" ? window : globalThis);
27393
27852
  }).call(globalThis, void 0, void 0, void 0);
27394
- (function runClassicContentModule73(module, exports, require2) {
27853
+ (function runClassicContentModule74(module, exports, require2) {
27395
27854
  (function initCodexOverleafMarkdownDomRenderer(root, factory) {
27396
27855
  const api = factory();
27397
27856
  if (typeof module === "object" && module.exports) {
@@ -27879,7 +28338,7 @@ ${block}`;
27879
28338
  };
27880
28339
  });
27881
28340
  }).call(globalThis, void 0, void 0, void 0);
27882
- (function runClassicContentModule74(module, exports, require2) {
28341
+ (function runClassicContentModule75(module, exports, require2) {
27883
28342
  (function initCodexOverleafMarkdownText() {
27884
28343
  "use strict";
27885
28344
  function create(deps = {}) {
@@ -28506,7 +28965,7 @@ ${block}`;
28506
28965
  window.CodexOverleafMarkdownText = { create };
28507
28966
  })();
28508
28967
  }).call(globalThis, void 0, void 0, void 0);
28509
- (function runClassicContentModule75(module, exports, require2) {
28968
+ (function runClassicContentModule76(module, exports, require2) {
28510
28969
  (function initCodexOverleafDiagnosticsController() {
28511
28970
  "use strict";
28512
28971
  function create(deps = {}) {
@@ -28823,6 +29282,16 @@ ${block}`;
28823
29282
  nextStep: "",
28824
29283
  technical: ""
28825
29284
  };
29285
+ const mirrorState = getOtWarmMirrorState?.() || {};
29286
+ if (mirrorState.failureActive && mirrorState.lastFailure) {
29287
+ const failure = mirrorState.lastFailure;
29288
+ disabledResult.status = "warning";
29289
+ disabledResult.summary = tr(otWarmMirrorController.getFailureMessageKey(failure.code));
29290
+ disabledResult.nextStep = tr("otFailureNextStep");
29291
+ disabledResult.technical = `lastErrorCode: ${failure.code}
29292
+ phase: ${failure.phase}
29293
+ failedAt: ${new Date(failure.at).toISOString()}`;
29294
+ }
28826
29295
  if (options2.collectOnly) {
28827
29296
  return disabledResult;
28828
29297
  }
@@ -28877,6 +29346,7 @@ ${block}`;
28877
29346
  const queuedEventCount = normalizeOtDiagnosticsCount(otStatus?.queuedEventCount);
28878
29347
  const lastEventAt = formatOtDiagnosticValue(otStatus?.lastEventAt, tr("noneValue"));
28879
29348
  const warmMirrorState = getOtWarmMirrorState() || {};
29349
+ const failureActive = warmMirrorState.failureActive === true;
28880
29350
  const lastOtPatchAt = formatOtDiagnosticValue(mirrorStatus?.lastOtPatchAt || warmMirrorState.lastPatchAt, tr("noneValue"));
28881
29351
  const lastOtErrorCode = formatOtDiagnosticValue(mirrorStatus?.lastOtErrorCode || warmMirrorState.lastErrorCode, tr("noneValue"));
28882
29352
  const lastErrorCode = formatOtDiagnosticValue(otStatus?.lastErrorCode || otStatus?.reason || otStatus?.error, tr("noneValue"));
@@ -28888,12 +29358,12 @@ ${block}`;
28888
29358
  const bridgeFailed = otStatus?.ok === false;
28889
29359
  const focusFiles = getActiveFocusFiles();
28890
29360
  const otWarmStart = otWarmMirrorController?.canUseOtWarmStart?.({ enabled, focusFiles, mirrorStatus }) || { ok: false };
28891
- const fallback = !enabled || statusValue !== "observing" || bridgeFailed || otWarmStart?.ok !== true;
29361
+ const fallback = !enabled || failureActive || statusValue !== "observing" || bridgeFailed || otWarmStart?.ok !== true;
28892
29362
  return {
28893
29363
  title: tr("diagnosticsOtTitle"),
28894
29364
  subtitle: tr("diagnosticsOtSubtitle"),
28895
29365
  status: enabled && !fallback && !bridgeFailed ? "completed" : enabled || bridgeFailed ? "warning" : "completed",
28896
- summary: tr(enabled ? "diagnosticsOtSummaryEnabled" : "diagnosticsOtSummaryDisabled"),
29366
+ summary: tr(failureActive ? otWarmMirrorController.getFailureMessageKey(warmMirrorState.lastErrorCode) : enabled ? "diagnosticsOtSummaryEnabled" : "diagnosticsOtSummaryDisabled"),
28897
29367
  bullets: [
28898
29368
  `${tr("otStatus")}: ${formatOtStatusLabel(statusValue)}`,
28899
29369
  `${tr("otFreshFiles")}: ${otFreshFileCount}`,
@@ -29052,6 +29522,8 @@ ${block}`;
29052
29522
  lines.push(`CodeMirror: docLength=${view.docLength || 0}; dispatch=${Boolean(view.hasDispatch)}; source=${view.source || "unknown"}`);
29053
29523
  }
29054
29524
  }
29525
+ const capture = probe?.trackedChangeCapture;
29526
+ if (capture) lines.push(tx("Tracked ledger: ", "\u7559\u75D5\u8BB0\u5F55\uFF1A") + "source=" + capture.source + "; ready=" + capture.ready + "; count=" + capture.count + "; doc=" + (capture.docId || "none") + "; adapter=" + capture.adapter + "; reason=" + (capture.reason || "none"));
29055
29527
  const projectDiagnostics = probe?.projectDiagnostics;
29056
29528
  if (projectDiagnostics) {
29057
29529
  lines.push(`Project records: ${projectDiagnostics.docRecordCount || 0}; roots=${(projectDiagnostics.internalRootKeys || []).slice(0, 6).join(", ") || "none"}`);
@@ -29075,7 +29547,7 @@ ${block}`;
29075
29547
  window.CodexOverleafDiagnosticsController = { create };
29076
29548
  })();
29077
29549
  }).call(globalThis, void 0, void 0, void 0);
29078
- (function runClassicContentModule76(module, exports, require2) {
29550
+ (function runClassicContentModule77(module, exports, require2) {
29079
29551
  (function initCodexOverleafRunGuidanceView() {
29080
29552
  "use strict";
29081
29553
  const LEGACY_GUIDANCE_TITLES = /* @__PURE__ */ new Set([
@@ -29115,7 +29587,7 @@ ${block}`;
29115
29587
  };
29116
29588
  })();
29117
29589
  }).call(globalThis, void 0, void 0, void 0);
29118
- (function runClassicContentModule77(module, exports, require2) {
29590
+ (function runClassicContentModule78(module, exports, require2) {
29119
29591
  (function initCodexOverleafRunGuidanceController() {
29120
29592
  "use strict";
29121
29593
  function create(deps = {}) {
@@ -29178,7 +29650,7 @@ ${block}`;
29178
29650
  window.CodexOverleafRunGuidanceController = { create };
29179
29651
  })();
29180
29652
  }).call(globalThis, void 0, void 0, void 0);
29181
- (function runClassicContentModule78(module, exports, require2) {
29653
+ (function runClassicContentModule79(module, exports, require2) {
29182
29654
  (function initCodexOverleafRunResultActions(root, factory) {
29183
29655
  const api = factory();
29184
29656
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -29278,18 +29750,239 @@ ${block}`;
29278
29750
  textarea.remove();
29279
29751
  if (!copied) throw new Error("clipboard_copy_failed");
29280
29752
  }
29281
- return Object.freeze({ configureResultActions });
29753
+ return Object.freeze({ configureResultActions, projectUndoAvailability });
29754
+ }
29755
+ function projectUndoAvailability(run, projectRunSettlement) {
29756
+ const payload = run.recoveryPayload && typeof run.recoveryPayload === "object" ? run.recoveryPayload : run;
29757
+ if (!Array.isArray(payload.appliedOperations)) return projectRunSettlement(run);
29758
+ const appliedOperations = payload.appliedOperations.filter((operation) => operation?.type !== "binary-create" && operation?.type !== "overwrite-binary");
29759
+ if (appliedOperations.length === payload.appliedOperations.length) return projectRunSettlement(run);
29760
+ return projectRunSettlement({
29761
+ ...run,
29762
+ recoveryPayload: { ...payload, appliedOperations }
29763
+ });
29764
+ }
29765
+ return Object.freeze({ create });
29766
+ });
29767
+ }).call(globalThis, void 0, void 0, void 0);
29768
+ (function runClassicContentModule80(module, exports, require2) {
29769
+ (function initRunScrollLayout(root, factory) {
29770
+ if (typeof module === "object" && module.exports) {
29771
+ module.exports = factory();
29772
+ } else {
29773
+ root.CodexOverleafModuleRegistry.define("RunScrollLayout", [], factory);
29774
+ }
29775
+ })(typeof window !== "undefined" ? window : globalThis, function runScrollLayoutFactory() {
29776
+ "use strict";
29777
+ const READING_ANCHORS = ".run-final-answer p, .run-final-answer li, .run-final-answer pre, .run-final-answer h1, .run-final-answer h2, .run-final-answer h3, .run-stream p, .run-stream li, .run-stream pre, [data-run-task], [data-run-status]";
29778
+ function create({ getScroller, onLayoutChange, isFollowing = () => true, window: host = globalThis } = {}) {
29779
+ let current = null;
29780
+ let resizeObserver = null;
29781
+ let mutationObserver = null;
29782
+ let anchor = null;
29783
+ let extent = "";
29784
+ function measure() {
29785
+ return current ? `${current.scrollHeight}:${current.clientHeight}` : "";
29786
+ }
29787
+ function captureAnchor(force = false) {
29788
+ if (!current || isFollowing()) {
29789
+ anchor = null;
29790
+ return;
29791
+ }
29792
+ if (!force && extent !== measure()) return;
29793
+ const bounds = current.getBoundingClientRect?.();
29794
+ if (!bounds) return;
29795
+ const nodes = current.querySelectorAll?.(READING_ANCHORS) || [];
29796
+ anchor = null;
29797
+ for (const node of nodes) {
29798
+ const rect = node.getBoundingClientRect();
29799
+ if (rect.height > 0 && rect.bottom > bounds.top && rect.top < bounds.bottom) {
29800
+ anchor = { node, offset: rect.top - bounds.top };
29801
+ break;
29802
+ }
29803
+ }
29804
+ }
29805
+ function restoreAnchor() {
29806
+ if (isFollowing() || !anchor || !current.contains?.(anchor.node)) return;
29807
+ const rect = anchor.node.getBoundingClientRect();
29808
+ if (!rect.height) return;
29809
+ const delta = rect.top - current.getBoundingClientRect().top - anchor.offset;
29810
+ if (Math.abs(delta) > 0.5) current.scrollTop += delta;
29811
+ }
29812
+ function snapshot() {
29813
+ captureAnchor();
29814
+ const run = anchor?.node.closest?.("[data-run-id]");
29815
+ return {
29816
+ scrollTop: current?.scrollTop || 0,
29817
+ runId: run?.dataset?.runId || "",
29818
+ index: run ? Array.from(run.querySelectorAll(READING_ANCHORS)).indexOf(anchor.node) : -1,
29819
+ offset: anchor?.offset || 0
29820
+ };
29821
+ }
29822
+ function restore(reading) {
29823
+ if (!current || !reading) return;
29824
+ current.scrollTop = reading.scrollTop;
29825
+ const run = Array.from(current.querySelectorAll?.("[data-run-id]") || []).find((node2) => node2.dataset.runId === reading.runId);
29826
+ const node = run?.querySelectorAll(READING_ANCHORS)[reading.index];
29827
+ anchor = node ? { node, offset: reading.offset } : null;
29828
+ restoreAnchor();
29829
+ extent = measure();
29830
+ captureAnchor(true);
29831
+ }
29832
+ function dispose() {
29833
+ resizeObserver?.disconnect();
29834
+ mutationObserver?.disconnect();
29835
+ resizeObserver = null;
29836
+ mutationObserver = null;
29837
+ current = null;
29838
+ anchor = null;
29839
+ extent = "";
29840
+ }
29841
+ function notify() {
29842
+ if (!current) return;
29843
+ const active = getScroller();
29844
+ if (active !== current) {
29845
+ bind(active);
29846
+ return;
29847
+ }
29848
+ restoreAnchor();
29849
+ extent = measure();
29850
+ onLayoutChange(current);
29851
+ captureAnchor(true);
29852
+ }
29853
+ function bind(scroller) {
29854
+ if (scroller === current) return;
29855
+ dispose();
29856
+ if (!scroller || typeof host.ResizeObserver !== "function") return;
29857
+ current = scroller;
29858
+ extent = measure();
29859
+ captureAnchor(true);
29860
+ resizeObserver = new host.ResizeObserver(notify);
29861
+ resizeObserver.observe(scroller);
29862
+ for (const child of scroller.children || []) resizeObserver.observe(child);
29863
+ if (typeof host.MutationObserver === "function") {
29864
+ mutationObserver = new host.MutationObserver((records) => {
29865
+ if (current !== scroller) return;
29866
+ for (const record of records) {
29867
+ for (const node of record.removedNodes) {
29868
+ if (node.nodeType === 1) resizeObserver.unobserve(node);
29869
+ }
29870
+ for (const node of record.addedNodes) {
29871
+ if (node.nodeType === 1) resizeObserver.observe(node);
29872
+ }
29873
+ }
29874
+ notify();
29875
+ });
29876
+ mutationObserver.observe(scroller, { childList: true });
29877
+ }
29878
+ }
29879
+ return Object.freeze({ bind, dispose, captureAnchor, snapshot, restore });
29282
29880
  }
29283
29881
  return Object.freeze({ create });
29284
29882
  });
29285
29883
  }).call(globalThis, void 0, void 0, void 0);
29286
- (function runClassicContentModule79(module, exports, require2) {
29884
+ (function runClassicContentModule81(module, exports, require2) {
29885
+ (function initRunFailureNotice(root, factory) {
29886
+ if (typeof module === "object" && module.exports) {
29887
+ module.exports = factory();
29888
+ } else {
29889
+ root.CodexOverleafModuleRegistry.define("RunFailureNotice", [], factory);
29890
+ }
29891
+ })(typeof window !== "undefined" ? window : globalThis, function runFailureNoticeFactory() {
29892
+ "use strict";
29893
+ const REVIEW_CODES = /* @__PURE__ */ new Set(["write_timeout", "partial_write_needs_review"]);
29894
+ const CANCEL_CODES = /* @__PURE__ */ new Set(["cancelled", "user_cancelled", "codex_cancelled"]);
29895
+ function create({
29896
+ tr,
29897
+ projectRunSettlement,
29898
+ sanitizeText = (value) => value,
29899
+ document: documentRef = globalThis.document
29900
+ } = {}) {
29901
+ const notices = /* @__PURE__ */ new WeakMap();
29902
+ function append(report, event = {}, run = {}) {
29903
+ const projection = typeof projectRunSettlement === "function" && run ? projectRunSettlement(run) : null;
29904
+ const failure = projection?.primaryFailure || event.failure;
29905
+ if (report.dataset.status === "cancelled" || CANCEL_CODES.has(failure?.code)) return;
29906
+ const facts = run?.settlement || run?.settlementFacts || {};
29907
+ const unconfirmed = REVIEW_CODES.has(failure?.code) || facts.documentEffect === "possibly-changed" || facts.evidence?.applied === "unknown" && facts.evidence?.settled === "needs-review";
29908
+ const partial = facts.evidence?.applied === "partial" || run?.partialWriteback === true;
29909
+ if (!failure && !unconfirmed && !partial && report.dataset.status !== "failed") return;
29910
+ const notice = documentRef.createElement("aside");
29911
+ notice.className = "run-feedback";
29912
+ notice.dataset.tone = unconfirmed || partial || failure?.terminalState === "degraded" || failure?.terminalState === "needs_review" ? "warning" : "error";
29913
+ notice.setAttribute("role", "note");
29914
+ const heading = documentRef.createElement("strong");
29915
+ heading.className = "run-feedback__title";
29916
+ heading.textContent = tr(unconfirmed ? "runFeedbackUnconfirmedTitle" : partial ? "runFeedbackPartialTitle" : "runFeedbackFailedTitle");
29917
+ const message = documentRef.createElement("p");
29918
+ const detail = typeof failure?.userMessage === "string" ? failure.userMessage : "";
29919
+ const file = safeProjectPath(failure?.file);
29920
+ message.textContent = sanitizeText([file, detail].filter(Boolean).join(": ").slice(0, 1e3)) || tr("runFeedbackGeneric");
29921
+ notice.append(heading, message);
29922
+ if (unconfirmed) {
29923
+ const warning = documentRef.createElement("p");
29924
+ warning.textContent = tr("runFeedbackUnconfirmed");
29925
+ notice.append(warning);
29926
+ }
29927
+ notices.set(report, { notice, unconfirmed, failure, file });
29928
+ report.append(notice);
29929
+ }
29930
+ function formatMetaValue(report, row) {
29931
+ if (!notices.get(report)?.unconfirmed) return row.value;
29932
+ const key = String(row.key || "").toLowerCase().replace(/[^a-z]/g, "");
29933
+ const label = String(row.label || "").trim().toLowerCase();
29934
+ if (key === "writeresult" || key === "next" || ["write result", "next", "\u5199\u5165\u7ED3\u679C", "\u4E0B\u4E00\u6B65"].includes(label)) {
29935
+ return tr("runFeedbackUnconfirmedResult");
29936
+ }
29937
+ return row.value;
29938
+ }
29939
+ function prepareRecovery(report, { openFile } = {}) {
29940
+ const info = notices.get(report);
29941
+ const target = info?.notice || report;
29942
+ if (!info?.unconfirmed) return { target, handled: false };
29943
+ const canOpen = Boolean(info.file && typeof openFile === "function");
29944
+ const button = documentRef.createElement("button");
29945
+ button.type = "button";
29946
+ button.className = "run-final-answer__recovery-action";
29947
+ button.dataset.recoveryFor = info.failure?.code || "write_unconfirmed";
29948
+ button.textContent = tr(canOpen ? "runFeedbackReviewFile" : "runFeedbackReviewDetails", { file: info.file });
29949
+ button.addEventListener("click", (event) => {
29950
+ event.stopPropagation();
29951
+ if (canOpen) {
29952
+ openFile(info.file);
29953
+ return;
29954
+ }
29955
+ const process = report.closest?.("[data-run-id]")?.querySelector?.("[data-run-process]");
29956
+ if (process) {
29957
+ process.open = true;
29958
+ process.scrollIntoView?.({ block: "nearest" });
29959
+ } else {
29960
+ button.disabled = true;
29961
+ button.title = tr("runFeedbackDetailsUnavailable");
29962
+ }
29963
+ });
29964
+ target.append(button);
29965
+ return { target, handled: true };
29966
+ }
29967
+ return Object.freeze({ append, formatMetaValue, prepareRecovery });
29968
+ }
29969
+ function safeProjectPath(value) {
29970
+ if (typeof value !== "string") return "";
29971
+ const path = value.replace(/\\/g, "/");
29972
+ return path && !path.startsWith("/") && !/^[A-Za-z]:/.test(path) && !path.split("/").includes("..") ? path.slice(0, 300) : "";
29973
+ }
29974
+ return Object.freeze({ create });
29975
+ });
29976
+ }).call(globalThis, void 0, void 0, void 0);
29977
+ (function runClassicContentModule82(module, exports, require2) {
29287
29978
  (function initCodexOverleafRunTimelineView() {
29288
29979
  "use strict";
29289
29980
  function create(deps = {}) {
29290
29981
  const {
29291
29982
  RunGuidanceView,
29292
29983
  RunResultActions,
29984
+ RunScrollLayout,
29985
+ RunFailureNotice,
29293
29986
  tr,
29294
29987
  tx,
29295
29988
  getLocale,
@@ -29323,17 +30016,25 @@ ${block}`;
29323
30016
  } = deps;
29324
30017
  let logAutoFollow = true;
29325
30018
  let userScrollIntentUntil = 0;
30019
+ let userScrollExtent = "";
29326
30020
  let scrollLogRafId = 0;
29327
30021
  let scrollLogPendingForce = false;
29328
30022
  let jumpToLatestButton = null;
29329
30023
  let unreadSinceDetach = 0;
29330
30024
  let runElapsedTimer = null;
30025
+ const scrollLayout = RunScrollLayout?.create({
30026
+ getScroller: getLogScrollContainer,
30027
+ isFollowing: () => logAutoFollow,
30028
+ onLayoutChange: (scroller) => logAutoFollow ? scrollLogToBottom() : updateJumpToLatestButton(scroller)
30029
+ });
30030
+ const failureNotice = RunFailureNotice?.create({ tr, projectRunSettlement, sanitizeText: sanitizeAssistantVisibleText });
29331
30031
  function resetAutoFollow() {
29332
30032
  logAutoFollow = true;
29333
30033
  userScrollIntentUntil = 0;
29334
30034
  }
29335
30035
  function bindLogAutoFollow() {
29336
30036
  const scroller = getLogScrollContainer();
30037
+ scrollLayout?.bind(scroller);
29337
30038
  if (!scroller || scroller.dataset.autoFollowBound === "true") {
29338
30039
  return;
29339
30040
  }
@@ -29343,15 +30044,14 @@ ${block}`;
29343
30044
  scroller.addEventListener("pointerdown", markUserScrollIntent, { passive: true });
29344
30045
  scroller.addEventListener("keydown", (event) => {
29345
30046
  if (["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "].includes(event.key)) {
29346
- markUserScrollIntent();
30047
+ markUserScrollIntent(event);
29347
30048
  }
29348
30049
  });
29349
30050
  scroller.addEventListener("scroll", () => {
29350
- if (Date.now() <= userScrollIntentUntil) {
30051
+ if (Date.now() <= userScrollIntentUntil && userScrollExtent === `${scroller.scrollHeight}:${scroller.clientHeight}`) {
29351
30052
  logAutoFollow = isLogNearBottom(scroller);
29352
- } else if (isLogNearBottom(scroller)) {
29353
- logAutoFollow = true;
29354
30053
  }
30054
+ scrollLayout?.captureAnchor();
29355
30055
  updateJumpToLatestButton(scroller);
29356
30056
  }, { passive: true });
29357
30057
  }
@@ -29382,6 +30082,7 @@ ${block}`;
29382
30082
  if (!el) {
29383
30083
  return;
29384
30084
  }
30085
+ if (el.dataset) el.dataset.logAutoFollow = logAutoFollow ? "true" : "false";
29385
30086
  const button = ensureJumpToLatestButton();
29386
30087
  if (!button) {
29387
30088
  return;
@@ -29404,8 +30105,16 @@ ${block}`;
29404
30105
  function getLogScrollContainer() {
29405
30106
  return getPanel()?.querySelector("[data-log]") || getPanel()?.querySelector("[data-main]");
29406
30107
  }
29407
- function markUserScrollIntent() {
30108
+ function markUserScrollIntent(event) {
30109
+ const scroller = getLogScrollContainer();
30110
+ if (!scroller || event?.type === "pointerdown" && event.target !== scroller) return;
30111
+ if (event?.target?.closest?.('input, textarea, select, [contenteditable="true"]')) return;
29408
30112
  userScrollIntentUntil = Date.now() + 1200;
30113
+ userScrollExtent = `${scroller.scrollHeight}:${scroller.clientHeight}`;
30114
+ if (event?.type === "wheel" && event.deltaY < 0 || ["ArrowUp", "PageUp", "Home"].includes(event?.key)) {
30115
+ logAutoFollow = false;
30116
+ scrollLogPendingForce = false;
30117
+ }
29409
30118
  }
29410
30119
  function isLogNearBottom(log) {
29411
30120
  if (!log) {
@@ -29424,7 +30133,7 @@ ${block}`;
29424
30133
  userScrollIntentUntil = 0;
29425
30134
  unreadSinceDetach = 0;
29426
30135
  scrollLogPendingForce = true;
29427
- } else if (!(logAutoFollow || isLogNearBottom(scroller))) {
30136
+ } else if (!logAutoFollow) {
29428
30137
  updateJumpToLatestButton(scroller);
29429
30138
  return;
29430
30139
  }
@@ -29433,7 +30142,7 @@ ${block}`;
29433
30142
  if (!el) {
29434
30143
  return;
29435
30144
  }
29436
- if (scrollLogPendingForce || logAutoFollow || isLogNearBottom(el)) {
30145
+ if (scrollLogPendingForce || logAutoFollow) {
29437
30146
  setLogScrollPosition(el);
29438
30147
  }
29439
30148
  scrollLogPendingForce = false;
@@ -29509,11 +30218,12 @@ ${block}`;
29509
30218
  }
29510
30219
  return tr("processed", { elapsed });
29511
30220
  }
29512
- function renderRunHistory() {
30221
+ function renderRunHistory(options2 = {}) {
29513
30222
  const log = getPanel()?.querySelector("[data-log]");
29514
30223
  if (!log) {
29515
30224
  return;
29516
30225
  }
30226
+ const reading = options2.preserveScroll === true && !logAutoFollow ? scrollLayout?.snapshot() || { scrollTop: log.scrollTop } : null;
29517
30227
  log.replaceChildren();
29518
30228
  if (!getState().runs?.length) {
29519
30229
  const empty = document.createElement("div");
@@ -29542,7 +30252,11 @@ ${block}`;
29542
30252
  for (const run of runs) {
29543
30253
  log.append(renderRunCard(run));
29544
30254
  }
29545
- scrollLogToBottom({ force: true });
30255
+ if (reading) {
30256
+ log.scrollTop = reading.scrollTop;
30257
+ scrollLayout?.restore(reading);
30258
+ updateJumpToLatestButton(log);
30259
+ } else scrollLogToBottom({ force: true });
29546
30260
  }
29547
30261
  function renderRunCard(run) {
29548
30262
  const root = document.createElement("section");
@@ -29817,7 +30531,7 @@ ${block}`;
29817
30531
  if (row.key) dt.dataset.metaKey = row.key;
29818
30532
  const dd = document.createElement("dd");
29819
30533
  dd.className = "run-final-answer__meta-value";
29820
- dd.textContent = row.value;
30534
+ dd.textContent = failureNotice?.formatMetaValue(report, row) ?? row.value;
29821
30535
  metaBlock.append(dt, dd);
29822
30536
  }
29823
30537
  if (metaBlock.children.length) {
@@ -29890,6 +30604,7 @@ ${lines}`
29890
30604
  decorateCompletionReport(main, { tx });
29891
30605
  report.append(main);
29892
30606
  }
30607
+ failureNotice?.append(report, event, run);
29893
30608
  appendCompletionMetaBlock(report, structured.meta);
29894
30609
  appendCompileFix(report);
29895
30610
  appendRejectedRedo(report);
@@ -29905,6 +30620,7 @@ ${lines}`
29905
30620
  report.append(body);
29906
30621
  appendCompileFix(report);
29907
30622
  appendRejectedRedo(report);
30623
+ failureNotice?.append(report, event, run);
29908
30624
  appendCompletionMetaBlock(report, split.meta);
29909
30625
  appendRecoveryActionForFailure(report, event, run);
29910
30626
  return report;
@@ -29958,6 +30674,9 @@ ${conclusion}` : `${label} ${conclusion}`;
29958
30674
  return button;
29959
30675
  }
29960
30676
  function appendRecoveryActionForFailure(report, event, run) {
30677
+ const recovery = failureNotice?.prepareRecovery(report, { openFile: openProjectFileForFailure });
30678
+ if (recovery?.handled) return;
30679
+ report = recovery?.target || report;
29961
30680
  const failureCode = event?.failure?.code || "";
29962
30681
  if (RETRYABLE_FAILURE_CODES.has(failureCode) && typeof refillComposerForRetry === "function") {
29963
30682
  const retry = buildRecoveryButton(
@@ -30073,7 +30792,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30073
30792
  configureLifecycleUndoButton(button, run);
30074
30793
  return;
30075
30794
  }
30076
- const projection = projectRunSettlement(run);
30795
+ const projection = RunResultActions.projectUndoAvailability(run, projectRunSettlement);
30077
30796
  if (!projection.canUndo && run.undoStatus !== "applied") {
30078
30797
  button.hidden = true;
30079
30798
  return;
@@ -30255,7 +30974,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30255
30974
  window.CodexOverleafRunTimelineView = { create };
30256
30975
  })();
30257
30976
  }).call(globalThis, void 0, void 0, void 0);
30258
- (function runClassicContentModule80(module, exports, require2) {
30977
+ (function runClassicContentModule83(module, exports, require2) {
30259
30978
  (function initCodexOverleafProjectSettingsCoordinator() {
30260
30979
  "use strict";
30261
30980
  function create(deps = {}) {
@@ -30584,7 +31303,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30584
31303
  window.CodexOverleafProjectSettingsCoordinator = { create };
30585
31304
  })();
30586
31305
  }).call(globalThis, void 0, void 0, void 0);
30587
- (function runClassicContentModule81(module, exports, require2) {
31306
+ (function runClassicContentModule84(module, exports, require2) {
30588
31307
  (function initCodexOverleafSessionMenuView() {
30589
31308
  "use strict";
30590
31309
  function create(deps = {}) {
@@ -30789,7 +31508,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30789
31508
  window.CodexOverleafSessionMenuView = { create };
30790
31509
  })();
30791
31510
  }).call(globalThis, void 0, void 0, void 0);
30792
- (function runClassicContentModule82(module, exports, require2) {
31511
+ (function runClassicContentModule85(module, exports, require2) {
30793
31512
  (function initCodexOverleafSessionManager() {
30794
31513
  "use strict";
30795
31514
  function create(deps = {}) {
@@ -31048,7 +31767,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31048
31767
  window.CodexOverleafSessionManager = { create };
31049
31768
  })();
31050
31769
  }).call(globalThis, void 0, void 0, void 0);
31051
- (function runClassicContentModule83(module, exports, require2) {
31770
+ (function runClassicContentModule86(module, exports, require2) {
31052
31771
  (function initCodexOverleafSessionForkController(root, factory) {
31053
31772
  const api = factory();
31054
31773
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -31071,19 +31790,26 @@ ${conclusion}` : `${label} ${conclusion}`;
31071
31790
  const source = findSource(previousState, runId);
31072
31791
  if (!source) return failFork("session_fork_source_missing", tr("forkRunUnavailable"));
31073
31792
  const { session, run, runIndex } = source;
31074
- if (run.status === "running" || !session.codexThreadId || !run.codexTurnId) {
31793
+ const draftThreadId = run.codexTurnId && run.interruptedDraft?.turnId === run.codexTurnId ? run.interruptedDraft.threadId : "";
31794
+ const sourceThreadId = String(run.codexThreadId || draftThreadId || session.codexThreadId || "").trim();
31795
+ if (run.status === "running" || !sourceThreadId || !run.codexTurnId) {
31075
31796
  return failFork("session_fork_unavailable", tr("forkRunUnavailable"));
31076
31797
  }
31077
- const response = await sendBackgroundNative({
31078
- method: "codex.thread.fork",
31079
- params: {
31080
- threadId: session.codexThreadId,
31081
- lastTurnId: run.codexTurnId,
31082
- speedTier: session.speedTier,
31083
- loadCodexLocalSkills: previousState.loadCodexLocalSkills !== false,
31084
- loadCodexOverleafSkills: previousState.loadCodexOverleafSkills !== false
31085
- }
31086
- });
31798
+ let response;
31799
+ try {
31800
+ response = await sendBackgroundNative({
31801
+ method: "codex.thread.fork",
31802
+ params: {
31803
+ threadId: sourceThreadId,
31804
+ lastTurnId: run.codexTurnId,
31805
+ speedTier: session.speedTier,
31806
+ loadCodexLocalSkills: previousState.loadCodexLocalSkills !== false,
31807
+ loadCodexOverleafSkills: previousState.loadCodexOverleafSkills !== false
31808
+ }
31809
+ });
31810
+ } catch (error) {
31811
+ return failFork(error?.code || "session_fork_failed", error?.message || tr("forkRunFailed"));
31812
+ }
31087
31813
  if (!response?.ok || !response.result?.threadId) {
31088
31814
  return failFork(response?.error?.code || "session_fork_failed", response?.error?.message || tr("forkRunFailed"));
31089
31815
  }
@@ -31177,7 +31903,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31177
31903
  return { copyRunSnapshot, create };
31178
31904
  });
31179
31905
  }).call(globalThis, void 0, void 0, void 0);
31180
- (function runClassicContentModule84(module, exports, require2) {
31906
+ (function runClassicContentModule87(module, exports, require2) {
31181
31907
  (function initCodexOverleafApplyResultFormatters() {
31182
31908
  "use strict";
31183
31909
  function create(deps = {}) {
@@ -31497,7 +32223,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31497
32223
  window.CodexOverleafApplyResultFormatters = { create };
31498
32224
  })();
31499
32225
  }).call(globalThis, void 0, void 0, void 0);
31500
- (function runClassicContentModule85(module, exports, require2) {
32226
+ (function runClassicContentModule88(module, exports, require2) {
31501
32227
  (function initCodexOverleafModelPickerSupport(root, factory) {
31502
32228
  if (typeof module === "object" && module.exports) {
31503
32229
  module.exports = factory(require2("../shared/models"));
@@ -31520,11 +32246,45 @@ ${conclusion}` : `${label} ${conclusion}`;
31520
32246
  function formatCompactModelLabel(label) {
31521
32247
  return String(label || "").replace(/^gpt[-\s]*/i, "");
31522
32248
  }
31523
- function retainSelectedModel(models, selectedModel) {
32249
+ function retainSelectedModel(models, selectedModel, options2 = {}) {
31524
32250
  const selectedId = normalizeModelOptionId(selectedModel);
31525
32251
  if (!selectedId) return "";
32252
+ if (options2.preserveUnavailable === true) return selectedId;
31526
32253
  return (Array.isArray(models) ? models : []).some((model) => normalizeModelOptionId(typeof model === "string" ? model : model?.id) === selectedId) ? selectedId : "";
31527
32254
  }
32255
+ function preserveSelectedCapabilities(models, selectedModel, preferences = {}, provisional = false) {
32256
+ const selectedId = normalizeModelOptionId(selectedModel);
32257
+ const saved = preferences || {};
32258
+ return (Array.isArray(models) ? models : []).map((model) => {
32259
+ if (model.id !== selectedId || !provisional && !model.unverified) return model;
32260
+ const reasoningEfforts = normalizeReasoningEffortsForSelect([
32261
+ ...model.reasoningEfforts || [],
32262
+ saved.reasoningEffort
32263
+ ]);
32264
+ const speedTiers = normalizeSpeedTiersForSelect(model.speedTiers);
32265
+ const speed = ["standard", "fast"].includes(saved.speedTier) ? saved.speedTier : "";
32266
+ if (speed && !speedTiers.includes(speed)) speedTiers.push(speed);
32267
+ return {
32268
+ ...model,
32269
+ unverified: true,
32270
+ reasoningEfforts,
32271
+ speedTiers,
32272
+ defaultReasoningEffort: reasoningEfforts.includes(saved.reasoningEffort) ? saved.reasoningEffort : model.defaultReasoningEffort,
32273
+ defaultSpeedTier: speed || model.defaultSpeedTier || "standard"
32274
+ };
32275
+ });
32276
+ }
32277
+ function getRenderedModelEntries(panel) {
32278
+ return Array.from(panel?.querySelector("[data-model]")?.options || []).map((option) => ({
32279
+ id: option.value,
32280
+ label: option.textContent,
32281
+ reasoningEfforts: (option.dataset.reasoningEfforts || "").split(",").filter(Boolean),
32282
+ defaultReasoningEffort: option.dataset.defaultReasoningEffort || "",
32283
+ reasoningPresentation: option.dataset.reasoningPresentation || "",
32284
+ speedTiers: (option.dataset.speedTiers || "standard").split(",").filter(Boolean),
32285
+ defaultSpeedTier: option.dataset.defaultSpeedTier || "standard"
32286
+ }));
32287
+ }
31528
32288
  function shouldUseBuiltInFallback(response) {
31529
32289
  const providerId = normalizeModelOptionId(response?.result?.providerId);
31530
32290
  return response?.ok === true && (!providerId || providerId === "builtin");
@@ -31593,12 +32353,14 @@ ${conclusion}` : `${label} ${conclusion}`;
31593
32353
  normalizeReasoningEffortsForSelect,
31594
32354
  normalizeSpeedTiersForSelect,
31595
32355
  retainSelectedModel,
32356
+ preserveSelectedCapabilities,
32357
+ getRenderedModelEntries,
31596
32358
  shouldUseBuiltInFallback
31597
32359
  };
31598
32360
  return api;
31599
32361
  });
31600
32362
  }).call(globalThis, void 0, void 0, void 0);
31601
- (function runClassicContentModule86(module, exports, require2) {
32363
+ (function runClassicContentModule89(module, exports, require2) {
31602
32364
  (function initCodexOverleafModelPicker() {
31603
32365
  "use strict";
31604
32366
  function create(deps = {}) {
@@ -31715,7 +32477,8 @@ ${conclusion}` : `${label} ${conclusion}`;
31715
32477
  return { stale: false, error: response?.error || { code: "provider_models_unavailable" } };
31716
32478
  }
31717
32479
  const sourceModels = hasDiscoveredModels ? response.result.models : fallbackModels;
31718
- const retainedSelectedModel = retainSelectedModel(sourceModels, currentSelectedModel);
32480
+ const preserveBuiltin = (selection?.providerId || "builtin") === "builtin" && (getState()?.providerId || "builtin") === "builtin";
32481
+ const retainedSelectedModel = retainSelectedModel(sourceModels, currentSelectedModel, { preserveUnavailable: preserveBuiltin });
31719
32482
  const normalized = modelCatalog.normalizeDiscoveredModels({ models: sourceModels, selectedModel: retainedSelectedModel });
31720
32483
  if (normalized.usedFallback && response.result?.providerId && response.result.providerId !== "builtin") {
31721
32484
  applyUnavailableModelOptions({ code: "provider_model_catalog_invalid", message: "The active provider returned no usable models." });
@@ -31724,9 +32487,10 @@ ${conclusion}` : `${label} ${conclusion}`;
31724
32487
  error: { code: "provider_model_catalog_invalid", message: "The active provider returned no usable models." }
31725
32488
  };
31726
32489
  }
31727
- renderModelOptions(normalized.models, retainedSelectedModel, { allowUnknownSelected: false });
32490
+ const renderedModels = preserveBuiltin ? Support.preserveSelectedCapabilities(normalized.models, retainedSelectedModel, getState(), !hasDiscoveredModels || response.result?.source === "fallback") : normalized.models;
32491
+ renderModelOptions(renderedModels, retainedSelectedModel, { allowUnknownSelected: false });
31728
32492
  modelDiscovery = {
31729
- status: hasDiscoveredModels && !normalized.usedFallback ? "discovered" : "fallback",
32493
+ status: hasDiscoveredModels && !normalized.usedFallback && response.result?.source !== "fallback" ? "discovered" : "fallback",
31730
32494
  source: hasDiscoveredModels ? response.result?.source || "unknown" : "fallback",
31731
32495
  fetchedAt: hasDiscoveredModels ? response.result?.fetchedAt || "" : "",
31732
32496
  errorCode: hasDiscoveredModels ? "" : response?.error?.code || "",
@@ -31752,15 +32516,16 @@ ${conclusion}` : `${label} ${conclusion}`;
31752
32516
  }
31753
32517
  }
31754
32518
  function applyUnavailableModelOptions(error) {
32519
+ const selectedModel = resolveSelectedModel();
31755
32520
  const modelSelect = getPanel()?.querySelector("[data-model]");
31756
32521
  if (modelSelect) {
31757
32522
  modelSelect.textContent = "";
31758
32523
  const option = document.createElement("option");
31759
- option.value = resolveSelectedModel();
32524
+ option.value = selectedModel;
31760
32525
  option.textContent = tx("Models unavailable", "\u6A21\u578B\u4E0D\u53EF\u7528");
31761
32526
  option.disabled = true;
31762
32527
  modelSelect.append(option);
31763
- modelSelect.value = "";
32528
+ modelSelect.value = selectedModel;
31764
32529
  modelSelect.disabled = true;
31765
32530
  }
31766
32531
  modelDiscovery = {
@@ -31770,8 +32535,18 @@ ${conclusion}` : `${label} ${conclusion}`;
31770
32535
  errorCode: error?.code || "",
31771
32536
  errorMessage: error?.message || (error ? String(error) : "")
31772
32537
  };
31773
- renderReasoningOptions([]);
31774
- renderSpeedOptions([]);
32538
+ const pendingModels = Support.preserveSelectedCapabilities(
32539
+ [{ id: selectedModel, unverified: true }],
32540
+ selectedModel,
32541
+ getState(),
32542
+ true
32543
+ );
32544
+ renderReasoningOptions(pendingModels);
32545
+ renderSpeedOptions(pendingModels);
32546
+ for (const selector of ["[data-reasoning]", "[data-speed]"]) {
32547
+ const input = getPanel()?.querySelector(selector);
32548
+ if (input) input.disabled = true;
32549
+ }
31775
32550
  renderModelConfigChoices();
31776
32551
  updateModelDisplay();
31777
32552
  }
@@ -31779,9 +32554,9 @@ ${conclusion}` : `${label} ${conclusion}`;
31779
32554
  const modelCatalog = getModelCatalog();
31780
32555
  const fallbackModels = modelCatalog.FALLBACK_MODELS;
31781
32556
  const sourceModels = fallbackModels;
31782
- const retainedSelectedModel = retainSelectedModel(sourceModels, selectedModel);
32557
+ const retainedSelectedModel = retainSelectedModel(sourceModels, selectedModel, { preserveUnavailable: (getState()?.providerId || "builtin") === "builtin" });
31783
32558
  const normalized = modelCatalog.normalizeDiscoveredModels({ models: sourceModels, selectedModel: retainedSelectedModel });
31784
- renderModelOptions(normalized.models, retainedSelectedModel);
32559
+ renderModelOptions(Support.preserveSelectedCapabilities(normalized.models, retainedSelectedModel, getState(), true), retainedSelectedModel);
31785
32560
  modelDiscovery = {
31786
32561
  status: "fallback",
31787
32562
  source: "fallback",
@@ -32076,7 +32851,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32076
32851
  window.CodexOverleafModelPicker = { create };
32077
32852
  })();
32078
32853
  }).call(globalThis, void 0, void 0, void 0);
32079
- (function runClassicContentModule87(module, exports, require2) {
32854
+ (function runClassicContentModule90(module, exports, require2) {
32080
32855
  (function initCodexOverleafProjectSessionCleanup() {
32081
32856
  "use strict";
32082
32857
  const FRESH_RUNNING_WINDOW_MS = 30 * 60 * 1e3;
@@ -32176,7 +32951,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32176
32951
  window.CodexOverleafProjectSessionCleanup = { create };
32177
32952
  })();
32178
32953
  }).call(globalThis, void 0, void 0, void 0);
32179
- (function runClassicContentModule88(module, exports, require2) {
32954
+ (function runClassicContentModule91(module, exports, require2) {
32180
32955
  (function initCodexOverleafRecentProjects() {
32181
32956
  "use strict";
32182
32957
  function create(deps = {}) {
@@ -33102,7 +33877,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33102
33877
  window.CodexOverleafRecentProjects = { create };
33103
33878
  })();
33104
33879
  }).call(globalThis, void 0, void 0, void 0);
33105
- (function runClassicContentModule89(module, exports, require2) {
33880
+ (function runClassicContentModule92(module, exports, require2) {
33106
33881
  (function initCodexOverleafOtWarmMirror() {
33107
33882
  "use strict";
33108
33883
  function create(deps = {}) {
@@ -33144,7 +33919,9 @@ ${conclusion}` : `${label} ${conclusion}`;
33144
33919
  RUN_SNAPSHOT_ZIP_TIMEOUT_MS
33145
33920
  } = deps;
33146
33921
  let currentOtStatus = "off";
33922
+ let otTogglePending = false;
33147
33923
  let otSyncRequestId = 0;
33924
+ let otStartRetryCount = 0;
33148
33925
  let otWarmMirrorProjectId = "";
33149
33926
  let lastExperimentalOtProjectId = "";
33150
33927
  let otWarmMirrorState = {
@@ -33155,7 +33932,10 @@ ${conclusion}` : `${label} ${conclusion}`;
33155
33932
  patchQueue: [],
33156
33933
  lastStatus: "off",
33157
33934
  lastPatchAt: 0,
33158
- lastErrorCode: ""
33935
+ lastErrorCode: "",
33936
+ failureActive: false,
33937
+ lastFailure: null,
33938
+ suspended: false
33159
33939
  };
33160
33940
  let mirrorPrefetchState = {
33161
33941
  inFlight: null,
@@ -33237,7 +34017,10 @@ ${conclusion}` : `${label} ${conclusion}`;
33237
34017
  async function handleExperimentalOtToggleClick(event) {
33238
34018
  const clicked = event?.currentTarget || event?.target;
33239
34019
  const targetEnabled = clicked && typeof clicked.checked === "boolean" ? clicked.checked : void 0;
34020
+ const projectId = getCurrentProjectId();
33240
34021
  event.preventDefault();
34022
+ await new Promise((resolve) => window.setTimeout(resolve, 0));
34023
+ if (projectId !== getCurrentProjectId()) return;
33241
34024
  await toggleExperimentalOtCheckbox(targetEnabled);
33242
34025
  }
33243
34026
  async function handleExperimentalOtToggleKeydown(event) {
@@ -33249,28 +34032,35 @@ ${conclusion}` : `${label} ${conclusion}`;
33249
34032
  }
33250
34033
  async function toggleExperimentalOtCheckbox(targetEnabled) {
33251
34034
  const checkbox = getPanel()?.querySelector("[data-experimental-ot]");
33252
- if (!checkbox) {
34035
+ if (!checkbox || otTogglePending) {
33253
34036
  return;
33254
34037
  }
33255
- const nextEnabled = typeof targetEnabled === "boolean" ? targetEnabled : !checkbox.checked;
33256
- if (nextEnabled) {
33257
- closeDiagnosticsMenu();
33258
- const approved = await showPluginConfirm({
33259
- title: tr("experimentalOtConfirmTitle"),
33260
- message: tr("experimentalOtConfirmMessage"),
33261
- confirmLabel: tr("experimentalOtConfirmEnable"),
33262
- cancelLabel: tr("confirmDefaultCancel")
33263
- });
33264
- if (!approved) {
33265
- return;
34038
+ const projectId = getCurrentProjectId();
34039
+ otTogglePending = true;
34040
+ try {
34041
+ const nextEnabled = typeof targetEnabled === "boolean" ? targetEnabled : !checkbox.checked;
34042
+ if (nextEnabled) {
34043
+ closeDiagnosticsMenu();
34044
+ const approved = await showPluginConfirm({
34045
+ title: tr("experimentalOtConfirmTitle"),
34046
+ message: tr("experimentalOtConfirmMessage"),
34047
+ confirmLabel: tr("experimentalOtConfirmEnable"),
34048
+ cancelLabel: tr("confirmDefaultCancel")
34049
+ });
34050
+ if (!approved) {
34051
+ return;
34052
+ }
34053
+ } else {
34054
+ closeDiagnosticsMenu();
33266
34055
  }
33267
- } else {
33268
- closeDiagnosticsMenu();
33269
- }
33270
- checkbox.checked = nextEnabled;
33271
- handleExperimentalOtToggleChange({ currentTarget: checkbox });
33272
- if (nextEnabled) {
33273
- showPluginToast(tr("experimentalOtEnabledToast"), { status: "info" });
34056
+ if (projectId !== getCurrentProjectId() || checkbox !== getPanel()?.querySelector("[data-experimental-ot]")) return;
34057
+ checkbox.checked = nextEnabled;
34058
+ handleExperimentalOtToggleChange({ currentTarget: checkbox });
34059
+ if (nextEnabled) {
34060
+ showPluginToast(tr("experimentalOtEnabledToast"), { status: "info" });
34061
+ }
34062
+ } finally {
34063
+ otTogglePending = false;
33274
34064
  }
33275
34065
  }
33276
34066
  function handleExperimentalOtToggleChange(event) {
@@ -33279,7 +34069,9 @@ ${conclusion}` : `${label} ${conclusion}`;
33279
34069
  return;
33280
34070
  }
33281
34071
  const projectId = getCurrentProjectId();
34072
+ otWarmMirrorState.failureActive = false;
33282
34073
  lastExperimentalOtProjectId = projectId;
34074
+ otStartRetryCount = 0;
33283
34075
  setExperimentalOtEnabledForProject(projectId, checkbox.checked);
33284
34076
  updateExperimentalOtToggleControl(checkbox.checked);
33285
34077
  const savedBadge = getPanel()?.querySelector('details[data-set-group="experimental"] [data-set-saved]');
@@ -33295,7 +34087,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33295
34087
  updateOtStatusDisplay(checkbox.checked ? currentOtStatus === "off" ? "starting" : currentOtStatus : "off");
33296
34088
  syncOtWarmMirrorController().catch((error) => {
33297
34089
  updateOtStatusDisplay("unavailable");
33298
- appendPlainLog(tx(`Experimental OT warm mirror unavailable: ${error.message}`, `\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
34090
+ appendPlainLog(tx(`OT warm mirror unavailable: ${error.message}`, `OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
33299
34091
  });
33300
34092
  saveStateSoon();
33301
34093
  }
@@ -33313,6 +34105,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33313
34105
  otWarmMirrorProjectId = projectId;
33314
34106
  ensureOtWarmMirrorStateProject(projectId);
33315
34107
  if (enabled) {
34108
+ otWarmMirrorState.suspended = false;
33316
34109
  const pause = otWarmMirrorController?.shouldPauseOtWarmMirror?.({ running: Boolean(getCurrentRunView()) }) || { pause: false };
33317
34110
  if (pause.pause) {
33318
34111
  clearOtEventPolling({ clearPatchQueue: true });
@@ -33324,7 +34117,12 @@ ${conclusion}` : `${label} ${conclusion}`;
33324
34117
  } else {
33325
34118
  clearOtEventPolling({ clearPatchQueue: true });
33326
34119
  }
33327
- const response = enabled ? await callPageBridge("startOtObserver", { projectId }) : await callPageBridge("stopOtObserver", {});
34120
+ let response;
34121
+ try {
34122
+ response = enabled ? await callPageBridge("startOtObserver", { projectId }) : await callPageBridge("stopOtObserver", {});
34123
+ } catch (error) {
34124
+ response = { ok: false, error: otWarmMirrorController.normalizeFailureCode(error?.code || error?.message, "ot_bridge_unavailable") };
34125
+ }
33328
34126
  if (!isCurrentOtSync(requestId, projectId) || getCurrentProjectId() !== projectId) {
33329
34127
  if (enabled) {
33330
34128
  await handleStaleOtStartResponse(projectId, requestId);
@@ -33341,7 +34139,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33341
34139
  }
33342
34140
  const status = readOtBridgeStatus(response);
33343
34141
  if (!isSuccessfulOtBridgeResponse(response) || status === "unavailable") {
33344
- handleFailedOtStart(projectId, requestId);
34142
+ await handleFailedOtStart(projectId, requestId, response);
33345
34143
  return response;
33346
34144
  }
33347
34145
  const pauseAfterStart = otWarmMirrorController?.shouldPauseOtWarmMirror?.({ running: Boolean(getCurrentRunView()) }) || { pause: false };
@@ -33349,7 +34147,10 @@ ${conclusion}` : `${label} ${conclusion}`;
33349
34147
  await pauseOtWarmMirror(pauseAfterStart.reason || "paused");
33350
34148
  return response;
33351
34149
  }
34150
+ if (status === "starting") recordOtFailure("missing_active_path", "start");
34151
+ else otWarmMirrorState.failureActive = false;
33352
34152
  updateOtStatusDisplay(status);
34153
+ otStartRetryCount = 0;
33353
34154
  scheduleOtEventPolling(projectId, { immediate: true });
33354
34155
  return response;
33355
34156
  }
@@ -33369,24 +34170,38 @@ ${conclusion}` : `${label} ${conclusion}`;
33369
34170
  await syncOtWarmMirrorController();
33370
34171
  }
33371
34172
  }
33372
- function handleFailedOtStart(projectId, requestId) {
34173
+ async function handleFailedOtStart(projectId, requestId, response) {
33373
34174
  if (!isCurrentOtSync(requestId, projectId) || getCurrentProjectId() !== projectId) {
33374
34175
  return;
33375
34176
  }
33376
34177
  clearOtEventPolling({ clearPatchQueue: true });
33377
- setExperimentalOtEnabledForProject(projectId, false);
33378
- const experimentalOtCheckbox = getPanel()?.querySelector("[data-experimental-ot]");
33379
- if (experimentalOtCheckbox) {
33380
- experimentalOtCheckbox.checked = false;
33381
- }
34178
+ recordOtFailure(readOtBridgeErrorCode(response, "ot_start_failed"), "start");
34179
+ otWarmMirrorState.suspended = true;
34180
+ updateExperimentalOtToggleControl(isExperimentalOtEnabledForProject(projectId));
33382
34181
  updateOtStatusDisplay("unavailable");
33383
- saveStateSoon();
34182
+ try {
34183
+ await callPageBridge("stopOtObserver", { projectId });
34184
+ } catch (_error) {
34185
+ }
34186
+ scheduleOtStartRetry(projectId, requestId);
34187
+ }
34188
+ function scheduleOtStartRetry(projectId, requestId) {
34189
+ const retryDelays = [1e3, 3e3, 6e3];
34190
+ const canRetry = () => isCurrentOtSync(requestId, projectId) && getCurrentProjectId() === projectId && isExperimentalOtEnabledForProject(projectId) && !getCurrentRunView();
34191
+ if (!canRetry() || otStartRetryCount >= retryDelays.length) return;
34192
+ const delayMs = retryDelays[otStartRetryCount++];
34193
+ otWarmMirrorState.pollTimer = window.setTimeout(() => {
34194
+ otWarmMirrorState.pollTimer = null;
34195
+ if (!canRetry()) return;
34196
+ syncOtWarmMirrorController().catch(() => {
34197
+ if (getCurrentProjectId() === projectId && isExperimentalOtEnabledForProject(projectId)) {
34198
+ updateOtStatusDisplay("unavailable");
34199
+ }
34200
+ });
34201
+ }, delayMs);
33384
34202
  }
33385
34203
  function readOtBridgeStatus(response) {
33386
- if (!response || response.ok === false) {
33387
- return "unavailable";
33388
- }
33389
- return response.getState() || response.status || response.result?.getState() || response.result?.status || "unavailable";
34204
+ return otWarmMirrorController.readOtBridgeStatus(response);
33390
34205
  }
33391
34206
  function ensureOtWarmMirrorStateProject(projectId) {
33392
34207
  if (!projectId || otWarmMirrorState.projectId === projectId) {
@@ -33394,8 +34209,12 @@ ${conclusion}` : `${label} ${conclusion}`;
33394
34209
  }
33395
34210
  clearOtEventPolling({ clearPatchQueue: true });
33396
34211
  otWarmMirrorState.projectId = projectId;
34212
+ otStartRetryCount = 0;
33397
34213
  otWarmMirrorState.lastPatchAt = 0;
33398
34214
  otWarmMirrorState.lastErrorCode = "";
34215
+ otWarmMirrorState.failureActive = false;
34216
+ otWarmMirrorState.lastFailure = null;
34217
+ otWarmMirrorState.suspended = false;
33399
34218
  otWarmMirrorState.lastStatus = currentOtStatus;
33400
34219
  }
33401
34220
  function clearOtEventPolling(options2 = {}) {
@@ -33429,7 +34248,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33429
34248
  }, delayMs);
33430
34249
  }
33431
34250
  function canPollOtWarmMirror(projectId) {
33432
- return Boolean(projectId && otWarmMirrorController?.shouldPauseOtWarmMirror && otWarmMirrorController?.buildPatchFilesRequest && isExperimentalOtEnabledForProject(projectId) && otWarmMirrorProjectId === projectId && getCurrentProjectId() === projectId);
34251
+ return Boolean(projectId && otWarmMirrorController?.shouldPauseOtWarmMirror && otWarmMirrorController?.buildPatchFilesRequest && isExperimentalOtEnabledForProject(projectId) && otWarmMirrorProjectId === projectId && getCurrentProjectId() === projectId && !otWarmMirrorState.suspended);
33433
34252
  }
33434
34253
  async function pollOtEvents(projectId = otWarmMirrorState.projectId || getCurrentProjectId()) {
33435
34254
  if (!canPollOtWarmMirror(projectId)) {
@@ -33449,11 +34268,13 @@ ${conclusion}` : `${label} ${conclusion}`;
33449
34268
  return { ok: false, skipped: true, reason: "project_changed" };
33450
34269
  }
33451
34270
  if (!isSuccessfulOtBridgeResponse(statusResponse)) {
33452
- otWarmMirrorState.lastErrorCode = readOtBridgeErrorCode(statusResponse, "get_ot_status_failed");
34271
+ recordOtFailure(readOtBridgeErrorCode(statusResponse, "get_ot_status_failed"), "observe");
33453
34272
  updateOtStatusDisplay("unavailable");
33454
34273
  return statusResponse;
33455
34274
  }
33456
34275
  const status = readOtBridgeStatus(statusResponse);
34276
+ if (status === "observing" && otWarmMirrorState.lastFailure?.phase === "start") Object.assign(otWarmMirrorState, { failureActive: false, lastErrorCode: "", lastFailure: null });
34277
+ if (["unavailable", "inconsistent"].includes(status)) recordOtFailure(readOtBridgeErrorCode(statusResponse, "ot_observer_unavailable"), "observe");
33457
34278
  if (status) {
33458
34279
  updateOtStatusDisplay(status);
33459
34280
  }
@@ -33462,7 +34283,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33462
34283
  return { ok: false, skipped: true, reason: "project_changed" };
33463
34284
  }
33464
34285
  if (!isSuccessfulOtBridgeResponse(drainResponse)) {
33465
- otWarmMirrorState.lastErrorCode = readOtBridgeErrorCode(drainResponse, "drain_ot_events_failed");
34286
+ recordOtFailure(readOtBridgeErrorCode(drainResponse, "drain_ot_events_failed"), "observe");
33466
34287
  updateOtStatusDisplay("unavailable");
33467
34288
  return drainResponse;
33468
34289
  }
@@ -33472,7 +34293,8 @@ ${conclusion}` : `${label} ${conclusion}`;
33472
34293
  }
33473
34294
  return drainResponse;
33474
34295
  } catch (error) {
33475
- otWarmMirrorState.lastErrorCode = error?.code || error?.message || "ot_poll_failed";
34296
+ if (!canPollOtWarmMirror(projectId)) return { ok: false, skipped: true, reason: "project_changed" };
34297
+ recordOtFailure(error?.code || error?.message || "ot_poll_failed", "observe");
33476
34298
  updateOtStatusDisplay("unavailable");
33477
34299
  return { ok: false, error };
33478
34300
  } finally {
@@ -33482,27 +34304,33 @@ ${conclusion}` : `${label} ${conclusion}`;
33482
34304
  }
33483
34305
  }
33484
34306
  function readOtBridgeEvents(response) {
33485
- if (Array.isArray(response)) {
33486
- return response;
33487
- }
33488
- if (Array.isArray(response?.events)) {
33489
- return response.events;
33490
- }
33491
- if (Array.isArray(response?.result?.events)) {
33492
- return response.result.events;
33493
- }
33494
- return [];
34307
+ return otWarmMirrorController.readOtBridgeEvents(response);
33495
34308
  }
33496
34309
  function readOtBridgeErrorCode(response, fallback) {
33497
- const value = response?.lastErrorCode || response?.reason || response?.error?.code || response?.error || fallback;
33498
- return typeof value === "string" ? value : fallback;
34310
+ return otWarmMirrorController.readOtBridgeErrorCode(response, fallback);
34311
+ }
34312
+ function recordOtFailure(code, phase) {
34313
+ code = otWarmMirrorController.normalizeFailureCode(code);
34314
+ otWarmMirrorState.lastErrorCode = code;
34315
+ otWarmMirrorState.failureActive = true;
34316
+ otWarmMirrorState.lastFailure = { code, phase, at: Date.now() };
33499
34317
  }
33500
34318
  function queueOtPatchEvents(events, projectId = getCurrentProjectId()) {
33501
34319
  if (!Array.isArray(events) || !events.length || !canPollOtWarmMirror(projectId)) {
33502
34320
  return;
33503
34321
  }
33504
34322
  ensureOtWarmMirrorStateProject(projectId);
33505
- otWarmMirrorState.patchQueue.push(...events);
34323
+ const queued = otWarmMirrorController.queuePatchEvents(otWarmMirrorState.patchQueue, events);
34324
+ if (!queued.ok) {
34325
+ recordOtFailure(queued.reason, "queue");
34326
+ clearOtEventPolling({ clearPatchQueue: true });
34327
+ otWarmMirrorState.suspended = true;
34328
+ updateOtStatusDisplay("inconsistent");
34329
+ void callPageBridge("stopOtObserver", { projectId }).catch(() => {
34330
+ });
34331
+ return;
34332
+ }
34333
+ otWarmMirrorState.patchQueue = queued.queue;
33506
34334
  scheduleOtPatchFlush(projectId);
33507
34335
  }
33508
34336
  function scheduleOtPatchFlush(projectId = otWarmMirrorState.projectId || getCurrentProjectId(), options2 = {}) {
@@ -33536,11 +34364,18 @@ ${conclusion}` : `${label} ${conclusion}`;
33536
34364
  clearOtEventPolling({ clearPatchQueue: false });
33537
34365
  return { ok: false, skipped: true, reason: pause.reason || "paused" };
33538
34366
  }
33539
- const maxBatch = Math.max(1, Number(otWarmMirrorController?.OT_MAX_PATCH_BATCH) || 25);
33540
- const batch = otWarmMirrorState.patchQueue.splice(0, maxBatch);
34367
+ let batch = otWarmMirrorController.takePatchBatch(otWarmMirrorState.patchQueue);
33541
34368
  if (!batch.length) {
33542
34369
  return { ok: true, skipped: true, reason: "empty_batch" };
33543
34370
  }
34371
+ const fresh = otWarmMirrorController.queuePatchEvents([], batch);
34372
+ if (!fresh.ok) {
34373
+ recordOtFailure(fresh.reason, "queue");
34374
+ clearOtEventPolling({ clearPatchQueue: true });
34375
+ updateOtStatusDisplay("inconsistent");
34376
+ return { ok: false, skipped: true, reason: fresh.reason };
34377
+ }
34378
+ batch = fresh.queue;
33544
34379
  otWarmMirrorState.flushing = true;
33545
34380
  try {
33546
34381
  const request = {
@@ -33554,40 +34389,35 @@ ${conclusion}` : `${label} ${conclusion}`;
33554
34389
  return { ok: false, skipped: true, reason: "project_changed" };
33555
34390
  }
33556
34391
  const response = await sendBackgroundNative(request);
34392
+ if (!canPollOtWarmMirror(projectId)) return { ok: false, skipped: true, reason: "project_changed" };
33557
34393
  if (!response?.ok) {
33558
- otWarmMirrorState.lastErrorCode = response?.error?.code || response?.error?.message || "mirror_patch_failed";
34394
+ recordOtFailure(response?.error?.code || "mirror_patch_failed", "flush");
33559
34395
  updateOtStatusDisplay("inconsistent");
33560
34396
  return response;
33561
34397
  }
33562
- const result = response.result || {};
33563
- const skippedFiles = Array.isArray(result?.skippedFiles) ? result.skippedFiles : [];
33564
- const skippedCount = Number.isFinite(Number(result?.skippedCount)) ? Number(result.skippedCount) : skippedFiles.length;
33565
- if (skippedFiles.length || skippedCount > 0) {
33566
- otWarmMirrorState.lastErrorCode = "mirror_patch_skipped";
33567
- updateOtStatusDisplay("inconsistent");
33568
- return response;
33569
- }
33570
- const appliedFiles = Array.isArray(result?.appliedFiles) ? result.appliedFiles : null;
33571
- const appliedCount = Number.isFinite(Number(result?.appliedCount)) ? Number(result.appliedCount) : NaN;
33572
- if (!appliedFiles || !Number.isFinite(appliedCount) || appliedCount <= 0 || appliedFiles.length !== appliedCount) {
33573
- otWarmMirrorState.lastErrorCode = "mirror_patch_invalid_result";
34398
+ const receipt = otWarmMirrorController.validatePatchReceipt(batch, response.result);
34399
+ if (!receipt.ok) {
34400
+ recordOtFailure(receipt.reason, "flush");
33574
34401
  updateOtStatusDisplay("inconsistent");
33575
34402
  return response;
33576
34403
  }
33577
34404
  otWarmMirrorState.lastPatchAt = Date.now();
33578
34405
  otWarmMirrorState.lastErrorCode = "";
33579
- if (canPollOtWarmMirror(projectId)) {
34406
+ otWarmMirrorState.failureActive = false;
34407
+ if (canPollOtWarmMirror(projectId) && !getCurrentRunView()) {
33580
34408
  updateOtStatusDisplay("observing");
33581
34409
  }
33582
34410
  return response;
33583
34411
  } catch (error) {
33584
- otWarmMirrorState.lastErrorCode = error?.code || error?.message || "mirror_patch_failed";
34412
+ if (!canPollOtWarmMirror(projectId)) return { ok: false, skipped: true, reason: "project_changed" };
34413
+ recordOtFailure(error?.code || "mirror_patch_failed", "flush");
33585
34414
  updateOtStatusDisplay("inconsistent");
33586
34415
  return { ok: false, error };
33587
34416
  } finally {
33588
34417
  otWarmMirrorState.flushing = false;
33589
- if (otWarmMirrorState.patchQueue.length && canPollOtWarmMirror(projectId) && !getCurrentRunView()) {
33590
- scheduleOtPatchFlush(projectId, { immediate: true });
34418
+ const nextProjectId = otWarmMirrorState.projectId;
34419
+ if (otWarmMirrorState.patchQueue.length && canPollOtWarmMirror(nextProjectId) && !getCurrentRunView()) {
34420
+ scheduleOtPatchFlush(nextProjectId, { immediate: true });
33591
34421
  }
33592
34422
  }
33593
34423
  }
@@ -33627,12 +34457,12 @@ ${conclusion}` : `${label} ${conclusion}`;
33627
34457
  }
33628
34458
  }
33629
34459
  function updateOtStatusDisplay(status = currentOtStatus) {
33630
- const normalized = normalizeOtStatus(status);
34460
+ const normalized = otWarmMirrorState.failureActive && status === "observing" ? "inconsistent" : normalizeOtStatus(status);
33631
34461
  currentOtStatus = normalized;
33632
34462
  otWarmMirrorState.lastStatus = normalized;
33633
34463
  const statusElement = getPanel()?.querySelector("[data-ot-status]");
33634
34464
  if (statusElement) {
33635
- const label = formatOtStatusLabel(normalized);
34465
+ const label = formatOtStatusLabel(normalized) + (otWarmMirrorState.failureActive ? ": " + tr(otWarmMirrorController.getFailureMessageKey(otWarmMirrorState.lastErrorCode)) : "");
33636
34466
  statusElement.textContent = formatOtToggleStatusText(label);
33637
34467
  statusElement.dataset.otStatus = normalized;
33638
34468
  statusElement.title = formatOtToggleStatusText(label);
@@ -33780,37 +34610,23 @@ ${conclusion}` : `${label} ${conclusion}`;
33780
34610
  return health.reusable === true;
33781
34611
  }
33782
34612
  async function resolveWarmRunStart(taskContext = {}) {
34613
+ const projectId = getCurrentProjectId();
33783
34614
  const focusFiles = taskContext.focusFiles || [];
33784
34615
  const mode = taskContext.mode || getState()?.mode;
33785
34616
  const mirrorStatus = await getMirrorFreshness();
34617
+ if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: "project_changed" };
33786
34618
  const otWarmStart = otWarmMirrorController.canUseOtWarmStart({
33787
- enabled: isExperimentalOtEnabled(),
34619
+ enabled: isExperimentalOtEnabled() && !otWarmMirrorState.failureActive,
33788
34620
  focusFiles,
33789
34621
  mirrorStatus
33790
34622
  });
33791
- if (otWarmStart.ok) {
33792
- return {
33793
- useExistingMirror: true,
33794
- warmStart: true,
33795
- otWarmStart: true,
33796
- reason: "ot_focus_fresh",
33797
- mirrorStatus,
33798
- focusFiles: otWarmStart.focusFiles,
33799
- project: {
33800
- capabilities: {
33801
- fullProjectSnapshot: false,
33802
- method: "ot-warm-mirror"
33803
- },
33804
- files: []
33805
- }
33806
- };
33807
- }
33808
- if (!mirrorStatus?.exists || !isMirrorReusable(mirrorStatus)) {
34623
+ if (!mirrorStatus?.exists || !otWarmStart.ok && !isMirrorReusable(mirrorStatus)) {
33809
34624
  return { useExistingMirror: false, reason: "mirror_not_fresh", mirrorStatus };
33810
34625
  }
33811
34626
  let overlayProject = null;
33812
34627
  try {
33813
34628
  overlayProject = await callPageBridge("getProjectSnapshot", {
34629
+ projectId,
33814
34630
  force: true,
33815
34631
  maxAgeMs: 0,
33816
34632
  preferLightweight: true,
@@ -33824,6 +34640,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33824
34640
  } catch (error) {
33825
34641
  return { useExistingMirror: false, reason: "overlay_probe_failed", mirrorStatus };
33826
34642
  }
34643
+ if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: "project_changed" };
33827
34644
  const fileOverlays = buildSnapshotFileOverlays(overlayProject, focusFiles);
33828
34645
  if (!fileOverlays.length) {
33829
34646
  return { useExistingMirror: false, reason: "missing_current_overlay", mirrorStatus };
@@ -33837,7 +34654,9 @@ ${conclusion}` : `${label} ${conclusion}`;
33837
34654
  return {
33838
34655
  useExistingMirror: true,
33839
34656
  warmStart: true,
33840
- reason: mode === "ask" ? "warm_whole_project_ask" : "warm_whole_project",
34657
+ otWarmStart: otWarmStart.ok,
34658
+ focusFiles,
34659
+ reason: otWarmStart.ok ? "ot_focus_fresh" : mode === "ask" ? "warm_whole_project_ask" : "warm_whole_project",
33841
34660
  mirrorStatus,
33842
34661
  fileOverlays,
33843
34662
  project: overlayProject
@@ -33922,7 +34741,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33922
34741
  return syncOtWarmMirrorController();
33923
34742
  }).catch((error) => {
33924
34743
  updateOtStatusDisplay("unavailable");
33925
- appendPlainLog(tx(`Experimental OT warm mirror unavailable: ${error.message}`, `\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
34744
+ appendPlainLog(tx(`OT warm mirror unavailable: ${error.message}`, `OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
33926
34745
  });
33927
34746
  }
33928
34747
  return {
@@ -33957,13 +34776,14 @@ ${conclusion}` : `${label} ${conclusion}`;
33957
34776
  window.CodexOverleafOtWarmMirror = { create };
33958
34777
  })();
33959
34778
  }).call(globalThis, void 0, void 0, void 0);
33960
- (function runClassicContentModule90(module, exports, require2) {
34779
+ (function runClassicContentModule93(module, exports, require2) {
33961
34780
  (function initCodexOverleafWritebackOrchestrator() {
33962
34781
  "use strict";
33963
34782
  let tr;
33964
34783
  let tx;
33965
34784
  let getLocale;
33966
34785
  let appendRunEvent;
34786
+ let appendRunRecordEvent;
33967
34787
  let pendingMirrorRefresh = null;
33968
34788
  let appendChangeSummary;
33969
34789
  let appendCompletionReport;
@@ -34010,6 +34830,34 @@ ${conclusion}` : `${label} ${conclusion}`;
34010
34830
  function buildSettlement(input = {}) {
34011
34831
  return writebackSettlement?.settle instanceof Function ? writebackSettlement.settle(input) : null;
34012
34832
  }
34833
+ function invalidateMirrorAfterUndo(runId, projectId, result = {}) {
34834
+ if (!projectId || result?.changedDocument !== true && !result?.applied?.length) return Promise.resolve("not-attempted");
34835
+ const pending = Promise.resolve(pendingMirrorRefresh).catch(() => {
34836
+ }).then(async () => {
34837
+ const response = await sendBackgroundNative({ method: "mirror.invalidate", params: { projectId } });
34838
+ if (!response?.ok || response.result?.invalidated !== true) {
34839
+ throw new Error(response?.error?.message || "Native host did not confirm mirror invalidation.");
34840
+ }
34841
+ if (getCurrentProjectId() === projectId) {
34842
+ resetContextProject();
34843
+ await callPageBridge("invalidateProjectSnapshot", { invalidateFileList: true });
34844
+ }
34845
+ return "invalidated";
34846
+ }).catch((error) => {
34847
+ appendRunRecordEvent?.(runId, {
34848
+ status: "warning",
34849
+ title: tx(
34850
+ `Overleaf undo finished, but local mirror invalidation failed: ${error.message}. Refresh this project or reconnect the native host before running again.`,
34851
+ `Overleaf \u64A4\u9500\u5DF2\u5B8C\u6210\uFF0C\u4F46\u672C\u5730\u955C\u50CF\u5931\u6548\u5904\u7406\u5931\u8D25\uFF1A${error.message}\u3002\u8BF7\u5237\u65B0\u9879\u76EE\u6216\u91CD\u65B0\u8FDE\u63A5 Native Host \u540E\u518D\u8FD0\u884C\u3002`
34852
+ )
34853
+ });
34854
+ return "failed";
34855
+ }).finally(() => {
34856
+ if (pendingMirrorRefresh === pending) pendingMirrorRefresh = null;
34857
+ });
34858
+ pendingMirrorRefresh = pending;
34859
+ return pending;
34860
+ }
34013
34861
  async function applySyncChangesToOverleaf(syncChanges = [], project = {}, options2 = {}) {
34014
34862
  const runMode = options2.mode || getState().mode;
34015
34863
  const runRequireReviewing = typeof options2.requireReviewing === "boolean" ? options2.requireReviewing : getState().requireReviewing === true;
@@ -34662,6 +35510,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34662
35510
  tx,
34663
35511
  getLocale,
34664
35512
  appendRunEvent,
35513
+ appendRunRecordEvent,
34665
35514
  appendChangeSummary,
34666
35515
  appendCompletionReport,
34667
35516
  appendOperationsPreview,
@@ -34707,13 +35556,161 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34707
35556
  return {
34708
35557
  applySyncChangesToOverleaf,
34709
35558
  resolveCompileLogContext,
35559
+ invalidateMirrorAfterUndo,
34710
35560
  getPendingMirrorRefresh: () => pendingMirrorRefresh
34711
35561
  };
34712
35562
  }
34713
35563
  window.CodexOverleafWritebackOrchestrator = { create };
34714
35564
  })();
34715
35565
  }).call(globalThis, void 0, void 0, void 0);
34716
- (function runClassicContentModule91(module, exports, require2) {
35566
+ (function runClassicContentModule94(module, exports, require2) {
35567
+ (function(root, factory) {
35568
+ const api = factory();
35569
+ if (typeof module === "object" && module.exports) module.exports = api;
35570
+ if (root) root.CodexOverleafTrackedChangeCaptureController = api;
35571
+ })(typeof window !== "undefined" ? window : globalThis, function() {
35572
+ "use strict";
35573
+ function create(deps = {}) {
35574
+ const now = deps.now || (() => Date.now());
35575
+ const defer = deps.setTimeout || setTimeout;
35576
+ const cancelTimer = deps.clearTimeout || clearTimeout;
35577
+ let timer = null, busy = false, disposed = false;
35578
+ const runs = () => deps.getRuns() || [];
35579
+ const pending = (run) => Array.isArray(run.trackedChangeCaptures) && run.trackedChangeCaptures.some((capture) => capture.state === "pending");
35580
+ const terminal = (run) => ["accepted", "rejected"].includes(run.trackedChangeStatus) || run.undoStatus === "applied" || run.forkSnapshot === true;
35581
+ function buildPostFiles(run) {
35582
+ const expectedFiles = Array.isArray(run?.undoExpectedFiles) ? run.undoExpectedFiles : [];
35583
+ const appliedOperations = Array.isArray(run?.appliedOperations) ? run.appliedOperations : [];
35584
+ if (!expectedFiles.length || !appliedOperations.length) return [];
35585
+ const postFilesByPath = deps.buildExpectedFilesAfterOperations({ files: expectedFiles }, appliedOperations);
35586
+ const expectedPaths = new Set(expectedFiles.map((file) => file?.path).filter(Boolean));
35587
+ return Array.from(postFilesByPath.entries()).filter(([path, content]) => expectedPaths.has(path) && typeof content === "string").map(([path, content]) => ({ path, content }));
35588
+ }
35589
+ function hasLegacyUntrackedCheckpoint(run) {
35590
+ if (!run || run.executionSnapshot || run.trackedChangeStatus || run.undoStatus === "applied" || run.forkSnapshot || run.undoTrackedChanges?.length || run.trackedChangeCaptures?.length) return false;
35591
+ const operations = Array.isArray(run.undoOperations) ? run.undoOperations : [];
35592
+ const before = Array.isArray(run.undoExpectedFiles) ? run.undoExpectedFiles : [];
35593
+ if (!operations.length || !before.length || before.some((file) => !file?.path || typeof file.content !== "string")) return false;
35594
+ const paths = new Set(before.map((file) => file.path));
35595
+ if (paths.size !== before.length || operations.some((op) => op?.type !== "edit" || !paths.has(op.path))) return false;
35596
+ const complete = (files) => Array.isArray(files) && files.length === paths.size && new Set(files.map((file) => file?.path)).size === paths.size && files.every((file) => paths.has(file?.path) && typeof file.content === "string");
35597
+ const storedPost = run.undoBaseFiles;
35598
+ try {
35599
+ const post = buildPostFiles(run);
35600
+ if (!complete(storedPost) || !complete(post)) return false;
35601
+ const postByPath = new Map(post.map((file) => [file.path, file.content]));
35602
+ if (!storedPost.every((file) => postByPath.get(file.path) === file.content)) return false;
35603
+ const restored = deps.buildExpectedFilesAfterOperations({ files: storedPost }, operations);
35604
+ return before.every((file) => restored.get(file.path) === file.content);
35605
+ } catch (_error) {
35606
+ return false;
35607
+ }
35608
+ }
35609
+ function schedule() {
35610
+ if (disposed || timer !== null || busy || !runs().some(pending)) return;
35611
+ timer = defer(() => {
35612
+ timer = null;
35613
+ flush().catch((error) => deps.onError?.(error));
35614
+ }, 500);
35615
+ }
35616
+ function record(run, captures) {
35617
+ if (!Array.isArray(captures) || !captures.length) return;
35618
+ const next = [...run.trackedChangeCaptures || [], ...captures.filter(Boolean)];
35619
+ if (next.every((capture) => capture.state === "observed")) return;
35620
+ run.trackedChangeCaptures = next;
35621
+ deps.notify?.(run, next.some((capture) => capture.state === "pending") ? "pending" : "needs_review", {
35622
+ captures: next.map((capture) => ({ path: capture.path, reason: capture.reason, ...capture.diagnostics }))
35623
+ });
35624
+ deps.persist().then(schedule).catch((error) => deps.onError?.(error));
35625
+ }
35626
+ async function flush() {
35627
+ if (disposed || busy) return;
35628
+ busy = true;
35629
+ try {
35630
+ for (const run of runs().filter(pending)) {
35631
+ if (terminal(run)) {
35632
+ run.trackedChangeCaptures = [];
35633
+ await deps.persist();
35634
+ continue;
35635
+ }
35636
+ if (run.runProjectId !== deps.getProjectId()) continue;
35637
+ const newer = runs().some((other) => other.id !== run.id && other.runProjectId === run.runProjectId && Date.parse(other.startedAt) > Date.parse(run.startedAt));
35638
+ const original = run.trackedChangeCaptures;
35639
+ const captures = original.map((capture) => ({ ...capture }));
35640
+ const postFiles = buildPostFiles(run);
35641
+ for (let i = 0; i < captures.length; i++) {
35642
+ const capture = captures[i];
35643
+ if (capture.state !== "pending") continue;
35644
+ const expected = postFiles.find((file) => file.path === capture.path);
35645
+ if (newer || !expected || now() >= capture.expiresAt || run.status === "cancelled") {
35646
+ capture.state = "needs_review";
35647
+ capture.reason = newer ? "capture_superseded" : !expected ? "capture_baseline_missing" : run.status === "cancelled" ? "capture_cancelled" : "capture_expired";
35648
+ continue;
35649
+ }
35650
+ let response;
35651
+ try {
35652
+ response = await deps.callPageBridge("reconcileTrackedChangeCapture", {
35653
+ runProjectId: run.runProjectId,
35654
+ capture,
35655
+ expectedContent: expected.content
35656
+ });
35657
+ } catch (_error) {
35658
+ response = null;
35659
+ }
35660
+ if (disposed || deps.getProjectId() !== run.runProjectId || !runs().includes(run) || terminal(run) || run.trackedChangeCaptures !== original) return;
35661
+ const superseded = runs().some((other) => other.id !== run.id && other.runProjectId === run.runProjectId && Date.parse(other.startedAt) > Date.parse(run.startedAt));
35662
+ if (superseded || run.status === "cancelled") {
35663
+ captures[i] = { ...capture, state: "needs_review", reason: "capture_superseded" };
35664
+ } else if (response?.capture?.path === capture.path && response.capture.runProjectId === run.runProjectId && ["observed", "pending", "needs_review"].includes(response.capture.state)) {
35665
+ captures[i] = response.capture;
35666
+ } else {
35667
+ captures[i] = { ...capture, state: "needs_review", reason: "capture_bridge_unavailable" };
35668
+ }
35669
+ }
35670
+ if (!runs().includes(run) || terminal(run) || deps.getProjectId() !== run.runProjectId) continue;
35671
+ if (captures.length && captures.every((capture) => capture.state === "observed")) {
35672
+ const refs = deps.normalizeTrackedChanges([
35673
+ ...run.undoTrackedChanges || [],
35674
+ ...captures.flatMap((capture) => capture.refs || [])
35675
+ ]);
35676
+ if (refs.length) {
35677
+ run.undoTrackedChanges = refs;
35678
+ run.trackedChangeStatus = "pending";
35679
+ run.trackedChangeCaptures = [];
35680
+ await deps.persist();
35681
+ deps.refresh(run);
35682
+ deps.notify?.(run, "observed", { count: refs.length });
35683
+ continue;
35684
+ }
35685
+ }
35686
+ const changed = JSON.stringify(original.map(summary)) !== JSON.stringify(captures.map(summary));
35687
+ if (changed) {
35688
+ run.trackedChangeCaptures = captures;
35689
+ await deps.persist();
35690
+ if (!pending(run)) deps.notify?.(run, "needs_review", {
35691
+ reasons: captures.filter((capture) => capture.state !== "observed").map((capture) => capture.reason)
35692
+ });
35693
+ }
35694
+ }
35695
+ } finally {
35696
+ busy = false;
35697
+ if (runs().some((run) => pending(run) && run.runProjectId === deps.getProjectId())) schedule();
35698
+ }
35699
+ }
35700
+ function summary(capture) {
35701
+ return { state: capture.state, reason: capture.reason, refs: (capture.refs || []).map((ref) => ref.key) };
35702
+ }
35703
+ function dispose() {
35704
+ disposed = true;
35705
+ if (timer !== null) cancelTimer(timer);
35706
+ timer = null;
35707
+ }
35708
+ return { record, schedule, flush, dispose, buildPostFiles, hasLegacyUntrackedCheckpoint };
35709
+ }
35710
+ return { create };
35711
+ });
35712
+ }).call(globalThis, void 0, void 0, void 0);
35713
+ (function runClassicContentModule95(module, exports, require2) {
34717
35714
  (function initCodexOverleafRunSettlementPersistence(root, factory) {
34718
35715
  const api = factory();
34719
35716
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -34756,7 +35753,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34756
35753
  return Object.freeze({ create });
34757
35754
  });
34758
35755
  }).call(globalThis, void 0, void 0, void 0);
34759
- (function runClassicContentModule92(module, exports, require2) {
35756
+ (function runClassicContentModule96(module, exports, require2) {
34760
35757
  (function initPostNavigationSettlementPersistence(root, factory) {
34761
35758
  const api = factory();
34762
35759
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -34848,7 +35845,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34848
35845
  return Object.freeze({ persist, persistRequired });
34849
35846
  });
34850
35847
  }).call(globalThis, void 0, void 0, void 0);
34851
- (function runClassicContentModule93(module, exports, require2) {
35848
+ (function runClassicContentModule97(module, exports, require2) {
34852
35849
  (function initCodexOverleafPanelMaintenance() {
34853
35850
  "use strict";
34854
35851
  function create(deps = {}) {
@@ -34951,30 +35948,43 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34951
35948
  list.append(row);
34952
35949
  }
34953
35950
  }
35951
+ let storageUsageRequestId = 0;
34954
35952
  function refreshStorageUsageSummary() {
34955
35953
  const node = getSettingsPanelInstance()?.container?.querySelector("[data-storage-usage]");
34956
35954
  if (!node) {
34957
35955
  return;
34958
35956
  }
34959
- const sessionCount = Array.isArray(getState()?.sessions) ? getState().sessions.length : 0;
34960
- const runCount = Array.isArray(getState()?.runs) ? getState().runs.length : 0;
35957
+ const requestId = ++storageUsageRequestId;
35958
+ const projectId = getCurrentProjectId?.();
35959
+ const isCurrent = () => requestId === storageUsageRequestId && projectId === getCurrentProjectId?.() && getSettingsPanelInstance()?.container?.querySelector("[data-storage-usage]") === node;
35960
+ const current = getState();
35961
+ const sessionCount = Array.isArray(current?.sessions) ? current.sessions.length : 0;
35962
+ const runCount = Array.isArray(current?.runs) ? current.runs.length : 0;
34961
35963
  const counts = tx(
34962
- `${sessionCount} session(s) \xB7 ${runCount} recent run(s) kept`,
34963
- `${sessionCount} \u4E2A\u4F1A\u8BDD \xB7 \u4FDD\u7559\u6700\u8FD1 ${runCount} \u8F6E\u8FD0\u884C`
35964
+ `${sessionCount} loaded session(s) in this project \xB7 ${runCount} run(s) in the active session`,
35965
+ `\u5F53\u524D\u9879\u76EE\u5DF2\u52A0\u8F7D ${sessionCount} \u4E2A\u4F1A\u8BDD \xB7 \u5F53\u524D\u4F1A\u8BDD ${runCount} \u8F6E\u8FD0\u884C`
34964
35966
  );
35967
+ node.removeAttribute?.("data-i18n");
34965
35968
  node.textContent = counts;
34966
- const estimate = navigator.storage?.estimate?.();
34967
- if (estimate?.then) {
34968
- estimate.then((info) => {
34969
- const usage = Number(info?.usage);
34970
- if (Number.isFinite(usage) && usage >= 0) {
34971
- const mb = usage / (1024 * 1024);
34972
- const size = mb >= 1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(usage / 1024))} KB`;
34973
- node.textContent = tx(`${counts} \xB7 ~${size} used on this site`, `${counts} \xB7 \u672C\u7AD9\u70B9\u7EA6\u5360\u7528 ${size}`);
34974
- }
34975
- }).catch(() => {
34976
- });
34977
- }
35969
+ const unavailable = () => {
35970
+ if (isCurrent()) node.textContent = tx(`${counts} \xB7 Site storage estimate unavailable`, `${counts} \xB7 \u672C\u7AD9\u70B9\u5BB9\u91CF\u6682\u65E0\u6CD5\u4F30\u7B97`);
35971
+ };
35972
+ let timer;
35973
+ const estimate = Promise.resolve().then(() => navigator.storage?.estimate?.());
35974
+ const deadline = new Promise((resolve) => {
35975
+ timer = setTimeout(() => resolve(null), 2e3);
35976
+ });
35977
+ return Promise.race([estimate, deadline]).then((info) => {
35978
+ if (!isCurrent()) return;
35979
+ const usage = info?.usage;
35980
+ if (!Number.isFinite(usage) || usage < 0) {
35981
+ unavailable();
35982
+ return;
35983
+ }
35984
+ const mb = usage / (1024 * 1024);
35985
+ const size = usage === 0 ? "0 KB" : mb >= 1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(usage / 1024))} KB`;
35986
+ node.textContent = tx(`${counts} \xB7 Site total ~${size}`, `${counts} \xB7 \u672C\u7AD9\u70B9\u603B\u5360\u7528\u7EA6 ${size}`);
35987
+ }).catch(unavailable).finally(() => clearTimeout(timer));
34978
35988
  }
34979
35989
  async function clearAllHistoryWithConfirm() {
34980
35990
  if (getCurrentRunView()) {
@@ -35166,7 +36176,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35166
36176
  window.CodexOverleafPanelMaintenance = { create };
35167
36177
  })();
35168
36178
  }).call(globalThis, void 0, void 0, void 0);
35169
- (function runClassicContentModule94(module, exports, require2) {
36179
+ (function runClassicContentModule98(module, exports, require2) {
35170
36180
  (function initCodexOverleafUpdateIdle(root) {
35171
36181
  "use strict";
35172
36182
  function create(options2 = {}) {
@@ -35234,7 +36244,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35234
36244
  root.CodexOverleafUpdateIdle = Object.freeze({ create });
35235
36245
  })(typeof window !== "undefined" ? window : globalThis);
35236
36246
  }).call(globalThis, void 0, void 0, void 0);
35237
- (function runClassicContentModule95(module, exports, require2) {
36247
+ (function runClassicContentModule99(module, exports, require2) {
35238
36248
  (function initCodexOverleafUpdateNotice(root, factory) {
35239
36249
  if (typeof module === "object" && module.exports) {
35240
36250
  module.exports = factory(require2("../shared/managedUpdateProjection"));
@@ -35322,7 +36332,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35322
36332
  install: "codex-overleaf/consent-update-install",
35323
36333
  retry: "codex-overleaf/consent-update-check",
35324
36334
  later: "codex-overleaf/consent-update-later",
35325
- dismiss: "codex-overleaf/consent-update-dismiss"
36335
+ dismiss: "codex-overleaf/consent-update-dismiss",
36336
+ reload: "codex-overleaf/consent-update-reload"
35326
36337
  }[action]);
35327
36338
  } catch (error) {
35328
36339
  currentView = await reconcileAfterActionError(error, {
@@ -35435,7 +36446,10 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35435
36446
  const stateName = currentView?.state?.state || "idle";
35436
36447
  let requestedAction = "check";
35437
36448
  try {
35438
- if (stateName === "update_available") {
36449
+ if (["reload_required", "reload_tabs_required"].includes(stateName)) {
36450
+ requestedAction = "reload";
36451
+ currentView = await request("codex-overleaf/consent-update-reload");
36452
+ } else if (stateName === "update_available") {
35439
36453
  requestedAction = "install";
35440
36454
  currentView = await request("codex-overleaf/consent-update-install");
35441
36455
  } else if (["failed", "rolled_back"].includes(stateName)) {
@@ -35494,7 +36508,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35494
36508
  command.textContent = manualUpdateCommand(state);
35495
36509
  body.append(recovery, command);
35496
36510
  }
35497
- if (!["update_available", "failed", "rolled_back"].includes(state.state)) {
36511
+ if (!["update_available", "failed", "rolled_back", "reload_required", "reload_tabs_required"].includes(state.state)) {
35498
36512
  const bar = document.createElement("div");
35499
36513
  bar.className = "codex-update-notice-progress" + (progress.determinate ? "" : " is-indeterminate");
35500
36514
  bar.setAttribute("role", "progressbar");
@@ -35540,7 +36554,11 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35540
36554
  );
35541
36555
  let statusText = "";
35542
36556
  let buttonText = tx("Check for updates", "\u68C0\u67E5\u66F4\u65B0");
35543
- if (stateName === "checking") {
36557
+ if (["reload_required", "reload_tabs_required"].includes(stateName)) {
36558
+ summaryText = tx(`Installed v${latestVersion}; running extension v${currentVersion}.`, `\u5DF2\u5B89\u88C5 v${latestVersion}\uFF1B\u6269\u5C55\u5F53\u524D\u8FD0\u884C v${currentVersion}\u3002`);
36559
+ statusText = getCopy(state).detail;
36560
+ buttonText = stateName === "reload_required" ? tx("Reload extension", "\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55") : tx("Refresh Overleaf tabs", "\u5237\u65B0 Overleaf \u6807\u7B7E\u9875");
36561
+ } else if (stateName === "checking") {
35544
36562
  statusText = tx("Checking the latest stable release\u2026", "\u6B63\u5728\u68C0\u67E5\u6700\u65B0\u7A33\u5B9A\u7248\u672C\u2026");
35545
36563
  buttonText = tx("Checking\u2026", "\u68C0\u67E5\u4E2D\u2026");
35546
36564
  } else if (stateName === "update_available") {
@@ -35565,6 +36583,9 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35565
36583
  }
35566
36584
  function visibleActions(state) {
35567
36585
  if (actionInFlight) return [];
36586
+ if (["reload_required", "reload_tabs_required"].includes(state)) {
36587
+ return [{ id: "reload", label: state === "reload_required" ? tx("Reload extension", "\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55") : tx("Refresh Overleaf tabs", "\u5237\u65B0 Overleaf \u6807\u7B7E\u9875"), primary: true }];
36588
+ }
35568
36589
  if (state === "update_available") {
35569
36590
  return [
35570
36591
  { id: "later", label: tx("Later", "\u7A0D\u540E"), primary: false },
@@ -35589,6 +36610,16 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35589
36610
  const target = state.latestVersion ? "v" + state.latestVersion : "";
35590
36611
  const blocker = blockersCopy(state.blockers?.length ? state.blockers : [state.blocker]);
35591
36612
  return {
36613
+ reload_required: {
36614
+ eyebrow: tx("Installed, awaiting reload", "\u5DF2\u5B89\u88C5\uFF0C\u7B49\u5F85\u52A0\u8F7D"),
36615
+ title: target,
36616
+ detail: state.message || tx("The new files are installed. Reload the extension when Overleaf is saved and idle.", "\u65B0\u7248\u6587\u4EF6\u5DF2\u5B89\u88C5\uFF0C\u8BF7\u5728 Overleaf \u4FDD\u5B58\u5E76\u7A7A\u95F2\u540E\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u3002")
36617
+ },
36618
+ reload_tabs_required: {
36619
+ eyebrow: tx("Updated runtime is ready", "\u65B0\u7248\u8FD0\u884C\u7EC4\u4EF6\u5DF2\u5C31\u7EEA"),
36620
+ title: target,
36621
+ detail: state.message || tx("Refresh the saved, idle Overleaf tabs to load the updated panel.", "\u5237\u65B0\u5DF2\u4FDD\u5B58\u4E14\u7A7A\u95F2\u7684 Overleaf \u6807\u7B7E\u9875\u4EE5\u52A0\u8F7D\u65B0\u7248\u9762\u677F\u3002")
36622
+ },
35592
36623
  update_available: {
35593
36624
  eyebrow: tx("Update available", "\u53D1\u73B0\u65B0\u7248\u672C"),
35594
36625
  title: target,
@@ -35678,7 +36709,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35678
36709
  return Object.freeze({ mount });
35679
36710
  });
35680
36711
  }).call(globalThis, void 0, void 0, void 0);
35681
- (function runClassicContentModule96(module, exports, require2) {
36712
+ (function runClassicContentModule100(module, exports, require2) {
35682
36713
  (function initCodexOverleafContentRuntime(root) {
35683
36714
  "use strict";
35684
36715
  function init(dependencies2) {
@@ -35731,6 +36762,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35731
36762
  "mirror.sync",
35732
36763
  "mirror.patchFiles",
35733
36764
  "mirror.confirmWriteback",
36765
+ "mirror.invalidate",
35734
36766
  "mirror.scanSensitive",
35735
36767
  "codex.history.clearPlugin",
35736
36768
  "skills.list",
@@ -36072,6 +37104,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36072
37104
  const runTimelineView = Modules.RunTimelineView.create({
36073
37105
  RunGuidanceView: Modules.RunGuidanceView,
36074
37106
  RunResultActions: runResultActions,
37107
+ RunScrollLayout: Modules.RunScrollLayout,
37108
+ RunFailureNotice: Modules.RunFailureNotice,
36075
37109
  tr,
36076
37110
  tx,
36077
37111
  getLocale,
@@ -36229,6 +37263,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36229
37263
  RUN_SNAPSHOT_ZIP_TIMEOUT_MS,
36230
37264
  getState: () => state,
36231
37265
  getCurrentRunView: () => currentRunView,
37266
+ appendRunRecordEvent,
36232
37267
  onMirrorRefreshSettled: runSettlementPersistence.settleMirrorRefresh
36233
37268
  });
36234
37269
  const {
@@ -36430,6 +37465,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36430
37465
  },
36431
37466
  saveState,
36432
37467
  saveStateSoon,
37468
+ onProjectFilesReady: refreshHistoricalProjectReferences,
36433
37469
  updateActiveSession,
36434
37470
  callPageBridge,
36435
37471
  getCurrentProjectId,
@@ -36473,6 +37509,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36473
37509
  }
36474
37510
  });
36475
37511
  let panel = null;
37512
+ let probeStatusSnapshot = null;
36476
37513
  let panelRendererInstance = null;
36477
37514
  let sessionPanelInstance = null;
36478
37515
  let settingsPanelInstance = null;
@@ -36646,7 +37683,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36646
37683
  });
36647
37684
  syncOtWarmMirrorController().catch((error) => {
36648
37685
  updateOtStatusDisplay("unavailable");
36649
- appendPlainLog(tx(`Failed to sync experimental OT warm mirror: ${error.message}`, `\u540C\u6B65\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF\u5931\u8D25\uFF1A${error.message}`));
37686
+ appendPlainLog(tx(`Failed to sync OT warm mirror: ${error.message}`, `\u540C\u6B65 OT \u9884\u70ED\u955C\u50CF\u5931\u8D25\uFF1A${error.message}`));
36650
37687
  });
36651
37688
  await refreshProbe({ quiet: true });
36652
37689
  }
@@ -36869,6 +37906,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36869
37906
  onProvidersOpen: () => providerSettingsCoordinator.open(),
36870
37907
  onClearAllHistory: () => clearAllHistoryWithConfirm(),
36871
37908
  onHistoryOpen: () => renderAuditHistoryPanel(),
37909
+ onStorageOpen: () => refreshStorageUsageSummary(),
36872
37910
  onHistoryFilter: () => applyAuditHistoryFilter(),
36873
37911
  // Experimental OT mirror: a single visible switch whose click is
36874
37912
  // intercepted so the confirm-before-enable flow still runs.
@@ -37095,6 +38133,17 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
37095
38133
  updateModelDisplay();
37096
38134
  renderSessionList();
37097
38135
  renderContextSelection();
38136
+ const probeStatus = panel.querySelector("[data-probe-status]");
38137
+ if (probeStatus?.dataset.refreshing === "true") probeStatus.textContent = tr("refreshProbeLoading");
38138
+ else if (probeStatus && probeStatusSnapshot?.projectId === getCurrentProjectId()) {
38139
+ const { failed, probe, userInitiated } = probeStatusSnapshot;
38140
+ const text = failed ? tr("refreshProbeFailed") : formatProbeStatusBar(probe);
38141
+ probeStatus.textContent = userInitiated && !failed ? tr("refreshProbeDone", { status: text }) : text;
38142
+ if (!failed) updateExistingProbeNotice(probe);
38143
+ }
38144
+ updateSkillsEntrySummary();
38145
+ providerSettingsCoordinator.renderSummary();
38146
+ if (settingsPanelInstance?.container?.querySelector("[data-storage-card]")?.open) refreshStorageUsageSummary();
37098
38147
  }
37099
38148
  function setElementTitleAndAria(selector, title, ariaLabel) {
37100
38149
  const element = panel?.querySelector(selector);
@@ -37817,7 +38866,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
37817
38866
  }
37818
38867
  }
37819
38868
  await saveState();
37820
- applyStateToPanel();
38869
+ applyStateToPanel({ preserveScroll: true });
37821
38870
  } catch (persistenceError) {
37822
38871
  appendRunEvent({
37823
38872
  title: tx("Codex result was generated, but saving local session history failed.", "Codex \u7ED3\u679C\u5DF2\u751F\u6210\uFF0C\u4F46\u4FDD\u5B58\u672C\u5730\u4F1A\u8BDD\u8BB0\u5F55\u5931\u8D25\u3002"),
@@ -37887,15 +38936,19 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
37887
38936
  setRunning(false);
37888
38937
  nativeChannel.clearActiveRequest();
37889
38938
  stopRunElapsedTick();
37890
- if (isExperimentalOtEnabled()) {
37891
- await resumeOtWarmMirror("run-settled");
37892
- }
37893
38939
  await flushQueuedSaveState().catch(() => {
37894
38940
  });
37895
38941
  currentRunView = null;
37896
38942
  runCancellationRequested = false;
37897
38943
  runCancellationController = null;
37898
38944
  activeTurnControl.release(settlement.requestId);
38945
+ if (isExperimentalOtEnabled()) {
38946
+ try {
38947
+ await resumeOtWarmMirror("run-settled");
38948
+ } catch (_error) {
38949
+ updateOtStatusDisplay("unavailable");
38950
+ }
38951
+ }
37899
38952
  if (settlement.requestId) {
37900
38953
  activeTurnControl.acknowledge(settlement.requestId).catch(() => {
37901
38954
  });
@@ -39948,6 +41001,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39948
41001
  return { safe, skipped };
39949
41002
  }
39950
41003
  async function refreshProbe(options2 = {}) {
41004
+ const projectId = getCurrentProjectId();
39951
41005
  const userInitiated = options2.userInitiated === true;
39952
41006
  if (userInitiated) {
39953
41007
  setRefreshProbeLoading(true);
@@ -39962,6 +41016,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39962
41016
  const probe = await callPageBridge("probe", {
39963
41017
  manualOverride: state?.requireReviewing === false
39964
41018
  });
41019
+ if (projectId !== getCurrentProjectId()) return null;
41020
+ probeStatusSnapshot = { projectId, probe, userInitiated };
39965
41021
  const status = panel?.querySelector("[data-probe-status]");
39966
41022
  if (status) {
39967
41023
  status.textContent = userInitiated ? tr("refreshProbeDone", { status: formatProbeStatusBar(probe) }) : formatProbeStatusBar(probe);
@@ -39978,6 +41034,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39978
41034
  if (!userInitiated) {
39979
41035
  throw error;
39980
41036
  }
41037
+ if (projectId !== getCurrentProjectId()) return null;
41038
+ probeStatusSnapshot = { projectId, failed: true };
39981
41039
  const status = panel?.querySelector("[data-probe-status]");
39982
41040
  if (status) {
39983
41041
  status.textContent = tr("refreshProbeFailed");
@@ -40459,6 +41517,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40459
41517
  title: session.title || "",
40460
41518
  titleSource: session.titleSource || "auto",
40461
41519
  focusFiles: session.focusFiles || [],
41520
+ projectReferenceFiles: session.projectReferenceFiles || [],
40462
41521
  codexThreadId: session.codexThreadId || "",
40463
41522
  createdAt: session.createdAt,
40464
41523
  updatedAt: session.updatedAt,
@@ -40770,15 +41829,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40770
41829
  renderSessionList();
40771
41830
  }
40772
41831
  function getRenderedModelEntries() {
40773
- return Array.from(panel?.querySelector("[data-model]")?.options || []).map((option) => ({
40774
- id: option.value,
40775
- label: option.textContent,
40776
- reasoningEfforts: (option.dataset.reasoningEfforts || "").split(",").filter(Boolean),
40777
- defaultReasoningEffort: option.dataset.defaultReasoningEffort || "",
40778
- reasoningPresentation: option.dataset.reasoningPresentation || "",
40779
- speedTiers: (option.dataset.speedTiers || "standard").split(",").filter(Boolean),
40780
- defaultSpeedTier: option.dataset.defaultSpeedTier || "standard"
40781
- }));
41832
+ return Modules.ModelPickerSupport.getRenderedModelEntries(panel);
40782
41833
  }
40783
41834
  async function selectMode(mode) {
40784
41835
  if (!["ask", "auto"].includes(mode)) {
@@ -40864,7 +41915,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40864
41915
  state = updateActiveSession(state, { task: "" });
40865
41916
  saveStateSoon();
40866
41917
  }
40867
- function applyStateToPanel() {
41918
+ function applyStateToPanel(options2 = {}) {
40868
41919
  applyPanelTheme(getThemePreference());
40869
41920
  if ((state.providerId || "builtin") === "builtin" && !modelSelectHasOption(state.model)) {
40870
41921
  renderModelOptions(getModelCatalog().FALLBACK_MODELS, state.model);
@@ -40898,7 +41949,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40898
41949
  syncModeControls();
40899
41950
  applySessionLabel();
40900
41951
  renderSessionList();
40901
- renderRunHistory();
41952
+ renderRunHistory(options2);
41953
+ getTrackedChangeCaptureController().schedule();
40902
41954
  renderContextSelection();
40903
41955
  renderContextSummary();
40904
41956
  if (!composerAttachmentsRestoredOnce && Array.isArray(state.composerAttachments) && state.composerAttachments.length && !composerAttachmentController.getAttachmentsForRun?.().length && typeof composerAttachmentController.restorePersistedAttachments === "function") {
@@ -40977,6 +42029,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40977
42029
  undoStatus: "",
40978
42030
  queueItemId,
40979
42031
  nativeRequestId: "",
42032
+ codexThreadId: "",
40980
42033
  codexTurnId: "",
40981
42034
  nativeEventSeq: 0,
40982
42035
  executionSnapshot: executionSnapshot ? RunExecutionSnapshot.cloneSnapshot(executionSnapshot) : void 0,
@@ -41115,6 +42168,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41115
42168
  currentRunView.activeTurn = { threadId, turnId };
41116
42169
  const record = findRunRecord(currentRunView.recordId, currentRunView.sessionId);
41117
42170
  if (record) {
42171
+ record.codexThreadId = threadId;
41118
42172
  record.codexTurnId = turnId;
41119
42173
  record.nativeEventSeq = Math.max(Number(record.nativeEventSeq || 0), Number(journalSeq || 0));
41120
42174
  }
@@ -41428,6 +42482,12 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41428
42482
  }
41429
42483
  return files;
41430
42484
  }
42485
+ function refreshHistoricalProjectReferences(project) {
42486
+ if (!project?.id || String(project.id) !== getCurrentProjectId()) return;
42487
+ state = updateActiveSession(state, { projectReferenceFiles: captureProjectReferenceFiles(project) });
42488
+ saveStateSoon();
42489
+ if (!currentRunView && !trackedChangeInFlight.size) renderRunHistory({ preserveScroll: true });
42490
+ }
41431
42491
  function persistCurrentProjectReferenceFiles(project) {
41432
42492
  const projectFiles = captureProjectReferenceFiles(project);
41433
42493
  if (currentRunView) {
@@ -41542,6 +42602,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41542
42602
  runProjectId: getRunProjectIdForWriteback(run)
41543
42603
  });
41544
42604
  const undoApplied = isUndoResultEffectivelyApplied(run, result);
42605
+ writebackOrchestrator.invalidateMirrorAfterUndo(runId, getRunProjectIdForWriteback(run), result);
41545
42606
  appendUndoReviewingPolicyEvent(runId, result.reviewingPolicy);
41546
42607
  appendRunRecordEvent(runId, {
41547
42608
  title: tr("undoResult", { applied: result.applied?.length || 0, skipped: result.skipped?.length || 0 }),
@@ -41594,11 +42655,14 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41594
42655
  detail: { [tr("detailWillUndo")]: trackedUndo ? formatTrackedChangeFiles(run.undoTrackedChanges) : formatTrackedUndoFiles(run) }
41595
42656
  });
41596
42657
  let result;
42658
+ let trackedUndoPostFilesAtDispatch;
41597
42659
  try {
42660
+ trackedUndoPostFilesAtDispatch = buildTrackedUndoPostFiles(run);
41598
42661
  result = await callPageBridge("rejectTrackedChanges", {
41599
42662
  trackedChanges: run.undoTrackedChanges || [],
41600
42663
  expectedFiles: run.undoExpectedFiles || [],
41601
- postFiles: buildTrackedUndoPostFiles(run),
42664
+ postFiles: trackedUndoPostFilesAtDispatch,
42665
+ untrackedUndo: run.executionSnapshot ? run.executionSnapshot.requireReviewing === false && ["submitted", "legacy-captured"].includes(run.executionSnapshot.source) : getTrackedChangeCaptureController().hasLegacyUntrackedCheckpoint(run),
41602
42666
  // Welcome-panel + write-guard:
41603
42667
  // bind the reject to the run's original project. If the user has
41604
42668
  // navigated away the page-side guard refuses with
@@ -41610,6 +42674,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41610
42674
  trackedChangeInFlight.delete(runId);
41611
42675
  }
41612
42676
  }
42677
+ writebackOrchestrator.invalidateMirrorAfterUndo(runId, getRunProjectIdForWriteback(run), result);
41613
42678
  appendRunRecordEvent(runId, {
41614
42679
  title: trackedUndo ? tr("undoTrackedResult", { applied: result.applied?.length || 0, skipped: result.skipped?.length || 0 }) : tr("undoNativeResult", { applied: result.applied?.length || 0, skipped: result.skipped?.length || 0 }),
41615
42680
  status: result.skipped?.length ? "failed" : "completed",
@@ -41627,7 +42692,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41627
42692
  });
41628
42693
  if (lifecycleReject) {
41629
42694
  WritebackSettlement.attachUndoNotVerifiedFailure(run, result, {
41630
- buildFailure: buildContentFailure
42695
+ buildFailure: buildContentFailure,
42696
+ postFiles: trackedUndoPostFilesAtDispatch
41631
42697
  });
41632
42698
  applyTrackedChangeSettlement(runId, "reject", result);
41633
42699
  return;
@@ -41807,6 +42873,28 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41807
42873
  function attachVerifiedContentToOperation(operation, result) {
41808
42874
  return WritebackSettlement.attachVerifiedContentToOperation(operation, result);
41809
42875
  }
42876
+ let trackedChangeCaptureController = null;
42877
+ function getTrackedChangeCaptureController() {
42878
+ return trackedChangeCaptureController || (trackedChangeCaptureController = Modules.TrackedChangeCaptureController.create({
42879
+ getRuns: () => (state.sessions || []).flatMap((session) => session.runs || []),
42880
+ getProjectId: () => window.location.pathname.match(/^\/project\/([^/]+)/)?.[1] || "",
42881
+ buildExpectedFilesAfterOperations,
42882
+ normalizeTrackedChanges: normalizeApplyTrackedChanges,
42883
+ callPageBridge,
42884
+ persist: () => flushQueuedSaveState({ preserveRunActionPayload: true }),
42885
+ refresh: (run) => refreshRunCardControls(run.id),
42886
+ notify: (run, kind, detail) => appendRunRecordEvent(run.id, {
42887
+ title: {
42888
+ pending: tx("Files written; confirming tracked changes.", "\u6587\u4EF6\u5DF2\u5199\u5165\uFF0C\u6B63\u5728\u786E\u8BA4\u7559\u75D5\u8BB0\u5F55\u3002"),
42889
+ observed: tx("Tracked changes confirmed; Accept is available.", "\u7559\u75D5\u8BB0\u5F55\u5DF2\u786E\u8BA4\uFF0C\u53EF\u4EE5\u63A5\u53D7\u6539\u52A8\u3002"),
42890
+ needs_review: tx("Tracked changes could not be safely attributed. Review them in Overleaf; Undo is preserved.", "\u7559\u75D5\u5F52\u5C5E\u5C1A\u672A\u786E\u8BA4\uFF0C\u8BF7\u5728 Overleaf \u5BA1\u9605\uFF1B\u539F\u6709\u64A4\u9500\u80FD\u529B\u5DF2\u4FDD\u7559\u3002")
42891
+ }[kind],
42892
+ status: "info",
42893
+ detail
42894
+ }),
42895
+ onError: (error) => appendPlainLog(tx("Tracked-change confirmation unavailable: ", "\u7559\u75D5\u786E\u8BA4\u6682\u4E0D\u53EF\u7528\uFF1A") + error.message)
42896
+ }));
42897
+ }
41810
42898
  function recordUndoFromApply(project, applyResult) {
41811
42899
  const appliedEntries = getAppliedEntries(applyResult);
41812
42900
  if (!currentRunView?.recordId || !appliedEntries.length) {
@@ -41843,6 +42931,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41843
42931
  if (combinedTrackedChanges.length) {
41844
42932
  record.trackedChangeStatus = "pending";
41845
42933
  }
42934
+ getTrackedChangeCaptureController().record(record, applyResult?.trackedChangeCaptures);
41846
42935
  refreshRunCardControls(record.id);
41847
42936
  if (combinedTrackedChanges.length) {
41848
42937
  appendRunEvent({
@@ -41904,28 +42993,10 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
41904
42993
  return WritebackSettlement.normalizeApplyTrackedChanges(changes);
41905
42994
  }
41906
42995
  function selectExpectedFilesForTrackedUndo(project, operations = [], trackedChanges = [], previousExpectedFiles = []) {
41907
- return WritebackSettlement.selectExpectedFilesForTrackedUndo(
41908
- project,
41909
- operations,
41910
- trackedChanges,
41911
- previousExpectedFiles
41912
- );
42996
+ return WritebackSettlement.selectExpectedFilesForTrackedUndo(project, operations, trackedChanges, previousExpectedFiles);
41913
42997
  }
41914
42998
  function buildTrackedUndoPostFiles(run) {
41915
- const expectedFiles = Array.isArray(run?.undoExpectedFiles) ? run.undoExpectedFiles : [];
41916
- const appliedOperations = Array.isArray(run?.appliedOperations) ? run.appliedOperations : [];
41917
- if (!expectedFiles.length || !appliedOperations.length) {
41918
- return [];
41919
- }
41920
- const postFilesByPath = buildExpectedFilesAfterOperations(
41921
- { files: expectedFiles },
41922
- appliedOperations
41923
- );
41924
- const expectedPaths = new Set(expectedFiles.map((file) => file?.path).filter(Boolean));
41925
- return Array.from(postFilesByPath.entries()).filter(([path, content]) => expectedPaths.has(path) && typeof content === "string").map(([path, content]) => ({
41926
- path,
41927
- content
41928
- }));
42999
+ return getTrackedChangeCaptureController().buildPostFiles(run);
41929
43000
  }
41930
43001
  function hasTrackedEditorUndo(run) {
41931
43002
  return buildTrackedUndoPostFiles(run).length > 0;
@@ -42502,7 +43573,7 @@ Reason: ${item.reason || "No reason provided"}`),
42502
43573
  root.CodexOverleafContentRuntime = { init };
42503
43574
  })(typeof window !== "undefined" ? window : globalThis);
42504
43575
  }).call(globalThis, void 0, void 0, void 0);
42505
- (function runClassicContentModule97(module, exports, require2) {
43576
+ (function runClassicContentModule101(module, exports, require2) {
42506
43577
  (function initCodexOverleafLegacyGlobalRegistry(root, factory) {
42507
43578
  "use strict";
42508
43579
  const api = factory();
@@ -42560,6 +43631,8 @@ Reason: ${item.reason || "No reason provided"}`),
42560
43631
  "RunInputQueue",
42561
43632
  "RunQueueScheduler",
42562
43633
  "RunResultActions",
43634
+ "RunScrollLayout",
43635
+ "RunFailureNotice",
42563
43636
  "RunSettlementPersistence",
42564
43637
  "RunTimelineView",
42565
43638
  "ScopedPersistenceCoordinator",
@@ -42576,6 +43649,7 @@ Reason: ${item.reason || "No reason provided"}`),
42576
43649
  "StorageMigration",
42577
43650
  "Summary",
42578
43651
  "Theme",
43652
+ "TrackedChangeCaptureController",
42579
43653
  "UndoOperations",
42580
43654
  "UpdateIdle",
42581
43655
  "UpdateNotice",