codex-overleaf-link 2.5.1 → 2.6.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 +149 -398
- package/README.zh-CN.md +153 -404
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +78 -3
- package/extension/src/backgroundSharedSessions.js +55 -2
- package/extension/src/backgroundUpdateCoordinator.js +128 -12
- package/extension/src/content/activeTurnControl.js +164 -7
- package/extension/src/content/composerAttachments.js +11 -2
- package/extension/src/content/composerPanel.js +92 -12
- package/extension/src/content/contentRuntime.js +920 -276
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsPanel.js +18 -0
- package/extension/src/content/generated/content.bundle.js +4496 -924
- package/extension/src/content/generated/content.bundle.meta.json +6 -3
- package/extension/src/content/legacyGlobalRegistry.js +4 -0
- package/extension/src/content/localSkillsPanel.js +2 -2
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +42 -5
- package/extension/src/content/modelPicker.js +1 -0
- package/extension/src/content/nativeCompatibilityController.js +29 -10
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/pageBridgeClient.js +2 -2
- package/extension/src/content/panelMaintenance.js +6 -2
- package/extension/src/content/panelRenderer.js +51 -27
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectSettingsCoordinator.js +30 -6
- package/extension/src/content/providerSettingsDialog.js +2 -1
- package/extension/src/content/recentProjects.js +4 -3
- package/extension/src/content/runActivitySummary.js +122 -14
- package/extension/src/content/runCompletionMeta.js +194 -6
- package/extension/src/content/runController.js +2 -1
- package/extension/src/content/runFailureNotice.js +8 -1
- package/extension/src/content/runPresence.js +18 -8
- package/extension/src/content/runQueueScheduler.js +9 -5
- package/extension/src/content/runRecoveryActions.js +2 -1
- package/extension/src/content/runResultActions.js +22 -20
- package/extension/src/content/runReviewController.js +94 -35
- package/extension/src/content/runTimelineView.js +224 -25
- package/extension/src/content/scopedPersistencePanelState.js +92 -23
- package/extension/src/content/sensitivePreflight.js +14 -1
- package/extension/src/content/sessionManager.js +49 -3
- package/extension/src/content/sessionMenuView.js +54 -18
- package/extension/src/content/sessionPersistence.js +25 -5
- package/extension/src/content/settingsPanel.js +15 -2
- package/extension/src/content/settingsWorkbench.js +127 -89
- package/extension/src/content/sharedSessionViewSync.js +4 -3
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateNotice.js +60 -6
- package/extension/src/content/writebackOrchestrator.js +35 -22
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/overleafCapabilities.js +4 -1
- package/extension/src/page/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +9 -1
- package/extension/src/page/writebackRouter.js +24 -77
- package/extension/src/pageBridge.js +107 -66
- package/extension/src/popup.js +1 -1
- package/extension/src/shared/agentTranscript.js +350 -17
- package/extension/src/shared/auditRecords.js +2 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +58 -17
- package/extension/src/shared/composerAttachmentDraft.js +6 -1
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/failureReasons.js +42 -3
- package/extension/src/shared/i18n.js +299 -29
- package/extension/src/shared/lineReferences.js +38 -31
- package/extension/src/shared/pageRpcContract.js +1 -1
- package/extension/src/shared/panelIcons.js +39 -0
- package/extension/src/shared/reviewing.js +130 -1
- package/extension/src/shared/runActivityModel.js +70 -4
- package/extension/src/shared/runExecutionSnapshot.js +13 -0
- package/extension/src/shared/sessionState.js +63 -4
- package/extension/src/shared/sessionTitle.js +14 -1
- package/extension/src/shared/sharedSessionBridge.js +81 -13
- package/extension/src/shared/staleGuard.js +72 -63
- package/extension/src/shared/storageDb.js +35 -3
- package/extension/src/shared/storageMigration.js +7 -1
- package/extension/src/shared/storageRunActions.js +62 -8
- package/extension/src/shared/undoOperations.js +126 -28
- package/extension/src/shared/updateRuntimeIdentity.js +16 -2
- package/extension/src/shared/writebackSettlement.js +21 -3
- package/extension/styles/panel.css +1106 -490
- package/native-host/src/bootstrapUpdate.js +52 -16
- package/native-host/src/chatBridgeRequest.js +60 -6
- package/native-host/src/chatBridgeResponse.js +10 -1
- package/native-host/src/chatCompletionsBridge.js +29 -2
- package/native-host/src/codexPromptAssembly.js +86 -118
- package/native-host/src/codexSessionRunner.js +126 -17
- package/native-host/src/providerContextError.js +20 -0
- package/native-host/src/taskRunnerRuntime.js +18 -6
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/updateManager.js +120 -15
- package/package.json +5 -8
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
require('./subagentActivityModel'),
|
|
13
13
|
require('./writebackIntent'),
|
|
14
14
|
require('./directoryCleanupProof'),
|
|
15
|
-
require('./undoOperations')
|
|
15
|
+
require('./undoOperations'),
|
|
16
|
+
require('./contextUsage')
|
|
16
17
|
);
|
|
17
18
|
} else {
|
|
18
19
|
root.CodexOverleafModuleRegistry.define(
|
|
19
20
|
'SessionState',
|
|
20
|
-
['I18n', 'SettlementFacts', 'RunInputQueue', 'SelectionContext', 'LineReferences', 'PathRedaction', 'SessionTitle', 'RunActivityModel', 'SubagentActivityModel', 'WritebackIntent', 'DirectoryCleanupProof', 'UndoOperations'],
|
|
21
|
+
['I18n', 'SettlementFacts', 'RunInputQueue', 'SelectionContext', 'LineReferences', 'PathRedaction', 'SessionTitle', 'RunActivityModel', 'SubagentActivityModel', 'WritebackIntent', 'DirectoryCleanupProof', 'UndoOperations', 'ContextUsage'],
|
|
21
22
|
factory
|
|
22
23
|
);
|
|
23
24
|
}
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
SubagentActivityModel,
|
|
34
35
|
WritebackIntent,
|
|
35
36
|
DirectoryCleanupProof,
|
|
36
|
-
UndoOperations
|
|
37
|
+
UndoOperations,
|
|
38
|
+
ContextUsage
|
|
37
39
|
) {
|
|
38
40
|
'use strict';
|
|
39
41
|
|
|
@@ -250,6 +252,7 @@
|
|
|
250
252
|
selectionContext: SelectionContext.normalize(session.selectionContext),
|
|
251
253
|
projectReferenceFiles: normalizeProjectReferenceFiles(session.projectReferenceFiles),
|
|
252
254
|
codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
|
|
255
|
+
contextUsage: ContextUsage.normalize(session.contextUsage),
|
|
253
256
|
forkedFromSessionId: typeof session.forkedFromSessionId === 'string' ? session.forkedFromSessionId : '',
|
|
254
257
|
forkedFromRunId: typeof session.forkedFromRunId === 'string' ? session.forkedFromRunId : '',
|
|
255
258
|
pendingInputs: normalizePendingInputs(
|
|
@@ -330,6 +333,7 @@
|
|
|
330
333
|
focusFiles: normalizeFocusFiles(active.focusFiles),
|
|
331
334
|
projectReferenceFiles: normalizeProjectReferenceFiles(active.projectReferenceFiles),
|
|
332
335
|
codexThreadId: active.codexThreadId || '',
|
|
336
|
+
contextUsage: ContextUsage.normalize(active.contextUsage),
|
|
333
337
|
providerId: state.providerId || 'builtin'
|
|
334
338
|
};
|
|
335
339
|
state.runs = Array.isArray(active.runs) ? active.runs : [];
|
|
@@ -374,6 +378,7 @@
|
|
|
374
378
|
selectionContext: SelectionContext.normalize(overrides.selectionContext),
|
|
375
379
|
projectReferenceFiles: normalizeProjectReferenceFiles(overrides.projectReferenceFiles),
|
|
376
380
|
codexThreadId: typeof overrides.codexThreadId === 'string' ? overrides.codexThreadId : '',
|
|
381
|
+
contextUsage: ContextUsage.normalize(overrides.contextUsage),
|
|
377
382
|
forkedFromSessionId: typeof overrides.forkedFromSessionId === 'string' ? overrides.forkedFromSessionId : '',
|
|
378
383
|
forkedFromRunId: typeof overrides.forkedFromRunId === 'string' ? overrides.forkedFromRunId : '',
|
|
379
384
|
pendingInputs: normalizePendingInputs(overrides.pendingInputs)
|
|
@@ -439,6 +444,34 @@
|
|
|
439
444
|
});
|
|
440
445
|
}
|
|
441
446
|
|
|
447
|
+
// Composer input changes only the draft on an already normalized state.
|
|
448
|
+
// Keep live run/event objects intact so typing cannot detach their writers.
|
|
449
|
+
function updateActiveSessionDraft(state, task) {
|
|
450
|
+
const active = getActiveSession(state);
|
|
451
|
+
if (!active) {
|
|
452
|
+
return mirrorActiveSession({ ...state });
|
|
453
|
+
}
|
|
454
|
+
const draft = sanitizeAssistantVisibleText(task);
|
|
455
|
+
if (draft === active.task && draft === state.task) {
|
|
456
|
+
return state;
|
|
457
|
+
}
|
|
458
|
+
const rawTitle = typeof active.title === 'string' ? active.title.trim() : '';
|
|
459
|
+
const derivedTitle = deriveSessionTitle(active.runs, task);
|
|
460
|
+
const titleSource = normalizeTitleSource(active.titleSource, rawTitle, derivedTitle);
|
|
461
|
+
const next = {
|
|
462
|
+
...active,
|
|
463
|
+
task: draft,
|
|
464
|
+
updatedAt: new Date().toISOString(),
|
|
465
|
+
title: normalizeSessionTitle(rawTitle, derivedTitle, titleSource),
|
|
466
|
+
titleSource
|
|
467
|
+
};
|
|
468
|
+
return {
|
|
469
|
+
...state,
|
|
470
|
+
sessions: state.sessions.map(session => session === active ? next : session),
|
|
471
|
+
task: next.task
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
442
475
|
function setActiveSession(state, sessionId) {
|
|
443
476
|
if (!(state.sessions || []).some(session => session.id === sessionId)) {
|
|
444
477
|
return mirrorActiveSession(state);
|
|
@@ -665,7 +698,17 @@
|
|
|
665
698
|
...(value.directoryCleanup === true ? { directoryCleanup: true, ...(value.coreRetained === true ? { coreRetained: true } : {}) } : {}) };
|
|
666
699
|
}
|
|
667
700
|
|
|
701
|
+
// Normalization makes UI copies of the same record. This identity is page-local:
|
|
702
|
+
// it never travels through JSON/storage and is never shared by a replacement run.
|
|
703
|
+
const runIdentities = new WeakMap();
|
|
704
|
+
function sameRunIdentity(left, right) {
|
|
705
|
+
return Boolean(left && right && (left === right
|
|
706
|
+
|| (runIdentities.has(left) && runIdentities.get(left) === runIdentities.get(right))));
|
|
707
|
+
}
|
|
708
|
+
|
|
668
709
|
function normalizeRun(run, options = {}) {
|
|
710
|
+
const sourceRun = run;
|
|
711
|
+
|
|
669
712
|
if (options.restoreRunningRuns === true || options.recoverSettledRuns === true) run = recoverSettledRunningRun(run);
|
|
670
713
|
run = recoverRecordedRunFailure(run, options.locale);
|
|
671
714
|
const shouldStopRestoredRun = options.restoreRunningRuns === true && run.status === 'running';
|
|
@@ -681,6 +724,12 @@
|
|
|
681
724
|
status: 'failed',
|
|
682
725
|
kind: 'report',
|
|
683
726
|
detail: i18n.t(locale, 'restoredRunStoppedDetail'),
|
|
727
|
+
detailStructured: {
|
|
728
|
+
conclusion: i18n.t(locale, 'restoredRunStoppedDetail'),
|
|
729
|
+
conclusionSource: { origin: 'product', key: 'reload_interrupted' },
|
|
730
|
+
// Ask forbids writeback; an interrupted Auto run has no zero-write proof.
|
|
731
|
+
meta: run.mode === 'ask' ? [{ key: 'writeResult', value: 'wrote 0 items, skipped 0 items' }] : []
|
|
732
|
+
},
|
|
684
733
|
failure: { code: 'native_request_failed', source: 'panel_reload' },
|
|
685
734
|
timestamp: new Date().toISOString()
|
|
686
735
|
});
|
|
@@ -715,7 +764,9 @@
|
|
|
715
764
|
undoStatus: sanitizeAssistantVisibleText(run.undoStatus),
|
|
716
765
|
...(run.undoOwnerProtocol === 1 ? { undoOwnerProtocol: 1 } : {}),
|
|
717
766
|
nativeRequestId: sanitizeAssistantVisibleText(run.nativeRequestId),
|
|
767
|
+
...(run.operation === 'compact' ? { operation: 'compact' } : {}),
|
|
718
768
|
codexThreadId: sanitizeAssistantVisibleText(run.codexThreadId), codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
|
|
769
|
+
contextUsage: run.forkSnapshot === true ? null : ContextUsage.normalize(run.contextUsage),
|
|
719
770
|
forkSnapshot: run.forkSnapshot === true,
|
|
720
771
|
forkSourceRunId: sanitizeAssistantVisibleText(run.forkSourceRunId),
|
|
721
772
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
@@ -744,6 +795,9 @@
|
|
|
744
795
|
applyTrackedChangeStatus(normalized, run.trackedChangeStatus);
|
|
745
796
|
Object.assign(normalized, pickWritebackRecovery(run));
|
|
746
797
|
|
|
798
|
+
const identity = runIdentities.get(sourceRun) || {};
|
|
799
|
+
runIdentities.set(sourceRun, identity);
|
|
800
|
+
runIdentities.set(normalized, identity);
|
|
747
801
|
return normalized;
|
|
748
802
|
}
|
|
749
803
|
|
|
@@ -1231,6 +1285,7 @@
|
|
|
1231
1285
|
selectionContext: SelectionContext.normalize(session.selectionContext),
|
|
1232
1286
|
projectReferenceFiles: normalizeProjectReferenceFiles(session.projectReferenceFiles),
|
|
1233
1287
|
codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
|
|
1288
|
+
contextUsage: ContextUsage.normalize(session.contextUsage),
|
|
1234
1289
|
forkedFromSessionId: normalizeTextField(session.forkedFromSessionId, 160),
|
|
1235
1290
|
forkedFromRunId: normalizeTextField(session.forkedFromRunId, 160),
|
|
1236
1291
|
pendingInputs: normalizePendingInputs(session.pendingInputs)
|
|
@@ -1319,7 +1374,7 @@
|
|
|
1319
1374
|
events: compactRunEvents(run.events, limits),
|
|
1320
1375
|
...(run.subagents?.length ? { subagents: SubagentActivityModel.normalize(run.subagents, sanitizeAssistantVisibleText) } : {}),
|
|
1321
1376
|
attachments: compactRunAttachmentsForStorage(run.attachments, limits),
|
|
1322
|
-
appliedOperations:
|
|
1377
|
+
appliedOperations: UndoOperations.compactUnavailableUndoOperations(run),
|
|
1323
1378
|
undoOperations: undoPayload.undoOperations,
|
|
1324
1379
|
undoBaseFiles: undoPayload.undoBaseFiles,
|
|
1325
1380
|
undoTrackedChanges: undoPayload.undoTrackedChanges,
|
|
@@ -1332,7 +1387,9 @@
|
|
|
1332
1387
|
undoStatus: summarizeTextForStorage(run.undoStatus, 'undo status'),
|
|
1333
1388
|
...(run.undoOwnerProtocol === 1 ? { undoOwnerProtocol: 1 } : {}),
|
|
1334
1389
|
nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
|
|
1390
|
+
...(run.operation === 'compact' ? { operation: 'compact' } : {}),
|
|
1335
1391
|
codexThreadId: normalizeTextField(run.codexThreadId, 160), codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
1392
|
+
contextUsage: run.forkSnapshot === true ? null : ContextUsage.normalize(run.contextUsage),
|
|
1336
1393
|
forkSnapshot: run.forkSnapshot === true,
|
|
1337
1394
|
forkSourceRunId: normalizeTextField(run.forkSourceRunId, 160),
|
|
1338
1395
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
@@ -1744,6 +1801,7 @@
|
|
|
1744
1801
|
}
|
|
1745
1802
|
|
|
1746
1803
|
return {
|
|
1804
|
+
sameRunIdentity,
|
|
1747
1805
|
normalizeRecoveryRetention,
|
|
1748
1806
|
pruneRunRecoveryScope,
|
|
1749
1807
|
pickWritebackRecovery,
|
|
@@ -1759,6 +1817,7 @@
|
|
|
1759
1817
|
selectVisibleSessionsForList,
|
|
1760
1818
|
setActiveSession,
|
|
1761
1819
|
updateActiveSession,
|
|
1820
|
+
updateActiveSessionDraft,
|
|
1762
1821
|
normalizeRuns,
|
|
1763
1822
|
normalizeRunStatus,
|
|
1764
1823
|
recoverRecordedRunFailure,
|
|
@@ -25,6 +25,19 @@
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function fromPrompt(value, { sanitize = text => text, maxChars = 24 } = {}) {
|
|
28
|
+
let title = projectPrompt(value, { sanitize, maxChars });
|
|
29
|
+
// Display titles pass through normalization again on unrelated callbacks.
|
|
30
|
+
// Finish removing nested formatting/prefixes now, before acknowledging a title.
|
|
31
|
+
// The bound guards against an input that never settles.
|
|
32
|
+
for (let pass = 0; pass < 8; pass += 1) {
|
|
33
|
+
const next = projectPrompt(title, { sanitize, maxChars });
|
|
34
|
+
if (next === title) return title;
|
|
35
|
+
title = next;
|
|
36
|
+
}
|
|
37
|
+
return title;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function projectPrompt(value, { sanitize, maxChars }) {
|
|
28
41
|
if (typeof value !== 'string' || !value.trim()) return '';
|
|
29
42
|
const source = String(sanitize(value.slice(0, INPUT_LIMIT)) || '');
|
|
30
43
|
let fence = null;
|
|
@@ -75,7 +88,7 @@
|
|
|
75
88
|
.replace(/\[(?:REDACTED_SECRET|local path(?::\d+)?)\]/gi, ' ')
|
|
76
89
|
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '')
|
|
77
90
|
.replace(/\s+/g, ' ').trim()
|
|
78
|
-
.replace(/^(?:\u8bf7(?:\u4f60)?\u5e2e\u6211|\u8bf7\u4f60|\u5e2e\u6211|\u9ebb\u70e6\u4f60(?:\u5e2e\u6211)?|\u9ebb\u70e6\u5e2e\u6211|please\s+)\s
|
|
91
|
+
.replace(/^(?:(?:\u8bf7(?:\u4f60)?\u5e2e\u6211|\u8bf7\u4f60|\u5e2e\u6211|\u9ebb\u70e6\u4f60(?:\u5e2e\u6211)?|\u9ebb\u70e6\u5e2e\u6211|please\s+)\s*)+/i, '');
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
function isScaffolding(value) {
|
|
@@ -160,7 +160,9 @@
|
|
|
160
160
|
Object.defineProperty(record, BASE, { value: base, enumerable: true });
|
|
161
161
|
return record;
|
|
162
162
|
}
|
|
163
|
-
|
|
163
|
+
// List reads keep a per-record merge base without changing the visible view's
|
|
164
|
+
// baseline. Hydration explicitly adopts; passive refresh adopts after apply.
|
|
165
|
+
async function list(projectId = '', adopt = false) {
|
|
164
166
|
const value = capture(projectId);
|
|
165
167
|
await migrate(value);
|
|
166
168
|
const result = []; let after = '';
|
|
@@ -185,29 +187,94 @@
|
|
|
185
187
|
return Boolean(base && (session.updatedAt !== base.updatedAt || session.task !== base.task));
|
|
186
188
|
}
|
|
187
189
|
function wasStored(id) { return baselines.has(cacheKey(capture(), id)); }
|
|
190
|
+
function acknowledgeReviewBaseline(base, record, receipt, runIds) {
|
|
191
|
+
if (!base || !receipt) return base;
|
|
192
|
+
// Only this caller's acknowledged review fields have been submitted.
|
|
193
|
+
// The envelope base still describes its own view, never the latest read.
|
|
194
|
+
return { ...base, runs: (base.runs || []).map(run => {
|
|
195
|
+
const proposed = record.runs?.find(item => item.id === run.id);
|
|
196
|
+
const saved = receipt.runs?.find(item => item.id === run.id);
|
|
197
|
+
return runIds.includes(run.id) && proposed && saved
|
|
198
|
+
&& legacy.reviewStateSnapshot(proposed) === legacy.reviewStateSnapshot(saved)
|
|
199
|
+
? legacy.copyRunReviewState(run, proposed) : run;
|
|
200
|
+
}) };
|
|
201
|
+
}
|
|
188
202
|
async function put(records, options = {}) {
|
|
189
203
|
if (!records.length) return [];
|
|
190
204
|
const value = capture(records[0].projectId, records[0].accountScopeId);
|
|
191
205
|
if (records.some(record => record.projectId !== value.projectId || record.accountScopeId !== value.accountScopeId))
|
|
192
206
|
throw failure('shared_store_scope_mismatch', 'A session batch must belong to one account and project.');
|
|
193
207
|
return withWrite(value, async () => {
|
|
194
|
-
const entries = records.map(record =>
|
|
195
|
-
record
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
const entries = records.map(record => {
|
|
209
|
+
let base = record[BASE] || baselines.get(cacheKey(value, record.id)) || null;
|
|
210
|
+
const receipt = options.reviewReceipts?.find(saved => saved.id === record.id
|
|
211
|
+
&& saved.projectId === record.projectId && saved.accountScopeId === record.accountScopeId);
|
|
212
|
+
base = acknowledgeReviewBaseline(base, record, receipt, options.reviewRunIds || []);
|
|
213
|
+
return { record: clean(record), base };
|
|
214
|
+
});
|
|
198
215
|
const transfer = await Transfer.encode(entries);
|
|
199
216
|
const written = await request(transfer.method, value, { ...transfer.payload, reviewRunIds: options.reviewRunIds || [] });
|
|
200
217
|
// Keep the caller's submitted view as its merge baseline. A merged
|
|
201
218
|
// remote change has not necessarily been applied to the visible UI.
|
|
202
|
-
|
|
219
|
+
if (options.adopt !== false)
|
|
220
|
+
for (const entry of entries) baselines.set(cacheKey(value, entry.record.id), clean(entry.record));
|
|
203
221
|
return written;
|
|
204
222
|
});
|
|
205
223
|
}
|
|
206
|
-
async function get(id) {
|
|
224
|
+
async function get(id, options = {}) {
|
|
207
225
|
const value = capture();
|
|
208
226
|
await migrate(value);
|
|
209
227
|
const record = await request('get', value, { id });
|
|
210
|
-
return tag(record, value, !heldLocks.has(lockKey('state', value)));
|
|
228
|
+
return tag(record, value, options.adopt !== false && !heldLocks.has(lockKey('state', value)));
|
|
229
|
+
}
|
|
230
|
+
async function putSessionReviewState(record, runIds) {
|
|
231
|
+
const value = capture(record.projectId, record.accountScopeId);
|
|
232
|
+
const ids = new Set(runIds);
|
|
233
|
+
const assertOwners = () => {
|
|
234
|
+
assertScope(value);
|
|
235
|
+
for (const runId of ids) {
|
|
236
|
+
const owner = heldLocks.get(lockKey('review', { ...value, runId }));
|
|
237
|
+
if (!owner || owner.lost) throw failure('storage_lock_lost', 'The shared review owner disconnected.');
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
assertOwners();
|
|
241
|
+
return withWrite(value, async () => {
|
|
242
|
+
assertOwners();
|
|
243
|
+
const latest = await request('get', value, { id: record.id });
|
|
244
|
+
if (!latest) throw failure('shared_session_deleted', 'The session no longer exists.');
|
|
245
|
+
if (latest.projectId !== value.projectId || latest.accountScopeId !== value.accountScopeId)
|
|
246
|
+
throw failure('shared_store_scope_mismatch', 'The session does not belong to the captured account and project.');
|
|
247
|
+
const proposed = new Map((record.runs || []).map(run => [run.id, run]));
|
|
248
|
+
const identity = run => JSON.stringify(['id', 'runProjectId', 'runAccountScopeId', 'startedAt', 'nativeEventSeq',
|
|
249
|
+
'codexThreadId', 'codexTurnId', 'executionSnapshot', 'forkSnapshot'].map(key => run[key] ?? null));
|
|
250
|
+
for (const id of ids) {
|
|
251
|
+
const current = latest.runs?.find(run => run.id === id), incoming = proposed.get(id);
|
|
252
|
+
if (!current || !incoming || incoming.runProjectId !== value.projectId
|
|
253
|
+
|| (incoming.runAccountScopeId && incoming.runAccountScopeId !== value.accountScopeId)
|
|
254
|
+
|| identity(current) !== identity(incoming))
|
|
255
|
+
throw failure('review_context_changed', 'The shared review run is no longer available in its original scope.');
|
|
256
|
+
}
|
|
257
|
+
const next = { ...clean(latest), runs: latest.runs.map(run => {
|
|
258
|
+
if (!ids.has(run.id) || run.undoStatus === 'applied' || ['accepted', 'rejected'].includes(run.trackedChangeStatus)) return run;
|
|
259
|
+
return legacy.copyRunReviewState(run, proposed.get(run.id));
|
|
260
|
+
}) };
|
|
261
|
+
const transfer = await Transfer.encode([{ record: next, base: clean(latest) }]);
|
|
262
|
+
assertOwners();
|
|
263
|
+
await request(transfer.method, value, { ...transfer.payload, reviewRunIds: [...ids] });
|
|
264
|
+
const saved = await request('get', value, { id: record.id });
|
|
265
|
+
assertOwners();
|
|
266
|
+
if (!saved || saved.projectId !== value.projectId || saved.accountScopeId !== value.accountScopeId
|
|
267
|
+
|| [...ids].some(id => {
|
|
268
|
+
const run = saved.runs?.find(run => run.id === id);
|
|
269
|
+
return !run || identity(run) !== identity(proposed.get(id))
|
|
270
|
+
|| legacy.reviewStateSnapshot(run) !== legacy.reviewStateSnapshot(proposed.get(id));
|
|
271
|
+
})) throw failure('review_state_not_persisted', 'The review outcome was not durably saved. Check Overleaf before retrying.');
|
|
272
|
+
// A later envelope retry must also recognize the review already saved,
|
|
273
|
+
// without adopting any peer envelope fields from this readback.
|
|
274
|
+
const key = cacheKey(value, record.id), base = baselines.get(key);
|
|
275
|
+
if (base) baselines.set(key, acknowledgeReviewBaseline(base, record, saved, [...ids]));
|
|
276
|
+
return saved;
|
|
277
|
+
});
|
|
211
278
|
}
|
|
212
279
|
async function erase(method, projectId = '', id, accountScopeId) {
|
|
213
280
|
const value = capture(projectId, accountScopeId);
|
|
@@ -228,7 +295,7 @@
|
|
|
228
295
|
return record;
|
|
229
296
|
});
|
|
230
297
|
route('getAllSessions', () => list());
|
|
231
|
-
route('getRecord', (store, id) => store === 'sessions' ? get(id) : api.getRecord(store, id));
|
|
298
|
+
route('getRecord', (store, id, options) => store === 'sessions' ? get(id, options) : api.getRecord(store, id));
|
|
232
299
|
route('getAllByIndex', async (store, index, value) => {
|
|
233
300
|
if (store !== 'sessions') return api.getAllByIndex(store, index, value);
|
|
234
301
|
const records = await list(index === 'projectId' ? value : '');
|
|
@@ -236,10 +303,10 @@
|
|
|
236
303
|
});
|
|
237
304
|
route('claimSessionsForAccount', async (projectId, accountScopeId, deleted = []) => {
|
|
238
305
|
capture(projectId, accountScopeId);
|
|
239
|
-
return (await list(projectId)).filter(record => !deleted.includes(record.id));
|
|
306
|
+
return (await list(projectId, true)).filter(record => !deleted.includes(record.id));
|
|
240
307
|
});
|
|
241
|
-
route('putRecord', async (store, record) =>
|
|
242
|
-
store === 'sessions' ? (await put([record]))[0] : api.putRecord(store, record));
|
|
308
|
+
route('putRecord', async (store, record, options) =>
|
|
309
|
+
store === 'sessions' ? (await put([record], options))[0] : api.putRecord(store, record));
|
|
243
310
|
route('putRecords', (store, records, options) =>
|
|
244
311
|
store === 'sessions' ? put(records, options) : api.putRecords(store, records));
|
|
245
312
|
route('deleteRecord', (store, id, expectedScope) =>
|
|
@@ -280,7 +347,8 @@
|
|
|
280
347
|
readSharedProjectSnapshot: projectId => list(projectId, false),
|
|
281
348
|
adoptSharedSessionBaseline: adopt,
|
|
282
349
|
hasUnpersistedSharedSession: hasLocalChanges,
|
|
283
|
-
wasSharedSessionStored: wasStored
|
|
350
|
+
wasSharedSessionStored: wasStored,
|
|
351
|
+
putSessionReviewState
|
|
284
352
|
});
|
|
285
353
|
return wrapped;
|
|
286
354
|
}
|
|
@@ -45,72 +45,62 @@
|
|
|
45
45
|
const baseContent = normalizeText(baseFileLookup.get(filePath));
|
|
46
46
|
const current = normalizeText(currentContent);
|
|
47
47
|
if (current !== baseContent) {
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'base[0..80]:', JSON.stringify(baseContent.slice(0, 80)),
|
|
52
|
-
'current[0..80]:', JSON.stringify(current.slice(0, 80)));
|
|
48
|
+
if (Array.isArray(operation.patches) && operation.patches.length) {
|
|
49
|
+
const preparedPatches = prepareOperationPatches(operation, currentContent, baseFileLookup);
|
|
50
|
+
if (preparedPatches.ok) return { ok: true, reconciled: true, strategy: 'change-envelope', preparedPatches };
|
|
53
51
|
}
|
|
54
|
-
|
|
55
|
-
if (patchRangeFreshness) {
|
|
56
|
-
return patchRangeFreshness;
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
code: 'stale_snapshot',
|
|
61
|
-
reasonKey: 'staleSnapshot',
|
|
62
|
-
reasonParams: { filePath },
|
|
63
|
-
reason: `${filePath} 在任务执行期间被你或协作者改过,Codex 没有覆盖它。请查看差异后重试。`
|
|
64
|
-
};
|
|
52
|
+
return staleSourceChanged(filePath);
|
|
65
53
|
}
|
|
66
54
|
|
|
67
55
|
return { ok: true };
|
|
68
56
|
}
|
|
69
57
|
|
|
70
|
-
function
|
|
71
|
-
const prepared = prepareOperationPatches(operation, currentContent, baseFileLookup);
|
|
72
|
-
if (!prepared.ok) {
|
|
73
|
-
return { ...prepared, code: 'stale_patch_range', reasonKey: 'stalePatchConflict' };
|
|
74
|
-
}
|
|
75
|
-
const patches = prepared.patches;
|
|
76
|
-
if (!patches.length) {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const current = String(currentContent ?? '');
|
|
81
|
-
for (const rawPatch of patches) {
|
|
82
|
-
const from = Number(rawPatch?.from);
|
|
83
|
-
const to = Number(rawPatch?.to);
|
|
84
|
-
const expected = String(rawPatch?.expected ?? '');
|
|
85
|
-
if (!Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to < from || to > current.length) {
|
|
86
|
-
return {
|
|
87
|
-
ok: false,
|
|
88
|
-
code: 'stale_patch_range',
|
|
89
|
-
reasonKey: 'stalePatchLocation',
|
|
90
|
-
reason: 'Codex 要修改的位置已经无法和当前 Overleaf 内容对齐,所以没有写入。请重新运行任务。'
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
if (current.slice(from, to) !== expected) {
|
|
94
|
-
return {
|
|
95
|
-
ok: false,
|
|
96
|
-
code: 'stale_patch_range',
|
|
97
|
-
reasonKey: 'stalePatchConflict',
|
|
98
|
-
reason: 'Codex 要修改的具体位置已经被你或协作者改过,所以没有覆盖它。请查看差异后重试。'
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
58
|
+
function staleSourceChanged(filePath) {
|
|
103
59
|
return {
|
|
104
|
-
ok:
|
|
105
|
-
|
|
106
|
-
|
|
60
|
+
ok: false,
|
|
61
|
+
code: 'stale_snapshot',
|
|
62
|
+
reasonKey: 'staleSnapshot',
|
|
63
|
+
reasonParams: { filePath },
|
|
64
|
+
reason: `${filePath} 在任务执行期间被你或协作者改过,Codex 没有覆盖它。请查看差异后重试。`
|
|
107
65
|
};
|
|
108
66
|
}
|
|
109
67
|
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// range check and the eventual editor dispatch use the same raw patches.
|
|
68
|
+
// Rebase only ranges wholly in a common prefix/suffix. All patches use the
|
|
69
|
+
// same envelope before any of them are applied to the editor.
|
|
113
70
|
function prepareOperationPatches(operation, currentContent, baseFileLookup) {
|
|
71
|
+
const path = normalizePath(operation?.path), base = baseFileLookup?.get(path);
|
|
72
|
+
const current = String(currentContent ?? '');
|
|
73
|
+
const envelope = typeof base === 'string' ? changedEnvelope(normalizeText(base), normalizeText(current)) : null;
|
|
74
|
+
const prepared = mapOperationPatches(operation, current, baseFileLookup, envelope);
|
|
75
|
+
return !prepared.ok && envelope ? staleSourceChanged(path) : prepared;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function changedEnvelope(base, current) {
|
|
79
|
+
if (base === current) return null;
|
|
80
|
+
let prefix = 0, suffix = 0;
|
|
81
|
+
while (prefix < base.length && prefix < current.length && base[prefix] === current[prefix]) prefix++;
|
|
82
|
+
while (suffix < base.length - prefix && suffix < current.length - prefix
|
|
83
|
+
&& base[base.length - suffix - 1] === current[current.length - suffix - 1]) suffix++;
|
|
84
|
+
return { prefix, end: base.length - suffix, delta: current.length - base.length };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function mapEnvelopeRange(from, to, envelope, insertion = from === to) {
|
|
88
|
+
if (!envelope) return { from, to };
|
|
89
|
+
if (insertion && (from === envelope.prefix || from === envelope.end)) return null;
|
|
90
|
+
if (to <= envelope.prefix) return { from, to };
|
|
91
|
+
if (from >= envelope.end) return { from: from + envelope.delta, to: to + envelope.delta };
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Historical checkpoints already refer to observed applied offsets. This
|
|
96
|
+
// compatibility converter never authorizes a new forward write or rebase.
|
|
97
|
+
function prepareStoredInversePatches(operation, currentContent, baseFileLookup) {
|
|
98
|
+
return mapOperationPatches(operation, currentContent, baseFileLookup, null);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Logical LF offsets bridge raw Native and editor coordinates. Expand split
|
|
102
|
+
// CRLF edges before dispatch; keep unchanged editor line endings intact.
|
|
103
|
+
function mapOperationPatches(operation, currentContent, baseFileLookup, envelope) {
|
|
114
104
|
const patches = Array.isArray(operation?.patches) ? operation.patches : [];
|
|
115
105
|
const current = String(currentContent ?? '');
|
|
116
106
|
const path = normalizePath(operation?.path);
|
|
@@ -120,8 +110,21 @@
|
|
|
120
110
|
}
|
|
121
111
|
const baseMap = lineEndingMap(base);
|
|
122
112
|
const currentMap = lineEndingMap(current);
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
if (!baseMap.removed.length && !currentMap.removed.length) {
|
|
114
|
+
if (!envelope) return { ok: true, patches };
|
|
115
|
+
const mapped = [];
|
|
116
|
+
let previousTo = 0;
|
|
117
|
+
for (const patch of patches) {
|
|
118
|
+
const from = Number(patch?.from), to = Number(patch?.to), expected = String(patch?.expected ?? '');
|
|
119
|
+
if (!Number.isInteger(from) || !Number.isInteger(to) || from < previousTo || to < from || to > base.length
|
|
120
|
+
|| base.slice(from, to) !== expected) return patchMappingFailure('stale_patch');
|
|
121
|
+
previousTo = to;
|
|
122
|
+
const range = mapEnvelopeRange(from, to, envelope);
|
|
123
|
+
if (!range || current.slice(range.from, range.to) !== expected) return patchMappingFailure('stale_patch');
|
|
124
|
+
mapped.push({ ...range, expected, insert: String(patch.insert ?? '') });
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, patches: mapped };
|
|
127
|
+
}
|
|
125
128
|
|
|
126
129
|
const groups = [];
|
|
127
130
|
let previousTo = 0;
|
|
@@ -132,6 +135,11 @@
|
|
|
132
135
|
}
|
|
133
136
|
if (base.slice(from, to) !== String(patch.expected ?? '')) return patchMappingFailure('stale_patch');
|
|
134
137
|
previousTo = to;
|
|
138
|
+
// Check each insertion boundary even if CRLF expansion later merges it
|
|
139
|
+
// with an adjacent nonempty range.
|
|
140
|
+
if (!mapEnvelopeRange(baseMap.toLogical(from), baseMap.toLogical(to), envelope, from === to)) {
|
|
141
|
+
return patchMappingFailure('stale_patch');
|
|
142
|
+
}
|
|
135
143
|
// Expand only a split CRLF or a new pair formed across an edit edge.
|
|
136
144
|
// Other neighbouring newlines belong to the editor and stay untouched.
|
|
137
145
|
let start = from, end = to;
|
|
@@ -147,9 +155,9 @@
|
|
|
147
155
|
|
|
148
156
|
const mapped = [], sourceChanges = [];
|
|
149
157
|
for (const group of groups) {
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
const from = currentMap.toRaw(
|
|
158
|
+
const range = mapEnvelopeRange(baseMap.toLogical(group.start), baseMap.toLogical(group.end), envelope);
|
|
159
|
+
if (!range || range.from < 0 || range.to > currentMap.length) return patchMappingFailure('stale_patch');
|
|
160
|
+
const from = currentMap.toRaw(range.from), to = currentMap.toRaw(range.to);
|
|
153
161
|
const expected = current.slice(from, to);
|
|
154
162
|
if (normalizeText(expected) !== normalizeText(base.slice(group.start, group.end))) {
|
|
155
163
|
return patchMappingFailure('stale_patch');
|
|
@@ -173,13 +181,13 @@
|
|
|
173
181
|
while (oldEnd > prefix && newEnd > prefix && expected[oldEnd - 1] === insert[newEnd - 1]) { oldEnd -= 1; newEnd -= 1; }
|
|
174
182
|
mapped.push({ from: from + prefix, to: from + oldEnd, expected: expected.slice(prefix, oldEnd), insert: insert.slice(prefix, newEnd) });
|
|
175
183
|
}
|
|
176
|
-
//
|
|
177
|
-
//
|
|
184
|
+
// Preserve source coordinates for line-ending-only conversion. A rebased
|
|
185
|
+
// entry instead advances to its actual verified editor post-image.
|
|
178
186
|
let sourceContent = base;
|
|
179
187
|
for (const patch of sourceChanges.reverse()) {
|
|
180
188
|
sourceContent = sourceContent.slice(0, patch.from) + patch.insert + sourceContent.slice(patch.to);
|
|
181
189
|
}
|
|
182
|
-
return { ok: true, patches: mapped, sourceContent };
|
|
190
|
+
return { ok: true, patches: mapped, ...(!envelope ? { sourceContent } : {}) };
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
function patchMappingFailure(code) {
|
|
@@ -257,6 +265,7 @@
|
|
|
257
265
|
buildBaseFileLookup,
|
|
258
266
|
checkOperationFreshness,
|
|
259
267
|
prepareOperationPatches,
|
|
268
|
+
prepareStoredInversePatches,
|
|
260
269
|
updateExpectedFileContent,
|
|
261
270
|
removeExpectedFile,
|
|
262
271
|
moveExpectedFile,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(function initStorageDb(root, factory) {
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
3
|
module.exports = factory(require('./storageRunActions'), require('./runInputQueue'),
|
|
4
|
-
require('./settlementFacts'), require('./storageSessionClaims'), require('./sessionState'), require('./runActivityModel'), require('./sharedSessionBridge'), require('./subagentActivityModel'), require('./storageValuePruning'), require('./composerAttachmentDraft'), require('./sharedSessionTransfer'));
|
|
4
|
+
require('./settlementFacts'), require('./storageSessionClaims'), require('./sessionState'), require('./runActivityModel'), require('./sharedSessionBridge'), require('./subagentActivityModel'), require('./storageValuePruning'), require('./composerAttachmentDraft'), require('./sharedSessionTransfer'), require('./contextUsage'));
|
|
5
5
|
} else {
|
|
6
6
|
root.CodexOverleafModuleRegistry.define('StorageDb', [
|
|
7
7
|
'StorageRunActions',
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
'SubagentActivityModel',
|
|
15
15
|
'StorageValuePruning',
|
|
16
16
|
'ComposerAttachmentDraft',
|
|
17
|
-
'SharedSessionTransfer'
|
|
17
|
+
'SharedSessionTransfer',
|
|
18
|
+
'ContextUsage'
|
|
18
19
|
], factory);
|
|
19
20
|
}
|
|
20
|
-
})(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims, SessionState, RunActivityModel, SharedSessionBridge, SubagentActivityModel, StorageValuePruning, ComposerAttachmentDraft, SharedSessionTransfer) {
|
|
21
|
+
})(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims, SessionState, RunActivityModel, SharedSessionBridge, SubagentActivityModel, StorageValuePruning, ComposerAttachmentDraft, SharedSessionTransfer, ContextUsage) {
|
|
21
22
|
'use strict';
|
|
22
23
|
const { removeEmptySummaryFields, removeEmptyFields } = StorageValuePruning;
|
|
23
24
|
if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactRunExecutionSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
|
|
@@ -276,6 +277,7 @@
|
|
|
276
277
|
title: normalizeSessionTitleForStorage(input.title, titleSource),
|
|
277
278
|
titleSource: titleSource,
|
|
278
279
|
codexThreadId: typeof input.codexThreadId === 'string' ? input.codexThreadId : '',
|
|
280
|
+
contextUsage: ContextUsage.normalize(input.contextUsage),
|
|
279
281
|
forkedFromSessionId: normalizeTextField(input.forkedFromSessionId, 160),
|
|
280
282
|
forkedFromRunId: normalizeTextField(input.forkedFromRunId, 160),
|
|
281
283
|
status: typeof input.status === 'string' && input.status ? input.status : 'active',
|
|
@@ -303,6 +305,17 @@
|
|
|
303
305
|
accountScopeUnavailable: accountScopeUnavailable,
|
|
304
306
|
safeTaskSummary: safeTaskSummary
|
|
305
307
|
};
|
|
308
|
+
// Optional provenance for the automatic clear of one captured submission.
|
|
309
|
+
// It is envelope metadata, not run/review authority; no schema migration is needed.
|
|
310
|
+
if (record.task === '' && typeof input.draftConsumption?.task === 'string'
|
|
311
|
+
&& input.draftConsumption.task && typeof input.draftConsumption.runId === 'string'
|
|
312
|
+
&& input.draftConsumption.runId && record.runs.some(run => run.id === input.draftConsumption.runId
|
|
313
|
+
&& !run.forkSnapshot && run.runProjectId === record.projectId)) {
|
|
314
|
+
record.draftConsumption = {
|
|
315
|
+
runId: input.draftConsumption.runId,
|
|
316
|
+
task: normalizeDisplayTextForStorage(input.draftConsumption.task, SESSION_STORAGE_LIMITS.taskChars)
|
|
317
|
+
};
|
|
318
|
+
}
|
|
306
319
|
if (options.preserveRunActionPayload === true) {
|
|
307
320
|
// Count the real display record before allocating recovery space. Adding
|
|
308
321
|
// older checkpoints must not make an otherwise valid history unwritable.
|
|
@@ -436,6 +449,8 @@
|
|
|
436
449
|
turnId: typeof input.turnId === 'string' ? input.turnId : '',
|
|
437
450
|
createdAt: typeof input.createdAt === 'string' ? input.createdAt : now,
|
|
438
451
|
completedAt: typeof input.completedAt === 'string' ? input.completedAt : '',
|
|
452
|
+
requireReviewing: typeof input.requireReviewing === 'boolean' ? input.requireReviewing : undefined,
|
|
453
|
+
trackingDecision: ['continue', 'disable'].includes(input.trackingDecision) ? input.trackingDecision : '',
|
|
439
454
|
mode: typeof input.mode === 'string' ? input.mode : '', ...StorageRunActions.compactProviderSnapshot(input),
|
|
440
455
|
model: typeof input.model === 'string' ? input.model : '',
|
|
441
456
|
reasoningEffort: typeof input.reasoningEffort === 'string' ? input.reasoningEffort : '',
|
|
@@ -708,7 +723,9 @@
|
|
|
708
723
|
undoStatus: normalizeDisplayTextForStorage(run.undoStatus, SESSION_STORAGE_LIMITS.statusTextChars),
|
|
709
724
|
...(run.undoOwnerProtocol === 1 ? { undoOwnerProtocol: 1 } : {}),
|
|
710
725
|
nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
|
|
726
|
+
...(run.operation === 'compact' ? { operation: 'compact' } : {}),
|
|
711
727
|
codexThreadId: normalizeTextField(run.codexThreadId, 160), codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
728
|
+
contextUsage: run.forkSnapshot === true ? null : ContextUsage.normalize(run.contextUsage),
|
|
712
729
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
713
730
|
queueItemId: normalizeTextField(run.queueItemId, 160)
|
|
714
731
|
};
|
|
@@ -775,6 +792,19 @@
|
|
|
775
792
|
if (event.failure) {
|
|
776
793
|
compact.failure = compactStructuredEventValueForStorage(event.failure, 0);
|
|
777
794
|
}
|
|
795
|
+
if (Array.isArray(event.compileErrors) && event.compileErrors.length) {
|
|
796
|
+
compact.compileErrors = event.compileErrors.slice(0, 5)
|
|
797
|
+
.map(item => normalizeDisplayTextForStorage(String(item), SESSION_STORAGE_LIMITS.detailChars))
|
|
798
|
+
.filter(Boolean);
|
|
799
|
+
}
|
|
800
|
+
if (Array.isArray(event.rejectedHunks) && event.rejectedHunks.length) {
|
|
801
|
+
compact.rejectedHunks = event.rejectedHunks.slice(0, 10)
|
|
802
|
+
.map(item => ({
|
|
803
|
+
path: normalizeDisplayTextForStorage(String(item?.path || ''), SESSION_STORAGE_LIMITS.pathChars),
|
|
804
|
+
summary: normalizeDisplayTextForStorage(String(item?.summary || ''), SESSION_STORAGE_LIMITS.detailChars)
|
|
805
|
+
}))
|
|
806
|
+
.filter(item => item.path);
|
|
807
|
+
}
|
|
778
808
|
return compact;
|
|
779
809
|
});
|
|
780
810
|
}
|
|
@@ -1343,6 +1373,8 @@
|
|
|
1343
1373
|
projectRetentionWarnings: StorageRunActions.projectRetentionWarnings,
|
|
1344
1374
|
projectRetentionUpdates: StorageRunActions.projectRetentionUpdates,
|
|
1345
1375
|
mergeSessionReviewState: StorageRunActions.mergeSessionReviewState,
|
|
1376
|
+
copyRunReviewState: StorageRunActions.copyRunReviewState,
|
|
1377
|
+
reviewStateSnapshot: StorageRunActions.reviewStateSnapshot,
|
|
1346
1378
|
buildTurnRecord: buildTurnRecord,
|
|
1347
1379
|
buildEventRecord: buildEventRecord,
|
|
1348
1380
|
buildArtifactRecord: buildArtifactRecord,
|