codex-overleaf-link 2.5.0 → 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.
Files changed (189) hide show
  1. package/README.md +155 -392
  2. package/README.zh-CN.md +154 -393
  3. package/extension/bootstrap/background.js +52 -9
  4. package/extension/bootstrap/manifest.template.json +1 -1
  5. package/extension/bootstrap/popup.html +1 -0
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/updateStatus.js +1 -0
  8. package/extension/src/background.js +283 -55
  9. package/extension/src/backgroundBootstrapMigration.js +181 -0
  10. package/extension/src/backgroundSharedSessions.js +129 -16
  11. package/extension/src/backgroundUpdateCoordinator.js +242 -29
  12. package/extension/src/backgroundUpdateSafety.js +173 -0
  13. package/extension/src/content/activeTurnControl.js +171 -8
  14. package/extension/src/content/applyResultFormatters.js +46 -73
  15. package/extension/src/content/assetTransferBroker.js +72 -14
  16. package/extension/src/content/composerAttachments.js +104 -47
  17. package/extension/src/content/composerPanel.js +110 -12
  18. package/extension/src/content/contentRuntime.js +1555 -926
  19. package/extension/src/content/contextTray.js +8 -2
  20. package/extension/src/content/contextUsageView.js +267 -0
  21. package/extension/src/content/diagnosticsController.js +122 -96
  22. package/extension/src/content/diagnosticsExport.js +19 -5
  23. package/extension/src/content/diagnosticsPanel.js +48 -0
  24. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  25. package/extension/src/content/generated/content.bundle.js +11077 -3412
  26. package/extension/src/content/generated/content.bundle.meta.json +14 -3
  27. package/extension/src/content/globalPreferencesController.js +1 -1
  28. package/extension/src/content/legacyGlobalRegistry.js +10 -0
  29. package/extension/src/content/localSkillsPanel.js +104 -14
  30. package/extension/src/content/markdownDomRenderer.js +5 -3
  31. package/extension/src/content/markdownText.js +47 -11
  32. package/extension/src/content/modelPicker.js +8 -4
  33. package/extension/src/content/nativeCompatibilityController.js +74 -30
  34. package/extension/src/content/otWarmMirror.js +5 -6
  35. package/extension/src/content/otWarmMirrorController.js +17 -20
  36. package/extension/src/content/pageBridgeClient.js +17 -4
  37. package/extension/src/content/panelMaintenance.js +46 -14
  38. package/extension/src/content/panelRenderer.js +57 -27
  39. package/extension/src/content/pendingInputView.js +1 -1
  40. package/extension/src/content/projectProviderSelection.js +13 -4
  41. package/extension/src/content/projectSessionCleanup.js +109 -62
  42. package/extension/src/content/projectSettingsCoordinator.js +67 -6
  43. package/extension/src/content/providerSettingsCoordinator.js +46 -8
  44. package/extension/src/content/providerSettingsDialog.js +73 -14
  45. package/extension/src/content/recentProjects.js +15 -8
  46. package/extension/src/content/runActivitySummary.js +160 -18
  47. package/extension/src/content/runCompletionMeta.js +532 -0
  48. package/extension/src/content/runController.js +39 -15
  49. package/extension/src/content/runFailureNotice.js +29 -13
  50. package/extension/src/content/runGuidanceController.js +72 -27
  51. package/extension/src/content/runGuidanceView.js +59 -10
  52. package/extension/src/content/runPresence.js +18 -8
  53. package/extension/src/content/runQueueScheduler.js +157 -47
  54. package/extension/src/content/runRecoveryActions.js +147 -0
  55. package/extension/src/content/runResultActions.js +28 -143
  56. package/extension/src/content/runReviewController.js +633 -0
  57. package/extension/src/content/runScrollLayout.js +21 -2
  58. package/extension/src/content/runTimelineView.js +367 -175
  59. package/extension/src/content/scopedPersistencePanelState.js +134 -20
  60. package/extension/src/content/selectionContextView.js +1 -1
  61. package/extension/src/content/sensitivePreflight.js +153 -0
  62. package/extension/src/content/sessionForkController.js +80 -17
  63. package/extension/src/content/sessionManager.js +50 -4
  64. package/extension/src/content/sessionMenuView.js +54 -18
  65. package/extension/src/content/sessionPersistence.js +25 -5
  66. package/extension/src/content/settingsPanel.js +22 -5
  67. package/extension/src/content/settingsWorkbench.js +132 -89
  68. package/extension/src/content/sharedSessionViewSync.js +49 -11
  69. package/extension/src/content/subagentActivityView.js +23 -7
  70. package/extension/src/content/updateIdle.js +4 -1
  71. package/extension/src/content/updateNotice.js +118 -12
  72. package/extension/src/content/updateSafetyReceiver.js +58 -0
  73. package/extension/src/content/writebackController.js +53 -4
  74. package/extension/src/content/writebackOrchestrator.js +116 -94
  75. package/extension/src/content/writingStyleSettings.js +35 -11
  76. package/extension/src/page/binaryAssetUploader.js +136 -14
  77. package/extension/src/page/compileBridge.js +193 -180
  78. package/extension/src/page/fileMover.js +256 -0
  79. package/extension/src/page/overleafCapabilities.js +4 -1
  80. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  81. package/extension/src/page/referenceProjects.js +1 -0
  82. package/extension/src/page/reviewContext.js +113 -0
  83. package/extension/src/page/saveState.js +6 -4
  84. package/extension/src/page/snapshotRouter.js +84 -39
  85. package/extension/src/page/textCoordinates.js +17 -1
  86. package/extension/src/page/textFileCreator.js +114 -26
  87. package/extension/src/page/trackedChangeCapture.js +12 -2
  88. package/extension/src/page/trackedChangeOwnership.js +19 -1
  89. package/extension/src/page/trackedChangeReplay.js +41 -21
  90. package/extension/src/page/trackedChangesLifecycle.js +217 -199
  91. package/extension/src/page/treeOperations.js +7 -10
  92. package/extension/src/page/updateSafetyProbe.js +41 -0
  93. package/extension/src/page/writebackReceiptJournal.js +2 -0
  94. package/extension/src/page/writebackRouter.js +103 -155
  95. package/extension/src/pageBridge.js +126 -69
  96. package/extension/src/popup.js +62 -10
  97. package/extension/src/shared/agentTranscript.js +373 -17
  98. package/extension/src/shared/auditRecords.js +4 -0
  99. package/extension/src/shared/compatibility.js +22 -6
  100. package/extension/src/shared/compileAdapter.js +129 -66
  101. package/extension/src/shared/composerAttachmentDraft.js +156 -0
  102. package/extension/src/shared/contextUsage.js +125 -0
  103. package/extension/src/shared/directoryCleanupProof.js +43 -0
  104. package/extension/src/shared/failureReasons.js +349 -4
  105. package/extension/src/shared/globalPreferences.js +30 -13
  106. package/extension/src/shared/governanceRules.js +4 -37
  107. package/extension/src/shared/i18n.js +590 -24
  108. package/extension/src/shared/lineReferences.js +38 -31
  109. package/extension/src/shared/managedUpdateProjection.js +2 -2
  110. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  111. package/extension/src/shared/pageRpcContract.js +5 -4
  112. package/extension/src/shared/panelIcons.js +39 -0
  113. package/extension/src/shared/projectFiles.js +21 -44
  114. package/extension/src/shared/providerProfiles.js +5 -3
  115. package/extension/src/shared/reviewing.js +130 -1
  116. package/extension/src/shared/runActivityModel.js +70 -4
  117. package/extension/src/shared/runExecutionSnapshot.js +13 -0
  118. package/extension/src/shared/runInputQueue.js +48 -5
  119. package/extension/src/shared/selectionContext.js +12 -3
  120. package/extension/src/shared/sessionState.js +132 -28
  121. package/extension/src/shared/sessionTitle.js +14 -1
  122. package/extension/src/shared/settlementFacts.js +10 -2
  123. package/extension/src/shared/sharedSessionBridge.js +118 -27
  124. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  125. package/extension/src/shared/staleGuard.js +166 -52
  126. package/extension/src/shared/storageDb.js +90 -26
  127. package/extension/src/shared/storageMigration.js +78 -112
  128. package/extension/src/shared/storageRunActions.js +267 -30
  129. package/extension/src/shared/subagentActivityModel.js +1 -1
  130. package/extension/src/shared/undoOperations.js +253 -32
  131. package/extension/src/shared/updateConsent.js +6 -4
  132. package/extension/src/shared/updateRuntimeIdentity.js +17 -3
  133. package/extension/src/shared/writebackIntent.js +16 -0
  134. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  135. package/extension/src/shared/writebackSettlement.js +58 -40
  136. package/extension/styles/panel.css +1123 -493
  137. package/native-host/src/anthropicBridgeRequest.js +4 -2
  138. package/native-host/src/anthropicBridgeResponse.js +6 -2
  139. package/native-host/src/anthropicMessagesBridge.js +24 -16
  140. package/native-host/src/bootstrapMigration.js +296 -0
  141. package/native-host/src/bootstrapMigrationContract.js +101 -0
  142. package/native-host/src/bootstrapUpdate.js +269 -0
  143. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  144. package/native-host/src/chatBridgeRequest.js +60 -6
  145. package/native-host/src/chatBridgeResponse.js +20 -1
  146. package/native-host/src/chatCompletionsBridge.js +48 -15
  147. package/native-host/src/codexConfigPaths.js +106 -0
  148. package/native-host/src/codexHome.js +66 -73
  149. package/native-host/src/codexHomeScope.js +124 -0
  150. package/native-host/src/codexInheritedAuth.js +90 -0
  151. package/native-host/src/codexLocalSkills.js +301 -0
  152. package/native-host/src/codexPromptAssembly.js +114 -127
  153. package/native-host/src/codexProviderTest.js +24 -26
  154. package/native-host/src/codexSessionRunner.js +234 -198
  155. package/native-host/src/codexSessionTiming.js +18 -8
  156. package/native-host/src/codexSkillIsolation.js +170 -0
  157. package/native-host/src/codexThreadFork.js +19 -3
  158. package/native-host/src/index.js +19 -7
  159. package/native-host/src/managedInstall.js +110 -35
  160. package/native-host/src/managedInstallRecovery.js +140 -0
  161. package/native-host/src/managedRuntimeEvidence.js +57 -0
  162. package/native-host/src/mirrorWorkspace.js +92 -22
  163. package/native-host/src/nativeDoctor.js +15 -0
  164. package/native-host/src/nativeHelperLifecycle.js +21 -0
  165. package/native-host/src/nativeHostRegistration.js +33 -0
  166. package/native-host/src/nativeRunLifecycle.js +71 -0
  167. package/native-host/src/processStopBarrier.js +42 -7
  168. package/native-host/src/projectPathPlatform.js +30 -0
  169. package/native-host/src/providerContextError.js +20 -0
  170. package/native-host/src/providerHttp.js +80 -0
  171. package/native-host/src/providerProfile.js +4 -2
  172. package/native-host/src/providerReasoning.js +2 -7
  173. package/native-host/src/providerRuntime.js +1 -0
  174. package/native-host/src/providerStore.js +72 -14
  175. package/native-host/src/responsesPassthroughBridge.js +266 -52
  176. package/native-host/src/runtimeInstaller.js +21 -1
  177. package/native-host/src/selectionScope.js +36 -8
  178. package/native-host/src/subagentAdmission.js +26 -0
  179. package/native-host/src/subagentBroker.js +79 -66
  180. package/native-host/src/subagentTelemetry.js +19 -10
  181. package/native-host/src/subagentWorkspacePath.js +5 -2
  182. package/native-host/src/taskRunnerRuntime.js +92 -57
  183. package/native-host/src/textPatch.js +473 -377
  184. package/native-host/src/tomlStatements.js +140 -0
  185. package/native-host/src/updateManager.js +409 -46
  186. package/native-host/src/writingStyleRuntime.js +57 -29
  187. package/native-host/src/writingStyleStore.js +73 -0
  188. package/package.json +5 -8
  189. package/scripts/install-managed.mjs +1 -0
@@ -3,8 +3,8 @@
3
3
 
4
4
  function create(deps = {}) {
5
5
  if (!deps.RunActivityModel) return null;
6
- const { RunActivityModel: Model, tx, findRunRecord, getPanel, cssEscape, isGuidance,
7
- formatElapsed, renderMarkdownBlockText, renderRunEvent, presence, classifyStatus, getProjectId, summarizeCommand,
6
+ const { RunActivityModel: Model, RunGuidanceView, getPendingGuidance = () => [], PanelIcons, tx, findRunRecord, getPanel, cssEscape, isGuidance,
7
+ formatElapsed, renderMarkdownBlockText, renderRunEvent, presence, getNotStartedRun, getFinishedRun, classifyStatus, getProjectId, summarizeCommand, projectReviewResultEvent, formatReviewResultFailure,
8
8
  sanitizeAssistantVisibleText: sanitize = value => String(value || '') } = deps;
9
9
  const cards = new WeakMap();
10
10
  const choices = new Map();
@@ -24,7 +24,7 @@
24
24
  return node;
25
25
  };
26
26
  const subagentView = deps.SubagentActivityView?.create({
27
- tx, getPanel, plain, make, icon, formatElapsed,
27
+ tx, getPanel, plain, plainThought, make, icon, formatElapsed, glyph: name => deps.PanelIcons?.svg?.(name) || '',
28
28
  renderBlocks: (parent, blocks, rows, card, run) => reconcile(parent, blocks, rows,
29
29
  block => buildBlock(block, card), (row, block) => updateBlock(row, block, card, run))
30
30
  });
@@ -115,6 +115,31 @@
115
115
  disclosure(el, key);
116
116
  return { el, label, target, status, diff, body, key, bodyStamp: '' };
117
117
  }
118
+ function reviewResultLines(item) {
119
+ const source = item.event || {};
120
+ if (item.kind !== 'notice' || source.streamRole || source.guidanceId || source.subagent || source.technicalDetail
121
+ || (source.kind && source.kind !== 'activity') || item.meta?.noticeSource || source.activity?.noticeSource || source.activity?.id) return [];
122
+ const event = projectReviewResultEvent?.(source) || source;
123
+ const english = /^(?:Undo result: (?:undone \d+ item\(s\), skipped \d+ item\(s\)|undone \d+ file\(s\), skipped \d+|rejected \d+ tracked change\(s\), skipped \d+)|Accept result: accepted \d+ tracked change\(s\), skipped \d+)$/;
124
+ const chinese = /^(?:撤销结果:(?:已撤销 \d+ 项,跳过 \d+ 项|已撤销 \d+ 个文件,跳过 \d+ 项|已拒绝 \d+ 条留痕,跳过 \d+ 条)|接受结果:已接受 \d+ 条留痕,跳过 \d+ 条)$/;
125
+ if (!english.test(event.title || '') && !chinese.test(event.title || '')) return [];
126
+ const skipped = event.detail?.Skipped || event.detail?.['跳过'];
127
+ if (!Array.isArray(skipped) && source.failure) {
128
+ const message = plain(formatReviewResultFailure?.(source.failure)?.message).slice(0, 1000);
129
+ return message ? [message] : [];
130
+ }
131
+ if (!Array.isArray(skipped)) return [];
132
+ return skipped.flatMap(entry => {
133
+ const fileValue = entry?.File ?? entry?.['文件'], reasonValue = entry?.Reason ?? entry?.['原因'];
134
+ if (typeof fileValue !== 'string' || typeof reasonValue !== 'string') return [];
135
+ if (source.failure?.file === fileValue && [source.failure.userMessage, fileValue + ': ' + source.failure.userMessage].includes(reasonValue)) {
136
+ const message = plain(formatReviewResultFailure?.(source.failure)?.message).slice(0, 1000);
137
+ if (message) return [message];
138
+ }
139
+ const file = plain(fileValue).slice(0, 300), reason = plain(reasonValue).slice(0, 1000);
140
+ return file && reason ? [reason.startsWith(file + ': ') ? reason : file + ': ' + reason] : [];
141
+ });
142
+ }
118
143
  function renderTool(row, item, card, run) {
119
144
  const meta = item.meta || {};
120
145
  const state = effectiveState(item, run);
@@ -140,7 +165,8 @@
140
165
  row.status.hidden = true;
141
166
  } else row.status.hidden = false;
142
167
  const publicText = plain(item.event?.title);
143
- const stamp = [publicText, meta.output, meta.command, meta.exitCode, file?.preview, state, meta.clipped].join('\0');
168
+ const reviewLines = reviewResultLines(item);
169
+ const stamp = [publicText, meta.output, meta.command, meta.exitCode, file?.preview, state, meta.clipped, JSON.stringify(reviewLines)].join('\0');
144
170
  if (stamp !== row.bodyStamp) {
145
171
  row.bodyStamp = stamp;
146
172
  row.body.replaceChildren();
@@ -155,9 +181,11 @@
155
181
  } else {
156
182
  if (meta.command) { const command = make('pre', 'run-workflow-command'); command.textContent = plain(meta.command); row.body.append(command); }
157
183
  if (meta.output) { const output = make('pre', 'run-workflow-output'); output.textContent = plain(meta.output); row.body.append(output); }
158
- const note = make('div', 'run-workflow-tool-note');
159
- note.textContent = publicText;
160
- row.body.append(note);
184
+ for (const value of reviewLines.length ? reviewLines : [publicText]) {
185
+ const note = make('div', 'run-workflow-tool-note');
186
+ note.textContent = value;
187
+ row.body.append(note);
188
+ }
161
189
  }
162
190
  if (meta.exitCode !== undefined) { const exit = make('div', 'run-workflow-tool-note'); exit.textContent = tx('Exit code: ', '退出码:') + meta.exitCode; row.body.append(exit); }
163
191
  if (meta.clipped || file?.clipped) { const more = make('div', 'run-workflow-tool-note'); more.textContent = tx('Preview only. More detail may be available in diagnostics.', '当前为摘要预览,更多信息可通过诊断查看。'); row.body.append(more); }
@@ -201,6 +229,16 @@
201
229
  el.addEventListener('toggle', row.showFull);
202
230
  return row;
203
231
  }
232
+ // One-line thought previews are plain text: drop the Markdown emphasis that
233
+ // reasoning summaries use for their headings ("**Reading the file**").
234
+ function plainThought(value) {
235
+ return String(value || '').replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, '$2').replace(/\s+/g, ' ').trim();
236
+ }
237
+ // A finished thought that opens with a bold heading is summarised by that heading.
238
+ function thoughtLead(value) {
239
+ const heading = /^\s*(\*\*|__)(?=\S)(.+?\S)\1\s*(?:\n|$)/.exec(String(value || ''));
240
+ return heading ? heading[2].trim() : firstSentence(plainThought(value));
241
+ }
204
242
  function updateThought(row, block, run) {
205
243
  const value = String(sanitize(block.event.title) || '').trim();
206
244
  const live = run.status === 'running' && block.event.status === 'running';
@@ -215,8 +253,7 @@
215
253
  const started = Date.parse(block.startedAt || ''), ended = Date.parse(block.event.timestamp || '');
216
254
  const seconds = Number.isFinite(started) && Number.isFinite(ended) && ended > started ? formatElapsed(ended - started) : '';
217
255
  text(row.label, seconds ? tx('Thought ' + seconds, '思考 ' + seconds) : tx('Thought', '思考'));
218
- const flat = value.replace(/\s+/g, ' ');
219
- text(row.lead, (flat.match(/^.{12,160}?[.!?。!?](?=\s|$)/) || [flat.slice(0, 160)])[0]);
256
+ text(row.lead, thoughtLead(value));
220
257
  row.value = value;
221
258
  row.showFull();
222
259
  }
@@ -350,10 +387,10 @@
350
387
  const live = run.status === 'running' && block.event.status === 'running';
351
388
  const started = Date.parse(block.startedAt || ''), ended = Date.parse(block.event.timestamp || '');
352
389
  const seconds = Number.isFinite(started) && Number.isFinite(ended) && ended > started ? ' ' + formatElapsed(ended - started) : '';
353
- const flat = value.replace(/\s+/g, ' ');
390
+ const flat = plainThought(value);
354
391
  entries.push({ key: block.key, kind: 'thought', state: live ? 'running' : 'completed',
355
392
  label: live ? tx('Thinking', '思考中') : tx('Thought', '思考') + seconds,
356
- lead: live ? (flat.length > 90 ? '…' + flat.slice(-90) : flat) : firstSentence(value) });
393
+ lead: live ? (flat.length > 90 ? '…' + flat.slice(-90) : flat) : thoughtLead(value) });
357
394
  } else if (block.kind !== 'message' && block.kind !== 'guidance') {
358
395
  for (const item of block.kind === 'exploreGroup' ? block.items : [block]) {
359
396
  if (item.kind === 'notice') continue;
@@ -464,6 +501,74 @@
464
501
  if (!choices.has(row.key)) row.el.open = items.some(item => card.projection.notices.some(notice => notice.key === item.key));
465
502
  }
466
503
 
504
+ // Compaction marks sit outside the activity rows: a manual /compact run is
505
+ // shown as a divider, and a completed automatic compaction leaves one above
506
+ // that turn's answer.
507
+ function mountCompaction(root, run) {
508
+ const node = make('div', 'run-compaction');
509
+ node.hidden = true;
510
+ const line = make('div', 'run-compaction-line');
511
+ const label = make('span', 'run-compaction-label');
512
+ const glyph = make('span', 'run-compaction-glyph');
513
+ glyph.setAttribute('aria-hidden', 'true');
514
+ const title = make('span', 'run-compaction-title');
515
+ const time = make('span', 'run-compaction-time');
516
+ label.append(glyph, title, time);
517
+ line.append(label);
518
+ const sub = make('div', 'run-compaction-sub');
519
+ node.append(line, sub);
520
+ if (run.operation === 'compact') {
521
+ root.dataset.operation = 'compact';
522
+ node.dataset.kind = 'manual';
523
+ root.querySelector('[data-run-task]')?.after(node);
524
+ } else {
525
+ node.dataset.kind = 'auto';
526
+ root.querySelector('[data-run-report]')?.before(node);
527
+ }
528
+ return { node, glyph, title, time, sub, icon: '' };
529
+ }
530
+ function renderCompaction(card, run, result) {
531
+ const view = card.compaction;
532
+ if (!view) return;
533
+ const clock = value => {
534
+ const date = new Date(value || '');
535
+ return Number.isFinite(date.getTime())
536
+ ? new Intl.DateTimeFormat(tx('en-US', 'zh-CN'), { hour: '2-digit', minute: '2-digit' }).format(date) : '';
537
+ };
538
+ const setIcon = name => {
539
+ if (view.icon === name) return;
540
+ view.icon = name;
541
+ view.glyph.innerHTML = name ? PanelIcons?.svg(name) || '' : '';
542
+ };
543
+ if (view.node.dataset.kind === 'manual') {
544
+ const state = run.status === 'running' ? 'running' : run.status === 'completed' ? 'completed'
545
+ : ['cancelled', 'rejected'].includes(run.status) ? 'cancelled' : run.status === 'interrupted' ? 'interrupted' : 'failed';
546
+ view.node.dataset.state = state;
547
+ view.node.hidden = false;
548
+ setIcon(state === 'running' ? '' : state === 'completed' ? 'compact' : 'alert');
549
+ text(view.title, ({
550
+ running: tx('Compacting context', '正在压缩上下文'),
551
+ completed: tx('Context compacted', '上下文已压缩'),
552
+ cancelled: tx('Compaction stopped', '已停止压缩'),
553
+ interrupted: tx('Compaction interrupted', '压缩已中断'),
554
+ failed: tx('Compaction did not finish', '压缩未完成')
555
+ })[state]);
556
+ text(view.time, state === 'running' ? '' : clock(run.finishedAt));
557
+ text(view.sub, state === 'running' ? tx('You can keep typing; messages send after compaction.', '可以继续输入,消息会在压缩完成后发送。')
558
+ : state === 'completed' ? tx('Earlier turns were summarized.', '此前的对话已整理为摘要。') : '');
559
+ view.sub.hidden = !view.sub.textContent;
560
+ return;
561
+ }
562
+ view.node.hidden = !result.compactedAt;
563
+ if (!result.compactedAt) return;
564
+ view.node.dataset.state = 'completed';
565
+ setIcon('compact');
566
+ text(view.title, tx('Context compacted automatically', '上下文已自动压缩'));
567
+ text(view.time, clock(result.compactedAt));
568
+ text(view.sub, tx('Codex summarized earlier turns partway through this turn, then continued.',
569
+ 'Codex 在这一轮中途整理了此前的对话,然后继续处理。'));
570
+ view.sub.hidden = false;
571
+ }
467
572
  function mount(root, run) {
468
573
  if (!Model || cards.has(root)) return;
469
574
  const process = root.querySelector('[data-run-process]');
@@ -502,8 +607,9 @@
502
607
  busy.setAttribute('role', 'status');
503
608
  events.after(busy);
504
609
  const card = { id: String(run.id), root, process, summary, events, metrics, attention,
610
+ guidance: root.querySelector('[data-run-guidance]'),
505
611
  busy, rows: new Map(), run, setup: setupEl, setupSummary, setupFold, setupSteps,
506
- peek, peekTrack, peekMore, peekRows: new Map() };
612
+ peek, peekTrack, peekMore, peekRows: new Map(), compaction: mountCompaction(root, run) };
507
613
  const expand = () => { remember(card.id + ':run', true); process.open = true; update({ root, recordId: run.id }); };
508
614
  peek.addEventListener('click', expand);
509
615
  peekMore.addEventListener('click', expand);
@@ -514,9 +620,10 @@
514
620
  events.dataset.expanded = String(process.open);
515
621
  card.attention.hidden = process.open || !card.noticeCount;
516
622
  if (card.projection) {
517
- renderSetup(card, card.projection.setup, card.run.status === 'running');
623
+ const active = card.run.status === 'running' && !card.notStarted;
624
+ renderSetup(card, card.projection.setup, active);
518
625
  if (!process.open) card.setup.hidden = true;
519
- renderPeek(card, card.projection, card.run, card.run.status === 'running');
626
+ renderPeek(card, card.projection, card.run, active);
520
627
  }
521
628
  });
522
629
  render(card, run);
@@ -524,13 +631,34 @@
524
631
  function render(card, run, incoming) {
525
632
  card.run = run;
526
633
  const result = Model.project(run, incoming, { isGuidance });
634
+ const notStarted = getNotStartedRun?.(run);
635
+ const outcome = getFinishedRun?.(run);
636
+ card.root.dataset.outcomeTone = outcome?.tone || '';
637
+ card.root.dataset.outcome = outcome?.kind || '';
638
+ card.notStarted = Boolean(notStarted);
639
+ if (notStarted) {
640
+ // The single launch card replaces preflight workflow/Notice projections.
641
+ // Stored events remain available to history, audit and recovery.
642
+ result.blocks = [];
643
+ result.notices = [];
644
+ result.setup = null;
645
+ }
646
+ // The outcome card owns the visible cause. Keep workflow evidence in the
647
+ // existing process disclosure, without another attention banner.
648
+ if (outcome) result.notices = [];
649
+ const guidanceEvents = (run.events || []).concat(incoming && !(run.events || []).includes(incoming) ? [incoming] : []);
650
+ RunGuidanceView?.reconcileGuidance(card.guidance,
651
+ guidanceEvents.concat(getPendingGuidance(run)),
652
+ tx, { active: run.status === 'running' });
653
+ result.blocks = result.blocks.filter(block => block.kind !== 'guidance');
527
654
  card.projection = result;
528
655
  subagentView?.update(card);
529
- const active = run.status === 'running';
656
+ const active = run.status === 'running' && !notStarted;
530
657
  const abnormal = ['failed', 'interrupted', 'needs_review_after_navigation', 'abandoned_after_navigation'].includes(run.status);
531
- if (choices.has(card.id + ':run')) card.process.open = choices.get(card.id + ':run');
658
+ if (notStarted) card.process.open = false;
659
+ else if (choices.has(card.id + ':run')) card.process.open = choices.get(card.id + ':run');
532
660
  // A live run starts collapsed onto its peek window; problems open it.
533
- else card.process.open = abnormal;
661
+ else card.process.open = abnormal && !outcome;
534
662
  card.events.dataset.expanded = String(card.process.open);
535
663
  card.summary.setAttribute('aria-expanded', String(card.process.open));
536
664
  reconcile(card.events, result.blocks, card.rows, block => buildBlock(block, card),
@@ -541,6 +669,7 @@
541
669
  else if (!choices.has(card.id + ':setup')) card.setup.open = false;
542
670
  if (!card.process.open) card.setup.hidden = true;
543
671
  renderPeek(card, result, run, active);
672
+ renderCompaction(card, run, result);
544
673
  card.noticeCount = result.notices.length;
545
674
  const latestNotice = result.notices.at(-1);
546
675
  card.attention.hidden = card.process.open || !latestNotice;
@@ -555,12 +684,13 @@
555
684
  || block.meta?.state === 'running');
556
685
  // The presence header already narrates the live state.
557
686
  card.busy.hidden = !active || hasActive || Boolean(presence);
558
- text(card.busy, result.stage === 'local_completed'
687
+ text(card.busy, result.compacting ? tx('Compacting context…', '正在压缩上下文…') : result.stage === 'local_completed'
559
688
  ? (run.mode === 'ask' ? tx('Preparing the answer…', '正在整理回答…') : tx('Preparing writeback and verification…', '正在准备写回与验证…'))
560
689
  : result.stage === 'preparing' ? tx('Preparing project context…', '正在准备项目上下文…') : tx('Working…', '正在处理…'));
561
690
  }
562
691
  // Live header: long phases are named; otherwise a rarely-changing word. Tool steps stay in the round list.
563
692
  function runningLabel(result, run) {
693
+ if (result.compacting) return { kind: 'stage', text: tx('Compacting context', '正在压缩上下文') };
564
694
  if (result.stage === 'sync') return { kind: 'stage', text: tx('Syncing changes to Overleaf', '正在同步改动到 Overleaf') };
565
695
  // Before the model's first response the header names the real start-up step, not a playful word.
566
696
  if (result.setup && !result.setup.done) return { kind: 'stage', text: setupLiveLabel(result.setup) };
@@ -574,6 +704,11 @@
574
704
  function paintPresence(card, run, result, active) {
575
705
  const status = card.root.querySelector('[data-run-status]');
576
706
  if (!presence || !status) return;
707
+ const notStarted = getNotStartedRun?.(run);
708
+ if (notStarted) {
709
+ presence.paintSettled(status, { kind: notStarted.cancelled ? 'not-started-cancelled' : 'not-started', text: notStarted.status });
710
+ return;
711
+ }
577
712
  // History cards arrive with plain status text; keep it for failure and fallback labels.
578
713
  if (!status.dataset.presence && !status.dataset.presenceText) status.dataset.presenceText = status.textContent;
579
714
  const started = Date.parse(run.startedAt || '');
@@ -587,6 +722,12 @@
587
722
  elapsedMs: Number.isFinite(started) && Number.isFinite(finished) ? Math.max(0, finished - started) : null,
588
723
  files: result.fileCount, filesClipped: result.clipped === true, added: 0, removed: 0, steps: 0, compiled: false,
589
724
  projectId: getProjectId?.() || '', animate: card.animateNext === true };
725
+ const outcome = getFinishedRun?.(run);
726
+ if (outcome?.status) {
727
+ facts.kind = outcome.kind;
728
+ facts.text = outcome.status;
729
+ }
730
+ if (outcome?.kind === 'compile-errors') facts.compileChip = outcome.compileChip;
590
731
  card.animateNext = false;
591
732
  for (const block of result.blocks) {
592
733
  if (['message', 'guidance', 'thought'].includes(block.kind)) continue;
@@ -597,6 +738,7 @@
597
738
  }
598
739
  if (block.kind === 'compile' && block.meta?.state === 'completed') facts.compiled = true;
599
740
  }
741
+ if (facts.compileChip) facts.compiled = false;
600
742
  presence.paintSettled(status, facts);
601
743
  }
602
744
  function update(view, incoming) {