minecodex 0.1.20 → 0.2.2
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/features/images/codex-feature.json +5 -0
- package/features/images/src/chatgpt-image-source-page.mjs +207 -0
- package/features/images/src/chatgpt-image-source.mjs +42 -0
- package/features/images/src/http-server.mjs +92 -3
- package/features/images/src/image-library.mjs +171 -4
- package/features/images/web/app.js +35 -13
- package/features/model-slider/codex-feature.json +5 -1
- package/package.json +1 -1
- package/packages/cli/src/chatgpt-launch-coordinator.mjs +27 -1
- package/packages/cli/src/commands.mjs +34 -16
- package/packages/cli/src/install-progress.mjs +56 -0
- package/packages/cli/src/platform.mjs +0 -12
- package/packages/cli/src/runtime-manager.mjs +25 -28
- package/packages/runtime-host/src/codex-runtime.mjs +358 -99
- package/packages/runtime-host/src/feature-registry.mjs +56 -10
- package/packages/runtime-host/src/main.mjs +11 -1
- package/packages/runtime-host/src/model-capabilities.mjs +86 -0
|
@@ -37,7 +37,7 @@ export function responsiveSummaryVisibleSurface({ displayMode, isPinned, isPopov
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function responsiveSummaryContentShift({ displayMode, isPinned }, layout = RESPONSIVE_SUMMARY_LAYOUT) {
|
|
40
|
-
return displayMode
|
|
40
|
+
return displayMode !== "overlay" && isPinned
|
|
41
41
|
? -(layout.panelWidth + layout.panelInset) / 2
|
|
42
42
|
: 0;
|
|
43
43
|
}
|
|
@@ -78,6 +78,18 @@ function requireDeclaredLoopbackSurface(feature, value) {
|
|
|
78
78
|
return url;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
export function pageScriptServiceOrigin(feature) {
|
|
82
|
+
if (!feature.pageScript || !feature.surfaceUrl || !feature.healthUrl) return null;
|
|
83
|
+
const surfaceUrl = new URL(feature.surfaceUrl);
|
|
84
|
+
const healthUrl = new URL(feature.healthUrl);
|
|
85
|
+
if (
|
|
86
|
+
surfaceUrl.origin !== healthUrl.origin
|
|
87
|
+
|| surfaceUrl.protocol !== "http:"
|
|
88
|
+
|| !LOOPBACK_SURFACE_HOSTS.has(surfaceUrl.hostname)
|
|
89
|
+
) return null;
|
|
90
|
+
return surfaceUrl.origin;
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
function findFrameByName(frameTree, frameName) {
|
|
82
94
|
if (frameTree.frame?.name === frameName) return frameTree.frame;
|
|
83
95
|
for (const child of frameTree.childFrames ?? []) {
|
|
@@ -127,9 +139,6 @@ export function createInjectionSource(features, {
|
|
|
127
139
|
const pinnedSurfaceMarker = "data-codex-personal-pinned-summary";
|
|
128
140
|
const modalSurfaceMarker = "data-codex-personal-modal";
|
|
129
141
|
const promptPreviewMarker = "data-codex-personal-prompt-preview";
|
|
130
|
-
const threadShiftStyle = document.createElement("style");
|
|
131
|
-
threadShiftStyle.setAttribute("data-codex-personal-summary-shift-style", "");
|
|
132
|
-
threadShiftStyle.textContent = "[data-codex-personal-summary-shift] { transform: none !important; --thread-wide-block-inline-shift: 0px !important; }";
|
|
133
142
|
const nativeLabels = {
|
|
134
143
|
sites: ["sites", "站点"],
|
|
135
144
|
scheduled: ["scheduled", "计划任务"],
|
|
@@ -254,7 +263,7 @@ export function createInjectionSource(features, {
|
|
|
254
263
|
const modelSelectorFeature = features.find(
|
|
255
264
|
(feature) => feature.entry?.kind === "composer-model-selector",
|
|
256
265
|
) ?? null;
|
|
257
|
-
const pageScriptFeatures = features.filter((feature) => feature.
|
|
266
|
+
const pageScriptFeatures = features.filter((feature) => feature.pageScript);
|
|
258
267
|
const pageSurfaces = new Map();
|
|
259
268
|
const pinnedSurfaces = new Map();
|
|
260
269
|
const surfaceRecords = new Map();
|
|
@@ -273,9 +282,9 @@ export function createInjectionSource(features, {
|
|
|
273
282
|
let savedComposerThread = null;
|
|
274
283
|
let currentThread = null;
|
|
275
284
|
let currentSummaryDisplayMode = null;
|
|
276
|
-
let
|
|
277
|
-
let shiftedThreadOriginal = null;
|
|
285
|
+
let shiftedConversationOwners = [];
|
|
278
286
|
let mainContentObserver = null;
|
|
287
|
+
let toolbarReadiness = null;
|
|
279
288
|
const domObservers = [];
|
|
280
289
|
const pageScriptCleanups = [];
|
|
281
290
|
const pendingHostActions = new Map();
|
|
@@ -304,6 +313,7 @@ export function createInjectionSource(features, {
|
|
|
304
313
|
let modelSelectorFastControlVisible = false;
|
|
305
314
|
// 持久化脏标记:仅在图标实际变化时写一次 localStorage。
|
|
306
315
|
let modelSelectorNativeFastIconsDirty = false;
|
|
316
|
+
const modelSelectorFastIconStorageKey = "codex-model-slider:fast-icons:v1";
|
|
307
317
|
const modelSelectorStyle = document.createElement("style");
|
|
308
318
|
modelSelectorStyle.setAttribute("data-codex-model-slider-style", "");
|
|
309
319
|
modelSelectorStyle.textContent = `
|
|
@@ -384,7 +394,7 @@ export function createInjectionSource(features, {
|
|
|
384
394
|
document.documentElement.append(modelSelectorStyle);
|
|
385
395
|
// 启动阶段就解析 Fast 图标:先读持久化的原生克隆,再从 app bundle 静态提取兜底,
|
|
386
396
|
// 避免首次会话必须等用户点开弹窗后 DOM 里才出现图标。
|
|
387
|
-
const persistedIcons = loadPersistedNativeFastIcons();
|
|
397
|
+
const persistedIcons = loadPersistedNativeFastIcons(modelSelectorFastIconStorageKey);
|
|
388
398
|
if (persistedIcons) {
|
|
389
399
|
modelSelectorNativeFastIcons = persistedIcons;
|
|
390
400
|
modelSelectorNativeFastIconSources = {};
|
|
@@ -404,9 +414,16 @@ export function createInjectionSource(features, {
|
|
|
404
414
|
"window",
|
|
405
415
|
"document",
|
|
406
416
|
"MutationObserver",
|
|
417
|
+
"config",
|
|
407
418
|
`"use strict";\n${source}`,
|
|
408
419
|
);
|
|
409
|
-
const cleanup = installer(
|
|
420
|
+
const cleanup = installer(
|
|
421
|
+
lifetime.signal,
|
|
422
|
+
window,
|
|
423
|
+
document,
|
|
424
|
+
MutationObserver,
|
|
425
|
+
feature.pageScript.config ?? {},
|
|
426
|
+
);
|
|
410
427
|
if (typeof cleanup === "function") pageScriptCleanups.push(cleanup);
|
|
411
428
|
} catch (error) {
|
|
412
429
|
console.warn(`[codex-personal] page-script feature failed: ${feature.id}`, error);
|
|
@@ -471,6 +488,57 @@ export function createInjectionSource(features, {
|
|
|
471
488
|
}) ?? null;
|
|
472
489
|
}
|
|
473
490
|
|
|
491
|
+
function structuralToolbarAnchor() {
|
|
492
|
+
const buttons = Array.from(document.querySelectorAll("button"));
|
|
493
|
+
return buttons.find((button) => /temporary chat/i.test(button.getAttribute("aria-label") ?? ""))
|
|
494
|
+
?? buttons.find((button) => new Set(["Toggle summary", "Toggle pinned summary"])
|
|
495
|
+
.has(button.getAttribute("aria-label")))
|
|
496
|
+
?? buttons.find((button) => button.getAttribute("aria-label") === "Toggle bottom panel")
|
|
497
|
+
?? buttons.find((button) => button.getAttribute("aria-label") === "Toggle side panel")
|
|
498
|
+
?? null;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function stopToolbarReadiness() {
|
|
502
|
+
if (!toolbarReadiness) return;
|
|
503
|
+
toolbarReadiness.observer.disconnect();
|
|
504
|
+
if (toolbarReadiness.retryTimer != null) clearTimeout(toolbarReadiness.retryTimer);
|
|
505
|
+
if (toolbarReadiness.deadlineTimer != null) clearTimeout(toolbarReadiness.deadlineTimer);
|
|
506
|
+
toolbarReadiness = null;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function ensureToolbarReadiness(candidate) {
|
|
510
|
+
const thread = threadIdentity();
|
|
511
|
+
if (toolbarReadiness?.candidate === candidate && toolbarReadiness.thread === thread) return;
|
|
512
|
+
stopToolbarReadiness();
|
|
513
|
+
const record = {
|
|
514
|
+
candidate,
|
|
515
|
+
thread,
|
|
516
|
+
attempts: 0,
|
|
517
|
+
retryTimer: null,
|
|
518
|
+
deadlineTimer: null,
|
|
519
|
+
observer: null,
|
|
520
|
+
};
|
|
521
|
+
const wake = () => {
|
|
522
|
+
if (toolbarReadiness !== record) return;
|
|
523
|
+
queueEnsure();
|
|
524
|
+
};
|
|
525
|
+
record.observer = new ResizeObserver(wake);
|
|
526
|
+
record.observer.observe(candidate);
|
|
527
|
+
const header = candidate.closest("header");
|
|
528
|
+
if (header) record.observer.observe(header);
|
|
529
|
+
const retry = () => {
|
|
530
|
+
if (toolbarReadiness !== record || record.attempts >= 8) return;
|
|
531
|
+
record.attempts += 1;
|
|
532
|
+
wake();
|
|
533
|
+
record.retryTimer = setTimeout(retry, 75);
|
|
534
|
+
};
|
|
535
|
+
record.retryTimer = setTimeout(retry, 75);
|
|
536
|
+
record.deadlineTimer = setTimeout(() => {
|
|
537
|
+
if (toolbarReadiness === record) stopToolbarReadiness();
|
|
538
|
+
}, 1_500);
|
|
539
|
+
toolbarReadiness = record;
|
|
540
|
+
}
|
|
541
|
+
|
|
474
542
|
function toolbarControlRoot(button) {
|
|
475
543
|
return button?.parentElement?.parentElement ?? button?.parentElement ?? button ?? null;
|
|
476
544
|
}
|
|
@@ -525,8 +593,10 @@ export function createInjectionSource(features, {
|
|
|
525
593
|
}
|
|
526
594
|
|
|
527
595
|
function mainContentViewport() {
|
|
528
|
-
|
|
596
|
+
const composer = currentComposer();
|
|
597
|
+
return composer?.closest("main[data-app-shell-main-surface]")
|
|
529
598
|
?? document.querySelector("main[data-app-shell-main-surface]")
|
|
599
|
+
?? document.querySelector("[data-app-shell-main-content-layout]")
|
|
530
600
|
?? document.querySelector("main");
|
|
531
601
|
}
|
|
532
602
|
|
|
@@ -666,8 +736,6 @@ export function createInjectionSource(features, {
|
|
|
666
736
|
return null;
|
|
667
737
|
}
|
|
668
738
|
|
|
669
|
-
const modelSelectorFastIconStorageKey = "codex-model-slider:fast-icons:v1";
|
|
670
|
-
|
|
671
739
|
function persistNativeFastIcons() {
|
|
672
740
|
if (!modelSelectorNativeFastIcons || !modelSelectorNativeFastIconsDirty) return;
|
|
673
741
|
try {
|
|
@@ -682,22 +750,29 @@ export function createInjectionSource(features, {
|
|
|
682
750
|
}
|
|
683
751
|
}
|
|
684
752
|
|
|
685
|
-
function loadPersistedNativeFastIcons() {
|
|
753
|
+
function loadPersistedNativeFastIcons(storageKey) {
|
|
754
|
+
let stored;
|
|
686
755
|
try {
|
|
687
|
-
|
|
688
|
-
if (!payload || typeof payload !== "object") return null;
|
|
689
|
-
const icons = {};
|
|
690
|
-
for (const [state, markup] of Object.entries(payload)) {
|
|
691
|
-
if (typeof markup !== "string") continue;
|
|
692
|
-
const container = document.createElement("template");
|
|
693
|
-
container.innerHTML = markup;
|
|
694
|
-
const svg = container.content.querySelector("svg");
|
|
695
|
-
if (svg) icons[state] = svg;
|
|
696
|
-
}
|
|
697
|
-
return Object.keys(icons).length ? icons : null;
|
|
756
|
+
stored = localStorage.getItem(storageKey);
|
|
698
757
|
} catch {
|
|
699
758
|
return null;
|
|
700
759
|
}
|
|
760
|
+
let payload;
|
|
761
|
+
try {
|
|
762
|
+
payload = JSON.parse(stored ?? "null");
|
|
763
|
+
} catch {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
if (!payload || typeof payload !== "object") return null;
|
|
767
|
+
const icons = {};
|
|
768
|
+
for (const [state, markup] of Object.entries(payload)) {
|
|
769
|
+
if (typeof markup !== "string") continue;
|
|
770
|
+
const container = document.createElement("template");
|
|
771
|
+
container.innerHTML = markup;
|
|
772
|
+
const svg = container.content.querySelector("svg");
|
|
773
|
+
if (svg) icons[state] = svg;
|
|
774
|
+
}
|
|
775
|
+
return Object.keys(icons).length ? icons : null;
|
|
701
776
|
}
|
|
702
777
|
|
|
703
778
|
function extractFastIconPath(source, prefix) {
|
|
@@ -911,14 +986,16 @@ export function createInjectionSource(features, {
|
|
|
911
986
|
(option) => String(option.thinkingEffort ?? "") === String(selected.thinkingEffort ?? ""),
|
|
912
987
|
);
|
|
913
988
|
const selectedEffort = selectedOption ? effortFor(selectedOption) : (efforts[0] ?? "instant");
|
|
989
|
+
const defaultEffort = String(version.defaultReasoningEffort ?? efforts[0] ?? "instant").toLowerCase();
|
|
914
990
|
const models = [{
|
|
915
991
|
model: String(selected.slug ?? ""),
|
|
916
992
|
displayName: String(version?.label ?? selected.slug ?? ""),
|
|
917
|
-
defaultReasoningEffort:
|
|
993
|
+
defaultReasoningEffort: defaultEffort,
|
|
918
994
|
supportedReasoningEfforts: efforts.map((effort) => ({ reasoningEffort: effort })),
|
|
919
995
|
}];
|
|
920
996
|
return {
|
|
921
997
|
models,
|
|
998
|
+
reasoningEffort: selectedEffort,
|
|
922
999
|
onSelectReasoningEffort(effort) {
|
|
923
1000
|
const target = options.find((option) => effortFor(option) === String(effort).toLowerCase());
|
|
924
1001
|
if (!target) return;
|
|
@@ -930,8 +1007,9 @@ export function createInjectionSource(features, {
|
|
|
930
1007
|
};
|
|
931
1008
|
}
|
|
932
1009
|
|
|
933
|
-
function nativeCatalogModelFor(identity) {
|
|
934
|
-
const
|
|
1010
|
+
function nativeCatalogModelFor(identity, selector = modelSelectorFeature?.modelSelector) {
|
|
1011
|
+
const controller = nativeComposerModelController();
|
|
1012
|
+
const models = controller?.models ?? [];
|
|
935
1013
|
const exact = models.find((model) => String(model.model ?? "").toLowerCase() === identity.raw.toLowerCase());
|
|
936
1014
|
const target = comparableModelLabel(identity.backend);
|
|
937
1015
|
const aliases = exact ? [] : models.filter((model) => (
|
|
@@ -939,13 +1017,25 @@ export function createInjectionSource(features, {
|
|
|
939
1017
|
));
|
|
940
1018
|
const model = exact ?? (aliases.length === 1 ? aliases[0] : null);
|
|
941
1019
|
if (!model) return null;
|
|
1020
|
+
const nativeReasoningLevels = (model.supportedReasoningEfforts ?? []).map((level) => ({
|
|
1021
|
+
effort: String(level?.reasoningEffort ?? level?.effort ?? level ?? "").toLowerCase(),
|
|
1022
|
+
})).filter((level) => level.effort);
|
|
1023
|
+
const allowed = selector?.reasoningEffortOverrides?.[identity.raw.toLowerCase()];
|
|
1024
|
+
const supportedReasoningLevels = allowed
|
|
1025
|
+
? nativeReasoningLevels.filter((level) => allowed.includes(level.effort))
|
|
1026
|
+
: nativeReasoningLevels;
|
|
1027
|
+
const currentReasoningLevel = String(controller?.reasoningEffort ?? "").toLowerCase();
|
|
942
1028
|
return {
|
|
943
1029
|
slug: String(model.model ?? ""),
|
|
944
1030
|
displayName: String(model.displayName ?? model.model ?? ""),
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1031
|
+
currentReasoningLevel: nativeReasoningLevels.some(
|
|
1032
|
+
(level) => level.effort === currentReasoningLevel,
|
|
1033
|
+
) ? currentReasoningLevel : null,
|
|
1034
|
+
currentReasoningLevelVisible: supportedReasoningLevels.some(
|
|
1035
|
+
(level) => level.effort === currentReasoningLevel,
|
|
1036
|
+
),
|
|
1037
|
+
defaultReasoningLevel: String(model.defaultReasoningEffort ?? "").toLowerCase(),
|
|
1038
|
+
supportedReasoningLevels,
|
|
949
1039
|
};
|
|
950
1040
|
}
|
|
951
1041
|
|
|
@@ -1003,6 +1093,19 @@ export function createInjectionSource(features, {
|
|
|
1003
1093
|
return formatIdentifier(identity.backend).toLowerCase() === nativeLabel ? native : thirdParty;
|
|
1004
1094
|
}
|
|
1005
1095
|
|
|
1096
|
+
function visibleReasoningLevelsFor(model) {
|
|
1097
|
+
if (!model) return [];
|
|
1098
|
+
const capability = model.modelSelectorCapability;
|
|
1099
|
+
if (capability && Array.isArray(capability.levels)) {
|
|
1100
|
+
const allowed = new Set(capability.levels);
|
|
1101
|
+
return (model.supportedReasoningLevels ?? []).filter((level) => {
|
|
1102
|
+
const effort = String(level?.effort ?? level ?? "").toLowerCase();
|
|
1103
|
+
return allowed.has(effort);
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
return model.supportedReasoningLevels ?? [];
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1006
1109
|
function loadModelFavorites(selector) {
|
|
1007
1110
|
try {
|
|
1008
1111
|
const value = JSON.parse(localStorage.getItem(selector.favoriteStorageKey) ?? "[]");
|
|
@@ -1591,6 +1694,9 @@ export function createInjectionSource(features, {
|
|
|
1591
1694
|
}
|
|
1592
1695
|
|
|
1593
1696
|
function selectNativeEffort(parentMenu, effort) {
|
|
1697
|
+
const identity = modelIdentity(nativeModelValue(parentMenu));
|
|
1698
|
+
const model = modelDefinitionFor(identity, modelSelectorFeature?.modelSelector);
|
|
1699
|
+
if (!model?.supportedReasoningLevels?.some((level) => level.effort === String(effort).toLowerCase())) return;
|
|
1594
1700
|
const controller = nativeComposerModelController();
|
|
1595
1701
|
if (typeof controller?.onSelectReasoningEffort === "function") {
|
|
1596
1702
|
controller.onSelectReasoningEffort(effort);
|
|
@@ -1666,6 +1772,24 @@ export function createInjectionSource(features, {
|
|
|
1666
1772
|
rail.setAttribute("aria-valuenow", String(index));
|
|
1667
1773
|
}
|
|
1668
1774
|
|
|
1775
|
+
function neutralizeStaleSliderVisual(rail) {
|
|
1776
|
+
for (const selector of [
|
|
1777
|
+
"[data-codex-model-slider-native-range]",
|
|
1778
|
+
"[data-codex-model-slider-native-thumb]",
|
|
1779
|
+
"[data-codex-model-slider-range]",
|
|
1780
|
+
"[data-codex-model-slider-thumb]",
|
|
1781
|
+
]) {
|
|
1782
|
+
rail.querySelectorAll(selector).forEach((element) => {
|
|
1783
|
+
element.style.setProperty("visibility", "hidden");
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
rail.querySelectorAll("[data-codex-model-slider-tick]").forEach((tick) => {
|
|
1787
|
+
tick.removeAttribute("data-selected");
|
|
1788
|
+
});
|
|
1789
|
+
rail.removeAttribute("aria-valuenow");
|
|
1790
|
+
rail.style.removeProperty("--codex-model-slider-progress");
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1669
1793
|
function nativeParticleSeed(index, salt) {
|
|
1670
1794
|
const value = Math.sin((index + 1) * 12.9898 + salt * 78.233) * 43758.5453;
|
|
1671
1795
|
return value - Math.floor(value);
|
|
@@ -1792,7 +1916,8 @@ export function createInjectionSource(features, {
|
|
|
1792
1916
|
|
|
1793
1917
|
function createGenericModelSlider(parentMenu, selector, identity, model, selectedEffort) {
|
|
1794
1918
|
const efforts = model.supportedReasoningLevels.map((level) => level.effort);
|
|
1795
|
-
const selectedIndex =
|
|
1919
|
+
const selectedIndex = efforts.indexOf(selectedEffort);
|
|
1920
|
+
const selectedEffortVisible = selectedIndex >= 0;
|
|
1796
1921
|
const classes = Object.fromEntries([
|
|
1797
1922
|
"Menu",
|
|
1798
1923
|
"ViewToggle",
|
|
@@ -1890,7 +2015,20 @@ export function createInjectionSource(features, {
|
|
|
1890
2015
|
rail.setAttribute("aria-label", locale === "zh-CN" ? "推理强度" : "Reasoning effort");
|
|
1891
2016
|
rail.setAttribute("aria-valuemin", "0");
|
|
1892
2017
|
rail.setAttribute("aria-valuemax", String(efforts.length - 1));
|
|
1893
|
-
rail.setAttribute("aria-valuetext",
|
|
2018
|
+
rail.setAttribute("aria-valuetext", selectedEffortVisible
|
|
2019
|
+
? effortLabel(selectedEffort)
|
|
2020
|
+
: (locale === "zh-CN" ? "当前档位不可用" : "Current effort unavailable"));
|
|
2021
|
+
rail.toggleAttribute("data-stale-reasoning-effort", !selectedEffortVisible);
|
|
2022
|
+
if (!selectedEffortVisible) {
|
|
2023
|
+
rail.setAttribute("aria-disabled", "true");
|
|
2024
|
+
rail.removeAttribute("tabindex");
|
|
2025
|
+
neutralizeStaleSliderVisual(rail);
|
|
2026
|
+
const staleView = document.createElement("div");
|
|
2027
|
+
staleView.className = classes.SimpleView;
|
|
2028
|
+
staleView.append(nativeSlider ?? rail);
|
|
2029
|
+
shell.append(viewControls, staleView);
|
|
2030
|
+
return shell;
|
|
2031
|
+
}
|
|
1894
2032
|
if (!nativeSlider) {
|
|
1895
2033
|
const track = document.createElement("div");
|
|
1896
2034
|
track.setAttribute("data-codex-model-slider-track", "");
|
|
@@ -1981,19 +2119,35 @@ export function createInjectionSource(features, {
|
|
|
1981
2119
|
function enhanceGenericModelPicker(parentMenu, selector) {
|
|
1982
2120
|
const identity = modelIdentity(nativeModelValue(parentMenu));
|
|
1983
2121
|
const model = modelDefinitionFor(identity, selector);
|
|
1984
|
-
const
|
|
2122
|
+
const catalogModel = catalogModelFor(identity, selector);
|
|
2123
|
+
const modelWithCapability = model?.modelSelectorCapability ? model : {
|
|
2124
|
+
...(model ?? {}),
|
|
2125
|
+
...(catalogModel ?? {}),
|
|
2126
|
+
supportedReasoningLevels: model?.supportedReasoningLevels ?? catalogModel?.supportedReasoningLevels ?? [],
|
|
2127
|
+
};
|
|
2128
|
+
const efforts = visibleReasoningLevelsFor(modelWithCapability);
|
|
1985
2129
|
if (!model || efforts.length < 2) return false;
|
|
1986
2130
|
// 控制器推导的任务内实际 effort 优先,避免子任务/主任务切换后触发器属性滞后;
|
|
1987
2131
|
// 触发器属性仅在控制器无法解析时作为实时回退,静态目录默认值最后兜底。
|
|
1988
2132
|
const nativeModel = nativeCatalogModelFor(identity);
|
|
1989
|
-
const selectedEffort = nativeModel?.
|
|
2133
|
+
const selectedEffort = nativeModel?.currentReasoningLevel
|
|
1990
2134
|
?? document.querySelector("[data-codex-intelligence-trigger]")
|
|
1991
2135
|
?.getAttribute("data-selected-reasoning-effort")
|
|
1992
2136
|
?? model.defaultReasoningLevel
|
|
1993
2137
|
?? efforts[0].effort;
|
|
2138
|
+
const effortRank = (value) => {
|
|
2139
|
+
const order = ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
2140
|
+
const index = order.indexOf(String(value ?? "").toLowerCase());
|
|
2141
|
+
return index < 0 ? -1 : index;
|
|
2142
|
+
};
|
|
2143
|
+
const visibleEffort = efforts.find((level) => level.effort === selectedEffort)?.effort
|
|
2144
|
+
?? efforts.filter((level) => effortRank(level.effort) <= effortRank(selectedEffort))
|
|
2145
|
+
.sort((left, right) => effortRank(right.effort) - effortRank(left.effort))[0]?.effort
|
|
2146
|
+
?? efforts[0].effort;
|
|
2147
|
+
const selectedEffortValue = visibleEffort;
|
|
1994
2148
|
const selectedServiceTier = nativeComposerModelController()?.selectedServiceTier;
|
|
1995
2149
|
const serviceTierKey = selectedServiceTier?.id ?? selectedServiceTier ?? "standard";
|
|
1996
|
-
const key = `${identity.raw}:${
|
|
2150
|
+
const key = `${identity.raw}:${selectedEffortValue}:${serviceTierKey}:${efforts.map((level) => level.effort).join(",")}`;
|
|
1997
2151
|
const existing = parentMenu.querySelector(":scope > [data-codex-model-slider-generic]");
|
|
1998
2152
|
const nativeTemplateReady = Boolean(
|
|
1999
2153
|
parentMenu.firstElementChild
|
|
@@ -2015,7 +2169,7 @@ export function createInjectionSource(features, {
|
|
|
2015
2169
|
existing?.remove();
|
|
2016
2170
|
const nativeContainer = parentMenu.firstElementChild;
|
|
2017
2171
|
if (!nativeContainer) return false;
|
|
2018
|
-
const shell = createGenericModelSlider(parentMenu, selector, identity, model,
|
|
2172
|
+
const shell = createGenericModelSlider(parentMenu, selector, identity, model, selectedEffortValue);
|
|
2019
2173
|
if (!shell) return false;
|
|
2020
2174
|
if (!modelSelectorGenericContainers.has(nativeContainer)) {
|
|
2021
2175
|
modelSelectorGenericContainers.set(nativeContainer, nativeContainer.style.display);
|
|
@@ -2357,9 +2511,11 @@ export function createInjectionSource(features, {
|
|
|
2357
2511
|
}
|
|
2358
2512
|
if (next === currentThread) return;
|
|
2359
2513
|
currentThread = next;
|
|
2514
|
+
stopToolbarReadiness();
|
|
2360
2515
|
savedComposerRange = null;
|
|
2361
2516
|
savedComposerThread = null;
|
|
2362
2517
|
hidePinnedSurfaces();
|
|
2518
|
+
queueEnsure();
|
|
2363
2519
|
}
|
|
2364
2520
|
|
|
2365
2521
|
function currentComposer() {
|
|
@@ -2560,24 +2716,23 @@ export function createInjectionSource(features, {
|
|
|
2560
2716
|
}
|
|
2561
2717
|
}
|
|
2562
2718
|
|
|
2563
|
-
function threadScrollContent() {
|
|
2564
|
-
const scrollContainer = mainContentViewport()?.querySelector(".thread-scroll-container");
|
|
2565
|
-
return scrollContainer?.firstElementChild instanceof HTMLElement
|
|
2566
|
-
? scrollContainer.firstElementChild
|
|
2567
|
-
: null;
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
2719
|
function restoreThreadContentShift() {
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2720
|
+
for (const { element, shift, translate } of shiftedConversationOwners) {
|
|
2721
|
+
if (!element.isConnected) continue;
|
|
2722
|
+
if (shift.value) {
|
|
2723
|
+
element.style.setProperty(
|
|
2724
|
+
"--thread-wide-block-inline-shift",
|
|
2725
|
+
shift.value,
|
|
2726
|
+
shift.priority,
|
|
2727
|
+
);
|
|
2728
|
+
} else {
|
|
2729
|
+
element.style.removeProperty("--thread-wide-block-inline-shift");
|
|
2730
|
+
}
|
|
2731
|
+
if (translate.value) element.style.setProperty("translate", translate.value, translate.priority);
|
|
2732
|
+
else element.style.removeProperty("translate");
|
|
2577
2733
|
element.removeAttribute("data-codex-personal-summary-shift");
|
|
2578
2734
|
}
|
|
2579
|
-
|
|
2580
|
-
shiftedThreadOriginal = null;
|
|
2735
|
+
shiftedConversationOwners = [];
|
|
2581
2736
|
}
|
|
2582
2737
|
|
|
2583
2738
|
function applyThreadContentShift(value) {
|
|
@@ -2585,45 +2740,40 @@ export function createInjectionSource(features, {
|
|
|
2585
2740
|
restoreThreadContentShift();
|
|
2586
2741
|
return;
|
|
2587
2742
|
}
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
if (!
|
|
2743
|
+
const viewport = mainContentViewport();
|
|
2744
|
+
const owners = viewport
|
|
2745
|
+
? Array.from(viewport.querySelectorAll('[style*="--thread-wide-block-inline-shift"]'))
|
|
2746
|
+
: [];
|
|
2747
|
+
if (!owners.length) {
|
|
2593
2748
|
restoreThreadContentShift();
|
|
2594
2749
|
return;
|
|
2595
2750
|
}
|
|
2596
|
-
if (
|
|
2751
|
+
if (
|
|
2752
|
+
shiftedConversationOwners.length !== owners.length
|
|
2753
|
+
|| owners.some((owner, index) => shiftedConversationOwners[index]?.element !== owner)
|
|
2754
|
+
) {
|
|
2597
2755
|
restoreThreadContentShift();
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
priority: content.style.getPropertyPriority("position"),
|
|
2604
|
-
},
|
|
2605
|
-
left: {
|
|
2606
|
-
value: content.style.getPropertyValue("left"),
|
|
2607
|
-
priority: content.style.getPropertyPriority("left"),
|
|
2756
|
+
shiftedConversationOwners = owners.map((owner) => ({
|
|
2757
|
+
element: owner,
|
|
2758
|
+
shift: {
|
|
2759
|
+
value: owner.style.getPropertyValue("--thread-wide-block-inline-shift"),
|
|
2760
|
+
priority: owner.style.getPropertyPriority("--thread-wide-block-inline-shift"),
|
|
2608
2761
|
},
|
|
2609
|
-
|
|
2610
|
-
value:
|
|
2611
|
-
priority:
|
|
2762
|
+
translate: {
|
|
2763
|
+
value: owner.style.getPropertyValue("translate"),
|
|
2764
|
+
priority: owner.style.getPropertyPriority("translate"),
|
|
2612
2765
|
},
|
|
2613
|
-
};
|
|
2766
|
+
}));
|
|
2767
|
+
}
|
|
2768
|
+
for (const owner of owners) {
|
|
2769
|
+
owner.style.setProperty(
|
|
2770
|
+
"--thread-wide-block-inline-shift",
|
|
2771
|
+
`${Number(value)}px`,
|
|
2772
|
+
"important",
|
|
2773
|
+
);
|
|
2774
|
+
owner.style.setProperty("translate", `${Number(value)}px 0`, "important");
|
|
2775
|
+
owner.setAttribute("data-codex-personal-summary-shift", String(value));
|
|
2614
2776
|
}
|
|
2615
|
-
const duration = reducedMotion() ? 0 : 300;
|
|
2616
|
-
content.style.setProperty("transition", duration
|
|
2617
|
-
? "left 300ms cubic-bezier(.2,.8,.2,1)"
|
|
2618
|
-
: "none", "important");
|
|
2619
|
-
content.style.setProperty("position", "relative", "important");
|
|
2620
|
-
content.style.setProperty("left", `${value}px`, "important");
|
|
2621
|
-
content.setAttribute("data-codex-personal-summary-shift", String(value));
|
|
2622
|
-
if (duration) window.setTimeout(() => {
|
|
2623
|
-
if (content.getAttribute("data-codex-personal-summary-shift") !== String(value)) return;
|
|
2624
|
-
content.style.setProperty("transition", "none", "important");
|
|
2625
|
-
content.style.setProperty("left", `${value}px`, "important");
|
|
2626
|
-
}, duration + 50);
|
|
2627
2777
|
}
|
|
2628
2778
|
|
|
2629
2779
|
function setSummarySurfaceVisible(surface, visible, mode) {
|
|
@@ -2728,7 +2878,7 @@ export function createInjectionSource(features, {
|
|
|
2728
2878
|
surface.setAttribute("data-codex-personal-summary-presentation", displayMode);
|
|
2729
2879
|
setSummarySurfaceVisible(surface, true, displayMode);
|
|
2730
2880
|
|
|
2731
|
-
const shift = displayMode
|
|
2881
|
+
const shift = displayMode !== "overlay"
|
|
2732
2882
|
? -(layout.panelWidth + layout.panelInset) / 2
|
|
2733
2883
|
: 0;
|
|
2734
2884
|
applyThreadContentShift(shift);
|
|
@@ -3373,8 +3523,12 @@ export function createInjectionSource(features, {
|
|
|
3373
3523
|
const detail = feature?.detailTabs?.find((candidate) => candidate.id === detailId);
|
|
3374
3524
|
if (!feature || !detail) throw new Error("The requested detail Tab is not registered");
|
|
3375
3525
|
const result = await openNativeDetailTab(feature, detail);
|
|
3376
|
-
if (!result)
|
|
3377
|
-
|
|
3526
|
+
if (!result) {
|
|
3527
|
+
const error = nativeTabCapabilityError
|
|
3528
|
+
?? new Error("Codex native right-panel tabs are unavailable");
|
|
3529
|
+
error.code = "NATIVE_TAB_UNAVAILABLE";
|
|
3530
|
+
throw error;
|
|
3531
|
+
}
|
|
3378
3532
|
return result;
|
|
3379
3533
|
}
|
|
3380
3534
|
|
|
@@ -3489,13 +3643,20 @@ export function createInjectionSource(features, {
|
|
|
3489
3643
|
}
|
|
3490
3644
|
|
|
3491
3645
|
function ensureToolbarEntries() {
|
|
3492
|
-
if (!toolbarFeatures.length)
|
|
3646
|
+
if (!toolbarFeatures.length) {
|
|
3647
|
+
stopToolbarReadiness();
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3493
3650
|
const temporaryChatButton = nativeTemporaryChatButton();
|
|
3494
3651
|
const summaryButton = nativeSummaryButton();
|
|
3495
3652
|
const bottomPanelButton = nativeBottomPanelButton();
|
|
3496
3653
|
const sidePanelButton = nativeSidePanelButton();
|
|
3497
3654
|
const anchorButton = temporaryChatButton ?? summaryButton ?? bottomPanelButton ?? sidePanelButton;
|
|
3498
|
-
if (!anchorButton)
|
|
3655
|
+
if (!anchorButton) {
|
|
3656
|
+
const candidate = structuralToolbarAnchor();
|
|
3657
|
+
if (candidate) ensureToolbarReadiness(candidate);
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
3499
3660
|
const anchorRoot = toolbarControlRoot(anchorButton);
|
|
3500
3661
|
// The bottom/side-panel toggles sit in narrow fixed containers that
|
|
3501
3662
|
// travel with the native panels. Entries anchored to them must live in
|
|
@@ -3506,7 +3667,10 @@ export function createInjectionSource(features, {
|
|
|
3506
3667
|
const useFallbackAnchor = anchorButton === sidePanelButton || anchorButton === bottomPanelButton;
|
|
3507
3668
|
const summaryGroup = useFallbackAnchor ? null : nativeSummaryToolbarGroup(anchorButton);
|
|
3508
3669
|
let targetGroup = summaryGroup ?? ensureFallbackSummaryToolbarGroup(anchorButton);
|
|
3509
|
-
if (!targetGroup)
|
|
3670
|
+
if (!targetGroup) {
|
|
3671
|
+
ensureToolbarReadiness(anchorButton);
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3510
3674
|
let cursor = useFallbackAnchor ? null : anchorRoot;
|
|
3511
3675
|
for (const feature of toolbarFeatures) {
|
|
3512
3676
|
const entries = Array.from(document.querySelectorAll(`[${entryMarker}="${feature.id}"]`));
|
|
@@ -3542,6 +3706,11 @@ export function createInjectionSource(features, {
|
|
|
3542
3706
|
if (!used) group.remove();
|
|
3543
3707
|
});
|
|
3544
3708
|
}
|
|
3709
|
+
if (toolbarFeatures.every((feature) => findEntry(feature.id)?.isConnected)) {
|
|
3710
|
+
stopToolbarReadiness();
|
|
3711
|
+
} else {
|
|
3712
|
+
ensureToolbarReadiness(anchorButton);
|
|
3713
|
+
}
|
|
3545
3714
|
}
|
|
3546
3715
|
|
|
3547
3716
|
function ensureEntries() {
|
|
@@ -3600,6 +3769,13 @@ export function createInjectionSource(features, {
|
|
|
3600
3769
|
}, record.origin);
|
|
3601
3770
|
}
|
|
3602
3771
|
|
|
3772
|
+
function hostActionFailure(error) {
|
|
3773
|
+
return {
|
|
3774
|
+
code: typeof error?.code === "string" ? error.code : "HOST_ACTION_FAILED",
|
|
3775
|
+
message: error?.message ?? "Host action failed",
|
|
3776
|
+
};
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3603
3779
|
async function handleSurfaceHostAction(record, message) {
|
|
3604
3780
|
const feature = featureById.get(record.featureId);
|
|
3605
3781
|
const { requestId, action, payload = {} } = message;
|
|
@@ -3663,7 +3839,7 @@ export function createInjectionSource(features, {
|
|
|
3663
3839
|
} catch (error) {
|
|
3664
3840
|
respondToSurface(record, requestId, {
|
|
3665
3841
|
ok: false,
|
|
3666
|
-
error:
|
|
3842
|
+
error: hostActionFailure(error),
|
|
3667
3843
|
});
|
|
3668
3844
|
}
|
|
3669
3845
|
}
|
|
@@ -3831,9 +4007,9 @@ export function createInjectionSource(features, {
|
|
|
3831
4007
|
if (ensureFrame != null) cancelAnimationFrame(ensureFrame);
|
|
3832
4008
|
if (ensureTimer != null) clearTimeout(ensureTimer);
|
|
3833
4009
|
for (const observer of domObservers) observer.disconnect();
|
|
4010
|
+
stopToolbarReadiness();
|
|
3834
4011
|
mainContentObserver?.observer.disconnect();
|
|
3835
4012
|
restoreThreadContentShift();
|
|
3836
|
-
threadShiftStyle.remove();
|
|
3837
4013
|
for (const root of document.querySelectorAll("[data-codex-personal-toolbar-entry]")) root.remove();
|
|
3838
4014
|
for (const group of document.querySelectorAll("[data-codex-personal-summary-toolbar-group]")) group.remove();
|
|
3839
4015
|
for (const surface of pageSurfaces.values()) surface.remove();
|
|
@@ -3864,7 +4040,15 @@ export function createInjectionSource(features, {
|
|
|
3864
4040
|
detailTabs: feature.detailTabs ?? [],
|
|
3865
4041
|
hostActions: feature.hostActions ?? [],
|
|
3866
4042
|
modelSelector: feature.modelSelector ?? null,
|
|
3867
|
-
pageScript: feature.pageScript
|
|
4043
|
+
pageScript: feature.pageScript ? {
|
|
4044
|
+
...feature.pageScript,
|
|
4045
|
+
config: {
|
|
4046
|
+
serviceOrigin: pageScriptServiceOrigin(feature),
|
|
4047
|
+
bindingName,
|
|
4048
|
+
bindingToken,
|
|
4049
|
+
featureId: feature.id,
|
|
4050
|
+
},
|
|
4051
|
+
} : null,
|
|
3868
4052
|
}));
|
|
3869
4053
|
return `(${install.toString()})(${JSON.stringify({
|
|
3870
4054
|
version: RUNTIME_VERSION,
|
|
@@ -3886,7 +4070,9 @@ export function createRuntimeReadyContract({
|
|
|
3886
4070
|
runtimeSessionId,
|
|
3887
4071
|
}) {
|
|
3888
4072
|
const discovered = Boolean(renderer.discovered);
|
|
4073
|
+
const discoveredTargets = Math.max(0, Number(renderer.discoveredTargets) || 0);
|
|
3889
4074
|
const injectedTargets = Math.max(0, Number(renderer.injectedTargets) || 0);
|
|
4075
|
+
const targetFailures = Array.isArray(renderer.targetFailures) ? renderer.targetFailures : [];
|
|
3890
4076
|
const active = Boolean(renderer.active) && discovered && injectedTargets > 0;
|
|
3891
4077
|
return {
|
|
3892
4078
|
pid,
|
|
@@ -3904,8 +4090,11 @@ export function createRuntimeReadyContract({
|
|
|
3904
4090
|
})),
|
|
3905
4091
|
renderer: {
|
|
3906
4092
|
discovered,
|
|
4093
|
+
discoveredTargets,
|
|
3907
4094
|
injectedTargets,
|
|
3908
4095
|
active,
|
|
4096
|
+
healthy: active && targetFailures.length === 0 && injectedTargets === discoveredTargets,
|
|
4097
|
+
targetFailures,
|
|
3909
4098
|
runtimeSessionId: renderer.runtimeSessionId ?? runtimeSessionId,
|
|
3910
4099
|
},
|
|
3911
4100
|
};
|
|
@@ -4106,6 +4295,8 @@ export class CodexRuntime {
|
|
|
4106
4295
|
this.onStatusChange = onStatusChange;
|
|
4107
4296
|
this.onManagedCodexPidChange = onManagedCodexPidChange;
|
|
4108
4297
|
this.injectedTargets = new Set();
|
|
4298
|
+
this.rendererTargetIds = new Set();
|
|
4299
|
+
this.targetFailures = new Map();
|
|
4109
4300
|
this.clients = new Map();
|
|
4110
4301
|
this.scriptIds = new Map();
|
|
4111
4302
|
this.bindingTokens = new Map();
|
|
@@ -4159,10 +4350,21 @@ export class CodexRuntime {
|
|
|
4159
4350
|
|
|
4160
4351
|
getRendererStatus() {
|
|
4161
4352
|
const injectedTargets = this.injectedTargets.size;
|
|
4353
|
+
const discoveredTargets = this.rendererTargetIds.size;
|
|
4354
|
+
const targetFailures = Array.from(this.targetFailures, ([targetId, failure]) => ({
|
|
4355
|
+
targetId,
|
|
4356
|
+
attempts: failure.attempts,
|
|
4357
|
+
state: "retrying",
|
|
4358
|
+
lastFailure: failure.lastFailure,
|
|
4359
|
+
})).sort((left, right) => left.targetId.localeCompare(right.targetId));
|
|
4360
|
+
const active = !this.stopping && this.rendererDiscovered && injectedTargets > 0;
|
|
4162
4361
|
return {
|
|
4163
4362
|
discovered: this.rendererDiscovered,
|
|
4363
|
+
discoveredTargets,
|
|
4164
4364
|
injectedTargets,
|
|
4165
|
-
active
|
|
4365
|
+
active,
|
|
4366
|
+
healthy: active && targetFailures.length === 0 && injectedTargets === discoveredTargets,
|
|
4367
|
+
targetFailures,
|
|
4166
4368
|
runtimeSessionId: this.runtimeSessionId,
|
|
4167
4369
|
};
|
|
4168
4370
|
}
|
|
@@ -4173,8 +4375,11 @@ export class CodexRuntime {
|
|
|
4173
4375
|
if (
|
|
4174
4376
|
previous
|
|
4175
4377
|
&& previous.discovered === status.discovered
|
|
4378
|
+
&& previous.discoveredTargets === status.discoveredTargets
|
|
4176
4379
|
&& previous.injectedTargets === status.injectedTargets
|
|
4177
4380
|
&& previous.active === status.active
|
|
4381
|
+
&& previous.healthy === status.healthy
|
|
4382
|
+
&& JSON.stringify(previous.targetFailures) === JSON.stringify(status.targetFailures)
|
|
4178
4383
|
&& previous.runtimeSessionId === status.runtimeSessionId
|
|
4179
4384
|
) return;
|
|
4180
4385
|
this.lastRendererStatus = status;
|
|
@@ -4218,6 +4423,8 @@ export class CodexRuntime {
|
|
|
4218
4423
|
this.bindingTokens.clear();
|
|
4219
4424
|
this.hostActionChains.clear();
|
|
4220
4425
|
this.injectedTargets.clear();
|
|
4426
|
+
this.rendererTargetIds.clear();
|
|
4427
|
+
this.targetFailures.clear();
|
|
4221
4428
|
this.rendererDiscovered = false;
|
|
4222
4429
|
this.browserClient?.socket.close();
|
|
4223
4430
|
this.browserClient = null;
|
|
@@ -4581,6 +4788,10 @@ export class CodexRuntime {
|
|
|
4581
4788
|
});
|
|
4582
4789
|
return;
|
|
4583
4790
|
}
|
|
4791
|
+
if (request.action === "import-generated-image") {
|
|
4792
|
+
await this.importGeneratedImage(feature, request.payload);
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4584
4795
|
try {
|
|
4585
4796
|
if (request.action !== "attach-file") throw new Error("Unsupported native Host action");
|
|
4586
4797
|
const result = await this.attachFileToComposer(client, request.payload?.path, request.requestId);
|
|
@@ -4593,6 +4804,22 @@ export class CodexRuntime {
|
|
|
4593
4804
|
}
|
|
4594
4805
|
}
|
|
4595
4806
|
|
|
4807
|
+
async importGeneratedImage(feature, payload) {
|
|
4808
|
+
const origin = pageScriptServiceOrigin(feature);
|
|
4809
|
+
if (!origin) throw new Error("Generated image import service is unavailable");
|
|
4810
|
+
const response = await this.fetchImpl(new URL("/api/import-generated-image", origin), {
|
|
4811
|
+
method: "POST",
|
|
4812
|
+
headers: {
|
|
4813
|
+
"Content-Type": "application/json",
|
|
4814
|
+
Origin: CODEX_APP_ORIGIN,
|
|
4815
|
+
},
|
|
4816
|
+
body: JSON.stringify(payload),
|
|
4817
|
+
});
|
|
4818
|
+
if (!response?.ok) {
|
|
4819
|
+
throw new Error(`Generated image import returned HTTP ${response?.status ?? "error"}`);
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
|
|
4596
4823
|
async refreshTargets() {
|
|
4597
4824
|
let response;
|
|
4598
4825
|
try {
|
|
@@ -4619,8 +4846,14 @@ export class CodexRuntime {
|
|
|
4619
4846
|
.filter((target) => target.type === "page" && target.url === "app://-/index.html")
|
|
4620
4847
|
.map((target) => target.id),
|
|
4621
4848
|
);
|
|
4849
|
+
this.rendererTargetIds = liveRendererIds;
|
|
4622
4850
|
let removedTarget = false;
|
|
4623
|
-
|
|
4851
|
+
const trackedTargetIds = new Set([
|
|
4852
|
+
...this.injectedTargets,
|
|
4853
|
+
...this.clients.keys(),
|
|
4854
|
+
...this.targetFailures.keys(),
|
|
4855
|
+
]);
|
|
4856
|
+
for (const id of trackedTargetIds) {
|
|
4624
4857
|
if (liveRendererIds.has(id)) continue;
|
|
4625
4858
|
removedTarget = true;
|
|
4626
4859
|
this.clients.get(id)?.socket.close();
|
|
@@ -4629,6 +4862,7 @@ export class CodexRuntime {
|
|
|
4629
4862
|
this.bindingTokens.delete(id);
|
|
4630
4863
|
this.hostActionChains.delete(id);
|
|
4631
4864
|
this.injectedTargets.delete(id);
|
|
4865
|
+
this.targetFailures.delete(id);
|
|
4632
4866
|
}
|
|
4633
4867
|
const rendererDiscovered = liveRendererIds.size > 0;
|
|
4634
4868
|
const rendererChanged = rendererDiscovered !== this.rendererDiscovered;
|
|
@@ -4639,8 +4873,12 @@ export class CodexRuntime {
|
|
|
4639
4873
|
if (target.type !== "page" || target.url !== "app://-/index.html" || this.injectedTargets.has(target.id)) {
|
|
4640
4874
|
continue;
|
|
4641
4875
|
}
|
|
4642
|
-
const
|
|
4876
|
+
const previousFailure = this.targetFailures.get(target.id);
|
|
4877
|
+
if (previousFailure && Date.now() < previousFailure.nextRetryAt) continue;
|
|
4878
|
+
let client;
|
|
4879
|
+
let scriptIdentifier = null;
|
|
4643
4880
|
try {
|
|
4881
|
+
client = await this.connectClient(target.webSocketDebuggerUrl);
|
|
4644
4882
|
await Promise.all([
|
|
4645
4883
|
client.send("Page.enable"),
|
|
4646
4884
|
client.send("Runtime.enable"),
|
|
@@ -4662,6 +4900,7 @@ export class CodexRuntime {
|
|
|
4662
4900
|
const script = await client.send("Page.addScriptToEvaluateOnNewDocument", {
|
|
4663
4901
|
source: createDocumentBootstrapSource(source),
|
|
4664
4902
|
});
|
|
4903
|
+
scriptIdentifier = script.identifier;
|
|
4665
4904
|
await waitForExpression(
|
|
4666
4905
|
client,
|
|
4667
4906
|
`document.readyState === "interactive" || document.readyState === "complete"`,
|
|
@@ -4674,15 +4913,33 @@ export class CodexRuntime {
|
|
|
4674
4913
|
+ ` && window.__codexPersonalRuntime?.sessionId === ${JSON.stringify(this.runtimeSessionId)}`,
|
|
4675
4914
|
);
|
|
4676
4915
|
this.clients.set(target.id, client);
|
|
4677
|
-
this.scriptIds.set(target.id,
|
|
4916
|
+
this.scriptIds.set(target.id, scriptIdentifier);
|
|
4678
4917
|
this.injectedTargets.add(target.id);
|
|
4918
|
+
this.targetFailures.delete(target.id);
|
|
4679
4919
|
this.notifyStatusChange();
|
|
4680
4920
|
} catch (error) {
|
|
4681
4921
|
this.bindingTokens.delete(target.id);
|
|
4682
|
-
client
|
|
4683
|
-
|
|
4922
|
+
if (client) {
|
|
4923
|
+
if (scriptIdentifier) {
|
|
4924
|
+
await client.send("Page.removeScriptToEvaluateOnNewDocument", {
|
|
4925
|
+
identifier: scriptIdentifier,
|
|
4926
|
+
}).catch(() => {});
|
|
4927
|
+
}
|
|
4928
|
+
await client.send("Runtime.removeBinding", { name: HOST_BINDING_NAME }).catch(() => {});
|
|
4929
|
+
client.socket.close();
|
|
4930
|
+
}
|
|
4931
|
+
const attempts = (previousFailure?.attempts ?? 0) + 1;
|
|
4932
|
+
const retryDelay = Math.min(5_000, 250 * (2 ** Math.min(attempts - 1, 5)));
|
|
4933
|
+
this.targetFailures.set(target.id, {
|
|
4934
|
+
attempts,
|
|
4935
|
+
lastFailure: error.message,
|
|
4936
|
+
nextRetryAt: Date.now() + retryDelay,
|
|
4937
|
+
});
|
|
4938
|
+
this.logger.warn?.(`Renderer injection failed for ${target.id}; retrying`, error.message);
|
|
4939
|
+
this.notifyStatusChange();
|
|
4684
4940
|
}
|
|
4685
4941
|
}
|
|
4942
|
+
this.notifyStatusChange();
|
|
4686
4943
|
}
|
|
4687
4944
|
|
|
4688
4945
|
async stop() {
|
|
@@ -4712,6 +4969,8 @@ export class CodexRuntime {
|
|
|
4712
4969
|
this.bindingTokens.clear();
|
|
4713
4970
|
this.hostActionChains.clear();
|
|
4714
4971
|
this.injectedTargets.clear();
|
|
4972
|
+
this.rendererTargetIds.clear();
|
|
4973
|
+
this.targetFailures.clear();
|
|
4715
4974
|
this.rendererDiscovered = false;
|
|
4716
4975
|
this.notifyStatusChange();
|
|
4717
4976
|
this.monitorPromise = null;
|