codex-overleaf-link 2.3.1 → 2.3.3
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 +23 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/content/assetTransferBroker.js +169 -0
- package/extension/src/content/contentRuntime.js +28 -1
- package/extension/src/content/generated/content.bundle.js +630 -68
- package/extension/src/content/generated/content.bundle.meta.json +6 -3
- package/extension/src/content/legacyGlobalRegistry.js +3 -0
- package/extension/src/content/markdownDomRenderer.js +70 -0
- package/extension/src/content/modelPicker.js +2 -2
- package/extension/src/content/modelPickerSupport.js +3 -3
- package/extension/src/content/pageBridgeClient.js +1 -0
- package/extension/src/content/pendingInputView.js +16 -3
- package/extension/src/content/providerSettingsDialog.js +1 -0
- package/extension/src/content/runResultActions.js +114 -0
- package/extension/src/content/runTimelineView.js +20 -0
- package/extension/src/content/sessionForkController.js +120 -0
- package/extension/src/content/writebackController.js +3 -0
- package/extension/src/content/writebackOrchestrator.js +3 -1
- package/extension/src/page/binaryAssetUploader.js +343 -0
- package/extension/src/page/trackedChangesLifecycle.js +2 -0
- package/extension/src/pageBridge.js +30 -1
- package/extension/src/shared/compatibility.js +4 -2
- package/extension/src/shared/i18n.js +16 -0
- package/extension/src/shared/models.js +13 -2
- package/extension/src/shared/pageRpcContract.js +5 -1
- package/extension/src/shared/sessionState.js +12 -4
- package/extension/src/shared/storageDb.js +1 -1
- package/extension/src/shared/storageRunActions.js +11 -1
- package/extension/styles/panel.css +232 -31
- package/native-host/src/codexModels.js +6 -2
- package/native-host/src/codexRuntimeIdentity.js +79 -4
- package/native-host/src/codexSessionRunner.js +5 -1
- package/native-host/src/codexThreadFork.js +103 -0
- package/native-host/src/mirrorWorkspace.js +5 -19
- package/native-host/src/nativeAssetTransfer.js +183 -0
- package/native-host/src/nativeEnvironment.js +4 -1
- package/native-host/src/taskRunnerRuntime.js +22 -0
- package/native-host/src/textPatch.js +707 -259
- package/package.json +1 -1
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"entry": "extension/entries/content-entry.mjs",
|
|
4
4
|
"output": "extension/src/content/generated/content.bundle.js",
|
|
5
|
-
"sourceDigest": "
|
|
6
|
-
"outputDigest": "
|
|
7
|
-
"byteLength":
|
|
5
|
+
"sourceDigest": "4b5c2f7a7653947ac71ae42a3301c7547f789c2ca33e25f99f6781f31d1147be",
|
|
6
|
+
"outputDigest": "4e32bcf7d8be72541bb39508e38335ad0e32e41844afa45d7f0d34c9481a1cec",
|
|
7
|
+
"byteLength": 2313648,
|
|
8
8
|
"inputs": [
|
|
9
9
|
"extension/entries/content-entry.mjs",
|
|
10
10
|
"extension/src/content/activeTurnControl.js",
|
|
11
11
|
"extension/src/content/applyResultFormatters.js",
|
|
12
|
+
"extension/src/content/assetTransferBroker.js",
|
|
12
13
|
"extension/src/content/composerAttachments.js",
|
|
13
14
|
"extension/src/content/composerPanel.js",
|
|
14
15
|
"extension/src/content/contentRuntime.js",
|
|
@@ -45,11 +46,13 @@
|
|
|
45
46
|
"extension/src/content/runGuidanceController.js",
|
|
46
47
|
"extension/src/content/runGuidanceView.js",
|
|
47
48
|
"extension/src/content/runQueueScheduler.js",
|
|
49
|
+
"extension/src/content/runResultActions.js",
|
|
48
50
|
"extension/src/content/runSettlementPersistence.js",
|
|
49
51
|
"extension/src/content/runTimelineView.js",
|
|
50
52
|
"extension/src/content/scopedPersistenceCoordinator.js",
|
|
51
53
|
"extension/src/content/scopedPersistenceDetachedCommit.js",
|
|
52
54
|
"extension/src/content/scopedPersistencePanelState.js",
|
|
55
|
+
"extension/src/content/sessionForkController.js",
|
|
53
56
|
"extension/src/content/sessionManager.js",
|
|
54
57
|
"extension/src/content/sessionMenuView.js",
|
|
55
58
|
"extension/src/content/sessionPanel.js",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
'ActiveTurnControl',
|
|
16
16
|
'AgentTranscript',
|
|
17
17
|
'ApplyResultFormatters',
|
|
18
|
+
'AssetTransferBroker',
|
|
18
19
|
'AuditRecords',
|
|
19
20
|
'Compatibility',
|
|
20
21
|
'ComposerAttachments',
|
|
@@ -53,12 +54,14 @@
|
|
|
53
54
|
'RunGuidanceController',
|
|
54
55
|
'RunInputQueue',
|
|
55
56
|
'RunQueueScheduler',
|
|
57
|
+
'RunResultActions',
|
|
56
58
|
'RunSettlementPersistence',
|
|
57
59
|
'RunTimelineView',
|
|
58
60
|
'ScopedPersistenceCoordinator',
|
|
59
61
|
'ScopedPersistenceTransaction',
|
|
60
62
|
'SensitiveScan',
|
|
61
63
|
'SessionManager',
|
|
64
|
+
'SessionForkController',
|
|
62
65
|
'SessionPanel',
|
|
63
66
|
'SessionPersistence',
|
|
64
67
|
'SessionState',
|
|
@@ -442,9 +442,79 @@
|
|
|
442
442
|
return error;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
+
function decorateCompletionReport(container, options = {}) {
|
|
446
|
+
const tx = typeof options.tx === 'function' ? options.tx : en => en;
|
|
447
|
+
const document = container?.ownerDocument || globalThis.document;
|
|
448
|
+
if (!container || !document) return;
|
|
449
|
+
|
|
450
|
+
for (const block of Array.from(container.children || [])) {
|
|
451
|
+
const textNode = Array.from(block.childNodes || [])
|
|
452
|
+
.find(node => node?.nodeType === 3 && String(node.textContent || '').trim());
|
|
453
|
+
if (!textNode) continue;
|
|
454
|
+
const text = String(textNode.textContent || '');
|
|
455
|
+
const match = text.match(/^(\s*)(Conclusion:|结论:|Changes:|修改:)(\s*)/);
|
|
456
|
+
if (!match || !textNode.parentNode) continue;
|
|
457
|
+
const label = document.createElement('span');
|
|
458
|
+
label.className = 'run-system-label';
|
|
459
|
+
label.dataset.systemLabel = ['Conclusion:', '结论:'].includes(match[2])
|
|
460
|
+
? 'conclusion'
|
|
461
|
+
: 'changes';
|
|
462
|
+
label.textContent = `${match[2]}${match[3]}`;
|
|
463
|
+
textNode.textContent = `${match[1]}${text.slice(match[0].length)}`;
|
|
464
|
+
textNode.parentNode.insertBefore(label, textNode);
|
|
465
|
+
}
|
|
466
|
+
decorateChangeReceipts(container, document, tx);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function decorateChangeReceipts(container, document, tx) {
|
|
470
|
+
const label = container.querySelector?.('.run-system-label[data-system-label="changes"]');
|
|
471
|
+
const list = label?.closest?.('p')?.nextElementSibling;
|
|
472
|
+
if (!list || !['UL', 'OL'].includes(list.tagName)) return;
|
|
473
|
+
|
|
474
|
+
const receipts = [];
|
|
475
|
+
for (const item of Array.from(list.children || [])) {
|
|
476
|
+
const original = String(item.textContent || '').trim();
|
|
477
|
+
const match = original.match(/^(.+?):\s+(edit|create|rename|move|delete)(?:\s+\((.+)\))?$/i)
|
|
478
|
+
|| original.match(/^(.+?):\s*(编辑|新建|重命名|移动|删除)(?:((.+)))?$/);
|
|
479
|
+
if (!match) return;
|
|
480
|
+
receipts.push({ item, original, file: match[1], operation: match[2] });
|
|
481
|
+
}
|
|
482
|
+
if (!receipts.length) return;
|
|
483
|
+
|
|
484
|
+
const syncedFiles = new Set(receipts.map(receipt => receipt.file)).size;
|
|
485
|
+
const summary = document.createElement('div');
|
|
486
|
+
summary.className = 'run-change-summary';
|
|
487
|
+
const check = document.createElement('span');
|
|
488
|
+
check.className = 'run-change-summary__check';
|
|
489
|
+
check.setAttribute('aria-hidden', 'true');
|
|
490
|
+
check.textContent = '✓';
|
|
491
|
+
const text = document.createElement('span');
|
|
492
|
+
text.textContent = tx(
|
|
493
|
+
`${syncedFiles} ${syncedFiles === 1 ? 'file' : 'files'} synced`,
|
|
494
|
+
`已同步 ${syncedFiles} 个文件`
|
|
495
|
+
);
|
|
496
|
+
summary.append(check, text);
|
|
497
|
+
list.parentNode?.insertBefore(summary, list);
|
|
498
|
+
list.classList.add('run-change-receipts');
|
|
499
|
+
|
|
500
|
+
for (const receipt of receipts) {
|
|
501
|
+
const file = document.createElement('span');
|
|
502
|
+
file.className = 'run-change-receipt__file';
|
|
503
|
+
file.textContent = receipt.file;
|
|
504
|
+
const operation = document.createElement('span');
|
|
505
|
+
operation.className = 'run-change-receipt__operation';
|
|
506
|
+
operation.textContent = receipt.operation;
|
|
507
|
+
receipt.item.classList.add('run-change-receipt');
|
|
508
|
+
receipt.item.setAttribute('aria-label', receipt.original);
|
|
509
|
+
receipt.item.title = receipt.original;
|
|
510
|
+
receipt.item.replaceChildren(file, operation);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
445
514
|
return {
|
|
446
515
|
DEFAULT_LIMITS,
|
|
447
516
|
countTokens,
|
|
517
|
+
decorateCompletionReport,
|
|
448
518
|
renderMarkdown,
|
|
449
519
|
stripEmptyHtmlComments
|
|
450
520
|
};
|
|
@@ -452,8 +452,8 @@
|
|
|
452
452
|
if (effort === 'xhigh') return 'Max';
|
|
453
453
|
}
|
|
454
454
|
const labels = getLocale() === 'zh'
|
|
455
|
-
? { none: '关闭', minimal: '最小', low: '低', medium: '中', high: '高', xhigh: '超高' }
|
|
456
|
-
: { none: 'Off', minimal: 'Minimal', low: 'Low', medium: 'Medium', high: 'High', xhigh: 'XHigh' };
|
|
455
|
+
? { none: '关闭', minimal: '最小', low: '低', medium: '中', high: '高', xhigh: '超高', max: '最大', ultra: '极限' }
|
|
456
|
+
: { none: 'Off', minimal: 'Minimal', low: 'Low', medium: 'Medium', high: 'High', xhigh: 'XHigh', max: 'Max', ultra: 'Ultra' };
|
|
457
457
|
return labels[effort] || effort || '';
|
|
458
458
|
}
|
|
459
459
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function normalizeReasoningEffortsForSelect(efforts) {
|
|
22
|
-
const allowed = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
22
|
+
const allowed = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
|
|
23
23
|
return Array.from(new Set((Array.isArray(efforts) ? efforts : [])
|
|
24
24
|
.map(effort => normalizeModelOptionId(effort))
|
|
25
25
|
.filter(effort => allowed.has(effort))));
|
|
@@ -92,12 +92,12 @@
|
|
|
92
92
|
const normalized = {
|
|
93
93
|
id,
|
|
94
94
|
label: typeof model?.label === 'string' && model.label.length > 0 ? model.label : id,
|
|
95
|
-
reasoningEfforts:
|
|
95
|
+
reasoningEfforts: normalizeReasoningEffortsForSelect(model?.reasoningEfforts),
|
|
96
96
|
reasoningPresentation: typeof model?.reasoningPresentation === 'string' ? model.reasoningPresentation : '',
|
|
97
97
|
speedTiers: normalizeSpeedTiersForSelect(model?.speedTiers)
|
|
98
98
|
};
|
|
99
99
|
if (Object.prototype.hasOwnProperty.call(Object(model), 'defaultReasoningEffort')) {
|
|
100
|
-
normalized.defaultReasoningEffort = model.defaultReasoningEffort;
|
|
100
|
+
normalized.defaultReasoningEffort = normalizeModelOptionId(model.defaultReasoningEffort);
|
|
101
101
|
}
|
|
102
102
|
if (Object.prototype.hasOwnProperty.call(Object(model), 'defaultSpeedTier')) {
|
|
103
103
|
normalized.defaultSpeedTier = model.defaultSpeedTier;
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
['src/page/overleafProjectSnapshot.js', 'codex-overleaf-project-snapshot-script'],
|
|
22
22
|
['src/page/pageBridgeCapability.js', 'codex-overleaf-page-bridge-capability-script'],
|
|
23
23
|
['src/page/treeOperations.js', 'codex-overleaf-tree-operations-script', true],
|
|
24
|
+
['src/page/binaryAssetUploader.js', 'codex-overleaf-binary-asset-uploader-script', true],
|
|
24
25
|
['src/page/snapshotRouter.js', 'codex-overleaf-snapshot-router-script'],
|
|
25
26
|
['src/page/writeGuard.js', 'codex-overleaf-write-guard-script', true],
|
|
26
27
|
['src/page/trackedChangesLifecycle.js', 'codex-overleaf-tracked-changes-lifecycle-script', true],
|
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
}
|
|
17
17
|
container.replaceChildren();
|
|
18
18
|
container.hidden = !items.length;
|
|
19
|
+
if (!items.length) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
19
22
|
for (const item of items) {
|
|
20
23
|
const row = document.createElement('article');
|
|
21
24
|
row.className = 'codex-pending-input';
|
|
22
25
|
row.dataset.status = item.status || 'queued';
|
|
26
|
+
const marker = document.createElement('span');
|
|
27
|
+
marker.className = 'codex-pending-input-marker';
|
|
28
|
+
marker.setAttribute('aria-hidden', 'true');
|
|
29
|
+
marker.append(createIcon('queue'));
|
|
23
30
|
const copy = document.createElement('div');
|
|
24
31
|
copy.className = 'codex-pending-input-copy';
|
|
25
32
|
const status = document.createElement('span');
|
|
@@ -34,7 +41,10 @@
|
|
|
34
41
|
const text = document.createElement('span');
|
|
35
42
|
text.className = 'codex-pending-input-text';
|
|
36
43
|
text.textContent = item.text;
|
|
37
|
-
copy.append(
|
|
44
|
+
copy.append(text);
|
|
45
|
+
if (item.status && item.status !== 'queued') {
|
|
46
|
+
copy.append(status);
|
|
47
|
+
}
|
|
38
48
|
const actions = document.createElement('div');
|
|
39
49
|
actions.className = 'codex-pending-input-actions';
|
|
40
50
|
if (item.status === 'paused') {
|
|
@@ -60,7 +70,8 @@
|
|
|
60
70
|
remove.disabled = item.status === 'steering' || item.status === 'executing';
|
|
61
71
|
text.title = item.text;
|
|
62
72
|
actions.append(remove);
|
|
63
|
-
row.
|
|
73
|
+
row.setAttribute('aria-label', `${status.textContent || options.tr?.('queuedInputQueued') || 'Queued'}: ${item.text}`);
|
|
74
|
+
row.append(marker, copy, actions);
|
|
64
75
|
container.append(row);
|
|
65
76
|
}
|
|
66
77
|
}
|
|
@@ -93,7 +104,9 @@
|
|
|
93
104
|
svg.setAttribute('focusable', 'false');
|
|
94
105
|
const paths = icon === 'guide'
|
|
95
106
|
? ['M4 4.5v4a4 4 0 0 0 4 4h7', 'm12 9 3.5 3.5L12 16']
|
|
96
|
-
:
|
|
107
|
+
: icon === 'queue'
|
|
108
|
+
? ['M4 5.5h12', 'M4 10h12', 'M4 14.5h8']
|
|
109
|
+
: ['M6 6h8', 'M8 6V4.5h4V6', 'm7.2 8 0.6 7.5h4.4l.6-7.5'];
|
|
97
110
|
for (const value of paths) {
|
|
98
111
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
99
112
|
path.setAttribute('d', value);
|
|
@@ -985,6 +985,7 @@
|
|
|
985
985
|
return;
|
|
986
986
|
}
|
|
987
987
|
const style = instance.document.defaultView.getComputedStyle(panel);
|
|
988
|
+
instance.root.dataset.theme = panel.dataset.theme === 'light' ? 'light' : 'dark';
|
|
988
989
|
instance.root.style.setProperty('--provider-panel-bg', style.backgroundColor || '#151716');
|
|
989
990
|
instance.root.style.setProperty('--provider-panel-color', style.color || '#eceeea');
|
|
990
991
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
(function initCodexOverleafRunResultActions(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafRunResultActions = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function runResultActionsFactory() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function create(deps = {}) {
|
|
9
|
+
const {
|
|
10
|
+
tr,
|
|
11
|
+
getLocale,
|
|
12
|
+
forkRunFromNode,
|
|
13
|
+
document: documentRef = globalThis.document,
|
|
14
|
+
navigator: navigatorRef = globalThis.navigator
|
|
15
|
+
} = deps;
|
|
16
|
+
|
|
17
|
+
function configureResultActions(root, run) {
|
|
18
|
+
const actions = root.querySelector('.run-result-actions');
|
|
19
|
+
const reports = root.querySelectorAll('.run-completion-report');
|
|
20
|
+
const report = reports[reports.length - 1];
|
|
21
|
+
if (!actions || !report) return;
|
|
22
|
+
report.append(actions);
|
|
23
|
+
actions.hidden = false;
|
|
24
|
+
configureCopyButton(root);
|
|
25
|
+
configureForkButton(root, run);
|
|
26
|
+
configureCompletedTime(actions, run);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function configureForkButton(root, run) {
|
|
30
|
+
const existing = root.querySelector('[data-run-fork]');
|
|
31
|
+
const button = existing.cloneNode(true);
|
|
32
|
+
existing.replaceWith(button);
|
|
33
|
+
const available = run.forkSnapshot !== true
|
|
34
|
+
&& run.status !== 'running'
|
|
35
|
+
&& Boolean(run.codexTurnId)
|
|
36
|
+
&& typeof forkRunFromNode === 'function';
|
|
37
|
+
button.hidden = false;
|
|
38
|
+
button.disabled = !available;
|
|
39
|
+
button.title = tr(available ? 'forkRunTitle' : 'forkRunUnavailable');
|
|
40
|
+
button.setAttribute('aria-label', button.title);
|
|
41
|
+
button.innerHTML = '<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M3 10h3.5c3.5 0 4.2-4 7.5-4h2m-2.5-2.5L16 6l-2.5 2.5M6.5 10c3.5 0 4.2 4 7.5 4h2m-2.5-2.5L16 14l-2.5 2.5"/></svg>';
|
|
42
|
+
if (!available) return;
|
|
43
|
+
button.addEventListener('click', async event => {
|
|
44
|
+
event.stopPropagation();
|
|
45
|
+
button.disabled = true;
|
|
46
|
+
button.classList.add('is-running');
|
|
47
|
+
try {
|
|
48
|
+
await forkRunFromNode(run.id);
|
|
49
|
+
} catch (_error) {
|
|
50
|
+
button.disabled = false;
|
|
51
|
+
button.classList.remove('is-running');
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function configureCompletedTime(actions, run) {
|
|
57
|
+
const target = actions.querySelector('[data-run-completed-time]');
|
|
58
|
+
const finishedAt = String(run.finishedAt || '').trim();
|
|
59
|
+
const date = finishedAt ? new Date(finishedAt) : null;
|
|
60
|
+
if (!target || !date || !Number.isFinite(date.getTime())) return;
|
|
61
|
+
target.hidden = false;
|
|
62
|
+
target.textContent = new Intl.DateTimeFormat(getLocale(), {
|
|
63
|
+
hour: '2-digit',
|
|
64
|
+
minute: '2-digit'
|
|
65
|
+
}).format(date);
|
|
66
|
+
target.title = date.toLocaleString(getLocale());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function configureCopyButton(root) {
|
|
70
|
+
const existing = root.querySelector('[data-run-copy]');
|
|
71
|
+
const button = existing.cloneNode(true);
|
|
72
|
+
existing.replaceWith(button);
|
|
73
|
+
const finalAnswer = root.querySelector('.run-completion-report .run-final-answer');
|
|
74
|
+
const copyText = String(finalAnswer?.innerText || finalAnswer?.textContent || '').trim();
|
|
75
|
+
button.hidden = false;
|
|
76
|
+
button.disabled = !copyText;
|
|
77
|
+
button.title = tr('copyConclusionTitle');
|
|
78
|
+
button.setAttribute('aria-label', button.title);
|
|
79
|
+
button.innerHTML = '<svg viewBox="0 0 20 20" aria-hidden="true"><rect x="6.5" y="6.5" width="9" height="9" rx="2"/><path d="M13.5 6.5V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6.5a2 2 0 0 0 2 2h1.5"/></svg>';
|
|
80
|
+
if (!copyText) return;
|
|
81
|
+
button.addEventListener('click', async event => {
|
|
82
|
+
event.stopPropagation();
|
|
83
|
+
try {
|
|
84
|
+
await writeClipboardText(copyText);
|
|
85
|
+
button.title = tr('copyConclusionDone');
|
|
86
|
+
} catch (_error) {
|
|
87
|
+
button.title = tr('copyConclusionFailed');
|
|
88
|
+
}
|
|
89
|
+
button.setAttribute('aria-label', button.title);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function writeClipboardText(text) {
|
|
94
|
+
if (navigatorRef?.clipboard?.writeText) {
|
|
95
|
+
await navigatorRef.clipboard.writeText(text);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const textarea = documentRef.createElement('textarea');
|
|
99
|
+
textarea.value = text;
|
|
100
|
+
textarea.setAttribute('readonly', '');
|
|
101
|
+
textarea.style.position = 'fixed';
|
|
102
|
+
textarea.style.opacity = '0';
|
|
103
|
+
documentRef.body.append(textarea);
|
|
104
|
+
textarea.select();
|
|
105
|
+
const copied = documentRef.execCommand('copy');
|
|
106
|
+
textarea.remove();
|
|
107
|
+
if (!copied) throw new Error('clipboard_copy_failed');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return Object.freeze({ configureResultActions });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return Object.freeze({ create });
|
|
114
|
+
});
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
function create(deps = {}) {
|
|
14
14
|
const {
|
|
15
15
|
RunGuidanceView,
|
|
16
|
+
RunResultActions,
|
|
16
17
|
tr,
|
|
17
18
|
tx,
|
|
18
19
|
getLocale,
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
cssEscape,
|
|
30
31
|
renderMarkdownInlineText,
|
|
31
32
|
renderMarkdownBlockText,
|
|
33
|
+
decorateCompletionReport,
|
|
32
34
|
sanitizeAssistantVisibleText,
|
|
33
35
|
sanitizeAssistantVisibleValue,
|
|
34
36
|
stripEmptyHtmlCommentPlaceholders = value => value,
|
|
@@ -346,6 +348,11 @@
|
|
|
346
348
|
</summary>
|
|
347
349
|
</details>
|
|
348
350
|
<div class="run-activity-list" data-run-events></div>
|
|
351
|
+
<div class="run-result-actions" hidden>
|
|
352
|
+
<button type="button" class="run-copy-action" data-run-copy hidden></button>
|
|
353
|
+
<button type="button" class="run-fork-action" data-run-fork hidden></button>
|
|
354
|
+
<span class="run-completed-time" data-run-completed-time hidden></span>
|
|
355
|
+
</div>
|
|
349
356
|
<div class="run-report" data-run-report aria-live="polite" hidden></div>
|
|
350
357
|
</div>
|
|
351
358
|
`;
|
|
@@ -399,6 +406,7 @@
|
|
|
399
406
|
}
|
|
400
407
|
}
|
|
401
408
|
|
|
409
|
+
RunResultActions.configureResultActions(root, run);
|
|
402
410
|
configureAcceptButton(root, run);
|
|
403
411
|
configureUndoButton(root, run);
|
|
404
412
|
return root;
|
|
@@ -702,6 +710,7 @@
|
|
|
702
710
|
}
|
|
703
711
|
if (mainSections.length) {
|
|
704
712
|
renderMarkdownBlockText(main, mainSections.join('\n\n'));
|
|
713
|
+
decorateCompletionReport(main, { tx });
|
|
705
714
|
report.append(main);
|
|
706
715
|
}
|
|
707
716
|
|
|
@@ -722,6 +731,7 @@
|
|
|
722
731
|
const body = document.createElement('div');
|
|
723
732
|
body.className = 'run-final-answer';
|
|
724
733
|
renderMarkdownBlockText(body, split.body || flatText);
|
|
734
|
+
decorateCompletionReport(body, { tx });
|
|
725
735
|
report.append(body);
|
|
726
736
|
appendCompileFix(report);
|
|
727
737
|
appendRejectedRedo(report);
|
|
@@ -886,6 +896,11 @@
|
|
|
886
896
|
const button = existing.cloneNode(true);
|
|
887
897
|
existing.replaceWith(button);
|
|
888
898
|
|
|
899
|
+
if (run.forkSnapshot === true) {
|
|
900
|
+
button.hidden = true;
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
889
904
|
if (isTrackedChangeLifecycleRun(run)) {
|
|
890
905
|
configureLifecycleUndoButton(button, run);
|
|
891
906
|
return;
|
|
@@ -982,6 +997,11 @@
|
|
|
982
997
|
const button = existing.cloneNode(true);
|
|
983
998
|
existing.replaceWith(button);
|
|
984
999
|
|
|
1000
|
+
if (run.forkSnapshot === true) {
|
|
1001
|
+
button.hidden = true;
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
985
1005
|
if (!isTrackedChangeLifecycleRun(run)) {
|
|
986
1006
|
button.hidden = true;
|
|
987
1007
|
return;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
(function initCodexOverleafSessionForkController(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafSessionForkController = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function sessionForkControllerFactory() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function create(deps = {}) {
|
|
9
|
+
const {
|
|
10
|
+
SessionState, getState, setState, sendBackgroundNative, saveState,
|
|
11
|
+
applyStateToPanel, showPluginToast, tr
|
|
12
|
+
} = deps;
|
|
13
|
+
|
|
14
|
+
async function forkRunFromNode(runId) {
|
|
15
|
+
const previousState = getState();
|
|
16
|
+
const source = findSource(previousState, runId);
|
|
17
|
+
if (!source) return failFork('session_fork_source_missing', tr('forkRunUnavailable'));
|
|
18
|
+
const { session, run, runIndex } = source;
|
|
19
|
+
if (run.status === 'running' || !session.codexThreadId || !run.codexTurnId) {
|
|
20
|
+
return failFork('session_fork_unavailable', tr('forkRunUnavailable'));
|
|
21
|
+
}
|
|
22
|
+
const response = await sendBackgroundNative({
|
|
23
|
+
method: 'codex.thread.fork',
|
|
24
|
+
params: {
|
|
25
|
+
threadId: session.codexThreadId,
|
|
26
|
+
lastTurnId: run.codexTurnId,
|
|
27
|
+
speedTier: session.speedTier,
|
|
28
|
+
loadCodexLocalSkills: previousState.loadCodexLocalSkills !== false,
|
|
29
|
+
loadCodexOverleafSkills: previousState.loadCodexOverleafSkills !== false
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (!response?.ok || !response.result?.threadId) {
|
|
33
|
+
return failFork(response?.error?.code || 'session_fork_failed', response?.error?.message || tr('forkRunFailed'));
|
|
34
|
+
}
|
|
35
|
+
const copiedRuns = session.runs.slice(0, runIndex + 1).map(copyRunSnapshot);
|
|
36
|
+
const titleBase = String(session.title || copiedRuns[0]?.task || tr('newSessionFallback')).trim();
|
|
37
|
+
const forkTitle = buildUniqueForkTitle(previousState.sessions, titleBase);
|
|
38
|
+
const forked = SessionState.createSession({
|
|
39
|
+
title: forkTitle,
|
|
40
|
+
titleSource: 'manual',
|
|
41
|
+
runs: copiedRuns,
|
|
42
|
+
task: '',
|
|
43
|
+
mode: session.mode,
|
|
44
|
+
providerId: session.providerId,
|
|
45
|
+
model: session.model,
|
|
46
|
+
reasoningEffort: session.reasoningEffort,
|
|
47
|
+
speedTier: session.speedTier,
|
|
48
|
+
requireReviewing: session.requireReviewing,
|
|
49
|
+
focusFiles: session.focusFiles,
|
|
50
|
+
projectReferenceFiles: session.projectReferenceFiles,
|
|
51
|
+
codexThreadId: response.result.threadId,
|
|
52
|
+
pendingInputs: [],
|
|
53
|
+
forkedFromSessionId: session.id,
|
|
54
|
+
forkedFromRunId: run.id
|
|
55
|
+
});
|
|
56
|
+
setState(SessionState.normalizePanelState({
|
|
57
|
+
...previousState,
|
|
58
|
+
sessions: [...(previousState.sessions || []), forked],
|
|
59
|
+
activeSessionId: forked.id
|
|
60
|
+
}));
|
|
61
|
+
try {
|
|
62
|
+
await saveState();
|
|
63
|
+
} catch (error) {
|
|
64
|
+
setState(previousState);
|
|
65
|
+
await sendBackgroundNative({
|
|
66
|
+
method: 'codex.history.clearPlugin',
|
|
67
|
+
params: { sessionId: forked.id, threadId: response.result.threadId }
|
|
68
|
+
}).catch(() => null);
|
|
69
|
+
showPluginToast(error.message || tr('forkRunFailed'), { status: 'failed', sticky: true });
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
applyStateToPanel();
|
|
73
|
+
showPluginToast(tr('forkRunDone'), { status: 'completed' });
|
|
74
|
+
return forked;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function failFork(code, message) {
|
|
78
|
+
showPluginToast(message, { status: 'failed', sticky: true });
|
|
79
|
+
throw codedError(code, message);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { forkRunFromNode };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildUniqueForkTitle(sessions = [], sourceTitle = '') {
|
|
86
|
+
const fallback = String(sourceTitle || 'New Session').trim() || 'New Session';
|
|
87
|
+
const baseTitle = fallback
|
|
88
|
+
.replace(/\s+\(fork\)$/i, '')
|
|
89
|
+
.replace(/\s+\(\d+\)$/, '')
|
|
90
|
+
.trim() || fallback;
|
|
91
|
+
const usedTitles = new Set((sessions || []).map(session => String(session?.title || '').trim()));
|
|
92
|
+
let index = 1;
|
|
93
|
+
while (usedTitles.has(`${baseTitle} (${index})`)) index += 1;
|
|
94
|
+
return `${baseTitle} (${index})`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function findSource(state, runId) {
|
|
98
|
+
for (const session of state?.sessions || []) {
|
|
99
|
+
const runIndex = (session.runs || []).findIndex(run => run.id === runId);
|
|
100
|
+
if (runIndex >= 0) return { session, run: session.runs[runIndex], runIndex };
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function copyRunSnapshot(run) {
|
|
106
|
+
const copy = JSON.parse(JSON.stringify(run || {}));
|
|
107
|
+
return {
|
|
108
|
+
...copy,
|
|
109
|
+
id: `fork_${String(copy.id || 'run')}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`,
|
|
110
|
+
forkSnapshot: true,
|
|
111
|
+
forkSourceRunId: String(copy.id || ''),
|
|
112
|
+
attachments: [], appliedOperations: [], undoOperations: [], undoBaseFiles: [],
|
|
113
|
+
undoTrackedChanges: [], undoExpectedFiles: [], undoStatus: '',
|
|
114
|
+
trackedChangeStatus: '', nativeRequestId: '', queueItemId: '', interruptedDraft: undefined
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function codedError(code, message) { const error = new Error(message); error.code = code; return error; }
|
|
119
|
+
return { copyRunSnapshot, create };
|
|
120
|
+
});
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
type: change.type,
|
|
16
16
|
path: change.path,
|
|
17
17
|
contentBase64: change.contentBase64 || '',
|
|
18
|
+
assetRef: change.assetRef && typeof change.assetRef === 'object' ? { ...change.assetRef } : undefined,
|
|
19
|
+
sha256: change.sha256 || change.assetRef?.sha256 || '',
|
|
20
|
+
mimeType: change.mimeType || change.assetRef?.mimeType || '',
|
|
18
21
|
size: Number.isFinite(Number(change.size)) ? Number(change.size) : undefined,
|
|
19
22
|
previousExists: change.previousExists === true,
|
|
20
23
|
previousKind: change.previousKind || '',
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
let onMirrorRefreshSettled;
|
|
62
62
|
let writebackSettlement;
|
|
63
63
|
let injectedWritebackSettlement;
|
|
64
|
+
let assetTransferBroker;
|
|
64
65
|
|
|
65
66
|
function buildSettlement(input = {}) {
|
|
66
67
|
return writebackSettlement?.settle instanceof Function
|
|
@@ -283,7 +284,7 @@
|
|
|
283
284
|
};
|
|
284
285
|
}
|
|
285
286
|
const applied = operations.length
|
|
286
|
-
? mergeApplyResultSkipped(await
|
|
287
|
+
? mergeApplyResultSkipped(await assetTransferBroker.applyOperations({
|
|
287
288
|
operations,
|
|
288
289
|
baseFiles: project?.files || [],
|
|
289
290
|
requireReviewing: runRequireReviewing,
|
|
@@ -859,6 +860,7 @@
|
|
|
859
860
|
getState,
|
|
860
861
|
getCurrentRunView,
|
|
861
862
|
} = deps);
|
|
863
|
+
assetTransferBroker = deps.assetTransferBroker;
|
|
862
864
|
onMirrorRefreshSettled = deps.onMirrorRefreshSettled;
|
|
863
865
|
writebackSettlement = deps.writebackSettlement;
|
|
864
866
|
compileAdapter = deps.compileAdapter;
|