codex-overleaf-link 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +20 -0
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +108 -110
- package/extension/src/content/generated/content.bundle.js +566 -331
- package/extension/src/content/generated/content.bundle.meta.json +4 -3
- package/extension/src/content/markdownText.js +12 -10
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/nativeCompatibilityController.js +2 -2
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/runTimelineView.js +6 -0
- package/extension/src/content/writebackOrchestrator.js +2 -49
- package/extension/src/page/trackedChangesLifecycle.js +10 -4
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/shared/agentTranscript.js +10 -3
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/pageRpcContract.js +2 -2
- package/extension/src/shared/runExecutionSnapshot.js +14 -4
- package/extension/src/shared/runExecutionSnapshotCodec.js +5 -2
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +20 -1
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +50 -47
- package/extension/src/shared/sessionState.js +46 -11
- package/extension/src/shared/storageDb.js +6 -5
- package/extension/src/shared/writebackOperationEvidence.js +16 -6
- package/extension/src/shared/writebackSettlement.js +28 -6
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/taskRunnerRuntime.js +11 -81
- package/native-host/src/updateManager.js +36 -1
- package/package.json +1 -1
|
@@ -35,8 +35,27 @@
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function normalizeMeta(value) {
|
|
38
|
+
var revision = normalizeRevision(value && value.revision);
|
|
39
|
+
var hasDurableRevision = Boolean(value
|
|
40
|
+
&& Object.prototype.hasOwnProperty.call(value, 'durableRevision'));
|
|
41
|
+
var durableRevision = hasDurableRevision
|
|
42
|
+
? Math.min(normalizeRevision(value.durableRevision), revision)
|
|
43
|
+
: revision;
|
|
44
|
+
var pendingValue = value && value.pendingCommit;
|
|
45
|
+
var pendingRevision = normalizeRevision(pendingValue && pendingValue.revision);
|
|
46
|
+
var pendingCommit = pendingValue && typeof pendingValue === 'object'
|
|
47
|
+
&& pendingRevision > durableRevision
|
|
48
|
+
&& pendingRevision <= revision
|
|
49
|
+
? {
|
|
50
|
+
revision: pendingRevision,
|
|
51
|
+
writerId: cleanPart(pendingValue.writerId),
|
|
52
|
+
startedAt: cleanPart(pendingValue.startedAt)
|
|
53
|
+
}
|
|
54
|
+
: null;
|
|
38
55
|
return {
|
|
39
|
-
revision:
|
|
56
|
+
revision: revision,
|
|
57
|
+
durableRevision: durableRevision,
|
|
58
|
+
pendingCommit: pendingCommit,
|
|
40
59
|
queueRevision: normalizeRevision(value && value.queueRevision),
|
|
41
60
|
writerId: cleanPart(value && value.writerId),
|
|
42
61
|
updatedAt: cleanPart(value && value.updatedAt),
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('ScopedPersistenceReceipt', [], factory);
|
|
6
|
+
}
|
|
7
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
function createFenceMeta(meta, currentMeta, writerId) {
|
|
11
|
+
var startedAt = new Date().toISOString();
|
|
12
|
+
var revision = currentMeta.revision + 1;
|
|
13
|
+
return {
|
|
14
|
+
...meta,
|
|
15
|
+
revision: revision,
|
|
16
|
+
durableRevision: currentMeta.durableRevision,
|
|
17
|
+
pendingCommit: { revision: revision, writerId: writerId, startedAt: startedAt },
|
|
18
|
+
writerId: writerId,
|
|
19
|
+
updatedAt: startedAt
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createCommittedMeta(fenceMeta) {
|
|
24
|
+
return {
|
|
25
|
+
...fenceMeta,
|
|
26
|
+
durableRevision: fenceMeta.revision,
|
|
27
|
+
pendingCommit: null,
|
|
28
|
+
updatedAt: new Date().toISOString()
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createRecoveredMeta(currentMeta, fenceMeta, writerId) {
|
|
33
|
+
var revision = fenceMeta.revision + 1;
|
|
34
|
+
return {
|
|
35
|
+
...currentMeta,
|
|
36
|
+
revision: revision,
|
|
37
|
+
durableRevision: revision,
|
|
38
|
+
pendingCommit: null,
|
|
39
|
+
queueRevision: fenceMeta.queueRevision + 1,
|
|
40
|
+
writerId: writerId,
|
|
41
|
+
updatedAt: new Date().toISOString()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function decorateReceiptError(error, revision) {
|
|
46
|
+
if (error && typeof error === 'object') {
|
|
47
|
+
error.code = error.code || 'persistence_receipt_write_failed';
|
|
48
|
+
error.persistenceActionCommitted = true;
|
|
49
|
+
error.persistenceRevision = revision;
|
|
50
|
+
}
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
createFenceMeta: createFenceMeta,
|
|
56
|
+
createCommittedMeta: createCommittedMeta,
|
|
57
|
+
createRecoveredMeta: createRecoveredMeta,
|
|
58
|
+
decorateReceiptError: decorateReceiptError
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
3
|
module.exports = factory(
|
|
4
4
|
require('./scopedPersistenceQueuePolicy'),
|
|
5
|
-
require('./scopedPersistenceBrowserAdapter')
|
|
5
|
+
require('./scopedPersistenceBrowserAdapter'),
|
|
6
|
+
require('./scopedPersistenceReceipt')
|
|
6
7
|
);
|
|
7
8
|
} else {
|
|
8
9
|
root.CodexOverleafModuleRegistry.define('ScopedPersistenceTransaction', [
|
|
9
10
|
'ScopedPersistenceQueuePolicy',
|
|
10
|
-
'ScopedPersistenceBrowserAdapter'
|
|
11
|
+
'ScopedPersistenceBrowserAdapter',
|
|
12
|
+
'ScopedPersistenceReceipt'
|
|
11
13
|
], factory);
|
|
12
14
|
}
|
|
13
|
-
})(typeof globalThis !== 'undefined' ? globalThis : this, function (QueuePolicy, BrowserAdapter) {
|
|
15
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function (QueuePolicy, BrowserAdapter, Receipt) {
|
|
14
16
|
'use strict';
|
|
15
17
|
|
|
16
18
|
var applyQueueMutations = QueuePolicy.applyQueueMutations;
|
|
@@ -43,9 +45,9 @@
|
|
|
43
45
|
var scope = normalizeScope(inputScope);
|
|
44
46
|
generation += 1;
|
|
45
47
|
var tokenGeneration = generation;
|
|
46
|
-
active =
|
|
48
|
+
active = tokenFromMeta(scope, tokenGeneration, false, {});
|
|
47
49
|
var meta = await strictRead(adapter, scope);
|
|
48
|
-
var token =
|
|
50
|
+
var token = tokenFromMeta(scope, tokenGeneration, false, meta);
|
|
49
51
|
if (active && active.generation === tokenGeneration && sameScope(active, token)) active = token;
|
|
50
52
|
return token;
|
|
51
53
|
}
|
|
@@ -60,10 +62,10 @@
|
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
if (!detached && active && sameScope(active, scope)) {
|
|
63
|
-
return
|
|
65
|
+
return tokenFromMeta(scope, active.generation, false, active);
|
|
64
66
|
}
|
|
65
67
|
var meta = await strictRead(adapter, scope);
|
|
66
|
-
return
|
|
68
|
+
return tokenFromMeta(scope, detached ? 0 : generation, detached, meta);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
function isCurrent(token) {
|
|
@@ -94,13 +96,7 @@
|
|
|
94
96
|
if (queueMutations.length
|
|
95
97
|
&& currentMeta.queueRevision !== normalizeRevision(token.queueRevision)) {
|
|
96
98
|
if (!token.detached && isCurrent(token)) {
|
|
97
|
-
active =
|
|
98
|
-
scope,
|
|
99
|
-
token.generation,
|
|
100
|
-
currentMeta.revision,
|
|
101
|
-
false,
|
|
102
|
-
currentMeta.queueRevision
|
|
103
|
-
);
|
|
99
|
+
active = tokenFromMeta(scope, token.generation, false, currentMeta);
|
|
104
100
|
}
|
|
105
101
|
return {
|
|
106
102
|
ok: false,
|
|
@@ -112,33 +108,26 @@
|
|
|
112
108
|
}
|
|
113
109
|
var projected = applyQueueMutations(currentMeta, queueMutations, writerId);
|
|
114
110
|
if (!projected.ok) return projected;
|
|
115
|
-
var nextMeta = {
|
|
111
|
+
var nextMeta = Receipt.createFenceMeta({
|
|
116
112
|
...projected.meta,
|
|
117
|
-
revision: currentMeta.revision + 1,
|
|
118
|
-
writerId: writerId,
|
|
119
|
-
updatedAt: new Date().toISOString(),
|
|
120
113
|
sessionTombstones: mergeTombstones(
|
|
121
114
|
projected.meta.sessionTombstones,
|
|
122
115
|
commitOptions && commitOptions.sessionTombstones,
|
|
123
116
|
200
|
|
124
117
|
)
|
|
125
|
-
};
|
|
118
|
+
}, currentMeta, writerId);
|
|
126
119
|
await adapter.write(scope, nextMeta);
|
|
127
120
|
if (!token.detached && isCurrent(token)) {
|
|
128
|
-
active =
|
|
129
|
-
scope,
|
|
130
|
-
token.generation,
|
|
131
|
-
nextMeta.revision,
|
|
132
|
-
false,
|
|
133
|
-
nextMeta.queueRevision
|
|
134
|
-
);
|
|
121
|
+
active = tokenFromMeta(scope, token.generation, false, nextMeta);
|
|
135
122
|
}
|
|
136
123
|
var value;
|
|
137
124
|
try {
|
|
138
125
|
value = await action({
|
|
139
126
|
scope: scope,
|
|
140
127
|
baseRevision: currentMeta.revision,
|
|
128
|
+
durableRevision: currentMeta.durableRevision,
|
|
141
129
|
conflict: conflict,
|
|
130
|
+
previousCommitIncomplete: Boolean(currentMeta.pendingCommit),
|
|
142
131
|
writerId: writerId,
|
|
143
132
|
currentMeta: currentMeta,
|
|
144
133
|
nextMeta: nextMeta
|
|
@@ -147,23 +136,11 @@
|
|
|
147
136
|
if (queueMutations.some(function (mutation) {
|
|
148
137
|
return ['remove', 'release', 'restore'].includes(cleanPart(mutation.type));
|
|
149
138
|
})) {
|
|
150
|
-
var recoveredMeta =
|
|
151
|
-
...currentMeta,
|
|
152
|
-
revision: nextMeta.revision + 1,
|
|
153
|
-
queueRevision: nextMeta.queueRevision + 1,
|
|
154
|
-
writerId: writerId,
|
|
155
|
-
updatedAt: new Date().toISOString()
|
|
156
|
-
};
|
|
139
|
+
var recoveredMeta = Receipt.createRecoveredMeta(currentMeta, nextMeta, writerId);
|
|
157
140
|
try {
|
|
158
141
|
await adapter.write(scope, recoveredMeta);
|
|
159
142
|
if (!token.detached && isCurrent(token)) {
|
|
160
|
-
active =
|
|
161
|
-
scope,
|
|
162
|
-
token.generation,
|
|
163
|
-
recoveredMeta.revision,
|
|
164
|
-
false,
|
|
165
|
-
recoveredMeta.queueRevision
|
|
166
|
-
);
|
|
143
|
+
active = tokenFromMeta(scope, token.generation, false, recoveredMeta);
|
|
167
144
|
}
|
|
168
145
|
} catch (recoveryError) {
|
|
169
146
|
if (error && typeof error === 'object') {
|
|
@@ -173,13 +150,24 @@
|
|
|
173
150
|
}
|
|
174
151
|
throw error;
|
|
175
152
|
}
|
|
153
|
+
var committedMeta = Receipt.createCommittedMeta(nextMeta);
|
|
154
|
+
try {
|
|
155
|
+
await adapter.write(scope, committedMeta);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
throw Receipt.decorateReceiptError(error, nextMeta.revision);
|
|
158
|
+
}
|
|
159
|
+
if (!token.detached && isCurrent(token)) {
|
|
160
|
+
active = tokenFromMeta(scope, token.generation, false, committedMeta);
|
|
161
|
+
}
|
|
176
162
|
return {
|
|
177
163
|
ok: true,
|
|
178
164
|
value: value,
|
|
179
|
-
revision:
|
|
180
|
-
|
|
181
|
-
|
|
165
|
+
revision: committedMeta.revision,
|
|
166
|
+
durableRevision: committedMeta.durableRevision,
|
|
167
|
+
queueRevision: committedMeta.queueRevision,
|
|
168
|
+
queueClaims: committedMeta.queueClaims,
|
|
182
169
|
rebased: conflict,
|
|
170
|
+
recoveredIncompleteCommit: Boolean(currentMeta.pendingCommit),
|
|
183
171
|
superseded: !token.detached && !isCurrent(token)
|
|
184
172
|
};
|
|
185
173
|
});
|
|
@@ -190,9 +178,7 @@
|
|
|
190
178
|
captureCommitView: captureCommitView,
|
|
191
179
|
commit: commit,
|
|
192
180
|
getActiveView: function () {
|
|
193
|
-
return active
|
|
194
|
-
? makeToken(active, active.generation, active.revision, false, active.queueRevision)
|
|
195
|
-
: null;
|
|
181
|
+
return active ? tokenFromMeta(active, active.generation, false, active) : null;
|
|
196
182
|
},
|
|
197
183
|
getWriterId: function () { return writerId; },
|
|
198
184
|
invalidate: function () {
|
|
@@ -203,17 +189,34 @@
|
|
|
203
189
|
});
|
|
204
190
|
}
|
|
205
191
|
|
|
206
|
-
function makeToken(
|
|
192
|
+
function makeToken(
|
|
193
|
+
scope,
|
|
194
|
+
tokenGeneration,
|
|
195
|
+
revision,
|
|
196
|
+
detached,
|
|
197
|
+
queueRevision,
|
|
198
|
+
durableRevision,
|
|
199
|
+
pendingCommit
|
|
200
|
+
) {
|
|
207
201
|
return Object.freeze({
|
|
208
202
|
accountScopeId: scope.accountScopeId,
|
|
209
203
|
projectId: scope.projectId,
|
|
210
204
|
generation: normalizeRevision(tokenGeneration),
|
|
211
205
|
revision: normalizeRevision(revision),
|
|
206
|
+
durableRevision: normalizeRevision(durableRevision),
|
|
207
|
+
pendingCommit: pendingCommit && typeof pendingCommit === 'object'
|
|
208
|
+
? Object.freeze({ ...pendingCommit })
|
|
209
|
+
: null,
|
|
212
210
|
queueRevision: normalizeRevision(queueRevision),
|
|
213
211
|
detached: detached === true
|
|
214
212
|
});
|
|
215
213
|
}
|
|
216
214
|
|
|
215
|
+
function tokenFromMeta(scope, tokenGeneration, detached, meta) {
|
|
216
|
+
return makeToken(scope, tokenGeneration, meta.revision, detached, meta.queueRevision,
|
|
217
|
+
meta.durableRevision, meta.pendingCommit);
|
|
218
|
+
}
|
|
219
|
+
|
|
217
220
|
async function strictRead(adapter, scope) {
|
|
218
221
|
return normalizeMeta(await adapter.read(scope));
|
|
219
222
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
'use strict';
|
|
25
25
|
|
|
26
26
|
const DEFAULT_PANEL_STATE = {
|
|
27
|
-
mode: '
|
|
27
|
+
mode: 'ask',
|
|
28
28
|
providerId: 'builtin',
|
|
29
29
|
model: 'gpt-5.4',
|
|
30
30
|
reasoningEffort: 'high',
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
customInstructionsByProject: {}
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const VALID_MODES = new Set(['ask', '
|
|
50
|
+
const VALID_MODES = new Set(['ask', 'auto']);
|
|
51
51
|
// Keep the persisted/session-level contract aligned with the model picker
|
|
52
52
|
// and native provider bridge. Custom providers can explicitly disable
|
|
53
53
|
// reasoning (`none`) or expose the lightest Codex tier (`minimal`). Dropping
|
|
@@ -127,9 +127,7 @@
|
|
|
127
127
|
...input
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
state.mode = DEFAULT_PANEL_STATE.mode;
|
|
132
|
-
}
|
|
130
|
+
state.mode = normalizeMode(state.mode);
|
|
133
131
|
if (!VALID_REASONING.has(state.reasoningEffort)) {
|
|
134
132
|
state.reasoningEffort = DEFAULT_PANEL_STATE.reasoningEffort;
|
|
135
133
|
}
|
|
@@ -221,7 +219,7 @@
|
|
|
221
219
|
history,
|
|
222
220
|
runs,
|
|
223
221
|
task: sanitizeAssistantVisibleText(session.task),
|
|
224
|
-
mode:
|
|
222
|
+
mode: normalizeMode(session.mode, fallbackState.mode),
|
|
225
223
|
providerId: typeof session.providerId === 'string' && session.providerId.trim()
|
|
226
224
|
? session.providerId.trim()
|
|
227
225
|
: (fallbackState.providerId || 'builtin'),
|
|
@@ -234,6 +232,7 @@
|
|
|
234
232
|
: fallbackState.speedTier,
|
|
235
233
|
requireReviewing: session.requireReviewing !== false,
|
|
236
234
|
focusFiles: normalizeFocusFiles(session.focusFiles),
|
|
235
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(session.projectReferenceFiles),
|
|
237
236
|
codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
|
|
238
237
|
pendingInputs: normalizePendingInputs(
|
|
239
238
|
session.pendingInputs,
|
|
@@ -311,13 +310,14 @@
|
|
|
311
310
|
id: active.id,
|
|
312
311
|
history: Array.isArray(active.history) ? active.history.slice(-10) : [],
|
|
313
312
|
focusFiles: normalizeFocusFiles(active.focusFiles),
|
|
313
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(active.projectReferenceFiles),
|
|
314
314
|
codexThreadId: active.codexThreadId || '',
|
|
315
315
|
providerId: state.providerId || 'builtin'
|
|
316
316
|
};
|
|
317
317
|
state.runs = Array.isArray(active.runs) ? active.runs : [];
|
|
318
318
|
state.task = typeof active.task === 'string' ? active.task : '';
|
|
319
319
|
state.focusFiles = normalizeFocusFiles(active.focusFiles);
|
|
320
|
-
state.mode =
|
|
320
|
+
state.mode = normalizeMode(active.mode);
|
|
321
321
|
state.requireReviewing = active.requireReviewing !== false;
|
|
322
322
|
|
|
323
323
|
return state;
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
history: normalizeHistoryEntries(overrides.history),
|
|
340
340
|
runs,
|
|
341
341
|
task: sanitizeAssistantVisibleText(overrides.task),
|
|
342
|
-
mode:
|
|
342
|
+
mode: normalizeMode(overrides.mode),
|
|
343
343
|
providerId: typeof overrides.providerId === 'string' && overrides.providerId.trim()
|
|
344
344
|
? overrides.providerId.trim()
|
|
345
345
|
: 'builtin',
|
|
@@ -352,6 +352,7 @@
|
|
|
352
352
|
: DEFAULT_PANEL_STATE.speedTier,
|
|
353
353
|
requireReviewing: overrides.requireReviewing !== false,
|
|
354
354
|
focusFiles: normalizeFocusFiles(overrides.focusFiles),
|
|
355
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(overrides.projectReferenceFiles),
|
|
355
356
|
codexThreadId: typeof overrides.codexThreadId === 'string' ? overrides.codexThreadId : '',
|
|
356
357
|
pendingInputs: normalizePendingInputs(overrides.pendingInputs)
|
|
357
358
|
};
|
|
@@ -904,6 +905,33 @@
|
|
|
904
905
|
return files.slice(0, 5);
|
|
905
906
|
}
|
|
906
907
|
|
|
908
|
+
function normalizeProjectReferenceFiles(value) {
|
|
909
|
+
const seen = new Set();
|
|
910
|
+
const files = [];
|
|
911
|
+
for (const item of Array.isArray(value) ? value : []) {
|
|
912
|
+
const rawPath = typeof item === 'string' ? item : item?.path;
|
|
913
|
+
if (typeof rawPath !== 'string' || !rawPath.trim() || rawPath.length > 512) {
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (/^(?:[A-Za-z]:|[\\/])/.test(rawPath) || rawPath.includes('\0')) {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
const path = rawPath.replace(/\\/g, '/').replace(/\/+/g, '/').replace(/^\.\//, '').trim();
|
|
920
|
+
if (!path || path.split('/').some(segment => !segment || segment === '.' || segment === '..') || seen.has(path)) {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
seen.add(path);
|
|
924
|
+
files.push({
|
|
925
|
+
path,
|
|
926
|
+
kind: item?.kind === 'binary' ? 'binary' : 'text'
|
|
927
|
+
});
|
|
928
|
+
if (files.length >= 5000) {
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return files;
|
|
933
|
+
}
|
|
934
|
+
|
|
907
935
|
function normalizeCustomInstructionsByProject(value) {
|
|
908
936
|
const result = {};
|
|
909
937
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -994,7 +1022,7 @@
|
|
|
994
1022
|
// Skills page was reopened.
|
|
995
1023
|
codexOverleafSkills: compactCodexOverleafSkillsForStorage(source.codexOverleafSkills),
|
|
996
1024
|
composerAttachments: normalizeComposerAttachmentsForPersistence(source.composerAttachments),
|
|
997
|
-
mode:
|
|
1025
|
+
mode: normalizeMode(active?.mode, source.mode),
|
|
998
1026
|
providerId: typeof source.providerId === 'string' && source.providerId.trim()
|
|
999
1027
|
? source.providerId.trim()
|
|
1000
1028
|
: 'builtin',
|
|
@@ -1092,6 +1120,7 @@
|
|
|
1092
1120
|
speedTier: normalizeSpeedTier(session.speedTier || fallbackState.speedTier),
|
|
1093
1121
|
requireReviewing: session.requireReviewing !== false,
|
|
1094
1122
|
focusFiles: normalizeFocusFiles(session.focusFiles),
|
|
1123
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(session.projectReferenceFiles),
|
|
1095
1124
|
codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
|
|
1096
1125
|
pendingInputs: normalizePendingInputs(session.pendingInputs)
|
|
1097
1126
|
};
|
|
@@ -1339,8 +1368,14 @@
|
|
|
1339
1368
|
};
|
|
1340
1369
|
}
|
|
1341
1370
|
|
|
1342
|
-
function normalizeMode(mode) {
|
|
1343
|
-
|
|
1371
|
+
function normalizeMode(mode, fallback = DEFAULT_PANEL_STATE.mode) {
|
|
1372
|
+
if (mode === 'confirm') {
|
|
1373
|
+
return 'ask';
|
|
1374
|
+
}
|
|
1375
|
+
if (VALID_MODES.has(mode)) {
|
|
1376
|
+
return mode;
|
|
1377
|
+
}
|
|
1378
|
+
return VALID_MODES.has(fallback) ? fallback : DEFAULT_PANEL_STATE.mode;
|
|
1344
1379
|
}
|
|
1345
1380
|
|
|
1346
1381
|
function normalizeReasoning(reasoningEffort) {
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
history: compactHistoryForStorage(input.history),
|
|
288
288
|
runs: compactRunsForStorage(input.runs, options),
|
|
289
289
|
task: normalizeDisplayTextForStorage(input.task, SESSION_STORAGE_LIMITS.taskChars),
|
|
290
|
-
mode:
|
|
290
|
+
mode: normalizeCurrentMode(input.mode), providerId: typeof input.providerId === 'string' && input.providerId ? input.providerId : 'builtin',
|
|
291
291
|
model: typeof input.model === 'string' ? input.model : '',
|
|
292
292
|
reasoningEffort: typeof input.reasoningEffort === 'string' ? input.reasoningEffort : '',
|
|
293
293
|
speedTier: typeof input.speedTier === 'string' ? input.speedTier : '',
|
|
@@ -436,13 +436,17 @@
|
|
|
436
436
|
|
|
437
437
|
// --- Helpers ---
|
|
438
438
|
|
|
439
|
+
function normalizeCurrentMode(mode) {
|
|
440
|
+
return mode === 'auto' ? 'auto' : 'ask';
|
|
441
|
+
}
|
|
442
|
+
|
|
439
443
|
function extractLightweightPrefs(state, projectId) {
|
|
440
444
|
var prefs = {
|
|
441
445
|
storageSchemaVersion: TARGET_SCHEMA_VERSION, providerId: typeof state.providerId === 'string' && state.providerId ? state.providerId : 'builtin',
|
|
442
446
|
model: typeof state.model === 'string' ? state.model : '',
|
|
443
447
|
reasoningEffort: typeof state.reasoningEffort === 'string' ? state.reasoningEffort : '',
|
|
444
448
|
speedTier: typeof state.speedTier === 'string' ? state.speedTier : '',
|
|
445
|
-
mode:
|
|
449
|
+
mode: normalizeCurrentMode(state.mode),
|
|
446
450
|
locale: typeof state.locale === 'string' ? state.locale : '',
|
|
447
451
|
requireReviewing: state.requireReviewing !== false,
|
|
448
452
|
autoRecompile: state.autoRecompile !== false,
|
|
@@ -1314,11 +1318,9 @@
|
|
|
1314
1318
|
}
|
|
1315
1319
|
|
|
1316
1320
|
// --- Utilities ---
|
|
1317
|
-
|
|
1318
1321
|
function generateId(prefix) {
|
|
1319
1322
|
return prefix + '_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
|
|
1320
1323
|
}
|
|
1321
|
-
|
|
1322
1324
|
function safeJsonStringify(value) {
|
|
1323
1325
|
try {
|
|
1324
1326
|
return JSON.stringify(value) || '';
|
|
@@ -1326,7 +1328,6 @@
|
|
|
1326
1328
|
return '[unserializable]';
|
|
1327
1329
|
}
|
|
1328
1330
|
}
|
|
1329
|
-
|
|
1330
1331
|
var hashString = StorageRunActions.hashString;
|
|
1331
1332
|
|
|
1332
1333
|
return {
|
|
@@ -114,12 +114,22 @@
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function normalizeTrackedChanges(value) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const normalized = [];
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
for (const item of Array.isArray(value) ? value : []) {
|
|
120
|
+
const change = {
|
|
121
|
+
key: normalizeText(item?.key),
|
|
122
|
+
id: normalizeText(item?.id),
|
|
123
|
+
path: normalizeText(item?.path),
|
|
124
|
+
label: normalizeText(item?.label)
|
|
125
|
+
};
|
|
126
|
+
if (!change.key && !change.id && !change.path) continue;
|
|
127
|
+
const identity = `${change.path}\u0000${change.key || change.id || 'path-only'}`;
|
|
128
|
+
if (seen.has(identity)) continue;
|
|
129
|
+
seen.add(identity);
|
|
130
|
+
normalized.push(change);
|
|
131
|
+
}
|
|
132
|
+
return normalized;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
function normalizeStringArray(value) {
|
|
@@ -270,9 +270,17 @@
|
|
|
270
270
|
}
|
|
271
271
|
const successful = isSuccessfulTrackedChangeSettlement(input.result);
|
|
272
272
|
const reviewCodes = kind === 'accept' ? ACCEPT_NEEDS_REVIEW_CODES : REJECT_NEEDS_REVIEW_CODES;
|
|
273
|
-
|
|
273
|
+
// Reject/Undo is an all-target recovery transaction. A successful entry
|
|
274
|
+
// only proves that one tracked target was restored; any failed/skipped
|
|
275
|
+
// sibling can still leave this run's content in another file. Keep that
|
|
276
|
+
// recovery actionable instead of collapsing a partial multi-file reject
|
|
277
|
+
// into the terminal `rejected` state. Accept retains its established
|
|
278
|
+
// best-effort terminal policy.
|
|
279
|
+
const incompleteReject = kind === 'reject'
|
|
280
|
+
&& collectRunResultSkipped(input.result).length > 0;
|
|
281
|
+
const needsReview = incompleteReject || (!successful && failures.some(failure =>
|
|
274
282
|
failure.terminalState === 'needs_review' || reviewCodes.has(failure.code)
|
|
275
|
-
);
|
|
283
|
+
));
|
|
276
284
|
const status = needsReview ? 'needs_review' : kind === 'accept' ? 'accepted' : 'rejected';
|
|
277
285
|
return {
|
|
278
286
|
decision: status,
|
|
@@ -595,7 +603,12 @@
|
|
|
595
603
|
: operation;
|
|
596
604
|
}
|
|
597
605
|
|
|
598
|
-
function selectExpectedFilesForTrackedUndo(
|
|
606
|
+
function selectExpectedFilesForTrackedUndo(
|
|
607
|
+
project,
|
|
608
|
+
operations = [],
|
|
609
|
+
trackedChanges = [],
|
|
610
|
+
previousExpectedFiles = []
|
|
611
|
+
) {
|
|
599
612
|
const paths = new Set();
|
|
600
613
|
for (const change of trackedChanges || []) {
|
|
601
614
|
if (change?.path) paths.add(change.path);
|
|
@@ -604,9 +617,18 @@
|
|
|
604
617
|
if (operation?.path) paths.add(operation.path);
|
|
605
618
|
if (operation?.to) paths.add(operation.to);
|
|
606
619
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
.
|
|
620
|
+
const expectedByPath = new Map();
|
|
621
|
+
for (const file of previousExpectedFiles || []) {
|
|
622
|
+
if (paths.has(file?.path) && typeof file?.content === 'string' && !expectedByPath.has(file.path)) {
|
|
623
|
+
expectedByPath.set(file.path, { path: file.path, content: file.content });
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
for (const file of project?.files || []) {
|
|
627
|
+
if (paths.has(file?.path) && typeof file?.content === 'string' && !expectedByPath.has(file.path)) {
|
|
628
|
+
expectedByPath.set(file.path, { path: file.path, content: file.content });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return Array.from(expectedByPath.values());
|
|
610
632
|
}
|
|
611
633
|
|
|
612
634
|
function acceptDiagnosticStatus(step, info) {
|
|
@@ -2686,13 +2686,23 @@
|
|
|
2686
2686
|
}
|
|
2687
2687
|
|
|
2688
2688
|
#codex-overleaf-panel .run-math--fallback {
|
|
2689
|
-
border-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2689
|
+
border-bottom: 1px dotted var(--tl-fg-4);
|
|
2690
|
+
border-radius: 0;
|
|
2691
|
+
background: transparent;
|
|
2692
|
+
color: inherit;
|
|
2693
|
+
padding: 0 2px;
|
|
2693
2694
|
white-space: pre-wrap;
|
|
2694
2695
|
overflow-wrap: anywhere;
|
|
2695
|
-
font:
|
|
2696
|
+
font: inherit;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
#codex-overleaf-panel .run-math--display.run-math--fallback {
|
|
2700
|
+
border: 1px solid var(--tl-border);
|
|
2701
|
+
border-left: 3px solid var(--tl-fg-4);
|
|
2702
|
+
border-radius: 6px;
|
|
2703
|
+
background: var(--tl-surface-2);
|
|
2704
|
+
padding: 8px 10px;
|
|
2705
|
+
text-align: left;
|
|
2696
2706
|
}
|
|
2697
2707
|
|
|
2698
2708
|
#codex-overleaf-panel .run-stream[data-stream-role="reasoning"] .run-stream-text {
|
|
@@ -3173,12 +3183,12 @@
|
|
|
3173
3183
|
white-space: nowrap;
|
|
3174
3184
|
}
|
|
3175
3185
|
|
|
3176
|
-
/* Mode switch (Ask/
|
|
3177
|
-
holding
|
|
3186
|
+
/* Mode switch (Ask/Auto) — a real segmented control: a bordered track
|
|
3187
|
+
holding two choices. Inactive choices are muted; hover is a light fill
|
|
3178
3188
|
(distinct from the active accent-wash fill) so hover never reads as active. */
|
|
3179
3189
|
#codex-overleaf-panel .codex-mode-switch {
|
|
3180
3190
|
display: grid;
|
|
3181
|
-
grid-template-columns: repeat(
|
|
3191
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
3182
3192
|
gap: 2px;
|
|
3183
3193
|
min-width: 0;
|
|
3184
3194
|
padding: 2px;
|
|
@@ -64,7 +64,7 @@ function buildCodexTurnPrompt(options = {}) {
|
|
|
64
64
|
'Mode for this turn:',
|
|
65
65
|
`- ${context.mode}`,
|
|
66
66
|
'- ask: inspect and explain only; do not edit files.',
|
|
67
|
-
'-
|
|
67
|
+
'- auto: edit the local workspace directly when the request calls for changes. The browser bridge handles authorization, deletion approval, and syncing back to Overleaf.',
|
|
68
68
|
'',
|
|
69
69
|
'Write expectation for this turn:',
|
|
70
70
|
formatWriteExpectation({
|