codex-chatgpt-control 0.2.0-alpha.1 → 0.3.0-alpha.1
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 +8 -0
- package/contracts/v1/fixtures/backend-capabilities.json +1 -0
- package/contracts/v1/fixtures/command-descriptors.json +33 -2
- package/contracts/v1/fixtures/doctor-scenario-preflight.json +12 -1
- package/contracts/v1/fixtures/help-root.json +1 -1
- package/contracts/v1/fixtures/reports-create-redacted.json +1 -1
- package/contracts/v1/parity-suite.json +8 -1
- package/dist/codex-chatgpt-control-backend.mjs +1199 -159
- package/dist/codex-chatgpt-control.bundle.mjs +1198 -156
- 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/clipboard.d.ts +11 -0
- package/dist/src/browser/clipboard.js +34 -7
- package/dist/src/browser/page-state.js +12 -2
- package/dist/src/client.d.ts +2 -1
- package/dist/src/client.js +3 -2
- package/dist/src/commands/context.js +3 -1
- package/dist/src/commands/conversation.d.ts +15 -0
- package/dist/src/commands/conversation.js +44 -0
- package/dist/src/commands/deadline.d.ts +8 -0
- package/dist/src/commands/deadline.js +14 -0
- package/dist/src/commands/doctor.js +9 -2
- package/dist/src/commands/files.js +62 -8
- package/dist/src/commands/messages.js +212 -72
- package/dist/src/commands/modes.d.ts +4 -1
- package/dist/src/commands/modes.js +215 -30
- package/dist/src/commands/probes.d.ts +15 -0
- package/dist/src/commands/probes.js +64 -0
- package/dist/src/commands/registry.js +34 -2
- package/dist/src/commands/reports.js +1 -1
- package/dist/src/commands/threads.js +2 -24
- package/dist/src/dom/generation-state.d.ts +7 -0
- package/dist/src/dom/generation-state.js +7 -1
- package/dist/src/dom/label-match.d.ts +3 -0
- package/dist/src/dom/label-match.js +25 -0
- package/dist/src/dom/locale/am.d.ts +6 -0
- package/dist/src/dom/locale/am.js +6 -0
- package/dist/src/dom/locale/ar.d.ts +7 -0
- package/dist/src/dom/locale/ar.js +7 -0
- package/dist/src/dom/locale/bg.d.ts +7 -0
- package/dist/src/dom/locale/bg.js +7 -0
- package/dist/src/dom/locale/bn.d.ts +7 -0
- package/dist/src/dom/locale/bn.js +7 -0
- package/dist/src/dom/locale/bs.d.ts +6 -0
- package/dist/src/dom/locale/bs.js +6 -0
- package/dist/src/dom/locale/ca.d.ts +6 -0
- package/dist/src/dom/locale/ca.js +6 -0
- package/dist/src/dom/locale/cs.d.ts +6 -0
- package/dist/src/dom/locale/cs.js +6 -0
- package/dist/src/dom/locale/da.d.ts +5 -0
- package/dist/src/dom/locale/da.js +5 -0
- package/dist/src/dom/locale/de.d.ts +6 -0
- package/dist/src/dom/locale/de.js +6 -0
- package/dist/src/dom/locale/el.d.ts +6 -0
- package/dist/src/dom/locale/el.js +6 -0
- package/dist/src/dom/locale/en.d.ts +14 -0
- package/dist/src/dom/locale/en.js +15 -0
- package/dist/src/dom/locale/es-419.d.ts +6 -0
- package/dist/src/dom/locale/es-419.js +6 -0
- package/dist/src/dom/locale/es-ES.d.ts +6 -0
- package/dist/src/dom/locale/es-ES.js +6 -0
- package/dist/src/dom/locale/et.d.ts +6 -0
- package/dist/src/dom/locale/et.js +6 -0
- package/dist/src/dom/locale/fa.d.ts +7 -0
- package/dist/src/dom/locale/fa.js +7 -0
- package/dist/src/dom/locale/fi.d.ts +6 -0
- package/dist/src/dom/locale/fi.js +6 -0
- package/dist/src/dom/locale/fr-CA.d.ts +6 -0
- package/dist/src/dom/locale/fr-CA.js +6 -0
- package/dist/src/dom/locale/fr-FR.d.ts +5 -0
- package/dist/src/dom/locale/fr-FR.js +5 -0
- package/dist/src/dom/locale/gu.d.ts +6 -0
- package/dist/src/dom/locale/gu.js +6 -0
- package/dist/src/dom/locale/hi.d.ts +6 -0
- package/dist/src/dom/locale/hi.js +6 -0
- package/dist/src/dom/locale/hr.d.ts +5 -0
- package/dist/src/dom/locale/hr.js +5 -0
- package/dist/src/dom/locale/hu.d.ts +6 -0
- package/dist/src/dom/locale/hu.js +6 -0
- package/dist/src/dom/locale/hy.d.ts +7 -0
- package/dist/src/dom/locale/hy.js +7 -0
- package/dist/src/dom/locale/id.d.ts +6 -0
- package/dist/src/dom/locale/id.js +6 -0
- package/dist/src/dom/locale/index.d.ts +5 -1
- package/dist/src/dom/locale/index.js +37 -0
- package/dist/src/dom/locale/is.d.ts +6 -0
- package/dist/src/dom/locale/is.js +6 -0
- package/dist/src/dom/locale/it.d.ts +6 -0
- package/dist/src/dom/locale/it.js +6 -0
- package/dist/src/dom/locale/ja.d.ts +6 -0
- package/dist/src/dom/locale/ja.js +6 -0
- package/dist/src/dom/locale/ka.d.ts +6 -0
- package/dist/src/dom/locale/ka.js +6 -0
- package/dist/src/dom/locale/kk.d.ts +6 -0
- package/dist/src/dom/locale/kk.js +6 -0
- package/dist/src/dom/locale/kn.d.ts +7 -0
- package/dist/src/dom/locale/kn.js +7 -0
- package/dist/src/dom/locale/ko.d.ts +6 -0
- package/dist/src/dom/locale/ko.js +6 -0
- package/dist/src/dom/locale/lt.d.ts +7 -0
- package/dist/src/dom/locale/lt.js +7 -0
- package/dist/src/dom/locale/lv.d.ts +6 -0
- package/dist/src/dom/locale/lv.js +6 -0
- package/dist/src/dom/locale/mk.d.ts +5 -0
- package/dist/src/dom/locale/mk.js +5 -0
- package/dist/src/dom/locale/ml.d.ts +7 -0
- package/dist/src/dom/locale/ml.js +7 -0
- package/dist/src/dom/locale/mn.d.ts +7 -0
- package/dist/src/dom/locale/mn.js +7 -0
- package/dist/src/dom/locale/mr.d.ts +7 -0
- package/dist/src/dom/locale/mr.js +7 -0
- package/dist/src/dom/locale/ms.d.ts +6 -0
- package/dist/src/dom/locale/ms.js +6 -0
- package/dist/src/dom/locale/my.d.ts +6 -0
- package/dist/src/dom/locale/my.js +6 -0
- package/dist/src/dom/locale/nb.d.ts +6 -0
- package/dist/src/dom/locale/nb.js +6 -0
- package/dist/src/dom/locale/nl.d.ts +6 -0
- package/dist/src/dom/locale/nl.js +6 -0
- package/dist/src/dom/locale/pa.d.ts +7 -0
- package/dist/src/dom/locale/pa.js +7 -0
- package/dist/src/dom/locale/pl.d.ts +6 -0
- package/dist/src/dom/locale/pl.js +6 -0
- package/dist/src/dom/locale/pt-BR.d.ts +6 -0
- package/dist/src/dom/locale/pt-BR.js +6 -0
- package/dist/src/dom/locale/pt-PT.d.ts +6 -0
- package/dist/src/dom/locale/pt-PT.js +6 -0
- package/dist/src/dom/locale/ro.d.ts +5 -0
- package/dist/src/dom/locale/ro.js +5 -0
- package/dist/src/dom/locale/ru.d.ts +3 -0
- package/dist/src/dom/locale/ru.js +3 -0
- package/dist/src/dom/locale/sk.d.ts +6 -0
- package/dist/src/dom/locale/sk.js +6 -0
- package/dist/src/dom/locale/sl.d.ts +6 -0
- package/dist/src/dom/locale/sl.js +6 -0
- package/dist/src/dom/locale/so.d.ts +6 -0
- package/dist/src/dom/locale/so.js +6 -0
- package/dist/src/dom/locale/sq.d.ts +6 -0
- package/dist/src/dom/locale/sq.js +6 -0
- package/dist/src/dom/locale/sr.d.ts +3 -0
- package/dist/src/dom/locale/sr.js +3 -0
- package/dist/src/dom/locale/sv.d.ts +6 -0
- package/dist/src/dom/locale/sv.js +6 -0
- package/dist/src/dom/locale/sw.d.ts +6 -0
- package/dist/src/dom/locale/sw.js +6 -0
- package/dist/src/dom/locale/ta.d.ts +7 -0
- package/dist/src/dom/locale/ta.js +7 -0
- package/dist/src/dom/locale/te.d.ts +7 -0
- package/dist/src/dom/locale/te.js +7 -0
- package/dist/src/dom/locale/th.d.ts +6 -0
- package/dist/src/dom/locale/th.js +6 -0
- package/dist/src/dom/locale/tr.d.ts +6 -0
- package/dist/src/dom/locale/tr.js +6 -0
- package/dist/src/dom/locale/types.d.ts +8 -1
- package/dist/src/dom/locale/uk.d.ts +6 -0
- package/dist/src/dom/locale/uk.js +6 -0
- package/dist/src/dom/locale/ur.d.ts +6 -0
- package/dist/src/dom/locale/ur.js +6 -0
- package/dist/src/dom/locale/vi.d.ts +6 -0
- package/dist/src/dom/locale/vi.js +6 -0
- package/dist/src/dom/locale/zh-HK.d.ts +7 -0
- package/dist/src/dom/locale/zh-HK.js +7 -0
- package/dist/src/dom/locale/zh-Hans.d.ts +7 -0
- package/dist/src/dom/locale/zh-Hans.js +7 -0
- package/dist/src/dom/locale/zh-TW.d.ts +7 -0
- package/dist/src/dom/locale/zh-TW.js +7 -0
- package/dist/src/dom/menus.js +13 -2
- package/dist/src/dom/wait-snapshot.d.ts +43 -0
- package/dist/src/dom/wait-snapshot.js +127 -0
- 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.js +99 -13
- package/dist/src/types.d.ts +28 -0
- package/package.json +2 -2
- package/references/backend-protocol.md +6 -4
- package/references/localization.md +14 -4
- package/references/python-parity.md +5 -3
- package/references/troubleshooting.md +28 -5
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { readPageState } from "../browser/page-state.js";
|
|
2
3
|
import { resultError, resultOk } from "../errors.js";
|
|
3
|
-
import { latestAssistantTurnHasResponseActions, readAssistantGenerationState } from "../dom/generation-state.js";
|
|
4
|
+
import { EMPTY_GENERATION_STATE, latestAssistantTurnHasResponseActions, readAssistantGenerationState } from "../dom/generation-state.js";
|
|
4
5
|
import { countPageMessages, isTransientAssistantText, readLatestMessage, readLatestMessageText, readLatestMessageTextSnapshot, readMessages } from "../dom/messages.js";
|
|
5
|
-
import { composerTextbox, sendButton } from "../dom/selectors.js";
|
|
6
|
+
import { composerTextbox, copyResponseButtons, sendButton } from "../dom/selectors.js";
|
|
7
|
+
import { readWaitDomSnapshot, waitTextMetadata } from "../dom/wait-snapshot.js";
|
|
6
8
|
import { normalizeLineBreaks, normalizeWhitespace } from "../dom/visible-text.js";
|
|
7
9
|
import { contextFromPage } from "./context.js";
|
|
10
|
+
import { createDeadline } from "./deadline.js";
|
|
8
11
|
import { withCommandOutputText } from "./output.js";
|
|
12
|
+
import { createSingleFlightProbe } from "./probes.js";
|
|
9
13
|
import { bootstrap } from "./session.js";
|
|
10
14
|
export function isResponseComplete(snapshot) {
|
|
11
15
|
return snapshot.latestText.trim().length > 0
|
|
@@ -222,7 +226,16 @@ async function readVisibleTextForSubmit(page) {
|
|
|
222
226
|
if (typeof page.evaluate !== "function") {
|
|
223
227
|
return undefined;
|
|
224
228
|
}
|
|
225
|
-
|
|
229
|
+
// Attachment/upload status renders inside the composer form; prefer that region over
|
|
230
|
+
// the whole page so the not-ready poll does not serialize the full document text.
|
|
231
|
+
return page.evaluate(() => {
|
|
232
|
+
const composerForm = document.querySelector("main form") ?? document.querySelector("form");
|
|
233
|
+
const scopedText = composerForm?.innerText;
|
|
234
|
+
if (scopedText !== undefined && scopedText.trim().length > 0) {
|
|
235
|
+
return scopedText;
|
|
236
|
+
}
|
|
237
|
+
return document.body?.innerText ?? "";
|
|
238
|
+
});
|
|
226
239
|
}
|
|
227
240
|
async function sendTimeoutWarnings(page) {
|
|
228
241
|
const state = await readSendButtonState(page).catch(() => undefined);
|
|
@@ -259,76 +272,123 @@ export async function waitForMessage(env, args = {}) {
|
|
|
259
272
|
const stableMs = args.stableMs ?? (args.mode === "deep_research" ? 10_000 : 2_000);
|
|
260
273
|
const pollMs = args.pollMs ?? 750;
|
|
261
274
|
const started = Date.now();
|
|
262
|
-
|
|
275
|
+
const deadline = createDeadline(timeoutMs, started);
|
|
276
|
+
const probeTimeoutMs = Math.max(50, Math.min(1000, Math.max(pollMs, Math.floor(timeoutMs / 4))));
|
|
277
|
+
const waitWarnings = new Set();
|
|
278
|
+
// One combined DOM probe per poll: counts, latest-text metadata, generation state, and
|
|
279
|
+
// response actions come back in a single evaluate, sampled from the same DOM instant.
|
|
280
|
+
// The full assistant text never crosses the bridge during polling; it is fetched once
|
|
281
|
+
// at loop exit. Page-state blocker scans run on a coarser cadence below.
|
|
282
|
+
const waitSnapshotProbe = createSingleFlightProbe("wait snapshot", readWaitDomSnapshot);
|
|
283
|
+
const pageStateProbe = createSingleFlightProbe("page state", readPageState);
|
|
284
|
+
const PAGE_STATE_POLL_STRIDE = 4;
|
|
285
|
+
let pollIndex = 0;
|
|
286
|
+
let lastTargetKey = "";
|
|
263
287
|
let lastChangedAt = Date.now();
|
|
288
|
+
let lastObservedTextLength = 0;
|
|
264
289
|
let latestAssistantCount = await countPageMessages(page, "assistant").catch(() => 0);
|
|
265
290
|
while (Date.now() - started < timeoutMs) {
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const progress = await readAssistantProgressSnapshot(page)
|
|
277
|
-
.catch(() => fallbackAssistantProgressSnapshot(page, latestAssistantCount));
|
|
278
|
-
latestAssistantCount = progress.assistantTurnCount;
|
|
279
|
-
const targetReached = waitTargetReached(args, progress);
|
|
280
|
-
const latestText = targetReached ? normalizeWhitespace(progress.latestText ?? "") : "";
|
|
281
|
-
if (latestText !== lastTargetText) {
|
|
282
|
-
lastTargetText = latestText;
|
|
291
|
+
const probeResult = await waitSnapshotProbe(page, deadline, { timeoutMs: probeTimeoutMs });
|
|
292
|
+
addWarnings(waitWarnings, probeResult.warnings);
|
|
293
|
+
const snapshot = await waitSnapshotFromProbeResult(page, probeResult, latestAssistantCount);
|
|
294
|
+
latestAssistantCount = snapshot.assistantTurnCount;
|
|
295
|
+
const targetReached = waitTargetReached(args, snapshot);
|
|
296
|
+
const targetKey = targetReached && snapshot.text.length > 0
|
|
297
|
+
? `${snapshot.text.length}:${snapshot.text.hash}`
|
|
298
|
+
: "";
|
|
299
|
+
if (targetKey !== lastTargetKey) {
|
|
300
|
+
lastTargetKey = targetKey;
|
|
283
301
|
lastChangedAt = Date.now();
|
|
284
302
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
303
|
+
if (targetReached && snapshot.text.length > 0) {
|
|
304
|
+
lastObservedTextLength = snapshot.text.length;
|
|
305
|
+
}
|
|
306
|
+
if (pollIndex % PAGE_STATE_POLL_STRIDE === 0) {
|
|
307
|
+
const state = await pageStateFromProbe(pageStateProbe(page, deadline, { timeoutMs: probeTimeoutMs }), waitWarnings);
|
|
308
|
+
if (state?.blocker !== undefined && state.blocker.kind !== "modal") {
|
|
309
|
+
return {
|
|
310
|
+
ok: false,
|
|
311
|
+
status: "blocked",
|
|
312
|
+
warnings: [...waitWarnings],
|
|
313
|
+
blocker: state.blocker,
|
|
314
|
+
context: await contextFromPage(page)
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
pollIndex += 1;
|
|
319
|
+
const hasResponseActions = await resolveResponseActions(page, snapshot);
|
|
320
|
+
if (targetReached && snapshot.generation.stopped && snapshot.text.length > 0) {
|
|
321
|
+
const stoppedText = normalizeWhitespace(await fetchLatestAssistantText(page) ?? "");
|
|
322
|
+
// A failed re-read must not fabricate an empty capture: real text was observed in
|
|
323
|
+
// the snapshot, so omit response content entirely and tell the caller how to get it.
|
|
324
|
+
const data = stoppedText.length > 0
|
|
325
|
+
? waitDataFromText(args, false, stoppedText, latestAssistantCount, Date.now() - started)
|
|
326
|
+
: waitDataWithoutText(latestAssistantCount, Date.now() - started);
|
|
327
|
+
if (stoppedText.length === 0) {
|
|
328
|
+
waitWarnings.add(`The interrupted assistant text (~${snapshot.text.length} chars observed) could not be re-read at wait exit; call messages.readLatest on the same thread to capture it.`);
|
|
329
|
+
}
|
|
293
330
|
return withCommandOutputText({
|
|
294
331
|
ok: false,
|
|
295
332
|
status: "partial",
|
|
296
|
-
data
|
|
297
|
-
complete: false,
|
|
298
|
-
responseText: latestText,
|
|
299
|
-
assistantTurnCount: latestAssistantCount,
|
|
300
|
-
elapsedMs: Date.now() - started
|
|
301
|
-
},
|
|
333
|
+
data,
|
|
302
334
|
warnings: [
|
|
335
|
+
...waitWarnings,
|
|
336
|
+
...(stoppedText.length > 0 ? responseContentWarnings(args, false) : []),
|
|
303
337
|
"ChatGPT generation appears to have been stopped or interrupted before completion.",
|
|
304
338
|
...snapshot.generation.signals.map(signal => `Generation state signal: ${signal}`)
|
|
305
339
|
],
|
|
306
340
|
context: await contextFromPage(page)
|
|
307
341
|
});
|
|
308
342
|
}
|
|
309
|
-
|
|
310
|
-
|
|
343
|
+
const metadataComplete = targetReached
|
|
344
|
+
&& snapshot.text.length > 0
|
|
345
|
+
&& !snapshot.text.transient
|
|
346
|
+
&& Date.now() - lastChangedAt >= stableMs
|
|
347
|
+
&& !snapshot.generation.active
|
|
348
|
+
&& !snapshot.generation.stopped
|
|
349
|
+
&& hasResponseActions;
|
|
350
|
+
if (metadataComplete) {
|
|
351
|
+
// Fetch the text once and confirm it still matches the stable snapshot before
|
|
352
|
+
// declaring completion; a hash mismatch means the answer moved on mid-fetch.
|
|
353
|
+
const latestText = normalizeWhitespace(await fetchLatestAssistantText(page) ?? "");
|
|
354
|
+
const fetchedMetadata = waitTextMetadata(latestText);
|
|
355
|
+
const completionSnapshot = {
|
|
356
|
+
latestText,
|
|
357
|
+
stableMs,
|
|
358
|
+
textStableForMs: Date.now() - lastChangedAt,
|
|
359
|
+
generation: snapshot.generation,
|
|
360
|
+
hasResponseActions
|
|
361
|
+
};
|
|
362
|
+
if (fetchedMetadata.hash === snapshot.text.hash && isResponseComplete(completionSnapshot)) {
|
|
363
|
+
const data = waitDataFromText(args, true, latestText, latestAssistantCount, Date.now() - started);
|
|
364
|
+
return withCommandOutputText(resultOk(data, await contextFromPage(page), [...waitWarnings, ...responseContentWarnings(args, true)]));
|
|
365
|
+
}
|
|
366
|
+
if (latestText.length > 0) {
|
|
367
|
+
lastTargetKey = `${fetchedMetadata.length}:${fetchedMetadata.hash}`;
|
|
368
|
+
lastChangedAt = Date.now();
|
|
369
|
+
lastObservedTextLength = fetchedMetadata.length;
|
|
370
|
+
}
|
|
311
371
|
}
|
|
312
372
|
await sleep(page, pollMs);
|
|
313
373
|
}
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
374
|
+
if (lastObservedTextLength > 0) {
|
|
375
|
+
const partialText = await fetchLatestAssistantText(page);
|
|
376
|
+
if (partialText !== undefined && normalizeWhitespace(partialText).length > 0) {
|
|
377
|
+
const data = waitDataFromText(args, false, normalizeWhitespace(partialText), latestAssistantCount, Date.now() - started);
|
|
378
|
+
return withCommandOutputText({
|
|
379
|
+
ok: false,
|
|
380
|
+
status: "partial",
|
|
381
|
+
data,
|
|
382
|
+
warnings: [...waitWarnings, ...responseContentWarnings(args, false), "Timed out after receiving partial assistant text."],
|
|
383
|
+
context: await contextFromPage(page)
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
waitWarnings.add(`Partial assistant text (${lastObservedTextLength} chars) was observed during polling but could not be re-read at wait exit.`);
|
|
327
387
|
}
|
|
328
388
|
return {
|
|
329
389
|
ok: false,
|
|
330
390
|
status: "timeout",
|
|
331
|
-
warnings: [],
|
|
391
|
+
warnings: [...waitWarnings],
|
|
332
392
|
error: {
|
|
333
393
|
name: "WaitTimeout",
|
|
334
394
|
message: "No assistant response appeared before the timeout.",
|
|
@@ -337,6 +397,107 @@ export async function waitForMessage(env, args = {}) {
|
|
|
337
397
|
context: await contextFromPage(page)
|
|
338
398
|
};
|
|
339
399
|
}
|
|
400
|
+
async function fetchLatestAssistantText(page) {
|
|
401
|
+
const first = await readLatestMessageText(page, "assistant").catch(() => undefined);
|
|
402
|
+
if (first !== undefined) {
|
|
403
|
+
return first;
|
|
404
|
+
}
|
|
405
|
+
// One retry: exit-time reads race DOM reflow/navigation, and a transient failure here
|
|
406
|
+
// would otherwise discard an answer the polling snapshots proved exists.
|
|
407
|
+
await sleep(page, 150);
|
|
408
|
+
return readLatestMessageText(page, "assistant").catch(() => undefined);
|
|
409
|
+
}
|
|
410
|
+
function waitDataWithoutText(assistantTurnCount, elapsedMs) {
|
|
411
|
+
return { complete: false, assistantTurnCount, elapsedMs };
|
|
412
|
+
}
|
|
413
|
+
async function resolveResponseActions(page, snapshot) {
|
|
414
|
+
if (snapshot.hasResponseActions !== undefined) {
|
|
415
|
+
return snapshot.hasResponseActions;
|
|
416
|
+
}
|
|
417
|
+
// No conversation-turn markers: fall back to the structural copy-button locator, as
|
|
418
|
+
// the standalone response-actions probe does.
|
|
419
|
+
try {
|
|
420
|
+
const copyButtons = copyResponseButtons(page);
|
|
421
|
+
const count = await copyButtons.count?.();
|
|
422
|
+
if (count !== undefined) {
|
|
423
|
+
return count > 0;
|
|
424
|
+
}
|
|
425
|
+
return await copyButtons.isVisible?.() === true;
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async function waitSnapshotFromProbeResult(page, result, previousAssistantTurnCount) {
|
|
432
|
+
if (result.ok && result.value !== undefined) {
|
|
433
|
+
return result.value;
|
|
434
|
+
}
|
|
435
|
+
if (!result.ok && (result.timedOut === true || result.skipped === true)) {
|
|
436
|
+
return {
|
|
437
|
+
turnCount: 0,
|
|
438
|
+
assistantTurnCount: previousAssistantTurnCount,
|
|
439
|
+
text: waitTextMetadata(""),
|
|
440
|
+
generation: EMPTY_GENERATION_STATE,
|
|
441
|
+
hasResponseActions: false
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return fallbackWaitSnapshot(page, previousAssistantTurnCount);
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Degraded snapshot for pages where the combined evaluate is unavailable or failed.
|
|
448
|
+
* Reuses the standalone facet probes, which carry their own content/locator fallbacks;
|
|
449
|
+
* text metadata is computed SDK-side from the extracted text.
|
|
450
|
+
*/
|
|
451
|
+
async function fallbackWaitSnapshot(page, previousAssistantTurnCount) {
|
|
452
|
+
const progress = await fallbackAssistantProgressSnapshot(page, previousAssistantTurnCount);
|
|
453
|
+
const generation = await readAssistantGenerationState(page).catch(() => EMPTY_GENERATION_STATE);
|
|
454
|
+
const hasResponseActions = await latestAssistantTurnHasResponseActions(page).catch(() => false);
|
|
455
|
+
const snapshot = {
|
|
456
|
+
turnCount: progress.turnCount ?? 0,
|
|
457
|
+
assistantTurnCount: progress.assistantTurnCount,
|
|
458
|
+
text: waitTextMetadata(progress.latestText),
|
|
459
|
+
generation,
|
|
460
|
+
hasResponseActions
|
|
461
|
+
};
|
|
462
|
+
if (progress.latestAssistantTurnIndex !== undefined) {
|
|
463
|
+
snapshot.latestAssistantTurnIndex = progress.latestAssistantTurnIndex;
|
|
464
|
+
}
|
|
465
|
+
return snapshot;
|
|
466
|
+
}
|
|
467
|
+
function waitDataFromText(args, complete, responseText, assistantTurnCount, elapsedMs) {
|
|
468
|
+
const data = {
|
|
469
|
+
complete,
|
|
470
|
+
assistantTurnCount,
|
|
471
|
+
elapsedMs
|
|
472
|
+
};
|
|
473
|
+
if (args.responseContent === "metadata") {
|
|
474
|
+
data.responseContent = "metadata";
|
|
475
|
+
data.responseChars = responseText.length;
|
|
476
|
+
data.responseSha256 = createHash("sha256").update(responseText).digest("hex");
|
|
477
|
+
return data;
|
|
478
|
+
}
|
|
479
|
+
data.responseText = responseText;
|
|
480
|
+
return data;
|
|
481
|
+
}
|
|
482
|
+
function responseContentWarnings(args, complete) {
|
|
483
|
+
if (args.responseContent !== "metadata")
|
|
484
|
+
return [];
|
|
485
|
+
return [
|
|
486
|
+
complete
|
|
487
|
+
? "Assistant response text was omitted because responseContent is metadata; call readLatest to capture the completed answer."
|
|
488
|
+
: "Partial assistant text was omitted because responseContent is metadata; call wait again on the same thread or readLatest after completion."
|
|
489
|
+
];
|
|
490
|
+
}
|
|
491
|
+
async function pageStateFromProbe(probe, warnings) {
|
|
492
|
+
const result = await probe;
|
|
493
|
+
addWarnings(warnings, result.warnings);
|
|
494
|
+
return result.ok ? result.value : undefined;
|
|
495
|
+
}
|
|
496
|
+
function addWarnings(target, warnings) {
|
|
497
|
+
for (const warning of warnings) {
|
|
498
|
+
target.add(warning);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
340
501
|
export async function readLatest(env, args = {}) {
|
|
341
502
|
const boot = await ensurePage(env);
|
|
342
503
|
if (!boot.ok) {
|
|
@@ -601,27 +762,6 @@ function renderSubmittedTurnMarkdownSyntax(text, preserveFenceLanguage = true) {
|
|
|
601
762
|
.replace(/\*([^*]+)\*/g, "$1")
|
|
602
763
|
.replace(/_([^_]+)_/g, "$1");
|
|
603
764
|
}
|
|
604
|
-
async function readAssistantProgressSnapshot(page) {
|
|
605
|
-
if (typeof page.evaluate === "function") {
|
|
606
|
-
return page.evaluate(() => {
|
|
607
|
-
const nodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
|
|
608
|
-
const assistantNodes = nodes.filter(node => node.getAttribute("data-message-author-role") === "assistant");
|
|
609
|
-
const latestAssistant = assistantNodes.at(-1);
|
|
610
|
-
const latestAssistantTurnIndex = latestAssistant === undefined ? undefined : nodes.indexOf(latestAssistant) + 1;
|
|
611
|
-
const snapshot = {
|
|
612
|
-
turnCount: nodes.length,
|
|
613
|
-
assistantTurnCount: assistantNodes.length
|
|
614
|
-
};
|
|
615
|
-
const latestText = latestAssistant?.innerText ?? latestAssistant?.textContent ?? undefined;
|
|
616
|
-
if (latestText !== undefined)
|
|
617
|
-
snapshot.latestText = latestText;
|
|
618
|
-
if (latestAssistantTurnIndex !== undefined)
|
|
619
|
-
snapshot.latestAssistantTurnIndex = latestAssistantTurnIndex;
|
|
620
|
-
return snapshot;
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
return fallbackAssistantProgressSnapshot(page, 0);
|
|
624
|
-
}
|
|
625
765
|
async function fallbackAssistantProgressSnapshot(page, previousAssistantTurnCount) {
|
|
626
766
|
const messages = await readMessages(page, { format: "normalized_text" }).catch(() => undefined);
|
|
627
767
|
if (messages !== undefined) {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { CommandResult, RuntimeEnv, SelectToolArgs, SetModeArgs } from "../types.js";
|
|
1
|
+
import type { CommandResult, GetModeArgs, RuntimeEnv, SelectToolArgs, SetModeArgs } from "../types.js";
|
|
2
2
|
export declare function setMode(env: RuntimeEnv, args: SetModeArgs): Promise<CommandResult<{
|
|
3
3
|
selected: string[];
|
|
4
4
|
candidates: string[];
|
|
5
5
|
}>>;
|
|
6
|
+
export declare function getMode(env: RuntimeEnv, args?: GetModeArgs): Promise<CommandResult<{
|
|
7
|
+
modes: string[];
|
|
8
|
+
}>>;
|
|
6
9
|
export declare function selectTool(env: RuntimeEnv, args: SelectToolArgs): Promise<CommandResult<{
|
|
7
10
|
selected?: string;
|
|
8
11
|
candidates: string[];
|