codex-chatgpt-control 0.2.0-alpha.1 → 0.5.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 +15 -0
- package/README.md +42 -2
- package/contracts/v1/fixtures/backend-capabilities.json +11 -0
- package/contracts/v1/fixtures/command-descriptors.json +289 -7
- package/contracts/v1/fixtures/describe-runner-run.json +5 -2
- package/contracts/v1/fixtures/doctor-scenario-preflight.json +45 -2
- package/contracts/v1/fixtures/help-root.json +1 -1
- package/contracts/v1/fixtures/output-json-parse-success.json +9 -0
- package/contracts/v1/fixtures/reports-create-redacted.json +1 -1
- package/contracts/v1/fixtures/run-timeout-partial.json +15 -4
- package/contracts/v1/fixtures/stream-in-progress.ndjson +3 -0
- package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
- package/contracts/v1/fixtures/surface-chat-legacy.json +35 -0
- package/contracts/v1/fixtures/surface-chat-simplified.json +37 -0
- package/contracts/v1/fixtures/surface-sidebar-false-positive.json +29 -0
- package/contracts/v1/fixtures/surface-work-advanced.json +45 -0
- package/contracts/v1/fixtures/surface-work-basic.json +38 -0
- package/contracts/v1/fixtures/workflow-ask-success.json +8 -0
- package/contracts/v1/manifest.json +32 -1
- package/contracts/v1/parity-suite.json +121 -1
- package/contracts/v1/schemas/backend-request.schema.json +15 -0
- package/contracts/v1/schemas/manifest.schema.json +6 -3
- package/contracts/v1/schemas/surface-profile.schema.json +166 -0
- package/dist/codex-chatgpt-control-backend.mjs +3198 -340
- package/dist/codex-chatgpt-control.bundle.mjs +4454 -1555
- package/dist/src/backend/client.d.ts +26 -1
- package/dist/src/backend/client.js +23 -1
- package/dist/src/backend/protocol.d.ts +1 -1
- package/dist/src/backend/protocol.js +11 -0
- package/dist/src/backend/session.js +22 -0
- package/dist/src/browser/attach.d.ts +1 -0
- package/dist/src/browser/attach.js +3 -3
- package/dist/src/browser/clipboard.d.ts +11 -0
- package/dist/src/browser/clipboard.js +34 -7
- package/dist/src/browser/page-state.js +17 -4
- package/dist/src/client.d.ts +32 -1
- package/dist/src/client.js +72 -16
- package/dist/src/commands/artifacts.js +1 -7
- package/dist/src/commands/configuration.d.ts +15 -0
- package/dist/src/commands/configuration.js +674 -0
- 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 +47 -3
- package/dist/src/commands/experience.d.ts +12 -0
- package/dist/src/commands/experience.js +288 -0
- package/dist/src/commands/files.js +61 -13
- package/dist/src/commands/messages.d.ts +2 -1
- package/dist/src/commands/messages.js +349 -87
- package/dist/src/commands/modes.d.ts +4 -1
- package/dist/src/commands/modes.js +243 -44
- package/dist/src/commands/probes.d.ts +15 -0
- package/dist/src/commands/probes.js +64 -0
- package/dist/src/commands/registry.js +102 -8
- package/dist/src/commands/reports.js +1 -1
- package/dist/src/commands/response-actions.js +1 -7
- package/dist/src/commands/sequence.js +24 -1
- package/dist/src/commands/session.d.ts +2 -0
- package/dist/src/commands/session.js +50 -1
- package/dist/src/commands/threads.js +3 -31
- package/dist/src/commands/work.d.ts +6 -0
- package/dist/src/commands/work.js +338 -0
- 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 +8 -1
- package/dist/src/dom/locale/am.js +8 -1
- package/dist/src/dom/locale/ar.d.ts +9 -1
- package/dist/src/dom/locale/ar.js +9 -1
- package/dist/src/dom/locale/bg.d.ts +9 -1
- package/dist/src/dom/locale/bg.js +9 -1
- package/dist/src/dom/locale/bn.d.ts +9 -1
- package/dist/src/dom/locale/bn.js +9 -1
- package/dist/src/dom/locale/bs.d.ts +8 -1
- package/dist/src/dom/locale/bs.js +8 -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 +8 -1
- package/dist/src/dom/locale/cs.js +8 -1
- package/dist/src/dom/locale/da.d.ts +7 -1
- package/dist/src/dom/locale/da.js +7 -1
- package/dist/src/dom/locale/de.d.ts +10 -3
- package/dist/src/dom/locale/de.js +10 -3
- package/dist/src/dom/locale/el.d.ts +8 -1
- package/dist/src/dom/locale/el.js +8 -1
- package/dist/src/dom/locale/en.d.ts +40 -1
- package/dist/src/dom/locale/en.js +42 -1
- package/dist/src/dom/locale/es-419.d.ts +8 -1
- package/dist/src/dom/locale/es-419.js +8 -1
- package/dist/src/dom/locale/es-ES.d.ts +8 -1
- package/dist/src/dom/locale/es-ES.js +8 -1
- package/dist/src/dom/locale/et.d.ts +8 -1
- package/dist/src/dom/locale/et.js +8 -1
- package/dist/src/dom/locale/fa.d.ts +9 -1
- package/dist/src/dom/locale/fa.js +9 -1
- package/dist/src/dom/locale/fi.d.ts +8 -1
- package/dist/src/dom/locale/fi.js +8 -1
- package/dist/src/dom/locale/fr-CA.d.ts +8 -1
- package/dist/src/dom/locale/fr-CA.js +8 -1
- package/dist/src/dom/locale/fr-FR.d.ts +8 -3
- package/dist/src/dom/locale/fr-FR.js +8 -3
- package/dist/src/dom/locale/gu.d.ts +8 -1
- package/dist/src/dom/locale/gu.js +8 -1
- package/dist/src/dom/locale/hi.d.ts +8 -1
- package/dist/src/dom/locale/hi.js +8 -1
- package/dist/src/dom/locale/hr.d.ts +7 -1
- package/dist/src/dom/locale/hr.js +7 -1
- package/dist/src/dom/locale/hu.d.ts +8 -1
- package/dist/src/dom/locale/hu.js +8 -1
- package/dist/src/dom/locale/hy.d.ts +9 -1
- package/dist/src/dom/locale/hy.js +9 -1
- package/dist/src/dom/locale/id.d.ts +8 -1
- package/dist/src/dom/locale/id.js +8 -1
- package/dist/src/dom/locale/index.d.ts +20 -1
- package/dist/src/dom/locale/index.js +102 -0
- package/dist/src/dom/locale/is.d.ts +8 -1
- package/dist/src/dom/locale/is.js +8 -1
- package/dist/src/dom/locale/it.d.ts +8 -1
- package/dist/src/dom/locale/it.js +8 -1
- package/dist/src/dom/locale/ja.d.ts +8 -1
- package/dist/src/dom/locale/ja.js +8 -1
- package/dist/src/dom/locale/ka.d.ts +8 -1
- package/dist/src/dom/locale/ka.js +8 -1
- package/dist/src/dom/locale/kk.d.ts +8 -1
- package/dist/src/dom/locale/kk.js +8 -1
- package/dist/src/dom/locale/kn.d.ts +9 -1
- package/dist/src/dom/locale/kn.js +9 -1
- package/dist/src/dom/locale/ko.d.ts +8 -1
- package/dist/src/dom/locale/ko.js +8 -1
- package/dist/src/dom/locale/lt.d.ts +9 -1
- package/dist/src/dom/locale/lt.js +9 -1
- package/dist/src/dom/locale/lv.d.ts +8 -1
- package/dist/src/dom/locale/lv.js +8 -1
- package/dist/src/dom/locale/mk.d.ts +7 -1
- package/dist/src/dom/locale/mk.js +7 -1
- package/dist/src/dom/locale/ml.d.ts +9 -1
- package/dist/src/dom/locale/ml.js +9 -1
- package/dist/src/dom/locale/mn.d.ts +9 -1
- package/dist/src/dom/locale/mn.js +9 -1
- package/dist/src/dom/locale/mr.d.ts +9 -1
- package/dist/src/dom/locale/mr.js +9 -1
- package/dist/src/dom/locale/ms.d.ts +8 -1
- package/dist/src/dom/locale/ms.js +8 -1
- package/dist/src/dom/locale/my.d.ts +8 -1
- package/dist/src/dom/locale/my.js +8 -1
- package/dist/src/dom/locale/nb.d.ts +8 -1
- package/dist/src/dom/locale/nb.js +8 -1
- package/dist/src/dom/locale/nl.d.ts +8 -1
- package/dist/src/dom/locale/nl.js +8 -1
- package/dist/src/dom/locale/pa.d.ts +9 -1
- package/dist/src/dom/locale/pa.js +9 -1
- package/dist/src/dom/locale/pl.d.ts +8 -1
- package/dist/src/dom/locale/pl.js +8 -1
- package/dist/src/dom/locale/pt-BR.d.ts +8 -1
- package/dist/src/dom/locale/pt-BR.js +8 -1
- package/dist/src/dom/locale/pt-PT.d.ts +8 -1
- package/dist/src/dom/locale/pt-PT.js +8 -1
- package/dist/src/dom/locale/ro.d.ts +7 -1
- package/dist/src/dom/locale/ro.js +7 -1
- package/dist/src/dom/locale/ru.d.ts +5 -1
- package/dist/src/dom/locale/ru.js +5 -1
- package/dist/src/dom/locale/sk.d.ts +8 -1
- package/dist/src/dom/locale/sk.js +8 -1
- package/dist/src/dom/locale/sl.d.ts +8 -1
- package/dist/src/dom/locale/sl.js +8 -1
- package/dist/src/dom/locale/so.d.ts +8 -1
- package/dist/src/dom/locale/so.js +8 -1
- package/dist/src/dom/locale/sq.d.ts +8 -1
- package/dist/src/dom/locale/sq.js +8 -1
- package/dist/src/dom/locale/sr.d.ts +5 -1
- package/dist/src/dom/locale/sr.js +5 -1
- package/dist/src/dom/locale/sv.d.ts +8 -1
- package/dist/src/dom/locale/sv.js +8 -1
- package/dist/src/dom/locale/sw.d.ts +8 -1
- package/dist/src/dom/locale/sw.js +8 -1
- package/dist/src/dom/locale/ta.d.ts +9 -1
- package/dist/src/dom/locale/ta.js +9 -1
- package/dist/src/dom/locale/te.d.ts +9 -1
- package/dist/src/dom/locale/te.js +9 -1
- package/dist/src/dom/locale/th.d.ts +8 -1
- package/dist/src/dom/locale/th.js +8 -1
- package/dist/src/dom/locale/tl.d.ts +4 -5
- package/dist/src/dom/locale/tl.js +4 -5
- package/dist/src/dom/locale/tr.d.ts +8 -1
- package/dist/src/dom/locale/tr.js +8 -1
- package/dist/src/dom/locale/types.d.ts +25 -1
- package/dist/src/dom/locale/uk.d.ts +8 -1
- package/dist/src/dom/locale/uk.js +8 -1
- package/dist/src/dom/locale/ur.d.ts +8 -1
- package/dist/src/dom/locale/ur.js +8 -1
- package/dist/src/dom/locale/vi.d.ts +8 -1
- package/dist/src/dom/locale/vi.js +8 -1
- package/dist/src/dom/locale/zh-HK.d.ts +9 -1
- package/dist/src/dom/locale/zh-HK.js +10 -2
- package/dist/src/dom/locale/zh-Hans.d.ts +9 -1
- package/dist/src/dom/locale/zh-Hans.js +9 -1
- package/dist/src/dom/locale/zh-TW.d.ts +9 -1
- package/dist/src/dom/locale/zh-TW.js +9 -1
- package/dist/src/dom/menus.js +13 -2
- package/dist/src/dom/selectors.js +6 -1
- package/dist/src/dom/wait-snapshot.d.ts +43 -0
- package/dist/src/dom/wait-snapshot.js +127 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/runner/responses.d.ts +3 -1
- package/dist/src/runner/responses.js +16 -1
- package/dist/src/runner/result.js +129 -4
- package/dist/src/runner/stream.d.ts +1 -1
- package/dist/src/runner/stream.js +6 -0
- package/dist/src/runner/types.d.ts +35 -1
- package/dist/src/safety/risk.d.ts +11 -0
- package/dist/src/safety/risk.js +11 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +6 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.js +147 -15
- package/dist/src/scripts/capture-intelligence-locales.d.ts +3 -0
- package/dist/src/scripts/capture-intelligence-locales.js +372 -3
- package/dist/src/scripts/capture-surface-profile.d.ts +23 -0
- package/dist/src/scripts/capture-surface-profile.js +304 -0
- package/dist/src/scripts/live-smoke/scenarios.js +34 -1
- package/dist/src/scripts/live-smoke.js +1 -0
- package/dist/src/types.d.ts +273 -2
- package/package.json +4 -3
- package/references/2026-07-16-chat-work-surfaces.md +87 -0
- package/references/agents-runner.md +13 -2
- package/references/backend-protocol.md +19 -7
- package/references/language-coverage.md +40 -3
- package/references/localization.md +48 -10
- package/references/python-parity.md +16 -4
- package/references/responses-adapter.md +12 -1
- package/references/troubleshooting.md +49 -6
|
@@ -1,12 +1,16 @@
|
|
|
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";
|
|
9
|
-
import {
|
|
12
|
+
import { createSingleFlightProbe } from "./probes.js";
|
|
13
|
+
import { ensurePage } from "./session.js";
|
|
10
14
|
export function isResponseComplete(snapshot) {
|
|
11
15
|
return snapshot.latestText.trim().length > 0
|
|
12
16
|
&& !isTransientAssistantText(snapshot.latestText)
|
|
@@ -94,8 +98,14 @@ export async function submitMessage(env, args = {}) {
|
|
|
94
98
|
}
|
|
95
99
|
if (userTurn === undefined) {
|
|
96
100
|
const latestUser = await readLatestMessage(page, "user", "normalized_text");
|
|
101
|
+
const generation = await readAssistantGenerationState(page).catch(() => EMPTY_GENERATION_STATE);
|
|
102
|
+
const turnCount = await countPageMessages(page).catch(() => undefined);
|
|
97
103
|
if (submittedUserTurnMatches(latestUser?.text, args.text)) {
|
|
98
|
-
return resultOk(submitData(latestUser?.text,
|
|
104
|
+
return resultOk(submitData(latestUser?.text, turnCount, generation.active ? "submitted_generating" : "submitted", generation), await contextFromPage(page));
|
|
105
|
+
}
|
|
106
|
+
const turnAdvanced = previousTurnCount !== undefined && turnCount !== undefined && turnCount > previousTurnCount;
|
|
107
|
+
if (turnAdvanced || generation.active) {
|
|
108
|
+
return resultOk(submitData(undefined, turnCount, generation.active ? "submitted_generating" : "submitted_unconfirmed", generation), await contextFromPage(page), ["Submitted prompt could not be matched to a rendered user turn yet, but ChatGPT page state indicates progress."]);
|
|
99
109
|
}
|
|
100
110
|
return {
|
|
101
111
|
ok: false,
|
|
@@ -109,7 +119,7 @@ export async function submitMessage(env, args = {}) {
|
|
|
109
119
|
context: await contextFromPage(page)
|
|
110
120
|
};
|
|
111
121
|
}
|
|
112
|
-
return resultOk(submitData(userTurn, await countPageMessages(page).catch(() => undefined)), await contextFromPage(page));
|
|
122
|
+
return resultOk(submitData(userTurn, await countPageMessages(page).catch(() => undefined), "submitted", await readAssistantGenerationState(page).catch(() => EMPTY_GENERATION_STATE)), await contextFromPage(page));
|
|
113
123
|
}
|
|
114
124
|
catch (error) {
|
|
115
125
|
return resultError(error instanceof Error ? error : new Error(String(error)), await contextFromPage(page));
|
|
@@ -222,7 +232,16 @@ async function readVisibleTextForSubmit(page) {
|
|
|
222
232
|
if (typeof page.evaluate !== "function") {
|
|
223
233
|
return undefined;
|
|
224
234
|
}
|
|
225
|
-
|
|
235
|
+
// Attachment/upload status renders inside the composer form; prefer that region over
|
|
236
|
+
// the whole page so the not-ready poll does not serialize the full document text.
|
|
237
|
+
return page.evaluate(() => {
|
|
238
|
+
const composerForm = document.querySelector("main form") ?? document.querySelector("form");
|
|
239
|
+
const scopedText = composerForm?.innerText;
|
|
240
|
+
if (scopedText !== undefined && scopedText.trim().length > 0) {
|
|
241
|
+
return scopedText;
|
|
242
|
+
}
|
|
243
|
+
return document.body?.innerText ?? "";
|
|
244
|
+
});
|
|
226
245
|
}
|
|
227
246
|
async function sendTimeoutWarnings(page) {
|
|
228
247
|
const state = await readSendButtonState(page).catch(() => undefined);
|
|
@@ -259,76 +278,135 @@ export async function waitForMessage(env, args = {}) {
|
|
|
259
278
|
const stableMs = args.stableMs ?? (args.mode === "deep_research" ? 10_000 : 2_000);
|
|
260
279
|
const pollMs = args.pollMs ?? 750;
|
|
261
280
|
const started = Date.now();
|
|
262
|
-
|
|
281
|
+
const deadline = createDeadline(timeoutMs, started);
|
|
282
|
+
const probeTimeoutMs = Math.max(50, Math.min(1000, Math.max(pollMs, Math.floor(timeoutMs / 4))));
|
|
283
|
+
const waitWarnings = new Set();
|
|
284
|
+
// One combined DOM probe per poll: counts, latest-text metadata, generation state, and
|
|
285
|
+
// response actions come back in a single evaluate, sampled from the same DOM instant.
|
|
286
|
+
// The full assistant text never crosses the bridge during polling; it is fetched once
|
|
287
|
+
// at loop exit. Page-state blocker scans run on a coarser cadence below.
|
|
288
|
+
const waitSnapshotProbe = createSingleFlightProbe("wait snapshot", readWaitDomSnapshot);
|
|
289
|
+
const pageStateProbe = createSingleFlightProbe("page state", readPageState);
|
|
290
|
+
const PAGE_STATE_POLL_STRIDE = 4;
|
|
291
|
+
let pollIndex = 0;
|
|
292
|
+
let lastTargetKey = "";
|
|
263
293
|
let lastChangedAt = Date.now();
|
|
294
|
+
let lastObservedTextLength = 0;
|
|
264
295
|
let latestAssistantCount = await countPageMessages(page, "assistant").catch(() => 0);
|
|
296
|
+
let lastGeneration = EMPTY_GENERATION_STATE;
|
|
265
297
|
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;
|
|
298
|
+
const probeResult = await waitSnapshotProbe(page, deadline, { timeoutMs: probeTimeoutMs });
|
|
299
|
+
addWarnings(waitWarnings, probeResult.warnings);
|
|
300
|
+
const snapshot = await waitSnapshotFromProbeResult(page, probeResult, latestAssistantCount);
|
|
301
|
+
latestAssistantCount = snapshot.assistantTurnCount;
|
|
302
|
+
const targetReached = waitTargetReached(args, snapshot);
|
|
303
|
+
const targetKey = targetReached && snapshot.text.length > 0
|
|
304
|
+
? `${snapshot.text.length}:${snapshot.text.hash}`
|
|
305
|
+
: "";
|
|
306
|
+
if (targetKey !== lastTargetKey) {
|
|
307
|
+
lastTargetKey = targetKey;
|
|
283
308
|
lastChangedAt = Date.now();
|
|
284
309
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
310
|
+
if (targetReached && snapshot.text.length > 0) {
|
|
311
|
+
lastObservedTextLength = snapshot.text.length;
|
|
312
|
+
}
|
|
313
|
+
if (pollIndex % PAGE_STATE_POLL_STRIDE === 0) {
|
|
314
|
+
const state = await pageStateFromProbe(pageStateProbe(page, deadline, { timeoutMs: probeTimeoutMs }), waitWarnings);
|
|
315
|
+
if (state?.blocker !== undefined && state.blocker.kind !== "modal") {
|
|
316
|
+
return {
|
|
317
|
+
ok: false,
|
|
318
|
+
status: "blocked",
|
|
319
|
+
warnings: [...waitWarnings],
|
|
320
|
+
blocker: state.blocker,
|
|
321
|
+
context: await contextFromPage(page)
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
pollIndex += 1;
|
|
326
|
+
const hasResponseActions = await resolveResponseActions(page, snapshot);
|
|
327
|
+
lastGeneration = snapshot.generation;
|
|
328
|
+
if (targetReached && snapshot.generation.stopped && snapshot.text.length > 0) {
|
|
329
|
+
const stoppedText = normalizeWhitespace(await fetchLatestAssistantText(page) ?? "");
|
|
330
|
+
// A failed re-read must not fabricate an empty capture: real text was observed in
|
|
331
|
+
// the snapshot, so omit response content entirely and tell the caller how to get it.
|
|
332
|
+
const data = stoppedText.length > 0
|
|
333
|
+
? waitDataFromText(args, false, stoppedText, latestAssistantCount, Date.now() - started)
|
|
334
|
+
: waitDataWithoutText(latestAssistantCount, Date.now() - started);
|
|
335
|
+
data.completionState = "stopped";
|
|
336
|
+
data.generationActive = snapshot.generation.active;
|
|
337
|
+
data.generationSignals = snapshot.generation.signals;
|
|
338
|
+
if (stoppedText.length === 0) {
|
|
339
|
+
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.`);
|
|
340
|
+
}
|
|
293
341
|
return withCommandOutputText({
|
|
294
342
|
ok: false,
|
|
295
343
|
status: "partial",
|
|
296
|
-
data
|
|
297
|
-
complete: false,
|
|
298
|
-
responseText: latestText,
|
|
299
|
-
assistantTurnCount: latestAssistantCount,
|
|
300
|
-
elapsedMs: Date.now() - started
|
|
301
|
-
},
|
|
344
|
+
data,
|
|
302
345
|
warnings: [
|
|
346
|
+
...waitWarnings,
|
|
347
|
+
...(stoppedText.length > 0 ? responseContentWarnings(args, false) : []),
|
|
303
348
|
"ChatGPT generation appears to have been stopped or interrupted before completion.",
|
|
304
349
|
...snapshot.generation.signals.map(signal => `Generation state signal: ${signal}`)
|
|
305
350
|
],
|
|
306
351
|
context: await contextFromPage(page)
|
|
307
352
|
});
|
|
308
353
|
}
|
|
309
|
-
|
|
310
|
-
|
|
354
|
+
const metadataComplete = targetReached
|
|
355
|
+
&& snapshot.text.length > 0
|
|
356
|
+
&& !snapshot.text.transient
|
|
357
|
+
&& Date.now() - lastChangedAt >= stableMs
|
|
358
|
+
&& !snapshot.generation.active
|
|
359
|
+
&& !snapshot.generation.stopped
|
|
360
|
+
&& hasResponseActions;
|
|
361
|
+
if (metadataComplete) {
|
|
362
|
+
// Fetch the text once and confirm it still matches the stable snapshot before
|
|
363
|
+
// declaring completion; a hash mismatch means the answer moved on mid-fetch.
|
|
364
|
+
const latestText = normalizeWhitespace(await fetchLatestAssistantText(page) ?? "");
|
|
365
|
+
const fetchedMetadata = waitTextMetadata(latestText);
|
|
366
|
+
const completionSnapshot = {
|
|
367
|
+
latestText,
|
|
368
|
+
stableMs,
|
|
369
|
+
textStableForMs: Date.now() - lastChangedAt,
|
|
370
|
+
generation: snapshot.generation,
|
|
371
|
+
hasResponseActions
|
|
372
|
+
};
|
|
373
|
+
if (fetchedMetadata.hash === snapshot.text.hash && isResponseComplete(completionSnapshot)) {
|
|
374
|
+
const data = waitDataFromText(args, true, latestText, latestAssistantCount, Date.now() - started);
|
|
375
|
+
data.completionState = "complete";
|
|
376
|
+
data.generationActive = false;
|
|
377
|
+
data.generationSignals = snapshot.generation.signals;
|
|
378
|
+
return withCommandOutputText(resultOk(data, await contextFromPage(page), [...waitWarnings, ...responseContentWarnings(args, true)]));
|
|
379
|
+
}
|
|
380
|
+
if (latestText.length > 0) {
|
|
381
|
+
lastTargetKey = `${fetchedMetadata.length}:${fetchedMetadata.hash}`;
|
|
382
|
+
lastChangedAt = Date.now();
|
|
383
|
+
lastObservedTextLength = fetchedMetadata.length;
|
|
384
|
+
}
|
|
311
385
|
}
|
|
312
386
|
await sleep(page, pollMs);
|
|
313
387
|
}
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
data
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
388
|
+
if (lastObservedTextLength > 0) {
|
|
389
|
+
const partialText = await fetchLatestAssistantText(page);
|
|
390
|
+
if (partialText !== undefined && normalizeWhitespace(partialText).length > 0) {
|
|
391
|
+
const normalizedPartialText = normalizeWhitespace(partialText);
|
|
392
|
+
const data = waitDataFromText(args, false, normalizedPartialText, latestAssistantCount, Date.now() - started);
|
|
393
|
+
data.completionState = completionStateFromGeneration(lastGeneration, false, normalizedPartialText.length > 0);
|
|
394
|
+
data.generationActive = lastGeneration.active;
|
|
395
|
+
data.generationSignals = lastGeneration.signals;
|
|
396
|
+
return withCommandOutputText({
|
|
397
|
+
ok: false,
|
|
398
|
+
status: "partial",
|
|
399
|
+
data,
|
|
400
|
+
warnings: [...waitWarnings, ...responseContentWarnings(args, false), "Timed out after receiving partial assistant text."],
|
|
401
|
+
context: await contextFromPage(page)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
waitWarnings.add(`Partial assistant text (${lastObservedTextLength} chars) was observed during polling but could not be re-read at wait exit.`);
|
|
327
405
|
}
|
|
328
406
|
return {
|
|
329
407
|
ok: false,
|
|
330
408
|
status: "timeout",
|
|
331
|
-
warnings: [],
|
|
409
|
+
warnings: [...waitWarnings],
|
|
332
410
|
error: {
|
|
333
411
|
name: "WaitTimeout",
|
|
334
412
|
message: "No assistant response appeared before the timeout.",
|
|
@@ -337,6 +415,107 @@ export async function waitForMessage(env, args = {}) {
|
|
|
337
415
|
context: await contextFromPage(page)
|
|
338
416
|
};
|
|
339
417
|
}
|
|
418
|
+
async function fetchLatestAssistantText(page) {
|
|
419
|
+
const first = await readLatestMessageText(page, "assistant").catch(() => undefined);
|
|
420
|
+
if (first !== undefined) {
|
|
421
|
+
return first;
|
|
422
|
+
}
|
|
423
|
+
// One retry: exit-time reads race DOM reflow/navigation, and a transient failure here
|
|
424
|
+
// would otherwise discard an answer the polling snapshots proved exists.
|
|
425
|
+
await sleep(page, 150);
|
|
426
|
+
return readLatestMessageText(page, "assistant").catch(() => undefined);
|
|
427
|
+
}
|
|
428
|
+
function waitDataWithoutText(assistantTurnCount, elapsedMs) {
|
|
429
|
+
return { complete: false, assistantTurnCount, elapsedMs };
|
|
430
|
+
}
|
|
431
|
+
async function resolveResponseActions(page, snapshot) {
|
|
432
|
+
if (snapshot.hasResponseActions !== undefined) {
|
|
433
|
+
return snapshot.hasResponseActions;
|
|
434
|
+
}
|
|
435
|
+
// No conversation-turn markers: fall back to the structural copy-button locator, as
|
|
436
|
+
// the standalone response-actions probe does.
|
|
437
|
+
try {
|
|
438
|
+
const copyButtons = copyResponseButtons(page);
|
|
439
|
+
const count = await copyButtons.count?.();
|
|
440
|
+
if (count !== undefined) {
|
|
441
|
+
return count > 0;
|
|
442
|
+
}
|
|
443
|
+
return await copyButtons.isVisible?.() === true;
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
async function waitSnapshotFromProbeResult(page, result, previousAssistantTurnCount) {
|
|
450
|
+
if (result.ok && result.value !== undefined) {
|
|
451
|
+
return result.value;
|
|
452
|
+
}
|
|
453
|
+
if (!result.ok && (result.timedOut === true || result.skipped === true)) {
|
|
454
|
+
return {
|
|
455
|
+
turnCount: 0,
|
|
456
|
+
assistantTurnCount: previousAssistantTurnCount,
|
|
457
|
+
text: waitTextMetadata(""),
|
|
458
|
+
generation: EMPTY_GENERATION_STATE,
|
|
459
|
+
hasResponseActions: false
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
return fallbackWaitSnapshot(page, previousAssistantTurnCount);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Degraded snapshot for pages where the combined evaluate is unavailable or failed.
|
|
466
|
+
* Reuses the standalone facet probes, which carry their own content/locator fallbacks;
|
|
467
|
+
* text metadata is computed SDK-side from the extracted text.
|
|
468
|
+
*/
|
|
469
|
+
async function fallbackWaitSnapshot(page, previousAssistantTurnCount) {
|
|
470
|
+
const progress = await fallbackAssistantProgressSnapshot(page, previousAssistantTurnCount);
|
|
471
|
+
const generation = await readAssistantGenerationState(page).catch(() => EMPTY_GENERATION_STATE);
|
|
472
|
+
const hasResponseActions = await latestAssistantTurnHasResponseActions(page).catch(() => false);
|
|
473
|
+
const snapshot = {
|
|
474
|
+
turnCount: progress.turnCount ?? 0,
|
|
475
|
+
assistantTurnCount: progress.assistantTurnCount,
|
|
476
|
+
text: waitTextMetadata(progress.latestText),
|
|
477
|
+
generation,
|
|
478
|
+
hasResponseActions
|
|
479
|
+
};
|
|
480
|
+
if (progress.latestAssistantTurnIndex !== undefined) {
|
|
481
|
+
snapshot.latestAssistantTurnIndex = progress.latestAssistantTurnIndex;
|
|
482
|
+
}
|
|
483
|
+
return snapshot;
|
|
484
|
+
}
|
|
485
|
+
function waitDataFromText(args, complete, responseText, assistantTurnCount, elapsedMs) {
|
|
486
|
+
const data = {
|
|
487
|
+
complete,
|
|
488
|
+
assistantTurnCount,
|
|
489
|
+
elapsedMs
|
|
490
|
+
};
|
|
491
|
+
if (args.responseContent === "metadata") {
|
|
492
|
+
data.responseContent = "metadata";
|
|
493
|
+
data.responseChars = responseText.length;
|
|
494
|
+
data.responseSha256 = createHash("sha256").update(responseText).digest("hex");
|
|
495
|
+
return data;
|
|
496
|
+
}
|
|
497
|
+
data.responseText = responseText;
|
|
498
|
+
return data;
|
|
499
|
+
}
|
|
500
|
+
function responseContentWarnings(args, complete) {
|
|
501
|
+
if (args.responseContent !== "metadata")
|
|
502
|
+
return [];
|
|
503
|
+
return [
|
|
504
|
+
complete
|
|
505
|
+
? "Assistant response text was omitted because responseContent is metadata; call readLatest to capture the completed answer."
|
|
506
|
+
: "Partial assistant text was omitted because responseContent is metadata; call wait again on the same thread or readLatest after completion."
|
|
507
|
+
];
|
|
508
|
+
}
|
|
509
|
+
async function pageStateFromProbe(probe, warnings) {
|
|
510
|
+
const result = await probe;
|
|
511
|
+
addWarnings(warnings, result.warnings);
|
|
512
|
+
return result.ok ? result.value : undefined;
|
|
513
|
+
}
|
|
514
|
+
function addWarnings(target, warnings) {
|
|
515
|
+
for (const warning of warnings) {
|
|
516
|
+
target.add(warning);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
340
519
|
export async function readLatest(env, args = {}) {
|
|
341
520
|
const boot = await ensurePage(env);
|
|
342
521
|
if (!boot.ok) {
|
|
@@ -391,9 +570,51 @@ export async function readLatest(env, args = {}) {
|
|
|
391
570
|
data.thoughtDurationText = latest.thoughtDurationText;
|
|
392
571
|
if (latest.sourcesAvailable !== undefined)
|
|
393
572
|
data.sourcesAvailable = latest.sourcesAvailable;
|
|
573
|
+
if (role === "assistant") {
|
|
574
|
+
const generation = await readAssistantGenerationState(page).catch(() => EMPTY_GENERATION_STATE);
|
|
575
|
+
data.completionState = completionStateFromGeneration(generation, undefined, latest.text.trim().length > 0);
|
|
576
|
+
data.generationActive = generation.active;
|
|
577
|
+
data.generationSignals = generation.signals;
|
|
578
|
+
}
|
|
394
579
|
return withCommandOutputText(resultOk(data, await contextFromPage(page), data.warnings ?? []));
|
|
395
580
|
}
|
|
581
|
+
export async function messageStatus(env, args = {}) {
|
|
582
|
+
const boot = await ensurePage(env);
|
|
583
|
+
if (!boot.ok) {
|
|
584
|
+
return boot;
|
|
585
|
+
}
|
|
586
|
+
const page = env.page;
|
|
587
|
+
// Reuse the combined wait-snapshot evaluate for counts and generation state in a single
|
|
588
|
+
// round trip; it never carries full assistant text, so a bounded preview is fetched
|
|
589
|
+
// separately below only when the snapshot proves an assistant turn has text.
|
|
590
|
+
const snapshot = await readWaitDomSnapshot(page).catch(() => undefined) ?? await fallbackWaitSnapshot(page, 0);
|
|
591
|
+
const maxPreviewChars = Math.max(0, args.maxPreviewChars ?? 240);
|
|
592
|
+
const latestText = snapshot.text.length > 0
|
|
593
|
+
? normalizeWhitespace(await fetchLatestAssistantText(page) ?? "")
|
|
594
|
+
: "";
|
|
595
|
+
const data = {
|
|
596
|
+
turnCount: snapshot.turnCount,
|
|
597
|
+
assistantTurnCount: snapshot.assistantTurnCount,
|
|
598
|
+
completionState: completionStateFromGeneration(snapshot.generation, undefined, latestText.length > 0),
|
|
599
|
+
generationActive: snapshot.generation.active,
|
|
600
|
+
generationSignals: snapshot.generation.signals
|
|
601
|
+
};
|
|
602
|
+
if (snapshot.latestAssistantTurnIndex !== undefined)
|
|
603
|
+
data.latestAssistantTurnIndex = snapshot.latestAssistantTurnIndex;
|
|
604
|
+
if (latestText.length > 0) {
|
|
605
|
+
data.latestAssistantTextLength = latestText.length;
|
|
606
|
+
data.latestAssistantPreview = latestText.length > maxPreviewChars
|
|
607
|
+
? `${latestText.slice(0, Math.max(0, maxPreviewChars - 1))}...`
|
|
608
|
+
: latestText;
|
|
609
|
+
}
|
|
610
|
+
return resultOk(data, await contextFromPage(page));
|
|
611
|
+
}
|
|
396
612
|
export async function askMessage(env, args) {
|
|
613
|
+
const normalizedPrompt = normalizeAskPrompt(args);
|
|
614
|
+
if (!normalizedPrompt.ok) {
|
|
615
|
+
return normalizedPrompt.result;
|
|
616
|
+
}
|
|
617
|
+
const prompt = normalizedPrompt.text;
|
|
397
618
|
const boot = await ensurePage(env);
|
|
398
619
|
if (!boot.ok) {
|
|
399
620
|
return boot;
|
|
@@ -401,7 +622,7 @@ export async function askMessage(env, args) {
|
|
|
401
622
|
const page = env.page;
|
|
402
623
|
const beforeTurnCount = await countPageMessages(page).catch(() => undefined);
|
|
403
624
|
const beforeAssistantTurnCount = await countPageMessages(page, "assistant").catch(() => undefined);
|
|
404
|
-
const composeArgs = { text:
|
|
625
|
+
const composeArgs = { text: prompt, mode: "replace" };
|
|
405
626
|
if (args.timeoutMs !== undefined) {
|
|
406
627
|
composeArgs.timeoutMs = args.timeoutMs;
|
|
407
628
|
}
|
|
@@ -409,7 +630,7 @@ export async function askMessage(env, args) {
|
|
|
409
630
|
if (!compose.ok) {
|
|
410
631
|
return forwardFailure(compose);
|
|
411
632
|
}
|
|
412
|
-
const submitArgs = { text:
|
|
633
|
+
const submitArgs = { text: prompt };
|
|
413
634
|
if (beforeTurnCount !== undefined) {
|
|
414
635
|
submitArgs.previousTurnCount = beforeTurnCount;
|
|
415
636
|
}
|
|
@@ -466,7 +687,7 @@ export async function askMessage(env, args) {
|
|
|
466
687
|
return forwardFailure(waitFailure);
|
|
467
688
|
}
|
|
468
689
|
const state = await readPageState(page).catch(() => undefined);
|
|
469
|
-
const data = { prompt
|
|
690
|
+
const data = { prompt };
|
|
470
691
|
const complete = waitResult?.data?.complete ?? (waitResult === undefined ? undefined : false);
|
|
471
692
|
if (complete !== undefined) {
|
|
472
693
|
data.complete = complete;
|
|
@@ -480,6 +701,7 @@ export async function askMessage(env, args) {
|
|
|
480
701
|
if (state?.title !== undefined) {
|
|
481
702
|
data.title = state.title;
|
|
482
703
|
}
|
|
704
|
+
applyMessageState(data, submit.data, waitResult?.data);
|
|
483
705
|
if (waitFailure !== undefined) {
|
|
484
706
|
data.complete = false;
|
|
485
707
|
return withCommandOutputText({
|
|
@@ -517,7 +739,7 @@ export async function waitAndRead(env, args = {}) {
|
|
|
517
739
|
}
|
|
518
740
|
return forwardFailure(read);
|
|
519
741
|
}
|
|
520
|
-
const data = askReadData("", read.data?.text, wait.data?.complete);
|
|
742
|
+
const data = askReadData("", read.data?.text, wait.data?.complete, wait.data, read.data);
|
|
521
743
|
const warnings = [...read.warnings, ...wait.warnings];
|
|
522
744
|
if (!wait.ok && wait.status === "partial") {
|
|
523
745
|
data.complete = false;
|
|
@@ -534,12 +756,6 @@ export async function waitAndRead(env, args = {}) {
|
|
|
534
756
|
}
|
|
535
757
|
return withCommandOutputText(resultOk(data, read.context, warnings));
|
|
536
758
|
}
|
|
537
|
-
async function ensurePage(env) {
|
|
538
|
-
if (env.page !== undefined) {
|
|
539
|
-
return resultOk({}, await contextFromPage(env.page));
|
|
540
|
-
}
|
|
541
|
-
return bootstrap(env, { preferExistingTab: true });
|
|
542
|
-
}
|
|
543
759
|
async function waitForSubmittedUserTurn(page, text, previousTurnCount, timeoutMs) {
|
|
544
760
|
const started = Date.now();
|
|
545
761
|
const wanted = text === undefined ? undefined : normalizeWhitespace(text);
|
|
@@ -601,27 +817,6 @@ function renderSubmittedTurnMarkdownSyntax(text, preserveFenceLanguage = true) {
|
|
|
601
817
|
.replace(/\*([^*]+)\*/g, "$1")
|
|
602
818
|
.replace(/_([^_]+)_/g, "$1");
|
|
603
819
|
}
|
|
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
820
|
async function fallbackAssistantProgressSnapshot(page, previousAssistantTurnCount) {
|
|
626
821
|
const messages = await readMessages(page, { format: "normalized_text" }).catch(() => undefined);
|
|
627
822
|
if (messages !== undefined) {
|
|
@@ -680,7 +875,7 @@ async function sleep(page, ms) {
|
|
|
680
875
|
}
|
|
681
876
|
await new Promise(resolve => setTimeout(resolve, ms));
|
|
682
877
|
}
|
|
683
|
-
function submitData(userTurnText, turnCount) {
|
|
878
|
+
function submitData(userTurnText, turnCount, submissionState, generation) {
|
|
684
879
|
const data = { submitted: true };
|
|
685
880
|
if (userTurnText !== undefined) {
|
|
686
881
|
data.userTurnText = userTurnText;
|
|
@@ -688,9 +883,13 @@ function submitData(userTurnText, turnCount) {
|
|
|
688
883
|
if (turnCount !== undefined) {
|
|
689
884
|
data.turnCount = turnCount;
|
|
690
885
|
}
|
|
886
|
+
data.submissionState = submissionState;
|
|
887
|
+
data.completionState = completionStateFromGeneration(generation);
|
|
888
|
+
data.generationActive = generation.active;
|
|
889
|
+
data.generationSignals = generation.signals;
|
|
691
890
|
return data;
|
|
692
891
|
}
|
|
693
|
-
function askReadData(prompt, responseText, complete) {
|
|
892
|
+
function askReadData(prompt, responseText, complete, wait, read) {
|
|
694
893
|
const data = { prompt };
|
|
695
894
|
if (responseText !== undefined) {
|
|
696
895
|
data.responseText = responseText;
|
|
@@ -698,8 +897,71 @@ function askReadData(prompt, responseText, complete) {
|
|
|
698
897
|
if (complete !== undefined) {
|
|
699
898
|
data.complete = complete;
|
|
700
899
|
}
|
|
900
|
+
applyMessageState(data, undefined, wait, read);
|
|
701
901
|
return data;
|
|
702
902
|
}
|
|
903
|
+
function applyMessageState(data, submit, wait, read) {
|
|
904
|
+
if (submit?.submissionState !== undefined)
|
|
905
|
+
data.submissionState = submit.submissionState;
|
|
906
|
+
const completionState = wait?.completionState ?? read?.completionState ?? submit?.completionState;
|
|
907
|
+
if (completionState !== undefined)
|
|
908
|
+
data.completionState = completionState;
|
|
909
|
+
const generationActive = wait?.generationActive ?? read?.generationActive ?? submit?.generationActive;
|
|
910
|
+
if (generationActive !== undefined)
|
|
911
|
+
data.generationActive = generationActive;
|
|
912
|
+
const generationSignals = wait?.generationSignals ?? read?.generationSignals ?? submit?.generationSignals;
|
|
913
|
+
if (generationSignals !== undefined)
|
|
914
|
+
data.generationSignals = generationSignals;
|
|
915
|
+
}
|
|
916
|
+
function completionStateFromGeneration(generation, complete, hasPartialText = false) {
|
|
917
|
+
if (complete === true)
|
|
918
|
+
return "complete";
|
|
919
|
+
if (generation.active)
|
|
920
|
+
return "generating";
|
|
921
|
+
if (generation.stopped)
|
|
922
|
+
return "stopped";
|
|
923
|
+
if (complete === false || hasPartialText)
|
|
924
|
+
return "partial";
|
|
925
|
+
return "unknown";
|
|
926
|
+
}
|
|
927
|
+
function normalizeAskPrompt(args) {
|
|
928
|
+
const text = args.text;
|
|
929
|
+
const prompt = args.prompt;
|
|
930
|
+
if (text !== undefined && prompt !== undefined && text !== prompt) {
|
|
931
|
+
return {
|
|
932
|
+
ok: false,
|
|
933
|
+
result: {
|
|
934
|
+
ok: false,
|
|
935
|
+
status: "error",
|
|
936
|
+
warnings: [],
|
|
937
|
+
error: {
|
|
938
|
+
name: "InvalidAskArgs",
|
|
939
|
+
message: "messages.ask received both text and prompt with different values.",
|
|
940
|
+
recoverable: false
|
|
941
|
+
},
|
|
942
|
+
context: { timestamp: new Date().toISOString() }
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
const normalized = text ?? prompt;
|
|
947
|
+
if (normalized === undefined || normalized.trim().length === 0) {
|
|
948
|
+
return {
|
|
949
|
+
ok: false,
|
|
950
|
+
result: {
|
|
951
|
+
ok: false,
|
|
952
|
+
status: "error",
|
|
953
|
+
warnings: [],
|
|
954
|
+
error: {
|
|
955
|
+
name: "InvalidAskArgs",
|
|
956
|
+
message: "messages.ask requires a non-empty text or prompt argument.",
|
|
957
|
+
recoverable: false
|
|
958
|
+
},
|
|
959
|
+
context: { timestamp: new Date().toISOString() }
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
return { ok: true, text: normalized };
|
|
964
|
+
}
|
|
703
965
|
function readRole(read) {
|
|
704
966
|
return typeof read === "object" ? read.role : undefined;
|
|
705
967
|
}
|
|
@@ -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[];
|