codex-overleaf-link 2.5.1 → 2.6.5
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 +148 -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 +46 -10
- package/extension/src/content/composerPanel.js +92 -12
- package/extension/src/content/contentRuntime.js +1114 -321
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsController.js +77 -34
- package/extension/src/content/diagnosticsExport.js +1 -1
- package/extension/src/content/diagnosticsPanel.js +147 -42
- package/extension/src/content/generated/content.bundle.js +5761 -1237
- 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 +6 -4
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +53 -14
- package/extension/src/content/modelPicker.js +29 -19
- package/extension/src/content/nativeCompatibilityController.js +52 -19
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/pageBridgeClient.js +7 -7
- package/extension/src/content/panelMaintenance.js +10 -2
- package/extension/src/content/panelRenderer.js +55 -28
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectSettingsCoordinator.js +30 -6
- package/extension/src/content/providerSettingsDialog.js +28 -19
- package/extension/src/content/recentProjects.js +30 -19
- 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 +27 -13
- package/extension/src/content/runRecoveryActions.js +2 -1
- package/extension/src/content/runResultActions.js +73 -24
- package/extension/src/content/runReviewController.js +102 -37
- package/extension/src/content/runTimelineView.js +224 -25
- package/extension/src/content/scopedPersistencePanelState.js +124 -46
- package/extension/src/content/selectionContextView.js +30 -3
- 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 +13 -4
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateNotice.js +60 -6
- package/extension/src/content/writebackOrchestrator.js +301 -72
- package/extension/src/content/writingStyleSettings.js +70 -17
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/overleafCapabilities.js +4 -1
- package/extension/src/page/overleafEditor.js +12 -3
- package/extension/src/page/overleafProjectSnapshot.js +56 -3
- package/extension/src/page/snapshotRouter.js +75 -66
- package/extension/src/page/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +9 -1
- package/extension/src/page/treeOperations.js +200 -19
- package/extension/src/page/writebackRouter.js +172 -113
- package/extension/src/pageBridge.js +124 -73
- package/extension/src/popup.js +1 -1
- package/extension/src/shared/agentTranscript.js +372 -18
- package/extension/src/shared/auditRecords.js +3 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +58 -17
- package/extension/src/shared/composerAttachmentDraft.js +27 -7
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/failureReasons.js +57 -3
- package/extension/src/shared/i18n.js +315 -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/projectFiles.js +2 -1
- 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/scopedPersistenceBrowserAdapter.js +34 -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 +130 -63
- package/extension/src/shared/storageDb.js +35 -3
- package/extension/src/shared/storageMigration.js +111 -2
- 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 +1293 -549
- 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 +127 -17
- package/native-host/src/debugLog.js +27 -3
- package/native-host/src/index.js +42 -11
- package/native-host/src/localSkills.js +1 -1
- package/native-host/src/mirrorWorkspace.js +47 -3
- package/native-host/src/nativeEnvironment.js +27 -5
- package/native-host/src/providerContextError.js +20 -0
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/skills/reviewer-response/SKILL.md +65 -0
- package/native-host/src/taskRunnerRuntime.js +28 -8
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/updateManager.js +144 -21
- package/native-host/src/writingStyleRuntime.js +1 -1
- package/package.json +5 -8
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const {
|
|
12
12
|
RunActivitySummary,
|
|
13
13
|
RunPresence,
|
|
14
|
+
PanelIcons,
|
|
14
15
|
RunActivityModel,
|
|
15
16
|
SubagentActivityView,
|
|
16
17
|
RunGuidanceView,
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
RunFailureNotice,
|
|
21
22
|
I18n,
|
|
22
23
|
FailureReasons,
|
|
24
|
+
projectContextLimitEvent = value => value,
|
|
23
25
|
localizeVisibleReason = value => value,
|
|
24
26
|
localizeCompileReason = value => value,
|
|
25
27
|
tr,
|
|
@@ -87,16 +89,18 @@
|
|
|
87
89
|
openStorageSettings, forceCancelStuckTaskForCurrentProject
|
|
88
90
|
});
|
|
89
91
|
const presence = RunPresence?.create({ tx, getLocale, formatElapsed });
|
|
92
|
+
const getNotStartedRun = run => RunResultActions.projectNotStartedRun?.(run, getLocale());
|
|
93
|
+
const getFinishedRun = run => RunResultActions.projectFinishedRun?.(run, getLocale(), { projectRunSettlement });
|
|
90
94
|
const displayRun = run => RunFailureNotice?.projectResolvedUndo?.(run) || run;
|
|
91
95
|
const displayLocalizedRun = run => {
|
|
92
96
|
const displayed = displayRun(run);
|
|
93
97
|
return displayed ? { ...displayed, events: (displayed.events || []).map(sanitizeRunEventForRender) } : displayed;
|
|
94
98
|
};
|
|
95
|
-
const activitySummary = RunActivitySummary?.create({ RunActivityModel, RunGuidanceView, getPendingGuidance, SubagentActivityView, tx, findRunRecord: (...args) => displayLocalizedRun(findRunRecord(...args)), getPanel, cssEscape,
|
|
99
|
+
const activitySummary = RunActivitySummary?.create({ RunActivityModel, RunGuidanceView, getPendingGuidance, SubagentActivityView, PanelIcons, tx, findRunRecord: (...args) => displayLocalizedRun(findRunRecord(...args)), getPanel, cssEscape,
|
|
96
100
|
projectReviewResultEvent: sanitizeRunEventForRender,
|
|
97
101
|
formatReviewResultFailure,
|
|
98
102
|
formatEventTime, formatElapsed, renderMarkdownBlockText, renderRunEvent, formatEventDetail,
|
|
99
|
-
presence, classifyStatus: classifyRunPresence, getProjectId: () => getCurrentProjectId?.() || '',
|
|
103
|
+
presence, getNotStartedRun, getFinishedRun, classifyStatus: classifyRunPresence, getProjectId: () => getCurrentProjectId?.() || '',
|
|
100
104
|
summarizeCommand: RunPresence?.commandSummary,
|
|
101
105
|
isGuidance: event => Boolean(RunGuidanceView.getGuidanceText(event)), sanitizeAssistantVisibleText });
|
|
102
106
|
|
|
@@ -180,7 +184,7 @@
|
|
|
180
184
|
? tx(`↓ Latest · ${unreadSinceDetach}`, `↓ 最新 · ${unreadSinceDetach}`)
|
|
181
185
|
: tx('↓ Latest', '↓ 最新');
|
|
182
186
|
button.setAttribute('aria-label', unreadSinceDetach > 0
|
|
183
|
-
? tx(`Jump to latest, ${unreadSinceDetach} new
|
|
187
|
+
? tx(`Jump to latest, ${unreadSinceDetach} new step${unreadSinceDetach === 1 ? '' : 's'}`, `跳到最新,${unreadSinceDetach} 个新步骤`)
|
|
184
188
|
: tx('Jump to latest', '跳到最新'));
|
|
185
189
|
}
|
|
186
190
|
|
|
@@ -285,6 +289,8 @@
|
|
|
285
289
|
scroller.scrollTop = scroller.scrollHeight;
|
|
286
290
|
}
|
|
287
291
|
function collapseRunProcess(view, statusText) {
|
|
292
|
+
const record = view?.recordId ? displayLocalizedRun(findRunRecord(view.recordId, view.sessionId)) : null;
|
|
293
|
+
if (record && getFinishedRun(record)) refreshCompletionView(view.root, view.report, record);
|
|
288
294
|
if (activitySummary) { activitySummary.settle(view, statusText); return; }
|
|
289
295
|
const runProcess = view?.runProcess || view?.root?.querySelector('[data-run-process]');
|
|
290
296
|
if (runProcess) {
|
|
@@ -297,11 +303,22 @@
|
|
|
297
303
|
const record = view?.recordId ? findRunRecord(view.recordId, view.sessionId) : null;
|
|
298
304
|
const stepCount = (record?.events || []).filter(event => (event.kind || 'activity') === 'activity').length;
|
|
299
305
|
statusEl.textContent = stepCount > 0
|
|
300
|
-
? `${statusText} · ${tx(`${stepCount}
|
|
306
|
+
? `${statusText} · ${tx(`${stepCount} step${stepCount === 1 ? '' : 's'}`, `${stepCount} 步`)}`
|
|
301
307
|
: statusText;
|
|
302
308
|
}
|
|
303
309
|
}
|
|
304
310
|
|
|
311
|
+
function refreshCompletionView(root, report, run) {
|
|
312
|
+
const event = (run.events || []).filter(item => item.kind === 'report').at(-1);
|
|
313
|
+
if (!root || !report || !event) return;
|
|
314
|
+
// Result actions may already live inside the previous report's facts row.
|
|
315
|
+
const actions = root.querySelector('.run-result-actions');
|
|
316
|
+
report.replaceChildren(renderCompletionReport(event, run));
|
|
317
|
+
if (actions) report.append(actions);
|
|
318
|
+
RunResultActions.configureResultActions(root, run);
|
|
319
|
+
configureEditButton(root, run);
|
|
320
|
+
}
|
|
321
|
+
|
|
305
322
|
// Keep elapsed labels live without creating new activity records.
|
|
306
323
|
function startRunElapsedTick() {
|
|
307
324
|
stopRunElapsedTick();
|
|
@@ -431,7 +448,6 @@
|
|
|
431
448
|
<div class="run-result-actions" hidden>
|
|
432
449
|
<button type="button" class="run-copy-action" data-run-copy hidden></button>
|
|
433
450
|
<button type="button" class="run-fork-action" data-run-fork hidden></button>
|
|
434
|
-
<span class="run-completed-time" data-run-completed-time hidden></span>
|
|
435
451
|
</div>
|
|
436
452
|
<div class="run-report" data-run-report aria-live="polite" hidden></div>
|
|
437
453
|
</div>
|
|
@@ -463,7 +479,7 @@
|
|
|
463
479
|
if (event.kind === 'report') {
|
|
464
480
|
report.hidden = false;
|
|
465
481
|
report.replaceChildren(renderCompletionReport(event, run));
|
|
466
|
-
} else if (activitySummary || event.kind === 'technical') {
|
|
482
|
+
} else if (getNotStartedRun(run) || activitySummary || event.kind === 'technical') {
|
|
467
483
|
continue;
|
|
468
484
|
} else if (RunGuidanceView.getGuidanceText(event)) {
|
|
469
485
|
continue;
|
|
@@ -484,6 +500,10 @@
|
|
|
484
500
|
}
|
|
485
501
|
|
|
486
502
|
function getRunStatusText(run = {}) {
|
|
503
|
+
const notStarted = getNotStartedRun(run);
|
|
504
|
+
if (notStarted) return notStarted.status;
|
|
505
|
+
const outcome = getFinishedRun(run);
|
|
506
|
+
if (outcome?.status) return outcome.status;
|
|
487
507
|
if (run.retryingWriteback) return tx('Checking file sync', '正在检查文件同步');
|
|
488
508
|
if (['Stopped tracking after a page refresh', '页面刷新后已停止跟踪'].includes(run.statusText)) return tr('restoredRunStoppedStatus');
|
|
489
509
|
const knownStatus = /^(?:Done|Failed|Cancelled|Running|已处理|处理失败|已取消|处理中)(?:\s+[\d.]+(?:ms|s|m|h|秒|分钟)(?:\s+[\d.]+(?:ms|s|m|h|秒|分钟))*)?$/.test(run.statusText || '');
|
|
@@ -567,6 +587,7 @@
|
|
|
567
587
|
}
|
|
568
588
|
|
|
569
589
|
function sanitizeRunEventForRender(event = {}) {
|
|
590
|
+
event = projectContextLimitEvent(event);
|
|
570
591
|
const streamRole = sanitizeAssistantVisibleText(event.streamRole);
|
|
571
592
|
const title = sanitizeAssistantVisibleText(event.title);
|
|
572
593
|
const activity = !streamRole && (!event.kind || event.kind === 'activity') && !event.guidanceId;
|
|
@@ -671,21 +692,41 @@
|
|
|
671
692
|
// The user's own open/closed choice per run survives card re-renders.
|
|
672
693
|
const metaOpenChoices = new Map();
|
|
673
694
|
|
|
695
|
+
// The completion time lives with the other run facts, not in the action row.
|
|
696
|
+
// `classed` matches lists whose rows carry the meta label/value classes.
|
|
697
|
+
function appendFinishedRow(list, run, { classed = false } = {}) {
|
|
698
|
+
const finishedAt = new Date(String(run?.finishedAt || ''));
|
|
699
|
+
if (!Number.isFinite(finishedAt.getTime())) return;
|
|
700
|
+
const dt = Object.assign(document.createElement('dt'), { textContent: tx('Finished', '完成于') });
|
|
701
|
+
dt.dataset.metaKey = 'finishedAt';
|
|
702
|
+
const dd = Object.assign(document.createElement('dd'), {
|
|
703
|
+
textContent: new Intl.DateTimeFormat(getLocale(), { hour: 'numeric', minute: '2-digit' }).format(finishedAt),
|
|
704
|
+
title: finishedAt.toLocaleString(getLocale()) });
|
|
705
|
+
if (classed) {
|
|
706
|
+
dt.className = 'run-final-answer__meta-label';
|
|
707
|
+
dd.className = 'run-final-answer__meta-value';
|
|
708
|
+
}
|
|
709
|
+
list.append(dt, dd);
|
|
710
|
+
}
|
|
711
|
+
|
|
674
712
|
// Renders the run-metadata rows (Why nothing changed / Write result / Undo /
|
|
675
713
|
// Next) behind a one-line summary beneath the answer. Shared by the
|
|
676
714
|
// structured and flat-fallback render paths so both demote identically.
|
|
677
|
-
function appendCompletionMetaBlock(report, meta, run) {
|
|
715
|
+
function appendCompletionMetaBlock(report, meta, run, { quiet = false } = {}) {
|
|
678
716
|
if (!Array.isArray(meta) || !meta.length) {
|
|
679
717
|
return;
|
|
680
718
|
}
|
|
681
|
-
const
|
|
719
|
+
const noWrites = getFinishedRun(run)?.noWrites;
|
|
720
|
+
const rows = projectCompletionMeta(meta, run).filter(row => row?.label && row.value
|
|
721
|
+
&& !(noWrites && ['writeResult', 'undo'].includes(row.key)))
|
|
682
722
|
.map(row => ({ ...row, shown: failureNotice?.formatMetaValue(report, row) ?? row.value }));
|
|
683
723
|
if (!rows.length) return;
|
|
684
724
|
const summary = RunResultActions.summarizeCompletionMeta(rows, run, {
|
|
685
|
-
tx, failed: report.dataset.status === 'failed', unconfirmed: rows.some(row => row.shown !== row.value)
|
|
725
|
+
tx, failed: report.dataset.status === 'failed' && !noWrites, unconfirmed: rows.some(row => row.shown !== row.value)
|
|
686
726
|
});
|
|
687
727
|
const metaBlock = document.createElement('dl');
|
|
688
728
|
metaBlock.className = 'run-final-answer__meta';
|
|
729
|
+
appendFinishedRow(metaBlock, run, { classed: true });
|
|
689
730
|
metaBlock.id = `codex-run-meta-${String(run?.id || 'report').replace(/[^a-zA-Z0-9_-]/g, '-')}`;
|
|
690
731
|
for (const row of rows) {
|
|
691
732
|
const dt = document.createElement('dt');
|
|
@@ -698,6 +739,12 @@
|
|
|
698
739
|
if (summary.warnKeys.has(row.key)) dt.dataset.tone = dd.dataset.tone = 'warn';
|
|
699
740
|
metaBlock.append(dt, dd);
|
|
700
741
|
}
|
|
742
|
+
if (quiet) {
|
|
743
|
+
const details = createOutcomeDetails(run);
|
|
744
|
+
details.querySelector('.run-outcome-details-content').append(metaBlock);
|
|
745
|
+
report.append(details);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
701
748
|
const facts = document.createElement('div');
|
|
702
749
|
facts.className = 'run-final-answer__facts';
|
|
703
750
|
const toggle = document.createElement('button');
|
|
@@ -712,12 +759,18 @@
|
|
|
712
759
|
toggle.append(item);
|
|
713
760
|
});
|
|
714
761
|
const more = Object.assign(document.createElement('span'), { className: 'run-final-answer__facts-more' });
|
|
715
|
-
|
|
762
|
+
// The chevron stays with the last fact, so a narrow row never leaves it alone on a line.
|
|
763
|
+
const tail = Object.assign(document.createElement('span'), { className: 'run-final-answer__facts-tail' });
|
|
764
|
+
if (toggle.lastElementChild) tail.append(toggle.lastElementChild);
|
|
765
|
+
tail.append(more);
|
|
766
|
+
toggle.append(tail);
|
|
716
767
|
const setOpen = open => {
|
|
717
768
|
facts.dataset.open = String(open);
|
|
718
769
|
metaBlock.hidden = !open;
|
|
719
770
|
toggle.setAttribute('aria-expanded', String(open));
|
|
720
|
-
|
|
771
|
+
// The facts are the toggle; the chevron and the tooltip carry its state.
|
|
772
|
+
toggle.title = open ? tx('Hide details', '收起详情') : tx('Show details', '展开详情');
|
|
773
|
+
more.innerHTML = `<svg class="codex-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="m6 4 4 4-4 4"/></svg>`;
|
|
721
774
|
};
|
|
722
775
|
setOpen(metaOpenChoices.get(run?.id) ?? summary.open);
|
|
723
776
|
toggle.addEventListener('click', event => {
|
|
@@ -730,11 +783,130 @@
|
|
|
730
783
|
report.append(facts);
|
|
731
784
|
}
|
|
732
785
|
|
|
786
|
+
function createOutcomeDetails(run) {
|
|
787
|
+
const details = document.createElement('details');
|
|
788
|
+
details.className = 'run-outcome-details';
|
|
789
|
+
details.open = metaOpenChoices.get(run?.id) ?? false;
|
|
790
|
+
details.append(Object.assign(document.createElement('summary'), { textContent: tr('runOutcomeDetails') }));
|
|
791
|
+
details.append(Object.assign(document.createElement('div'), { className: 'run-outcome-details-content' }));
|
|
792
|
+
details.addEventListener('toggle', () => { if (run?.id) metaOpenChoices.set(run.id, details.open); });
|
|
793
|
+
return details;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function renderOutcomeCard(report, event, run, outcome) {
|
|
797
|
+
const card = document.createElement('div');
|
|
798
|
+
card.className = 'run-outcome';
|
|
799
|
+
card.dataset.outcome = outcome.kind;
|
|
800
|
+
const main = document.createElement('div');
|
|
801
|
+
main.className = 'run-final-answer';
|
|
802
|
+
const title = document.createElement('div');
|
|
803
|
+
title.className = 'run-final-heading';
|
|
804
|
+
title.textContent = sanitizeAssistantVisibleText(outcome.title);
|
|
805
|
+
main.append(title);
|
|
806
|
+
if (outcome.body) main.append(Object.assign(document.createElement('p'), {
|
|
807
|
+
className: 'run-outcome-body', textContent: sanitizeAssistantVisibleText(outcome.body)
|
|
808
|
+
}));
|
|
809
|
+
if (outcome.files.length) {
|
|
810
|
+
const files = document.createElement('ul');
|
|
811
|
+
files.className = 'run-outcome-files';
|
|
812
|
+
for (const item of outcome.files) {
|
|
813
|
+
const row = document.createElement('li');
|
|
814
|
+
const mark = document.createElement('span');
|
|
815
|
+
mark.className = 'run-outcome-file-icon';
|
|
816
|
+
mark.dataset.written = String(item.written);
|
|
817
|
+
mark.innerHTML = RunPresence.icon(item.written ? 'written' : 'skipped');
|
|
818
|
+
mark.setAttribute('aria-label', tr(item.written ? 'runOutcomeWritten' : 'runOutcomeSkipped'));
|
|
819
|
+
row.append(mark, Object.assign(document.createElement('span'), {
|
|
820
|
+
className: 'run-outcome-file-name', textContent: sanitizeAssistantVisibleText(item.file)
|
|
821
|
+
}));
|
|
822
|
+
if (item.reason) row.append(Object.assign(document.createElement('span'), {
|
|
823
|
+
className: 'run-outcome-file-reason', textContent: sanitizeAssistantVisibleText(item.reason)
|
|
824
|
+
}));
|
|
825
|
+
files.append(row);
|
|
826
|
+
}
|
|
827
|
+
main.append(files);
|
|
828
|
+
}
|
|
829
|
+
const next = document.createElement('dl');
|
|
830
|
+
next.className = 'run-outcome-next';
|
|
831
|
+
next.append(Object.assign(document.createElement('dt'), { textContent: tr('detailNext') }),
|
|
832
|
+
Object.assign(document.createElement('dd'), { textContent: sanitizeAssistantVisibleText(outcome.nextStep) }));
|
|
833
|
+
card.append(main, next);
|
|
834
|
+
report.append(card);
|
|
835
|
+
failureNotice?.append(report, event, run, card);
|
|
836
|
+
appendRecoveryActionForFailure(report, event, run);
|
|
837
|
+
|
|
838
|
+
const details = createOutcomeDetails(run);
|
|
839
|
+
const rows = projectCompletionMeta(event.detailStructured.meta || [], run)
|
|
840
|
+
.filter(row => row?.value && row.key !== 'nextStep'
|
|
841
|
+
&& !(outcome.noWrites && ['writeResult', 'undo'].includes(row.key)));
|
|
842
|
+
if (outcome.failure?.code) rows.unshift({ label: tr('runOutcomeCode'), value: outcome.failure.code });
|
|
843
|
+
if (outcome.failure?.stage) rows.push({ label: tr('runOutcomeStage'), value: outcome.failure.stage });
|
|
844
|
+
if (outcome.noWrites) rows.push({ label: tr('runOutcomeWrites'), value: tr('runOutcomeNoneAttempted') });
|
|
845
|
+
const meta = document.createElement('dl');
|
|
846
|
+
meta.className = 'run-final-answer__meta';
|
|
847
|
+
appendFinishedRow(meta, run);
|
|
848
|
+
for (const row of rows) {
|
|
849
|
+
const label = Object.assign(document.createElement('dt'), { textContent: row.label });
|
|
850
|
+
if (row.key) label.dataset.metaKey = row.key;
|
|
851
|
+
meta.append(label, Object.assign(document.createElement('dd'), {
|
|
852
|
+
textContent: sanitizeAssistantVisibleText(failureNotice?.formatMetaValue(report, row) ?? row.value)
|
|
853
|
+
}));
|
|
854
|
+
}
|
|
855
|
+
const detailContent = details.querySelector('.run-outcome-details-content');
|
|
856
|
+
detailContent.append(meta);
|
|
857
|
+
// Preserve the original report body as optional context without repeating
|
|
858
|
+
// its skipped-item prose in the default outcome view.
|
|
859
|
+
if (event.detailStructured.body) {
|
|
860
|
+
const body = document.createElement('div');
|
|
861
|
+
renderMarkdownBlockText(body, RunResultActions.projectCompletionBody(event.detailStructured.body, tx));
|
|
862
|
+
detailContent.append(body);
|
|
863
|
+
}
|
|
864
|
+
card.append(details);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function formatCompileErrorNames(errors, totalCount) {
|
|
868
|
+
const names = [...new Set(errors.map(error => String(error)
|
|
869
|
+
.trim().replace(/^!\s*/, '').replace(/^(?:[A-Za-z]:)?[^:\r\n]+:\d+:\s*/, '').replace(/^!\s*/, '')
|
|
870
|
+
.split(/[ \t]+l\.\d+\b|[\r\n]/, 1)[0]
|
|
871
|
+
.trim().replace(/\.$/, '').replace(/\s+/g, ' ').trim()).filter(Boolean))];
|
|
872
|
+
const shown = names.slice(0, 3);
|
|
873
|
+
// History may retain fewer diagnostics than the original count. Preserve
|
|
874
|
+
// that omitted count without inventing names or changing the repair draft.
|
|
875
|
+
const more = Math.max(0, names.length - shown.length) + Math.max(0, totalCount - errors.length);
|
|
876
|
+
if (more) shown.push(tx(`+${more} more`, `另 ${more} 个`));
|
|
877
|
+
return shown.join(tx('; ', ';'));
|
|
878
|
+
}
|
|
879
|
+
|
|
733
880
|
function renderCompletionReport(input, run) {
|
|
734
881
|
const event = sanitizeRunEventForRender(input);
|
|
735
882
|
const report = document.createElement('section');
|
|
736
883
|
report.className = 'run-completion-report';
|
|
737
884
|
report.dataset.status = event.status || 'completed';
|
|
885
|
+
const notStarted = getNotStartedRun(run);
|
|
886
|
+
if (notStarted) {
|
|
887
|
+
report.dataset.status = 'not-started';
|
|
888
|
+
const card = document.createElement('div');
|
|
889
|
+
card.className = 'run-outcome';
|
|
890
|
+
const main = document.createElement('div');
|
|
891
|
+
main.className = 'run-final-answer';
|
|
892
|
+
const title = document.createElement('div');
|
|
893
|
+
title.className = 'run-final-heading';
|
|
894
|
+
title.textContent = notStarted.title;
|
|
895
|
+
const body = document.createElement('p');
|
|
896
|
+
body.className = 'run-outcome-body';
|
|
897
|
+
body.textContent = notStarted.body;
|
|
898
|
+
main.append(title, body);
|
|
899
|
+
const next = document.createElement('dl');
|
|
900
|
+
next.className = 'run-outcome-next';
|
|
901
|
+
next.append(Object.assign(document.createElement('dt'), {
|
|
902
|
+
textContent: tr('detailNext')
|
|
903
|
+
}), Object.assign(document.createElement('dd'), {
|
|
904
|
+
textContent: notStarted.nextStep
|
|
905
|
+
}));
|
|
906
|
+
card.append(main, next);
|
|
907
|
+
report.append(card);
|
|
908
|
+
return report;
|
|
909
|
+
}
|
|
738
910
|
// Compile errors captured post-write get a one-click fix loop: the
|
|
739
911
|
// structured error list rides on the report event (compileErrors).
|
|
740
912
|
const appendCompileFix = target => {
|
|
@@ -743,7 +915,7 @@
|
|
|
743
915
|
return;
|
|
744
916
|
}
|
|
745
917
|
const fix = buildRecoveryButton('compile_errors',
|
|
746
|
-
tx(`Fix ${compileErrors.length} compile error
|
|
918
|
+
tx(`Fix ${compileErrors.length} compile error${compileErrors.length === 1 ? '' : 's'}`, `修复 ${compileErrors.length} 个编译错误`),
|
|
747
919
|
tx('Prefills a task with @compile-log and the captured errors.', '自动组一条带 @compile-log 和错误列表的任务。'));
|
|
748
920
|
fix.addEventListener('click', clickEvent => {
|
|
749
921
|
clickEvent.stopPropagation();
|
|
@@ -781,6 +953,13 @@
|
|
|
781
953
|
// visually demoted block beneath, with a separator and muted color so
|
|
782
954
|
// they read as run metadata rather than part of the answer.
|
|
783
955
|
const structured = event.detailStructured;
|
|
956
|
+
const outcome = getFinishedRun(run);
|
|
957
|
+
if (outcome && !['cancelled', 'compile-errors'].includes(outcome.kind)) {
|
|
958
|
+
renderOutcomeCard(report, event, run, outcome);
|
|
959
|
+
appendCompileFix(report);
|
|
960
|
+
appendRejectedRedo(report);
|
|
961
|
+
return report;
|
|
962
|
+
}
|
|
784
963
|
const hasStructured = structured
|
|
785
964
|
&& (structured.conclusion || structured.body || (Array.isArray(structured.meta) && structured.meta.length));
|
|
786
965
|
if (hasStructured) {
|
|
@@ -788,7 +967,8 @@
|
|
|
788
967
|
main.className = 'run-final-answer';
|
|
789
968
|
const mainSections = [];
|
|
790
969
|
if (structured.conclusion) {
|
|
791
|
-
mainSections.push(
|
|
970
|
+
mainSections.push(outcome?.kind === 'compile-errors' ? outcome.conclusion
|
|
971
|
+
: RunResultActions.projectCompletionConclusion?.(event, run, tx('en', 'zh'), { localizeCompileReason }) ?? String(structured.conclusion));
|
|
792
972
|
}
|
|
793
973
|
if (structured.body) {
|
|
794
974
|
mainSections.push(RunResultActions.projectCompletionBody?.(structured.body, tx) ?? structured.body);
|
|
@@ -800,8 +980,20 @@
|
|
|
800
980
|
}
|
|
801
981
|
|
|
802
982
|
failureNotice?.append(report, event, run);
|
|
803
|
-
|
|
804
|
-
|
|
983
|
+
if (outcome?.kind === 'compile-errors') {
|
|
984
|
+
const compile = document.createElement('div');
|
|
985
|
+
compile.className = 'run-outcome run-outcome-compile';
|
|
986
|
+
const capturedErrors = Array.isArray(event.compileErrors) ? event.compileErrors.filter(Boolean) : [];
|
|
987
|
+
compile.append(Object.assign(document.createElement('strong'), { className: 'run-final-heading', textContent: outcome.title }),
|
|
988
|
+
Object.assign(document.createElement('p'), { className: 'run-outcome-body',
|
|
989
|
+
textContent: formatCompileErrorNames(capturedErrors.length ? capturedErrors : outcome.compileErrors, outcome.compileCount) }));
|
|
990
|
+
appendCompileFix(compile);
|
|
991
|
+
report.append(compile);
|
|
992
|
+
appendCompletionMetaBlock(report, structured.meta, run, { quiet: true });
|
|
993
|
+
} else {
|
|
994
|
+
appendCompletionMetaBlock(report, structured.meta, run);
|
|
995
|
+
appendCompileFix(report);
|
|
996
|
+
}
|
|
805
997
|
appendRejectedRedo(report);
|
|
806
998
|
appendRecoveryActionForFailure(report, event, run);
|
|
807
999
|
return report;
|
|
@@ -816,7 +1008,8 @@
|
|
|
816
1008
|
const split = splitFlatCompletionReport(flatText);
|
|
817
1009
|
const body = document.createElement('div');
|
|
818
1010
|
body.className = 'run-final-answer';
|
|
819
|
-
renderMarkdownBlockText(body,
|
|
1011
|
+
renderMarkdownBlockText(body, RunResultActions.projectCompletionConclusion?.(event, run, tx('en', 'zh'),
|
|
1012
|
+
{ flat: true, text: split.body || flatText, localizeCompileReason }) ?? (split.body || flatText));
|
|
820
1013
|
decorateCompletionReport(body, { tx });
|
|
821
1014
|
report.append(body);
|
|
822
1015
|
appendCompileFix(report);
|
|
@@ -855,7 +1048,8 @@
|
|
|
855
1048
|
&& Boolean(String(run?.task || '').trim())
|
|
856
1049
|
&& typeof canEditRun === 'function' && canEditRun(run)
|
|
857
1050
|
&& typeof refillComposerForRetry === 'function';
|
|
858
|
-
|
|
1051
|
+
const cardHasEdit = Boolean(root.querySelector('.run-outcome [data-recovery-action="edit-resend"]'));
|
|
1052
|
+
button.hidden = !available || cardHasEdit;
|
|
859
1053
|
if (!available) return;
|
|
860
1054
|
button.disabled = typeof options.running === 'boolean'
|
|
861
1055
|
? options.running : Boolean(getCurrentRunView?.());
|
|
@@ -903,7 +1097,7 @@
|
|
|
903
1097
|
}
|
|
904
1098
|
|
|
905
1099
|
const projection = RunResultActions.projectUndoAvailability(run, projectRunSettlement);
|
|
906
|
-
if (!projection.canUndo && run.undoStatus !== 'applied') {
|
|
1100
|
+
if (!projection.canUndo && run.undoStatus !== 'applied' && !projection.undoUnavailable?.length) {
|
|
907
1101
|
button.hidden = true;
|
|
908
1102
|
return;
|
|
909
1103
|
}
|
|
@@ -911,7 +1105,7 @@
|
|
|
911
1105
|
button.hidden = false;
|
|
912
1106
|
const inFlight = trackedChangeInFlight?.get(run.id) === 'reject';
|
|
913
1107
|
const applied = run.undoStatus === 'applied' && !inFlight;
|
|
914
|
-
button.disabled = inFlight || (run.undoStatus === 'running' && !run.undoRecoveryCheckRequired) || run.undoStatus === 'applied';
|
|
1108
|
+
button.disabled = !projection.canUndo || inFlight || (run.undoStatus === 'running' && !run.undoRecoveryCheckRequired) || run.undoStatus === 'applied';
|
|
915
1109
|
button.textContent = applied
|
|
916
1110
|
? tr('undoApplied')
|
|
917
1111
|
: (run.partialWriteback ? tr('undoPartialRun') : tr('undoRun'));
|
|
@@ -919,6 +1113,8 @@
|
|
|
919
1113
|
? tr('undoAppliedTitle')
|
|
920
1114
|
: (run.partialWriteback ? tr('undoPartialRunTitle') : tr('undoRunTitle'));
|
|
921
1115
|
if (run.undoRecoveryUnavailable) button.title = tx('Undo recovery checks are unavailable. Reload this page after the connection recovers.', '撤销恢复检查暂不可用,请在连接恢复后重新加载此页面。');
|
|
1116
|
+
if (projection.undoUnavailable?.length) button.title = tr('failureReason_snapshot_undo_unavailable_user')
|
|
1117
|
+
+ ' ' + projection.undoUnavailable.map(item => item.path).join(', ');
|
|
922
1118
|
button.addEventListener('click', event => {
|
|
923
1119
|
event.stopPropagation();
|
|
924
1120
|
undoRun(run.id);
|
|
@@ -935,6 +1131,9 @@
|
|
|
935
1131
|
const status = run.trackedChangeStatus || '';
|
|
936
1132
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
937
1133
|
const projection = projectRunSettlement(run);
|
|
1134
|
+
const undoTitle = projection.undoUnavailable?.length
|
|
1135
|
+
? tr('failureReason_snapshot_undo_unavailable_user') + ' ' + projection.undoUnavailable.map(item => item.path).join(', ')
|
|
1136
|
+
: tr('undoRunTitle');
|
|
938
1137
|
// §7 settlement matrix: needs_review keeps BOTH controls visible AND
|
|
939
1138
|
// actionable. Branch placed before the terminal branches so a
|
|
940
1139
|
// needs_review run is never treated as terminal.
|
|
@@ -942,7 +1141,7 @@
|
|
|
942
1141
|
button.hidden = false;
|
|
943
1142
|
button.disabled = !projection.canUndo || inFlight === 'reject' || inFlight === 'accept';
|
|
944
1143
|
button.textContent = tr('undoRun');
|
|
945
|
-
button.title =
|
|
1144
|
+
button.title = undoTitle;
|
|
946
1145
|
button.addEventListener('click', event => {
|
|
947
1146
|
event.stopPropagation();
|
|
948
1147
|
undoRun(run.id);
|
|
@@ -962,7 +1161,7 @@
|
|
|
962
1161
|
button.hidden = false;
|
|
963
1162
|
button.disabled = true;
|
|
964
1163
|
button.textContent = tr('undoRun');
|
|
965
|
-
button.title = tr('
|
|
1164
|
+
button.title = tr('undoAcceptedTitle');
|
|
966
1165
|
return;
|
|
967
1166
|
}
|
|
968
1167
|
if (status !== 'pending' && !inFlight) {
|
|
@@ -972,7 +1171,7 @@
|
|
|
972
1171
|
button.hidden = false;
|
|
973
1172
|
button.disabled = !projection.canUndo || inFlight === 'reject' || inFlight === 'accept';
|
|
974
1173
|
button.textContent = tr('undoRun');
|
|
975
|
-
button.title =
|
|
1174
|
+
button.title = undoTitle;
|
|
976
1175
|
button.addEventListener('click', event => {
|
|
977
1176
|
event.stopPropagation();
|
|
978
1177
|
undoRun(run.id);
|
|
@@ -1041,7 +1240,7 @@
|
|
|
1041
1240
|
button.hidden = false;
|
|
1042
1241
|
button.disabled = true;
|
|
1043
1242
|
button.textContent = tr('runAcceptTracked');
|
|
1044
|
-
button.title = tr('
|
|
1243
|
+
button.title = tr('undoAppliedTitle');
|
|
1045
1244
|
return;
|
|
1046
1245
|
}
|
|
1047
1246
|
if (status !== 'pending' && !inFlight) {
|
|
@@ -1102,7 +1301,7 @@
|
|
|
1102
1301
|
const log = getPanel()?.querySelector('[data-log]');
|
|
1103
1302
|
const existing = log?.querySelector(`[data-run-id="${cssEscape(runId)}"]`);
|
|
1104
1303
|
const run = findRunRecord(runId);
|
|
1105
|
-
if (!log || !existing || !run || displayRun(run) === run) {
|
|
1304
|
+
if (!log || !existing || !run || (displayRun(run) === run && !getFinishedRun(run))) {
|
|
1106
1305
|
return false;
|
|
1107
1306
|
}
|
|
1108
1307
|
const reading = scrollLayout?.snapshot(), open = existing.querySelector('[data-run-process]')?.open;
|
|
@@ -1132,7 +1331,7 @@
|
|
|
1132
1331
|
RunResultActions.configureResultActions(existing, run);
|
|
1133
1332
|
const reportEvent = (run.events || []).filter(event => event.kind === 'report').at(-1);
|
|
1134
1333
|
const report = current.report || existing.querySelector('[data-run-report]');
|
|
1135
|
-
if (reportEvent && report)
|
|
1334
|
+
if (reportEvent && report) refreshCompletionView(existing, report, displayLocalizedRun(run));
|
|
1136
1335
|
activitySummary?.update(current);
|
|
1137
1336
|
if (!activitySummary) RunGuidanceView.reconcileGuidance?.(existing.querySelector('[data-run-guidance]'),
|
|
1138
1337
|
(run.events || []).concat(getPendingGuidance(run)), tx, { active: run.status === 'running' });
|