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.
Files changed (233) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +42 -2
  3. package/contracts/v1/fixtures/backend-capabilities.json +11 -0
  4. package/contracts/v1/fixtures/command-descriptors.json +289 -7
  5. package/contracts/v1/fixtures/describe-runner-run.json +5 -2
  6. package/contracts/v1/fixtures/doctor-scenario-preflight.json +45 -2
  7. package/contracts/v1/fixtures/help-root.json +1 -1
  8. package/contracts/v1/fixtures/output-json-parse-success.json +9 -0
  9. package/contracts/v1/fixtures/reports-create-redacted.json +1 -1
  10. package/contracts/v1/fixtures/run-timeout-partial.json +15 -4
  11. package/contracts/v1/fixtures/stream-in-progress.ndjson +3 -0
  12. package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
  13. package/contracts/v1/fixtures/surface-chat-legacy.json +35 -0
  14. package/contracts/v1/fixtures/surface-chat-simplified.json +37 -0
  15. package/contracts/v1/fixtures/surface-sidebar-false-positive.json +29 -0
  16. package/contracts/v1/fixtures/surface-work-advanced.json +45 -0
  17. package/contracts/v1/fixtures/surface-work-basic.json +38 -0
  18. package/contracts/v1/fixtures/workflow-ask-success.json +8 -0
  19. package/contracts/v1/manifest.json +32 -1
  20. package/contracts/v1/parity-suite.json +121 -1
  21. package/contracts/v1/schemas/backend-request.schema.json +15 -0
  22. package/contracts/v1/schemas/manifest.schema.json +6 -3
  23. package/contracts/v1/schemas/surface-profile.schema.json +166 -0
  24. package/dist/codex-chatgpt-control-backend.mjs +3198 -340
  25. package/dist/codex-chatgpt-control.bundle.mjs +4454 -1555
  26. package/dist/src/backend/client.d.ts +26 -1
  27. package/dist/src/backend/client.js +23 -1
  28. package/dist/src/backend/protocol.d.ts +1 -1
  29. package/dist/src/backend/protocol.js +11 -0
  30. package/dist/src/backend/session.js +22 -0
  31. package/dist/src/browser/attach.d.ts +1 -0
  32. package/dist/src/browser/attach.js +3 -3
  33. package/dist/src/browser/clipboard.d.ts +11 -0
  34. package/dist/src/browser/clipboard.js +34 -7
  35. package/dist/src/browser/page-state.js +17 -4
  36. package/dist/src/client.d.ts +32 -1
  37. package/dist/src/client.js +72 -16
  38. package/dist/src/commands/artifacts.js +1 -7
  39. package/dist/src/commands/configuration.d.ts +15 -0
  40. package/dist/src/commands/configuration.js +674 -0
  41. package/dist/src/commands/context.js +3 -1
  42. package/dist/src/commands/conversation.d.ts +15 -0
  43. package/dist/src/commands/conversation.js +44 -0
  44. package/dist/src/commands/deadline.d.ts +8 -0
  45. package/dist/src/commands/deadline.js +14 -0
  46. package/dist/src/commands/doctor.js +47 -3
  47. package/dist/src/commands/experience.d.ts +12 -0
  48. package/dist/src/commands/experience.js +288 -0
  49. package/dist/src/commands/files.js +61 -13
  50. package/dist/src/commands/messages.d.ts +2 -1
  51. package/dist/src/commands/messages.js +349 -87
  52. package/dist/src/commands/modes.d.ts +4 -1
  53. package/dist/src/commands/modes.js +243 -44
  54. package/dist/src/commands/probes.d.ts +15 -0
  55. package/dist/src/commands/probes.js +64 -0
  56. package/dist/src/commands/registry.js +102 -8
  57. package/dist/src/commands/reports.js +1 -1
  58. package/dist/src/commands/response-actions.js +1 -7
  59. package/dist/src/commands/sequence.js +24 -1
  60. package/dist/src/commands/session.d.ts +2 -0
  61. package/dist/src/commands/session.js +50 -1
  62. package/dist/src/commands/threads.js +3 -31
  63. package/dist/src/commands/work.d.ts +6 -0
  64. package/dist/src/commands/work.js +338 -0
  65. package/dist/src/dom/generation-state.d.ts +7 -0
  66. package/dist/src/dom/generation-state.js +7 -1
  67. package/dist/src/dom/label-match.d.ts +3 -0
  68. package/dist/src/dom/label-match.js +25 -0
  69. package/dist/src/dom/locale/am.d.ts +8 -1
  70. package/dist/src/dom/locale/am.js +8 -1
  71. package/dist/src/dom/locale/ar.d.ts +9 -1
  72. package/dist/src/dom/locale/ar.js +9 -1
  73. package/dist/src/dom/locale/bg.d.ts +9 -1
  74. package/dist/src/dom/locale/bg.js +9 -1
  75. package/dist/src/dom/locale/bn.d.ts +9 -1
  76. package/dist/src/dom/locale/bn.js +9 -1
  77. package/dist/src/dom/locale/bs.d.ts +8 -1
  78. package/dist/src/dom/locale/bs.js +8 -1
  79. package/dist/src/dom/locale/ca.d.ts +8 -1
  80. package/dist/src/dom/locale/ca.js +8 -1
  81. package/dist/src/dom/locale/cs.d.ts +8 -1
  82. package/dist/src/dom/locale/cs.js +8 -1
  83. package/dist/src/dom/locale/da.d.ts +7 -1
  84. package/dist/src/dom/locale/da.js +7 -1
  85. package/dist/src/dom/locale/de.d.ts +10 -3
  86. package/dist/src/dom/locale/de.js +10 -3
  87. package/dist/src/dom/locale/el.d.ts +8 -1
  88. package/dist/src/dom/locale/el.js +8 -1
  89. package/dist/src/dom/locale/en.d.ts +40 -1
  90. package/dist/src/dom/locale/en.js +42 -1
  91. package/dist/src/dom/locale/es-419.d.ts +8 -1
  92. package/dist/src/dom/locale/es-419.js +8 -1
  93. package/dist/src/dom/locale/es-ES.d.ts +8 -1
  94. package/dist/src/dom/locale/es-ES.js +8 -1
  95. package/dist/src/dom/locale/et.d.ts +8 -1
  96. package/dist/src/dom/locale/et.js +8 -1
  97. package/dist/src/dom/locale/fa.d.ts +9 -1
  98. package/dist/src/dom/locale/fa.js +9 -1
  99. package/dist/src/dom/locale/fi.d.ts +8 -1
  100. package/dist/src/dom/locale/fi.js +8 -1
  101. package/dist/src/dom/locale/fr-CA.d.ts +8 -1
  102. package/dist/src/dom/locale/fr-CA.js +8 -1
  103. package/dist/src/dom/locale/fr-FR.d.ts +8 -3
  104. package/dist/src/dom/locale/fr-FR.js +8 -3
  105. package/dist/src/dom/locale/gu.d.ts +8 -1
  106. package/dist/src/dom/locale/gu.js +8 -1
  107. package/dist/src/dom/locale/hi.d.ts +8 -1
  108. package/dist/src/dom/locale/hi.js +8 -1
  109. package/dist/src/dom/locale/hr.d.ts +7 -1
  110. package/dist/src/dom/locale/hr.js +7 -1
  111. package/dist/src/dom/locale/hu.d.ts +8 -1
  112. package/dist/src/dom/locale/hu.js +8 -1
  113. package/dist/src/dom/locale/hy.d.ts +9 -1
  114. package/dist/src/dom/locale/hy.js +9 -1
  115. package/dist/src/dom/locale/id.d.ts +8 -1
  116. package/dist/src/dom/locale/id.js +8 -1
  117. package/dist/src/dom/locale/index.d.ts +20 -1
  118. package/dist/src/dom/locale/index.js +102 -0
  119. package/dist/src/dom/locale/is.d.ts +8 -1
  120. package/dist/src/dom/locale/is.js +8 -1
  121. package/dist/src/dom/locale/it.d.ts +8 -1
  122. package/dist/src/dom/locale/it.js +8 -1
  123. package/dist/src/dom/locale/ja.d.ts +8 -1
  124. package/dist/src/dom/locale/ja.js +8 -1
  125. package/dist/src/dom/locale/ka.d.ts +8 -1
  126. package/dist/src/dom/locale/ka.js +8 -1
  127. package/dist/src/dom/locale/kk.d.ts +8 -1
  128. package/dist/src/dom/locale/kk.js +8 -1
  129. package/dist/src/dom/locale/kn.d.ts +9 -1
  130. package/dist/src/dom/locale/kn.js +9 -1
  131. package/dist/src/dom/locale/ko.d.ts +8 -1
  132. package/dist/src/dom/locale/ko.js +8 -1
  133. package/dist/src/dom/locale/lt.d.ts +9 -1
  134. package/dist/src/dom/locale/lt.js +9 -1
  135. package/dist/src/dom/locale/lv.d.ts +8 -1
  136. package/dist/src/dom/locale/lv.js +8 -1
  137. package/dist/src/dom/locale/mk.d.ts +7 -1
  138. package/dist/src/dom/locale/mk.js +7 -1
  139. package/dist/src/dom/locale/ml.d.ts +9 -1
  140. package/dist/src/dom/locale/ml.js +9 -1
  141. package/dist/src/dom/locale/mn.d.ts +9 -1
  142. package/dist/src/dom/locale/mn.js +9 -1
  143. package/dist/src/dom/locale/mr.d.ts +9 -1
  144. package/dist/src/dom/locale/mr.js +9 -1
  145. package/dist/src/dom/locale/ms.d.ts +8 -1
  146. package/dist/src/dom/locale/ms.js +8 -1
  147. package/dist/src/dom/locale/my.d.ts +8 -1
  148. package/dist/src/dom/locale/my.js +8 -1
  149. package/dist/src/dom/locale/nb.d.ts +8 -1
  150. package/dist/src/dom/locale/nb.js +8 -1
  151. package/dist/src/dom/locale/nl.d.ts +8 -1
  152. package/dist/src/dom/locale/nl.js +8 -1
  153. package/dist/src/dom/locale/pa.d.ts +9 -1
  154. package/dist/src/dom/locale/pa.js +9 -1
  155. package/dist/src/dom/locale/pl.d.ts +8 -1
  156. package/dist/src/dom/locale/pl.js +8 -1
  157. package/dist/src/dom/locale/pt-BR.d.ts +8 -1
  158. package/dist/src/dom/locale/pt-BR.js +8 -1
  159. package/dist/src/dom/locale/pt-PT.d.ts +8 -1
  160. package/dist/src/dom/locale/pt-PT.js +8 -1
  161. package/dist/src/dom/locale/ro.d.ts +7 -1
  162. package/dist/src/dom/locale/ro.js +7 -1
  163. package/dist/src/dom/locale/ru.d.ts +5 -1
  164. package/dist/src/dom/locale/ru.js +5 -1
  165. package/dist/src/dom/locale/sk.d.ts +8 -1
  166. package/dist/src/dom/locale/sk.js +8 -1
  167. package/dist/src/dom/locale/sl.d.ts +8 -1
  168. package/dist/src/dom/locale/sl.js +8 -1
  169. package/dist/src/dom/locale/so.d.ts +8 -1
  170. package/dist/src/dom/locale/so.js +8 -1
  171. package/dist/src/dom/locale/sq.d.ts +8 -1
  172. package/dist/src/dom/locale/sq.js +8 -1
  173. package/dist/src/dom/locale/sr.d.ts +5 -1
  174. package/dist/src/dom/locale/sr.js +5 -1
  175. package/dist/src/dom/locale/sv.d.ts +8 -1
  176. package/dist/src/dom/locale/sv.js +8 -1
  177. package/dist/src/dom/locale/sw.d.ts +8 -1
  178. package/dist/src/dom/locale/sw.js +8 -1
  179. package/dist/src/dom/locale/ta.d.ts +9 -1
  180. package/dist/src/dom/locale/ta.js +9 -1
  181. package/dist/src/dom/locale/te.d.ts +9 -1
  182. package/dist/src/dom/locale/te.js +9 -1
  183. package/dist/src/dom/locale/th.d.ts +8 -1
  184. package/dist/src/dom/locale/th.js +8 -1
  185. package/dist/src/dom/locale/tl.d.ts +4 -5
  186. package/dist/src/dom/locale/tl.js +4 -5
  187. package/dist/src/dom/locale/tr.d.ts +8 -1
  188. package/dist/src/dom/locale/tr.js +8 -1
  189. package/dist/src/dom/locale/types.d.ts +25 -1
  190. package/dist/src/dom/locale/uk.d.ts +8 -1
  191. package/dist/src/dom/locale/uk.js +8 -1
  192. package/dist/src/dom/locale/ur.d.ts +8 -1
  193. package/dist/src/dom/locale/ur.js +8 -1
  194. package/dist/src/dom/locale/vi.d.ts +8 -1
  195. package/dist/src/dom/locale/vi.js +8 -1
  196. package/dist/src/dom/locale/zh-HK.d.ts +9 -1
  197. package/dist/src/dom/locale/zh-HK.js +10 -2
  198. package/dist/src/dom/locale/zh-Hans.d.ts +9 -1
  199. package/dist/src/dom/locale/zh-Hans.js +9 -1
  200. package/dist/src/dom/locale/zh-TW.d.ts +9 -1
  201. package/dist/src/dom/locale/zh-TW.js +9 -1
  202. package/dist/src/dom/menus.js +13 -2
  203. package/dist/src/dom/selectors.js +6 -1
  204. package/dist/src/dom/wait-snapshot.d.ts +43 -0
  205. package/dist/src/dom/wait-snapshot.js +127 -0
  206. package/dist/src/index.d.ts +3 -0
  207. package/dist/src/index.js +3 -0
  208. package/dist/src/runner/responses.d.ts +3 -1
  209. package/dist/src/runner/responses.js +16 -1
  210. package/dist/src/runner/result.js +129 -4
  211. package/dist/src/runner/stream.d.ts +1 -1
  212. package/dist/src/runner/stream.js +6 -0
  213. package/dist/src/runner/types.d.ts +35 -1
  214. package/dist/src/safety/risk.d.ts +11 -0
  215. package/dist/src/safety/risk.js +11 -0
  216. package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +6 -0
  217. package/dist/src/scripts/apply-intelligence-locale-captures.js +147 -15
  218. package/dist/src/scripts/capture-intelligence-locales.d.ts +3 -0
  219. package/dist/src/scripts/capture-intelligence-locales.js +372 -3
  220. package/dist/src/scripts/capture-surface-profile.d.ts +23 -0
  221. package/dist/src/scripts/capture-surface-profile.js +304 -0
  222. package/dist/src/scripts/live-smoke/scenarios.js +34 -1
  223. package/dist/src/scripts/live-smoke.js +1 -0
  224. package/dist/src/types.d.ts +273 -2
  225. package/package.json +4 -3
  226. package/references/2026-07-16-chat-work-surfaces.md +87 -0
  227. package/references/agents-runner.md +13 -2
  228. package/references/backend-protocol.md +19 -7
  229. package/references/language-coverage.md +40 -3
  230. package/references/localization.md +48 -10
  231. package/references/python-parity.md +16 -4
  232. package/references/responses-adapter.md +12 -1
  233. package/references/troubleshooting.md +49 -6
@@ -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 { ArtifactDownloadArgs, ArtifactWaitArgs, BootstrapArgs, CommandResult, CopyResponseArgs, DownloadLatestArgs, ListArtifactsArgs, NewThreadArgs, OpenThreadArgs, ReadLatestArgs, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, WaitArgs } from "../types.js";
9
+ import type { ArtifactDownloadArgs, ArtifactWaitArgs, ApplyConfigurationData, ApplyConfigurationArgs, BootstrapArgs, ChatGPTExperience, CommandResult, ConfigurationInspectionData, CopyResponseArgs, DetectExperienceArgs, DetectExperienceData, DownloadLatestArgs, GetModeArgs, GetModeData, InspectConfigurationArgs, ListArtifactsArgs, MessageStatusArgs, NewThreadArgs, OpenThreadArgs, OpenExperienceData, ReadLatestArgs, ReadWorkLatestArgs, ReadWorkLatestData, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, StartWorkArgs, StartWorkData, SteerWorkArgs, SteerWorkData, WaitArgs, WorkStatusArgs, WorkStatusData, WorkWaitArgs, WorkWaitData } 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>;
@@ -60,6 +60,29 @@ export type ChatGPTBackendClient = {
60
60
  session: {
61
61
  bootstrap(args?: BootstrapArgs): Promise<CommandResult<unknown>>;
62
62
  };
63
+ experience: {
64
+ detect(args?: DetectExperienceArgs): Promise<CommandResult<DetectExperienceData>>;
65
+ open(args: {
66
+ experience: Exclude<ChatGPTExperience, "unknown">;
67
+ timeoutMs?: number;
68
+ }): Promise<CommandResult<OpenExperienceData>>;
69
+ };
70
+ configuration: {
71
+ inspect(args?: InspectConfigurationArgs): Promise<CommandResult<ConfigurationInspectionData>>;
72
+ apply(args: ApplyConfigurationArgs): Promise<CommandResult<ApplyConfigurationData>>;
73
+ };
74
+ work: {
75
+ start(args: StartWorkArgs): Promise<CommandResult<StartWorkData>>;
76
+ status(args?: WorkStatusArgs): Promise<CommandResult<WorkStatusData>>;
77
+ wait(args?: WorkWaitArgs): Promise<CommandResult<WorkWaitData>>;
78
+ steer(args: SteerWorkArgs): Promise<CommandResult<SteerWorkData>>;
79
+ readLatest(args?: ReadWorkLatestArgs): Promise<CommandResult<ReadWorkLatestData>>;
80
+ artifacts: {
81
+ listLatest(args?: ListArtifactsArgs): Promise<CommandResult<unknown>>;
82
+ wait(args?: ArtifactWaitArgs): Promise<CommandResult<unknown>>;
83
+ downloadLatest(args: ArtifactDownloadArgs): Promise<CommandResult<unknown>>;
84
+ };
85
+ };
63
86
  threads: {
64
87
  "new"(args?: NewThreadArgs): Promise<CommandResult<unknown>>;
65
88
  search(args: SearchThreadsArgs): Promise<CommandResult<unknown>>;
@@ -84,6 +107,7 @@ export type ChatGPTBackendClient = {
84
107
  }): Promise<CommandResult<unknown>>;
85
108
  wait(args?: WaitArgs): Promise<CommandResult<unknown>>;
86
109
  readLatest(args?: ReadLatestArgs): Promise<CommandResult<unknown>>;
110
+ status(args?: MessageStatusArgs): Promise<CommandResult<unknown>>;
87
111
  waitAndRead(args?: WaitArgs & ReadLatestArgs): Promise<CommandResult<unknown>>;
88
112
  };
89
113
  files: {
@@ -95,6 +119,7 @@ export type ChatGPTBackendClient = {
95
119
  };
96
120
  modes: {
97
121
  set(args: SetModeArgs): Promise<CommandResult<unknown>>;
122
+ get(args?: GetModeArgs): Promise<CommandResult<GetModeData>>;
98
123
  };
99
124
  tools: {
100
125
  select(args: SelectToolArgs): Promise<CommandResult<unknown>>;
@@ -63,6 +63,26 @@ export function createChatGPTBackendClient(transport) {
63
63
  session: {
64
64
  bootstrap: args => request("session.bootstrap", args ?? {})
65
65
  },
66
+ experience: {
67
+ detect: args => request("experience.detect", args ?? {}),
68
+ open: args => request("experience.open", args)
69
+ },
70
+ configuration: {
71
+ inspect: args => request("configuration.inspect", args ?? {}),
72
+ apply: args => request("configuration.apply", args)
73
+ },
74
+ work: {
75
+ start: args => request("work.start", args),
76
+ status: args => request("work.status", args ?? {}),
77
+ wait: args => request("work.wait", args ?? {}),
78
+ steer: args => request("work.steer", args),
79
+ readLatest: args => request("work.readLatest", args ?? {}),
80
+ artifacts: {
81
+ listLatest: args => request("artifacts.listLatest", args ?? {}),
82
+ wait: args => request("artifacts.wait", args ?? {}),
83
+ downloadLatest: args => request("artifacts.downloadLatest", args)
84
+ }
85
+ },
66
86
  threads: {
67
87
  new: args => request("threads.new", args ?? {}),
68
88
  search: args => request("threads.search", args),
@@ -74,6 +94,7 @@ export function createChatGPTBackendClient(transport) {
74
94
  ask: args => request("messages.ask", args),
75
95
  wait: args => request("messages.wait", args ?? {}),
76
96
  readLatest: args => request("messages.readLatest", args ?? {}),
97
+ status: args => request("messages.status", args ?? {}),
77
98
  waitAndRead: args => request("messages.waitAndRead", args)
78
99
  },
79
100
  artifacts: {
@@ -86,7 +107,8 @@ export function createChatGPTBackendClient(transport) {
86
107
  downloadLatest: args => request("files.downloadLatest", args)
87
108
  },
88
109
  modes: {
89
- set: args => request("modes.set", args)
110
+ set: args => request("modes.set", args),
111
+ get: args => request("modes.get", args ?? {})
90
112
  },
91
113
  tools: {
92
114
  select: args => request("tools.select", 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", "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"];
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", "experience.detect", "experience.open", "configuration.inspect", "configuration.apply", "work.start", "work.status", "work.wait", "work.steer", "work.readLatest", "threads.new", "threads.search", "threads.open", "messages.compose", "messages.submit", "messages.ask", "messages.wait", "messages.readLatest", "messages.status", "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", "modes.get", "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;
@@ -28,6 +28,15 @@ export const backendCommands = [
28
28
  "describe",
29
29
  "help",
30
30
  "session.bootstrap",
31
+ "experience.detect",
32
+ "experience.open",
33
+ "configuration.inspect",
34
+ "configuration.apply",
35
+ "work.start",
36
+ "work.status",
37
+ "work.wait",
38
+ "work.steer",
39
+ "work.readLatest",
31
40
  "threads.new",
32
41
  "threads.search",
33
42
  "threads.open",
@@ -36,6 +45,7 @@ export const backendCommands = [
36
45
  "messages.ask",
37
46
  "messages.wait",
38
47
  "messages.readLatest",
48
+ "messages.status",
39
49
  "messages.waitAndRead",
40
50
  "artifacts.listLatest",
41
51
  "artifacts.wait",
@@ -47,6 +57,7 @@ export const backendCommands = [
47
57
  "projects.sources.planAdd",
48
58
  "projects.sources.add",
49
59
  "modes.set",
60
+ "modes.get",
50
61
  "tools.select",
51
62
  "response.copy"
52
63
  ];
@@ -123,6 +123,24 @@ async function dispatchBackendCommand(client, request) {
123
123
  return client.reports.summarize(requiredRecord(payload, "result"), optionalRecord(payload, "args"));
124
124
  case "session.bootstrap":
125
125
  return client.session.bootstrap(emptyToUndefined(payload));
126
+ case "experience.detect":
127
+ return client.experience.detect(emptyToUndefined(payload));
128
+ case "experience.open":
129
+ return client.experience.open(payload);
130
+ case "configuration.inspect":
131
+ return client.configuration.inspect(emptyToUndefined(payload));
132
+ case "configuration.apply":
133
+ return client.configuration.apply(payload);
134
+ case "work.start":
135
+ return client.work.start(payload);
136
+ case "work.status":
137
+ return client.work.status(emptyToUndefined(payload));
138
+ case "work.wait":
139
+ return client.work.wait(emptyToUndefined(payload));
140
+ case "work.steer":
141
+ return client.work.steer(payload);
142
+ case "work.readLatest":
143
+ return client.work.readLatest(emptyToUndefined(payload));
126
144
  case "threads.new":
127
145
  return client.threads.new(emptyToUndefined(payload));
128
146
  case "threads.search":
@@ -139,6 +157,8 @@ async function dispatchBackendCommand(client, request) {
139
157
  return client.messages.wait(emptyToUndefined(payload));
140
158
  case "messages.readLatest":
141
159
  return client.messages.readLatest(emptyToUndefined(payload));
160
+ case "messages.status":
161
+ return client.messages.status(emptyToUndefined(payload));
142
162
  case "messages.waitAndRead":
143
163
  return client.messages.waitAndRead(payload);
144
164
  case "artifacts.listLatest":
@@ -161,6 +181,8 @@ async function dispatchBackendCommand(client, request) {
161
181
  return client.projects.sources.add(payload);
162
182
  case "modes.set":
163
183
  return client.modes.set(payload);
184
+ case "modes.get":
185
+ return client.modes.get(emptyToUndefined(payload));
164
186
  case "tools.select":
165
187
  return client.tools.select(payload);
166
188
  case "response.copy":
@@ -6,3 +6,4 @@ export type AttachedBrowser = {
6
6
  tabId?: string;
7
7
  };
8
8
  export declare function attachChatGPTBrowser(env: RuntimeEnv, args?: BootstrapArgs): Promise<AttachedBrowser>;
9
+ export declare function tabIdFromPage(page: PageLike): string | undefined;
@@ -16,7 +16,7 @@ export async function attachChatGPTBrowser(env, args = {}) {
16
16
  page,
17
17
  browserName: browser.name ?? "chrome"
18
18
  };
19
- const tabId = getTabId(page);
19
+ const tabId = tabIdFromPage(page);
20
20
  if (tabId !== undefined) {
21
21
  attached.tabId = tabId;
22
22
  }
@@ -332,7 +332,7 @@ function mismatchReasonForNoMatches(policy, tabs, chatgptTabs) {
332
332
  async function pageMatchesExistingTarget(page, policy) {
333
333
  const url = await Promise.resolve(page.url?.()).catch(() => undefined);
334
334
  const title = await Promise.resolve(page.title?.()).catch(() => undefined);
335
- const tab = { id: getTabId(page) ?? "" };
335
+ const tab = { id: tabIdFromPage(page) ?? "" };
336
336
  if (url !== undefined)
337
337
  tab.url = url;
338
338
  if (title !== undefined)
@@ -557,7 +557,7 @@ function normalizePage(pageOrTab) {
557
557
  }
558
558
  return pageOrTab;
559
559
  }
560
- function getTabId(page) {
560
+ export function tabIdFromPage(page) {
561
561
  const maybe = page;
562
562
  const id = maybe.id ?? maybe.tabId;
563
563
  return typeof id === "string" ? id : undefined;
@@ -1,2 +1,13 @@
1
+ export type ClipboardReadCommand = {
2
+ command: string;
3
+ args: string[];
4
+ };
5
+ /**
6
+ * Ordered clipboard-read command candidates for a platform. The first command that
7
+ * succeeds wins; callers fall back to DOM extraction when none do. Linux ordering
8
+ * prefers Wayland's wl-paste only when a Wayland session is detectable, otherwise the
9
+ * X11 tools go first so plain X sessions do not pay a doomed wl-paste attempt.
10
+ */
11
+ export declare function clipboardReadCommandsForPlatform(platform: NodeJS.Platform, env?: Record<string, string | undefined>): ClipboardReadCommand[];
1
12
  export declare function readSystemClipboard(): Promise<string | undefined>;
2
13
  export declare function waitForClipboardChange(before: string | undefined, timeoutMs: number, pollMs?: number): Promise<string | undefined>;
@@ -1,17 +1,44 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  const execFileAsync = promisify(execFile);
4
- export async function readSystemClipboard() {
5
- if (typeof process === "undefined" || process.platform !== "darwin") {
6
- return undefined;
4
+ /**
5
+ * Ordered clipboard-read command candidates for a platform. The first command that
6
+ * succeeds wins; callers fall back to DOM extraction when none do. Linux ordering
7
+ * prefers Wayland's wl-paste only when a Wayland session is detectable, otherwise the
8
+ * X11 tools go first so plain X sessions do not pay a doomed wl-paste attempt.
9
+ */
10
+ export function clipboardReadCommandsForPlatform(platform, env = {}) {
11
+ if (platform === "darwin") {
12
+ return [{ command: "pbpaste", args: [] }];
13
+ }
14
+ if (platform === "win32") {
15
+ return [{ command: "powershell.exe", args: ["-NoProfile", "-Command", "Get-Clipboard -Raw"] }];
7
16
  }
8
- try {
9
- const { stdout } = await execFileAsync("pbpaste", [], { timeout: 2000, maxBuffer: 10 * 1024 * 1024 });
10
- return stdout;
17
+ if (platform === "linux") {
18
+ const waylandCommand = { command: "wl-paste", args: ["--no-newline"] };
19
+ const x11Commands = [
20
+ { command: "xclip", args: ["-selection", "clipboard", "-o"] },
21
+ { command: "xsel", args: ["--clipboard", "--output"] }
22
+ ];
23
+ const isWayland = typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0;
24
+ return isWayland ? [waylandCommand, ...x11Commands] : [...x11Commands, waylandCommand];
11
25
  }
12
- catch {
26
+ return [];
27
+ }
28
+ export async function readSystemClipboard() {
29
+ if (typeof process === "undefined") {
13
30
  return undefined;
14
31
  }
32
+ for (const { command, args } of clipboardReadCommandsForPlatform(process.platform, process.env)) {
33
+ try {
34
+ const { stdout } = await execFileAsync(command, args, { timeout: 2000, maxBuffer: 10 * 1024 * 1024 });
35
+ return stdout;
36
+ }
37
+ catch {
38
+ // Missing tool or empty clipboard: try the next candidate.
39
+ }
40
+ }
41
+ return undefined;
15
42
  }
16
43
  export async function waitForClipboardChange(before, timeoutMs, pollMs = 150) {
17
44
  const started = Date.now();
@@ -3,8 +3,18 @@ import { compactVisibleText } from "../safety/redaction.js";
3
3
  import { escapeRegExp, localeLabels } from "../dom/locale-labels.js";
4
4
  import { withTimeout } from "../commands/timeouts.js";
5
5
  export function parseConversationId(url) {
6
- const match = /\/c\/([A-Za-z0-9-]+)/.exec(url);
7
- return match?.[1];
6
+ let parsed;
7
+ try {
8
+ parsed = new URL(url, "https://chatgpt.com");
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ const segments = parsed.pathname.split("/").filter(Boolean);
14
+ if (segments[0] !== "c" || segments[1] === undefined || segments[1].length === 0) {
15
+ return undefined;
16
+ }
17
+ return segments[1];
8
18
  }
9
19
  export async function readPageState(page) {
10
20
  const rawUrl = typeof page.url === "function" ? await Promise.resolve(page.url()).catch(() => "") : "";
@@ -12,8 +22,11 @@ export async function readPageState(page) {
12
22
  const rawTitle = typeof page.title === "function" ? await page.title().catch(() => undefined) : undefined;
13
23
  const title = typeof rawTitle === "string" ? rawTitle : undefined;
14
24
  const visibleText = await readVisibleText(page);
15
- const blocker = classifyVisibleText(visibleText);
16
- const signedIn = isLikelySignedIn(visibleText) && blocker?.kind !== "login_required";
25
+ const signedIn = isLikelySignedIn(visibleText);
26
+ const classifiedBlocker = classifyVisibleText(visibleText);
27
+ const blocker = classifiedBlocker?.kind === "login_required" && signedIn
28
+ ? undefined
29
+ : classifiedBlocker;
17
30
  const conversationId = parseConversationId(url);
18
31
  const state = {
19
32
  url,
@@ -1,4 +1,4 @@
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";
1
+ import type { AskArgs, ArtifactDownloadArgs, ArtifactWaitArgs, ApplyConfigurationData, ApplyConfigurationArgs, AttachFilesArgs, BootstrapArgs, ChatGPTExperience, CommandResult, ConfigurationInspectionData, ConfigurationSelection, CopyResponseArgs, DetectExperienceArgs, DetectExperienceData, DownloadLatestArgs, FilePreflightArgs, FilePreflightData, GetModeArgs, GetModeData, InspectConfigurationArgs, ListArtifactsArgs, MessageStatusArgs, NewThreadArgs, OpenThreadArgs, OpenExperienceData, ProjectSourcesAddArgs, ProjectSourcesAddData, ProjectSourcesAddPlanData, ProjectSourcesListArgs, ProjectSourcesListData, ProjectSourcesPlanAddArgs, ReadLatestArgs, RuntimeEnv, ReadWorkLatestArgs, ReadWorkLatestData, SearchThreadsArgs, SelectToolArgs, SequencePlan, SetModeArgs, StartWorkArgs, StartWorkData, SteerWorkArgs, SteerWorkData, ThreadTarget, WaitArgs, WorkStatusArgs, WorkStatusData, WorkWaitArgs, WorkWaitData } 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";
@@ -8,6 +8,8 @@ import { type ReportRedactionOptions } from "./safety/report-redaction.js";
8
8
  import { type BlockerExplanation, type ExplainBlockerOptions } from "./diagnostics/blockers.js";
9
9
  export type ChatGPTClientOptions = RuntimeEnv & {
10
10
  defaults?: {
11
+ experience?: Exclude<ChatGPTExperience, "unknown">;
12
+ configuration?: ConfigurationSelection;
11
13
  mode?: SetModeArgs;
12
14
  wait?: boolean | WaitArgs;
13
15
  read?: boolean | ReadLatestArgs;
@@ -59,6 +61,8 @@ export type AskWorkflowArgs = {
59
61
  thread?: WorkflowThread;
60
62
  existingTab?: BootstrapArgs["existingTab"];
61
63
  preferExistingTab?: boolean;
64
+ experience?: Exclude<ChatGPTExperience, "unknown">;
65
+ configuration?: ConfigurationSelection;
62
66
  mode?: SetModeArgs;
63
67
  tools?: SelectToolArgs[];
64
68
  files?: FileInput[];
@@ -83,6 +87,8 @@ export type RunMessagesArgs = {
83
87
  thread?: WorkflowThread;
84
88
  existingTab?: BootstrapArgs["existingTab"];
85
89
  preferExistingTab?: boolean;
90
+ experience?: Exclude<ChatGPTExperience, "unknown">;
91
+ configuration?: ConfigurationSelection;
86
92
  mode?: SetModeArgs;
87
93
  messages: Array<{
88
94
  id?: string;
@@ -136,6 +142,29 @@ export type ChatGPTClient = {
136
142
  session: {
137
143
  bootstrap(args?: BootstrapArgs): Promise<CommandResult<unknown>>;
138
144
  };
145
+ experience: {
146
+ detect(args?: DetectExperienceArgs): Promise<CommandResult<DetectExperienceData>>;
147
+ open(args: {
148
+ experience: Exclude<ChatGPTExperience, "unknown">;
149
+ timeoutMs?: number;
150
+ }): Promise<CommandResult<OpenExperienceData>>;
151
+ };
152
+ configuration: {
153
+ inspect(args?: InspectConfigurationArgs): Promise<CommandResult<ConfigurationInspectionData>>;
154
+ apply(args: ApplyConfigurationArgs): Promise<CommandResult<ApplyConfigurationData>>;
155
+ };
156
+ work: {
157
+ start(args: StartWorkArgs): Promise<CommandResult<StartWorkData>>;
158
+ status(args?: WorkStatusArgs): Promise<CommandResult<WorkStatusData>>;
159
+ wait(args?: WorkWaitArgs): Promise<CommandResult<WorkWaitData>>;
160
+ steer(args: SteerWorkArgs): Promise<CommandResult<SteerWorkData>>;
161
+ readLatest(args?: ReadWorkLatestArgs): Promise<CommandResult<ReadWorkLatestData>>;
162
+ artifacts: {
163
+ listLatest(args?: ListArtifactsArgs): Promise<CommandResult<unknown>>;
164
+ wait(args?: ArtifactWaitArgs): Promise<CommandResult<unknown>>;
165
+ downloadLatest(args: ArtifactDownloadArgs): Promise<CommandResult<unknown>>;
166
+ };
167
+ };
139
168
  threads: {
140
169
  "new"(args?: NewThreadArgs): Promise<CommandResult<unknown>>;
141
170
  search(args: SearchThreadsArgs): Promise<CommandResult<unknown>>;
@@ -155,6 +184,7 @@ export type ChatGPTClient = {
155
184
  ask(args: AskArgs): Promise<CommandResult<unknown>>;
156
185
  wait(args?: WaitArgs): Promise<CommandResult<unknown>>;
157
186
  readLatest(args?: ReadLatestArgs): Promise<CommandResult<unknown>>;
187
+ status(args?: MessageStatusArgs): Promise<CommandResult<unknown>>;
158
188
  waitAndRead(args?: WaitArgs & ReadLatestArgs): Promise<CommandResult<unknown>>;
159
189
  };
160
190
  files: {
@@ -171,6 +201,7 @@ export type ChatGPTClient = {
171
201
  };
172
202
  modes: {
173
203
  set(args: SetModeArgs): Promise<CommandResult<unknown>>;
204
+ get(args?: GetModeArgs): Promise<CommandResult<GetModeData>>;
174
205
  };
175
206
  tools: {
176
207
  select(args: SelectToolArgs): Promise<CommandResult<unknown>>;
@@ -2,14 +2,17 @@ import { downloadLatestArtifact, listLatestArtifacts, waitForArtifact } from "./
2
2
  import { attachFiles, downloadLatestFile, preflightFiles } from "./commands/files.js";
3
3
  import { addProjectSources, buildProjectSourceAddPlan, listProjectSources } from "./commands/project-sources.js";
4
4
  import { doctor } from "./commands/doctor.js";
5
- import { askMessage, composeMessage, readLatest, submitMessage, waitAndRead, waitForMessage } from "./commands/messages.js";
6
- import { selectTool, setMode } from "./commands/modes.js";
5
+ import { askMessage, composeMessage, messageStatus, readLatest, submitMessage, waitAndRead, waitForMessage } from "./commands/messages.js";
6
+ import { getMode, selectTool, setMode } from "./commands/modes.js";
7
+ import { applyConfiguration, inspectConfiguration } from "./commands/configuration.js";
8
+ import { detectExperience, openExperience } from "./commands/experience.js";
7
9
  import { createRunReport } from "./commands/reports.js";
8
10
  import { copyResponse } from "./commands/response-actions.js";
9
11
  import { commandDescriptors, describeCommand, helpText } from "./commands/registry.js";
10
12
  import { runSequence } from "./commands/sequence.js";
11
13
  import { bootstrap } from "./commands/session.js";
12
14
  import { newThread, openThread, searchThreads } from "./commands/threads.js";
15
+ import { readLatestWork, startWork, steerWork, waitForWork, workStatus } from "./commands/work.js";
13
16
  import { resultError, resultOk } from "./errors.js";
14
17
  import { createChatGPTAgent } from "./runner/agent.js";
15
18
  import { toRunResult } from "./runner/result.js";
@@ -60,6 +63,26 @@ export function createChatGPT(options = {}) {
60
63
  session: {
61
64
  bootstrap: args => bootstrap(env, args)
62
65
  },
66
+ experience: {
67
+ detect: args => detectExperience(env, args),
68
+ open: args => openExperience(env, args)
69
+ },
70
+ configuration: {
71
+ inspect: args => inspectConfiguration(env, args),
72
+ apply: args => applyConfiguration(env, args)
73
+ },
74
+ work: {
75
+ start: args => startWork(env, args),
76
+ status: args => workStatus(env, args),
77
+ wait: args => waitForWork(env, args),
78
+ steer: args => steerWork(env, args),
79
+ readLatest: args => readLatestWork(env, args),
80
+ artifacts: {
81
+ listLatest: args => listLatestArtifacts(env, args),
82
+ wait: args => waitForArtifact(env, args),
83
+ downloadLatest: args => downloadLatestArtifact(env, args)
84
+ }
85
+ },
63
86
  threads: {
64
87
  new: args => newThread(env, args),
65
88
  search: args => searchThreads(env, args),
@@ -71,6 +94,7 @@ export function createChatGPT(options = {}) {
71
94
  ask: args => askMessage(env, args),
72
95
  wait: args => waitForMessage(env, args),
73
96
  readLatest: args => readLatest(env, args),
97
+ status: args => messageStatus(env, args),
74
98
  waitAndRead: args => waitAndRead(env, args)
75
99
  },
76
100
  files: {
@@ -91,7 +115,8 @@ export function createChatGPT(options = {}) {
91
115
  downloadLatest: args => downloadLatestArtifact(env, args)
92
116
  },
93
117
  modes: {
94
- set: args => setMode(env, args)
118
+ set: args => setMode(env, args),
119
+ get: args => getMode(env, args)
95
120
  },
96
121
  tools: {
97
122
  select: args => selectTool(env, args)
@@ -165,6 +190,30 @@ function pathsFromAttachStep(step) {
165
190
  const paths = step.args.paths;
166
191
  return paths.every(item => typeof item === "string") ? paths : [];
167
192
  }
193
+ function appendSurfaceConfigurationSteps(steps, preferences) {
194
+ if (preferences.experience !== undefined) {
195
+ steps.push({
196
+ id: "experience",
197
+ command: "experience.open",
198
+ args: { experience: preferences.experience }
199
+ });
200
+ }
201
+ if (preferences.configuration !== undefined) {
202
+ steps.push({
203
+ id: "configuration",
204
+ command: "configuration.apply",
205
+ args: {
206
+ ...(preferences.experience === undefined ? {} : { experience: preferences.experience }),
207
+ desired: preferences.configuration,
208
+ strict: true
209
+ }
210
+ });
211
+ return;
212
+ }
213
+ if (preferences.mode !== undefined) {
214
+ steps.push({ id: "mode", command: "modes.set", args: preferences.mode });
215
+ }
216
+ }
168
217
  function normalizeLimits(limits) {
169
218
  return {
170
219
  maxPromptsPerRun: limits?.maxPromptsPerRun ?? 5,
@@ -177,7 +226,7 @@ function normalizeLimits(limits) {
177
226
  function checkRunBudget(plan, limits) {
178
227
  const prompts = plan.steps.filter(step => step.command === "messages.ask" || step.command === "messages.submit").length;
179
228
  const threads = plan.steps.filter(step => step.command === "threads.new" || step.command === "threads.open").length;
180
- const reads = plan.steps.filter(step => step.command === "messages.readLatest" || step.command === "messages.waitAndRead" || step.command === "response.copy").length
229
+ const reads = plan.steps.filter(step => step.command === "messages.readLatest" || step.command === "messages.status" || step.command === "messages.waitAndRead" || step.command === "response.copy").length
181
230
  + plan.steps.filter(step => step.command === "messages.ask" && askStepReads(step.args)).length;
182
231
  const violations = [];
183
232
  if (prompts > limits.maxPromptsPerRun)
@@ -269,10 +318,11 @@ function planAgentWorkflowFromNormalized(agent, input, defaults = {}) {
269
318
  bootstrapStepForWorkflow(thread, input.existingTab ?? agent.defaults.existingTab ?? defaults.existingTab, input.preferExistingTab ?? agent.defaults.preferExistingTab ?? defaults.preferExistingTab),
270
319
  ...threadSteps(thread)
271
320
  ];
272
- const mode = input.mode ?? agent.defaults.mode ?? defaults.mode;
273
- if (mode !== undefined) {
274
- steps.push({ id: "mode", command: "modes.set", args: mode });
275
- }
321
+ appendSurfaceConfigurationSteps(steps, {
322
+ experience: input.experience ?? agent.defaults.experience ?? defaults.experience,
323
+ configuration: input.configuration ?? agent.defaults.configuration ?? defaults.configuration,
324
+ mode: input.mode ?? agent.defaults.mode ?? defaults.mode
325
+ });
276
326
  for (const [index, tool] of input.tools.entries()) {
277
327
  steps.push({ id: `tool${index + 1}`, command: "tools.select", args: tool });
278
328
  }
@@ -337,6 +387,10 @@ function normalizeRunnerInput(agent, input) {
337
387
  normalized.existingTab = args.existingTab;
338
388
  if (args.preferExistingTab !== undefined)
339
389
  normalized.preferExistingTab = args.preferExistingTab;
390
+ if (args.experience !== undefined)
391
+ normalized.experience = args.experience;
392
+ if (args.configuration !== undefined)
393
+ normalized.configuration = args.configuration;
340
394
  if (mode !== undefined)
341
395
  normalized.mode = mode;
342
396
  if (args.response !== undefined)
@@ -457,10 +511,11 @@ function planAskWorkflow(args, defaults = {}) {
457
511
  bootstrapStepForWorkflow(thread, args.existingTab ?? defaults.existingTab, args.preferExistingTab ?? defaults.preferExistingTab),
458
512
  ...threadSteps(thread)
459
513
  ];
460
- const mode = args.mode ?? defaults.mode;
461
- if (mode !== undefined) {
462
- steps.push({ id: "mode", command: "modes.set", args: mode });
463
- }
514
+ appendSurfaceConfigurationSteps(steps, {
515
+ experience: args.experience ?? defaults.experience,
516
+ configuration: args.configuration ?? defaults.configuration,
517
+ mode: args.mode ?? defaults.mode
518
+ });
464
519
  for (const [index, tool] of (args.tools ?? []).entries()) {
465
520
  steps.push({ id: `tool${index + 1}`, command: "tools.select", args: tool });
466
521
  }
@@ -528,10 +583,11 @@ function planRunMessages(args, defaults = {}) {
528
583
  bootstrapStepForWorkflow(thread, args.existingTab ?? defaults.existingTab, args.preferExistingTab ?? defaults.preferExistingTab),
529
584
  ...threadSteps(thread)
530
585
  ];
531
- const mode = args.mode ?? defaults.mode;
532
- if (mode !== undefined) {
533
- steps.push({ id: "mode", command: "modes.set", args: mode });
534
- }
586
+ appendSurfaceConfigurationSteps(steps, {
587
+ experience: args.experience ?? defaults.experience,
588
+ configuration: args.configuration ?? defaults.configuration,
589
+ mode: args.mode ?? defaults.mode
590
+ });
535
591
  args.messages.forEach((message, index) => {
536
592
  steps.push({
537
593
  id: message.id ?? `message${index + 1}`,
@@ -7,7 +7,7 @@ import { cssSelectors, requiredLocator } from "../dom/selectors.js";
7
7
  import { localeLabels } from "../dom/locale-labels.js";
8
8
  import { resultOk } from "../errors.js";
9
9
  import { contextFromPage } from "./context.js";
10
- import { bootstrap } from "./session.js";
10
+ import { ensurePage } from "./session.js";
11
11
  import { localGuardTimeout, withTimeout } from "./timeouts.js";
12
12
  export async function listLatestArtifacts(env, args = {}) {
13
13
  const boot = await ensurePage(env);
@@ -408,12 +408,6 @@ function artifactDownloadBlocker(error, context) {
408
408
  context
409
409
  };
410
410
  }
411
- async function ensurePage(env) {
412
- if (env.page !== undefined) {
413
- return resultOk({}, await contextFromPage(env.page));
414
- }
415
- return bootstrap(env, { preferExistingTab: true });
416
- }
417
411
  async function hasStopControl(page, timeoutMs) {
418
412
  if (typeof page.evaluate !== "function")
419
413
  return false;
@@ -0,0 +1,15 @@
1
+ import { type MenuItem } from "../dom/menus.js";
2
+ import type { ApplyConfigurationArgs, ApplyConfigurationData, ChatGPTExperience, CommandResult, ConfigurationAxis, ConfigurationInspectionData, ConfigurationSelection, InspectConfigurationArgs, RuntimeEnv, SurfaceSelectorProfile } from "../types.js";
3
+ export type ConfigurationPanelSnapshot = {
4
+ openerLabel?: string;
5
+ axisRows: Array<{
6
+ axis: ConfigurationAxis;
7
+ label: string;
8
+ value?: string;
9
+ }>;
10
+ advancedVisible: boolean;
11
+ };
12
+ export declare function inspectConfiguration(env: RuntimeEnv, args?: InspectConfigurationArgs): Promise<CommandResult<ConfigurationInspectionData>>;
13
+ export declare function applyConfiguration(env: RuntimeEnv, args: ApplyConfigurationArgs): Promise<CommandResult<ApplyConfigurationData>>;
14
+ export declare function configurationInspectionFromSurface(experience: ChatGPTExperience, detectedProfile: SurfaceSelectorProfile, evidence: ConfigurationInspectionData["evidence"], panel: ConfigurationPanelSnapshot, menuItems: MenuItem[]): ConfigurationInspectionData;
15
+ export declare function configurationMatchesSelection(inspection: ConfigurationInspectionData, desired: ConfigurationSelection): boolean;