codex-chatgpt-control 0.5.0-alpha.1 → 0.5.1-alpha.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/CHANGELOG.md +22 -0
- package/contracts/v1/fixtures/backend-capabilities.json +1 -0
- package/contracts/v1/fixtures/command-descriptors.json +29 -2
- package/contracts/v1/fixtures/doctor-bridge-upload.json +2 -2
- package/contracts/v1/fixtures/doctor-scenario-preflight.json +26 -4
- package/contracts/v1/fixtures/files-attach-indeterminate.json +30 -0
- package/contracts/v1/fixtures/help-root.json +1 -1
- package/contracts/v1/fixtures/messages-stop-indeterminate.json +31 -0
- package/contracts/v1/fixtures/messages-stop-needs-confirmation.json +20 -0
- package/contracts/v1/fixtures/messages-stop-noop.json +19 -0
- package/contracts/v1/fixtures/messages-stop-success.json +21 -0
- package/contracts/v1/fixtures/primitive-bootstrap-blocker.json +3 -3
- package/contracts/v1/fixtures/reports-create-redacted.json +3 -3
- package/contracts/v1/fixtures/reports-summarize-redacted.json +1 -1
- package/contracts/v1/fixtures/run-browser-bridge-blocker.json +12 -12
- package/contracts/v1/fixtures/stream-blocked.ndjson +2 -2
- package/contracts/v1/manifest.json +25 -0
- package/contracts/v1/parity-suite.json +19 -1
- package/dist/codex-chatgpt-control-backend.mjs +3654 -603
- package/dist/codex-chatgpt-control.bundle.mjs +3627 -546
- package/dist/src/backend/client.d.ts +2 -1
- package/dist/src/backend/client.js +1 -0
- package/dist/src/backend/protocol.d.ts +1 -1
- package/dist/src/backend/protocol.js +1 -0
- package/dist/src/backend/session.js +2 -0
- package/dist/src/browser/attach.d.ts +1 -0
- package/dist/src/browser/attach.js +69 -35
- package/dist/src/browser/chatgpt-url.d.ts +8 -0
- package/dist/src/browser/chatgpt-url.js +28 -0
- package/dist/src/browser/downloads.d.ts +1 -1
- package/dist/src/browser/downloads.js +13 -4
- package/dist/src/browser/page-state.js +211 -4
- package/dist/src/client.d.ts +2 -1
- package/dist/src/client.js +2 -1
- package/dist/src/commands/configuration.js +330 -73
- package/dist/src/commands/context.d.ts +5 -1
- package/dist/src/commands/context.js +2 -2
- package/dist/src/commands/conversation.js +13 -1
- package/dist/src/commands/doctor.js +24 -2
- package/dist/src/commands/experience.d.ts +1 -0
- package/dist/src/commands/experience.js +156 -23
- package/dist/src/commands/files.d.ts +1 -0
- package/dist/src/commands/files.js +877 -170
- package/dist/src/commands/messages.d.ts +2 -1
- package/dist/src/commands/messages.js +310 -2
- package/dist/src/commands/modes.js +197 -52
- package/dist/src/commands/registry.js +8 -0
- package/dist/src/commands/sequence.js +3 -1
- package/dist/src/commands/session.d.ts +4 -1
- package/dist/src/commands/session.js +34 -3
- package/dist/src/dom/generation-state.d.ts +6 -1
- package/dist/src/dom/generation-state.js +206 -45
- package/dist/src/dom/locale/am.d.ts +23 -1
- package/dist/src/dom/locale/am.js +23 -1
- package/dist/src/dom/locale/ar.d.ts +21 -1
- package/dist/src/dom/locale/ar.js +24 -4
- package/dist/src/dom/locale/bg.d.ts +19 -1
- package/dist/src/dom/locale/bg.js +22 -4
- package/dist/src/dom/locale/bn.d.ts +23 -1
- package/dist/src/dom/locale/bn.js +23 -1
- package/dist/src/dom/locale/bs.d.ts +17 -1
- package/dist/src/dom/locale/bs.js +21 -5
- package/dist/src/dom/locale/ca.d.ts +20 -1
- package/dist/src/dom/locale/ca.js +25 -6
- package/dist/src/dom/locale/cs.d.ts +17 -1
- package/dist/src/dom/locale/cs.js +17 -1
- package/dist/src/dom/locale/da.d.ts +17 -1
- package/dist/src/dom/locale/da.js +18 -2
- package/dist/src/dom/locale/de.d.ts +16 -1
- package/dist/src/dom/locale/de.js +18 -3
- package/dist/src/dom/locale/el.d.ts +18 -1
- package/dist/src/dom/locale/el.js +21 -4
- package/dist/src/dom/locale/en.d.ts +2 -1
- package/dist/src/dom/locale/en.js +2 -1
- package/dist/src/dom/locale/es-419.d.ts +18 -1
- package/dist/src/dom/locale/es-419.js +18 -1
- package/dist/src/dom/locale/es-ES.d.ts +18 -1
- package/dist/src/dom/locale/es-ES.js +18 -1
- package/dist/src/dom/locale/et.d.ts +16 -1
- package/dist/src/dom/locale/et.js +16 -1
- package/dist/src/dom/locale/fa.d.ts +21 -1
- package/dist/src/dom/locale/fa.js +23 -3
- package/dist/src/dom/locale/fi.d.ts +18 -1
- package/dist/src/dom/locale/fi.js +18 -1
- package/dist/src/dom/locale/fr-CA.d.ts +15 -1
- package/dist/src/dom/locale/fr-CA.js +15 -1
- package/dist/src/dom/locale/fr-FR.d.ts +18 -1
- package/dist/src/dom/locale/fr-FR.js +22 -5
- package/dist/src/dom/locale/gu.d.ts +23 -1
- package/dist/src/dom/locale/gu.js +27 -5
- package/dist/src/dom/locale/hi.d.ts +23 -1
- package/dist/src/dom/locale/hi.js +25 -3
- package/dist/src/dom/locale/hr.d.ts +16 -1
- package/dist/src/dom/locale/hr.js +19 -4
- package/dist/src/dom/locale/hu.d.ts +17 -1
- package/dist/src/dom/locale/hu.js +20 -4
- package/dist/src/dom/locale/hy.d.ts +23 -1
- package/dist/src/dom/locale/hy.js +26 -4
- package/dist/src/dom/locale/id.d.ts +17 -1
- package/dist/src/dom/locale/id.js +19 -3
- package/dist/src/dom/locale/index.js +1 -0
- package/dist/src/dom/locale/is.d.ts +21 -1
- package/dist/src/dom/locale/is.js +21 -1
- package/dist/src/dom/locale/it.d.ts +15 -1
- package/dist/src/dom/locale/it.js +20 -6
- package/dist/src/dom/locale/ja.d.ts +19 -1
- package/dist/src/dom/locale/ja.js +23 -5
- package/dist/src/dom/locale/ka.d.ts +23 -1
- package/dist/src/dom/locale/ka.js +23 -1
- package/dist/src/dom/locale/kk.d.ts +23 -1
- package/dist/src/dom/locale/kk.js +25 -3
- package/dist/src/dom/locale/kn.d.ts +22 -1
- package/dist/src/dom/locale/kn.js +26 -5
- package/dist/src/dom/locale/ko.d.ts +17 -1
- package/dist/src/dom/locale/ko.js +17 -1
- package/dist/src/dom/locale/lt.d.ts +18 -1
- package/dist/src/dom/locale/lt.js +20 -3
- package/dist/src/dom/locale/lv.d.ts +18 -1
- package/dist/src/dom/locale/lv.js +18 -1
- package/dist/src/dom/locale/mk.d.ts +22 -1
- package/dist/src/dom/locale/mk.js +26 -5
- package/dist/src/dom/locale/ml.d.ts +23 -1
- package/dist/src/dom/locale/ml.js +25 -3
- package/dist/src/dom/locale/mn.d.ts +23 -1
- package/dist/src/dom/locale/mn.js +23 -1
- package/dist/src/dom/locale/mr.d.ts +23 -1
- package/dist/src/dom/locale/mr.js +25 -3
- package/dist/src/dom/locale/ms.d.ts +20 -1
- package/dist/src/dom/locale/ms.js +20 -1
- package/dist/src/dom/locale/my.d.ts +22 -1
- package/dist/src/dom/locale/my.js +24 -3
- package/dist/src/dom/locale/nb.d.ts +17 -1
- package/dist/src/dom/locale/nb.js +17 -1
- package/dist/src/dom/locale/nl.d.ts +20 -2
- package/dist/src/dom/locale/nl.js +22 -4
- package/dist/src/dom/locale/pa.d.ts +23 -1
- package/dist/src/dom/locale/pa.js +27 -5
- package/dist/src/dom/locale/pl.d.ts +17 -1
- package/dist/src/dom/locale/pl.js +19 -3
- package/dist/src/dom/locale/pt-BR.d.ts +18 -1
- package/dist/src/dom/locale/pt-BR.js +20 -3
- package/dist/src/dom/locale/pt-PT.d.ts +18 -1
- package/dist/src/dom/locale/pt-PT.js +22 -5
- package/dist/src/dom/locale/ro.d.ts +15 -1
- package/dist/src/dom/locale/ro.js +18 -4
- package/dist/src/dom/locale/ru.d.ts +23 -1
- package/dist/src/dom/locale/ru.js +24 -2
- package/dist/src/dom/locale/sk.d.ts +17 -1
- package/dist/src/dom/locale/sk.js +19 -3
- package/dist/src/dom/locale/sl.d.ts +21 -1
- package/dist/src/dom/locale/sl.js +24 -4
- package/dist/src/dom/locale/so.d.ts +22 -1
- package/dist/src/dom/locale/so.js +24 -3
- package/dist/src/dom/locale/sq.d.ts +21 -1
- package/dist/src/dom/locale/sq.js +25 -5
- package/dist/src/dom/locale/sr.d.ts +24 -1
- package/dist/src/dom/locale/sr.js +25 -2
- package/dist/src/dom/locale/sv.d.ts +16 -1
- package/dist/src/dom/locale/sv.js +16 -1
- package/dist/src/dom/locale/sw.d.ts +18 -1
- package/dist/src/dom/locale/sw.js +18 -1
- package/dist/src/dom/locale/ta.d.ts +23 -1
- package/dist/src/dom/locale/ta.js +27 -5
- package/dist/src/dom/locale/te.d.ts +23 -1
- package/dist/src/dom/locale/te.js +27 -5
- package/dist/src/dom/locale/th.d.ts +11 -1
- package/dist/src/dom/locale/th.js +11 -1
- package/dist/src/dom/locale/tl.d.ts +8 -1
- package/dist/src/dom/locale/tl.js +8 -1
- package/dist/src/dom/locale/tr.d.ts +21 -1
- package/dist/src/dom/locale/tr.js +23 -3
- package/dist/src/dom/locale/types.d.ts +1 -1
- package/dist/src/dom/locale/uk.d.ts +23 -1
- package/dist/src/dom/locale/uk.js +25 -3
- package/dist/src/dom/locale/ur.d.ts +22 -1
- package/dist/src/dom/locale/ur.js +25 -4
- package/dist/src/dom/locale/vi.d.ts +22 -1
- package/dist/src/dom/locale/vi.js +25 -4
- package/dist/src/dom/locale/zh-HK.d.ts +21 -1
- package/dist/src/dom/locale/zh-HK.js +23 -3
- package/dist/src/dom/locale/zh-Hans.d.ts +23 -1
- package/dist/src/dom/locale/zh-Hans.js +27 -5
- package/dist/src/dom/locale/zh-TW.d.ts +23 -1
- package/dist/src/dom/locale/zh-TW.js +25 -3
- package/dist/src/dom/menus.js +26 -2
- package/dist/src/dom/selectors.d.ts +1 -0
- package/dist/src/dom/selectors.js +9 -1
- package/dist/src/dom/wait-snapshot.d.ts +1 -0
- package/dist/src/dom/wait-snapshot.js +45 -22
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +2 -2
- package/dist/src/platform/local-paths.d.ts +1 -0
- package/dist/src/platform/local-paths.js +7 -3
- package/dist/src/safety/risk.d.ts +1 -0
- package/dist/src/safety/risk.js +1 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +10 -1
- package/dist/src/scripts/apply-intelligence-locale-captures.js +219 -7
- package/dist/src/scripts/capture-intelligence-locales.d.ts +119 -1
- package/dist/src/scripts/capture-intelligence-locales.js +653 -232
- package/dist/src/scripts/capture-surface-profile.d.ts +3 -1
- package/dist/src/scripts/capture-surface-profile.js +75 -25
- package/dist/src/scripts/live-smoke/scenarios.d.ts +3 -0
- package/dist/src/scripts/live-smoke/scenarios.js +321 -13
- package/dist/src/scripts/live-smoke.js +1 -0
- package/dist/src/scripts/locale-capture/surface-graph.d.ts +31 -0
- package/dist/src/scripts/locale-capture/surface-graph.js +50 -0
- package/dist/src/scripts/release-canary-module.d.ts +20 -0
- package/dist/src/scripts/release-canary-module.js +86 -0
- package/dist/src/types.d.ts +30 -6
- package/package.json +8 -2
- package/references/2026-07-16-chat-work-surfaces.md +13 -0
- package/references/2026-08-08-model-selection-locale-sweep.md +32 -0
- package/references/backend-protocol.md +22 -7
- package/references/language-coverage.md +24 -13
- package/references/localization.md +36 -14
- package/references/python-parity.md +9 -3
- package/references/troubleshooting.md +26 -4
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BrowserLike } from "../types.js";
|
|
2
|
+
import type { LiveSmokeScenarioResult } from "./live-smoke/types.js";
|
|
3
|
+
export type ReleaseCanaryOptions = {
|
|
4
|
+
tabId: string;
|
|
5
|
+
reportDir?: string;
|
|
6
|
+
includeUpload?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type ReleaseCanaryResult = {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
profilePaths: string[];
|
|
11
|
+
reportPath?: string;
|
|
12
|
+
results: LiveSmokeScenarioResult[];
|
|
13
|
+
failures: string[];
|
|
14
|
+
};
|
|
15
|
+
type ReleaseCanaryRuntime = {
|
|
16
|
+
agent?: unknown;
|
|
17
|
+
browser?: BrowserLike;
|
|
18
|
+
};
|
|
19
|
+
export declare function runReleaseCanary(runtime: ReleaseCanaryRuntime, options: ReleaseCanaryOptions): Promise<ReleaseCanaryResult>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { main as captureSurfaceProfile } from "./capture-surface-profile.js";
|
|
4
|
+
import { filterScenarios, runLiveSmoke } from "./live-smoke/harness.js";
|
|
5
|
+
import { optionalScenarios, requiredScenarios } from "./live-smoke/scenarios.js";
|
|
6
|
+
const CORE_SCENARIOS = [
|
|
7
|
+
"chat-work-expansion",
|
|
8
|
+
"configuration-mutate-restore",
|
|
9
|
+
"download-generated-file",
|
|
10
|
+
];
|
|
11
|
+
export async function runReleaseCanary(runtime, options) {
|
|
12
|
+
if (runtime.agent === undefined || runtime.agent === null) {
|
|
13
|
+
throw new Error("runReleaseCanary must run in a Codex bridge-hosted JavaScript context.");
|
|
14
|
+
}
|
|
15
|
+
if (options.tabId.trim().length === 0) {
|
|
16
|
+
throw new Error("runReleaseCanary requires an exact dedicated ChatGPT tab id.");
|
|
17
|
+
}
|
|
18
|
+
const reportDir = resolve(options.reportDir ?? join(process.cwd(), "reports", "release-canary"));
|
|
19
|
+
const profileDir = join(reportDir, "surface-profiles");
|
|
20
|
+
await mkdir(profileDir, { recursive: true });
|
|
21
|
+
const stamp = new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-");
|
|
22
|
+
const profilePaths = [
|
|
23
|
+
join(profileDir, `${stamp}-chat.json`),
|
|
24
|
+
join(profileDir, `${stamp}-work.json`),
|
|
25
|
+
];
|
|
26
|
+
try {
|
|
27
|
+
for (const [index, experience] of ["chat", "work"].entries()) {
|
|
28
|
+
const exitCode = await captureSurfaceProfile([
|
|
29
|
+
"--id", `release-canary-${experience}`,
|
|
30
|
+
"--experience", experience,
|
|
31
|
+
"--tab-id", options.tabId,
|
|
32
|
+
"--if-missing", "block",
|
|
33
|
+
"--out", profilePaths[index],
|
|
34
|
+
"--provenance", "Sanitized release canary capture from a dedicated visible ChatGPT tab."
|
|
35
|
+
], runtime);
|
|
36
|
+
if (exitCode !== 0) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
profilePaths: profilePaths.slice(0, index),
|
|
40
|
+
results: [],
|
|
41
|
+
failures: [`surface-profile-${experience}`]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await closeDedicatedProfileTab(runtime.browser, options.tabId);
|
|
48
|
+
}
|
|
49
|
+
const names = options.includeUpload === true
|
|
50
|
+
? [...CORE_SCENARIOS, "attach-one-file"]
|
|
51
|
+
: CORE_SCENARIOS;
|
|
52
|
+
const context = {
|
|
53
|
+
agent: runtime.agent,
|
|
54
|
+
...(runtime.browser === undefined ? {} : { browser: runtime.browser }),
|
|
55
|
+
reportDir: join(reportDir, "live-smoke"),
|
|
56
|
+
env: {
|
|
57
|
+
CHATGPT_E2E_CONFIGURATION_MUTATION: "1",
|
|
58
|
+
CHATGPT_E2E_DOWNLOAD: "1",
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const scenarios = filterScenarios([...requiredScenarios, ...optionalScenarios], names.join(","));
|
|
62
|
+
if (scenarios.length !== names.length) {
|
|
63
|
+
throw new Error(`Release canary scenario registration drift: expected ${names.length}, found ${scenarios.length}.`);
|
|
64
|
+
}
|
|
65
|
+
const smoke = await runLiveSmoke(context, scenarios);
|
|
66
|
+
const failures = smoke.results.filter(result => result.status !== "pass").map(result => result.name);
|
|
67
|
+
return {
|
|
68
|
+
ok: failures.length === 0,
|
|
69
|
+
profilePaths,
|
|
70
|
+
reportPath: smoke.reportPath,
|
|
71
|
+
results: smoke.results,
|
|
72
|
+
failures
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function closeDedicatedProfileTab(browser, tabId) {
|
|
76
|
+
const tabs = browser?.tabs;
|
|
77
|
+
const get = tabs?.get;
|
|
78
|
+
if (tabs === undefined || typeof get !== "function") {
|
|
79
|
+
throw new Error("Release canary requires browser.tabs.get so its dedicated profile tab can be closed before behavior tests.");
|
|
80
|
+
}
|
|
81
|
+
const tab = await get.call(tabs, tabId);
|
|
82
|
+
if (typeof tab.close !== "function") {
|
|
83
|
+
throw new Error("Release canary dedicated profile tab does not expose close().");
|
|
84
|
+
}
|
|
85
|
+
await tab.close();
|
|
86
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -432,6 +432,17 @@ export type DownloadLatestArgs = {
|
|
|
432
432
|
};
|
|
433
433
|
timeoutMs?: number;
|
|
434
434
|
};
|
|
435
|
+
export type StopGenerationArgs = {
|
|
436
|
+
/** Required acknowledgement because stopping changes the visible Chat turn. */
|
|
437
|
+
confirmStop?: boolean;
|
|
438
|
+
timeoutMs?: number;
|
|
439
|
+
};
|
|
440
|
+
export type StopGenerationData = {
|
|
441
|
+
wasGenerating: boolean;
|
|
442
|
+
stopped: boolean;
|
|
443
|
+
signalsBefore: string[];
|
|
444
|
+
signalsAfter: string[];
|
|
445
|
+
};
|
|
435
446
|
export type DownloadedFile = {
|
|
436
447
|
path: string;
|
|
437
448
|
suggestedFilename?: string;
|
|
@@ -767,6 +778,10 @@ export type SequenceStep = {
|
|
|
767
778
|
id: string;
|
|
768
779
|
command: "messages.status";
|
|
769
780
|
args?: MessageStatusArgs;
|
|
781
|
+
} | {
|
|
782
|
+
id: string;
|
|
783
|
+
command: "messages.stop";
|
|
784
|
+
args: StopGenerationArgs;
|
|
770
785
|
} | {
|
|
771
786
|
id: string;
|
|
772
787
|
command: "messages.waitAndRead";
|
|
@@ -869,27 +884,33 @@ export type BrowserUserTabInfo = {
|
|
|
869
884
|
title?: string;
|
|
870
885
|
url?: string;
|
|
871
886
|
};
|
|
887
|
+
export type BrowserOperationOptions = {
|
|
888
|
+
timeoutMs?: number;
|
|
889
|
+
};
|
|
872
890
|
export type LocatorLike = {
|
|
873
|
-
click?: (options?: unknown) => Promise<void>;
|
|
891
|
+
click?: (options?: BrowserOperationOptions & Record<string, unknown>) => Promise<void>;
|
|
892
|
+
press?: (key: string, options?: unknown) => Promise<void>;
|
|
874
893
|
fill?: (value: string, options?: unknown) => Promise<void>;
|
|
875
894
|
textContent?: (options?: unknown) => Promise<string | null>;
|
|
876
895
|
innerText?: (options?: unknown) => Promise<string>;
|
|
877
896
|
innerHTML?: (options?: unknown) => Promise<string>;
|
|
878
897
|
count?: () => Promise<number>;
|
|
898
|
+
allTextContents?: (options?: BrowserOperationOptions) => Promise<string[]>;
|
|
879
899
|
nth?: (index: number) => LocatorLike;
|
|
880
900
|
first?: () => LocatorLike;
|
|
881
901
|
last?: () => LocatorLike;
|
|
882
902
|
isVisible?: (options?: unknown) => Promise<boolean>;
|
|
883
|
-
evaluate?: <T>(fn: (element: Element) => T) => Promise<T>;
|
|
903
|
+
evaluate?: <T>(fn: (element: Element) => T, arg?: unknown, options?: BrowserOperationOptions) => Promise<T>;
|
|
884
904
|
locator?: (selector: string) => LocatorLike;
|
|
885
905
|
filter?: (options: Record<string, unknown>) => LocatorLike;
|
|
886
906
|
getByRole?: (role: string, options?: Record<string, unknown>) => LocatorLike;
|
|
887
907
|
getByText?: (text: string | RegExp, options?: Record<string, unknown>) => LocatorLike;
|
|
888
|
-
setInputFiles?: (paths: string[]) => Promise<void>;
|
|
908
|
+
setInputFiles?: (paths: string[], options?: BrowserOperationOptions) => Promise<void>;
|
|
889
909
|
};
|
|
890
910
|
export type FileChooserLike = {
|
|
911
|
+
element?: () => LocatorLike | Promise<LocatorLike>;
|
|
891
912
|
isMultiple?: () => boolean | Promise<boolean>;
|
|
892
|
-
setFiles: (paths: string[]) => Promise<void>;
|
|
913
|
+
setFiles: (paths: string[], options?: BrowserOperationOptions) => Promise<void>;
|
|
893
914
|
};
|
|
894
915
|
export type WaitForEventOptions = {
|
|
895
916
|
timeout?: number;
|
|
@@ -922,11 +943,14 @@ export type PageLike = {
|
|
|
922
943
|
y: number;
|
|
923
944
|
button?: number;
|
|
924
945
|
}) => Promise<void> | void;
|
|
946
|
+
keypress?: (options: {
|
|
947
|
+
keys: string[];
|
|
948
|
+
}) => Promise<void> | void;
|
|
925
949
|
};
|
|
926
950
|
waitForTimeout?: (ms: number) => Promise<void>;
|
|
927
951
|
waitForEvent?: (event: string, optionsOrCallback?: WaitForEventOptions | unknown) => Promise<unknown>;
|
|
928
|
-
evaluate?: <T, A = unknown>(fn: (arg: A) => T | Promise<T>, arg?: A) => Promise<T>;
|
|
929
|
-
content?: () => Promise<string>;
|
|
952
|
+
evaluate?: <T, A = unknown>(fn: (arg: A) => T | Promise<T>, arg?: A, options?: BrowserOperationOptions) => Promise<T>;
|
|
953
|
+
content?: (options?: BrowserOperationOptions) => Promise<string>;
|
|
930
954
|
close?: () => Promise<void>;
|
|
931
955
|
capabilities?: {
|
|
932
956
|
get?: (id: string) => Promise<unknown> | unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-chatgpt-control",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1-alpha.2",
|
|
4
4
|
"description": "Unofficial SDK for controlling visible ChatGPT Chat and Work sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/codex-chatgpt-control.bundle.mjs",
|
|
27
27
|
"bundle:backend": "esbuild src/scripts/backend-server.ts --bundle --platform=node --format=esm --outfile=dist/codex-chatgpt-control-backend.mjs",
|
|
28
28
|
"bundle:live-smoke": "esbuild src/scripts/live-smoke-module.ts --bundle --platform=node --format=esm --outfile=dist/codex-chatgpt-control-live-smoke.bundle.mjs",
|
|
29
|
+
"bundle:release-canary": "esbuild src/scripts/release-canary-module.ts --bundle --platform=node --format=esm --outfile=dist/codex-chatgpt-control-release-canary.bundle.mjs",
|
|
30
|
+
"contract:generate": "node scripts/generate-contract-fixtures.mjs",
|
|
29
31
|
"contract:validate": "node scripts/validate-contract.mjs",
|
|
30
32
|
"docs:drift": "tsx src/scripts/check-surface-drift.ts",
|
|
31
33
|
"parity:fixtures": "node scripts/check-parity-fixtures.mjs",
|
|
@@ -34,11 +36,15 @@
|
|
|
34
36
|
"test": "vitest run",
|
|
35
37
|
"test:watch": "vitest",
|
|
36
38
|
"thread": "tsx src/scripts/continue-thread.ts",
|
|
39
|
+
"capture:intelligence-locales": "tsx src/scripts/capture-intelligence-locales.ts",
|
|
40
|
+
"capture:intelligence-locales:queue": "tsx src/scripts/capture-intelligence-locales.ts --print-queue",
|
|
37
41
|
"capture:surface-profile": "tsx src/scripts/capture-surface-profile.ts",
|
|
42
|
+
"apply:intelligence-locales": "tsx src/scripts/apply-intelligence-locale-captures.ts",
|
|
38
43
|
"smoke:hi": "tsx src/scripts/smoke-hi.ts",
|
|
39
44
|
"smoke:search": "tsx src/scripts/smoke-search-open-read.ts",
|
|
40
45
|
"smoke:attach": "tsx src/scripts/smoke-attach-file.ts",
|
|
41
|
-
"smoke:live": "tsx src/scripts/live-smoke.ts"
|
|
46
|
+
"smoke:live": "tsx src/scripts/live-smoke.ts",
|
|
47
|
+
"smoke:release:build": "npm run bundle:release-canary"
|
|
42
48
|
},
|
|
43
49
|
"exports": {
|
|
44
50
|
".": {
|
|
@@ -50,6 +50,19 @@ Legacy `modes.set/get` and runner `mode` inputs remain supported. They preserve
|
|
|
50
50
|
their pre-0.5 warning-oriented behavior. New code requiring a verified
|
|
51
51
|
postcondition should use `configuration.apply({ strict: true })`.
|
|
52
52
|
|
|
53
|
+
## Surface Transitions
|
|
54
|
+
|
|
55
|
+
On the current home surface, ChatGPT exposes Chat and Work as a radio group in
|
|
56
|
+
the page banner. The checked radio is the strongest experience signal; the
|
|
57
|
+
textbox accessible name is shared and cannot classify the surface by itself.
|
|
58
|
+
Older button, tab, link, and menu-item controls remain compatibility fallbacks.
|
|
59
|
+
|
|
60
|
+
An active Work task may remove the home radio group while retaining the Work
|
|
61
|
+
model-plus-effort opener, such as `5.5 Light`. That compound control is a Work
|
|
62
|
+
continuation signal. To switch panes from an active conversation, the SDK
|
|
63
|
+
returns to the stable ChatGPT home surface and selects the requested radio; it
|
|
64
|
+
does not infer or navigate to a private Work route.
|
|
65
|
+
|
|
53
66
|
## Work Lifecycle
|
|
54
67
|
|
|
55
68
|
`work.start` defaults to `newTask: true`. If messages are already loaded and a
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Model Selection Locale Sweep
|
|
3
|
+
date: 2026-08-08
|
|
4
|
+
type: review
|
|
5
|
+
status: complete
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Model Selection Locale Sweep
|
|
9
|
+
|
|
10
|
+
English plus all 64 non-English languages advertised in ChatGPT Settings were
|
|
11
|
+
captured from one authenticated live account against the current selector UI.
|
|
12
|
+
|
|
13
|
+
The latest record for every locale passed these gates:
|
|
14
|
+
|
|
15
|
+
- rendered `html lang` matched the requested language;
|
|
16
|
+
- Chat exposed Power plus ordered Model/Effort rows;
|
|
17
|
+
- Work exposed Power plus ordered Model/Effort/Speed rows;
|
|
18
|
+
- Chat contained 3 model and 5 effort options;
|
|
19
|
+
- Work contained 4 model, 6 effort, and 2 speed options;
|
|
20
|
+
- captured labels were non-empty;
|
|
21
|
+
- Chat and the initial rendered language were restored.
|
|
22
|
+
|
|
23
|
+
The append-only private-source evidence contains 77 attempts: 65 latest
|
|
24
|
+
successes and 12 earlier blocked attempts retained for diagnostics. Every
|
|
25
|
+
blocked locale was subsequently recaptured successfully. The reviewed apply
|
|
26
|
+
updated all 64 non-English locale contributions and was idempotent on a second
|
|
27
|
+
run.
|
|
28
|
+
|
|
29
|
+
This is point-in-time evidence for the account and rollout observed on
|
|
30
|
+
2026-08-08. It does not guarantee identical model inventory for every plan,
|
|
31
|
+
region, workspace, or experiment. Generation-state, project, and plugin labels
|
|
32
|
+
were outside this no-prompt sweep.
|
|
@@ -71,6 +71,10 @@ Browser-control blockers are not protocol errors. They are normal command or run
|
|
|
71
71
|
|
|
72
72
|
Use `messages.status({ maxPreviewChars })` for a compact latest-assistant progress snapshot when a host tool-call ceiling is shorter than the expected ChatGPT generation. It returns counts, latest-assistant preview length, `completionState`, `generationActive`, and generation signals without treating partial text as final, and without the cost of a full `readLatest`/`wait` probe.
|
|
73
73
|
|
|
74
|
+
Use `messages.stop({ confirmStop: true })` only after the caller explicitly decides that the current visible response should stop. It clicks one uniquely scoped, visible stop control and succeeds only after generation is observed inactive. Without the exact boolean `confirmStop: true`, it returns `needs_confirmation`; if generation is observably inactive, it is a no-op. An unavailable or ambiguous control, an uninspectable generation state, or an unverified postcondition fails closed.
|
|
75
|
+
|
|
76
|
+
If the Stop activation starts but its completion races the command deadline, the result is `status: "timeout"` with blocker code `stop_generation_unverified` and `resumable: false`. The browser-native deadline terminates the request before the command returns, so it cannot click later; the click may already have taken effect before termination. Inspect the current visible generation state and never retry Stop automatically.
|
|
77
|
+
|
|
74
78
|
## Streaming
|
|
75
79
|
|
|
76
80
|
Streaming commands emit backend event lines until `completed` or `error`.
|
|
@@ -134,6 +138,10 @@ Use `files.preflight` for non-mutating local validation before browser upload wo
|
|
|
134
138
|
|
|
135
139
|
`files.attach` accepts `includeDiagnostics: true` to return metadata-only upload diagnostics in `data.diagnostics`: the preflight result plus the browser input's selected file names and sizes when the DOM exposes them. Pair `includeDiagnostics: true` with `includeHashes: true` when diagnosing whether a non-empty local file became an empty browser-side `File`; do not persist these diagnostics in public reports unless the user has approved content fingerprint metadata.
|
|
136
140
|
|
|
141
|
+
Once the native file handoff starts, any timeout, bridge error, or missing post-handoff composer evidence is indeterminate: `files.attach` returns `status: "partial"`, blocker code `attachment_outcome_indeterminate`, and `resumable: false`. Browser-native deadlines terminate the handoff request before the command returns, so it cannot mutate later; the file may already be present. Inspect the current composer, do not submit, and never retry the attachment automatically.
|
|
142
|
+
|
|
143
|
+
The Codex Chrome chooser intentionally exposes `isMultiple()` and `setFiles()` but no backing-element accessor. Before accepting that opaque chooser, the Node runtime proves the initiating input or control belongs to the unique active composer; the CDP fallback independently resolves and clicks that exact unique input. Browser providers that expose a chooser backing element receive an additional identity cross-check before handoff.
|
|
144
|
+
|
|
137
145
|
## Project Sources
|
|
138
146
|
|
|
139
147
|
Project Sources V1 is a narrow visible-UI surface for ChatGPT Project URLs such as `https://chatgpt.com/g/g-p-.../project`.
|
|
@@ -164,9 +172,16 @@ capability to inventory and bundle the latest non-SVG image asset. This is an
|
|
|
164
172
|
implementation detail of the TypeScript runtime; the wire command and result
|
|
165
173
|
shape are unchanged.
|
|
166
174
|
|
|
167
|
-
`files.downloadLatest` preserves the existing file-link behavior
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
`files.downloadLatest` preserves the existing file-link behavior, recognizes
|
|
176
|
+
filename-labelled buttons in the latest assistant turn, opens ChatGPT's artifact
|
|
177
|
+
preview, and then activates its visible Download control. `filenamePattern` is
|
|
178
|
+
an optional case-insensitive regular expression that prevents a different file
|
|
179
|
+
or image fallback from being accepted as the requested output. When the browser
|
|
180
|
+
bridge exposes only a completed local download `path()`, the runtime copies that
|
|
181
|
+
file into `destDir`; bridges exposing `saveAs()` remain supported. Without a
|
|
182
|
+
filename pattern, the command falls back to generated-artifact download only
|
|
183
|
+
when no conventional ChatGPT file affordance is visible. Artifact failures are
|
|
184
|
+
reported as structured blockers such as
|
|
170
185
|
`artifact_unavailable`, `artifact_selector_drift`, or
|
|
171
186
|
`artifact_download_unavailable`, not protocol errors.
|
|
172
187
|
|
|
@@ -220,13 +235,13 @@ For live browser control, the backend process must have access to a compatible b
|
|
|
220
235
|
- Explicit `RuntimeEnv.browser` or `RuntimeEnv.page` in a future embedding.
|
|
221
236
|
- A future Python-native/native-host/CDP backend that implements this same protocol.
|
|
222
237
|
|
|
223
|
-
Important: in Codex, `globalThis.agent` is not present until the Chrome plugin runtime is bootstrapped. Do not diagnose bridge availability by checking `globalThis.agent` in an ordinary shell or before
|
|
238
|
+
Important: in Codex, `globalThis.agent` is not present until the Chrome plugin runtime is bootstrapped. Do not diagnose bridge availability by checking `globalThis.agent` in an ordinary shell or before assigning the object returned by the Chrome plugin's `setupBrowserRuntime()`.
|
|
224
239
|
|
|
225
240
|
The live Chrome bootstrap is:
|
|
226
241
|
|
|
227
242
|
```js
|
|
228
|
-
const { setupBrowserRuntime } = await import("/
|
|
229
|
-
await setupBrowserRuntime(
|
|
243
|
+
const { setupBrowserRuntime } = await import("/absolute/path/to/the/current/chrome/scripts/browser-client.mjs");
|
|
244
|
+
globalThis.agent = await setupBrowserRuntime();
|
|
230
245
|
globalThis.browser = await agent.browsers.get("extension");
|
|
231
246
|
```
|
|
232
247
|
|
|
@@ -270,7 +285,7 @@ python scripts/live_smoke.py \
|
|
|
270
285
|
--backend-command "node scripts/http_stdio_relay.mjs"
|
|
271
286
|
```
|
|
272
287
|
|
|
273
|
-
|
|
288
|
+
Create the backend server and wait on it in the **same** bridge-hosted JS execution. A server created in one Node REPL call cannot be kept bridge-capable by waiting in a second call. That single execution must remain active for the duration of the Python smoke. The relay path is:
|
|
274
289
|
|
|
275
290
|
```text
|
|
276
291
|
Python SDK -> stdio relay -> bridge-hosted Node backend -> Codex Chrome bridge -> ChatGPT
|
|
@@ -5,12 +5,10 @@ The full set of languages ChatGPT exposes in **Settings → General → Language
|
|
|
5
5
|
localization rollout described in [`localization.md`](./localization.md). Strings are
|
|
6
6
|
stored per-locale under `src/dom/locale/<bcp47>.ts`.
|
|
7
7
|
|
|
8
|
-
The status table
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
green row below as full Work support until that locale has matching profile
|
|
13
|
-
evidence.
|
|
8
|
+
The status table began as the pre-existing Chat/localization registry. On
|
|
9
|
+
2026-08-08, English plus all 64 non-English entries were recaptured against the
|
|
10
|
+
current Chat and Work Power/Advanced selector graph. The latest record for
|
|
11
|
+
every locale passed; see the [reviewed sweep summary](./2026-08-08-model-selection-locale-sweep.md).
|
|
14
12
|
|
|
15
13
|
- Speaker counts are **crude guesstimates** (native + second-language, rounded), not
|
|
16
14
|
researched figures. They exist only to prioritize the rollout.
|
|
@@ -124,21 +122,33 @@ For a single current Chat or Work surface, generate a sanitized, unverified
|
|
|
124
122
|
profile draft from an already-open authorized tab:
|
|
125
123
|
|
|
126
124
|
```bash
|
|
127
|
-
npm run capture:surface-profile -- --id work-basic-en --locale en-US
|
|
125
|
+
npm run capture:surface-profile -- --id work-basic-en --locale en-US --experience work
|
|
128
126
|
```
|
|
129
127
|
|
|
130
|
-
This command does not submit a prompt or change configuration.
|
|
131
|
-
|
|
128
|
+
This command does not submit a prompt or change model configuration. When
|
|
129
|
+
`--experience` is supplied it visibly selects Chat or Work and restores the
|
|
130
|
+
prior pane by default. It opens visible menus for inspection, normalizes any conversation URL to `/c/sanitized`, and
|
|
132
131
|
writes only bounded composer/configuration structure under
|
|
133
132
|
`outputs/surface-profiles/`.
|
|
134
133
|
|
|
135
134
|
Use the repository capture script to drive the visible ChatGPT UI through Settings → General
|
|
136
|
-
→ Language, then open the composer
|
|
135
|
+
→ Language, then open the current composer selector and record localized labels:
|
|
137
136
|
|
|
138
137
|
```bash
|
|
139
138
|
npm run capture:intelligence-locales -- --auto-switch --all --if-missing open
|
|
140
139
|
```
|
|
141
140
|
|
|
141
|
+
For a complete current selector sweep, add `--capture-surfaces`. This reads the
|
|
142
|
+
two ordered pane radios, both Power controls, both Advanced labels, Chat's
|
|
143
|
+
ordered Model/Effort rows, Work's ordered Model/Effort/Speed rows, and every
|
|
144
|
+
visible submenu option. It restores the original compact/advanced state, Chat
|
|
145
|
+
pane, and initial rendered language:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm run capture:intelligence-locales -- \
|
|
149
|
+
--auto-switch --all --capture-surfaces --if-missing open
|
|
150
|
+
```
|
|
151
|
+
|
|
142
152
|
To also capture localized running-generation controls such as `stopControl`, run the same
|
|
143
153
|
sweep with the bounded generation probe enabled:
|
|
144
154
|
|
|
@@ -166,9 +176,10 @@ changes the settings or picker DOM, the run should emit a blocker record instead
|
|
|
166
176
|
|
|
167
177
|
1. Switch ChatGPT **Settings → General → Language** to the target, reload, and confirm via
|
|
168
178
|
`document.documentElement.lang` + visible text.
|
|
169
|
-
2. Read static controls (composer, add-files, new chat, current
|
|
170
|
-
3. Open the
|
|
171
|
-
|
|
179
|
+
2. Read static controls (composer, add-files, new chat, current selector label) via JS.
|
|
180
|
+
3. Open the selector with a **real** click, record the Power slider, expand
|
|
181
|
+
Advanced when needed, and read the ordered Chat Model/Effort or Work
|
|
182
|
+
Model/Effort/Speed menus. Restore the initial compact/advanced state.
|
|
172
183
|
4. Open search → placeholder; observe a conversation → copy-response + response-actions.
|
|
173
184
|
`stopControl` is present only mid-generation and should be captured with
|
|
174
185
|
`--capture-generation-state`; login/captcha/rate-limit copy needs a logged-out/limited
|
|
@@ -71,7 +71,7 @@ Localized — lives in `src/dom/locale/en.ts` (English) and per-locale files, sa
|
|
|
71
71
|
| `workComposerTextbox` | Work composer textbox | `aria-label` / placeholder |
|
|
72
72
|
| `newWork` | start-another-Work-task control | visible button or link text |
|
|
73
73
|
| `experienceOptions.chat` / `.work` | Chat/Work switch controls | visible button, tab, link, or menu text |
|
|
74
|
-
| `configurationAxes.*` | model/intelligence/effort/speed/advanced
|
|
74
|
+
| `configurationAxes.*` | power/model/intelligence/effort/speed/advanced controls | visible or accessible text |
|
|
75
75
|
| `configurationOptions.*` | Chat and Work configuration values | visible menu text |
|
|
76
76
|
| `sendButton` | send button | `aria-label` |
|
|
77
77
|
| `searchChatsButton` | search-chats button | `aria-label` |
|
|
@@ -106,18 +106,19 @@ Localized — lives in `src/dom/locale/en.ts` (English) and per-locale files, sa
|
|
|
106
106
|
The type definitions for `LocaleStrings` (complete) and `LocaleContribution` (partial, for
|
|
107
107
|
non-English files) live in [`src/dom/locale/types.ts`](../src/dom/locale/types.ts).
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
English plus all 64 advertised non-English Settings languages were recaptured
|
|
110
|
+
against the current Chat and Work Power/Advanced selector graph on 2026-08-08.
|
|
111
|
+
That proves the reviewed labels for the observed account and rollout, not every
|
|
112
|
+
plan, region, workspace, or staged experiment. Keep capability discovery and
|
|
113
|
+
fail-closed shape checks even when a locale is present in the registry.
|
|
114
114
|
|
|
115
115
|
Create a read-only draft from an already-open authorized ChatGPT tab:
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
118
|
npm run capture:surface-profile -- \
|
|
119
119
|
--id work-basic-de \
|
|
120
|
-
--locale de-DE
|
|
120
|
+
--locale de-DE \
|
|
121
|
+
--experience work
|
|
121
122
|
```
|
|
122
123
|
|
|
123
124
|
The command defaults region, plan, account, and workspace metadata to
|
|
@@ -127,6 +128,23 @@ identifiers, and excludes prompt/response/sidebar text. Review the draft under
|
|
|
127
128
|
then move a sanitized fixture into `contracts/v1/fixtures/` and update the
|
|
128
129
|
manifest/parity matrix.
|
|
129
130
|
|
|
131
|
+
To refresh every language in one visible-session sweep, use the existing
|
|
132
|
+
Settings language loop with structural Chat/Work capture enabled:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm run capture:intelligence-locales -- \
|
|
136
|
+
--auto-switch --all --capture-surfaces --if-missing open
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Review the JSONL evidence, then apply it only with the explicit review gate:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm run apply:intelligence-locales -- --in <capture.jsonl> --reviewed
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The applier keeps older locale contributions valid, adds only observed
|
|
146
|
+
non-English labels, and refuses incomplete surface captures instead of guessing.
|
|
147
|
+
|
|
130
148
|
## Adding a new language
|
|
131
149
|
|
|
132
150
|
### Step 1 — put a real ChatGPT session into the target language
|
|
@@ -186,11 +204,13 @@ Leave the canonical English first (it comes from `en.ts`), and leave the API key
|
|
|
186
204
|
(`web_search`, `modeOptions.pro`, `modeOptions.high`, and the other semantic ids)
|
|
187
205
|
unchanged.
|
|
188
206
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
`modeLabels`,
|
|
193
|
-
|
|
207
|
+
Current ChatGPT rollouts may expose a basic Power control and nested Advanced
|
|
208
|
+
Model/Effort/Speed rows. Add localized equivalents only after observing those
|
|
209
|
+
exact labels in the target locale. Put legacy broad picker/opening labels in
|
|
210
|
+
`modeLabels`, selectable Chat effort labels under
|
|
211
|
+
`modeOptions.<semantic-id>`, and current selector labels under
|
|
212
|
+
`configurationAxes` / `configurationOptions` so short labels such as `Pro`
|
|
213
|
+
cannot match unrelated rows like `Move to project`.
|
|
194
214
|
|
|
195
215
|
Then open [`src/dom/locale/index.ts`](../src/dom/locale/index.ts) and register the new
|
|
196
216
|
locale:
|
|
@@ -274,8 +294,10 @@ Notes:
|
|
|
274
294
|
fixture, or protocol change, because API keys and wire shapes are unchanged.
|
|
275
295
|
- All consumer import paths (`from "../dom/locale-labels.js"`) are unchanged — the barrel
|
|
276
296
|
`locale-labels.ts` re-exports everything from `locale/index.ts`.
|
|
277
|
-
- `doctor({ check: ["localization"] })` verifies that the
|
|
278
|
-
|
|
297
|
+
- `doctor({ check: ["localization"] })` verifies that the legacy selector,
|
|
298
|
+
tool, mode-option, configuration-axis (including Power/Advanced), and
|
|
299
|
+
configuration-option registries are populated and canonical English values
|
|
300
|
+
are present. It also reports running-state localization coverage
|
|
279
301
|
separately from the flattened selector list, because `stopControl` and
|
|
280
302
|
`stoppedAssistant` require a live mid-generation capture. It does not yet prove full
|
|
281
303
|
localized selector coverage; treat localized workflow failures as selector-wiring work
|
|
@@ -34,6 +34,10 @@ Incomplete response capture is also shared contract behavior. Python must preser
|
|
|
34
34
|
|
|
35
35
|
For long-answer polling, Python forwards `response_content="metadata"` to the shared wire field `responseContent: "metadata"` on `messages.wait`. The TypeScript backend then omits assistant text from wait results and returns compact metadata such as `data.responseChars` and `data.responseSha256`; Python must preserve those fields without trying to reconstruct omitted content.
|
|
36
36
|
|
|
37
|
+
Python exposes the explicit stop primitive as `chatgpt.messages.stop(confirm_stop=True)`, mapping to `messages.stop` with `confirmStop: true`. The TypeScript backend owns visible-control selection, the single operation deadline, and inactive-state verification; Python adds no independent browser behavior.
|
|
38
|
+
|
|
39
|
+
Python preserves the backend's indeterminate Stop result exactly: `status == "timeout"`, blocker code `stop_generation_unverified`, and `resumable == False`. The browser request is terminated at its native deadline and cannot click later, but the click may already have taken effect. Python callers must inspect the visible state and must not retry automatically.
|
|
40
|
+
|
|
37
41
|
Generated-image behavior stays owned by the TypeScript runtime. Python exposes
|
|
38
42
|
the same backend commands through `chatgpt.artifacts.list_latest(...)`,
|
|
39
43
|
`chatgpt.artifacts.wait(...)`, and `chatgpt.artifacts.download_latest(...)`.
|
|
@@ -63,6 +67,8 @@ Python does not reinterpret attachment paths. It sends the path string to the No
|
|
|
63
67
|
|
|
64
68
|
Python exposes the backend-visible `files.preflight` command as `chatgpt.files.preflight(...)`. It returns the same `CommandResult` as TypeScript and can be decoded with `FilePreflightData` when callers want typed metadata. The command validates paths, readability, file-vs-directory status, size limits, duplicate basenames, duplicate resolved paths, zero-byte files, and extension-based MIME/category guesses without opening ChatGPT or reading file contents for MIME detection. Zero-byte files are blocked before browser interaction. Optional `include_hashes=True` / wire `includeHashes: true` adds SHA-256 metadata to `FilePreflightFile.sha256` for local diagnostics; file contents are never returned.
|
|
65
69
|
|
|
70
|
+
Python also preserves a post-handoff attachment uncertainty as `status == "partial"`, blocker code `attachment_outcome_indeterminate`, and `resumable == False`. Callers must inspect the current composer, must not submit, and must not automatically repeat the file handoff.
|
|
71
|
+
|
|
66
72
|
## Project Sources
|
|
67
73
|
|
|
68
74
|
Python exposes the protocol-visible Project Sources commands through `chatgpt.projects.sources`:
|
|
@@ -211,8 +217,8 @@ python scripts/live_smoke.py --mode browser-bridge
|
|
|
211
217
|
When the live backend is hosted inside the Codex Chrome plugin runtime, do not test bridge availability from a normal shell or an unbootstrapped Node REPL. First initialize the Chrome runtime:
|
|
212
218
|
|
|
213
219
|
```js
|
|
214
|
-
const { setupBrowserRuntime } = await import("/
|
|
215
|
-
await setupBrowserRuntime(
|
|
220
|
+
const { setupBrowserRuntime } = await import("/absolute/path/to/the/current/chrome/scripts/browser-client.mjs");
|
|
221
|
+
globalThis.agent = await setupBrowserRuntime();
|
|
216
222
|
globalThis.browser = await agent.browsers.get("extension");
|
|
217
223
|
```
|
|
218
224
|
|
|
@@ -225,7 +231,7 @@ python scripts/live_smoke.py \
|
|
|
225
231
|
--backend-command "node scripts/http_stdio_relay.mjs"
|
|
226
232
|
```
|
|
227
233
|
|
|
228
|
-
|
|
234
|
+
Create the backend server and wait on it in the **same** bridge-hosted JS execution. Starting the server in one Node REPL call and trying to keep it alive with a second call does not preserve the first call's browser execution context. Keep that single execution active while Python runs. If it returns first, the browser client no longer has an active execution context and operations can fail with `node_repl exec context not found`.
|
|
229
235
|
|
|
230
236
|
This is the intended live test chain:
|
|
231
237
|
|
|
@@ -33,8 +33,8 @@ Use `chatgpt.explainBlocker(result)` or Python `explain_blocker(result)` when re
|
|
|
33
33
|
Do not conclude that Chrome or the extension is broken from a plain shell result, or from checking `globalThis.agent` before the Chrome plugin runtime is initialized. For a true Codex Chrome-plugin live run, bootstrap the runtime first:
|
|
34
34
|
|
|
35
35
|
```js
|
|
36
|
-
const { setupBrowserRuntime } = await import("/
|
|
37
|
-
await setupBrowserRuntime(
|
|
36
|
+
const { setupBrowserRuntime } = await import("/absolute/path/to/the/current/chrome/scripts/browser-client.mjs");
|
|
37
|
+
globalThis.agent = await setupBrowserRuntime();
|
|
38
38
|
globalThis.browser = await agent.browsers.get("extension");
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -49,7 +49,7 @@ python scripts/live_smoke.py \
|
|
|
49
49
|
--backend-command "node scripts/http_stdio_relay.mjs"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Create the backend server and wait on it in the **same** bridge-hosted JS execution. Starting it in one Node REPL call and waiting in another loses the first call's browser execution context. Keep that single call active while Python runs. If it returns first, browser operations can fail with `node_repl exec context not found`.
|
|
53
53
|
|
|
54
54
|
## `login_required`
|
|
55
55
|
|
|
@@ -80,6 +80,13 @@ loaded and the SDK refused to append. Pass `newTask: false` only when the user
|
|
|
80
80
|
intends to continue that exact task. If a Work submission returns partial or
|
|
81
81
|
timeout, use `work.status`, `work.wait`, or `work.readLatest`; do not resubmit.
|
|
82
82
|
|
|
83
|
+
On the current home page, inspect the `Select chat surface` Chat/Work radio
|
|
84
|
+
group before diagnosing Work as unavailable. Once a Work task is active, that
|
|
85
|
+
radio group can disappear; the compound model-plus-effort opener is the SDK's
|
|
86
|
+
continuation evidence, while the visible Work task chrome can corroborate a
|
|
87
|
+
manual diagnosis. `experience.open` returns to the home selector before
|
|
88
|
+
changing panes from an active task.
|
|
89
|
+
|
|
83
90
|
## Existing Tab Not Found Or Ambiguous
|
|
84
91
|
|
|
85
92
|
When `existingTab` targeting cannot select one already-open tab, inspect `blocker.diagnostics.existingTab` or the rendered blocker explanation. Diagnostics are metadata-only: requested target, whether user-open tabs were available, candidate tab IDs, URLs, titles, conversation IDs, omitted candidate count, and mismatch reason. They must not include page text or chat content.
|
|
@@ -144,6 +151,14 @@ const final = await chatgpt.messages.readLatest({ format: "markdown" });
|
|
|
144
151
|
|
|
145
152
|
Active generation may appear as a visible or accessible-name control such as `Stop answering`, `Stop generating`, or `Stop streaming`. Stopped generation may appear as `Stopped thinking`. Treat all of those as incomplete states.
|
|
146
153
|
|
|
154
|
+
If the caller explicitly supersedes a running request, use `messages.stop({ confirmStop: true })`. The command does nothing when generation is observably inactive and fails closed when it cannot inspect the state, uniquely activate the visible composer stop control, or verify the inactive postcondition. Do not use it as an automatic timeout-recovery retry mechanism.
|
|
155
|
+
|
|
156
|
+
If Stop returns `stop_generation_unverified` with `resumable: false`, the activation began but its outcome is unknown. The browser request has been terminated and cannot click later, although the click may already have taken effect. Inspect the visible generation state before any further action; never retry Stop automatically.
|
|
157
|
+
|
|
158
|
+
If attachment returns `attachment_outcome_indeterminate` with `resumable: false`, the native handoff began but the file's composer state is unknown. The browser request has been terminated and cannot attach later, although the file may already be present. Inspect the current composer, do not submit, and do not retry automatically.
|
|
159
|
+
|
|
160
|
+
The Codex Chrome file chooser does not expose its backing input. This is expected: the SDK proves the unique active-composer trigger before opening the chooser and uses any provider-specific backing-element accessor only as an additional cross-check. A missing chooser `element()` method by itself is not an upload failure.
|
|
161
|
+
|
|
147
162
|
When the outer host tool-call ceiling is short, poll in bounded chunks instead of issuing a single very long wait:
|
|
148
163
|
|
|
149
164
|
```ts
|
|
@@ -161,7 +176,14 @@ When ChatGPT exposes previous/next response controls, `readLatest` and `copyLate
|
|
|
161
176
|
|
|
162
177
|
## Download Unavailable
|
|
163
178
|
|
|
164
|
-
The command only downloads visible files with a download affordance.
|
|
179
|
+
The command only downloads visible files with a download affordance. Current
|
|
180
|
+
ChatGPT file answers may first expose a filename-labelled button; the SDK opens
|
|
181
|
+
that artifact preview and then uses its visible Download control. When the
|
|
182
|
+
expected filename is known, pass a case-insensitive regular expression such as
|
|
183
|
+
`filenamePattern: "^report\\.csv$"`. A `download_filename_not_found` blocker
|
|
184
|
+
means no visible assistant file matched; the SDK deliberately did not accept an
|
|
185
|
+
unrelated image fallback. If no download control exists, ask ChatGPT to create
|
|
186
|
+
or expose the file again.
|
|
165
187
|
|
|
166
188
|
## Redacted Reports
|
|
167
189
|
|