codex-chatgpt-control 0.1.0-alpha.1 → 0.2.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 +6 -0
- package/README.md +55 -0
- package/contracts/v1/fixtures/backend-capabilities.json +7 -0
- package/contracts/v1/fixtures/blocker-explanation-profiles.json +114 -0
- package/contracts/v1/fixtures/command-descriptors.json +192 -15
- package/contracts/v1/fixtures/doctor-bridge-upload.json +8 -0
- package/contracts/v1/fixtures/doctor-scenario-preflight.json +108 -0
- package/contracts/v1/fixtures/existing-tab-diagnostics-blocker.json +57 -0
- package/contracts/v1/fixtures/files-preflight-success.json +32 -0
- package/contracts/v1/fixtures/help-root.json +1 -1
- package/contracts/v1/fixtures/output-json-parse-success.json +17 -2
- package/contracts/v1/fixtures/project-sources-plan-add.json +71 -0
- package/contracts/v1/fixtures/read-latest-clipped.json +31 -0
- package/contracts/v1/fixtures/reports-create-redacted.json +15 -0
- package/contracts/v1/fixtures/responses-basic-success.json +11 -0
- package/contracts/v1/fixtures/run-basic-success.json +15 -0
- package/contracts/v1/fixtures/run-browser-bridge-blocker.json +2 -2
- package/contracts/v1/fixtures/run-timeout-partial.json +2 -2
- package/contracts/v1/fixtures/runner-budget-blocker.json +1 -1
- package/contracts/v1/fixtures/stream-blocked.ndjson +1 -1
- package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
- package/contracts/v1/manifest.json +30 -0
- package/contracts/v1/parity-suite.json +72 -6
- package/contracts/v1/schemas/backend-request.schema.json +3 -0
- package/contracts/v1/schemas/command-result.schema.json +48 -0
- package/contracts/v1/schemas/response.schema.json +28 -0
- package/contracts/v1/schemas/run-result.schema.json +84 -0
- package/contracts/v1/surface-drift-policy.json +210 -0
- package/dist/codex-chatgpt-control-backend.mjs +5137 -488
- package/dist/codex-chatgpt-control.bundle.mjs +5001 -286
- package/dist/src/backend/client.d.ts +6 -1
- package/dist/src/backend/client.js +5 -0
- package/dist/src/backend/protocol.d.ts +1 -1
- package/dist/src/backend/protocol.js +7 -0
- package/dist/src/backend/session.js +14 -0
- package/dist/src/browser/attach.js +128 -20
- package/dist/src/browser/downloads.js +2 -1
- package/dist/src/browser/page-state.js +6 -3
- package/dist/src/client.d.ts +16 -1
- package/dist/src/client.js +86 -7
- package/dist/src/commands/artifacts.d.ts +5 -0
- package/dist/src/commands/artifacts.js +432 -0
- package/dist/src/commands/context.js +5 -2
- package/dist/src/commands/doctor.d.ts +10 -2
- package/dist/src/commands/doctor.js +242 -14
- package/dist/src/commands/files.d.ts +2 -1
- package/dist/src/commands/files.js +352 -18
- package/dist/src/commands/messages.d.ts +2 -1
- package/dist/src/commands/messages.js +232 -40
- package/dist/src/commands/modes.js +202 -20
- package/dist/src/commands/project-sources.d.ts +10 -0
- package/dist/src/commands/project-sources.js +643 -0
- package/dist/src/commands/registry.js +76 -14
- package/dist/src/commands/reports.d.ts +9 -0
- package/dist/src/commands/reports.js +83 -6
- package/dist/src/commands/response-actions.js +33 -0
- package/dist/src/commands/sequence.js +14 -0
- package/dist/src/commands/threads.js +2 -1
- package/dist/src/commands/timeouts.d.ts +2 -0
- package/dist/src/commands/timeouts.js +18 -0
- package/dist/src/diagnostics/blockers.d.ts +54 -0
- package/dist/src/diagnostics/blockers.js +299 -0
- package/dist/src/dom/artifacts.d.ts +7 -0
- package/dist/src/dom/artifacts.js +198 -0
- package/dist/src/dom/generation-state.d.ts +8 -0
- package/dist/src/dom/generation-state.js +103 -0
- package/dist/src/dom/locale/am.d.ts +28 -0
- package/dist/src/dom/locale/am.js +28 -0
- package/dist/src/dom/locale/ar.d.ts +28 -0
- package/dist/src/dom/locale/ar.js +28 -0
- package/dist/src/dom/locale/bg.d.ts +28 -0
- package/dist/src/dom/locale/bg.js +28 -0
- package/dist/src/dom/locale/bn.d.ts +28 -0
- package/dist/src/dom/locale/bn.js +28 -0
- package/dist/src/dom/locale/bs.d.ts +28 -0
- package/dist/src/dom/locale/bs.js +28 -0
- package/dist/src/dom/locale/ca.d.ts +28 -0
- package/dist/src/dom/locale/ca.js +28 -0
- package/dist/src/dom/locale/cs.d.ts +28 -0
- package/dist/src/dom/locale/cs.js +28 -0
- package/dist/src/dom/locale/da.d.ts +28 -0
- package/dist/src/dom/locale/da.js +28 -0
- package/dist/src/dom/locale/de.d.ts +30 -0
- package/dist/src/dom/locale/de.js +30 -0
- package/dist/src/dom/locale/el.d.ts +28 -0
- package/dist/src/dom/locale/el.js +28 -0
- package/dist/src/dom/locale/en.d.ts +50 -0
- package/dist/src/dom/locale/en.js +56 -0
- package/dist/src/dom/locale/es-419.d.ts +28 -0
- package/dist/src/dom/locale/es-419.js +28 -0
- package/dist/src/dom/locale/es-ES.d.ts +28 -0
- package/dist/src/dom/locale/es-ES.js +28 -0
- package/dist/src/dom/locale/et.d.ts +28 -0
- package/dist/src/dom/locale/et.js +28 -0
- package/dist/src/dom/locale/fa.d.ts +28 -0
- package/dist/src/dom/locale/fa.js +28 -0
- package/dist/src/dom/locale/fi.d.ts +28 -0
- package/dist/src/dom/locale/fi.js +28 -0
- package/dist/src/dom/locale/fr-CA.d.ts +29 -0
- package/dist/src/dom/locale/fr-CA.js +29 -0
- package/dist/src/dom/locale/fr-FR.d.ts +30 -0
- package/dist/src/dom/locale/fr-FR.js +30 -0
- package/dist/src/dom/locale/gu.d.ts +28 -0
- package/dist/src/dom/locale/gu.js +28 -0
- package/dist/src/dom/locale/hi.d.ts +28 -0
- package/dist/src/dom/locale/hi.js +28 -0
- package/dist/src/dom/locale/hr.d.ts +28 -0
- package/dist/src/dom/locale/hr.js +28 -0
- package/dist/src/dom/locale/hu.d.ts +28 -0
- package/dist/src/dom/locale/hu.js +28 -0
- package/dist/src/dom/locale/hy.d.ts +28 -0
- package/dist/src/dom/locale/hy.js +28 -0
- package/dist/src/dom/locale/id.d.ts +28 -0
- package/dist/src/dom/locale/id.js +28 -0
- package/dist/src/dom/locale/index.d.ts +79 -0
- package/dist/src/dom/locale/index.js +202 -0
- package/dist/src/dom/locale/is.d.ts +28 -0
- package/dist/src/dom/locale/is.js +28 -0
- package/dist/src/dom/locale/it.d.ts +27 -0
- package/dist/src/dom/locale/it.js +27 -0
- package/dist/src/dom/locale/ja.d.ts +27 -0
- package/dist/src/dom/locale/ja.js +27 -0
- package/dist/src/dom/locale/ka.d.ts +28 -0
- package/dist/src/dom/locale/ka.js +28 -0
- package/dist/src/dom/locale/kk.d.ts +28 -0
- package/dist/src/dom/locale/kk.js +28 -0
- package/dist/src/dom/locale/kn.d.ts +28 -0
- package/dist/src/dom/locale/kn.js +28 -0
- package/dist/src/dom/locale/ko.d.ts +28 -0
- package/dist/src/dom/locale/ko.js +28 -0
- package/dist/src/dom/locale/lt.d.ts +28 -0
- package/dist/src/dom/locale/lt.js +28 -0
- package/dist/src/dom/locale/lv.d.ts +28 -0
- package/dist/src/dom/locale/lv.js +28 -0
- package/dist/src/dom/locale/mk.d.ts +28 -0
- package/dist/src/dom/locale/mk.js +28 -0
- package/dist/src/dom/locale/ml.d.ts +28 -0
- package/dist/src/dom/locale/ml.js +28 -0
- package/dist/src/dom/locale/mn.d.ts +28 -0
- package/dist/src/dom/locale/mn.js +28 -0
- package/dist/src/dom/locale/mr.d.ts +28 -0
- package/dist/src/dom/locale/mr.js +28 -0
- package/dist/src/dom/locale/ms.d.ts +28 -0
- package/dist/src/dom/locale/ms.js +28 -0
- package/dist/src/dom/locale/my.d.ts +28 -0
- package/dist/src/dom/locale/my.js +28 -0
- package/dist/src/dom/locale/nb.d.ts +28 -0
- package/dist/src/dom/locale/nb.js +28 -0
- package/dist/src/dom/locale/nl.d.ts +28 -0
- package/dist/src/dom/locale/nl.js +28 -0
- package/dist/src/dom/locale/pa.d.ts +28 -0
- package/dist/src/dom/locale/pa.js +28 -0
- package/dist/src/dom/locale/pl.d.ts +28 -0
- package/dist/src/dom/locale/pl.js +28 -0
- package/dist/src/dom/locale/pt-BR.d.ts +28 -0
- package/dist/src/dom/locale/pt-BR.js +28 -0
- package/dist/src/dom/locale/pt-PT.d.ts +28 -0
- package/dist/src/dom/locale/pt-PT.js +28 -0
- package/dist/src/dom/locale/ro.d.ts +28 -0
- package/dist/src/dom/locale/ro.js +28 -0
- package/dist/src/dom/locale/ru.d.ts +28 -0
- package/dist/src/dom/locale/ru.js +28 -0
- package/dist/src/dom/locale/sk.d.ts +28 -0
- package/dist/src/dom/locale/sk.js +28 -0
- package/dist/src/dom/locale/sl.d.ts +28 -0
- package/dist/src/dom/locale/sl.js +28 -0
- package/dist/src/dom/locale/so.d.ts +28 -0
- package/dist/src/dom/locale/so.js +28 -0
- package/dist/src/dom/locale/sq.d.ts +28 -0
- package/dist/src/dom/locale/sq.js +28 -0
- package/dist/src/dom/locale/sr.d.ts +28 -0
- package/dist/src/dom/locale/sr.js +28 -0
- package/dist/src/dom/locale/sv.d.ts +28 -0
- package/dist/src/dom/locale/sv.js +28 -0
- package/dist/src/dom/locale/sw.d.ts +28 -0
- package/dist/src/dom/locale/sw.js +28 -0
- package/dist/src/dom/locale/ta.d.ts +28 -0
- package/dist/src/dom/locale/ta.js +28 -0
- package/dist/src/dom/locale/te.d.ts +28 -0
- package/dist/src/dom/locale/te.js +28 -0
- package/dist/src/dom/locale/th.d.ts +28 -0
- package/dist/src/dom/locale/th.js +28 -0
- package/dist/src/dom/locale/tl.d.ts +28 -0
- package/dist/src/dom/locale/tl.js +28 -0
- package/dist/src/dom/locale/tr.d.ts +28 -0
- package/dist/src/dom/locale/tr.js +28 -0
- package/dist/src/dom/locale/types.d.ts +45 -0
- package/dist/src/dom/locale/types.js +1 -0
- package/dist/src/dom/locale/uk.d.ts +28 -0
- package/dist/src/dom/locale/uk.js +28 -0
- package/dist/src/dom/locale/ur.d.ts +28 -0
- package/dist/src/dom/locale/ur.js +28 -0
- package/dist/src/dom/locale/vi.d.ts +28 -0
- package/dist/src/dom/locale/vi.js +28 -0
- package/dist/src/dom/locale/zh-HK.d.ts +28 -0
- package/dist/src/dom/locale/zh-HK.js +28 -0
- package/dist/src/dom/locale/zh-Hans.d.ts +28 -0
- package/dist/src/dom/locale/zh-Hans.js +28 -0
- package/dist/src/dom/locale/zh-TW.d.ts +29 -0
- package/dist/src/dom/locale/zh-TW.js +29 -0
- package/dist/src/dom/locale-labels.d.ts +26 -0
- package/dist/src/dom/locale-labels.js +26 -0
- package/dist/src/dom/menus.d.ts +6 -0
- package/dist/src/dom/menus.js +34 -8
- package/dist/src/dom/message-format.d.ts +2 -1
- package/dist/src/dom/message-format.js +67 -10
- package/dist/src/dom/messages.d.ts +2 -1
- package/dist/src/dom/messages.js +2 -4
- package/dist/src/dom/selectors.d.ts +1 -0
- package/dist/src/dom/selectors.js +42 -15
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +5 -0
- package/dist/src/platform/local-paths.d.ts +4 -0
- package/dist/src/platform/local-paths.js +20 -0
- package/dist/src/runner/interruptions.js +15 -4
- package/dist/src/runner/responses.js +18 -1
- package/dist/src/runner/result.js +15 -0
- package/dist/src/runner/resume.js +1 -0
- package/dist/src/runner/types.d.ts +3 -0
- package/dist/src/safety/risk.d.ts +7 -0
- package/dist/src/safety/risk.js +7 -0
- package/dist/src/safety/untrusted-output.d.ts +73 -0
- package/dist/src/safety/untrusted-output.js +199 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +1 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.js +184 -0
- package/dist/src/scripts/capture-intelligence-locales.d.ts +25 -0
- package/dist/src/scripts/capture-intelligence-locales.js +669 -0
- package/dist/src/scripts/check-surface-drift.d.ts +1 -0
- package/dist/src/scripts/check-surface-drift.js +11 -0
- package/dist/src/scripts/live-smoke/scenarios.js +84 -0
- package/dist/src/scripts/locale-capture/language-coverage.d.ts +10 -0
- package/dist/src/scripts/locale-capture/language-coverage.js +39 -0
- package/dist/src/testing/surface-drift.d.ts +66 -0
- package/dist/src/testing/surface-drift.js +363 -0
- package/dist/src/types.d.ts +201 -2
- package/package.json +2 -1
- package/references/backend-protocol.md +93 -1
- package/references/language-coverage.md +142 -0
- package/references/localization.md +247 -0
- package/references/python-parity.md +63 -1
- package/references/safety.md +2 -0
- package/references/troubleshooting.md +58 -0
|
@@ -6,7 +6,7 @@ import type { ChatGPTResponsesCreateArgs } from "../runner/responses.js";
|
|
|
6
6
|
import type { ChatGPTRunStream } from "../runner/stream.js";
|
|
7
7
|
import type { ChatGPTAgent, ChatGPTAgentConfig, ChatGPTResponse, ChatGPTRunInput, ChatGPTRunResult } from "../runner/types.js";
|
|
8
8
|
import type { ReportRedactionOptions } from "../safety/report-redaction.js";
|
|
9
|
-
import type { BootstrapArgs, CommandResult, CopyResponseArgs, DownloadLatestArgs, NewThreadArgs, OpenThreadArgs, ReadLatestArgs, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, WaitArgs } from "../types.js";
|
|
9
|
+
import type { ArtifactDownloadArgs, ArtifactWaitArgs, BootstrapArgs, CommandResult, CopyResponseArgs, DownloadLatestArgs, ListArtifactsArgs, NewThreadArgs, OpenThreadArgs, ReadLatestArgs, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, WaitArgs } from "../types.js";
|
|
10
10
|
import { type BackendEvent, type BackendRequest, type BackendResponse } from "./protocol.js";
|
|
11
11
|
export type BackendTransport = {
|
|
12
12
|
request(request: BackendRequest): Promise<BackendResponse>;
|
|
@@ -44,6 +44,11 @@ export type ChatGPTBackendClient = {
|
|
|
44
44
|
readLatest(args?: ReadLatestArgs): Promise<CommandResult<unknown>>;
|
|
45
45
|
copyLatest(args?: CopyResponseArgs): Promise<CommandResult<unknown>>;
|
|
46
46
|
downloadLatest(args: DownloadLatestArgs): Promise<CommandResult<unknown>>;
|
|
47
|
+
artifacts: {
|
|
48
|
+
listLatest(args?: ListArtifactsArgs): Promise<CommandResult<unknown>>;
|
|
49
|
+
wait(args?: ArtifactWaitArgs): Promise<CommandResult<unknown>>;
|
|
50
|
+
downloadLatest(args: ArtifactDownloadArgs): Promise<CommandResult<unknown>>;
|
|
51
|
+
};
|
|
47
52
|
runPlan(plan: SequencePlan | NamedWorkflowInvocation): Promise<CommandResult<unknown>>;
|
|
48
53
|
doctor(args?: DoctorArgs): Promise<CommandResult<DoctorReport>>;
|
|
49
54
|
createReport(result: CommandResult<unknown>, args?: RunReportOptions): Promise<CommandResult<RunReportData>>;
|
|
@@ -76,6 +76,11 @@ export function createChatGPTBackendClient(transport) {
|
|
|
76
76
|
readLatest: args => request("messages.readLatest", args ?? {}),
|
|
77
77
|
waitAndRead: args => request("messages.waitAndRead", args)
|
|
78
78
|
},
|
|
79
|
+
artifacts: {
|
|
80
|
+
listLatest: args => request("artifacts.listLatest", args ?? {}),
|
|
81
|
+
wait: args => request("artifacts.wait", args ?? {}),
|
|
82
|
+
downloadLatest: args => request("artifacts.downloadLatest", args)
|
|
83
|
+
},
|
|
79
84
|
files: {
|
|
80
85
|
attach: args => request("files.attach", args),
|
|
81
86
|
downloadLatest: args => request("files.downloadLatest", args)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const BACKEND_REQUEST_SCHEMA_VERSION: "chatgpt.browser_control.backend_request.v1";
|
|
2
2
|
export declare const BACKEND_RESPONSE_SCHEMA_VERSION: "chatgpt.browser_control.backend_response.v1";
|
|
3
3
|
export declare const BACKEND_EVENT_SCHEMA_VERSION: "chatgpt.browser_control.backend_event.v1";
|
|
4
|
-
export declare const backendCommands: readonly ["backend.version", "backend.health", "backend.capabilities", "runner.run", "runner.plan", "runner.stream", "responses.create", "ask", "askInThread", "askWithFiles", "askAndDownload", "runMessages", "openThread", "readLatest", "copyLatest", "downloadLatest", "runPlan", "doctor", "createReport", "reports.create", "reports.redact", "reports.summarize", "commands", "describe", "help", "session.bootstrap", "threads.new", "threads.search", "threads.open", "messages.compose", "messages.submit", "messages.ask", "messages.wait", "messages.readLatest", "messages.waitAndRead", "files.attach", "files.downloadLatest", "modes.set", "tools.select", "response.copy"];
|
|
4
|
+
export declare const backendCommands: readonly ["backend.version", "backend.health", "backend.capabilities", "runner.run", "runner.plan", "runner.stream", "responses.create", "ask", "askInThread", "askWithFiles", "askAndDownload", "runMessages", "openThread", "readLatest", "copyLatest", "downloadLatest", "runPlan", "doctor", "createReport", "reports.create", "reports.redact", "reports.summarize", "commands", "describe", "help", "session.bootstrap", "threads.new", "threads.search", "threads.open", "messages.compose", "messages.submit", "messages.ask", "messages.wait", "messages.readLatest", "messages.waitAndRead", "artifacts.listLatest", "artifacts.wait", "artifacts.downloadLatest", "files.preflight", "files.attach", "files.downloadLatest", "projects.sources.list", "projects.sources.planAdd", "projects.sources.add", "modes.set", "tools.select", "response.copy"];
|
|
5
5
|
export type BackendCommand = typeof backendCommands[number];
|
|
6
6
|
export type BackendRequest = {
|
|
7
7
|
schemaVersion: typeof BACKEND_REQUEST_SCHEMA_VERSION;
|
|
@@ -37,8 +37,15 @@ export const backendCommands = [
|
|
|
37
37
|
"messages.wait",
|
|
38
38
|
"messages.readLatest",
|
|
39
39
|
"messages.waitAndRead",
|
|
40
|
+
"artifacts.listLatest",
|
|
41
|
+
"artifacts.wait",
|
|
42
|
+
"artifacts.downloadLatest",
|
|
43
|
+
"files.preflight",
|
|
40
44
|
"files.attach",
|
|
41
45
|
"files.downloadLatest",
|
|
46
|
+
"projects.sources.list",
|
|
47
|
+
"projects.sources.planAdd",
|
|
48
|
+
"projects.sources.add",
|
|
42
49
|
"modes.set",
|
|
43
50
|
"tools.select",
|
|
44
51
|
"response.copy"
|
|
@@ -141,10 +141,24 @@ async function dispatchBackendCommand(client, request) {
|
|
|
141
141
|
return client.messages.readLatest(emptyToUndefined(payload));
|
|
142
142
|
case "messages.waitAndRead":
|
|
143
143
|
return client.messages.waitAndRead(payload);
|
|
144
|
+
case "artifacts.listLatest":
|
|
145
|
+
return client.artifacts.listLatest(emptyToUndefined(payload));
|
|
146
|
+
case "artifacts.wait":
|
|
147
|
+
return client.artifacts.wait(emptyToUndefined(payload));
|
|
148
|
+
case "artifacts.downloadLatest":
|
|
149
|
+
return client.artifacts.downloadLatest(payload);
|
|
150
|
+
case "files.preflight":
|
|
151
|
+
return client.files.preflight(payload);
|
|
144
152
|
case "files.attach":
|
|
145
153
|
return client.files.attach(payload);
|
|
146
154
|
case "files.downloadLatest":
|
|
147
155
|
return client.files.downloadLatest(payload);
|
|
156
|
+
case "projects.sources.list":
|
|
157
|
+
return client.projects.sources.list(payload);
|
|
158
|
+
case "projects.sources.planAdd":
|
|
159
|
+
return client.projects.sources.planAdd(payload);
|
|
160
|
+
case "projects.sources.add":
|
|
161
|
+
return client.projects.sources.add(payload);
|
|
148
162
|
case "modes.set":
|
|
149
163
|
return client.modes.set(payload);
|
|
150
164
|
case "tools.select":
|
|
@@ -2,6 +2,8 @@ import { BrowserBridgeUnavailableError, ChatGPTControlError, LoginRequiredError
|
|
|
2
2
|
import { parseConversationId, readPageState } from "./page-state.js";
|
|
3
3
|
const CHATGPT_HOME = "https://chatgpt.com/";
|
|
4
4
|
const CHATGPT_HOSTS = new Set(["chatgpt.com", "www.chatgpt.com", "chat.openai.com"]);
|
|
5
|
+
const MAX_EXISTING_TAB_DIAGNOSTIC_CANDIDATES = 10;
|
|
6
|
+
const MAX_EXISTING_TAB_DIAGNOSTIC_FIELD_LENGTH = 240;
|
|
5
7
|
export async function attachChatGPTBrowser(env, args = {}) {
|
|
6
8
|
const browser = await getBrowser(env);
|
|
7
9
|
const page = await getOrCreateChatGPTPage(browser, env, args);
|
|
@@ -102,12 +104,12 @@ async function getOrCreateChatGPTPage(browser, env, args) {
|
|
|
102
104
|
}
|
|
103
105
|
if (explicitExistingPolicy !== undefined) {
|
|
104
106
|
const existing = await selectExistingTab(browser, explicitExistingPolicy);
|
|
105
|
-
if (existing !== undefined) {
|
|
106
|
-
return existing;
|
|
107
|
+
if (existing.page !== undefined) {
|
|
108
|
+
return existing.page;
|
|
107
109
|
}
|
|
108
110
|
const ifMissing = explicitExistingPolicy.ifMissing ?? "block";
|
|
109
111
|
if (ifMissing === "block") {
|
|
110
|
-
throw new ExistingTabSelectionError("No already-open ChatGPT tab matched the requested existing-tab target.", "existing_tab_not_found");
|
|
112
|
+
throw new ExistingTabSelectionError("No already-open ChatGPT tab matched the requested existing-tab target.", "existing_tab_not_found", existing.diagnostics?.candidateTabs, existing.diagnostics);
|
|
111
113
|
}
|
|
112
114
|
const missingUrl = ifMissing === "open"
|
|
113
115
|
? urlFromExistingTarget(explicitExistingPolicy.target) ?? targetUrl
|
|
@@ -163,8 +165,8 @@ function normalizeExplicitExistingTabPolicy(args) {
|
|
|
163
165
|
};
|
|
164
166
|
}
|
|
165
167
|
async function selectExistingTab(browser, policy) {
|
|
166
|
-
const userMatch = await selectExistingUserTab(browser, policy);
|
|
167
|
-
if (userMatch !== undefined) {
|
|
168
|
+
const userMatch = await selectExistingUserTab(browser, policy, shouldCollectExistingTabDiagnostics(policy));
|
|
169
|
+
if (userMatch.page !== undefined) {
|
|
168
170
|
return userMatch;
|
|
169
171
|
}
|
|
170
172
|
if (policy.target?.type === "selected" && typeof browser.tabs?.selected === "function") {
|
|
@@ -172,7 +174,7 @@ async function selectExistingTab(browser, policy) {
|
|
|
172
174
|
if (selected !== undefined) {
|
|
173
175
|
const normalized = normalizePage(selected);
|
|
174
176
|
if (await pageMatchesExistingTarget(normalized, policy)) {
|
|
175
|
-
return normalized;
|
|
177
|
+
return { page: normalized };
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
180
|
}
|
|
@@ -181,28 +183,37 @@ async function selectExistingTab(browser, policy) {
|
|
|
181
183
|
if (tab !== undefined) {
|
|
182
184
|
const normalized = normalizePage(tab);
|
|
183
185
|
if (await pageMatchesExistingTarget(normalized, policy)) {
|
|
184
|
-
return normalized;
|
|
186
|
+
return { page: normalized };
|
|
185
187
|
}
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
|
-
return undefined
|
|
190
|
+
return userMatch.diagnostics === undefined
|
|
191
|
+
? { diagnostics: diagnosticsForUnavailableUserTabs(policy) }
|
|
192
|
+
: userMatch;
|
|
189
193
|
}
|
|
190
|
-
async function selectExistingUserTab(browser, policy) {
|
|
194
|
+
async function selectExistingUserTab(browser, policy, collectDiagnostics) {
|
|
191
195
|
const openTabs = browser.user?.openTabs;
|
|
192
196
|
const claimTab = browser.user?.claimTab;
|
|
193
197
|
if (typeof openTabs !== "function" || typeof claimTab !== "function") {
|
|
194
|
-
return
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
const tabs = await Promise.resolve(openTabs.call(browser.user)).catch(() => undefined);
|
|
201
|
+
if (tabs === undefined) {
|
|
202
|
+
return collectDiagnostics
|
|
203
|
+
? { diagnostics: diagnosticsForUnavailableUserTabs(policy, "user_open_tabs_unavailable") }
|
|
204
|
+
: {};
|
|
195
205
|
}
|
|
196
|
-
const tabs = await Promise.resolve(openTabs.call(browser.user)).catch(() => []);
|
|
197
206
|
const matches = tabs.filter(tab => userTabMatchesTarget(tab, policy));
|
|
207
|
+
const diagnostics = collectDiagnostics ? diagnosticsForUserTabs(policy, tabs, matches) : undefined;
|
|
198
208
|
if (matches.length === 0) {
|
|
199
|
-
return undefined;
|
|
209
|
+
return diagnostics === undefined ? {} : { diagnostics };
|
|
200
210
|
}
|
|
201
211
|
if (matches.length > 1 && (policy.ifMultiple ?? "block") !== "first") {
|
|
202
|
-
throw new ExistingTabSelectionError("Multiple already-open ChatGPT tabs matched the requested existing-tab target.", "existing_tab_ambiguous", matches);
|
|
212
|
+
throw new ExistingTabSelectionError("Multiple already-open ChatGPT tabs matched the requested existing-tab target.", "existing_tab_ambiguous", matches, diagnostics);
|
|
203
213
|
}
|
|
204
214
|
const selected = matches[0];
|
|
205
|
-
|
|
215
|
+
const page = normalizePage(await claimTab.call(browser.user, selected));
|
|
216
|
+
return diagnostics === undefined ? { page } : { page, diagnostics };
|
|
206
217
|
}
|
|
207
218
|
function userTabMatchesTarget(tab, policy) {
|
|
208
219
|
const target = policy.target ?? { type: "selected", host: "chatgpt" };
|
|
@@ -224,6 +235,100 @@ function userTabMatchesTarget(tab, policy) {
|
|
|
224
235
|
return titleMatches(tab.title, target.title, target.exact ?? true);
|
|
225
236
|
}
|
|
226
237
|
}
|
|
238
|
+
function diagnosticsForUserTabs(policy, tabs, matches) {
|
|
239
|
+
const chatgptTabs = tabs.filter(tab => isChatGPTUrl(tab.url));
|
|
240
|
+
const candidateTabs = matches.length > 1 ? matches : chatgptTabs;
|
|
241
|
+
const cappedTabs = candidateTabs.slice(0, MAX_EXISTING_TAB_DIAGNOSTIC_CANDIDATES);
|
|
242
|
+
const diagnostics = {
|
|
243
|
+
requestedTarget: diagnosticTarget(policy.target ?? { type: "selected", host: "chatgpt" }),
|
|
244
|
+
userOpenTabsAvailable: true,
|
|
245
|
+
chatgptTabCount: chatgptTabs.length,
|
|
246
|
+
mismatchReason: matches.length > 1 ? "multiple_candidates" : mismatchReasonForNoMatches(policy, tabs, chatgptTabs),
|
|
247
|
+
candidateTabs: cappedTabs.map(diagnosticCandidate)
|
|
248
|
+
};
|
|
249
|
+
const omittedCandidateCount = candidateTabs.length - cappedTabs.length;
|
|
250
|
+
if (omittedCandidateCount > 0)
|
|
251
|
+
diagnostics.omittedCandidateCount = omittedCandidateCount;
|
|
252
|
+
return diagnostics;
|
|
253
|
+
}
|
|
254
|
+
function shouldCollectExistingTabDiagnostics(policy) {
|
|
255
|
+
return (policy.ifMissing ?? "block") === "block" || (policy.ifMultiple ?? "block") !== "first";
|
|
256
|
+
}
|
|
257
|
+
function diagnosticsForUnavailableUserTabs(policy, mismatchReason = undefined) {
|
|
258
|
+
const target = policy.target ?? { type: "selected", host: "chatgpt" };
|
|
259
|
+
return {
|
|
260
|
+
requestedTarget: diagnosticTarget(target),
|
|
261
|
+
userOpenTabsAvailable: false,
|
|
262
|
+
chatgptTabCount: 0,
|
|
263
|
+
mismatchReason: mismatchReason ?? (target.type === "tabId" ? "explicit_tab_id_not_open" : "selected_tab_unavailable"),
|
|
264
|
+
candidateTabs: []
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function diagnosticTarget(target) {
|
|
268
|
+
switch (target.type) {
|
|
269
|
+
case "selected": {
|
|
270
|
+
const value = { type: target.type };
|
|
271
|
+
if (target.host !== undefined)
|
|
272
|
+
value.host = target.host;
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
case "tabId":
|
|
276
|
+
return { type: target.type, tabId: target.tabId };
|
|
277
|
+
case "conversationId":
|
|
278
|
+
case "conversation_id":
|
|
279
|
+
return { type: target.type, conversationId: target.conversationId };
|
|
280
|
+
case "url":
|
|
281
|
+
return { type: target.type, url: target.url };
|
|
282
|
+
case "title": {
|
|
283
|
+
const value = { type: target.type, title: target.title };
|
|
284
|
+
if (target.exact !== undefined)
|
|
285
|
+
value.exact = target.exact;
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function diagnosticCandidate(tab) {
|
|
291
|
+
const candidate = { id: tab.id };
|
|
292
|
+
if (tab.url !== undefined) {
|
|
293
|
+
candidate.url = truncateDiagnosticField(tab.url);
|
|
294
|
+
const conversationId = parseConversationId(tab.url);
|
|
295
|
+
if (conversationId !== undefined)
|
|
296
|
+
candidate.conversationId = conversationId;
|
|
297
|
+
}
|
|
298
|
+
if (tab.title !== undefined)
|
|
299
|
+
candidate.title = truncateDiagnosticField(tab.title);
|
|
300
|
+
if (tab.lastOpened !== undefined)
|
|
301
|
+
candidate.lastOpened = truncateDiagnosticField(tab.lastOpened);
|
|
302
|
+
if (tab.tabGroup !== undefined)
|
|
303
|
+
candidate.tabGroup = truncateDiagnosticField(tab.tabGroup);
|
|
304
|
+
return candidate;
|
|
305
|
+
}
|
|
306
|
+
function truncateDiagnosticField(value) {
|
|
307
|
+
return value.length <= MAX_EXISTING_TAB_DIAGNOSTIC_FIELD_LENGTH
|
|
308
|
+
? value
|
|
309
|
+
: `${value.slice(0, MAX_EXISTING_TAB_DIAGNOSTIC_FIELD_LENGTH - 1)}…`;
|
|
310
|
+
}
|
|
311
|
+
function mismatchReasonForNoMatches(policy, tabs, chatgptTabs) {
|
|
312
|
+
const target = policy.target ?? { type: "selected", host: "chatgpt" };
|
|
313
|
+
if (tabs.length === 0)
|
|
314
|
+
return "no_candidate";
|
|
315
|
+
if (chatgptTabs.length === 0 && (policy.requireChatGPT ?? targetRequiresChatGPT(target))) {
|
|
316
|
+
return "non_chatgpt_tab";
|
|
317
|
+
}
|
|
318
|
+
switch (target.type) {
|
|
319
|
+
case "tabId":
|
|
320
|
+
return tabs.some(tab => tab.id === target.tabId) ? "non_chatgpt_tab" : "explicit_tab_id_not_open";
|
|
321
|
+
case "conversationId":
|
|
322
|
+
case "conversation_id":
|
|
323
|
+
return "conversation_id_mismatch";
|
|
324
|
+
case "url":
|
|
325
|
+
return "url_mismatch";
|
|
326
|
+
case "title":
|
|
327
|
+
return "title_mismatch";
|
|
328
|
+
case "selected":
|
|
329
|
+
return "selected_tab_unavailable";
|
|
330
|
+
}
|
|
331
|
+
}
|
|
227
332
|
async function pageMatchesExistingTarget(page, policy) {
|
|
228
333
|
const url = await Promise.resolve(page.url?.()).catch(() => undefined);
|
|
229
334
|
const title = await Promise.resolve(page.title?.()).catch(() => undefined);
|
|
@@ -239,9 +344,9 @@ async function findExistingChatGPTTab(browser) {
|
|
|
239
344
|
target: { type: "selected", host: "chatgpt" },
|
|
240
345
|
ifMultiple: "first",
|
|
241
346
|
requireChatGPT: true
|
|
242
|
-
});
|
|
243
|
-
if (userTab !== undefined) {
|
|
244
|
-
return userTab;
|
|
347
|
+
}, false).catch(() => ({ page: undefined }));
|
|
348
|
+
if (userTab.page !== undefined) {
|
|
349
|
+
return userTab.page;
|
|
245
350
|
}
|
|
246
351
|
const selected = browser.tabs?.selected;
|
|
247
352
|
if (typeof selected === "function") {
|
|
@@ -282,8 +387,8 @@ async function findExistingChatGPTTab(browser) {
|
|
|
282
387
|
return undefined;
|
|
283
388
|
}
|
|
284
389
|
class ExistingTabSelectionError extends ChatGPTControlError {
|
|
285
|
-
constructor(message, code, candidates = []) {
|
|
286
|
-
|
|
390
|
+
constructor(message, code, candidates = [], diagnostics) {
|
|
391
|
+
const details = {
|
|
287
392
|
code,
|
|
288
393
|
candidates: candidates.map(tab => ({ label: userTabCandidateLabel(tab) })),
|
|
289
394
|
remediation: [
|
|
@@ -298,7 +403,10 @@ class ExistingTabSelectionError extends ChatGPTControlError {
|
|
|
298
403
|
userActionRequired: false
|
|
299
404
|
}
|
|
300
405
|
]
|
|
301
|
-
}
|
|
406
|
+
};
|
|
407
|
+
if (diagnostics !== undefined)
|
|
408
|
+
details.diagnostics = { existingTab: diagnostics };
|
|
409
|
+
super(message, "not_found", true, undefined, details);
|
|
302
410
|
}
|
|
303
411
|
}
|
|
304
412
|
function targetRequiresChatGPT(target) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdir, stat } from "node:fs/promises";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { localGuardTimeout, withTimeout } from "../commands/timeouts.js";
|
|
3
4
|
export async function waitForDownloadFromClick(page, click, destDir, timeoutMs) {
|
|
4
5
|
const absoluteDest = resolve(destDir);
|
|
5
6
|
await mkdir(absoluteDest, { recursive: true });
|
|
@@ -7,7 +8,7 @@ export async function waitForDownloadFromClick(page, click, destDir, timeoutMs)
|
|
|
7
8
|
if (downloadPromise === undefined) {
|
|
8
9
|
throw new Error("The active browser page does not expose download events.");
|
|
9
10
|
}
|
|
10
|
-
await click();
|
|
11
|
+
await withTimeout(click(), localGuardTimeout(timeoutMs, 10000), "Download control click did not complete before the local guard timeout.");
|
|
11
12
|
const download = await downloadPromise;
|
|
12
13
|
const suggestedFilename = download.suggestedFilename?.() ?? `chatgpt-download-${Date.now()}`;
|
|
13
14
|
const targetPath = join(absoluteDest, basename(suggestedFilename));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { classifyVisibleText } from "../safety/blockers.js";
|
|
2
2
|
import { compactVisibleText } from "../safety/redaction.js";
|
|
3
|
+
import { escapeRegExp, localeLabels } from "../dom/locale-labels.js";
|
|
4
|
+
import { withTimeout } from "../commands/timeouts.js";
|
|
3
5
|
export function parseConversationId(url) {
|
|
4
6
|
const match = /\/c\/([A-Za-z0-9-]+)/.exec(url);
|
|
5
7
|
return match?.[1];
|
|
@@ -32,7 +34,7 @@ export async function readPageState(page) {
|
|
|
32
34
|
export async function readVisibleText(page) {
|
|
33
35
|
if (typeof page.evaluate === "function") {
|
|
34
36
|
try {
|
|
35
|
-
return await page.evaluate(() => document.body?.innerText ?? "");
|
|
37
|
+
return await withTimeout(page.evaluate(() => document.body?.innerText ?? ""), 1000, "Timed out while reading visible page text.");
|
|
36
38
|
}
|
|
37
39
|
catch {
|
|
38
40
|
// Fall back to content parsing below.
|
|
@@ -40,7 +42,7 @@ export async function readVisibleText(page) {
|
|
|
40
42
|
}
|
|
41
43
|
if (typeof page.content === "function") {
|
|
42
44
|
try {
|
|
43
|
-
const html = await page.content();
|
|
45
|
+
const html = await withTimeout(page.content(), 1000, "Timed out while reading page content.");
|
|
44
46
|
return htmlToText(html);
|
|
45
47
|
}
|
|
46
48
|
catch {
|
|
@@ -62,5 +64,6 @@ export function htmlToText(html) {
|
|
|
62
64
|
.trim();
|
|
63
65
|
}
|
|
64
66
|
function isLikelySignedIn(visibleText) {
|
|
65
|
-
|
|
67
|
+
const markers = localeLabels.signedInMarkers.map(escapeRegExp).join("|");
|
|
68
|
+
return new RegExp(`\\b(${markers})\\b`, "i").test(visibleText);
|
|
66
69
|
}
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { AskArgs, AttachFilesArgs, BootstrapArgs, CommandResult, CopyResponseArgs, DownloadLatestArgs, NewThreadArgs, OpenThreadArgs, ReadLatestArgs, RuntimeEnv, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, ThreadTarget, WaitArgs } from "./types.js";
|
|
1
|
+
import type { AskArgs, ArtifactDownloadArgs, ArtifactWaitArgs, AttachFilesArgs, BootstrapArgs, CommandResult, CopyResponseArgs, DownloadLatestArgs, FilePreflightArgs, FilePreflightData, ListArtifactsArgs, NewThreadArgs, OpenThreadArgs, ProjectSourcesAddArgs, ProjectSourcesAddData, ProjectSourcesAddPlanData, ProjectSourcesListArgs, ProjectSourcesListData, ProjectSourcesPlanAddArgs, ReadLatestArgs, RuntimeEnv, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, ThreadTarget, WaitArgs } from "./types.js";
|
|
2
2
|
import { type DoctorArgs, type DoctorReport } from "./commands/doctor.js";
|
|
3
3
|
import { type RunReportData, type RunReportOptions } from "./commands/reports.js";
|
|
4
4
|
import { type CommandDescriptor } from "./commands/registry.js";
|
|
5
5
|
import type { ChatGPTAgent, ChatGPTAgentConfig, ChatGPTResponse, ChatGPTRunner, ChatGPTRunInput, ChatGPTRunResult } from "./runner/types.js";
|
|
6
6
|
import { type ChatGPTResponsesCreateArgs } from "./runner/responses.js";
|
|
7
7
|
import { type ReportRedactionOptions } from "./safety/report-redaction.js";
|
|
8
|
+
import { type BlockerExplanation, type ExplainBlockerOptions } from "./diagnostics/blockers.js";
|
|
8
9
|
export type ChatGPTClientOptions = RuntimeEnv & {
|
|
9
10
|
defaults?: {
|
|
10
11
|
mode?: SetModeArgs;
|
|
@@ -112,9 +113,15 @@ export type ChatGPTClient = {
|
|
|
112
113
|
readLatest(args?: ReadLatestArgs): Promise<CommandResult<unknown>>;
|
|
113
114
|
copyLatest(args?: CopyResponseArgs): Promise<CommandResult<unknown>>;
|
|
114
115
|
downloadLatest(args: DownloadLatestArgs): Promise<CommandResult<unknown>>;
|
|
116
|
+
artifacts: {
|
|
117
|
+
listLatest(args?: ListArtifactsArgs): Promise<CommandResult<unknown>>;
|
|
118
|
+
wait(args?: ArtifactWaitArgs): Promise<CommandResult<unknown>>;
|
|
119
|
+
downloadLatest(args: ArtifactDownloadArgs): Promise<CommandResult<unknown>>;
|
|
120
|
+
};
|
|
115
121
|
runPlan(plan: SequencePlan | NamedWorkflowInvocation): Promise<CommandResult<unknown>>;
|
|
116
122
|
doctor(args?: DoctorArgs): Promise<CommandResult<DoctorReport>>;
|
|
117
123
|
createReport(result: CommandResult<unknown>, args?: RunReportOptions): Promise<CommandResult<RunReportData>>;
|
|
124
|
+
explainBlocker(resultOrBlocker: CommandResult<unknown> | NonNullable<CommandResult["blocker"]> | undefined, options?: ExplainBlockerOptions): BlockerExplanation;
|
|
118
125
|
reports: {
|
|
119
126
|
create(result: CommandResult<unknown>, args?: RunReportOptions): Promise<CommandResult<RunReportData>>;
|
|
120
127
|
redact(value: unknown, args?: ReportRedactionOptions): Promise<CommandResult<unknown>>;
|
|
@@ -151,9 +158,17 @@ export type ChatGPTClient = {
|
|
|
151
158
|
waitAndRead(args?: WaitArgs & ReadLatestArgs): Promise<CommandResult<unknown>>;
|
|
152
159
|
};
|
|
153
160
|
files: {
|
|
161
|
+
preflight(args: FilePreflightArgs): Promise<CommandResult<FilePreflightData>>;
|
|
154
162
|
attach(args: AttachFilesArgs): Promise<CommandResult<unknown>>;
|
|
155
163
|
downloadLatest(args: DownloadLatestArgs): Promise<CommandResult<unknown>>;
|
|
156
164
|
};
|
|
165
|
+
projects: {
|
|
166
|
+
sources: {
|
|
167
|
+
list(args: ProjectSourcesListArgs): Promise<CommandResult<ProjectSourcesListData>>;
|
|
168
|
+
planAdd(args: ProjectSourcesPlanAddArgs): Promise<CommandResult<ProjectSourcesAddPlanData>>;
|
|
169
|
+
add(args: ProjectSourcesAddArgs): Promise<CommandResult<ProjectSourcesAddData | ProjectSourcesAddPlanData>>;
|
|
170
|
+
};
|
|
171
|
+
};
|
|
157
172
|
modes: {
|
|
158
173
|
set(args: SetModeArgs): Promise<CommandResult<unknown>>;
|
|
159
174
|
};
|
package/dist/src/client.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { downloadLatestArtifact, listLatestArtifacts, waitForArtifact } from "./commands/artifacts.js";
|
|
2
|
+
import { attachFiles, downloadLatestFile, preflightFiles } from "./commands/files.js";
|
|
3
|
+
import { addProjectSources, buildProjectSourceAddPlan, listProjectSources } from "./commands/project-sources.js";
|
|
2
4
|
import { doctor } from "./commands/doctor.js";
|
|
3
5
|
import { askMessage, composeMessage, readLatest, submitMessage, waitAndRead, waitForMessage } from "./commands/messages.js";
|
|
4
6
|
import { selectTool, setMode } from "./commands/modes.js";
|
|
@@ -14,6 +16,7 @@ import { toRunResult } from "./runner/result.js";
|
|
|
14
16
|
import { responseFromRunResult, responsesCreateArgsToRunInput, unsupportedResponse, validateResponsesCreateArgs } from "./runner/responses.js";
|
|
15
17
|
import { streamFromRunResult } from "./runner/stream.js";
|
|
16
18
|
import { redactReportValue } from "./safety/report-redaction.js";
|
|
19
|
+
import { explainCommandBlocker } from "./diagnostics/blockers.js";
|
|
17
20
|
export function createChatGPT(options = {}) {
|
|
18
21
|
const env = runtimeEnv(options);
|
|
19
22
|
const limits = normalizeLimits(options.limits);
|
|
@@ -44,6 +47,7 @@ export function createChatGPT(options = {}) {
|
|
|
44
47
|
runPlan: plan => runPlanInvocation(plan, env, limits, options.defaults, options.reporting),
|
|
45
48
|
doctor: args => doctor(env, args),
|
|
46
49
|
createReport: (result, args) => createRunReport(env, result, args ?? options.reporting ?? {}),
|
|
50
|
+
explainBlocker: (resultOrBlocker, args) => explainCommandBlocker(resultOrBlocker, args),
|
|
47
51
|
reports: {
|
|
48
52
|
create: (result, args) => createRunReport(env, result, args ?? options.reporting ?? {}),
|
|
49
53
|
redact: async (value, args) => resultOk(redactReportValue(value, args), {}),
|
|
@@ -70,9 +74,22 @@ export function createChatGPT(options = {}) {
|
|
|
70
74
|
waitAndRead: args => waitAndRead(env, args)
|
|
71
75
|
},
|
|
72
76
|
files: {
|
|
77
|
+
preflight: args => preflightFiles(env, args),
|
|
73
78
|
attach: args => attachFiles(env, args),
|
|
74
79
|
downloadLatest: args => downloadLatestFile(env, args)
|
|
75
80
|
},
|
|
81
|
+
projects: {
|
|
82
|
+
sources: {
|
|
83
|
+
list: args => listProjectSources(env, args),
|
|
84
|
+
planAdd: args => buildProjectSourceAddPlan(env, args),
|
|
85
|
+
add: args => addProjectSources(env, args)
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
artifacts: {
|
|
89
|
+
listLatest: args => listLatestArtifacts(env, args),
|
|
90
|
+
wait: args => waitForArtifact(env, args),
|
|
91
|
+
downloadLatest: args => downloadLatestArtifact(env, args)
|
|
92
|
+
},
|
|
76
93
|
modes: {
|
|
77
94
|
set: args => setMode(env, args)
|
|
78
95
|
},
|
|
@@ -88,6 +105,9 @@ async function runGuarded(plan, env, limits, report) {
|
|
|
88
105
|
const budget = checkRunBudget(plan, limits);
|
|
89
106
|
if (budget !== undefined)
|
|
90
107
|
return budget;
|
|
108
|
+
const filePreflight = await preflightPlanFiles(plan, env);
|
|
109
|
+
if (filePreflight !== undefined)
|
|
110
|
+
return filePreflight;
|
|
91
111
|
const result = await runSequence(plan, env);
|
|
92
112
|
if (report === undefined || report.enabled === false)
|
|
93
113
|
return result;
|
|
@@ -133,6 +153,18 @@ async function runGuarded(plan, env, limits, report) {
|
|
|
133
153
|
]
|
|
134
154
|
};
|
|
135
155
|
}
|
|
156
|
+
async function preflightPlanFiles(plan, env) {
|
|
157
|
+
const paths = plan.steps
|
|
158
|
+
.flatMap(step => step.command === "files.attach" ? pathsFromAttachStep(step) : []);
|
|
159
|
+
if (paths.length === 0)
|
|
160
|
+
return undefined;
|
|
161
|
+
const result = await preflightFiles(env, { paths });
|
|
162
|
+
return result.ok ? undefined : result;
|
|
163
|
+
}
|
|
164
|
+
function pathsFromAttachStep(step) {
|
|
165
|
+
const paths = step.args.paths;
|
|
166
|
+
return paths.every(item => typeof item === "string") ? paths : [];
|
|
167
|
+
}
|
|
136
168
|
function normalizeLimits(limits) {
|
|
137
169
|
return {
|
|
138
170
|
maxPromptsPerRun: limits?.maxPromptsPerRun ?? 5,
|
|
@@ -232,6 +264,7 @@ function planAgentWorkflowFromNormalized(agent, input, defaults = {}) {
|
|
|
232
264
|
const wait = input.wait ?? agent.defaults.wait ?? defaults.wait ?? true;
|
|
233
265
|
const read = input.read ?? agent.defaults.read ?? defaults.read ?? { format: "markdown" };
|
|
234
266
|
const thread = input.thread ?? agent.defaults.thread ?? { type: "new" };
|
|
267
|
+
const artifactDownload = input.download !== undefined && input.download !== false && usesCreateImageTool(input.tools);
|
|
235
268
|
const steps = [
|
|
236
269
|
bootstrapStepForWorkflow(thread, input.existingTab ?? agent.defaults.existingTab ?? defaults.existingTab, input.preferExistingTab ?? agent.defaults.preferExistingTab ?? defaults.preferExistingTab),
|
|
237
270
|
...threadSteps(thread)
|
|
@@ -246,6 +279,9 @@ function planAgentWorkflowFromNormalized(agent, input, defaults = {}) {
|
|
|
246
279
|
if (input.files.length > 0) {
|
|
247
280
|
steps.push({ id: "attach", command: "files.attach", args: { paths: input.files } });
|
|
248
281
|
}
|
|
282
|
+
if (artifactDownload) {
|
|
283
|
+
steps.push({ id: "artifactBaseline", command: "artifacts.listLatest", args: { kind: "image" } });
|
|
284
|
+
}
|
|
249
285
|
if (agent.instructionsMode === "visible_setup_message" && hasInstructions(agent)) {
|
|
250
286
|
steps.push({
|
|
251
287
|
id: "agent_setup",
|
|
@@ -262,15 +298,22 @@ function planAgentWorkflowFromNormalized(agent, input, defaults = {}) {
|
|
|
262
298
|
command: "messages.ask",
|
|
263
299
|
args: {
|
|
264
300
|
text: renderRunnerPrompt(agent, input.prompt),
|
|
265
|
-
wait,
|
|
266
|
-
read
|
|
301
|
+
wait: artifactDownload ? false : wait,
|
|
302
|
+
read: artifactDownload ? false : read
|
|
267
303
|
}
|
|
268
304
|
});
|
|
305
|
+
if (artifactDownload) {
|
|
306
|
+
steps.push({
|
|
307
|
+
id: "artifact",
|
|
308
|
+
command: "artifacts.wait",
|
|
309
|
+
args: artifactWaitArgs(wait, input.download === false ? undefined : input.download)
|
|
310
|
+
});
|
|
311
|
+
}
|
|
269
312
|
if (input.copy !== undefined && input.copy !== false) {
|
|
270
313
|
steps.push({ id: "copy", command: "response.copy", args: input.copy });
|
|
271
314
|
}
|
|
272
315
|
if (input.download !== undefined && input.download !== false) {
|
|
273
|
-
steps.push({ id: "download", command: "files.downloadLatest", args: input.download });
|
|
316
|
+
steps.push({ id: "download", command: artifactDownload ? "artifacts.downloadLatest" : "files.downloadLatest", args: input.download });
|
|
274
317
|
}
|
|
275
318
|
return {
|
|
276
319
|
name: `agent-run:${agent.name}`,
|
|
@@ -425,17 +468,28 @@ function planAskWorkflow(args, defaults = {}) {
|
|
|
425
468
|
if (files.length > 0) {
|
|
426
469
|
steps.push({ id: "attach", command: "files.attach", args: { paths: files } });
|
|
427
470
|
}
|
|
471
|
+
const artifactDownload = args.download !== undefined && usesCreateImageTool(args.tools ?? []);
|
|
472
|
+
if (artifactDownload) {
|
|
473
|
+
steps.push({ id: "artifactBaseline", command: "artifacts.listLatest", args: { kind: "image" } });
|
|
474
|
+
}
|
|
428
475
|
steps.push({
|
|
429
476
|
id: "ask",
|
|
430
477
|
command: "messages.ask",
|
|
431
478
|
args: {
|
|
432
479
|
text: args.prompt,
|
|
433
|
-
wait: args.wait ?? defaults.wait ?? true,
|
|
434
|
-
read: args.read ?? defaults.read ?? { format: "markdown" }
|
|
480
|
+
wait: artifactDownload ? false : args.wait ?? defaults.wait ?? true,
|
|
481
|
+
read: artifactDownload ? false : args.read ?? defaults.read ?? { format: "markdown" }
|
|
435
482
|
}
|
|
436
483
|
});
|
|
437
484
|
if (args.download !== undefined) {
|
|
438
|
-
|
|
485
|
+
if (artifactDownload) {
|
|
486
|
+
steps.push({
|
|
487
|
+
id: "artifact",
|
|
488
|
+
command: "artifacts.wait",
|
|
489
|
+
args: artifactWaitArgs(args.wait ?? defaults.wait ?? true, args.download)
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
steps.push({ id: "download", command: artifactDownload ? "artifacts.downloadLatest" : "files.downloadLatest", args: args.download });
|
|
439
493
|
}
|
|
440
494
|
return {
|
|
441
495
|
name: args.download === undefined ? "ask" : "ask-and-download",
|
|
@@ -443,6 +497,31 @@ function planAskWorkflow(args, defaults = {}) {
|
|
|
443
497
|
steps
|
|
444
498
|
};
|
|
445
499
|
}
|
|
500
|
+
function usesCreateImageTool(tools) {
|
|
501
|
+
return tools.some(tool => normalizeToolName(tool.tool) === "create_image");
|
|
502
|
+
}
|
|
503
|
+
function normalizeToolName(tool) {
|
|
504
|
+
return tool.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
505
|
+
}
|
|
506
|
+
function artifactWaitArgs(wait, download) {
|
|
507
|
+
const args = {
|
|
508
|
+
kind: "image",
|
|
509
|
+
afterArtifactCount: "${artifactBaseline.data.count}",
|
|
510
|
+
requireDownload: true
|
|
511
|
+
};
|
|
512
|
+
if (typeof wait === "object") {
|
|
513
|
+
if (wait.timeoutMs !== undefined)
|
|
514
|
+
args.timeoutMs = wait.timeoutMs;
|
|
515
|
+
if (wait.stableMs !== undefined)
|
|
516
|
+
args.stableMs = wait.stableMs;
|
|
517
|
+
if (wait.pollMs !== undefined)
|
|
518
|
+
args.pollMs = wait.pollMs;
|
|
519
|
+
}
|
|
520
|
+
if (args.timeoutMs === undefined && download?.timeoutMs !== undefined) {
|
|
521
|
+
args.timeoutMs = download.timeoutMs;
|
|
522
|
+
}
|
|
523
|
+
return args;
|
|
524
|
+
}
|
|
446
525
|
function planRunMessages(args, defaults = {}) {
|
|
447
526
|
const thread = args.thread ?? { type: "new" };
|
|
448
527
|
const steps = [
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ArtifactDownloadArgs, ArtifactListData, ArtifactWaitArgs, ArtifactWaitData, CommandResult, DownloadedFile, ListArtifactsArgs, LocatorLike, RuntimeEnv } from "../types.js";
|
|
2
|
+
export declare function listLatestArtifacts(env: RuntimeEnv, args?: ListArtifactsArgs): Promise<CommandResult<ArtifactListData>>;
|
|
3
|
+
export declare function waitForArtifact(env: RuntimeEnv, args?: ArtifactWaitArgs): Promise<CommandResult<ArtifactWaitData>>;
|
|
4
|
+
export declare function downloadLatestArtifact(env: RuntimeEnv, args: ArtifactDownloadArgs): Promise<CommandResult<DownloadedFile>>;
|
|
5
|
+
export declare function locatorCountWithTimeout(locator: LocatorLike | undefined, timeoutMs: number, code: string): Promise<number>;
|