codex-chatgpt-control 0.1.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.
Files changed (179) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +54 -0
  4. package/contracts/v1/fixtures/backend-capabilities.json +54 -0
  5. package/contracts/v1/fixtures/backend-error-event-missing-stream-input.json +10 -0
  6. package/contracts/v1/fixtures/backend-error-missing-run-input.json +10 -0
  7. package/contracts/v1/fixtures/backend-runner-plan-request.json +12 -0
  8. package/contracts/v1/fixtures/backend-version.json +10 -0
  9. package/contracts/v1/fixtures/command-descriptors.json +803 -0
  10. package/contracts/v1/fixtures/describe-runner-run.json +31 -0
  11. package/contracts/v1/fixtures/doctor-bridge-upload.json +34 -0
  12. package/contracts/v1/fixtures/help-root.json +6 -0
  13. package/contracts/v1/fixtures/named-plan-two-turn.json +39 -0
  14. package/contracts/v1/fixtures/output-json-parse-success.json +115 -0
  15. package/contracts/v1/fixtures/primitive-bootstrap-blocker.json +43 -0
  16. package/contracts/v1/fixtures/report-redaction-default.json +16 -0
  17. package/contracts/v1/fixtures/reports-create-redacted.json +16 -0
  18. package/contracts/v1/fixtures/reports-summarize-redacted.json +26 -0
  19. package/contracts/v1/fixtures/responses-basic-success.json +27 -0
  20. package/contracts/v1/fixtures/responses-hidden-instructions-unsupported.json +22 -0
  21. package/contracts/v1/fixtures/responses-unknown-field-unsupported.json +22 -0
  22. package/contracts/v1/fixtures/responses-unsupported-previous-response-id.json +22 -0
  23. package/contracts/v1/fixtures/responses-unsupported-temperature.json +22 -0
  24. package/contracts/v1/fixtures/run-basic-success.json +36 -0
  25. package/contracts/v1/fixtures/run-browser-bridge-blocker.json +180 -0
  26. package/contracts/v1/fixtures/run-file-attach-success.json +63 -0
  27. package/contracts/v1/fixtures/run-report-redacted.json +52 -0
  28. package/contracts/v1/fixtures/run-selector-drift-blocker.json +60 -0
  29. package/contracts/v1/fixtures/run-timeout-partial.json +81 -0
  30. package/contracts/v1/fixtures/run-two-turn-success.json +83 -0
  31. package/contracts/v1/fixtures/run-upload-permission-blocker.json +56 -0
  32. package/contracts/v1/fixtures/runner-budget-blocker.json +106 -0
  33. package/contracts/v1/fixtures/runner-full-agent-config.json +50 -0
  34. package/contracts/v1/fixtures/runner-input-items-and-files-plan.json +53 -0
  35. package/contracts/v1/fixtures/runner-metadata-only-plan.json +31 -0
  36. package/contracts/v1/fixtures/runner-visible-prefix-plan.json +28 -0
  37. package/contracts/v1/fixtures/runner-visible-setup-plan.json +37 -0
  38. package/contracts/v1/fixtures/stream-basic.ndjson +3 -0
  39. package/contracts/v1/fixtures/stream-blocked.ndjson +2 -0
  40. package/contracts/v1/fixtures/stream-submitted-completed.ndjson +3 -0
  41. package/contracts/v1/fixtures/workflow-ask-success.json +67 -0
  42. package/contracts/v1/manifest.json +210 -0
  43. package/contracts/v1/parity-suite.json +635 -0
  44. package/contracts/v1/schemas/agent.schema.json +52 -0
  45. package/contracts/v1/schemas/backend-event.schema.json +74 -0
  46. package/contracts/v1/schemas/backend-request.schema.json +59 -0
  47. package/contracts/v1/schemas/backend-response.schema.json +42 -0
  48. package/contracts/v1/schemas/capabilities.schema.json +33 -0
  49. package/contracts/v1/schemas/command-descriptor.schema.json +31 -0
  50. package/contracts/v1/schemas/command-result.schema.json +99 -0
  51. package/contracts/v1/schemas/manifest.schema.json +69 -0
  52. package/contracts/v1/schemas/response.schema.json +51 -0
  53. package/contracts/v1/schemas/run-result.schema.json +138 -0
  54. package/contracts/v1/schemas/sequence-plan.schema.json +44 -0
  55. package/contracts/v1/schemas/stream-event.schema.json +25 -0
  56. package/dist/codex-chatgpt-control-backend.mjs +5284 -0
  57. package/dist/codex-chatgpt-control.bundle.mjs +6013 -0
  58. package/dist/src/backend/client.d.ts +129 -0
  59. package/dist/src/backend/client.js +415 -0
  60. package/dist/src/backend/protocol.d.ts +74 -0
  61. package/dist/src/backend/protocol.js +128 -0
  62. package/dist/src/backend/session.d.ts +11 -0
  63. package/dist/src/backend/session.js +223 -0
  64. package/dist/src/backend/stdio-server.d.ts +9 -0
  65. package/dist/src/backend/stdio-server.js +88 -0
  66. package/dist/src/browser/attach.d.ts +8 -0
  67. package/dist/src/browser/attach.js +456 -0
  68. package/dist/src/browser/clipboard.d.ts +2 -0
  69. package/dist/src/browser/clipboard.js +26 -0
  70. package/dist/src/browser/downloads.d.ts +7 -0
  71. package/dist/src/browser/downloads.js +29 -0
  72. package/dist/src/browser/page-state.d.ts +17 -0
  73. package/dist/src/browser/page-state.js +66 -0
  74. package/dist/src/client.d.ts +167 -0
  75. package/dist/src/client.js +666 -0
  76. package/dist/src/commands/confirmations.d.ts +9 -0
  77. package/dist/src/commands/confirmations.js +33 -0
  78. package/dist/src/commands/context.d.ts +2 -0
  79. package/dist/src/commands/context.js +32 -0
  80. package/dist/src/commands/doctor.d.ts +16 -0
  81. package/dist/src/commands/doctor.js +120 -0
  82. package/dist/src/commands/files.d.ts +4 -0
  83. package/dist/src/commands/files.js +314 -0
  84. package/dist/src/commands/helpers.d.ts +16 -0
  85. package/dist/src/commands/helpers.js +128 -0
  86. package/dist/src/commands/messages.d.ts +16 -0
  87. package/dist/src/commands/messages.js +538 -0
  88. package/dist/src/commands/modes.d.ts +9 -0
  89. package/dist/src/commands/modes.js +323 -0
  90. package/dist/src/commands/output.d.ts +5 -0
  91. package/dist/src/commands/output.js +31 -0
  92. package/dist/src/commands/registry.d.ts +17 -0
  93. package/dist/src/commands/registry.js +275 -0
  94. package/dist/src/commands/reports.d.ts +14 -0
  95. package/dist/src/commands/reports.js +47 -0
  96. package/dist/src/commands/response-actions.d.ts +2 -0
  97. package/dist/src/commands/response-actions.js +141 -0
  98. package/dist/src/commands/sequence.d.ts +9 -0
  99. package/dist/src/commands/sequence.js +228 -0
  100. package/dist/src/commands/session.d.ts +2 -0
  101. package/dist/src/commands/session.js +25 -0
  102. package/dist/src/commands/threads.d.ts +6 -0
  103. package/dist/src/commands/threads.js +300 -0
  104. package/dist/src/dom/fixtures.d.ts +1 -0
  105. package/dist/src/dom/fixtures.js +8 -0
  106. package/dist/src/dom/menus.d.ts +8 -0
  107. package/dist/src/dom/menus.js +40 -0
  108. package/dist/src/dom/message-format.d.ts +30 -0
  109. package/dist/src/dom/message-format.js +593 -0
  110. package/dist/src/dom/messages.d.ts +40 -0
  111. package/dist/src/dom/messages.js +127 -0
  112. package/dist/src/dom/selectors.d.ts +20 -0
  113. package/dist/src/dom/selectors.js +72 -0
  114. package/dist/src/dom/visible-text.d.ts +5 -0
  115. package/dist/src/dom/visible-text.js +27 -0
  116. package/dist/src/errors.d.ts +32 -0
  117. package/dist/src/errors.js +127 -0
  118. package/dist/src/index.d.ts +34 -0
  119. package/dist/src/index.js +34 -0
  120. package/dist/src/logger.d.ts +15 -0
  121. package/dist/src/logger.js +25 -0
  122. package/dist/src/runner/agent.d.ts +2 -0
  123. package/dist/src/runner/agent.js +17 -0
  124. package/dist/src/runner/index.d.ts +7 -0
  125. package/dist/src/runner/index.js +7 -0
  126. package/dist/src/runner/interruptions.d.ts +3 -0
  127. package/dist/src/runner/interruptions.js +65 -0
  128. package/dist/src/runner/responses.d.ts +30 -0
  129. package/dist/src/runner/responses.js +171 -0
  130. package/dist/src/runner/result.d.ts +3 -0
  131. package/dist/src/runner/result.js +128 -0
  132. package/dist/src/runner/resume.d.ts +11 -0
  133. package/dist/src/runner/resume.js +26 -0
  134. package/dist/src/runner/stream.d.ts +13 -0
  135. package/dist/src/runner/stream.js +66 -0
  136. package/dist/src/runner/types.d.ts +264 -0
  137. package/dist/src/runner/types.js +1 -0
  138. package/dist/src/safety/blockers.d.ts +7 -0
  139. package/dist/src/safety/blockers.js +60 -0
  140. package/dist/src/safety/redaction.d.ts +2 -0
  141. package/dist/src/safety/redaction.js +18 -0
  142. package/dist/src/safety/report-redaction.d.ts +8 -0
  143. package/dist/src/safety/report-redaction.js +74 -0
  144. package/dist/src/safety/risk.d.ts +26 -0
  145. package/dist/src/safety/risk.js +28 -0
  146. package/dist/src/scripts/backend-server.d.ts +2 -0
  147. package/dist/src/scripts/backend-server.js +7 -0
  148. package/dist/src/scripts/check-parity-suite.d.ts +1 -0
  149. package/dist/src/scripts/check-parity-suite.js +10 -0
  150. package/dist/src/scripts/continue-thread.d.ts +27 -0
  151. package/dist/src/scripts/continue-thread.js +388 -0
  152. package/dist/src/scripts/live-smoke/harness.d.ts +11 -0
  153. package/dist/src/scripts/live-smoke/harness.js +151 -0
  154. package/dist/src/scripts/live-smoke/scenarios.d.ts +3 -0
  155. package/dist/src/scripts/live-smoke/scenarios.js +648 -0
  156. package/dist/src/scripts/live-smoke/types.d.ts +53 -0
  157. package/dist/src/scripts/live-smoke/types.js +1 -0
  158. package/dist/src/scripts/live-smoke-module.d.ts +3 -0
  159. package/dist/src/scripts/live-smoke-module.js +2 -0
  160. package/dist/src/scripts/live-smoke.d.ts +1 -0
  161. package/dist/src/scripts/live-smoke.js +54 -0
  162. package/dist/src/scripts/smoke-attach-file.d.ts +1 -0
  163. package/dist/src/scripts/smoke-attach-file.js +39 -0
  164. package/dist/src/scripts/smoke-hi.d.ts +1 -0
  165. package/dist/src/scripts/smoke-hi.js +25 -0
  166. package/dist/src/scripts/smoke-search-open-read.d.ts +1 -0
  167. package/dist/src/scripts/smoke-search-open-read.js +41 -0
  168. package/dist/src/testing/parity-suite.d.ts +12 -0
  169. package/dist/src/testing/parity-suite.js +213 -0
  170. package/dist/src/types.d.ts +495 -0
  171. package/dist/src/types.js +1 -0
  172. package/package.json +81 -0
  173. package/references/agents-runner.md +32 -0
  174. package/references/backend-protocol.md +238 -0
  175. package/references/python-parity.md +168 -0
  176. package/references/responses-adapter.md +28 -0
  177. package/references/safety.md +14 -0
  178. package/references/streaming.md +15 -0
  179. package/references/troubleshooting.md +91 -0
@@ -0,0 +1,127 @@
1
+ import { extractRoleMessageHtml, formatMessageHtml, normalizeResponseFormat } from "./message-format.js";
2
+ import { normalizeWhitespace } from "./visible-text.js";
3
+ export function extractMessagesFromHtml(html, args = {}) {
4
+ return extractRoleMessageHtml(html)
5
+ .filter(message => args.role === undefined || message.role === args.role)
6
+ .map(message => normalizeExtractedMessage(message, args));
7
+ }
8
+ export async function readMessages(page, args = {}) {
9
+ if (typeof page.evaluate === "function") {
10
+ const messages = await page.evaluate(() => {
11
+ const nodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
12
+ return nodes
13
+ .map(node => {
14
+ const role = node.getAttribute("data-message-author-role");
15
+ if (role !== "user" && role !== "assistant") {
16
+ return undefined;
17
+ }
18
+ return {
19
+ role,
20
+ html: node.innerHTML,
21
+ metadataHtml: node.closest("[data-testid^='conversation-turn']")?.outerHTML ?? node.outerHTML
22
+ };
23
+ })
24
+ .filter(Boolean);
25
+ });
26
+ return messages
27
+ .filter(message => args.role === undefined || message.role === args.role)
28
+ .map(message => normalizeExtractedMessage(message, args));
29
+ }
30
+ if (typeof page.content === "function") {
31
+ const html = await page.content();
32
+ return extractMessagesFromHtml(html, args);
33
+ }
34
+ return [];
35
+ }
36
+ export async function readLatestMessage(page, role = "assistant", format = "markdown", maxChars) {
37
+ if (typeof page.evaluate === "function") {
38
+ const message = await page.evaluate((wantedRole) => {
39
+ const nodes = Array.from(document.querySelectorAll(`[data-message-author-role="${wantedRole}"]`));
40
+ const node = nodes.at(-1);
41
+ if (node === undefined)
42
+ return undefined;
43
+ return {
44
+ role: wantedRole,
45
+ html: node.innerHTML,
46
+ metadataHtml: node.closest("[data-testid^='conversation-turn']")?.outerHTML ?? node.outerHTML
47
+ };
48
+ }, role).catch(() => undefined);
49
+ if (message !== undefined) {
50
+ const args = { role, format };
51
+ if (maxChars !== undefined)
52
+ args.maxChars = maxChars;
53
+ return normalizeExtractedMessage(message, args);
54
+ }
55
+ return undefined;
56
+ }
57
+ const args = { role, format };
58
+ if (maxChars !== undefined)
59
+ args.maxChars = maxChars;
60
+ const messages = await readMessages(page, args);
61
+ return messages.at(-1);
62
+ }
63
+ export async function readLatestMessageText(page, role = "assistant") {
64
+ if (typeof page.evaluate === "function") {
65
+ return page.evaluate((wantedRole) => {
66
+ const nodes = Array.from(document.querySelectorAll(`[data-message-author-role="${wantedRole}"]`));
67
+ const node = nodes.at(-1);
68
+ return node?.innerText ?? node?.textContent ?? undefined;
69
+ }, role).catch(() => undefined);
70
+ }
71
+ return readLatestMessage(page, role, "normalized_text")
72
+ .then(message => message?.text)
73
+ .catch(() => undefined);
74
+ }
75
+ export async function readLatestMessageTextSnapshot(page, role) {
76
+ if (typeof page.evaluate === "function") {
77
+ return page.evaluate((wantedRole) => {
78
+ const allNodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
79
+ const roleNodes = allNodes.filter(node => node.getAttribute("data-message-author-role") === wantedRole);
80
+ const latest = roleNodes.at(-1);
81
+ const latestText = latest?.innerText ?? latest?.textContent ?? undefined;
82
+ const snapshot = { turnCount: allNodes.length };
83
+ if (latestText !== undefined)
84
+ snapshot.latestText = latestText;
85
+ return snapshot;
86
+ }, role);
87
+ }
88
+ const messages = await readMessages(page, { role, format: "normalized_text" });
89
+ const allMessages = await readMessages(page, { format: "normalized_text" });
90
+ const snapshot = { turnCount: allMessages.length };
91
+ const latestText = messages.at(-1)?.text;
92
+ if (latestText !== undefined)
93
+ snapshot.latestText = latestText;
94
+ return snapshot;
95
+ }
96
+ export function isTransientAssistantText(text) {
97
+ const normalized = normalizeWhitespace(text)
98
+ .replace(/[.。…]+$/g, "")
99
+ .trim()
100
+ .toLowerCase();
101
+ return normalized === "thinking"
102
+ || normalized === "reasoning"
103
+ || normalized === "searching"
104
+ || normalized === "searching the web"
105
+ || /^analyzing (?:the )?images?$/.test(normalized)
106
+ || /^processing (?:the )?images?$/.test(normalized)
107
+ || /^reading (?:the )?images?$/.test(normalized);
108
+ }
109
+ export function countMessages(messages, role) {
110
+ return role === undefined ? messages.length : messages.filter(message => message.role === role).length;
111
+ }
112
+ export async function countPageMessages(page, role) {
113
+ if (typeof page.evaluate === "function") {
114
+ return page.evaluate((wantedRole) => {
115
+ const selector = wantedRole === undefined
116
+ ? "[data-message-author-role]"
117
+ : `[data-message-author-role="${wantedRole}"]`;
118
+ return document.querySelectorAll(selector).length;
119
+ }, role);
120
+ }
121
+ return countMessages(await readMessages(page), role);
122
+ }
123
+ function normalizeExtractedMessage(message, args = {}) {
124
+ const metadataHtml = message.role === "assistant" ? message.metadataHtml : undefined;
125
+ const content = formatMessageHtml(message.html, normalizeResponseFormat(args.format), args.maxChars, metadataHtml);
126
+ return { role: message.role, ...content };
127
+ }
@@ -0,0 +1,20 @@
1
+ import type { LocatorLike, PageLike } from "../types.js";
2
+ export declare const cssSelectors: {
3
+ readonly assistantMessages: "[data-message-author-role='assistant']";
4
+ readonly userMessages: "[data-message-author-role='user']";
5
+ readonly roleMessages: "[data-message-author-role]";
6
+ readonly conversationTurns: "[data-testid^='conversation-turn']";
7
+ readonly hiddenFileInputs: "input[type='file']";
8
+ readonly downloadControls: string;
9
+ };
10
+ export declare function composerTextbox(page: PageLike): LocatorLike;
11
+ export declare function sendButton(page: PageLike): LocatorLike;
12
+ export declare function searchChatsButton(page: PageLike): LocatorLike;
13
+ export declare function searchChatsInput(page: PageLike): LocatorLike;
14
+ export declare function newChatButton(page: PageLike): LocatorLike;
15
+ export declare function addFilesButton(page: PageLike): LocatorLike;
16
+ export declare function copyResponseButtons(page: PageLike): LocatorLike;
17
+ export declare function assistantMessageNodes(page: PageLike): LocatorLike;
18
+ export declare function userMessageNodes(page: PageLike): LocatorLike;
19
+ export declare function roleMessageNodes(page: PageLike): LocatorLike;
20
+ export declare function requiredLocator(page: PageLike, selector: string): LocatorLike;
@@ -0,0 +1,72 @@
1
+ export const cssSelectors = {
2
+ assistantMessages: "[data-message-author-role='assistant']",
3
+ userMessages: "[data-message-author-role='user']",
4
+ roleMessages: "[data-message-author-role]",
5
+ conversationTurns: "[data-testid^='conversation-turn']",
6
+ hiddenFileInputs: "input[type='file']",
7
+ downloadControls: [
8
+ "main [data-message-author-role='assistant'] a[download]",
9
+ "main [data-message-author-role='assistant'] a[href*='/backend-api/files/']",
10
+ "main [data-message-author-role='assistant'] button[aria-label*='Download']",
11
+ "main [data-message-author-role='assistant'] a[aria-label*='Download']",
12
+ "main a[download]",
13
+ "main a[href*='/backend-api/files/']"
14
+ ].join(", ")
15
+ };
16
+ export function composerTextbox(page) {
17
+ if (typeof page.getByRole !== "function") {
18
+ return requiredLocator(page, "[contenteditable='true'], textarea");
19
+ }
20
+ return page.getByRole("textbox", { name: "Chat with ChatGPT" });
21
+ }
22
+ export function sendButton(page) {
23
+ if (typeof page.getByRole !== "function") {
24
+ return requiredLocator(page, "button[aria-label*='Send']");
25
+ }
26
+ return page.getByRole("button", { name: "Send prompt" });
27
+ }
28
+ export function searchChatsButton(page) {
29
+ if (typeof page.getByRole !== "function") {
30
+ return requiredLocator(page, "button");
31
+ }
32
+ return page.getByRole("button", { name: "Search chats" });
33
+ }
34
+ export function searchChatsInput(page) {
35
+ if (typeof page.getByPlaceholder === "function") {
36
+ return page.getByPlaceholder("Search chats...");
37
+ }
38
+ return requiredLocator(page, "input[placeholder*='Search chats']");
39
+ }
40
+ export function newChatButton(page) {
41
+ if (typeof page.getByRole !== "function") {
42
+ return requiredLocator(page, "a[href='/'], button");
43
+ }
44
+ return page.getByRole("button", { name: "New chat" });
45
+ }
46
+ export function addFilesButton(page) {
47
+ if (typeof page.getByRole !== "function") {
48
+ return requiredLocator(page, "button[aria-label*='Add']");
49
+ }
50
+ return page.getByRole("button", { name: "Add files and more" });
51
+ }
52
+ export function copyResponseButtons(page) {
53
+ if (typeof page.getByRole !== "function") {
54
+ return requiredLocator(page, "button[aria-label*='Copy response']");
55
+ }
56
+ return page.getByRole("button", { name: "Copy response" });
57
+ }
58
+ export function assistantMessageNodes(page) {
59
+ return requiredLocator(page, cssSelectors.assistantMessages);
60
+ }
61
+ export function userMessageNodes(page) {
62
+ return requiredLocator(page, cssSelectors.userMessages);
63
+ }
64
+ export function roleMessageNodes(page) {
65
+ return requiredLocator(page, cssSelectors.roleMessages);
66
+ }
67
+ export function requiredLocator(page, selector) {
68
+ if (typeof page.locator !== "function") {
69
+ throw new Error(`Page does not support locator("${selector}")`);
70
+ }
71
+ return page.locator(selector);
72
+ }
@@ -0,0 +1,5 @@
1
+ export declare function normalizeWhitespace(text: string): string;
2
+ export declare function normalizeLineBreaks(text: string): string;
3
+ export declare function decodeBasicEntities(text: string): string;
4
+ export declare function stripTags(html: string): string;
5
+ export declare function normalizeLabel(text: string): string;
@@ -0,0 +1,27 @@
1
+ export function normalizeWhitespace(text) {
2
+ return text.replace(/\s+/g, " ").trim();
3
+ }
4
+ export function normalizeLineBreaks(text) {
5
+ return text.replace(/\r\n?/g, "\n");
6
+ }
7
+ export function decodeBasicEntities(text) {
8
+ return text
9
+ .replace(/ /g, " ")
10
+ .replace(/&/g, "&")
11
+ .replace(/&lt;/g, "<")
12
+ .replace(/&gt;/g, ">")
13
+ .replace(/&quot;/g, "\"")
14
+ .replace(/&#39;/g, "'");
15
+ }
16
+ export function stripTags(html) {
17
+ return normalizeWhitespace(decodeBasicEntities(html
18
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
19
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
20
+ .replace(/<button[\s\S]*?<\/button>/gi, " ")
21
+ .replace(/<nav[\s\S]*?<\/nav>/gi, " ")
22
+ .replace(/<svg[\s\S]*?<\/svg>/gi, " ")
23
+ .replace(/<[^>]+>/g, " ")));
24
+ }
25
+ export function normalizeLabel(text) {
26
+ return normalizeWhitespace(text).toLowerCase();
27
+ }
@@ -0,0 +1,32 @@
1
+ import type { BlockerKind, CommandContext, CommandResult } from "./types.js";
2
+ type BlockerDetails = Partial<Omit<NonNullable<CommandResult["blocker"]>, "kind" | "message" | "visibleText">>;
3
+ export declare const BROWSER_BRIDGE_UNAVAILABLE_MESSAGE = "Codex cannot access the ChatGPT browser bridge from this backend process. In an ordinary shell this is expected; for a live Codex Chrome run, bootstrap the Chrome plugin runtime with setupBrowserRuntime({ globals: globalThis }) before using globalThis.agent.";
4
+ export declare const BROWSER_BRIDGE_REMEDIATION: NonNullable<NonNullable<CommandResult["blocker"]>["remediation"]>;
5
+ export declare class ChatGPTControlError extends Error {
6
+ readonly kind: BlockerKind;
7
+ readonly recoverable: boolean;
8
+ readonly visibleText?: string | undefined;
9
+ readonly blockerDetails: BlockerDetails;
10
+ constructor(message: string, kind: BlockerKind, recoverable: boolean, visibleText?: string | undefined, blockerDetails?: BlockerDetails);
11
+ }
12
+ export declare class BrowserBridgeUnavailableError extends ChatGPTControlError {
13
+ constructor(message?: string);
14
+ }
15
+ export declare class LoginRequiredError extends ChatGPTControlError {
16
+ constructor(visibleText?: string);
17
+ }
18
+ export declare class SelectorDriftError extends ChatGPTControlError {
19
+ constructor(message: string, visibleText?: string);
20
+ }
21
+ export declare class ConfirmationRequiredError extends ChatGPTControlError {
22
+ constructor(message: string, visibleText?: string);
23
+ }
24
+ export declare class TimeoutPartialError extends ChatGPTControlError {
25
+ constructor(message: string, visibleText?: string);
26
+ }
27
+ export declare function contextNow(partial?: Partial<CommandContext>): CommandContext;
28
+ export declare function resultOk<T>(data: T, context?: Partial<CommandContext>, warnings?: string[]): CommandResult<T>;
29
+ export declare function resultBlocked(kind: BlockerKind, message: string, visibleText?: string, context?: Partial<CommandContext>): CommandResult<never>;
30
+ export declare function resultError(error: Error, context?: Partial<CommandContext>, recoverable?: boolean): CommandResult<never>;
31
+ export declare function toCommandResult(error: unknown, context?: Partial<CommandContext>): CommandResult<never>;
32
+ export {};
@@ -0,0 +1,127 @@
1
+ export const BROWSER_BRIDGE_UNAVAILABLE_MESSAGE = "Codex cannot access the ChatGPT browser bridge from this backend process. In an ordinary shell this is expected; for a live Codex Chrome run, bootstrap the Chrome plugin runtime with setupBrowserRuntime({ globals: globalThis }) before using globalThis.agent.";
2
+ export const BROWSER_BRIDGE_REMEDIATION = [
3
+ {
4
+ label: "Ordinary shell",
5
+ instruction: "Treat browser_bridge_unavailable from a plain shell as an expected protocol/blocker-path result, not proof that Chrome, ChatGPT, or the Codex extension is broken.",
6
+ userActionRequired: false
7
+ },
8
+ {
9
+ label: "Codex Chrome bootstrap",
10
+ instruction: "For a live run, initialize the Chrome plugin runtime in node_repl with setupBrowserRuntime({ globals: globalThis }), then set globalThis.browser = await agent.browsers.get(\"extension\") before calling createChatGPT({ agent: globalThis.agent }).",
11
+ userActionRequired: false
12
+ },
13
+ {
14
+ label: "Python live bridge",
15
+ instruction: "For Python browser-bridge smokes, keep the bridge-hosted Node backend JS execution alive and run scripts/http_stdio_relay.mjs with CHATGPT_BROWSER_BACKEND_HTTP_URL; a plain Python-spawned Node subprocess cannot inherit globalThis.agent.",
16
+ userActionRequired: false
17
+ },
18
+ {
19
+ label: "Extension availability",
20
+ instruction: "If this command was already running inside a bootstrapped bridge host, verify the Codex Chrome extension is installed and enabled, then restart Chrome or Codex before retrying.",
21
+ userActionRequired: true
22
+ }
23
+ ];
24
+ export class ChatGPTControlError extends Error {
25
+ kind;
26
+ recoverable;
27
+ visibleText;
28
+ blockerDetails;
29
+ constructor(message, kind, recoverable, visibleText, blockerDetails = {}) {
30
+ super(message);
31
+ this.kind = kind;
32
+ this.recoverable = recoverable;
33
+ this.visibleText = visibleText;
34
+ this.blockerDetails = blockerDetails;
35
+ this.name = new.target.name;
36
+ }
37
+ }
38
+ export class BrowserBridgeUnavailableError extends ChatGPTControlError {
39
+ constructor(message = BROWSER_BRIDGE_UNAVAILABLE_MESSAGE) {
40
+ super(message, "browser_bridge_unavailable", true, undefined, {
41
+ code: "codex_chrome_bridge_unavailable",
42
+ remediation: BROWSER_BRIDGE_REMEDIATION
43
+ });
44
+ }
45
+ }
46
+ export class LoginRequiredError extends ChatGPTControlError {
47
+ constructor(visibleText) {
48
+ super("ChatGPT login is required before this command can continue.", "login_required", true, visibleText);
49
+ }
50
+ }
51
+ export class SelectorDriftError extends ChatGPTControlError {
52
+ constructor(message, visibleText) {
53
+ super(message, "selector_drift", true, visibleText);
54
+ }
55
+ }
56
+ export class ConfirmationRequiredError extends ChatGPTControlError {
57
+ constructor(message, visibleText) {
58
+ super(message, "confirmation", true, visibleText);
59
+ }
60
+ }
61
+ export class TimeoutPartialError extends ChatGPTControlError {
62
+ constructor(message, visibleText) {
63
+ super(message, "unknown", true, visibleText);
64
+ }
65
+ }
66
+ export function contextNow(partial = {}) {
67
+ return {
68
+ timestamp: new Date().toISOString(),
69
+ ...partial
70
+ };
71
+ }
72
+ export function resultOk(data, context = {}, warnings = []) {
73
+ return {
74
+ ok: true,
75
+ status: "ok",
76
+ data,
77
+ warnings,
78
+ context: contextNow(context)
79
+ };
80
+ }
81
+ export function resultBlocked(kind, message, visibleText, context = {}) {
82
+ const blocker = visibleText === undefined ? { kind, message } : { kind, message, visibleText };
83
+ return {
84
+ ok: false,
85
+ status: "blocked",
86
+ warnings: [],
87
+ blocker,
88
+ context: contextNow(context)
89
+ };
90
+ }
91
+ export function resultError(error, context = {}, recoverable = error instanceof ChatGPTControlError ? error.recoverable : false) {
92
+ const blocker = error instanceof ChatGPTControlError
93
+ ? error.visibleText === undefined
94
+ ? {
95
+ kind: error.kind,
96
+ message: error.message,
97
+ ...error.blockerDetails
98
+ }
99
+ : {
100
+ kind: error.kind,
101
+ message: error.message,
102
+ visibleText: error.visibleText,
103
+ ...error.blockerDetails
104
+ }
105
+ : undefined;
106
+ const result = {
107
+ ok: false,
108
+ status: blocker ? "blocked" : "error",
109
+ warnings: [],
110
+ error: {
111
+ name: error.name,
112
+ message: error.message,
113
+ recoverable
114
+ },
115
+ context: contextNow(context)
116
+ };
117
+ if (blocker !== undefined) {
118
+ result.blocker = blocker;
119
+ }
120
+ return result;
121
+ }
122
+ export function toCommandResult(error, context = {}) {
123
+ if (error instanceof Error) {
124
+ return resultError(error, context);
125
+ }
126
+ return resultError(new Error(String(error)), context);
127
+ }
@@ -0,0 +1,34 @@
1
+ export * from "./types.js";
2
+ export * from "./errors.js";
3
+ export * from "./logger.js";
4
+ export * from "./browser/attach.js";
5
+ export * from "./browser/clipboard.js";
6
+ export * from "./browser/page-state.js";
7
+ export * from "./dom/messages.js";
8
+ export * from "./dom/message-format.js";
9
+ export * from "./dom/selectors.js";
10
+ export * from "./dom/visible-text.js";
11
+ export * from "./dom/fixtures.js";
12
+ export * from "./safety/blockers.js";
13
+ export * from "./safety/redaction.js";
14
+ export * from "./safety/report-redaction.js";
15
+ export * from "./commands/session.js";
16
+ export * from "./commands/threads.js";
17
+ export * from "./commands/messages.js";
18
+ export * from "./commands/sequence.js";
19
+ export * from "./commands/files.js";
20
+ export * from "./commands/modes.js";
21
+ export * from "./commands/response-actions.js";
22
+ export * from "./commands/confirmations.js";
23
+ export * from "./commands/helpers.js";
24
+ export * from "./commands/doctor.js";
25
+ export * from "./commands/reports.js";
26
+ export * from "./commands/registry.js";
27
+ export * from "./client.js";
28
+ export * from "./browser/downloads.js";
29
+ export * from "./dom/menus.js";
30
+ export * from "./safety/risk.js";
31
+ export * from "./runner/index.js";
32
+ export * from "./backend/client.js";
33
+ export * from "./backend/protocol.js";
34
+ export * from "./backend/session.js";
@@ -0,0 +1,34 @@
1
+ export * from "./types.js";
2
+ export * from "./errors.js";
3
+ export * from "./logger.js";
4
+ export * from "./browser/attach.js";
5
+ export * from "./browser/clipboard.js";
6
+ export * from "./browser/page-state.js";
7
+ export * from "./dom/messages.js";
8
+ export * from "./dom/message-format.js";
9
+ export * from "./dom/selectors.js";
10
+ export * from "./dom/visible-text.js";
11
+ export * from "./dom/fixtures.js";
12
+ export * from "./safety/blockers.js";
13
+ export * from "./safety/redaction.js";
14
+ export * from "./safety/report-redaction.js";
15
+ export * from "./commands/session.js";
16
+ export * from "./commands/threads.js";
17
+ export * from "./commands/messages.js";
18
+ export * from "./commands/sequence.js";
19
+ export * from "./commands/files.js";
20
+ export * from "./commands/modes.js";
21
+ export * from "./commands/response-actions.js";
22
+ export * from "./commands/confirmations.js";
23
+ export * from "./commands/helpers.js";
24
+ export * from "./commands/doctor.js";
25
+ export * from "./commands/reports.js";
26
+ export * from "./commands/registry.js";
27
+ export * from "./client.js";
28
+ export * from "./browser/downloads.js";
29
+ export * from "./dom/menus.js";
30
+ export * from "./safety/risk.js";
31
+ export * from "./runner/index.js";
32
+ export * from "./backend/client.js";
33
+ export * from "./backend/protocol.js";
34
+ export * from "./backend/session.js";
@@ -0,0 +1,15 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export type LogEvent = {
3
+ level: LogLevel;
4
+ event: string;
5
+ message: string;
6
+ timestamp: string;
7
+ data?: Record<string, unknown>;
8
+ };
9
+ export type Logger = {
10
+ log(event: LogEvent): void;
11
+ };
12
+ export declare function createMemoryLogger(): Logger & {
13
+ events: LogEvent[];
14
+ };
15
+ export declare function redactLogEvent(event: LogEvent): LogEvent;
@@ -0,0 +1,25 @@
1
+ import { compactVisibleText } from "./safety/redaction.js";
2
+ export function createMemoryLogger() {
3
+ const events = [];
4
+ return {
5
+ events,
6
+ log(event) {
7
+ events.push(redactLogEvent(event));
8
+ }
9
+ };
10
+ }
11
+ export function redactLogEvent(event) {
12
+ const redacted = {
13
+ level: event.level,
14
+ event: event.event,
15
+ message: compactVisibleText(event.message),
16
+ timestamp: event.timestamp
17
+ };
18
+ if (event.data !== undefined) {
19
+ redacted.data = Object.fromEntries(Object.entries(event.data).map(([key, value]) => [
20
+ key,
21
+ typeof value === "string" ? compactVisibleText(value) : value
22
+ ]));
23
+ }
24
+ return redacted;
25
+ }
@@ -0,0 +1,2 @@
1
+ import type { ChatGPTAgent, ChatGPTAgentConfig } from "./types.js";
2
+ export declare function createChatGPTAgent<TOutput = string>(config: ChatGPTAgentConfig<TOutput>): ChatGPTAgent<TOutput>;
@@ -0,0 +1,17 @@
1
+ export function createChatGPTAgent(config) {
2
+ const name = config.name.trim();
3
+ if (name.length === 0) {
4
+ throw new Error("ChatGPT agent name must be a non-empty string.");
5
+ }
6
+ return {
7
+ kind: "chatgpt_browser_agent",
8
+ name,
9
+ ...(config.instructions === undefined ? {} : { instructions: config.instructions }),
10
+ instructionsMode: config.instructionsMode ?? "visible_prefix",
11
+ defaults: { ...(config.defaults ?? {}) },
12
+ tools: [...(config.tools ?? [])],
13
+ guardrails: [...(config.guardrails ?? [])],
14
+ ...(config.output === undefined ? {} : { output: config.output }),
15
+ ...(config.metadata === undefined ? {} : { metadata: { ...config.metadata } })
16
+ };
17
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./agent.js";
2
+ export * from "./interruptions.js";
3
+ export * from "./result.js";
4
+ export * from "./responses.js";
5
+ export * from "./resume.js";
6
+ export * from "./stream.js";
7
+ export * from "./types.js";
@@ -0,0 +1,7 @@
1
+ export * from "./agent.js";
2
+ export * from "./interruptions.js";
3
+ export * from "./result.js";
4
+ export * from "./responses.js";
5
+ export * from "./resume.js";
6
+ export * from "./stream.js";
7
+ export * from "./types.js";
@@ -0,0 +1,3 @@
1
+ import type { CommandResult } from "../types.js";
2
+ import type { ChatGPTInterruption } from "./types.js";
3
+ export declare function interruptionFromCommandResult(result: CommandResult<unknown>, command?: string): ChatGPTInterruption | undefined;
@@ -0,0 +1,65 @@
1
+ import { augmentCommandBlocker, resumeDecisionForBlocker } from "./resume.js";
2
+ export function interruptionFromCommandResult(result, command) {
3
+ if (!isInterruptingResult(result)) {
4
+ return undefined;
5
+ }
6
+ const id = `interruption-${Date.now().toString(36)}`;
7
+ const blocker = result.blocker === undefined ? undefined : augmentCommandBlocker(result.blocker);
8
+ const remediation = blocker?.remediation ?? [];
9
+ const interruption = {
10
+ id,
11
+ type: interruptionType(result, blocker),
12
+ status: result.status,
13
+ message: blocker?.message ?? result.error?.message ?? result.status,
14
+ resume: resumeDecisionForBlocker(blocker, id)
15
+ };
16
+ if (blocker !== undefined) {
17
+ interruption.blocker = blocker;
18
+ if (blocker.fieldPath !== undefined)
19
+ interruption.fieldPath = blocker.fieldPath;
20
+ }
21
+ if (command !== undefined)
22
+ interruption.command = command;
23
+ if (remediation.length > 0) {
24
+ interruption.fix = {
25
+ summary: "Resolve the reported blocker before resuming.",
26
+ steps: remediation.map(step => step.instruction)
27
+ };
28
+ }
29
+ return interruption;
30
+ }
31
+ function isInterruptingResult(result) {
32
+ return result.blocker !== undefined
33
+ || result.status === "needs_confirmation"
34
+ || result.status === "unsupported"
35
+ || result.status === "timeout";
36
+ }
37
+ function interruptionType(result, blocker) {
38
+ switch (blocker?.kind) {
39
+ case "confirmation":
40
+ return "approval_required";
41
+ case "permission":
42
+ case "upload_failed":
43
+ case "download_unavailable":
44
+ return "permission_required";
45
+ case "login_required":
46
+ return "login_required";
47
+ case "captcha":
48
+ return "captcha";
49
+ case "rate_limit":
50
+ return "rate_limit";
51
+ case "selector_drift":
52
+ return "selector_drift";
53
+ case "browser_bridge_unavailable":
54
+ case "not_found":
55
+ case "modal":
56
+ case "unknown":
57
+ case undefined:
58
+ break;
59
+ }
60
+ if (result.status === "needs_confirmation")
61
+ return "approval_required";
62
+ if (result.status === "timeout")
63
+ return "timeout";
64
+ return "unsupported";
65
+ }