codex-overleaf-link 2.5.0 → 2.5.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 +38 -26
- package/README.zh-CN.md +39 -27
- package/extension/bootstrap/background.js +52 -9
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/bootstrap/popup.html +1 -0
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/src/background.js +206 -53
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +74 -14
- package/extension/src/backgroundUpdateCoordinator.js +118 -21
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +8 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +72 -14
- package/extension/src/content/composerAttachments.js +93 -45
- package/extension/src/content/composerPanel.js +18 -0
- package/extension/src/content/contentRuntime.js +661 -676
- package/extension/src/content/diagnosticsController.js +122 -96
- package/extension/src/content/diagnosticsExport.js +19 -5
- package/extension/src/content/diagnosticsPanel.js +30 -0
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +6879 -2786
- package/extension/src/content/generated/content.bundle.meta.json +11 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +6 -0
- package/extension/src/content/localSkillsPanel.js +102 -12
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +7 -4
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +15 -2
- package/extension/src/content/panelMaintenance.js +40 -12
- package/extension/src/content/panelRenderer.js +6 -0
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +46 -8
- package/extension/src/content/providerSettingsDialog.js +71 -13
- package/extension/src/content/recentProjects.js +11 -5
- package/extension/src/content/runActivitySummary.js +40 -6
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +37 -14
- package/extension/src/content/runFailureNotice.js +21 -12
- package/extension/src/content/runGuidanceController.js +72 -27
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runQueueScheduler.js +148 -42
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +8 -125
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +146 -153
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +1 -1
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/settingsPanel.js +7 -3
- package/extension/src/content/settingsWorkbench.js +5 -0
- package/extension/src/content/sharedSessionViewSync.js +45 -8
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +60 -8
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +53 -4
- package/extension/src/content/writebackOrchestrator.js +82 -73
- package/extension/src/content/writingStyleSettings.js +35 -11
- package/extension/src/page/binaryAssetUploader.js +136 -14
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +1 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +6 -4
- package/extension/src/page/snapshotRouter.js +84 -39
- package/extension/src/page/textFileCreator.js +105 -25
- package/extension/src/page/trackedChangeCapture.js +12 -2
- package/extension/src/page/trackedChangeOwnership.js +19 -1
- package/extension/src/page/trackedChangeReplay.js +41 -21
- package/extension/src/page/trackedChangesLifecycle.js +217 -199
- package/extension/src/page/treeOperations.js +7 -10
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +2 -0
- package/extension/src/page/writebackRouter.js +80 -79
- package/extension/src/pageBridge.js +19 -3
- package/extension/src/popup.js +62 -10
- package/extension/src/shared/agentTranscript.js +23 -0
- package/extension/src/shared/auditRecords.js +2 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +79 -57
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +309 -3
- package/extension/src/shared/globalPreferences.js +30 -13
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +298 -2
- package/extension/src/shared/managedUpdateProjection.js +2 -2
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +5 -4
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +5 -3
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/selectionContext.js +12 -3
- package/extension/src/shared/sessionState.js +72 -27
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +37 -14
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +58 -26
- package/extension/src/shared/storageMigration.js +71 -111
- package/extension/src/shared/storageRunActions.js +209 -26
- package/extension/src/shared/subagentActivityModel.js +1 -1
- package/extension/src/shared/undoOperations.js +136 -13
- package/extension/src/shared/updateConsent.js +6 -4
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +16 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +38 -38
- package/extension/styles/panel.css +19 -5
- package/native-host/src/anthropicBridgeRequest.js +4 -2
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexPromptAssembly.js +31 -12
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexSessionRunner.js +110 -183
- package/native-host/src/codexSessionTiming.js +18 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +110 -35
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/processStopBarrier.js +42 -7
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerProfile.js +4 -2
- package/native-host/src/providerReasoning.js +2 -7
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +72 -14
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +36 -8
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +79 -66
- package/native-host/src/subagentTelemetry.js +19 -10
- package/native-host/src/subagentWorkspacePath.js +5 -2
- package/native-host/src/taskRunnerRuntime.js +77 -54
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/updateManager.js +297 -39
- package/native-host/src/writingStyleRuntime.js +57 -29
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
(function
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) module.exports = factory(require('./runRecoveryActions'));
|
|
3
|
+
else root.CodexOverleafModuleRegistry.define('RunTimelineView', ['RunRecoveryActions'], factory);
|
|
4
|
+
})(typeof window !== 'undefined' ? window : globalThis, function (RunRecoveryActions) {
|
|
2
5
|
'use strict';
|
|
3
6
|
|
|
7
|
+
|
|
4
8
|
// Owns scroll, cards, reports and mature run actions. The optional activity
|
|
5
9
|
// presenter projects stored events without taking ownership of settlement.
|
|
6
10
|
function create(deps = {}) {
|
|
@@ -10,9 +14,14 @@
|
|
|
10
14
|
RunActivityModel,
|
|
11
15
|
SubagentActivityView,
|
|
12
16
|
RunGuidanceView,
|
|
17
|
+
getPendingGuidance = () => [],
|
|
13
18
|
RunResultActions,
|
|
14
19
|
RunScrollLayout,
|
|
15
20
|
RunFailureNotice,
|
|
21
|
+
I18n,
|
|
22
|
+
FailureReasons,
|
|
23
|
+
localizeVisibleReason = value => value,
|
|
24
|
+
localizeCompileReason = value => value,
|
|
16
25
|
tr,
|
|
17
26
|
tx,
|
|
18
27
|
getLocale,
|
|
@@ -47,6 +56,7 @@
|
|
|
47
56
|
trackedChangeInFlight,
|
|
48
57
|
projectRunSettlement,
|
|
49
58
|
getCurrentProjectId,
|
|
59
|
+
getAccountScopeId,
|
|
50
60
|
getProjectName
|
|
51
61
|
} = deps;
|
|
52
62
|
|
|
@@ -59,6 +69,7 @@
|
|
|
59
69
|
// drives the floating "jump to latest" button's counter.
|
|
60
70
|
let scrollLogRafId = 0;
|
|
61
71
|
let scrollLogPendingForce = false;
|
|
72
|
+
let pendingGuidanceReveal = null;
|
|
62
73
|
let jumpToLatestButton = null;
|
|
63
74
|
let unreadSinceDetach = 0;
|
|
64
75
|
// Elapsed time belongs to the run; tool rows retain their own lifecycle.
|
|
@@ -69,9 +80,21 @@
|
|
|
69
80
|
onLayoutChange: scroller => logAutoFollow ? scrollLogToBottom() : updateJumpToLatestButton(scroller)
|
|
70
81
|
});
|
|
71
82
|
const failureNotice = RunFailureNotice?.create({ tr, projectRunSettlement, sanitizeText: sanitizeAssistantVisibleText });
|
|
83
|
+
const { buildRecoveryButton, appendRecoveryActionForFailure } = RunRecoveryActions.create({
|
|
84
|
+
document: typeof document === 'undefined' ? undefined : document,
|
|
85
|
+
window: typeof window === 'undefined' ? undefined : window,
|
|
86
|
+
tx, failureNotice, refillComposerForRetry, openProjectFileForFailure, showNativeSetupGuidance,
|
|
87
|
+
openStorageSettings, forceCancelStuckTaskForCurrentProject
|
|
88
|
+
});
|
|
72
89
|
const presence = RunPresence?.create({ tx, getLocale, formatElapsed });
|
|
73
90
|
const displayRun = run => RunFailureNotice?.projectResolvedUndo?.(run) || run;
|
|
74
|
-
const
|
|
91
|
+
const displayLocalizedRun = run => {
|
|
92
|
+
const displayed = displayRun(run);
|
|
93
|
+
return displayed ? { ...displayed, events: (displayed.events || []).map(sanitizeRunEventForRender) } : displayed;
|
|
94
|
+
};
|
|
95
|
+
const activitySummary = RunActivitySummary?.create({ RunActivityModel, RunGuidanceView, getPendingGuidance, SubagentActivityView, tx, findRunRecord: (...args) => displayLocalizedRun(findRunRecord(...args)), getPanel, cssEscape,
|
|
96
|
+
projectReviewResultEvent: sanitizeRunEventForRender,
|
|
97
|
+
formatReviewResultFailure,
|
|
75
98
|
formatEventTime, formatElapsed, renderMarkdownBlockText, renderRunEvent, formatEventDetail,
|
|
76
99
|
presence, classifyStatus: classifyRunPresence, getProjectId: () => getCurrentProjectId?.() || '',
|
|
77
100
|
summarizeCommand: RunPresence?.commandSummary,
|
|
@@ -82,6 +105,7 @@
|
|
|
82
105
|
function resetAutoFollow() {
|
|
83
106
|
logAutoFollow = true;
|
|
84
107
|
userScrollIntentUntil = 0;
|
|
108
|
+
pendingGuidanceReveal = null;
|
|
85
109
|
}
|
|
86
110
|
|
|
87
111
|
function bindLogAutoFollow() {
|
|
@@ -183,6 +207,7 @@
|
|
|
183
207
|
if ((event?.type === 'wheel' && event.deltaY < 0) || ['ArrowUp', 'PageUp', 'Home'].includes(event?.key)) {
|
|
184
208
|
logAutoFollow = false;
|
|
185
209
|
scrollLogPendingForce = false;
|
|
210
|
+
pendingGuidanceReveal = null;
|
|
186
211
|
}
|
|
187
212
|
}
|
|
188
213
|
|
|
@@ -207,6 +232,7 @@
|
|
|
207
232
|
userScrollIntentUntil = 0;
|
|
208
233
|
unreadSinceDetach = 0;
|
|
209
234
|
scrollLogPendingForce = true;
|
|
235
|
+
pendingGuidanceReveal = null;
|
|
210
236
|
} else if (!logAutoFollow) {
|
|
211
237
|
// The user has scrolled up: do not yank them. Just keep the jump button
|
|
212
238
|
// state current.
|
|
@@ -241,7 +267,21 @@
|
|
|
241
267
|
writeNow();
|
|
242
268
|
});
|
|
243
269
|
}
|
|
270
|
+
function revealGuidance(row) {
|
|
271
|
+
if (!logAutoFollow || activitySummary?.isSubagentViewOpen?.() || !getLogScrollContainer()?.contains?.(row)) return false;
|
|
272
|
+
pendingGuidanceReveal = row;
|
|
273
|
+
scrollLogToBottom();
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
244
276
|
function setLogScrollPosition(scroller) {
|
|
277
|
+
const reveal = pendingGuidanceReveal;
|
|
278
|
+
pendingGuidanceReveal = null;
|
|
279
|
+
const result = reveal && scrollLayout?.revealGuidance(reveal);
|
|
280
|
+
if (result) {
|
|
281
|
+
logAutoFollow = result.following;
|
|
282
|
+
if (!logAutoFollow) scrollLayout.captureAnchor(true);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
245
285
|
scroller.scrollTop = scroller.scrollHeight;
|
|
246
286
|
}
|
|
247
287
|
function collapseRunProcess(view, statusText) {
|
|
@@ -357,7 +397,7 @@
|
|
|
357
397
|
}
|
|
358
398
|
|
|
359
399
|
function renderRunCard(run) {
|
|
360
|
-
run =
|
|
400
|
+
run = displayLocalizedRun(run);
|
|
361
401
|
const root = document.createElement('section');
|
|
362
402
|
root.className = 'transcript-turn run-card';
|
|
363
403
|
root.dataset.status = run.status || 'completed';
|
|
@@ -418,11 +458,7 @@
|
|
|
418
458
|
truncated.textContent = tr('eventsTruncatedNote');
|
|
419
459
|
events.append(truncated);
|
|
420
460
|
}
|
|
421
|
-
|
|
422
|
-
const renderedGuidanceIds = new Set();
|
|
423
|
-
const completedLegacyGuidance = new Set((run.events || [])
|
|
424
|
-
.filter(event => event.kind === 'guidance' && !event.guidanceId && event.status === 'completed')
|
|
425
|
-
.map(event => event.title));
|
|
461
|
+
if (!activitySummary) RunGuidanceView.reconcileGuidance?.(guidance, (run.events || []).concat(getPendingGuidance(run)), tx, { active: run.status === 'running' });
|
|
426
462
|
for (const event of run.events || []) {
|
|
427
463
|
if (event.kind === 'report') {
|
|
428
464
|
report.hidden = false;
|
|
@@ -430,16 +466,7 @@
|
|
|
430
466
|
} else if (activitySummary || event.kind === 'technical') {
|
|
431
467
|
continue;
|
|
432
468
|
} else if (RunGuidanceView.getGuidanceText(event)) {
|
|
433
|
-
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
if (!event.guidanceId && event.status === 'queued' && completedLegacyGuidance.has(event.title)) {
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
if (event.guidanceId) {
|
|
440
|
-
renderedGuidanceIds.add(event.guidanceId);
|
|
441
|
-
}
|
|
442
|
-
guidanceTarget.append(renderRunEvent(event));
|
|
469
|
+
continue;
|
|
443
470
|
} else if (event.kind === 'stream') {
|
|
444
471
|
upsertStreamEvent({ events }, event);
|
|
445
472
|
} else {
|
|
@@ -458,6 +485,14 @@
|
|
|
458
485
|
|
|
459
486
|
function getRunStatusText(run = {}) {
|
|
460
487
|
if (run.retryingWriteback) return tx('Checking file sync', '正在检查文件同步');
|
|
488
|
+
if (['Stopped tracking after a page refresh', '页面刷新后已停止跟踪'].includes(run.statusText)) return tr('restoredRunStoppedStatus');
|
|
489
|
+
const knownStatus = /^(?:Done|Failed|Cancelled|Running|已处理|处理失败|已取消|处理中)(?:\s+[\d.]+(?:ms|s|m|h|秒|分钟)(?:\s+[\d.]+(?:ms|s|m|h|秒|分钟))*)?$/.test(run.statusText || '');
|
|
490
|
+
if (knownStatus) {
|
|
491
|
+
const elapsed = String(run.statusText).replace(/^(?:Done|Failed|Cancelled|Running|已处理|处理失败|已取消|处理中)\s*/, '');
|
|
492
|
+
const key = run.status === 'running' ? 'processing' : run.status === 'failed' ? 'processedFailed'
|
|
493
|
+
: ['rejected', 'cancelled'].includes(run.status) ? 'processedCancelled' : 'processed';
|
|
494
|
+
return tr(key, { elapsed }).trim();
|
|
495
|
+
}
|
|
461
496
|
if (run.statusText) {
|
|
462
497
|
return run.statusText;
|
|
463
498
|
}
|
|
@@ -534,18 +569,61 @@
|
|
|
534
569
|
function sanitizeRunEventForRender(event = {}) {
|
|
535
570
|
const streamRole = sanitizeAssistantVisibleText(event.streamRole);
|
|
536
571
|
const title = sanitizeAssistantVisibleText(event.title);
|
|
572
|
+
const activity = !streamRole && (!event.kind || event.kind === 'activity') && !event.guidanceId;
|
|
573
|
+
const localized = activity ? localizeProductActivity(title, sanitizeAssistantVisibleValue(event.detail)) : null;
|
|
537
574
|
return {
|
|
538
575
|
...event,
|
|
576
|
+
...(activity && event.activity?.kind === 'compile' ? { activity: { ...event.activity, output: localizeCompileReason(event.activity.output) } } : {}),
|
|
539
577
|
title: streamRole === 'reasoning'
|
|
540
578
|
? stripEmptyHtmlCommentPlaceholders(title)
|
|
541
|
-
: title,
|
|
579
|
+
: localized?.title || title,
|
|
542
580
|
status: sanitizeAssistantVisibleText(event.status),
|
|
543
|
-
detail: sanitizeAssistantVisibleValue(event.detail),
|
|
581
|
+
detail: localized?.detail ?? sanitizeAssistantVisibleValue(event.detail),
|
|
544
582
|
technicalDetail: sanitizeAssistantVisibleValue(event.technicalDetail),
|
|
545
583
|
streamKey: sanitizeAssistantVisibleText(event.streamKey),
|
|
546
584
|
streamRole
|
|
547
585
|
};
|
|
548
586
|
}
|
|
587
|
+
|
|
588
|
+
function localizeProductActivity(title, detail) {
|
|
589
|
+
let known = false, nextTitle = title;
|
|
590
|
+
const keys = ['undoUnsafeFullFileTitle', 'undoNoTraceTitle', 'undoFileSelectTitle', 'undoNoTraceStarted', 'undoTrackedTitle', 'undoNativeTitle', 'undoTrackedStarted', 'undoNativeStarted', 'undoSwitchedEditing', 'undoReviewingRestoreUnverified', 'runAcceptTrackedStarted', 'runAcceptTrackedFailed', 'undoResult', 'undoTrackedResult', 'undoNativeResult', 'runAcceptTrackedResult'];
|
|
591
|
+
if (I18n?.t) outer: for (const key of keys) for (const locale of ['en', 'zh']) {
|
|
592
|
+
const template = I18n.t(locale, key, { applied: '__APPLIED__', skipped: '__SKIPPED__' });
|
|
593
|
+
const pattern = template.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace('__APPLIED__', '(\\d+)').replace('__SKIPPED__', '(\\d+)');
|
|
594
|
+
const match = new RegExp('^' + pattern + '$').exec(title);
|
|
595
|
+
if (match) { known = true; nextTitle = tr(key, { applied: match[1], skipped: match[2] }); break outer; }
|
|
596
|
+
}
|
|
597
|
+
let countedTitle;
|
|
598
|
+
const writeResult = /^(?:Write result: wrote (\d+) item\(s\), skipped (\d+)|写入结果:已写入 (\d+) 项,跳过 (\d+) 项)$/.exec(title);
|
|
599
|
+
const governance = /^(?:Project governance blocked (\d+) write\(s\) before review\.|项目治理规则在审核前阻止了 (\d+) 项写入。)$/.exec(title);
|
|
600
|
+
if (writeResult) {
|
|
601
|
+
const applied = writeResult[1] ?? writeResult[3], skipped = writeResult[2] ?? writeResult[4];
|
|
602
|
+
countedTitle = tx(`Write result: wrote ${applied} item(s), skipped ${skipped}`, `写入结果:已写入 ${applied} 项,跳过 ${skipped} 项`);
|
|
603
|
+
} else if (governance) {
|
|
604
|
+
const count = governance[1] ?? governance[2];
|
|
605
|
+
countedTitle = tx(`Project governance blocked ${count} write(s) before review.`, `项目治理规则在审核前阻止了 ${count} 项写入。`);
|
|
606
|
+
}
|
|
607
|
+
const labels = ['detailAccepted', 'detailRejected', 'detailUndone', 'detailSkipped', 'detailFile', 'detailRecord', 'detailReason'];
|
|
608
|
+
const project = (value, reason = false) => {
|
|
609
|
+
if (typeof value === 'string') return reason ? localizeVisibleReason(value) : value;
|
|
610
|
+
if (Array.isArray(value)) return value.map(item => project(item, reason));
|
|
611
|
+
if (!value || typeof value !== 'object') return value;
|
|
612
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
613
|
+
const label = labels.find(id => ['en', 'zh'].some(locale => I18n?.t?.(locale, id) === key));
|
|
614
|
+
return [label ? tr(label) : key, project(child, label === 'detailReason')];
|
|
615
|
+
}));
|
|
616
|
+
};
|
|
617
|
+
return { title: countedTitle || (known ? nextTitle : localizeCompileReason(localizeVisibleReason(title))),
|
|
618
|
+
detail: typeof detail === 'string' ? localizeVisibleReason(detail) : known ? project(detail) : detail };
|
|
619
|
+
}
|
|
620
|
+
function formatReviewResultFailure(failure) {
|
|
621
|
+
if (failure?.code === 'unknown_legacy_failure' && (!failure.evidence?.originalCode || failure.evidence.originalCode === 'unknown_legacy_failure')
|
|
622
|
+
&& ['Unknown reason', '未知原因'].includes(failure.userMessage)) {
|
|
623
|
+
failure = { ...failure, userMessage: tr('unknownReason') };
|
|
624
|
+
}
|
|
625
|
+
return FailureReasons?.formatFailureReasonForDisplay(failure, getLocale?.(), key => { const value = tr(key, failure?.file ? undefined : { file: tr('unknownFile'), activeFile: tr('unknownFile') }); return value && value !== key ? value : undefined; });
|
|
626
|
+
}
|
|
549
627
|
function renderActivityLine(input) {
|
|
550
628
|
const event = sanitizeRunEventForRender(input);
|
|
551
629
|
const row = document.createElement('div');
|
|
@@ -586,7 +664,8 @@
|
|
|
586
664
|
// The result presenter owns parsing and current lifecycle metadata.
|
|
587
665
|
const splitFlatCompletionReport = text => RunResultActions.splitFlatCompletionReport(text, tx);
|
|
588
666
|
const projectCompletionMeta = (meta, run) => RunResultActions.projectCompletionMeta(meta, run, {
|
|
589
|
-
tx, trackedChangeInFlight, isTrackedChangeLifecycleRun, projectRunSettlement
|
|
667
|
+
tx, getLocale, tr, trackedChangeInFlight, isTrackedChangeLifecycleRun, projectRunSettlement,
|
|
668
|
+
projectId: getCurrentProjectId?.(), accountScopeId: getAccountScopeId?.()
|
|
590
669
|
});
|
|
591
670
|
|
|
592
671
|
// The user's own open/closed choice per run survives card re-renders.
|
|
@@ -712,7 +791,7 @@
|
|
|
712
791
|
mainSections.push(String(structured.conclusion));
|
|
713
792
|
}
|
|
714
793
|
if (structured.body) {
|
|
715
|
-
mainSections.push(structured.body);
|
|
794
|
+
mainSections.push(RunResultActions.projectCompletionBody?.(structured.body, tx) ?? structured.body);
|
|
716
795
|
}
|
|
717
796
|
if (mainSections.length) {
|
|
718
797
|
renderMarkdownBlockText(main, mainSections.join('\n\n'));
|
|
@@ -755,132 +834,6 @@
|
|
|
755
834
|
// the normal cancel button is hidden because currentRunView is null).
|
|
756
835
|
// Recovery-action registry: failure codes whose "next step" is executable
|
|
757
836
|
// from the panel get a button, not prose. One primary action per failure.
|
|
758
|
-
const RETRYABLE_FAILURE_CODES = new Set([
|
|
759
|
-
'native_request_failed', 'codex_timeout', 'codex_no_usable_result',
|
|
760
|
-
'codex_output_limit', 'stale_source_changed', 'patch_anchor_not_found',
|
|
761
|
-
'target_editor_not_ready', 'write_timeout', 'partial_write_needs_review',
|
|
762
|
-
'write_operation_failed'
|
|
763
|
-
]);
|
|
764
|
-
const NATIVE_SETUP_FAILURE_CODES = new Set([
|
|
765
|
-
'native_bridge_unavailable', 'native_protocol_incompatible'
|
|
766
|
-
]);
|
|
767
|
-
const OPEN_FILE_FAILURE_CODES = new Set([
|
|
768
|
-
'target_file_not_active', 'target_file_open_failed', 'target_file_not_found'
|
|
769
|
-
]);
|
|
770
|
-
const STORAGE_FAILURE_CODES = new Set([
|
|
771
|
-
'storage_quota_exceeded', 'run_state_persist_failed'
|
|
772
|
-
]);
|
|
773
|
-
// A missing `codex` binary is fixed in the terminal, not the panel — the
|
|
774
|
-
// action opens the README troubleshooting section (PATH / login checks).
|
|
775
|
-
const CODEX_INSTALL_FAILURE_CODES = new Set(['codex_not_found']);
|
|
776
|
-
const CODEX_CLI_TROUBLESHOOTING_URL = 'https://github.com/Ghqqqq/codex-overleaf-link#faq-and-troubleshooting';
|
|
777
|
-
|
|
778
|
-
function buildRecoveryButton(failureCode, label, title) {
|
|
779
|
-
const button = document.createElement('button');
|
|
780
|
-
button.type = 'button';
|
|
781
|
-
button.className = 'run-final-answer__recovery-action';
|
|
782
|
-
button.dataset.recoveryFor = failureCode;
|
|
783
|
-
button.textContent = label;
|
|
784
|
-
if (title) {
|
|
785
|
-
button.title = title;
|
|
786
|
-
}
|
|
787
|
-
return button;
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
function appendEditAndResend(report, run, actionKey) {
|
|
791
|
-
const retry = buildRecoveryButton(actionKey,
|
|
792
|
-
tx('Edit & resend', '编辑后重发'),
|
|
793
|
-
tx('Put this run\u2019s task back into the composer so you can adjust and resend it.', '把本轮任务填回输入框,修改后可直接重发。'));
|
|
794
|
-
retry.addEventListener('click', clickEvent => {
|
|
795
|
-
clickEvent.stopPropagation();
|
|
796
|
-
refillComposerForRetry(run || null);
|
|
797
|
-
});
|
|
798
|
-
report.append(retry);
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
function appendRecoveryActionForFailure(report, event, run) {
|
|
802
|
-
const recovery = failureNotice?.prepareRecovery(report, { openFile: openProjectFileForFailure });
|
|
803
|
-
const failureCode = event?.failure?.code || '';
|
|
804
|
-
if (recovery?.handled) return;
|
|
805
|
-
report = recovery?.target || report;
|
|
806
|
-
if (RETRYABLE_FAILURE_CODES.has(failureCode) && typeof refillComposerForRetry === 'function') {
|
|
807
|
-
appendEditAndResend(report, run, failureCode);
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
if (NATIVE_SETUP_FAILURE_CODES.has(failureCode) && typeof showNativeSetupGuidance === 'function') {
|
|
811
|
-
const fix = buildRecoveryButton(failureCode,
|
|
812
|
-
tx('Fix the native host', '修复 native host'),
|
|
813
|
-
tx('Opens the install/update guidance with the copyable command.', '打开安装/更新指引(含可复制命令)。'));
|
|
814
|
-
fix.addEventListener('click', clickEvent => {
|
|
815
|
-
clickEvent.stopPropagation();
|
|
816
|
-
showNativeSetupGuidance();
|
|
817
|
-
});
|
|
818
|
-
report.append(fix);
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
if (OPEN_FILE_FAILURE_CODES.has(failureCode) && event?.failure?.file && typeof openProjectFileForFailure === 'function') {
|
|
822
|
-
const open = buildRecoveryButton(failureCode,
|
|
823
|
-
tx(`Open ${event.failure.file} in Overleaf`, `在 Overleaf 打开 ${event.failure.file}`), '');
|
|
824
|
-
open.addEventListener('click', clickEvent => {
|
|
825
|
-
clickEvent.stopPropagation();
|
|
826
|
-
openProjectFileForFailure(event.failure.file);
|
|
827
|
-
});
|
|
828
|
-
report.append(open);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
if (STORAGE_FAILURE_CODES.has(failureCode) && typeof openStorageSettings === 'function') {
|
|
832
|
-
const clean = buildRecoveryButton(failureCode,
|
|
833
|
-
tx('Open history & storage cleanup', '打开历史与存储清理'), '');
|
|
834
|
-
clean.addEventListener('click', clickEvent => {
|
|
835
|
-
clickEvent.stopPropagation();
|
|
836
|
-
openStorageSettings();
|
|
837
|
-
});
|
|
838
|
-
report.append(clean);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
if (CODEX_INSTALL_FAILURE_CODES.has(failureCode)) {
|
|
842
|
-
const guide = buildRecoveryButton(failureCode,
|
|
843
|
-
tx('Codex CLI troubleshooting guide', '查看 Codex CLI 排查指引'),
|
|
844
|
-
tx('Opens the README section on fixing a missing `codex` command (PATH / login).', '打开 README 中「找不到 codex 命令」的排查说明(PATH/登录)。'));
|
|
845
|
-
guide.addEventListener('click', clickEvent => {
|
|
846
|
-
clickEvent.stopPropagation();
|
|
847
|
-
window.open(CODEX_CLI_TROUBLESHOOTING_URL, '_blank', 'noopener');
|
|
848
|
-
});
|
|
849
|
-
report.append(guide);
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
if (failureCode !== 'codex_project_locked') return;
|
|
853
|
-
const button = document.createElement('button');
|
|
854
|
-
button.type = 'button';
|
|
855
|
-
button.className = 'run-final-answer__recovery-action';
|
|
856
|
-
button.dataset.recoveryFor = failureCode;
|
|
857
|
-
button.textContent = tx('Force-release the stuck task', '强制释放卡住的任务');
|
|
858
|
-
button.title = tx(
|
|
859
|
-
'Sends a force-cancel to the native host that drops the project lock so a new run can start. Use this when refreshing the tab did not free the project.',
|
|
860
|
-
'向 native host 发送强制取消请求,释放当前 Overleaf 项目的占用,让新的任务可以启动。刷新页面没用时使用。'
|
|
861
|
-
);
|
|
862
|
-
button.addEventListener('click', event => {
|
|
863
|
-
event.stopPropagation();
|
|
864
|
-
button.disabled = true;
|
|
865
|
-
button.textContent = tx('Releasing…', '正在释放…');
|
|
866
|
-
forceCancelStuckTaskForCurrentProject().then(result => {
|
|
867
|
-
if (result?.ok) {
|
|
868
|
-
button.textContent = tx('Force-released — you can retry the run.', '已释放,可以重试本轮任务。');
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
// The helper resolves (never throws) with a non-ok result when the
|
|
872
|
-
// request was not delivered. Report the real outcome instead of a
|
|
873
|
-
// blanket success, and re-enable the button so the user can retry.
|
|
874
|
-
button.disabled = false;
|
|
875
|
-
button.textContent = tx('Release failed — refresh the tab, then try again.', '释放失败,请刷新页面后重试。');
|
|
876
|
-
}).catch(() => {
|
|
877
|
-
button.disabled = false;
|
|
878
|
-
button.textContent = tx('Release failed — refresh the tab, then try again.', '释放失败,请刷新页面后重试。');
|
|
879
|
-
});
|
|
880
|
-
});
|
|
881
|
-
report.append(button);
|
|
882
|
-
}
|
|
883
|
-
|
|
884
837
|
// A run belongs to the tracked-change lifecycle iff trackedChangeStatus is set
|
|
885
838
|
// or it still has tracked-change refs. Every other run is a legacy-undo run.
|
|
886
839
|
function isTrackedChangeLifecycleRun(run) {
|
|
@@ -956,13 +909,16 @@
|
|
|
956
909
|
}
|
|
957
910
|
|
|
958
911
|
button.hidden = false;
|
|
959
|
-
|
|
960
|
-
|
|
912
|
+
const inFlight = trackedChangeInFlight?.get(run.id) === 'reject';
|
|
913
|
+
const applied = run.undoStatus === 'applied' && !inFlight;
|
|
914
|
+
button.disabled = inFlight || (run.undoStatus === 'running' && !run.undoRecoveryCheckRequired) || run.undoStatus === 'applied';
|
|
915
|
+
button.textContent = applied
|
|
961
916
|
? tr('undoApplied')
|
|
962
917
|
: (run.partialWriteback ? tr('undoPartialRun') : tr('undoRun'));
|
|
963
|
-
button.title =
|
|
918
|
+
button.title = applied
|
|
964
919
|
? tr('undoAppliedTitle')
|
|
965
920
|
: (run.partialWriteback ? tr('undoPartialRunTitle') : tr('undoRunTitle'));
|
|
921
|
+
if (run.undoRecoveryUnavailable) button.title = tx('Undo recovery checks are unavailable. Reload this page after the connection recovers.', '撤销恢复检查暂不可用,请在连接恢复后重新加载此页面。');
|
|
966
922
|
button.addEventListener('click', event => {
|
|
967
923
|
event.stopPropagation();
|
|
968
924
|
undoRun(run.id);
|
|
@@ -1158,7 +1114,43 @@
|
|
|
1158
1114
|
return true;
|
|
1159
1115
|
}
|
|
1160
1116
|
|
|
1117
|
+
function refreshLocale() {
|
|
1118
|
+
const log = getPanel()?.querySelector('[data-log]');
|
|
1119
|
+
if (!log) return;
|
|
1120
|
+
if (!getState().runs?.length) { renderRunHistory({ preserveScroll: true }); return; }
|
|
1121
|
+
const reading = scrollLayout?.snapshot() || { scrollTop: log.scrollTop };
|
|
1122
|
+
for (const run of getState().runs) {
|
|
1123
|
+
const existing = log.querySelector(`[data-run-id="${cssEscape(run.id)}"]`);
|
|
1124
|
+
if (!existing) continue;
|
|
1125
|
+
const current = getCurrentRunView();
|
|
1126
|
+
if (current?.root === existing) {
|
|
1127
|
+
// Streaming owners retain their DOM references and buffered model text.
|
|
1128
|
+
const status = existing.querySelector('[data-run-status]');
|
|
1129
|
+
if (status) status.textContent = getRunStatusText(run);
|
|
1130
|
+
configureAcceptButton(existing, run); configureUndoButton(existing, run);
|
|
1131
|
+
configureEditButton(existing, run); configureWritebackButton(existing, run);
|
|
1132
|
+
RunResultActions.configureResultActions(existing, run);
|
|
1133
|
+
const reportEvent = (run.events || []).filter(event => event.kind === 'report').at(-1);
|
|
1134
|
+
const report = current.report || existing.querySelector('[data-run-report]');
|
|
1135
|
+
if (reportEvent && report) report.replaceChildren(renderCompletionReport(reportEvent, displayLocalizedRun(run)));
|
|
1136
|
+
activitySummary?.update(current);
|
|
1137
|
+
if (!activitySummary) RunGuidanceView.reconcileGuidance?.(existing.querySelector('[data-run-guidance]'),
|
|
1138
|
+
(run.events || []).concat(getPendingGuidance(run)), tx, { active: run.status === 'running' });
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
const open = existing.querySelector('[data-run-process]')?.open;
|
|
1142
|
+
const next = renderRunCard(run);
|
|
1143
|
+
const process = next.querySelector('[data-run-process]');
|
|
1144
|
+
if (process && typeof open === 'boolean') process.open = open;
|
|
1145
|
+
existing.replaceWith(next);
|
|
1146
|
+
}
|
|
1147
|
+
log.scrollTop = reading.scrollTop;
|
|
1148
|
+
scrollLayout?.restore(reading);
|
|
1149
|
+
updateJumpToLatestButton(log);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1161
1152
|
return {
|
|
1153
|
+
refreshLocale,
|
|
1162
1154
|
refreshResolvedUndo: refreshRunCard,
|
|
1163
1155
|
refreshActivitySummary: activitySummary?.update,
|
|
1164
1156
|
appendActivityDetail,
|
|
@@ -1166,6 +1158,7 @@
|
|
|
1166
1158
|
bindLogAutoFollow,
|
|
1167
1159
|
bumpUnreadIfDetached,
|
|
1168
1160
|
scrollLogToBottom,
|
|
1161
|
+
revealGuidance,
|
|
1169
1162
|
collapseRunProcess,
|
|
1170
1163
|
startRunElapsedTick,
|
|
1171
1164
|
stopRunElapsedTick,
|
|
@@ -1184,5 +1177,5 @@
|
|
|
1184
1177
|
};
|
|
1185
1178
|
}
|
|
1186
1179
|
|
|
1187
|
-
|
|
1188
|
-
})
|
|
1180
|
+
return { create };
|
|
1181
|
+
});
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
return JSON.parse(JSON.stringify(value));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
async function applyReviewTransition({ getState, runId, transition, persist }) {
|
|
18
|
+
async function applyReviewTransition({ getState, runId, transition, persist, legacyUndo = false, assertCurrent }) {
|
|
19
19
|
const targets = () => Array.from(new Set([
|
|
20
20
|
...(getState().runs || []),
|
|
21
21
|
...(getState().sessions || []).flatMap(session => session.runs || [])
|
|
22
22
|
])).filter(run => run.id === runId);
|
|
23
|
+
assertCurrent?.();
|
|
24
|
+
const capturedState = getState(), capturedSessionId = capturedState.activeSessionId;
|
|
23
25
|
const records = targets();
|
|
24
26
|
const sessions = (getState().sessions || []).filter(session =>
|
|
25
27
|
(session.runs || []).some(run => run.id === runId));
|
|
@@ -31,12 +33,19 @@
|
|
|
31
33
|
(Date.parse(session.lastActivityAt) || 0) + 1)).toISOString();
|
|
32
34
|
session.lastActivityAt = session.updatedAt;
|
|
33
35
|
}
|
|
36
|
+
const staged = records.map(record => JSON.stringify(record));
|
|
34
37
|
try {
|
|
35
|
-
await persist({ preserveRunActionPayload: true, reviewRunIds: [runId] });
|
|
38
|
+
await persist({ preserveRunActionPayload: true, reviewRunIds: [runId], requireDurable: true });
|
|
39
|
+
assertCurrent?.();
|
|
40
|
+
if (getState() !== capturedState || getState().activeSessionId !== capturedSessionId
|
|
41
|
+
|| records.some(record => !targets().includes(record))) throw reviewPersistenceError();
|
|
36
42
|
} catch (error) {
|
|
43
|
+
try { assertCurrent?.(); } catch (_stale) { throw error; }
|
|
37
44
|
// The remote action may have succeeded. Keep its recovery data and expose uncertainty.
|
|
38
|
-
for (const record of
|
|
39
|
-
|
|
45
|
+
for (const record of records) {
|
|
46
|
+
if (getState() !== capturedState || !targets().includes(record)
|
|
47
|
+
|| JSON.stringify(record) !== staged[records.indexOf(record)]) continue;
|
|
48
|
+
Object.assign(record, cloneSnapshot(previous), legacyUndo ? { undoStatus: 'partial' } : { trackedChangeStatus: 'needs_review' });
|
|
40
49
|
for (const field of ['settlement', 'settlementFacts']) {
|
|
41
50
|
if (!Object.prototype.hasOwnProperty.call(previous, field)) delete record[field];
|
|
42
51
|
}
|
|
@@ -51,6 +60,28 @@
|
|
|
51
60
|
});
|
|
52
61
|
}
|
|
53
62
|
|
|
63
|
+
function verifyRecoveredRuns(recoveredRuns, proposed, written) {
|
|
64
|
+
for (var target of recoveredRuns || []) {
|
|
65
|
+
var expectedSession = proposed.find(session => session.id === target.sessionId);
|
|
66
|
+
var storedSession = (written || []).find(session => session.id === target.sessionId
|
|
67
|
+
&& session.projectId === expectedSession?.projectId
|
|
68
|
+
&& session.accountScopeId === expectedSession?.accountScopeId);
|
|
69
|
+
var expected = expectedSession?.runs?.find(run => run.id === target.runId);
|
|
70
|
+
var stored = storedSession?.runs?.find(run => run.id === target.runId);
|
|
71
|
+
var retained = expected && stored
|
|
72
|
+
&& expected.runProjectId === target.projectId && stored.runProjectId === target.projectId
|
|
73
|
+
&& Number(stored.nativeEventSeq || 0) >= Number(expected.nativeEventSeq || 0)
|
|
74
|
+
&& stored.codexThreadId === expected.codexThreadId
|
|
75
|
+
&& stored.codexTurnId === expected.codexTurnId
|
|
76
|
+
&& JSON.stringify(stored.interruptedDraft) === JSON.stringify(expected.interruptedDraft)
|
|
77
|
+
&& (expected.events || []).every(event => (stored.events || [])
|
|
78
|
+
.some(saved => JSON.stringify(saved) === JSON.stringify(event)));
|
|
79
|
+
if (!retained) throw Object.assign(new Error('Recovered run was not durably saved; its replay journal is retained.'), {
|
|
80
|
+
code: 'recovery_state_not_persisted'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
54
85
|
async function persistPanelState(input) {
|
|
55
86
|
var state = cloneSnapshot(input.state || {});
|
|
56
87
|
var compactState = cloneSnapshot(input.compactState || {});
|
|
@@ -71,6 +102,12 @@
|
|
|
71
102
|
code: 'stale_view'
|
|
72
103
|
});
|
|
73
104
|
}
|
|
105
|
+
if ((input.recoveryRuns || []).some(target => target.projectId !== projectId
|
|
106
|
+
|| target.accountScopeId !== accountScopeId)) {
|
|
107
|
+
throw Object.assign(new Error('Journal recovery scope changed before persistence.'), {
|
|
108
|
+
code: 'recovery_scope_changed'
|
|
109
|
+
});
|
|
110
|
+
}
|
|
74
111
|
var reviewRuns = (input.reviewRunIds || []).map(id =>
|
|
75
112
|
(state.sessions || []).flatMap(session => session.runs || []).find(run => run.id === id));
|
|
76
113
|
if (reviewRuns.some(run => !run)) throw reviewPersistenceError();
|
|
@@ -129,10 +166,11 @@
|
|
|
129
166
|
focusFiles: Array.isArray(session.focusFiles) ? session.focusFiles : [],
|
|
130
167
|
createdAt: session.createdAt,
|
|
131
168
|
updatedAt: session.updatedAt,
|
|
132
|
-
|
|
169
|
+
pendingInputs: (Array.isArray(session.pendingInputs) ? session.pendingInputs : [])
|
|
133
170
|
.filter(function (item) { return !queueTombstones[item?.id]; })
|
|
134
171
|
}), { preserveRunActionPayload: true });
|
|
135
172
|
});
|
|
173
|
+
sessionRecords = StorageDb.compactSessionBatchActions?.(sessionRecords, { activeSessionId: state.activeSessionId }) || sessionRecords;
|
|
136
174
|
var written = await input.SessionPersistence.writeSessions({
|
|
137
175
|
Migration: Migration,
|
|
138
176
|
StorageDb: StorageDb,
|
|
@@ -146,8 +184,15 @@
|
|
|
146
184
|
writerId: input.persistenceContext?.writerId || ''
|
|
147
185
|
});
|
|
148
186
|
if (!reviewRuns.every(reviewRun => (written || []).some(session => (session.runs || []).some(run =>
|
|
149
|
-
run.id === reviewRun.id
|
|
187
|
+
run.id === reviewRun.id
|
|
188
|
+
&& (!reviewRun.trackedChangeStatus && ['applied', 'partial'].includes(reviewRun.undoStatus)
|
|
189
|
+
? run.undoStatus === reviewRun.undoStatus && (reviewRun.undoStatus === 'applied'
|
|
190
|
+
|| ['undoOperations', 'undoBaseFiles', 'undoExpectedFiles', 'undoTrackedChanges']
|
|
191
|
+
.every(field => JSON.stringify(run[field] || []) === JSON.stringify(reviewRun[field] || [])))
|
|
192
|
+
: run.trackedChangeStatus === reviewRun.trackedChangeStatus)
|
|
150
193
|
)))) throw reviewPersistenceError();
|
|
194
|
+
verifyRecoveredRuns(input.recoveryRuns, sessionRecords, written);
|
|
195
|
+
return written;
|
|
151
196
|
}
|
|
152
197
|
|
|
153
198
|
return Object.freeze({ persistPanelState: persistPanelState, applyReviewTransition: applyReviewTransition });
|