codex-overleaf-link 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- 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 +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- 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 +8 -6
- 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 +17 -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 +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- 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 +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- 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 +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- 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/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
(function initCodexOverleafRunExecutionSnapshot(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(require('./providerProfiles'), require('./runExecutionSnapshotCodec'));
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('RunExecutionSnapshot', [
|
|
6
|
+
'ProviderProfiles',
|
|
7
|
+
'RunExecutionSnapshotCodec'
|
|
8
|
+
], factory);
|
|
9
|
+
}
|
|
10
|
+
})(typeof window !== 'undefined' ? window : globalThis, function runExecutionSnapshotFactory(ProviderProfiles, Codec) {
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
function create(input = {}) {
|
|
14
|
+
const providerId = Codec.normalizeText(input.providerId) || 'builtin';
|
|
15
|
+
return Codec.normalizeSnapshot(input, {
|
|
16
|
+
source: input.source || 'submitted',
|
|
17
|
+
requireProviderRevision: providerId !== 'builtin'
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function capture(input = {}, options = {}) {
|
|
22
|
+
return Codec.normalizeSnapshot(input, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function captureRawQueueTuple(rawQueueItem = {}, fallbackInput = {}) {
|
|
26
|
+
const rawPayload = rawQueueItem?.payload && typeof rawQueueItem.payload === 'object'
|
|
27
|
+
? rawQueueItem.payload
|
|
28
|
+
: rawQueueItem;
|
|
29
|
+
const persisted = rawQueueItem?.executionSnapshot || rawPayload?.executionSnapshot;
|
|
30
|
+
if (persisted && typeof persisted === 'object') {
|
|
31
|
+
return Codec.normalizeSnapshot(persisted, {
|
|
32
|
+
source: persisted.source || 'submitted',
|
|
33
|
+
migrateLegacyConfirm: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const raw = rawPayload && typeof rawPayload === 'object' ? rawPayload : {};
|
|
37
|
+
const fallback = fallbackInput && typeof fallbackInput === 'object' ? fallbackInput : {};
|
|
38
|
+
const captured = {};
|
|
39
|
+
let inferred = false;
|
|
40
|
+
for (const field of Codec.TUPLE_FIELDS) {
|
|
41
|
+
if (Codec.hasOwn(raw, field)) captured[field] = Codec.cloneValue(raw[field]);
|
|
42
|
+
else {
|
|
43
|
+
captured[field] = Codec.cloneValue(fallback[field]);
|
|
44
|
+
inferred = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const providerId = Codec.normalizeText(captured.providerId) || 'builtin';
|
|
48
|
+
if (providerId !== 'builtin' && !Codec.normalizeRevision(captured.providerRevision)
|
|
49
|
+
&& (!Codec.hasOwn(raw, 'providerRevision') || captured.providerRevision === '' || captured.providerRevision == null)) {
|
|
50
|
+
captured.providerRevision = fallback.providerRevision;
|
|
51
|
+
inferred = true;
|
|
52
|
+
}
|
|
53
|
+
return Codec.normalizeSnapshot(captured, {
|
|
54
|
+
source: inferred ? 'legacy-inferred' : 'legacy-captured',
|
|
55
|
+
migrateLegacyConfirm: true
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveForExecution(value = {}, options = {}) {
|
|
60
|
+
const persisted = value.executionSnapshot || value;
|
|
61
|
+
const snapshot = Codec.normalizeSnapshot(persisted, {
|
|
62
|
+
source: persisted.source || 'submitted',
|
|
63
|
+
migrateLegacyConfirm: true
|
|
64
|
+
});
|
|
65
|
+
if (snapshot.providerId === 'builtin') return Codec.normalizeSnapshot(snapshot, { source: snapshot.source });
|
|
66
|
+
const catalog = options.catalog || {};
|
|
67
|
+
const provider = ProviderProfiles?.getProviderById
|
|
68
|
+
? ProviderProfiles.getProviderById(catalog, snapshot.providerId)
|
|
69
|
+
: (catalog.providers || []).find(item => item?.id === snapshot.providerId);
|
|
70
|
+
if (!provider) throw Codec.snapshotError('provider_not_found', 'The selected provider no longer exists.');
|
|
71
|
+
const currentRevision = Codec.normalizeRevision(provider.revision);
|
|
72
|
+
if (!currentRevision) throw Codec.snapshotError('provider_revision_conflict', 'The selected provider has no usable revision.');
|
|
73
|
+
if (!snapshot.providerRevision) {
|
|
74
|
+
if (snapshot.source !== 'legacy-inferred') {
|
|
75
|
+
throw Codec.snapshotError('provider_revision_conflict', 'The selected provider revision was not captured for this run.');
|
|
76
|
+
}
|
|
77
|
+
return Codec.normalizeSnapshot({ ...snapshot, providerRevision: currentRevision }, {
|
|
78
|
+
source: 'legacy-inferred',
|
|
79
|
+
requireProviderRevision: true
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (snapshot.providerRevision !== currentRevision) {
|
|
83
|
+
throw Codec.snapshotError('provider_revision_conflict', 'The selected provider changed after this run was submitted.');
|
|
84
|
+
}
|
|
85
|
+
return Codec.normalizeSnapshot(snapshot, { source: snapshot.source, requireProviderRevision: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function snapshotOf(value = {}) {
|
|
89
|
+
const persisted = value.executionSnapshot || value;
|
|
90
|
+
return Codec.normalizeSnapshot(persisted, {
|
|
91
|
+
source: persisted.source || 'submitted',
|
|
92
|
+
migrateLegacyConfirm: true
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function cloneSnapshot(value = {}) {
|
|
97
|
+
const snapshot = snapshotOf(value);
|
|
98
|
+
return { ...snapshot, focusFiles: [...snapshot.focusFiles] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function toLegacyQueueTuple(value = {}) {
|
|
102
|
+
const snapshot = snapshotOf(value);
|
|
103
|
+
return {
|
|
104
|
+
mode: snapshot.mode,
|
|
105
|
+
providerId: snapshot.providerId,
|
|
106
|
+
providerRevision: snapshot.providerRevision,
|
|
107
|
+
model: snapshot.model,
|
|
108
|
+
reasoningEffort: snapshot.reasoningEffort,
|
|
109
|
+
speedTier: snapshot.speedTier,
|
|
110
|
+
autoRecompile: snapshot.autoRecompile,
|
|
111
|
+
requireReviewing: snapshot.requireReviewing,
|
|
112
|
+
focusFiles: [...snapshot.focusFiles]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function toQueuePayload(value = {}) {
|
|
117
|
+
const snapshot = snapshotOf(value);
|
|
118
|
+
return { executionSnapshot: cloneSnapshot(snapshot), ...toLegacyQueueTuple(snapshot) };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function toRunCompatibilityTuple(value = {}) {
|
|
122
|
+
const snapshot = snapshotOf(value);
|
|
123
|
+
return {
|
|
124
|
+
...toLegacyQueueTuple(snapshot),
|
|
125
|
+
providerRevision: snapshot.providerId === 'builtin' ? 0 : Number(snapshot.providerRevision),
|
|
126
|
+
executionSnapshot: cloneSnapshot(snapshot)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function projectForDisplay(value = {}) {
|
|
131
|
+
const snapshot = snapshotOf(value);
|
|
132
|
+
return {
|
|
133
|
+
providerId: snapshot.providerId,
|
|
134
|
+
model: snapshot.model,
|
|
135
|
+
reasoningEffort: snapshot.reasoningEffort,
|
|
136
|
+
speedTier: snapshot.speedTier,
|
|
137
|
+
mode: snapshot.mode,
|
|
138
|
+
focusFiles: [...snapshot.focusFiles]
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function equalsExecutionConfig(left = {}, right = {}) {
|
|
143
|
+
const leftTuple = toLegacyQueueTuple(left);
|
|
144
|
+
const rightTuple = toLegacyQueueTuple(right);
|
|
145
|
+
return Codec.TUPLE_FIELDS.every(field =>
|
|
146
|
+
JSON.stringify(leftTuple[field]) === JSON.stringify(rightTuple[field])
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function applyToState(state = {}, value = {}) {
|
|
151
|
+
const snapshot = snapshotOf(value);
|
|
152
|
+
return {
|
|
153
|
+
...state,
|
|
154
|
+
...toLegacyQueueTuple(snapshot),
|
|
155
|
+
providerRevision: snapshot.providerId === 'builtin' ? 0 : Number(snapshot.providerRevision)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function toProviderSelection(value = {}) {
|
|
160
|
+
const snapshot = snapshotOf(value);
|
|
161
|
+
return {
|
|
162
|
+
providerId: snapshot.providerId,
|
|
163
|
+
providerRevision: snapshot.providerId === 'builtin' ? 0 : Number(snapshot.providerRevision)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
SCHEMA_VERSION: Codec.SCHEMA_VERSION,
|
|
169
|
+
TUPLE_FIELDS: Codec.TUPLE_FIELDS,
|
|
170
|
+
applyToState,
|
|
171
|
+
capture,
|
|
172
|
+
captureRawQueueTuple,
|
|
173
|
+
cloneSnapshot,
|
|
174
|
+
create,
|
|
175
|
+
equalsExecutionConfig,
|
|
176
|
+
fromQueuePayload: captureRawQueueTuple,
|
|
177
|
+
normalizeRevision: Codec.normalizeRevision,
|
|
178
|
+
normalizeSnapshot: Codec.normalizeSnapshot,
|
|
179
|
+
projectForDisplay,
|
|
180
|
+
resolveForExecution,
|
|
181
|
+
toLegacyQueueTuple,
|
|
182
|
+
toProviderSelection,
|
|
183
|
+
toQueuePayload,
|
|
184
|
+
toRunCompatibilityTuple,
|
|
185
|
+
validate: Codec.validate
|
|
186
|
+
};
|
|
187
|
+
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
(function initCodexOverleafRunExecutionSnapshotCodec(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafRunExecutionSnapshotCodec = factory();
|
|
6
|
+
}
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function runExecutionSnapshotCodecFactory() {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const SCHEMA_VERSION = 1;
|
|
11
|
+
const VALID_MODES = new Set(['ask', 'auto']);
|
|
12
|
+
const VALID_SOURCES = new Set(['submitted', 'legacy-captured', 'legacy-inferred']);
|
|
13
|
+
const TUPLE_FIELDS = Object.freeze([
|
|
14
|
+
'mode', 'providerId', 'providerRevision', 'model', 'reasoningEffort',
|
|
15
|
+
'speedTier', 'autoRecompile', 'requireReviewing', 'focusFiles'
|
|
16
|
+
]);
|
|
17
|
+
const SECRET_FIELD_PATTERN = /(?:api.?key|authorization|password|secret|access.?token|refresh.?token)/i;
|
|
18
|
+
|
|
19
|
+
function normalizeSnapshot(value = {}, options = {}) {
|
|
20
|
+
const input = value && typeof value === 'object' ? value : {};
|
|
21
|
+
assertNoSecretFields(input);
|
|
22
|
+
if (input.mode === 'confirm' && options.migrateLegacyConfirm !== true) {
|
|
23
|
+
throw snapshotError('suggest_mode_removed', 'Suggest mode has been removed. Choose Ask or Auto.');
|
|
24
|
+
}
|
|
25
|
+
const providerId = normalizeText(input.providerId) || 'builtin';
|
|
26
|
+
const revision = normalizeRevisionResult(input.providerRevision);
|
|
27
|
+
if (!revision.ok) throw snapshotError('provider_revision_conflict', revision.error);
|
|
28
|
+
const snapshot = {
|
|
29
|
+
schemaVersion: SCHEMA_VERSION,
|
|
30
|
+
mode: input.mode === 'auto' ? 'auto' : 'ask',
|
|
31
|
+
providerId,
|
|
32
|
+
providerRevision: providerId === 'builtin' ? '' : revision.value,
|
|
33
|
+
model: normalizeText(input.model).slice(0, 160),
|
|
34
|
+
reasoningEffort: normalizeText(input.reasoningEffort).slice(0, 32),
|
|
35
|
+
speedTier: input.speedTier === 'fast' ? 'fast' : 'standard',
|
|
36
|
+
autoRecompile: input.autoRecompile !== false,
|
|
37
|
+
requireReviewing: input.requireReviewing !== false,
|
|
38
|
+
focusFiles: normalizePaths(input.focusFiles),
|
|
39
|
+
capturedAt: normalizeTimestamp(input.capturedAt),
|
|
40
|
+
source: VALID_SOURCES.has(options.source || input.source)
|
|
41
|
+
? (options.source || input.source)
|
|
42
|
+
: 'submitted'
|
|
43
|
+
};
|
|
44
|
+
const result = validate(snapshot, options);
|
|
45
|
+
if (!result.ok) throw snapshotError(result.errors[0].code, result.errors[0].message);
|
|
46
|
+
snapshot.focusFiles = Object.freeze([...snapshot.focusFiles]);
|
|
47
|
+
return Object.freeze(snapshot);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validate(value = {}, options = {}) {
|
|
51
|
+
const errors = [];
|
|
52
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
53
|
+
return fail('invalid_execution_snapshot', 'Execution snapshot must be an object.');
|
|
54
|
+
}
|
|
55
|
+
if (value.schemaVersion !== undefined && Number(value.schemaVersion) !== SCHEMA_VERSION) {
|
|
56
|
+
errors.push(errorItem('invalid_execution_snapshot', `Unsupported execution snapshot schema version: ${value.schemaVersion}`));
|
|
57
|
+
}
|
|
58
|
+
if (!VALID_MODES.has(value.mode)) {
|
|
59
|
+
errors.push(errorItem('invalid_execution_snapshot', 'Execution snapshot mode is invalid.'));
|
|
60
|
+
}
|
|
61
|
+
const providerId = normalizeText(value.providerId);
|
|
62
|
+
if (!providerId) errors.push(errorItem('invalid_execution_snapshot', 'Execution snapshot provider is missing.'));
|
|
63
|
+
const revision = normalizeRevisionResult(value.providerRevision);
|
|
64
|
+
if (!revision.ok) {
|
|
65
|
+
errors.push(errorItem('provider_revision_conflict', revision.error));
|
|
66
|
+
} else if (providerId === 'builtin' && revision.value) {
|
|
67
|
+
errors.push(errorItem('provider_revision_conflict', 'Built-in Codex cannot carry a provider revision.'));
|
|
68
|
+
} else if (providerId !== 'builtin' && options.requireProviderRevision === true && !revision.value) {
|
|
69
|
+
errors.push(errorItem('provider_revision_conflict', 'The selected provider revision was not available when the run was submitted.'));
|
|
70
|
+
}
|
|
71
|
+
if (!Array.isArray(value.focusFiles) || value.focusFiles.length > 100) {
|
|
72
|
+
errors.push(errorItem('invalid_execution_snapshot', 'Execution snapshot focus files are invalid.'));
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
assertNoSecretFields(value);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
errors.push(errorItem(error.code, error.message));
|
|
78
|
+
}
|
|
79
|
+
return { ok: errors.length === 0, errors };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeRevision(value) {
|
|
83
|
+
const result = normalizeRevisionResult(value);
|
|
84
|
+
return result.ok ? result.value : '';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizeRevisionResult(value) {
|
|
88
|
+
if (value === undefined || value === null || value === '') return { ok: true, value: '' };
|
|
89
|
+
const text = typeof value === 'string' ? value.trim() : String(value);
|
|
90
|
+
if (!text) return { ok: true, value: '' };
|
|
91
|
+
const number = Number(text);
|
|
92
|
+
return Number.isSafeInteger(number) && number >= 0
|
|
93
|
+
? { ok: true, value: String(number) }
|
|
94
|
+
: { ok: false, value: '', error: 'Provider revision must be a finite non-negative integer.' };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function normalizePaths(value) {
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
const result = [];
|
|
100
|
+
for (const raw of Array.isArray(value) ? value : []) {
|
|
101
|
+
const path = normalizeProjectPath(raw);
|
|
102
|
+
if (!path || seen.has(path)) continue;
|
|
103
|
+
seen.add(path);
|
|
104
|
+
result.push(path);
|
|
105
|
+
if (result.length >= 100) break;
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function normalizeProjectPath(value) {
|
|
111
|
+
const result = [];
|
|
112
|
+
for (const segment of String(value || '').replace(/\0/g, '').replace(/\\/g, '/').replace(/^\/+/, '').split('/')) {
|
|
113
|
+
if (!segment || segment === '.') continue;
|
|
114
|
+
if (segment === '..') return '';
|
|
115
|
+
result.push(segment);
|
|
116
|
+
}
|
|
117
|
+
return result.join('/').trim();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizeTimestamp(value) {
|
|
121
|
+
if (typeof value === 'string' && value) return value;
|
|
122
|
+
if (Number.isFinite(Number(value)) && Number(value) > 0) return new Date(Number(value)).toISOString();
|
|
123
|
+
return new Date().toISOString();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function assertNoSecretFields(value) {
|
|
127
|
+
for (const key of Object.keys(value && typeof value === 'object' ? value : {})) {
|
|
128
|
+
if (SECRET_FIELD_PATTERN.test(key)) {
|
|
129
|
+
throw snapshotError('invalid_execution_snapshot', `Execution snapshot cannot contain secret field "${key}".`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function cloneValue(value) {
|
|
135
|
+
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function normalizeText(value) {
|
|
139
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function hasOwn(value, key) {
|
|
143
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function snapshotError(code, message) {
|
|
147
|
+
const error = new Error(message);
|
|
148
|
+
error.code = code;
|
|
149
|
+
return error;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function errorItem(code, message) {
|
|
153
|
+
return { code, message };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function fail(code, message) {
|
|
157
|
+
return { ok: false, errors: [errorItem(code, message)] };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
SCHEMA_VERSION,
|
|
162
|
+
TUPLE_FIELDS,
|
|
163
|
+
cloneValue,
|
|
164
|
+
hasOwn,
|
|
165
|
+
normalizeRevision,
|
|
166
|
+
normalizeSnapshot,
|
|
167
|
+
normalizeText,
|
|
168
|
+
snapshotError,
|
|
169
|
+
validate
|
|
170
|
+
};
|
|
171
|
+
});
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
(function initCodexOverleafRunInputQueue(root, factory) {
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
|
-
module.exports = factory();
|
|
3
|
+
module.exports = factory(require('./runExecutionSnapshot'));
|
|
4
4
|
} else {
|
|
5
|
-
root.
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('RunInputQueue', ['RunExecutionSnapshot'], factory);
|
|
6
6
|
}
|
|
7
|
-
})(typeof window !== 'undefined' ? window : globalThis, function runInputQueueFactory() {
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function runInputQueueFactory(RunExecutionSnapshot) {
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const MAX_ITEMS = 20;
|
|
11
11
|
const MAX_TEXT_CHARS = 12000;
|
|
12
12
|
const MAX_QUEUE_BYTES = 128 * 1024;
|
|
13
|
+
const MAX_PERSISTED_QUEUE_BYTES = 512 * 1024;
|
|
13
14
|
const ACTIVE_STATUSES = new Set(['claimed', 'executing', 'steering']);
|
|
14
15
|
const VALID_STATUSES = new Set(['queued', 'paused', ...ACTIVE_STATUSES]);
|
|
15
16
|
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
if (options.recoverActive === true && ACTIVE_STATUSES.has(status)) {
|
|
28
29
|
status = 'paused';
|
|
29
30
|
}
|
|
31
|
+
const executionSnapshot = normalizeExecutionSnapshot(raw, options.fallbackInput);
|
|
30
32
|
normalized.push({
|
|
31
33
|
id,
|
|
32
34
|
clientUserMessageId: String(raw.clientUserMessageId || id),
|
|
@@ -38,7 +40,8 @@
|
|
|
38
40
|
linkedRunId: status === 'executing' ? String(raw.linkedRunId || '') : '',
|
|
39
41
|
claimToken: status === 'claimed' || status === 'executing' ? String(raw.claimToken || '') : '',
|
|
40
42
|
pauseReason: status === 'paused' ? String(raw.pauseReason || '') : '',
|
|
41
|
-
|
|
43
|
+
executionSnapshot: RunExecutionSnapshot.cloneSnapshot(executionSnapshot),
|
|
44
|
+
payload: RunExecutionSnapshot.toQueuePayload(executionSnapshot)
|
|
42
45
|
});
|
|
43
46
|
if (normalized.length >= MAX_ITEMS) {
|
|
44
47
|
break;
|
|
@@ -63,6 +66,7 @@
|
|
|
63
66
|
const nowFactory = deps.now || (() => new Date().toISOString());
|
|
64
67
|
const id = String(input.id || idFactory());
|
|
65
68
|
const timestamp = String(nowFactory());
|
|
69
|
+
const executionSnapshot = normalizeExecutionSnapshot(input, input.fallbackInput);
|
|
66
70
|
const item = {
|
|
67
71
|
id,
|
|
68
72
|
clientUserMessageId: String(input.clientUserMessageId || id),
|
|
@@ -74,10 +78,11 @@
|
|
|
74
78
|
linkedRunId: '',
|
|
75
79
|
claimToken: '',
|
|
76
80
|
pauseReason: input.status === 'paused' ? String(input.pauseReason || '') : '',
|
|
77
|
-
|
|
81
|
+
executionSnapshot: RunExecutionSnapshot.cloneSnapshot(executionSnapshot),
|
|
82
|
+
payload: RunExecutionSnapshot.toQueuePayload(executionSnapshot)
|
|
78
83
|
};
|
|
79
84
|
const next = [...queue, item];
|
|
80
|
-
if (
|
|
85
|
+
if (estimateLegacyBytes(next) > MAX_QUEUE_BYTES || estimateBytes(next) > MAX_PERSISTED_QUEUE_BYTES) {
|
|
81
86
|
return failure(queue, 'queue_too_large');
|
|
82
87
|
}
|
|
83
88
|
return { ok: true, queue: next, item };
|
|
@@ -114,13 +119,37 @@
|
|
|
114
119
|
return { ok: false, queue, item: null };
|
|
115
120
|
}
|
|
116
121
|
const tokenFactory = deps.randomUUID || (() => 'claim_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8));
|
|
117
|
-
|
|
122
|
+
let item = {
|
|
118
123
|
...queue[index],
|
|
119
124
|
status: 'claimed',
|
|
120
125
|
claimToken: String(tokenFactory()),
|
|
121
126
|
updatedAt: new Date().toISOString(),
|
|
122
127
|
pauseReason: ''
|
|
123
128
|
};
|
|
129
|
+
try {
|
|
130
|
+
const captured = RunExecutionSnapshot.normalizeSnapshot(
|
|
131
|
+
item.executionSnapshot || item.payload?.executionSnapshot || item.payload,
|
|
132
|
+
{ source: item.executionSnapshot?.source || item.payload?.executionSnapshot?.source || 'submitted' }
|
|
133
|
+
);
|
|
134
|
+
const snapshot = deps.resolveExecutionSnapshot instanceof Function
|
|
135
|
+
? deps.resolveExecutionSnapshot(captured)
|
|
136
|
+
: captured;
|
|
137
|
+
item = {
|
|
138
|
+
...item,
|
|
139
|
+
executionSnapshot: RunExecutionSnapshot.cloneSnapshot(snapshot),
|
|
140
|
+
payload: RunExecutionSnapshot.toQueuePayload(snapshot)
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
queue,
|
|
146
|
+
item: null,
|
|
147
|
+
error: {
|
|
148
|
+
code: error?.code || 'provider_revision_conflict',
|
|
149
|
+
message: error?.message || ''
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
124
153
|
queue[index] = item;
|
|
125
154
|
return { ok: true, queue, item };
|
|
126
155
|
}
|
|
@@ -160,21 +189,14 @@
|
|
|
160
189
|
return normalizeQueue(items).map(item => item.id === id ? updater(item) : item);
|
|
161
190
|
}
|
|
162
191
|
|
|
163
|
-
function
|
|
164
|
-
return
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
autoRecompile: payload.autoRecompile !== false,
|
|
172
|
-
requireReviewing: payload.requireReviewing !== false,
|
|
173
|
-
focusFiles: (Array.isArray(payload.focusFiles) ? payload.focusFiles : [])
|
|
174
|
-
.map(path => String(path || '').replace(/\\/g, '/').replace(/^\/+/, '').trim())
|
|
175
|
-
.filter(Boolean)
|
|
176
|
-
.slice(0, 100)
|
|
177
|
-
};
|
|
192
|
+
function normalizeExecutionSnapshot(raw = {}, fallbackInput = {}) {
|
|
193
|
+
return RunExecutionSnapshot.captureRawQueueTuple(raw, fallbackInput);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function normalizePayload(payload = {}, fallbackInput = {}) {
|
|
197
|
+
return RunExecutionSnapshot.toQueuePayload(
|
|
198
|
+
RunExecutionSnapshot.captureRawQueueTuple(payload, fallbackInput)
|
|
199
|
+
);
|
|
178
200
|
}
|
|
179
201
|
|
|
180
202
|
function normalizeTimestamp(value) {
|
|
@@ -183,7 +205,10 @@
|
|
|
183
205
|
|
|
184
206
|
function trimToBudget(items) {
|
|
185
207
|
const queue = items.slice(-MAX_ITEMS);
|
|
186
|
-
while (queue.length &&
|
|
208
|
+
while (queue.length && (
|
|
209
|
+
estimateLegacyBytes(queue) > MAX_QUEUE_BYTES
|
|
210
|
+
|| estimateBytes(queue) > MAX_PERSISTED_QUEUE_BYTES
|
|
211
|
+
)) {
|
|
187
212
|
queue.shift();
|
|
188
213
|
}
|
|
189
214
|
return queue;
|
|
@@ -196,6 +221,29 @@
|
|
|
196
221
|
: text.length * 2;
|
|
197
222
|
}
|
|
198
223
|
|
|
224
|
+
function estimateLegacyBytes(items) {
|
|
225
|
+
return estimateBytes((Array.isArray(items) ? items : []).map(item => {
|
|
226
|
+
const legacyItem = { ...(item || {}) };
|
|
227
|
+
delete legacyItem.executionSnapshot;
|
|
228
|
+
return {
|
|
229
|
+
...legacyItem,
|
|
230
|
+
payload: item?.payload && typeof item.payload === 'object'
|
|
231
|
+
? {
|
|
232
|
+
mode: item.payload.mode,
|
|
233
|
+
providerId: item.payload.providerId,
|
|
234
|
+
providerRevision: item.payload.providerRevision,
|
|
235
|
+
model: item.payload.model,
|
|
236
|
+
reasoningEffort: item.payload.reasoningEffort,
|
|
237
|
+
speedTier: item.payload.speedTier,
|
|
238
|
+
autoRecompile: item.payload.autoRecompile,
|
|
239
|
+
requireReviewing: item.payload.requireReviewing,
|
|
240
|
+
focusFiles: item.payload.focusFiles
|
|
241
|
+
}
|
|
242
|
+
: {}
|
|
243
|
+
};
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
|
|
199
247
|
function compactForStorage(items, helpers = {}) {
|
|
200
248
|
const normalizeField = helpers.normalizeField || (value => String(value || ''));
|
|
201
249
|
const normalizeDisplay = helpers.normalizeDisplay || normalizeField;
|
|
@@ -204,6 +252,11 @@
|
|
|
204
252
|
.slice(-MAX_ITEMS)
|
|
205
253
|
.map(item => {
|
|
206
254
|
const payload = item?.payload && typeof item.payload === 'object' ? item.payload : {};
|
|
255
|
+
const snapshot = RunExecutionSnapshot.captureRawQueueTuple(item, payload);
|
|
256
|
+
const compactSnapshot = RunExecutionSnapshot.cloneSnapshot({
|
|
257
|
+
...snapshot,
|
|
258
|
+
focusFiles: normalizePaths(snapshot.focusFiles)
|
|
259
|
+
});
|
|
207
260
|
return {
|
|
208
261
|
id: normalizeField(item?.id, 160),
|
|
209
262
|
clientUserMessageId: normalizeField(item?.clientUserMessageId, 160),
|
|
@@ -215,17 +268,8 @@
|
|
|
215
268
|
linkedRunId: normalizeField(item?.linkedRunId, 160),
|
|
216
269
|
claimToken: normalizeField(item?.claimToken, 160),
|
|
217
270
|
pauseReason: normalizeField(item?.pauseReason, 160),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
providerId: normalizeField(payload.providerId, 160) || 'builtin',
|
|
221
|
-
providerRevision: normalizeField(payload.providerRevision, 160),
|
|
222
|
-
model: normalizeField(payload.model, 160),
|
|
223
|
-
reasoningEffort: normalizeField(payload.reasoningEffort, 32),
|
|
224
|
-
speedTier: payload.speedTier === 'fast' ? 'fast' : 'standard',
|
|
225
|
-
autoRecompile: payload.autoRecompile !== false,
|
|
226
|
-
requireReviewing: payload.requireReviewing !== false,
|
|
227
|
-
focusFiles: normalizePaths(payload.focusFiles)
|
|
228
|
-
}
|
|
271
|
+
executionSnapshot: compactSnapshot,
|
|
272
|
+
payload: RunExecutionSnapshot.toQueuePayload(compactSnapshot)
|
|
229
273
|
};
|
|
230
274
|
})
|
|
231
275
|
.filter(item => item.id && item.text);
|
|
@@ -238,10 +282,13 @@
|
|
|
238
282
|
return {
|
|
239
283
|
MAX_ITEMS,
|
|
240
284
|
MAX_QUEUE_BYTES,
|
|
285
|
+
MAX_PERSISTED_QUEUE_BYTES,
|
|
241
286
|
MAX_TEXT_CHARS,
|
|
242
287
|
claimNext,
|
|
243
288
|
compactForStorage,
|
|
244
289
|
enqueue,
|
|
290
|
+
estimateBytes,
|
|
291
|
+
estimateLegacyBytes,
|
|
245
292
|
markExecuting,
|
|
246
293
|
markSteering,
|
|
247
294
|
normalizeQueue,
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(require('./scopedPersistenceQueuePolicy'));
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define(
|
|
6
|
+
'ScopedPersistenceBrowserAdapter',
|
|
7
|
+
['ScopedPersistenceQueuePolicy'],
|
|
8
|
+
factory
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function (QueuePolicy) {
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
var META_PREFIX = 'codex-overleaf-scoped-persistence-v1:';
|
|
15
|
+
var fallbackMeta = new Map();
|
|
16
|
+
var cleanPart = QueuePolicy.cleanPart;
|
|
17
|
+
var normalizeMeta = QueuePolicy.normalizeMeta;
|
|
18
|
+
|
|
19
|
+
function normalizeScope(input) {
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
accountScopeId: cleanPart(input && input.accountScopeId),
|
|
22
|
+
projectId: cleanPart(input && input.projectId)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function scopeKey(scope) {
|
|
27
|
+
var normalized = normalizeScope(scope);
|
|
28
|
+
if (!normalized.accountScopeId) {
|
|
29
|
+
var error = new Error('A stable account scope is required for durable persistence.');
|
|
30
|
+
error.code = 'account_scope_unavailable';
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
return META_PREFIX + encodeURIComponent(normalized.accountScopeId)
|
|
34
|
+
+ ':' + encodeURIComponent(normalized.projectId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createBrowserAdapter(options) {
|
|
38
|
+
options = options || {};
|
|
39
|
+
var chromeApi = options.chromeApi || (typeof chrome !== 'undefined' ? chrome : null);
|
|
40
|
+
var locksApi = Object.prototype.hasOwnProperty.call(options, 'locksApi')
|
|
41
|
+
? options.locksApi
|
|
42
|
+
: (typeof navigator !== 'undefined' ? navigator.locks : null);
|
|
43
|
+
return {
|
|
44
|
+
async read(scope) {
|
|
45
|
+
var key = scopeKey(scope);
|
|
46
|
+
if (!chromeApi || !chromeApi.storage || !chromeApi.storage.local) {
|
|
47
|
+
return normalizeMeta(fallbackMeta.get(key));
|
|
48
|
+
}
|
|
49
|
+
var stored = await chromeApi.storage.local.get(key);
|
|
50
|
+
return normalizeMeta(stored && stored[key]);
|
|
51
|
+
},
|
|
52
|
+
async write(scope, meta) {
|
|
53
|
+
var key = scopeKey(scope);
|
|
54
|
+
var normalized = normalizeMeta(meta);
|
|
55
|
+
if (!chromeApi || !chromeApi.storage || !chromeApi.storage.local) {
|
|
56
|
+
fallbackMeta.set(key, normalized);
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
await chromeApi.storage.local.set({ [key]: normalized });
|
|
60
|
+
return normalized;
|
|
61
|
+
},
|
|
62
|
+
withLock(scope, work) {
|
|
63
|
+
var key = scopeKey(scope);
|
|
64
|
+
if (locksApi && typeof locksApi.request === 'function') {
|
|
65
|
+
return locksApi.request(key, { mode: 'exclusive' }, work);
|
|
66
|
+
}
|
|
67
|
+
var error = new Error('A cross-realm browser lock is required for durable persistence.');
|
|
68
|
+
error.code = 'storage_lock_unavailable';
|
|
69
|
+
return Promise.reject(error);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Object.freeze({
|
|
75
|
+
createBrowserAdapter: createBrowserAdapter,
|
|
76
|
+
normalizeScope: normalizeScope,
|
|
77
|
+
scopeKey: scopeKey
|
|
78
|
+
});
|
|
79
|
+
});
|