codex-chatgpt-control 0.5.1-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 +12 -0
- package/contracts/v1/fixtures/backend-capabilities.json +1 -0
- package/contracts/v1/fixtures/command-descriptors.json +21 -0
- 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 +8 -1
- package/dist/codex-chatgpt-control-backend.mjs +2259 -498
- package/dist/codex-chatgpt-control.bundle.mjs +2263 -472
- 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/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 +46 -12
- 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/files.d.ts +1 -0
- package/dist/src/commands/files.js +742 -191
- 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 +3 -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 +10 -1
- package/dist/src/dom/locale/am.js +10 -1
- package/dist/src/dom/locale/ar.d.ts +8 -1
- package/dist/src/dom/locale/ar.js +9 -2
- package/dist/src/dom/locale/bg.d.ts +6 -1
- package/dist/src/dom/locale/bg.js +6 -1
- package/dist/src/dom/locale/bn.d.ts +10 -1
- package/dist/src/dom/locale/bn.js +10 -1
- package/dist/src/dom/locale/bs.d.ts +5 -1
- package/dist/src/dom/locale/bs.js +5 -1
- package/dist/src/dom/locale/ca.d.ts +8 -1
- package/dist/src/dom/locale/ca.js +8 -1
- package/dist/src/dom/locale/cs.d.ts +5 -1
- package/dist/src/dom/locale/cs.js +6 -2
- package/dist/src/dom/locale/da.d.ts +5 -1
- package/dist/src/dom/locale/da.js +6 -2
- package/dist/src/dom/locale/de.d.ts +4 -1
- package/dist/src/dom/locale/de.js +5 -2
- package/dist/src/dom/locale/el.d.ts +5 -1
- package/dist/src/dom/locale/el.js +6 -2
- 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 +5 -1
- package/dist/src/dom/locale/es-419.js +5 -1
- package/dist/src/dom/locale/es-ES.d.ts +5 -1
- package/dist/src/dom/locale/es-ES.js +5 -1
- package/dist/src/dom/locale/et.d.ts +4 -1
- package/dist/src/dom/locale/et.js +5 -2
- package/dist/src/dom/locale/fa.d.ts +9 -1
- package/dist/src/dom/locale/fa.js +10 -2
- package/dist/src/dom/locale/fi.d.ts +5 -1
- package/dist/src/dom/locale/fi.js +6 -2
- package/dist/src/dom/locale/fr-CA.d.ts +4 -1
- package/dist/src/dom/locale/fr-CA.js +4 -1
- package/dist/src/dom/locale/fr-FR.d.ts +5 -1
- package/dist/src/dom/locale/fr-FR.js +7 -3
- package/dist/src/dom/locale/gu.d.ts +10 -1
- package/dist/src/dom/locale/gu.js +10 -1
- package/dist/src/dom/locale/hi.d.ts +10 -1
- package/dist/src/dom/locale/hi.js +11 -2
- package/dist/src/dom/locale/hr.d.ts +4 -1
- package/dist/src/dom/locale/hr.js +4 -1
- package/dist/src/dom/locale/hu.d.ts +4 -1
- package/dist/src/dom/locale/hu.js +5 -2
- package/dist/src/dom/locale/hy.d.ts +10 -1
- package/dist/src/dom/locale/hy.js +11 -2
- package/dist/src/dom/locale/id.d.ts +5 -1
- package/dist/src/dom/locale/id.js +5 -1
- package/dist/src/dom/locale/index.js +1 -0
- package/dist/src/dom/locale/is.d.ts +9 -1
- package/dist/src/dom/locale/is.js +10 -2
- package/dist/src/dom/locale/it.d.ts +4 -1
- package/dist/src/dom/locale/it.js +6 -3
- package/dist/src/dom/locale/ja.d.ts +6 -1
- package/dist/src/dom/locale/ja.js +7 -2
- package/dist/src/dom/locale/ka.d.ts +10 -1
- package/dist/src/dom/locale/ka.js +11 -2
- package/dist/src/dom/locale/kk.d.ts +10 -1
- package/dist/src/dom/locale/kk.js +11 -2
- package/dist/src/dom/locale/kn.d.ts +9 -1
- package/dist/src/dom/locale/kn.js +10 -2
- package/dist/src/dom/locale/ko.d.ts +6 -1
- package/dist/src/dom/locale/ko.js +7 -2
- package/dist/src/dom/locale/lt.d.ts +5 -1
- package/dist/src/dom/locale/lt.js +6 -2
- package/dist/src/dom/locale/lv.d.ts +5 -1
- package/dist/src/dom/locale/lv.js +5 -1
- package/dist/src/dom/locale/mk.d.ts +9 -1
- package/dist/src/dom/locale/mk.js +10 -2
- package/dist/src/dom/locale/ml.d.ts +10 -1
- package/dist/src/dom/locale/ml.js +10 -1
- package/dist/src/dom/locale/mn.d.ts +10 -1
- package/dist/src/dom/locale/mn.js +13 -4
- package/dist/src/dom/locale/mr.d.ts +10 -1
- package/dist/src/dom/locale/mr.js +10 -1
- package/dist/src/dom/locale/ms.d.ts +9 -1
- package/dist/src/dom/locale/ms.js +9 -1
- package/dist/src/dom/locale/my.d.ts +9 -1
- package/dist/src/dom/locale/my.js +9 -1
- package/dist/src/dom/locale/nb.d.ts +5 -1
- package/dist/src/dom/locale/nb.js +6 -2
- package/dist/src/dom/locale/nl.d.ts +8 -2
- package/dist/src/dom/locale/nl.js +8 -2
- package/dist/src/dom/locale/pa.d.ts +10 -1
- package/dist/src/dom/locale/pa.js +10 -1
- package/dist/src/dom/locale/pl.d.ts +5 -1
- package/dist/src/dom/locale/pl.js +6 -2
- package/dist/src/dom/locale/pt-BR.d.ts +5 -1
- package/dist/src/dom/locale/pt-BR.js +6 -2
- package/dist/src/dom/locale/pt-PT.d.ts +5 -1
- package/dist/src/dom/locale/pt-PT.js +5 -1
- package/dist/src/dom/locale/ro.d.ts +4 -1
- package/dist/src/dom/locale/ro.js +4 -1
- package/dist/src/dom/locale/ru.d.ts +8 -1
- package/dist/src/dom/locale/ru.js +9 -2
- package/dist/src/dom/locale/sk.d.ts +5 -1
- package/dist/src/dom/locale/sk.js +5 -1
- package/dist/src/dom/locale/sl.d.ts +9 -1
- package/dist/src/dom/locale/sl.js +9 -1
- package/dist/src/dom/locale/so.d.ts +9 -1
- package/dist/src/dom/locale/so.js +9 -1
- package/dist/src/dom/locale/sq.d.ts +10 -1
- package/dist/src/dom/locale/sq.js +12 -3
- package/dist/src/dom/locale/sr.d.ts +9 -1
- package/dist/src/dom/locale/sr.js +10 -2
- package/dist/src/dom/locale/sv.d.ts +4 -1
- package/dist/src/dom/locale/sv.js +4 -1
- package/dist/src/dom/locale/sw.d.ts +5 -1
- package/dist/src/dom/locale/sw.js +7 -3
- package/dist/src/dom/locale/ta.d.ts +10 -1
- package/dist/src/dom/locale/ta.js +12 -3
- package/dist/src/dom/locale/te.d.ts +10 -1
- package/dist/src/dom/locale/te.js +11 -2
- package/dist/src/dom/locale/th.d.ts +6 -1
- package/dist/src/dom/locale/th.js +6 -1
- package/dist/src/dom/locale/tl.d.ts +1 -1
- package/dist/src/dom/locale/tl.js +2 -2
- package/dist/src/dom/locale/tr.d.ts +9 -1
- package/dist/src/dom/locale/tr.js +10 -2
- package/dist/src/dom/locale/types.d.ts +1 -1
- package/dist/src/dom/locale/uk.d.ts +10 -1
- package/dist/src/dom/locale/uk.js +11 -2
- package/dist/src/dom/locale/ur.d.ts +9 -1
- package/dist/src/dom/locale/ur.js +9 -1
- package/dist/src/dom/locale/vi.d.ts +9 -1
- package/dist/src/dom/locale/vi.js +10 -2
- package/dist/src/dom/locale/zh-HK.d.ts +8 -1
- package/dist/src/dom/locale/zh-HK.js +9 -2
- package/dist/src/dom/locale/zh-Hans.d.ts +10 -1
- package/dist/src/dom/locale/zh-Hans.js +11 -2
- package/dist/src/dom/locale/zh-TW.d.ts +10 -1
- package/dist/src/dom/locale/zh-TW.js +11 -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/safety/risk.d.ts +1 -0
- package/dist/src/safety/risk.js +1 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +2 -2
- package/dist/src/scripts/apply-intelligence-locale-captures.js +54 -14
- package/dist/src/scripts/capture-intelligence-locales.d.ts +114 -1
- package/dist/src/scripts/capture-intelligence-locales.js +437 -304
- package/dist/src/scripts/live-smoke/scenarios.d.ts +2 -1
- package/dist/src/scripts/live-smoke/scenarios.js +9 -10
- package/dist/src/scripts/locale-capture/surface-graph.d.ts +7 -2
- package/dist/src/scripts/locale-capture/surface-graph.js +7 -0
- package/dist/src/types.d.ts +27 -6
- package/package.json +2 -1
- package/references/2026-08-08-model-selection-locale-sweep.md +32 -0
- package/references/backend-protocol.md +11 -3
- package/references/language-coverage.md +14 -14
- package/references/localization.md +17 -13
- package/references/python-parity.md +8 -2
- package/references/troubleshooting.md +10 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AssistantGenerationState } from "../dom/generation-state.js";
|
|
2
|
-
import type { AskArgs, AskReadData, CommandResult, ComposeArgs, ComposeData, MessageStatusArgs, MessageStatusData, ReadLatestArgs, ReadLatestData, RuntimeEnv, SubmitArgs, SubmitData, WaitAndReadArgs, WaitArgs, WaitData } from "../types.js";
|
|
2
|
+
import type { AskArgs, AskReadData, CommandResult, ComposeArgs, ComposeData, MessageStatusArgs, MessageStatusData, ReadLatestArgs, ReadLatestData, RuntimeEnv, SubmitArgs, SubmitData, StopGenerationArgs, StopGenerationData, WaitAndReadArgs, WaitArgs, WaitData } from "../types.js";
|
|
3
3
|
export type CompletionSnapshot = {
|
|
4
4
|
textStableForMs: number;
|
|
5
5
|
stableMs: number;
|
|
@@ -10,6 +10,7 @@ export type CompletionSnapshot = {
|
|
|
10
10
|
export declare function isResponseComplete(snapshot: CompletionSnapshot): boolean;
|
|
11
11
|
export declare function composeMessage(env: RuntimeEnv, args: ComposeArgs): Promise<CommandResult<ComposeData>>;
|
|
12
12
|
export declare function submitMessage(env: RuntimeEnv, args?: SubmitArgs): Promise<CommandResult<SubmitData>>;
|
|
13
|
+
export declare function stopGeneration(env: RuntimeEnv, args?: StopGenerationArgs): Promise<CommandResult<StopGenerationData>>;
|
|
13
14
|
export declare function waitForMessage(env: RuntimeEnv, args?: WaitArgs): Promise<CommandResult<WaitData>>;
|
|
14
15
|
export declare function readLatest(env: RuntimeEnv, args?: ReadLatestArgs): Promise<CommandResult<ReadLatestData>>;
|
|
15
16
|
export declare function messageStatus(env: RuntimeEnv, args?: MessageStatusArgs): Promise<CommandResult<MessageStatusData>>;
|
|
@@ -3,18 +3,20 @@ import { readPageState } from "../browser/page-state.js";
|
|
|
3
3
|
import { resultError, resultOk } from "../errors.js";
|
|
4
4
|
import { EMPTY_GENERATION_STATE, latestAssistantTurnHasResponseActions, readAssistantGenerationState } from "../dom/generation-state.js";
|
|
5
5
|
import { countPageMessages, isTransientAssistantText, readLatestMessage, readLatestMessageText, readLatestMessageTextSnapshot, readMessages } from "../dom/messages.js";
|
|
6
|
-
import { composerTextbox, copyResponseButtons, sendButton } from "../dom/selectors.js";
|
|
6
|
+
import { composerTextbox, copyResponseButtons, sendButton, stopGenerationButton } from "../dom/selectors.js";
|
|
7
7
|
import { readWaitDomSnapshot, waitTextMetadata } from "../dom/wait-snapshot.js";
|
|
8
8
|
import { normalizeLineBreaks, normalizeWhitespace } from "../dom/visible-text.js";
|
|
9
9
|
import { contextFromPage } from "./context.js";
|
|
10
|
-
import { createDeadline } from "./deadline.js";
|
|
10
|
+
import { createDeadline, remainingMs } from "./deadline.js";
|
|
11
11
|
import { withCommandOutputText } from "./output.js";
|
|
12
12
|
import { createSingleFlightProbe } from "./probes.js";
|
|
13
13
|
import { ensurePage } from "./session.js";
|
|
14
|
+
import { withTimeout } from "./timeouts.js";
|
|
14
15
|
export function isResponseComplete(snapshot) {
|
|
15
16
|
return snapshot.latestText.trim().length > 0
|
|
16
17
|
&& !isTransientAssistantText(snapshot.latestText)
|
|
17
18
|
&& snapshot.textStableForMs >= snapshot.stableMs
|
|
19
|
+
&& snapshot.generation.observed
|
|
18
20
|
&& !snapshot.generation.active
|
|
19
21
|
&& !snapshot.generation.stopped
|
|
20
22
|
&& snapshot.hasResponseActions;
|
|
@@ -125,6 +127,311 @@ export async function submitMessage(env, args = {}) {
|
|
|
125
127
|
return resultError(error instanceof Error ? error : new Error(String(error)), await contextFromPage(page));
|
|
126
128
|
}
|
|
127
129
|
}
|
|
130
|
+
export async function stopGeneration(env, args = {}) {
|
|
131
|
+
const timeoutMs = Math.max(1, args.timeoutMs ?? 5_000);
|
|
132
|
+
const deadline = createDeadline(timeoutMs);
|
|
133
|
+
if (args.confirmStop !== true) {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
status: "needs_confirmation",
|
|
137
|
+
warnings: [],
|
|
138
|
+
blocker: {
|
|
139
|
+
kind: "confirmation",
|
|
140
|
+
code: "stop_generation_confirmation_required",
|
|
141
|
+
fieldPath: "confirmStop",
|
|
142
|
+
message: "Stopping the visible ChatGPT response requires an explicit caller decision.",
|
|
143
|
+
resumable: true
|
|
144
|
+
},
|
|
145
|
+
context: env.page === undefined
|
|
146
|
+
? { timestamp: new Date().toISOString() }
|
|
147
|
+
: await stopContext(env.page, deadline)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
let boot;
|
|
151
|
+
try {
|
|
152
|
+
boot = await withinStopDeadline(deadline, () => ensurePage(env, { minimalContext: true }), "ChatGPT page verification");
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return stopDeadlineResult(error);
|
|
156
|
+
}
|
|
157
|
+
if (!boot.ok)
|
|
158
|
+
return boot;
|
|
159
|
+
const page = env.page;
|
|
160
|
+
const warnings = new Set();
|
|
161
|
+
let stopActivationStarted = false;
|
|
162
|
+
let beforeActivation;
|
|
163
|
+
try {
|
|
164
|
+
const beforeProbeTimeoutMs = Math.min(1_000, Math.max(1, remainingMs(deadline)));
|
|
165
|
+
const beforeResult = await readStopGenerationState(page, deadline, beforeProbeTimeoutMs);
|
|
166
|
+
addWarnings(warnings, beforeResult.warnings);
|
|
167
|
+
if (!beforeResult.ok || !beforeResult.value.observed) {
|
|
168
|
+
if (remainingMs(deadline) <= 0) {
|
|
169
|
+
return stopDeadlineResult(undefined, undefined, [...warnings]);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
status: "blocked",
|
|
174
|
+
warnings: [...warnings],
|
|
175
|
+
blocker: {
|
|
176
|
+
kind: "selector_drift",
|
|
177
|
+
code: "stop_generation_state_unavailable",
|
|
178
|
+
message: "ChatGPT generation state could not be inspected safely, so no control was clicked.",
|
|
179
|
+
resumable: true
|
|
180
|
+
},
|
|
181
|
+
context: await stopContext(page, deadline)
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const before = beforeResult.value;
|
|
185
|
+
beforeActivation = before;
|
|
186
|
+
if (!before.active) {
|
|
187
|
+
return resultOk({
|
|
188
|
+
wasGenerating: false,
|
|
189
|
+
stopped: false,
|
|
190
|
+
signalsBefore: before.signals,
|
|
191
|
+
signalsAfter: before.signals
|
|
192
|
+
}, await stopContext(page, deadline), [...warnings]);
|
|
193
|
+
}
|
|
194
|
+
const resolution = await resolveStopControl(page, deadline);
|
|
195
|
+
if (!resolution.ok) {
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
status: "blocked",
|
|
199
|
+
data: { wasGenerating: true, stopped: false, signalsBefore: before.signals, signalsAfter: before.signals },
|
|
200
|
+
warnings: [...warnings],
|
|
201
|
+
blocker: {
|
|
202
|
+
kind: "selector_drift",
|
|
203
|
+
code: resolution.code,
|
|
204
|
+
message: resolution.message,
|
|
205
|
+
resumable: true
|
|
206
|
+
},
|
|
207
|
+
context: await stopContext(page, deadline)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
stopActivationStarted = true;
|
|
211
|
+
await withinNativeStopDeadline(deadline, timeoutMs => resolution.control.click?.({ timeoutMs })
|
|
212
|
+
?? Promise.reject(new Error("The resolved Stop control does not support click().")), "Stop control click");
|
|
213
|
+
let after = EMPTY_GENERATION_STATE;
|
|
214
|
+
while (remainingMs(deadline) > 0) {
|
|
215
|
+
const afterProbeTimeoutMs = Math.min(1_000, Math.max(1, remainingMs(deadline)));
|
|
216
|
+
const afterResult = await readStopGenerationState(page, deadline, afterProbeTimeoutMs);
|
|
217
|
+
addWarnings(warnings, afterResult.warnings);
|
|
218
|
+
if (afterResult.ok) {
|
|
219
|
+
after = afterResult.value;
|
|
220
|
+
if (after.observed && !after.active) {
|
|
221
|
+
return resultOk({
|
|
222
|
+
wasGenerating: true,
|
|
223
|
+
stopped: true,
|
|
224
|
+
signalsBefore: before.signals,
|
|
225
|
+
signalsAfter: after.signals
|
|
226
|
+
}, await stopContext(page, deadline), [...warnings]);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
await sleepWithinDeadline(page, deadline, 100);
|
|
230
|
+
}
|
|
231
|
+
const data = {
|
|
232
|
+
wasGenerating: true,
|
|
233
|
+
stopped: false,
|
|
234
|
+
signalsBefore: before.signals,
|
|
235
|
+
signalsAfter: after.signals
|
|
236
|
+
};
|
|
237
|
+
return stopDeadlineResult(undefined, data, [...warnings]);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
if (remainingMs(deadline) <= 0 || error instanceof StopDeadlineError) {
|
|
241
|
+
const data = stopActivationStarted && beforeActivation !== undefined
|
|
242
|
+
? {
|
|
243
|
+
wasGenerating: true,
|
|
244
|
+
stopped: false,
|
|
245
|
+
signalsBefore: beforeActivation.signals,
|
|
246
|
+
signalsAfter: beforeActivation.signals
|
|
247
|
+
}
|
|
248
|
+
: undefined;
|
|
249
|
+
return stopDeadlineResult(error, data, [...warnings]);
|
|
250
|
+
}
|
|
251
|
+
return resultError(error instanceof Error ? error : new Error(String(error)), await stopContext(page, deadline));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async function readStopGenerationState(page, deadline, capMs) {
|
|
255
|
+
const timeoutMs = Math.min(capMs, remainingMs(deadline));
|
|
256
|
+
if (timeoutMs <= 0) {
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
timedOut: true,
|
|
260
|
+
warnings: ["Skipped generation state DOM probe because no deadline budget remained."]
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
// readAssistantGenerationState propagates this one budget through its
|
|
265
|
+
// evaluate/content fallbacks. Await it directly so no browser request is
|
|
266
|
+
// abandoned behind an SDK-side Promise.race.
|
|
267
|
+
const value = await readAssistantGenerationState(page, { timeoutMs });
|
|
268
|
+
return { ok: true, value, warnings: [] };
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
warnings: [`generation state DOM probe failed: ${error instanceof Error ? error.message : String(error)}`]
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function resolveStopControl(page, deadline) {
|
|
278
|
+
const candidates = stopGenerationButton(page);
|
|
279
|
+
if (typeof candidates.count !== "function") {
|
|
280
|
+
return {
|
|
281
|
+
ok: false,
|
|
282
|
+
code: "stop_generation_control_unavailable",
|
|
283
|
+
message: "ChatGPT is generating, but the Stop-control candidate set could not be enumerated safely."
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const count = await stopControlCount(candidates, deadline);
|
|
287
|
+
if (count === 0) {
|
|
288
|
+
return {
|
|
289
|
+
ok: false,
|
|
290
|
+
code: "stop_generation_control_unavailable",
|
|
291
|
+
message: "ChatGPT is generating, but no visible Stop control was found."
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (count > 8) {
|
|
295
|
+
return {
|
|
296
|
+
ok: false,
|
|
297
|
+
code: "stop_generation_control_ambiguous",
|
|
298
|
+
message: `ChatGPT exposed ${count} Stop-control candidates; refusing to choose among an unexpectedly broad set.`
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const eligible = [];
|
|
302
|
+
for (let index = 0; index < count; index += 1) {
|
|
303
|
+
const control = count === 1
|
|
304
|
+
? candidates
|
|
305
|
+
: typeof candidates.nth === "function"
|
|
306
|
+
? candidates.nth(index)
|
|
307
|
+
: undefined;
|
|
308
|
+
if (control === undefined || typeof control.evaluate !== "function") {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const evaluationTimeoutMs = Math.min(1_000, Math.max(1, remainingMs(deadline)));
|
|
312
|
+
const scoped = await withinNativeStopDeadline(deadline, timeoutMs => control.evaluate((element) => {
|
|
313
|
+
const button = element;
|
|
314
|
+
if (button.closest("[hidden], [inert], [aria-hidden='true']") !== null)
|
|
315
|
+
return false;
|
|
316
|
+
if (button.disabled || button.getAttribute("aria-disabled") === "true")
|
|
317
|
+
return false;
|
|
318
|
+
const visible = (node) => {
|
|
319
|
+
if (node.hidden || node.closest("[hidden], [inert], [aria-hidden='true']") !== null)
|
|
320
|
+
return false;
|
|
321
|
+
const style = window.getComputedStyle(node);
|
|
322
|
+
const rect = node.getBoundingClientRect();
|
|
323
|
+
return style.display !== "none"
|
|
324
|
+
&& style.visibility !== "hidden"
|
|
325
|
+
&& style.opacity !== "0"
|
|
326
|
+
&& style.pointerEvents !== "none"
|
|
327
|
+
&& (rect.width > 0 || rect.height > 0);
|
|
328
|
+
};
|
|
329
|
+
const textboxes = Array.from(document.querySelectorAll("textarea, [contenteditable='true'], [role='textbox']")).filter(visible);
|
|
330
|
+
const activeComposers = [...new Set(textboxes
|
|
331
|
+
.map(textbox => textbox.closest("form")
|
|
332
|
+
?? textbox.closest("[data-testid*='composer' i]")
|
|
333
|
+
?? textbox.closest("[aria-label*='composer' i]")
|
|
334
|
+
?? textbox.closest("[class*='composer' i]"))
|
|
335
|
+
.filter((value) => value !== null))];
|
|
336
|
+
return activeComposers.length === 1 && activeComposers[0].contains(button);
|
|
337
|
+
}, undefined, { timeoutMs }), `Stop control ${index + 1} visibility and scope check`, evaluationTimeoutMs);
|
|
338
|
+
if (scoped)
|
|
339
|
+
eligible.push(control);
|
|
340
|
+
}
|
|
341
|
+
if (eligible.length === 1) {
|
|
342
|
+
return { ok: true, control: eligible[0] };
|
|
343
|
+
}
|
|
344
|
+
if (eligible.length > 1) {
|
|
345
|
+
return {
|
|
346
|
+
ok: false,
|
|
347
|
+
code: "stop_generation_control_ambiguous",
|
|
348
|
+
message: `ChatGPT exposed ${eligible.length} visible Stop controls in active composer regions; no control was clicked.`
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
ok: false,
|
|
353
|
+
code: "stop_generation_control_unavailable",
|
|
354
|
+
message: "ChatGPT is generating, but no uniquely scoped visible Stop control could be activated."
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
async function stopControlCount(candidates, deadline) {
|
|
358
|
+
if (typeof candidates.allTextContents === "function") {
|
|
359
|
+
const values = await withinNativeStopDeadline(deadline, timeoutMs => candidates.allTextContents({ timeoutMs }), "Stop control enumeration", 1_000);
|
|
360
|
+
return values.length;
|
|
361
|
+
}
|
|
362
|
+
return withinStopDeadline(deadline, () => candidates.count(), "Stop control enumeration");
|
|
363
|
+
}
|
|
364
|
+
async function withinStopDeadline(deadline, operation, label) {
|
|
365
|
+
const budget = remainingMs(deadline);
|
|
366
|
+
if (budget <= 0)
|
|
367
|
+
throw new StopDeadlineError(label);
|
|
368
|
+
try {
|
|
369
|
+
return await withTimeout(operation(), budget, `${label} exceeded the messages.stop deadline.`);
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
if (remainingMs(deadline) <= 0)
|
|
373
|
+
throw new StopDeadlineError(label);
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function withinNativeStopDeadline(deadline, operation, label, capMs = 2_000) {
|
|
378
|
+
const timeoutMs = Math.min(capMs, remainingMs(deadline));
|
|
379
|
+
if (timeoutMs <= 0)
|
|
380
|
+
throw new StopDeadlineError(label);
|
|
381
|
+
try {
|
|
382
|
+
// The Chrome bridge owns this timeout. Unlike Promise.race, a native
|
|
383
|
+
// timeout terminates the browser request instead of abandoning it in flight.
|
|
384
|
+
return await operation(timeoutMs);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (remainingMs(deadline) <= 0 || isNativeBrowserTimeout(error)) {
|
|
388
|
+
throw new StopDeadlineError(label);
|
|
389
|
+
}
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function isNativeBrowserTimeout(error) {
|
|
394
|
+
return error instanceof Error && /timed?\s*out|timeout/i.test(error.message);
|
|
395
|
+
}
|
|
396
|
+
async function sleepWithinDeadline(_page, deadline, requestedMs) {
|
|
397
|
+
const waitMs = Math.min(requestedMs, Math.max(0, remainingMs(deadline) - 1));
|
|
398
|
+
if (waitMs <= 0)
|
|
399
|
+
return;
|
|
400
|
+
// Polling does not require a browser round trip. A host timer cannot leave a
|
|
401
|
+
// bridge request stranded after the Stop deadline.
|
|
402
|
+
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
403
|
+
}
|
|
404
|
+
async function stopContext(page, _deadline) {
|
|
405
|
+
return contextFromPage(page, {}, { minimal: true });
|
|
406
|
+
}
|
|
407
|
+
function stopDeadlineResult(error, data, warnings = []) {
|
|
408
|
+
const message = data?.wasGenerating === true
|
|
409
|
+
? "Stop activation reached its browser-native deadline and was terminated, but the click may already have taken effect. Inspect the visible generation state and do not retry automatically."
|
|
410
|
+
: "ChatGPT generation could not be inspected and stopped before the single operation deadline.";
|
|
411
|
+
const result = {
|
|
412
|
+
ok: false,
|
|
413
|
+
status: "timeout",
|
|
414
|
+
warnings: error === undefined
|
|
415
|
+
? warnings
|
|
416
|
+
: [...warnings, error instanceof Error ? error.message : String(error)],
|
|
417
|
+
blocker: {
|
|
418
|
+
kind: "selector_drift",
|
|
419
|
+
code: data?.wasGenerating === true ? "stop_generation_unverified" : "stop_generation_deadline_exhausted",
|
|
420
|
+
message,
|
|
421
|
+
resumable: data?.wasGenerating !== true
|
|
422
|
+
},
|
|
423
|
+
context: { timestamp: new Date().toISOString() }
|
|
424
|
+
};
|
|
425
|
+
if (data !== undefined)
|
|
426
|
+
result.data = data;
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
class StopDeadlineError extends Error {
|
|
430
|
+
constructor(label) {
|
|
431
|
+
super(`${label} could not complete before the messages.stop deadline.`);
|
|
432
|
+
this.name = "StopDeadlineError";
|
|
433
|
+
}
|
|
434
|
+
}
|
|
128
435
|
async function clickSendControl(page) {
|
|
129
436
|
try {
|
|
130
437
|
await sendButton(page).click?.();
|
|
@@ -355,6 +662,7 @@ export async function waitForMessage(env, args = {}) {
|
|
|
355
662
|
&& snapshot.text.length > 0
|
|
356
663
|
&& !snapshot.text.transient
|
|
357
664
|
&& Date.now() - lastChangedAt >= stableMs
|
|
665
|
+
&& snapshot.generation.observed
|
|
358
666
|
&& !snapshot.generation.active
|
|
359
667
|
&& !snapshot.generation.stopped
|
|
360
668
|
&& hasResponseActions;
|