codex-overleaf-link 2.2.0 → 2.3.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 (72) hide show
  1. package/README.md +25 -23
  2. package/extension/bootstrap/manifest.template.json +1 -1
  3. package/extension/runtime-manifest.json +5 -71
  4. package/extension/src/background.js +53 -69
  5. package/extension/src/backgroundUpdateCoordinator.js +128 -66
  6. package/extension/src/content/applyResultFormatters.js +2 -3
  7. package/extension/src/content/contentRuntime.js +650 -1449
  8. package/extension/src/content/contextTray.js +4 -6
  9. package/extension/src/content/diagnosticsController.js +1 -2
  10. package/extension/src/content/diffReviewPanel.js +14 -13
  11. package/extension/src/content/generated/content.bundle.js +41370 -0
  12. package/extension/src/content/generated/content.bundle.meta.json +102 -0
  13. package/extension/src/content/legacyGlobalRegistry.js +129 -0
  14. package/extension/src/content/markdownText.js +5 -7
  15. package/extension/src/content/modelPicker.js +1 -1
  16. package/extension/src/content/modelPickerSupport.js +10 -5
  17. package/extension/src/content/moduleRegistryKernel.js +125 -0
  18. package/extension/src/content/nativeCompatibilityController.js +313 -0
  19. package/extension/src/content/otWarmMirror.js +7 -5
  20. package/extension/src/content/pageBridgeClient.js +224 -0
  21. package/extension/src/content/panelMaintenance.js +8 -3
  22. package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
  23. package/extension/src/content/projectProviderSelection.js +4 -12
  24. package/extension/src/content/projectSessionCleanup.js +3 -3
  25. package/extension/src/content/providerSettingsCoordinator.js +59 -26
  26. package/extension/src/content/providerSettingsDialog.js +30 -18
  27. package/extension/src/content/recentProjects.js +15 -23
  28. package/extension/src/content/runGuidanceController.js +1 -1
  29. package/extension/src/content/runQueueScheduler.js +129 -29
  30. package/extension/src/content/runSettlementPersistence.js +51 -0
  31. package/extension/src/content/runTimelineView.js +11 -8
  32. package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
  33. package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
  34. package/extension/src/content/scopedPersistencePanelState.js +111 -0
  35. package/extension/src/content/sessionManager.js +15 -12
  36. package/extension/src/content/sessionPersistence.js +67 -8
  37. package/extension/src/content/updateNotice.js +10 -4
  38. package/extension/src/content/writebackOrchestrator.js +91 -23
  39. package/extension/src/contentScript.js +5 -1
  40. package/extension/src/page/trackedChangesLifecycle.js +6 -1
  41. package/extension/src/pageBridge.js +47 -98
  42. package/extension/src/shared/agentTranscript.js +8 -5
  43. package/extension/src/shared/compatibility.js +1 -1
  44. package/extension/src/shared/governanceRules.js +1 -1
  45. package/extension/src/shared/managedUpdateProjection.js +337 -0
  46. package/extension/src/shared/nativeRequestIdentity.js +60 -0
  47. package/extension/src/shared/otText.js +0 -19
  48. package/extension/src/shared/pageRpcContract.js +136 -0
  49. package/extension/src/shared/runExecutionSnapshot.js +177 -0
  50. package/extension/src/shared/runExecutionSnapshotCodec.js +168 -0
  51. package/extension/src/shared/runInputQueue.js +81 -34
  52. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
  53. package/extension/src/shared/scopedPersistenceQueuePolicy.js +181 -0
  54. package/extension/src/shared/scopedPersistenceTransaction.js +238 -0
  55. package/extension/src/shared/sessionState.js +64 -48
  56. package/extension/src/shared/settlementFacts.js +351 -0
  57. package/extension/src/shared/storageDb.js +19 -5
  58. package/extension/src/shared/storageMigration.js +275 -61
  59. package/extension/src/shared/storageSessionClaims.js +60 -0
  60. package/extension/src/shared/updateConsent.js +4 -0
  61. package/extension/src/shared/updateRevocationIntent.js +109 -0
  62. package/extension/src/shared/writebackEvidenceProjection.js +150 -0
  63. package/extension/src/shared/writebackOperationEvidence.js +143 -0
  64. package/extension/src/shared/writebackSettlement.js +705 -0
  65. package/native-host/src/codexRuntimeIdentity.js +1 -1
  66. package/native-host/src/codexSessionRunner.js +7 -13
  67. package/native-host/src/index.js +3 -69
  68. package/native-host/src/managedInstall.js +50 -8
  69. package/native-host/src/mirrorWorkspace.js +15 -8
  70. package/native-host/src/nativeTransportEnvelope.js +148 -0
  71. package/native-host/src/taskRunnerRuntime.js +3 -3
  72. package/package.json +13 -5
@@ -0,0 +1,150 @@
1
+ (function initCodexOverleafWritebackEvidenceProjection(root, factory) {
2
+ const api = factory();
3
+ if (typeof module === 'object' && module.exports) module.exports = api;
4
+ if (root) root.CodexOverleafWritebackEvidenceProjection = api;
5
+ })(typeof window !== 'undefined' ? window : globalThis, function writebackEvidenceProjectionFactory() {
6
+ 'use strict';
7
+
8
+ function aggregateFileSettlements(operationEvidence = [], readbacks = []) {
9
+ const byPath = new Map();
10
+ const byRelatedPath = new Map();
11
+ for (const evidence of operationEvidence) {
12
+ const key = evidence.path || evidence.targetPath || 'unknown file';
13
+ if (!byPath.has(key)) {
14
+ byPath.set(key, {
15
+ path: key,
16
+ operationIds: [],
17
+ relatedPaths: [],
18
+ applied: 'none',
19
+ documentEffect: 'unchanged',
20
+ readBack: 'unknown',
21
+ recoveryKind: '',
22
+ failureCodes: [],
23
+ operationCount: 0,
24
+ appliedCount: 0,
25
+ failedCount: 0
26
+ });
27
+ }
28
+ const file = byPath.get(key);
29
+ file.operationCount++;
30
+ file.operationIds.push(evidence.operationId);
31
+ if (evidence.targetPath && evidence.targetPath !== key && !file.relatedPaths.includes(evidence.targetPath)) {
32
+ file.relatedPaths.push(evidence.targetPath);
33
+ byRelatedPath.set(evidence.targetPath, file);
34
+ }
35
+ if (evidence.collection === 'applied' && evidence.ok !== false) file.appliedCount++;
36
+ if (evidence.collection !== 'applied' || evidence.ok === false) file.failedCount++;
37
+ if (evidence.changedDocument) file.documentEffect = 'changed';
38
+ else if (evidence.mayHaveMutated && file.documentEffect !== 'changed') file.documentEffect = 'possible';
39
+ for (const code of evidence.failureCodes) {
40
+ if (!file.failureCodes.includes(code)) file.failureCodes.push(code);
41
+ }
42
+ }
43
+ for (const readback of normalizeFileReadbackEvidence(readbacks)) {
44
+ const file = byPath.get(readback.path) || byRelatedPath.get(readback.path);
45
+ if (!file) continue;
46
+ const deletesPath = operationEvidence.some(evidence =>
47
+ evidence.kind === 'delete' && evidence.path === readback.path
48
+ );
49
+ file.readBack = deletesPath && readback.state === 'missing' ? 'match' : readback.state;
50
+ if (file.readBack === 'mismatch') {
51
+ file.documentEffect = file.documentEffect === 'unchanged' ? 'possible' : file.documentEffect;
52
+ if (!file.failureCodes.includes('write_observed_mismatch')) {
53
+ file.failureCodes.push('write_observed_mismatch');
54
+ }
55
+ }
56
+ }
57
+ return Array.from(byPath.values()).map(file => {
58
+ file.applied = file.appliedCount === file.operationCount
59
+ ? 'complete'
60
+ : file.appliedCount > 0
61
+ ? 'partial'
62
+ : file.failedCount > 0
63
+ ? 'failed'
64
+ : 'none';
65
+ delete file.operationCount;
66
+ delete file.appliedCount;
67
+ delete file.failedCount;
68
+ return file;
69
+ }).sort((left, right) => left.path.localeCompare(right.path));
70
+ }
71
+
72
+ function normalizeFileReadbackEvidence(value = []) {
73
+ return (Array.isArray(value) ? value : [])
74
+ .filter(item => item && typeof item.path === 'string')
75
+ .map(item => ({
76
+ path: item.path,
77
+ state: ['match', 'mismatch', 'missing', 'unknown'].includes(item.state)
78
+ ? item.state
79
+ : 'unknown'
80
+ }));
81
+ }
82
+
83
+ function deriveApplyResultReadbacks(applyResult = {}) {
84
+ const readbacksByPath = new Map();
85
+ const record = (path, state) => {
86
+ if (typeof path !== 'string' || !path) return;
87
+ const current = readbacksByPath.get(path);
88
+ if (current === 'mismatch') return;
89
+ if (state === 'mismatch' || !current) readbacksByPath.set(path, state);
90
+ };
91
+ for (const entry of [
92
+ ...(Array.isArray(applyResult?.applied) ? applyResult.applied : []),
93
+ ...(Array.isArray(applyResult?.skipped) ? applyResult.skipped : [])
94
+ ]) {
95
+ const operation = entry?.operation || {};
96
+ const result = entry?.result || {};
97
+ const code = result?.failure?.code || result?.code || '';
98
+ const path = operation.type === 'rename' || operation.type === 'move'
99
+ ? operation.to || operation.path
100
+ : operation.path;
101
+ if (code === 'write_observed_mismatch') {
102
+ record(path, 'mismatch');
103
+ } else if (result.verified === true) {
104
+ record(path, operation.type === 'delete' ? 'missing' : 'match');
105
+ }
106
+ }
107
+ return Array.from(readbacksByPath, ([path, state]) => ({ path, state }));
108
+ }
109
+
110
+ function deriveDocumentEffect(operationEvidence = []) {
111
+ if (operationEvidence.some(entry => entry.changedDocument === true)) return 'changed';
112
+ if (operationEvidence.some(entry => entry.mayHaveMutated === true)) return 'possibly-changed';
113
+ if (operationEvidence.length) return 'unchanged';
114
+ return 'unknown';
115
+ }
116
+
117
+ function deriveAppliedEvidence(operations = [], operationEvidence = []) {
118
+ const plannedCount = Array.isArray(operations) ? operations.length : 0;
119
+ const applied = operationEvidence.filter(entry => entry.collection === 'applied');
120
+ const skipped = operationEvidence.filter(entry => entry.collection === 'skipped');
121
+ const successful = applied.filter(entry => entry.ok !== false);
122
+ if (!plannedCount && !operationEvidence.length) return 'none';
123
+ if (!successful.length && (skipped.length || applied.some(entry => entry.ok === false))) return 'none';
124
+ if (successful.length && (skipped.length || applied.some(entry => entry.ok === false))) return 'partial';
125
+ if (successful.length && (!plannedCount || successful.length >= plannedCount)) return 'complete';
126
+ return successful.length ? 'partial' : 'unknown';
127
+ }
128
+
129
+ function deriveReadbackEvidence(fileSettlements = [], readbacks = []) {
130
+ if (!Array.isArray(readbacks) || !readbacks.length) return 'not-attempted';
131
+ const states = fileSettlements.map(file => file.readBack);
132
+ if (states.includes('mismatch')) return 'mismatch';
133
+ const exactCount = states.filter(state => state === 'match').length;
134
+ const unavailableCount = states.filter(state =>
135
+ state === 'missing' || state === 'unknown'
136
+ ).length;
137
+ if (exactCount && unavailableCount) return 'partial';
138
+ if (exactCount && exactCount === states.length) return 'exact';
139
+ return 'unavailable';
140
+ }
141
+
142
+ return Object.freeze({
143
+ aggregateFileSettlements,
144
+ deriveAppliedEvidence,
145
+ deriveApplyResultReadbacks,
146
+ deriveDocumentEffect,
147
+ deriveReadbackEvidence,
148
+ normalizeFileReadbackEvidence
149
+ });
150
+ });
@@ -0,0 +1,143 @@
1
+ (function initCodexOverleafWritebackOperationEvidence(root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory();
4
+ } else {
5
+ root.CodexOverleafModuleRegistry.define('WritebackOperationEvidence', [], factory);
6
+ }
7
+ })(typeof window !== 'undefined' ? window : globalThis, function writebackOperationEvidenceFactory() {
8
+ 'use strict';
9
+
10
+ function normalizeOperationEvidence(operations = [], applyResult = {}) {
11
+ const planned = Array.isArray(operations) ? operations : [];
12
+ const entries = [];
13
+ const claimedPlanned = new Set();
14
+ let fallbackCursor = 0;
15
+
16
+ const operationKey = operation => {
17
+ if (!operation || typeof operation !== 'object') return '';
18
+ return normalizeText(operation.id)
19
+ || [
20
+ normalizeText(operation.type),
21
+ normalizeText(operation.path || operation.from),
22
+ normalizeText(operation.to || operation.destinationPath),
23
+ Number.isFinite(Number(operation.sequence)) ? Number(operation.sequence) : ''
24
+ ].join(':');
25
+ };
26
+
27
+ const claimPlannedOperation = explicitOperation => {
28
+ if (explicitOperation && typeof explicitOperation === 'object') {
29
+ const explicitKey = operationKey(explicitOperation);
30
+ const matchedIndex = planned.findIndex((operation, plannedIndex) =>
31
+ !claimedPlanned.has(plannedIndex) && operationKey(operation) === explicitKey
32
+ );
33
+ if (matchedIndex >= 0) claimedPlanned.add(matchedIndex);
34
+ return explicitOperation;
35
+ }
36
+ while (claimedPlanned.has(fallbackCursor)) fallbackCursor++;
37
+ if (fallbackCursor >= planned.length) return {};
38
+ const operation = planned[fallbackCursor];
39
+ claimedPlanned.add(fallbackCursor);
40
+ fallbackCursor++;
41
+ return operation;
42
+ };
43
+
44
+ const push = (entry, collection, index) => {
45
+ const operation = claimPlannedOperation(entry?.operation);
46
+ const result = entry?.result || entry || {};
47
+ const failure = normalizeFailure(result, operation);
48
+ const changedDocument = result.changedDocument === true
49
+ || (collection === 'applied' && result.ok !== false);
50
+ const mayHaveMutated = changedDocument
51
+ || result.mayHaveMutated === true
52
+ || failure?.changedDocument === true;
53
+ entries.push({
54
+ operationId: normalizeText(operation.id) || `${collection}:${index}:${operation.type || 'unknown'}:${operation.path || operation.to || ''}`,
55
+ sequence: Number.isFinite(Number(operation.sequence)) ? Number(operation.sequence) : index,
56
+ path: normalizeText(operation.path || operation.from),
57
+ targetPath: normalizeText(operation.to || operation.destinationPath),
58
+ kind: normalizeText(operation.type) || 'unknown',
59
+ collection,
60
+ ok: result.ok !== false,
61
+ changedDocument,
62
+ mayHaveMutated,
63
+ failureCodes: failure?.code ? [failure.code] : [],
64
+ failures: failure ? [failure] : [],
65
+ trackedChanges: normalizeTrackedChanges(result.trackedChanges || entry?.trackedChanges),
66
+ undoIds: normalizeStringArray(result.undoIds || entry?.undoIds),
67
+ baseAvailable: operation.baseContent !== undefined || operation.expected !== undefined,
68
+ expectedAvailable: operation.expected !== undefined || result.verifiedContent !== undefined
69
+ });
70
+ };
71
+ (Array.isArray(applyResult?.applied) ? applyResult.applied : [])
72
+ .forEach((entry, index) => push(entry, 'applied', index));
73
+ (Array.isArray(applyResult?.skipped) ? applyResult.skipped : [])
74
+ .forEach((entry, index) => push(entry, 'skipped', index));
75
+ planned.forEach((operation, index) => {
76
+ if (claimedPlanned.has(index)) return;
77
+ const path = normalizeText(operation?.path || operation?.from || operation?.to);
78
+ push({
79
+ operation,
80
+ result: {
81
+ ok: false,
82
+ mayHaveMutated: false,
83
+ failure: {
84
+ code: 'write_result_missing',
85
+ stage: 'write',
86
+ severity: 'error',
87
+ userMessage: `No write result was returned for ${path || 'a planned operation'}.`,
88
+ retryable: true,
89
+ nextAction: 'Retry the task and review the Overleaf document before continuing.',
90
+ file: path,
91
+ operationType: normalizeText(operation?.type),
92
+ changedDocument: false,
93
+ terminalState: 'needs_review'
94
+ }
95
+ }
96
+ }, 'skipped', index);
97
+ });
98
+ return entries;
99
+ }
100
+
101
+ function normalizeFailure(result, operation) {
102
+ if (result?.failure && typeof result.failure === 'object') return cloneValue(result.failure);
103
+ if (!result?.code) return null;
104
+ return {
105
+ code: result.code,
106
+ stage: result.stage || 'write',
107
+ severity: result.severity || 'error',
108
+ userMessage: result.reason || result.message || result.code,
109
+ retryable: result.retryable === true,
110
+ file: operation?.path || operation?.to || '',
111
+ changedDocument: result.changedDocument === true,
112
+ terminalState: result.terminalState || ''
113
+ };
114
+ }
115
+
116
+ function normalizeTrackedChanges(value) {
117
+ return (Array.isArray(value) ? value : []).map(item => ({
118
+ key: normalizeText(item?.key),
119
+ id: normalizeText(item?.id),
120
+ path: normalizeText(item?.path),
121
+ label: normalizeText(item?.label)
122
+ })).filter(item => item.key || item.id || item.path);
123
+ }
124
+
125
+ function normalizeStringArray(value) {
126
+ return (Array.isArray(value) ? value : []).map(normalizeText).filter(Boolean);
127
+ }
128
+
129
+ function cloneValue(value) {
130
+ if (value === undefined) return undefined;
131
+ return JSON.parse(JSON.stringify(value));
132
+ }
133
+
134
+ function normalizeText(value) {
135
+ return typeof value === 'string' ? value.trim() : '';
136
+ }
137
+
138
+ return {
139
+ normalizeFailure,
140
+ normalizeOperationEvidence,
141
+ normalizeTrackedChanges
142
+ };
143
+ });