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.
- package/README.md +155 -392
- package/README.zh-CN.md +154 -393
- 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 +283 -55
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +129 -16
- package/extension/src/backgroundUpdateCoordinator.js +242 -29
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +171 -8
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +72 -14
- package/extension/src/content/composerAttachments.js +104 -47
- package/extension/src/content/composerPanel.js +110 -12
- package/extension/src/content/contentRuntime.js +1555 -926
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsController.js +122 -96
- package/extension/src/content/diagnosticsExport.js +19 -5
- package/extension/src/content/diagnosticsPanel.js +48 -0
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +11077 -3412
- package/extension/src/content/generated/content.bundle.meta.json +14 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +10 -0
- package/extension/src/content/localSkillsPanel.js +104 -14
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +47 -11
- package/extension/src/content/modelPicker.js +8 -4
- package/extension/src/content/nativeCompatibilityController.js +74 -30
- package/extension/src/content/otWarmMirror.js +5 -6
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +17 -4
- package/extension/src/content/panelMaintenance.js +46 -14
- package/extension/src/content/panelRenderer.js +57 -27
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +67 -6
- package/extension/src/content/providerSettingsCoordinator.js +46 -8
- package/extension/src/content/providerSettingsDialog.js +73 -14
- package/extension/src/content/recentProjects.js +15 -8
- package/extension/src/content/runActivitySummary.js +160 -18
- package/extension/src/content/runCompletionMeta.js +532 -0
- package/extension/src/content/runController.js +39 -15
- package/extension/src/content/runFailureNotice.js +29 -13
- package/extension/src/content/runGuidanceController.js +72 -27
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +18 -8
- package/extension/src/content/runQueueScheduler.js +157 -47
- package/extension/src/content/runRecoveryActions.js +147 -0
- package/extension/src/content/runResultActions.js +28 -143
- package/extension/src/content/runReviewController.js +633 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +367 -175
- package/extension/src/content/scopedPersistencePanelState.js +134 -20
- package/extension/src/content/selectionContextView.js +1 -1
- package/extension/src/content/sensitivePreflight.js +153 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +50 -4
- package/extension/src/content/sessionMenuView.js +54 -18
- package/extension/src/content/sessionPersistence.js +25 -5
- package/extension/src/content/settingsPanel.js +22 -5
- package/extension/src/content/settingsWorkbench.js +132 -89
- package/extension/src/content/sharedSessionViewSync.js +49 -11
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +118 -12
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +53 -4
- package/extension/src/content/writebackOrchestrator.js +116 -94
- package/extension/src/content/writingStyleSettings.js +35 -11
- package/extension/src/page/binaryAssetUploader.js +136 -14
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafCapabilities.js +4 -1
- 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/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +114 -26
- 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 +103 -155
- package/extension/src/pageBridge.js +126 -69
- package/extension/src/popup.js +62 -10
- package/extension/src/shared/agentTranscript.js +373 -17
- package/extension/src/shared/auditRecords.js +4 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +129 -66
- package/extension/src/shared/composerAttachmentDraft.js +156 -0
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +349 -4
- package/extension/src/shared/globalPreferences.js +30 -13
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +590 -24
- package/extension/src/shared/lineReferences.js +38 -31
- 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/panelIcons.js +39 -0
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +5 -3
- 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/runInputQueue.js +48 -5
- package/extension/src/shared/selectionContext.js +12 -3
- package/extension/src/shared/sessionState.js +132 -28
- package/extension/src/shared/sessionTitle.js +14 -1
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +118 -27
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +166 -52
- package/extension/src/shared/storageDb.js +90 -26
- package/extension/src/shared/storageMigration.js +78 -112
- package/extension/src/shared/storageRunActions.js +267 -30
- package/extension/src/shared/subagentActivityModel.js +1 -1
- package/extension/src/shared/undoOperations.js +253 -32
- package/extension/src/shared/updateConsent.js +6 -4
- package/extension/src/shared/updateRuntimeIdentity.js +17 -3
- package/extension/src/shared/writebackIntent.js +16 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +58 -40
- package/extension/styles/panel.css +1123 -493
- 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 +269 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeRequest.js +60 -6
- package/native-host/src/chatBridgeResponse.js +20 -1
- package/native-host/src/chatCompletionsBridge.js +48 -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 +114 -127
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexSessionRunner.js +234 -198
- 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/providerContextError.js +20 -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 +92 -57
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/updateManager.js +409 -46
- package/native-host/src/writingStyleRuntime.js +57 -29
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +5 -8
- package/scripts/install-managed.mjs +1 -0
|
@@ -100,6 +100,21 @@
|
|
|
100
100
|
return pending;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function selectionViolationNextStep() {
|
|
104
|
+
return tx('Select the intended region again and request only changes within it. Valid written parts can be undone.',
|
|
105
|
+
'请重新选择目标范围,仅请求修改该范围。已写入的合法部分可撤销。');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildWithheldEntry(change) {
|
|
109
|
+
const reason = writebackController.formatUnsupportedLocalChangeReason(change, getLocale());
|
|
110
|
+
const nextAction = change.reason === 'selection_scope_violation' ? selectionViolationNextStep()
|
|
111
|
+
: tx('The main agent must review the withheld file before syncing it.', '主代理需核对未同步文件后再写回。');
|
|
112
|
+
return { operation: { type: 'create', path: change.path }, result: { ok: false,
|
|
113
|
+
code: change.reason, reason, changedDocument: false, failure: { code: change.reason,
|
|
114
|
+
stage: 'preflight', severity: 'blocked', retryable: false, terminalState: 'blocked', changedDocument: false,
|
|
115
|
+
userMessage: reason, nextAction } } };
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
async function applySyncChangesToOverleaf(syncChanges = [], project = {}, options = {}) {
|
|
104
119
|
const activeRun = getCurrentRunView?.();
|
|
105
120
|
const saveTarget = { runProjectId: activeRun?.runProjectId || '', recordId: activeRun?.recordId || '', sessionId: activeRun?.sessionId || '' };
|
|
@@ -110,20 +125,23 @@
|
|
|
110
125
|
const assistantMessage = cleanFinalAnswer(options.assistantMessage || getAssistantAnswerForCurrentRun());
|
|
111
126
|
const unsupportedChanges = Array.isArray(options.unsupportedChanges) ? options.unsupportedChanges : [];
|
|
112
127
|
appendUnsupportedLocalChanges(unsupportedChanges);
|
|
113
|
-
const withheldOnly = unsupportedChanges.filter(change => ['subagent_unfinished_output', 'subagent_unauthorized_edit'].includes(change.reason));
|
|
128
|
+
const withheldOnly = unsupportedChanges.filter(change => ['subagent_unfinished_output', 'subagent_unauthorized_edit', 'selection_scope_violation'].includes(change.reason));
|
|
114
129
|
if (runMode !== 'ask' && !syncChanges.length && withheldOnly.length) {
|
|
115
|
-
const skipped = withheldOnly.map(
|
|
116
|
-
|
|
117
|
-
reason: writebackController.formatUnsupportedLocalChangeReason(change, getLocale())
|
|
118
|
-
} }));
|
|
130
|
+
const skipped = withheldOnly.map(buildWithheldEntry);
|
|
131
|
+
const hasSelectionViolation = withheldOnly.some(change => change.reason === 'selection_scope_violation');
|
|
119
132
|
const applied = { ok: false, applied: [], skipped };
|
|
120
|
-
appendCompletionReport({ conclusion: assistantMessage || tx('Some generated
|
|
133
|
+
appendCompletionReport({ conclusionKey: options.assistantMessageKey || (!assistantMessage ? 'withheld_changes' : undefined), conclusion: assistantMessage || tx('Some generated changes were blocked.', '部分生成的改动已被阻止。'),
|
|
121
134
|
status: 'failed', operations: [], applyResults: [applied], mode: runMode,
|
|
122
|
-
nextStep:
|
|
123
|
-
|
|
135
|
+
nextStep: hasSelectionViolation
|
|
136
|
+
? selectionViolationNextStep()
|
|
137
|
+
: tx('The main agent must review and adopt the withheld files before syncing them.', '主代理需核对并接管未同步文件后再写回。') });
|
|
138
|
+
return { summaryLine: hasSelectionViolation
|
|
139
|
+
? tx('Changes outside the selection were blocked.', '选区外改动已被阻止。')
|
|
140
|
+
: tx('Generated files are awaiting review.', '生成文件待核对。'), hasSkippedOperations: true,
|
|
124
141
|
applied, settlement: buildSettlement({ operations: [], applyResult: applied }),
|
|
125
142
|
audit: buildAuditSummaryFromApply({ operations: [], applyResults: [applied], resultStatus: 'completed_with_skips' }) };
|
|
126
143
|
}
|
|
144
|
+
|
|
127
145
|
// Guard on the immutable submitted mode, not the mutable panel state.
|
|
128
146
|
if (options.mode === 'ask' && ((syncChanges || []).length || unsupportedChanges.length)) {
|
|
129
147
|
appendRunEvent({
|
|
@@ -133,7 +151,7 @@
|
|
|
133
151
|
),
|
|
134
152
|
status: 'warning'
|
|
135
153
|
});
|
|
136
|
-
appendCompletionReport({
|
|
154
|
+
appendCompletionReport({ conclusionKey: options.assistantMessageKey || (!assistantMessage ? 'ask_ignored_changes' : undefined),
|
|
137
155
|
conclusion: assistantMessage || tx(
|
|
138
156
|
'Codex finished in Ask mode. Any local file changes were ignored and were not synced to Overleaf.',
|
|
139
157
|
'Codex 已在 Ask 模式完成;本地文件改动已忽略,未同步到 Overleaf。'
|
|
@@ -166,7 +184,16 @@
|
|
|
166
184
|
}
|
|
167
185
|
let operations = buildSyncApplyOperations(syncChanges, project);
|
|
168
186
|
let visibleSyncChanges = syncChanges || [];
|
|
169
|
-
|
|
187
|
+
// A previous Native build can still report inferred metadata moves.
|
|
188
|
+
// Forward sync never promotes them to a new file or a new capability.
|
|
189
|
+
let additionalSkippedEntries = operations.filter(operation => operation.type === 'move').map(operation => ({
|
|
190
|
+
operation,
|
|
191
|
+
result: { ok: false, code: 'file_tree_controls_unavailable', changedDocument: false,
|
|
192
|
+
reason: tx('Automatic file moves are unavailable. Handle the path change in Overleaf before retrying.',
|
|
193
|
+
'暂不支持自动移动文件,请先在 Overleaf 中处理路径变化后再重试。') }
|
|
194
|
+
}));
|
|
195
|
+
operations = operations.filter(operation => operation.type !== 'move');
|
|
196
|
+
if (additionalSkippedEntries.length) visibleSyncChanges = filterSyncChangesByOperations(syncChanges, operations);
|
|
170
197
|
let skippedFilesForAudit = additionalSkippedEntries;
|
|
171
198
|
let appliedFilesForAudit = [];
|
|
172
199
|
const pathSafety = partitionUnsafeProjectPathOperations(operations);
|
|
@@ -191,13 +218,27 @@
|
|
|
191
218
|
visibleSyncChanges = filterSyncChangesByOperations(syncChanges, operations);
|
|
192
219
|
}
|
|
193
220
|
if (!operations.length) {
|
|
221
|
+
const blockedSettlement = buildSettlement({
|
|
222
|
+
operations: buildSyncApplyOperations(syncChanges, project),
|
|
223
|
+
applyResult: { ok: additionalSkippedEntries.length === 0, applied: [], skipped: additionalSkippedEntries }
|
|
224
|
+
});
|
|
225
|
+
const blockedFailure = blockedSettlement
|
|
226
|
+
? writebackSettlement.projectRunSettlement({ settlement: blockedSettlement.facts }).primaryFailure : undefined;
|
|
194
227
|
appendRunEvent({
|
|
195
228
|
title: tx('Codex did not produce file changes that need to sync back to Overleaf.', 'Codex 没有产生需要同步回 Overleaf 的文件改动。'),
|
|
196
229
|
status: 'completed'
|
|
197
230
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
231
|
+
const blockedConclusion = assistantMessage
|
|
232
|
+
? `${assistantMessage}\n\n${tx('Sync note: no changes were written to Overleaf; every change was blocked before writing.', '同步提示:没有向 Overleaf 写入任何改动;所有改动都在写入前被拦下。')}`
|
|
233
|
+
: tx('No changes were written to Overleaf; every change was blocked before writing.', '没有向 Overleaf 写入任何改动;所有改动都在写入前被拦下。');
|
|
234
|
+
appendCompletionReport({ conclusionKey: additionalSkippedEntries.length ? undefined
|
|
235
|
+
: options.assistantMessageKey || (!assistantMessage ? 'no_sync_changes' : undefined),
|
|
236
|
+
conclusion: additionalSkippedEntries.length ? blockedConclusion
|
|
237
|
+
: assistantMessage || tx('Codex finished locally. There are no changes to sync back to Overleaf.', 'Codex 已完成本地处理,没有需要同步回 Overleaf 的改动。'),
|
|
238
|
+
conclusionSource: additionalSkippedEntries.length ? { origin: 'product', key: 'writeback',
|
|
239
|
+
outcome: 'blocked_sync', prefixLength: assistantMessage.length } : undefined,
|
|
240
|
+
status: additionalSkippedEntries.length ? 'failed' : runMode === 'ask' ? tr('modeAsk') : 'completed',
|
|
241
|
+
failure: blockedFailure,
|
|
201
242
|
operations: [],
|
|
202
243
|
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
203
244
|
unchangedReason: additionalSkippedEntries.find(item => item?.result?.reason)?.result.reason
|
|
@@ -208,14 +249,7 @@
|
|
|
208
249
|
return {
|
|
209
250
|
summaryLine: assistantMessage || tx('No changes to sync', '没有需要同步的改动'),
|
|
210
251
|
hasSkippedOperations: additionalSkippedEntries.length > 0,
|
|
211
|
-
settlement:
|
|
212
|
-
operations: buildSyncApplyOperations(syncChanges, project),
|
|
213
|
-
applyResult: {
|
|
214
|
-
ok: additionalSkippedEntries.length === 0,
|
|
215
|
-
applied: [],
|
|
216
|
-
skipped: additionalSkippedEntries
|
|
217
|
-
}
|
|
218
|
-
}),
|
|
252
|
+
settlement: blockedSettlement,
|
|
219
253
|
audit: buildAuditSummaryFromApply({
|
|
220
254
|
operations: buildSyncApplyOperations(syncChanges, project),
|
|
221
255
|
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
@@ -258,13 +292,20 @@
|
|
|
258
292
|
additionalSkippedEntries.push(...binaryDecision.skipped);
|
|
259
293
|
|
|
260
294
|
if (!operations.length) {
|
|
295
|
+
const approvalSettlement = buildSettlement({
|
|
296
|
+
operations: buildSyncApplyOperations(syncChanges, project),
|
|
297
|
+
applyResult: { ok: additionalSkippedEntries.length === 0, applied: [], skipped: additionalSkippedEntries }
|
|
298
|
+
});
|
|
299
|
+
const approvalFailure = approvalSettlement
|
|
300
|
+
? writebackSettlement.projectRunSettlement({ settlement: approvalSettlement.facts }).primaryFailure : undefined;
|
|
261
301
|
appendRunEvent({
|
|
262
302
|
title: tx('No approved file changes remain to sync back to Overleaf.', '没有剩余已确认的文件改动需要同步回 Overleaf。'),
|
|
263
303
|
status: 'completed'
|
|
264
304
|
});
|
|
265
|
-
appendCompletionReport({
|
|
305
|
+
appendCompletionReport({ conclusionKey: options.assistantMessageKey || (!assistantMessage ? 'no_approved_changes' : undefined),
|
|
266
306
|
conclusion: assistantMessage || tx('No approved changes were written back to Overleaf.', '没有已确认的改动写回 Overleaf。'),
|
|
267
|
-
status: runMode === 'ask' ? tr('modeAsk') : 'completed',
|
|
307
|
+
status: additionalSkippedEntries.length ? 'failed' : runMode === 'ask' ? tr('modeAsk') : 'completed',
|
|
308
|
+
failure: approvalFailure,
|
|
268
309
|
operations: [],
|
|
269
310
|
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
270
311
|
unchangedReason: additionalSkippedEntries.find(item => item?.result?.reason)?.result.reason
|
|
@@ -275,14 +316,7 @@
|
|
|
275
316
|
return {
|
|
276
317
|
summaryLine: tx('No changes applied', '没有应用改动'),
|
|
277
318
|
hasSkippedOperations: additionalSkippedEntries.length > 0,
|
|
278
|
-
settlement:
|
|
279
|
-
operations: buildSyncApplyOperations(syncChanges, project),
|
|
280
|
-
applyResult: {
|
|
281
|
-
ok: additionalSkippedEntries.length === 0,
|
|
282
|
-
applied: [],
|
|
283
|
-
skipped: additionalSkippedEntries
|
|
284
|
-
}
|
|
285
|
-
}),
|
|
319
|
+
settlement: approvalSettlement,
|
|
286
320
|
audit: buildAuditSummaryFromApply({
|
|
287
321
|
operations: buildSyncApplyOperations(syncChanges, project),
|
|
288
322
|
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
@@ -299,8 +333,8 @@
|
|
|
299
333
|
if (!reviewing.ok) {
|
|
300
334
|
const blocked = buildReviewingBlockedApplyResult(operations, reviewing);
|
|
301
335
|
appendApplyResult(blocked);
|
|
302
|
-
appendCompletionReport({
|
|
303
|
-
conclusion: tx(
|
|
336
|
+
appendCompletionReport({ conclusionKey: 'reviewing_unavailable',
|
|
337
|
+
conclusion: reviewing.code === 'reviewing_unavailable' ? tr('failureReason_reviewing_unavailable_user') : tx(
|
|
304
338
|
'No files were written: Track is enabled, but Codex could not verify Overleaf Reviewing/Track Changes.',
|
|
305
339
|
'这轮没有写入:已开启“留痕”要求,但 Codex 没能确认 Overleaf 正在用 Reviewing/Track Changes。'
|
|
306
340
|
),
|
|
@@ -308,7 +342,7 @@
|
|
|
308
342
|
operations,
|
|
309
343
|
applyResults: [blocked],
|
|
310
344
|
mode: runMode,
|
|
311
|
-
nextStep: tx(
|
|
345
|
+
nextStep: reviewing.code === 'reviewing_unavailable' ? tr('failureReason_reviewing_unavailable_next') : tx(
|
|
312
346
|
'Switch Overleaf to Reviewing/Track Changes manually and rerun, or turn off Track before writing.',
|
|
313
347
|
'请在 Overleaf 手动切到 Reviewing/Track Changes 后重新运行,或关闭“留痕”再写入。'
|
|
314
348
|
)
|
|
@@ -336,7 +370,7 @@
|
|
|
336
370
|
status: 'running',
|
|
337
371
|
activity: { v: 1, kind: 'sync', id: writebackActivityId, state: 'running', paths: operations.map(operation => operation.path).filter(Boolean) }
|
|
338
372
|
});
|
|
339
|
-
await stageWritebackRecovery?.(operations, saveTarget, runRequireReviewing);
|
|
373
|
+
await stageWritebackRecovery?.(operations, saveTarget, runRequireReviewing, project);
|
|
340
374
|
const applied = operations.length
|
|
341
375
|
? mergeApplyResultSkipped(await assetTransferBroker.applyOperations({
|
|
342
376
|
operations,
|
|
@@ -351,12 +385,8 @@
|
|
|
351
385
|
}), additionalSkippedEntries)
|
|
352
386
|
: mergeApplyResultSkipped({ ok: true, applied: [], skipped: [] }, additionalSkippedEntries);
|
|
353
387
|
const hasConfirmedApplyResult = hasApplyResultEntries(applied);
|
|
354
|
-
for (const change of unsupportedChanges.filter(item => ['subagent_unfinished_output', 'subagent_unauthorized_edit'].includes(item.reason))) {
|
|
355
|
-
|
|
356
|
-
applied.skipped.push({ operation: { type: 'create', path: change.path }, result: { ok: false,
|
|
357
|
-
code: change.reason, reason, changedDocument: false, failure: { code: change.reason,
|
|
358
|
-
stage: 'preflight', severity: 'blocked', retryable: false, terminalState: 'blocked', changedDocument: false,
|
|
359
|
-
userMessage: reason, nextAction: 'The main agent must review the withheld file before syncing it.' } } });
|
|
388
|
+
for (const change of unsupportedChanges.filter(item => ['subagent_unfinished_output', 'subagent_unauthorized_edit', 'selection_scope_violation'].includes(item.reason))) {
|
|
389
|
+
applied.skipped.push(buildWithheldEntry(change));
|
|
360
390
|
applied.ok = false;
|
|
361
391
|
}
|
|
362
392
|
// Record the apply result + undo checkpoint IMMEDIATELY, before any
|
|
@@ -367,7 +397,7 @@
|
|
|
367
397
|
// landed in Overleaf. The manual-confirm path already records undo right
|
|
368
398
|
// after its write — match that ordering here (v1.6.2).
|
|
369
399
|
appendApplyResult(applied);
|
|
370
|
-
recordUndoFromApply(project, applied);
|
|
400
|
+
await recordUndoFromApply(project, applied);
|
|
371
401
|
const skippedEntries = getSkippedEntries(applied);
|
|
372
402
|
if (skippedEntries.length) {
|
|
373
403
|
appendPartialWritebackWarning(applied);
|
|
@@ -392,7 +422,7 @@
|
|
|
392
422
|
? await verifyPostWriteSaveState(applied, project, saveTarget)
|
|
393
423
|
: { verification: { ok: false, state: 'not_checked', reason: 'No applied writeback operations were returned.' } };
|
|
394
424
|
const saveVerification = confirmation.verification;
|
|
395
|
-
await settleWritebackRecovery?.(applied, project, saveVerification, saveTarget);
|
|
425
|
+
await settleWritebackRecovery?.(applied, project, saveVerification, saveTarget, runRequireReviewing);
|
|
396
426
|
if (appliedPaths.length) {
|
|
397
427
|
appendPostWriteSaveVerificationWarning(saveVerification, saveTarget);
|
|
398
428
|
appendRunEvent({ kind: 'technical', title: 'Post-write save confirmation', status: 'info', detail: saveVerification });
|
|
@@ -430,7 +460,7 @@
|
|
|
430
460
|
title: tx(`Post-write compile failed: ${error.message}`, `写后编译出错:${error.message}`),
|
|
431
461
|
status: 'failed'
|
|
432
462
|
});
|
|
433
|
-
return buildPostWriteCompileSummary({ error });
|
|
463
|
+
return compileAdapter.buildPostWriteCompileSummary({ error });
|
|
434
464
|
})
|
|
435
465
|
: null;
|
|
436
466
|
const hasSkippedApplyResult = skippedEntries.length > 0;
|
|
@@ -462,6 +492,10 @@
|
|
|
462
492
|
: tx('Review the synced file in Overleaf.', '请在 Overleaf 中查看同步后的文件。');
|
|
463
493
|
appendCompletionReport({
|
|
464
494
|
conclusion: appendCompileSummaryToConclusion(writebackConclusion, compileSummary),
|
|
495
|
+
conclusionSource: { origin: 'product', key: 'writeback',
|
|
496
|
+
outcome: !hasConfirmedApplyResult ? 'unconfirmed_sync' : hasSkippedApplyResult ? 'partial_sync' : 'synced',
|
|
497
|
+
prefixLength: assistantMessage.length, prefixKey: options.assistantMessageKey, hasCompile: Boolean(compileSummary), compileStatus: compileSummary?.status || '',
|
|
498
|
+
compileReason: compileAdapter.localizeCompileReason(compileSummary?.reason || '', getLocale()), errors: compileSummary?.errors?.slice(0, 3) || [], errorCount: compileSummary?.errors?.length || 0, warningCount: compileSummary?.warnings?.length || 0 },
|
|
465
499
|
status: writebackIncomplete ? 'failed' : 'completed',
|
|
466
500
|
operations,
|
|
467
501
|
applyResults: [applied],
|
|
@@ -568,37 +602,29 @@
|
|
|
568
602
|
return 'failed';
|
|
569
603
|
}
|
|
570
604
|
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
//
|
|
575
|
-
// written files without re-downloading the project. Anything unusual —
|
|
576
|
-
// tree ops, binary writes, dirty mirror, an older native host without the
|
|
577
|
-
// method — falls back to the full zip resync below.
|
|
578
|
-
const MIRROR_CONFIRMABLE_OPERATION_TYPES = new Set(['edit']);
|
|
605
|
+
// Text-only confirmation requires the page's verified post-image digest.
|
|
606
|
+
// Local files can change after writeback; Native must match those digests
|
|
607
|
+
// before advancing its baseline. Missing evidence and non-text/tree writes
|
|
608
|
+
// retain the authoritative ZIP fallback below.
|
|
579
609
|
|
|
580
610
|
async function tryConfirmMirrorWriteback(applied, runProjectId) {
|
|
581
|
-
const entries = Array.isArray(applied?.applied) ? applied.applied : [];
|
|
582
|
-
if (!entries.length) {
|
|
583
|
-
return false;
|
|
584
|
-
}
|
|
585
|
-
const paths = [];
|
|
586
|
-
for (const entry of entries) {
|
|
587
|
-
const operation = entry?.operation || {};
|
|
588
|
-
if (!MIRROR_CONFIRMABLE_OPERATION_TYPES.has(operation.type) || !operation.path) {
|
|
589
|
-
return false;
|
|
590
|
-
}
|
|
591
|
-
paths.push(operation.path);
|
|
592
|
-
}
|
|
593
611
|
try {
|
|
612
|
+
if (!runProjectId || getCurrentProjectId() !== runProjectId) return false;
|
|
613
|
+
const proof = await writebackController.buildMirrorWritebackConfirmation?.(applied);
|
|
614
|
+
if (!proof || getCurrentProjectId() !== runProjectId) return false;
|
|
594
615
|
const response = await sendBackgroundNative({
|
|
595
616
|
method: 'mirror.confirmWriteback',
|
|
596
617
|
params: {
|
|
597
618
|
projectId: runProjectId,
|
|
598
|
-
|
|
619
|
+
...proof
|
|
599
620
|
}
|
|
600
621
|
});
|
|
601
|
-
|
|
622
|
+
const confirmations = Array.isArray(response?.result?.confirmed) ? response.result.confirmed : [];
|
|
623
|
+
const confirmed = new Map(confirmations.map(file => [file?.path, file?.hash]));
|
|
624
|
+
return getCurrentProjectId() === runProjectId && response?.ok === true && response?.result?.ok === true
|
|
625
|
+
&& confirmations.length === proof.expectedFiles.length
|
|
626
|
+
&& confirmed.size === proof.expectedFiles.length
|
|
627
|
+
&& proof.expectedFiles.every(file => confirmed.get(file.path) === file.sha256);
|
|
602
628
|
} catch (error) {
|
|
603
629
|
return false;
|
|
604
630
|
}
|
|
@@ -732,52 +758,45 @@
|
|
|
732
758
|
let logResult = null;
|
|
733
759
|
try {
|
|
734
760
|
logResult = await callPageBridge('getCompileLog', {
|
|
761
|
+
captureId: result.compile?.captureId,
|
|
735
762
|
triggerIfStale: false,
|
|
736
763
|
maxAgeMs: 30000,
|
|
737
764
|
waitForSaveMs: 0,
|
|
738
765
|
runProjectId: options.runProjectId || getCurrentRunView()?.runProjectId || getCurrentProjectId()
|
|
739
766
|
});
|
|
740
|
-
} catch (
|
|
741
|
-
logResult =
|
|
767
|
+
} catch (error) {
|
|
768
|
+
logResult = { ok: false, reason: error.message || 'compile_log_fetch_failed' };
|
|
742
769
|
}
|
|
743
|
-
const compile = result
|
|
770
|
+
const compile = compileAdapter.buildPostWriteCompileSummary({ result, logResult });
|
|
771
|
+
compile.errors = compile.errors.map(value => CompileAdapter.localizeCompileReason(value, getLocale()));
|
|
744
772
|
if (compile?.status === 'success') {
|
|
745
773
|
appendRunEvent({ title: tx('Compile succeeded.', '编译成功。'), status: 'completed',
|
|
746
774
|
activity: activity('completed', [...(logResult?.errors || []), ...(logResult?.warnings || [])].slice(0, 5).map(CompileAdapter.formatCompileDiagnosticForSummary).join('\n')) });
|
|
747
775
|
} else if (compile?.status === 'triggered') {
|
|
748
|
-
appendRunEvent({ title: tx('Overleaf compile was triggered. The page will continue showing progress.', '已触发 Overleaf 编译;页面会继续显示编译进度。'), status: 'completed', activity: activity('triggered') });
|
|
776
|
+
appendRunEvent({ title: tx('Overleaf compile was triggered. The page will continue showing progress.', '已触发 Overleaf 编译;页面会继续显示编译进度。'), status: 'completed', activity: activity('triggered', compile.reason) });
|
|
749
777
|
} else {
|
|
750
|
-
appendRunEvent({ title:
|
|
778
|
+
appendRunEvent({ title: compile.status === 'unconfirmed' ? tr('runCompileUnconfirmed')
|
|
779
|
+
: tx(`Compile finished with status: ${compile.status}`, `编译完成,状态:${compile.status}`),
|
|
780
|
+
status: 'completed', activity: activity(compile.status === 'failed' ? 'failed' : 'unknown', compile.errors.join('\n') || compile.reason) });
|
|
751
781
|
}
|
|
752
|
-
return
|
|
782
|
+
return compile;
|
|
753
783
|
} else {
|
|
754
|
-
const reason = result?.reason || tx('unknown reason', '未知原因');
|
|
784
|
+
const reason = CompileAdapter.localizeCompileReason(result?.reason, getLocale()) || tx('unknown reason', '未知原因');
|
|
755
785
|
appendRunEvent({ title: tx(`Post-write compile did not succeed: ${reason}`, `写后编译未成功:${reason}`), status: 'failed', activity: activity('failed', reason) });
|
|
756
|
-
return buildPostWriteCompileSummary({ result });
|
|
786
|
+
return compileAdapter.buildPostWriteCompileSummary({ result });
|
|
757
787
|
}
|
|
758
788
|
} catch (error) {
|
|
759
|
-
|
|
760
|
-
|
|
789
|
+
const reason = CompileAdapter.localizeCompileReason(error.message, getLocale());
|
|
790
|
+
appendRunEvent({ title: tx(`Post-write compile failed: ${reason}`, `写后编译出错:${reason}`), status: 'failed', activity: activity('failed', reason) });
|
|
791
|
+
return compileAdapter.buildPostWriteCompileSummary({ error });
|
|
761
792
|
}
|
|
762
793
|
}
|
|
763
794
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
// First structured per-operation failure among the skipped writeback
|
|
769
|
-
// entries — the run-level failure the completion report promotes.
|
|
795
|
+
// Use the same normalized failure selection as settlement and its UI, while
|
|
796
|
+
// preserving the selected producer's full evidence on the historical report.
|
|
770
797
|
function promoteWritebackSkippedFailure(applied) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
if (failure?.code) {
|
|
774
|
-
if (!failure.file && entry?.path) {
|
|
775
|
-
return { ...failure, file: entry.path };
|
|
776
|
-
}
|
|
777
|
-
return failure;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
return null;
|
|
798
|
+
const failures = writebackSettlement.collectFailuresFromResult(applied);
|
|
799
|
+
return writebackSettlement.selectPrimaryFailure(failures);
|
|
781
800
|
}
|
|
782
801
|
|
|
783
802
|
function appendCompileSummaryToConclusion(conclusion, compileSummary) {
|
|
@@ -806,10 +825,13 @@
|
|
|
806
825
|
`已触发写后编译;最新日志显示 ${warnings.length} 个警告。`
|
|
807
826
|
)
|
|
808
827
|
: tx('Post-write compile was triggered; Overleaf may still be updating the result.', '已触发写后编译;Overleaf 可能仍在更新结果。');
|
|
828
|
+
} else if (compileSummary.status === 'unconfirmed') {
|
|
829
|
+
summary = tr('runCompileUnconfirmed');
|
|
809
830
|
} else if (compileSummary.status === 'failed') {
|
|
831
|
+
const reason = compileAdapter.localizeCompileReason(compileSummary.reason, getLocale());
|
|
810
832
|
summary = tx(
|
|
811
|
-
`Post-write compile check failed: ${
|
|
812
|
-
`写后编译检查失败:${
|
|
833
|
+
`Post-write compile check failed: ${reason || 'unknown reason'}`,
|
|
834
|
+
`写后编译检查失败:${reason || '未知原因'}`
|
|
813
835
|
);
|
|
814
836
|
} else {
|
|
815
837
|
summary = tx(
|
|
@@ -16,7 +16,18 @@
|
|
|
16
16
|
let pickerOpen = false, query = '', filter = 'all';
|
|
17
17
|
let activeGeneration = null, cancelling = false;
|
|
18
18
|
let preview = null, previewBusy = false, previewFile = 'references/writing-style.md';
|
|
19
|
-
const
|
|
19
|
+
const ownMessagePairs = new Map();
|
|
20
|
+
const tx = (en, zh) => {
|
|
21
|
+
const pair = [en, zh];
|
|
22
|
+
ownMessagePairs.set(en, pair); ownMessagePairs.set(zh, pair);
|
|
23
|
+
return deps.tx(en, zh);
|
|
24
|
+
};
|
|
25
|
+
// Cached status text keeps its diagnostic value; only known copy is
|
|
26
|
+
// projected again when sync renders under a different language.
|
|
27
|
+
const displayMessage = value => {
|
|
28
|
+
const pair = ownMessagePairs.get(value);
|
|
29
|
+
return pair ? deps.tx(...pair) : value;
|
|
30
|
+
};
|
|
20
31
|
const node = (tag, className, text) => {
|
|
21
32
|
const element = document.createElement(tag);
|
|
22
33
|
if (className) element.className = className;
|
|
@@ -62,6 +73,12 @@
|
|
|
62
73
|
const value = deps.getScope();
|
|
63
74
|
return value?.accountScopeId && value?.projectId ? value : null;
|
|
64
75
|
}
|
|
76
|
+
function errorMessage(message, code) {
|
|
77
|
+
// codex.run currently wraps style-build error codes as codex_run_failed.
|
|
78
|
+
if (code === 'writing_style_busy' || message === 'Writing-style settings are being updated by another Native Host. Retry shortly.') return tx('Writing-style settings are being updated by another Native Host. Retry shortly.', '写作风格设置正在由另一个本地进程更新,请稍后重试。');
|
|
79
|
+
if (code === 'writing_style_changed' || message === 'The saved writing skill changed. Rebuild it in General settings.' || message === 'Writing-style asset integrity changed.') return tx(message || 'The saved writing skill changed. Rebuild it in General settings.', '写作风格资产不可用,请在常规设置中重新生成。');
|
|
80
|
+
return message;
|
|
81
|
+
}
|
|
65
82
|
function alignScope() {
|
|
66
83
|
const next = currentScope(), key = next ? next.accountScopeId + ':' + next.projectId : '';
|
|
67
84
|
if (scopeKey !== key) {
|
|
@@ -83,7 +100,7 @@
|
|
|
83
100
|
async function request(method, params = {}) {
|
|
84
101
|
const response = await deps.request({ method, params });
|
|
85
102
|
if (!response?.ok) {
|
|
86
|
-
const error = new Error(response?.error?.message || tx('The request could not be completed.', '请求未能完成。'));
|
|
103
|
+
const error = new Error(errorMessage(response?.error?.message, response?.error?.code) || tx('The request could not be completed.', '请求未能完成。'));
|
|
87
104
|
error.code = response?.error?.code || 'writing_style_failed'; throw error;
|
|
88
105
|
}
|
|
89
106
|
return response.result;
|
|
@@ -105,7 +122,7 @@
|
|
|
105
122
|
meta = value;
|
|
106
123
|
if (setupEnabled === null) desiredEnabled = value.enabled === true;
|
|
107
124
|
if (!dirty && !activeGeneration) restoreSources(value);
|
|
108
|
-
if (value.error && !message) { message = value.error; messageTone = 'error'; }
|
|
125
|
+
if (value.error && !message) { message = errorMessage(value.error, value.errorCode); messageTone = 'error'; }
|
|
109
126
|
clearTimeout(pollTimer); pollTimer = null;
|
|
110
127
|
if (value.building) pollTimer = setTimeout(() => { void ensureLoaded(true).catch(() => {}); }, 1800);
|
|
111
128
|
else if (!activeGeneration) cancelling = false;
|
|
@@ -198,7 +215,8 @@
|
|
|
198
215
|
activeGeneration = operation; cancelling = false; pickerOpen = false; preview = null;
|
|
199
216
|
message = ''; messageTone = ''; expanded = true; render();
|
|
200
217
|
const stillActive = () => {
|
|
201
|
-
|
|
218
|
+
const current = currentScope();
|
|
219
|
+
if (operation.cancelled || generation !== epoch || current?.accountScopeId !== target.accountScopeId || current?.projectId !== target.projectId) {
|
|
202
220
|
const error = new Error(tx('Style generation cancelled.', '已取消风格生成。'));
|
|
203
221
|
error.code = 'codex_cancelled'; throw error;
|
|
204
222
|
}
|
|
@@ -206,7 +224,7 @@
|
|
|
206
224
|
try {
|
|
207
225
|
const settings = await deps.getModelSettings();
|
|
208
226
|
stillActive();
|
|
209
|
-
const attachments = [], selected = [];
|
|
227
|
+
const attachments = [], selected = [], projectFiles = [];
|
|
210
228
|
for (const source of chosen) {
|
|
211
229
|
stillActive();
|
|
212
230
|
if (source.kind === 'pdf') {
|
|
@@ -218,10 +236,11 @@
|
|
|
218
236
|
const snapshot = await deps.getReferenceSnapshot(source.projectId, operation.readId);
|
|
219
237
|
operation.readId = ''; stillActive();
|
|
220
238
|
if (snapshot?.projectId !== source.projectId || snapshot?.capabilities?.referenceTextComplete !== true) {
|
|
221
|
-
throw new Error(tx('The reference project could not be read completely: ', '参考项目未能完整读取:'
|
|
239
|
+
throw new Error(tx('The reference project could not be read completely: ' + source.name, '参考项目未能完整读取:' + source.name));
|
|
222
240
|
}
|
|
223
241
|
const body = new Blob([JSON.stringify(snapshot)], { type: 'application/json' });
|
|
224
|
-
if (body.size > LIMITS.maxProjectBytes) throw new Error(tx('Reference project is too large: ', '参考项目过大:'
|
|
242
|
+
if (body.size > LIMITS.maxProjectBytes) throw new Error(tx('Reference project is too large: ' + source.name, '参考项目过大:' + source.name));
|
|
243
|
+
projectFiles.push(...(snapshot.files || []).map(file => ({ ...file, path: source.projectId + '/' + file.path })));
|
|
225
244
|
const id = crypto.randomUUID();
|
|
226
245
|
attachments.push({ id, name: source.projectId + '.writing-reference.json', size: body.size,
|
|
227
246
|
mimeType: 'application/json', kind: 'file', contentBase64: await encode(body) });
|
|
@@ -229,6 +248,11 @@
|
|
|
229
248
|
} else selected.push({ kind: 'saved', sourceId: source.sourceId });
|
|
230
249
|
}
|
|
231
250
|
stillActive();
|
|
251
|
+
if (projectFiles.length) {
|
|
252
|
+
const check = await deps.preflightProjectReferences({ project: { files: projectFiles }, assertCurrent: stillActive });
|
|
253
|
+
stillActive();
|
|
254
|
+
if (check.blocked) throw new Error(tx('Blocked: sensitive content review required', '已阻止:需要处理敏感内容'));
|
|
255
|
+
}
|
|
232
256
|
operation.phase = 'learning'; operation.sourceName = ''; render();
|
|
233
257
|
const tracked = await deps.startBuild({ ...settings, ...target, mode: 'ask',
|
|
234
258
|
task: 'Generate the selected project writing skill and its section references.',
|
|
@@ -241,7 +265,7 @@
|
|
|
241
265
|
const response = await tracked.promise;
|
|
242
266
|
if (generation !== epoch) return;
|
|
243
267
|
if (!response?.ok) {
|
|
244
|
-
const error = new Error(response?.error?.message || tx('Style generation failed.', '风格生成失败。'));
|
|
268
|
+
const error = new Error(errorMessage(response?.error?.message, response?.error?.code) || tx('Style generation failed.', '风格生成失败。'));
|
|
245
269
|
error.code = response?.error?.code; throw error;
|
|
246
270
|
}
|
|
247
271
|
const result = response.result?.writingStyle;
|
|
@@ -253,7 +277,7 @@
|
|
|
253
277
|
} catch (error) {
|
|
254
278
|
if (generation === epoch) {
|
|
255
279
|
message = operation.cancelled || error.code === 'codex_cancelled'
|
|
256
|
-
? tx('Style generation cancelled.', '已取消风格生成。') : error.message;
|
|
280
|
+
? tx('Style generation cancelled.', '已取消风格生成。') : errorMessage(error.message, error.code);
|
|
257
281
|
messageTone = operation.cancelled || error.code === 'codex_cancelled' ? '' : 'error';
|
|
258
282
|
}
|
|
259
283
|
} finally {
|
|
@@ -340,7 +364,7 @@
|
|
|
340
364
|
list.append(loading);
|
|
341
365
|
} else if (projectError) {
|
|
342
366
|
const error = node('div', 'ws2-picker-state ws2-picker-error');
|
|
343
|
-
error.append(node('strong', '', tx('Projects could not be loaded', '项目列表未能加载')), node('p', '', projectError),
|
|
367
|
+
error.append(node('strong', '', tx('Projects could not be loaded', '项目列表未能加载')), node('p', '', displayMessage(projectError)),
|
|
344
368
|
button(tx('Retry', '重试'), () => { void loadProjects(true); }, 'ws2-button', 'retry-projects'));
|
|
345
369
|
list.append(error);
|
|
346
370
|
} else {
|
|
@@ -540,7 +564,7 @@
|
|
|
540
564
|
}
|
|
541
565
|
}
|
|
542
566
|
if (message || !meta) {
|
|
543
|
-
const status = node('div', 'ws2-status', message || tx('Loading writing-style settings…', '正在加载写作风格设置…'));
|
|
567
|
+
const status = node('div', 'ws2-status', displayMessage(message) || tx('Loading writing-style settings…', '正在加载写作风格设置…'));
|
|
544
568
|
status.dataset.tone = messageTone;
|
|
545
569
|
status.setAttribute('role', messageTone === 'error' ? 'alert' : 'status');
|
|
546
570
|
if (!meta && message) status.append(button(tx('Retry', '重试'), () => {
|