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.
- package/README.md +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +53 -69
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/contentRuntime.js +650 -1449
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41370 -0
- package/extension/src/content/generated/content.bundle.meta.json +102 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +5 -7
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +7 -5
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +11 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +91 -23
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +6 -1
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +8 -5
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +177 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +168 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +181 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +238 -0
- package/extension/src/shared/sessionState.js +64 -48
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +19 -5
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +143 -0
- package/extension/src/shared/writebackSettlement.js +705 -0
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/mirrorWorkspace.js +15 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +3 -3
- package/package.json +13 -5
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
sendBackgroundNative,
|
|
18
18
|
PANEL_STATE_BASE_KEY,
|
|
19
19
|
PROJECT_EDITOR_RESERVED_IDS,
|
|
20
|
-
STATUS_BADGE_CLASS
|
|
20
|
+
STATUS_BADGE_CLASS,
|
|
21
|
+
ProjectSessionCleanup, SessionPersistence, SessionState, StorageDb, StorageKeys, StorageMigration
|
|
21
22
|
} = deps;
|
|
22
|
-
const projectSessionCleanup =
|
|
23
|
+
const projectSessionCleanup = ProjectSessionCleanup.create({
|
|
23
24
|
tr, getAccountScopeId: getCachedAccountScopeId, showPluginConfirm, showPluginToast,
|
|
24
|
-
sendBackgroundNative, mutateProjectPanelState, renderRecentProjectsVariant
|
|
25
|
+
sendBackgroundNative, mutateProjectPanelState, renderRecentProjectsVariant,
|
|
26
|
+
SessionState, StorageDb, StorageMigration
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
// chrome.storage.local cache key (spec §5.6.3). The cache is keyed by
|
|
@@ -496,7 +498,6 @@
|
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
function sessionRecordDisplayTitle(record) {
|
|
499
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
500
501
|
if (record && typeof record.title === 'string' && record.title.trim()) {
|
|
501
502
|
return record.title.trim();
|
|
502
503
|
}
|
|
@@ -505,12 +506,11 @@
|
|
|
505
506
|
}
|
|
506
507
|
|
|
507
508
|
async function loadProjectSessionRecords(projectId) {
|
|
508
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
509
509
|
if (!StorageDb) {
|
|
510
510
|
return [];
|
|
511
511
|
}
|
|
512
512
|
var records = await StorageDb.getAllByIndex('sessions', 'projectId', projectId);
|
|
513
|
-
var Persistence =
|
|
513
|
+
var Persistence = SessionPersistence;
|
|
514
514
|
var deletedIds = await Persistence.getDeletedSessionIds(projectId);
|
|
515
515
|
var scope = getCachedAccountScopeId();
|
|
516
516
|
return (records || [])
|
|
@@ -521,7 +521,6 @@
|
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
async function renderProjectSessions(sessionsEl, projectId) {
|
|
524
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
525
524
|
sessionsEl.innerHTML = '';
|
|
526
525
|
var sessionsLoading = document.createElement('div');
|
|
527
526
|
sessionsLoading.className = 'recent-projects-loading';
|
|
@@ -544,7 +543,6 @@
|
|
|
544
543
|
}
|
|
545
544
|
|
|
546
545
|
function renderProjectSessionRow(sessionsEl, projectId, record) {
|
|
547
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
548
546
|
var running = Boolean(StorageDb)
|
|
549
547
|
&& settleDashboardRunStatus(StorageDb.derivePrimaryStatusBadge(record), record.updatedAt || record.lastActivityAt) === 'running';
|
|
550
548
|
var row = document.createElement('div');
|
|
@@ -614,17 +612,21 @@
|
|
|
614
612
|
}
|
|
615
613
|
|
|
616
614
|
async function activateSessionAndOpenProject(projectId, record) {
|
|
617
|
-
var Migration =
|
|
615
|
+
var Migration = StorageMigration;
|
|
618
616
|
try {
|
|
619
617
|
if (Migration && Migration.loadPrefs && Migration.savePrefs) {
|
|
620
|
-
var
|
|
618
|
+
var accountScopeId = getCachedAccountScopeId();
|
|
619
|
+
if (!accountScopeId) {
|
|
620
|
+
throw new Error('Account scope is unavailable.');
|
|
621
|
+
}
|
|
622
|
+
var prefs = await Migration.loadPrefs(accountScopeId, projectId);
|
|
621
623
|
prefs = prefs && typeof prefs === 'object' ? prefs : {};
|
|
622
624
|
var map = prefs.activeSessionByProject && typeof prefs.activeSessionByProject === 'object'
|
|
623
625
|
? prefs.activeSessionByProject
|
|
624
626
|
: {};
|
|
625
627
|
map[projectId] = record.id;
|
|
626
628
|
prefs.activeSessionByProject = map;
|
|
627
|
-
await Migration.savePrefs(prefs);
|
|
629
|
+
await Migration.savePrefs(prefs, accountScopeId, projectId);
|
|
628
630
|
}
|
|
629
631
|
} catch (_error) { /* fall through: opening the project still works */ }
|
|
630
632
|
openProjectFromRow(projectId);
|
|
@@ -636,7 +638,6 @@
|
|
|
636
638
|
// panel-state blob is deliberately left alone — an empty projectId would
|
|
637
639
|
// map getProjectStorageKey onto the global legacy key.
|
|
638
640
|
async function cleanupDeadProjectEntry(projectId) {
|
|
639
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
640
641
|
if (!StorageDb) {
|
|
641
642
|
return;
|
|
642
643
|
}
|
|
@@ -684,7 +685,6 @@
|
|
|
684
685
|
}
|
|
685
686
|
|
|
686
687
|
function projectPanelStateKey(projectId) {
|
|
687
|
-
var StorageKeys = window.CodexOverleafStorageKeys;
|
|
688
688
|
return StorageKeys.getProjectStorageKey(PANEL_STATE_BASE_KEY, 'https://www.overleaf.com/project/' + projectId);
|
|
689
689
|
}
|
|
690
690
|
|
|
@@ -694,7 +694,6 @@
|
|
|
694
694
|
// NOTE: if the project is open in another tab, that tab's in-memory state
|
|
695
695
|
// wins on its next save; the IndexedDB record mutation below still holds.
|
|
696
696
|
async function mutateProjectPanelState(projectId, mutate) {
|
|
697
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
698
697
|
var key = projectPanelStateKey(projectId);
|
|
699
698
|
var stored = await chrome.storage.local.get(key);
|
|
700
699
|
var blob = stored && stored[key];
|
|
@@ -709,8 +708,6 @@
|
|
|
709
708
|
}
|
|
710
709
|
|
|
711
710
|
async function deleteDashboardSession(sessionsEl, projectId, record) {
|
|
712
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
713
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
714
711
|
// v1.8.1 P1b: ANY record whose stored badge is 'running' gets the
|
|
715
712
|
// stronger confirm — even one the 30-minute heuristic settled to
|
|
716
713
|
// interrupted. Activity timestamps are only bumped once a minute during
|
|
@@ -731,7 +728,7 @@
|
|
|
731
728
|
if (!approved) {
|
|
732
729
|
return;
|
|
733
730
|
}
|
|
734
|
-
await
|
|
731
|
+
await SessionPersistence.addTombstones(projectId, [record.id]);
|
|
735
732
|
try {
|
|
736
733
|
await mutateProjectPanelState(projectId, function (state) {
|
|
737
734
|
return SessionState.deleteSession(state, record.id);
|
|
@@ -773,8 +770,6 @@
|
|
|
773
770
|
}
|
|
774
771
|
|
|
775
772
|
function beginDashboardSessionRename(sessionsEl, projectId, record, row, titleEl) {
|
|
776
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
777
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
778
773
|
if (row.querySelector('input')) {
|
|
779
774
|
return;
|
|
780
775
|
}
|
|
@@ -816,8 +811,6 @@
|
|
|
816
811
|
}
|
|
817
812
|
|
|
818
813
|
async function commitDashboardSessionRename(sessionsEl, projectId, record, rawTitle) {
|
|
819
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
820
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
821
814
|
if (!SessionState) {
|
|
822
815
|
return;
|
|
823
816
|
}
|
|
@@ -963,9 +956,8 @@
|
|
|
963
956
|
// there really is more than one page of projects.
|
|
964
957
|
var pageLimit = 10;
|
|
965
958
|
try {
|
|
966
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
967
959
|
if (StorageDb) {
|
|
968
|
-
var deletedSessionIdsByProject = await
|
|
960
|
+
var deletedSessionIdsByProject = await SessionPersistence.loadTombstones();
|
|
969
961
|
rows = await StorageDb.listRecentProjectsAcrossAccount({
|
|
970
962
|
accountScopeId: accountScopeId,
|
|
971
963
|
limit: showAll ? 500 : pageLimit + 1,
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function appendToView(event, view) {
|
|
50
|
-
const text =
|
|
50
|
+
const text = deps.RunGuidanceView?.getGuidanceText(event);
|
|
51
51
|
if (!text) return false;
|
|
52
52
|
const guidance = view.guidance || view.root?.querySelector('[data-run-guidance]');
|
|
53
53
|
const target = view.terminalStatus ? guidance : view.events;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
3
|
module.exports = factory(require('../shared/runInputQueue'));
|
|
4
4
|
} else {
|
|
5
|
-
root.
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('RunQueueScheduler', ['RunInputQueue'], factory);
|
|
6
6
|
}
|
|
7
7
|
})(typeof window !== 'undefined' ? window : globalThis, function runQueueSchedulerFactory(Queue) {
|
|
8
8
|
'use strict';
|
|
@@ -19,18 +19,49 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function afterSettlement({ sessionId, status, queueItemId = '', continueQueue = false } = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
const originalQueue = options.getQueue?.(sessionId) || [];
|
|
23
|
+
const shouldContinue = status === 'completed' || continueQueue;
|
|
24
|
+
const shouldRemove = Boolean(queueItemId && shouldContinue);
|
|
25
|
+
const claimedItem = shouldRemove
|
|
26
|
+
? originalQueue.find(item => item?.id === queueItemId)
|
|
27
|
+
: null;
|
|
28
|
+
if (shouldRemove) {
|
|
29
|
+
await options.persist?.({
|
|
30
|
+
queueMutation: {
|
|
31
|
+
type: 'remove',
|
|
32
|
+
sessionId,
|
|
33
|
+
itemId: queueItemId,
|
|
34
|
+
claimToken: claimedItem?.claimToken
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const latestQueue = options.getQueue?.(sessionId) || originalQueue;
|
|
38
|
+
options.setQueue?.(
|
|
39
|
+
sessionId,
|
|
40
|
+
dequeue(latestQueue, queueItemId, {
|
|
41
|
+
sessionId,
|
|
42
|
+
reason: status === 'completed' ? 'completed' : 'continue_queue'
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
} else if (!shouldContinue) {
|
|
46
|
+
const paused = originalQueue.length
|
|
47
|
+
? Queue.pauseAll(originalQueue, status || 'run_did_not_complete')
|
|
48
|
+
: originalQueue;
|
|
49
|
+
options.setQueue?.(sessionId, paused);
|
|
50
|
+
try {
|
|
51
|
+
await options.persist?.({
|
|
52
|
+
queueMutation: { type: 'transition', sessionId }
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const latestQueue = options.getQueue?.(sessionId) || paused;
|
|
56
|
+
options.setQueue?.(
|
|
57
|
+
sessionId,
|
|
58
|
+
Queue.pauseAll(latestQueue, status || 'run_did_not_complete')
|
|
59
|
+
);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
29
62
|
}
|
|
30
|
-
options.setQueue?.(sessionId, queue);
|
|
31
|
-
await options.persist?.();
|
|
32
63
|
options.onChange?.(sessionId);
|
|
33
|
-
if (
|
|
64
|
+
if (shouldContinue) {
|
|
34
65
|
scheduleOne(sessionId);
|
|
35
66
|
}
|
|
36
67
|
}
|
|
@@ -39,15 +70,33 @@
|
|
|
39
70
|
if (scheduling || options.isRunning?.() || options.canStart?.(sessionId) === false) {
|
|
40
71
|
return false;
|
|
41
72
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
});
|
|
45
|
-
if (!claimed.ok) {
|
|
73
|
+
const initialQueue = options.getQueue?.(sessionId) || [];
|
|
74
|
+
if (!initialQueue.some(item => item?.status === 'queued')) {
|
|
46
75
|
return false;
|
|
47
76
|
}
|
|
48
77
|
scheduling = true;
|
|
49
|
-
|
|
50
|
-
Promise.resolve(options.
|
|
78
|
+
let claimed = null;
|
|
79
|
+
Promise.resolve(options.prepareClaim?.(sessionId))
|
|
80
|
+
.then(() => {
|
|
81
|
+
claimed = Queue.claimNext(options.getQueue?.(sessionId) || initialQueue, {
|
|
82
|
+
randomUUID: options.randomUUID,
|
|
83
|
+
resolveExecutionSnapshot: options.resolveExecutionSnapshot
|
|
84
|
+
});
|
|
85
|
+
if (!claimed.ok) {
|
|
86
|
+
const error = new Error(claimed.error?.message || claimed.error?.code || 'queue_claim_failed');
|
|
87
|
+
error.code = claimed.error?.code || 'queue_claim_failed';
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
options.setQueue?.(sessionId, claimed.queue);
|
|
91
|
+
return options.persist?.({
|
|
92
|
+
queueMutation: {
|
|
93
|
+
type: 'claim',
|
|
94
|
+
sessionId,
|
|
95
|
+
itemId: claimed.item.id,
|
|
96
|
+
claimToken: claimed.item.claimToken
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
})
|
|
51
100
|
.then(() => {
|
|
52
101
|
options.onChange?.(sessionId);
|
|
53
102
|
scheduling = false;
|
|
@@ -55,27 +104,64 @@
|
|
|
55
104
|
})
|
|
56
105
|
.catch(error => {
|
|
57
106
|
scheduling = false;
|
|
58
|
-
const paused = Queue.pauseAll(
|
|
107
|
+
const paused = Queue.pauseAll(
|
|
108
|
+
options.getQueue?.(sessionId) || claimed?.queue || initialQueue,
|
|
109
|
+
error?.code || 'start_failed'
|
|
110
|
+
);
|
|
59
111
|
options.setQueue?.(sessionId, paused);
|
|
60
112
|
options.onChange?.(sessionId);
|
|
61
|
-
return options.persist?.(
|
|
113
|
+
return Promise.resolve(options.persist?.({
|
|
114
|
+
queueMutation: claimed?.item?.id
|
|
115
|
+
? {
|
|
116
|
+
type: 'release',
|
|
117
|
+
sessionId,
|
|
118
|
+
itemId: claimed.item.id,
|
|
119
|
+
claimToken: claimed.item.claimToken
|
|
120
|
+
}
|
|
121
|
+
: { type: 'transition', sessionId }
|
|
122
|
+
})).catch(() => undefined);
|
|
62
123
|
});
|
|
63
124
|
return true;
|
|
64
125
|
}
|
|
65
126
|
|
|
66
127
|
async function markExecuting(sessionId, itemId, _runId) {
|
|
67
128
|
const queue = options.getQueue?.(sessionId) || [];
|
|
129
|
+
const item = queue.find(entry => entry?.id === itemId);
|
|
130
|
+
try {
|
|
131
|
+
await options.persist?.({
|
|
132
|
+
queueMutation: { type: 'remove', sessionId, itemId, claimToken: item?.claimToken }
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
const latestQueue = options.getQueue?.(sessionId) || queue;
|
|
136
|
+
options.setQueue?.(
|
|
137
|
+
sessionId,
|
|
138
|
+
Queue.pauseAll(latestQueue, error?.code || 'queue_promotion_persistence_failed')
|
|
139
|
+
);
|
|
140
|
+
options.onChange?.(sessionId);
|
|
141
|
+
try {
|
|
142
|
+
await options.persist?.({
|
|
143
|
+
queueMutation: { type: 'restore', sessionId, itemId, claimToken: item?.claimToken }
|
|
144
|
+
});
|
|
145
|
+
} catch (recoveryError) {
|
|
146
|
+
if (error && typeof error === 'object') {
|
|
147
|
+
error.persistenceRecoveryError = recoveryError?.message || String(recoveryError);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
const latestQueue = options.getQueue?.(sessionId) || queue;
|
|
68
153
|
options.setQueue?.(
|
|
69
154
|
sessionId,
|
|
70
|
-
dequeue(
|
|
155
|
+
dequeue(latestQueue, itemId, { sessionId, runId: _runId, reason: 'executing' })
|
|
71
156
|
);
|
|
72
|
-
await options.persist?.();
|
|
73
157
|
options.onChange?.(sessionId);
|
|
74
158
|
}
|
|
75
159
|
|
|
76
160
|
async function resume(sessionId) {
|
|
77
161
|
options.setQueue?.(sessionId, Queue.resumeAll(options.getQueue?.(sessionId) || []));
|
|
78
|
-
await options.persist?.(
|
|
162
|
+
await options.persist?.({
|
|
163
|
+
queueMutation: { type: 'transition', sessionId }
|
|
164
|
+
});
|
|
79
165
|
options.onChange?.(sessionId);
|
|
80
166
|
scheduleOne(sessionId);
|
|
81
167
|
}
|
|
@@ -88,7 +174,7 @@
|
|
|
88
174
|
const scheduler = create({
|
|
89
175
|
getQueue: sessionId => options.getSession?.(sessionId)?.pendingInputs || [],
|
|
90
176
|
setQueue: (sessionId, pendingInputs) => options.setQueue?.(sessionId, pendingInputs),
|
|
91
|
-
persist:
|
|
177
|
+
persist: persistenceOptions => options.save?.(persistenceOptions),
|
|
92
178
|
isRunning: () => Boolean(options.getCurrentRun?.()),
|
|
93
179
|
canStart: sessionId => options.canStart?.(sessionId) !== false,
|
|
94
180
|
start: (item, sessionId) => startQueuedInput(item, sessionId),
|
|
@@ -97,6 +183,8 @@
|
|
|
97
183
|
sessionId: context.sessionId,
|
|
98
184
|
recordId: item.sourceRunId || ''
|
|
99
185
|
}),
|
|
186
|
+
prepareClaim: options.prepareClaim,
|
|
187
|
+
resolveExecutionSnapshot: options.resolveExecutionSnapshot,
|
|
100
188
|
randomUUID: options.randomUUID
|
|
101
189
|
});
|
|
102
190
|
|
|
@@ -128,7 +216,9 @@
|
|
|
128
216
|
// active turn, or until the scheduler promotes it to the next run.
|
|
129
217
|
options.clearTask?.();
|
|
130
218
|
render();
|
|
131
|
-
options.saveSoon?.(
|
|
219
|
+
options.saveSoon?.({
|
|
220
|
+
queueMutation: { type: 'enqueue', sessionId: session.id, itemId: result.item.id }
|
|
221
|
+
});
|
|
132
222
|
}
|
|
133
223
|
|
|
134
224
|
async function guide(itemId) {
|
|
@@ -142,7 +232,9 @@
|
|
|
142
232
|
}
|
|
143
233
|
options.setQueue?.(session.id, Queue.markSteering(session.pendingInputs, itemId));
|
|
144
234
|
render();
|
|
145
|
-
await options.save?.(
|
|
235
|
+
await options.save?.({
|
|
236
|
+
queueMutation: { type: 'transition', sessionId: session.id, itemId }
|
|
237
|
+
});
|
|
146
238
|
try {
|
|
147
239
|
const response = await options.sendSteer?.({
|
|
148
240
|
requestId: run.nativeRequestId,
|
|
@@ -174,7 +266,11 @@
|
|
|
174
266
|
options.toast?.(response?.error?.message || options.tr?.('queuedInputGuideFailed'), 'warning');
|
|
175
267
|
}
|
|
176
268
|
render();
|
|
177
|
-
await options.save?.(
|
|
269
|
+
await options.save?.({
|
|
270
|
+
queueMutation: response?.ok
|
|
271
|
+
? { type: 'remove', sessionId: session.id, itemId }
|
|
272
|
+
: { type: 'transition', sessionId: session.id, itemId }
|
|
273
|
+
});
|
|
178
274
|
} catch (error) {
|
|
179
275
|
const latest = options.getSession?.(session.id);
|
|
180
276
|
const paused = (latest?.pendingInputs || []).map(entry => entry.id === itemId
|
|
@@ -183,7 +279,9 @@
|
|
|
183
279
|
options.setQueue?.(session.id, Queue.normalizeQueue(paused));
|
|
184
280
|
options.toast?.(error?.message || options.tr?.('queuedInputGuideFailed'), 'warning');
|
|
185
281
|
render();
|
|
186
|
-
await options.save?.(
|
|
282
|
+
await options.save?.({
|
|
283
|
+
queueMutation: { type: 'transition', sessionId: session.id, itemId }
|
|
284
|
+
});
|
|
187
285
|
}
|
|
188
286
|
}
|
|
189
287
|
|
|
@@ -201,7 +299,9 @@
|
|
|
201
299
|
}
|
|
202
300
|
options.setQueue?.(session.id, Queue.remove(session.pendingInputs || [], itemId));
|
|
203
301
|
render();
|
|
204
|
-
options.saveSoon?.(
|
|
302
|
+
options.saveSoon?.({
|
|
303
|
+
queueMutation: { type: 'remove', sessionId: session.id, itemId }
|
|
304
|
+
});
|
|
205
305
|
}
|
|
206
306
|
|
|
207
307
|
function resume() {
|
|
@@ -242,7 +342,7 @@
|
|
|
242
342
|
error.code = 'queue_session_not_active';
|
|
243
343
|
throw error;
|
|
244
344
|
}
|
|
245
|
-
options.applyQueuedInput?.(item);
|
|
345
|
+
await options.applyQueuedInput?.(item);
|
|
246
346
|
await options.run?.(item);
|
|
247
347
|
}
|
|
248
348
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
(function initCodexOverleafRunSettlementPersistence(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafRunSettlementPersistence = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function runSettlementPersistenceFactory() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function create(options = {}) {
|
|
9
|
+
const Settlement = options.writebackSettlement;
|
|
10
|
+
|
|
11
|
+
function findTarget(recordId, sessionId) {
|
|
12
|
+
return typeof options.findRunRecord === 'function'
|
|
13
|
+
? options.findRunRecord(recordId, sessionId)
|
|
14
|
+
: null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function applyToCurrentRun(settlement) {
|
|
18
|
+
const view = options.getCurrentRunView?.();
|
|
19
|
+
if (!settlement || !view?.recordId) return;
|
|
20
|
+
const record = findTarget(view.recordId, view.sessionId);
|
|
21
|
+
if (!record) return;
|
|
22
|
+
Object.assign(record, Settlement.applySettlementTransition(record, settlement));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function settleMirrorRefresh(result = {}) {
|
|
26
|
+
const record = findTarget(result.recordId, result.sessionId);
|
|
27
|
+
if (!record) return;
|
|
28
|
+
if (result.runProjectId && result.runProjectId !== options.getCurrentProjectId?.()) return;
|
|
29
|
+
const existing = record.settlement && typeof record.settlement === 'object'
|
|
30
|
+
? record.settlement
|
|
31
|
+
: record.settlementFacts;
|
|
32
|
+
if (!existing || typeof existing !== 'object') return;
|
|
33
|
+
record.settlement = Settlement.compactSettlementFacts({
|
|
34
|
+
...existing,
|
|
35
|
+
evidence: {
|
|
36
|
+
...(existing.evidence || {}),
|
|
37
|
+
mirrored: result.state
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
delete record.settlementFacts;
|
|
41
|
+
options.saveStateSoon?.();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
applyToCurrentRun,
|
|
46
|
+
settleMirrorRefresh
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Object.freeze({ create });
|
|
51
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
(function initCodexOverleafRunTimelineView() {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const RunGuidanceView = window.CodexOverleafRunGuidanceView;
|
|
5
4
|
// Run-timeline view — the timeline render pipeline carved out of
|
|
6
5
|
// contentRuntime.js (v1.4.6 structural-debt phase 2): the scroll engine +
|
|
7
6
|
// jump-to-latest button, the live-elapsed tick and collapsed-header summary,
|
|
@@ -13,6 +12,7 @@
|
|
|
13
12
|
// that owns it.
|
|
14
13
|
function create(deps = {}) {
|
|
15
14
|
const {
|
|
15
|
+
RunGuidanceView,
|
|
16
16
|
tr,
|
|
17
17
|
tx,
|
|
18
18
|
getLocale,
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
showNativeSetupGuidance,
|
|
41
41
|
openProjectFileForFailure,
|
|
42
42
|
openStorageSettings,
|
|
43
|
-
trackedChangeInFlight
|
|
43
|
+
trackedChangeInFlight,
|
|
44
|
+
projectRunSettlement
|
|
44
45
|
} = deps;
|
|
45
46
|
|
|
46
47
|
let logAutoFollow = true;
|
|
@@ -884,8 +885,8 @@
|
|
|
884
885
|
return;
|
|
885
886
|
}
|
|
886
887
|
|
|
887
|
-
const
|
|
888
|
-
if (!
|
|
888
|
+
const projection = projectRunSettlement(run);
|
|
889
|
+
if (!projection.canUndo && run.undoStatus !== 'applied') {
|
|
889
890
|
button.hidden = true;
|
|
890
891
|
return;
|
|
891
892
|
}
|
|
@@ -912,13 +913,14 @@
|
|
|
912
913
|
// same executable state as `pending`.
|
|
913
914
|
function configureLifecycleUndoButton(button, run) {
|
|
914
915
|
const status = run.trackedChangeStatus || '';
|
|
916
|
+
const projection = projectRunSettlement(run);
|
|
915
917
|
// §7 settlement matrix: needs_review keeps BOTH controls visible AND
|
|
916
918
|
// actionable. Branch placed before the terminal branches so a
|
|
917
919
|
// needs_review run is never treated as terminal.
|
|
918
920
|
if (status === 'needs_review') {
|
|
919
921
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
920
922
|
button.hidden = false;
|
|
921
|
-
button.disabled = inFlight === 'reject' || inFlight === 'accept';
|
|
923
|
+
button.disabled = !projection.canUndo || inFlight === 'reject' || inFlight === 'accept';
|
|
922
924
|
button.textContent = tr('undoRun');
|
|
923
925
|
button.title = tr('undoRunTitle');
|
|
924
926
|
button.addEventListener('click', event => {
|
|
@@ -949,7 +951,7 @@
|
|
|
949
951
|
}
|
|
950
952
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
951
953
|
button.hidden = false;
|
|
952
|
-
button.disabled = inFlight === 'reject' || inFlight === 'accept';
|
|
954
|
+
button.disabled = !projection.canUndo || inFlight === 'reject' || inFlight === 'accept';
|
|
953
955
|
button.textContent = tr('undoRun');
|
|
954
956
|
button.title = tr('undoRunTitle');
|
|
955
957
|
button.addEventListener('click', event => {
|
|
@@ -980,13 +982,14 @@
|
|
|
980
982
|
}
|
|
981
983
|
|
|
982
984
|
const status = run.trackedChangeStatus || '';
|
|
985
|
+
const projection = projectRunSettlement(run);
|
|
983
986
|
// §7 settlement matrix: needs_review keeps BOTH controls visible AND
|
|
984
987
|
// actionable. It remains an internal retryable proof state, while the
|
|
985
988
|
// primary button label stays in the same executable state as `pending`.
|
|
986
989
|
if (status === 'needs_review') {
|
|
987
990
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
988
991
|
button.hidden = false;
|
|
989
|
-
button.disabled = inFlight === 'accept' || inFlight === 'reject';
|
|
992
|
+
button.disabled = !projection.canAccept || inFlight === 'accept' || inFlight === 'reject';
|
|
990
993
|
if (inFlight === 'accept') {
|
|
991
994
|
button.textContent = tr('runAcceptTrackedConfirming');
|
|
992
995
|
button.title = tr('runAcceptTrackedConfirming');
|
|
@@ -1020,7 +1023,7 @@
|
|
|
1020
1023
|
|
|
1021
1024
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
1022
1025
|
button.hidden = false;
|
|
1023
|
-
button.disabled = inFlight === 'accept' || inFlight === 'reject';
|
|
1026
|
+
button.disabled = !projection.canAccept || inFlight === 'accept' || inFlight === 'reject';
|
|
1024
1027
|
if (inFlight === 'accept') {
|
|
1025
1028
|
button.textContent = tr('runAcceptTrackedConfirming');
|
|
1026
1029
|
button.title = tr('runAcceptTrackedConfirming');
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(
|
|
4
|
+
require('./scopedPersistencePanelState'),
|
|
5
|
+
require('./scopedPersistenceDetachedCommit')
|
|
6
|
+
);
|
|
7
|
+
} else {
|
|
8
|
+
root.CodexOverleafModuleRegistry.define(
|
|
9
|
+
'ScopedPersistenceCoordinator',
|
|
10
|
+
['ScopedPersistencePanelState', 'ScopedPersistenceDetachedCommit'],
|
|
11
|
+
factory
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function (PanelState, DetachedCommit) {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
function createController(options) {
|
|
18
|
+
options = options || {};
|
|
19
|
+
var Transaction = options.Transaction;
|
|
20
|
+
if (!Transaction || typeof Transaction.createCoordinator !== 'function') return null;
|
|
21
|
+
var writerId = getStableWriterId(options.sessionStorage);
|
|
22
|
+
var coordinator = Transaction.createCoordinator({
|
|
23
|
+
adapter: Transaction.createBrowserAdapter({ chromeApi: options.chromeApi }),
|
|
24
|
+
writerId: writerId
|
|
25
|
+
});
|
|
26
|
+
var commitDetached = (options.DetachedCommit || DetachedCommit)?.create?.({
|
|
27
|
+
Transaction: Transaction, chromeApi: options.chromeApi, writerId: writerId
|
|
28
|
+
});
|
|
29
|
+
var activeScope = null;
|
|
30
|
+
|
|
31
|
+
function scope(projectId) {
|
|
32
|
+
var accountScopeId = '';
|
|
33
|
+
try {
|
|
34
|
+
accountScopeId = typeof options.getAccountScopeId === 'function'
|
|
35
|
+
? options.getAccountScopeId() || ''
|
|
36
|
+
: '';
|
|
37
|
+
} catch (_error) {
|
|
38
|
+
accountScopeId = '';
|
|
39
|
+
}
|
|
40
|
+
return { accountScopeId: accountScopeId, projectId: projectId };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
async beginHydration(projectId) {
|
|
45
|
+
var nextScope = scope(projectId);
|
|
46
|
+
if (!nextScope.accountScopeId) {
|
|
47
|
+
activeScope = null;
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
var token = await coordinator.beginHydration(nextScope);
|
|
51
|
+
if (coordinator.isCurrent(token)) activeScope = nextScope;
|
|
52
|
+
return token;
|
|
53
|
+
},
|
|
54
|
+
isCurrent: coordinator.isCurrent,
|
|
55
|
+
async commit(projectId, commitOptions, action) {
|
|
56
|
+
var nextScope = scope(projectId);
|
|
57
|
+
if (!nextScope.accountScopeId) {
|
|
58
|
+
return { ok: false, reason: 'account_scope_unavailable' };
|
|
59
|
+
}
|
|
60
|
+
if (!activeScope
|
|
61
|
+
|| activeScope.accountScopeId !== nextScope.accountScopeId
|
|
62
|
+
|| activeScope.projectId !== nextScope.projectId) {
|
|
63
|
+
var hydration = await coordinator.beginHydration(nextScope);
|
|
64
|
+
if (!coordinator.isCurrent(hydration)) {
|
|
65
|
+
return { ok: false, reason: 'stale_view' };
|
|
66
|
+
}
|
|
67
|
+
activeScope = nextScope;
|
|
68
|
+
}
|
|
69
|
+
var view = await coordinator.captureCommitView(nextScope, {
|
|
70
|
+
detached: Boolean(commitOptions && commitOptions.detached)
|
|
71
|
+
});
|
|
72
|
+
return coordinator.commit(view, action, {
|
|
73
|
+
rebase: !commitOptions || commitOptions.rebase !== false,
|
|
74
|
+
queueMutation: commitOptions && commitOptions.queueMutation,
|
|
75
|
+
queueMutations: commitOptions && commitOptions.queueMutations,
|
|
76
|
+
sessionTombstones: commitOptions && commitOptions.sessionTombstones
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
commitDetached: commitDetached
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function getStableWriterId(storage) {
|
|
83
|
+
var key = 'codex-overleaf-scoped-writer-id';
|
|
84
|
+
try {
|
|
85
|
+
var existing = storage?.getItem?.(key);
|
|
86
|
+
if (existing) return existing;
|
|
87
|
+
var created = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
88
|
+
? crypto.randomUUID()
|
|
89
|
+
: 'writer-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
|
|
90
|
+
storage?.setItem?.(key, created);
|
|
91
|
+
return created;
|
|
92
|
+
} catch (_error) {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
createController: createController,
|
|
98
|
+
persistPanelState: PanelState?.persistPanelState || null
|
|
99
|
+
});
|
|
100
|
+
});
|