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
@@ -4,6 +4,8 @@ const acceptedTopLevelFields = new Set([
4
4
  "thread",
5
5
  "existingTab",
6
6
  "preferExistingTab",
7
+ "experience",
8
+ "configuration",
7
9
  "attachments",
8
10
  "mode",
9
11
  "tools",
@@ -14,7 +16,7 @@ const acceptedTopLevelFields = new Set([
14
16
  "instructionsMode"
15
17
  ]);
16
18
  const unsupportedAlternatives = {
17
- model: "Use mode for visible ChatGPT UI mode preference. This does not select an API model.",
19
+ model: "Use experience plus configuration for visible ChatGPT UI preferences. Legacy mode remains supported. These do not select an API model.",
18
20
  temperature: "No browser-control equivalent. ChatGPT web does not expose API temperature.",
19
21
  top_p: "No browser-control equivalent. ChatGPT web does not expose API nucleus sampling.",
20
22
  seed: "No browser-control equivalent. Visible ChatGPT web does not expose deterministic API seeds.",
@@ -112,6 +114,10 @@ export function responsesCreateArgsToRunInput(args) {
112
114
  runInput.existingTab = args.existingTab;
113
115
  if (args.preferExistingTab !== undefined)
114
116
  runInput.preferExistingTab = args.preferExistingTab;
117
+ if (args.experience !== undefined)
118
+ runInput.experience = args.experience;
119
+ if (args.configuration !== undefined)
120
+ runInput.configuration = args.configuration;
115
121
  if (args.attachments !== undefined)
116
122
  runInput.attachments = args.attachments;
117
123
  if (args.mode !== undefined)
@@ -133,6 +139,15 @@ export function responseFromRunResult(result, now = new Date()) {
133
139
  const reportPath = result.data?.reportPath ?? result.reportPath;
134
140
  if (reportPath !== undefined)
135
141
  browserControl.reportPath = reportPath;
142
+ const submissionState = result.state.submissionState ?? result.data?.submissionState;
143
+ const completionState = result.state.completionState ?? result.data?.completionState;
144
+ const generationActive = result.data?.generationActive;
145
+ if (submissionState !== undefined)
146
+ browserControl.submissionState = submissionState;
147
+ if (completionState !== undefined)
148
+ browserControl.completionState = completionState;
149
+ if (generationActive !== undefined)
150
+ browserControl.generationActive = generationActive;
136
151
  if (result.output_text.length > 0) {
137
152
  const envelopeArgs = {
138
153
  outputText: result.output_text,
@@ -9,6 +9,15 @@ export function toRunResult(agent, result) {
9
9
  const output = runItemsFromResult(result, outputText);
10
10
  const state = runStateFromResult(result, interruptions);
11
11
  const data = { outputText };
12
+ const submissionState = readSubmissionState(result.data);
13
+ const completionState = readCompletionState(result.data);
14
+ const generationActive = readGenerationActive(result.data);
15
+ if (submissionState !== undefined)
16
+ data.submissionState = submissionState;
17
+ if (completionState !== undefined)
18
+ data.completionState = completionState;
19
+ if (generationActive !== undefined)
20
+ data.generationActive = generationActive;
12
21
  if (outputText.length > 0) {
13
22
  const envelopeArgs = {
14
23
  outputText,
@@ -73,15 +82,56 @@ function parseFinalOutput(agent, outputText) {
73
82
  return outputText;
74
83
  }
75
84
  function runItemsFromResult(result, outputText) {
76
- const items = messageItemsFromData(result.data);
77
- if (!items.some(item => item.type === "message.completed") && outputText.length > 0) {
78
- items.push({ type: "message.completed", role: "assistant", output_text: outputText, format: "markdown" });
85
+ const items = lifecycleItemsFromSteps(result.steps);
86
+ items.push(...messageItemsFromData(result.data));
87
+ if (!items.some(item => item.type === "message.completed" || item.type === "message.in_progress") && outputText.length > 0) {
88
+ if (result.status === "partial" && readCompletionState(result.data) !== "complete") {
89
+ items.push(inProgressItem(outputText, readCompletionState(result.data), readGenerationActive(result.data)));
90
+ }
91
+ else {
92
+ items.push({ type: "message.completed", role: "assistant", output_text: outputText, format: "markdown" });
93
+ }
79
94
  }
80
95
  if (result.blocker !== undefined) {
81
96
  items.push({ type: "run.blocked", blocker: augmentCommandBlocker(result.blocker) });
82
97
  }
83
98
  return items;
84
99
  }
100
+ function lifecycleItemsFromSteps(steps) {
101
+ if (steps === undefined)
102
+ return [];
103
+ const items = [];
104
+ for (const step of steps) {
105
+ if (!step.ok || !isRecord(step.dataPreview))
106
+ continue;
107
+ if (step.command === "experience.open") {
108
+ const experience = step.dataPreview.experience;
109
+ if (experience === "chat" || experience === "work") {
110
+ const item = {
111
+ type: "experience.opened",
112
+ experience
113
+ };
114
+ if (typeof step.dataPreview.changed === "boolean")
115
+ item.changed = step.dataPreview.changed;
116
+ items.push(item);
117
+ }
118
+ continue;
119
+ }
120
+ if (step.command === "configuration.apply") {
121
+ const item = {
122
+ type: "configuration.applied"
123
+ };
124
+ if (isRecord(step.dataPreview.requested)) {
125
+ item.requested = step.dataPreview.requested;
126
+ }
127
+ if (typeof step.dataPreview.verified === "boolean") {
128
+ item.verified = step.dataPreview.verified;
129
+ }
130
+ items.push(item);
131
+ }
132
+ }
133
+ return items;
134
+ }
85
135
  function messageItemsFromData(data) {
86
136
  if (!isRecord(data))
87
137
  return [];
@@ -95,7 +145,12 @@ function messageItemsFromData(data) {
95
145
  });
96
146
  }
97
147
  if (typeof data.responseText === "string" && data.responseText.length > 0) {
98
- items.push({ type: "message.completed", role: "assistant", output_text: data.responseText, format: "markdown" });
148
+ if (readCompletionState(data) === "complete" || data.complete === true) {
149
+ items.push({ type: "message.completed", role: "assistant", output_text: data.responseText, format: "markdown" });
150
+ }
151
+ else {
152
+ items.push(inProgressItem(data.responseText, readCompletionState(data), readGenerationActive(data)));
153
+ }
99
154
  }
100
155
  if (items.length > 0)
101
156
  return items;
@@ -116,8 +171,78 @@ function runStateFromResult(result, interruptions) {
116
171
  const thread = threadRefFromContext(result.context);
117
172
  if (thread !== undefined)
118
173
  state.thread = thread;
174
+ const submissionState = readSubmissionState(result.data);
175
+ const completionState = readCompletionState(result.data);
176
+ if (submissionState !== undefined)
177
+ state.submissionState = submissionState;
178
+ if (completionState !== undefined)
179
+ state.completionState = completionState;
119
180
  return state;
120
181
  }
182
+ function inProgressItem(outputText, completionState, generationActive) {
183
+ const item = {
184
+ type: "message.in_progress",
185
+ role: "assistant",
186
+ output_text: outputText,
187
+ preview: outputText.length > 160 ? `${outputText.slice(0, 159)}...` : outputText,
188
+ format: "markdown",
189
+ textLength: outputText.length,
190
+ textHash: hashText(outputText)
191
+ };
192
+ if (completionState !== undefined)
193
+ item.completionState = completionState;
194
+ if (generationActive !== undefined)
195
+ item.generationActive = generationActive;
196
+ return item;
197
+ }
198
+ function readCompletionState(data) {
199
+ if (!isRecord(data))
200
+ return undefined;
201
+ const value = data.completionState;
202
+ if (value === "complete" || value === "generating" || value === "stopped" || value === "partial" || value === "unknown") {
203
+ return value;
204
+ }
205
+ for (const nested of Object.values(data)) {
206
+ const nestedState = readCompletionState(nested);
207
+ if (nestedState !== undefined)
208
+ return nestedState;
209
+ }
210
+ return undefined;
211
+ }
212
+ function readSubmissionState(data) {
213
+ if (!isRecord(data))
214
+ return undefined;
215
+ const value = data.submissionState;
216
+ if (value === "not_submitted" || value === "submitted" || value === "submitted_unconfirmed" || value === "submitted_generating") {
217
+ return value;
218
+ }
219
+ for (const nested of Object.values(data)) {
220
+ const nestedState = readSubmissionState(nested);
221
+ if (nestedState !== undefined)
222
+ return nestedState;
223
+ }
224
+ return undefined;
225
+ }
226
+ function readGenerationActive(data) {
227
+ if (!isRecord(data))
228
+ return undefined;
229
+ if (typeof data.generationActive === "boolean")
230
+ return data.generationActive;
231
+ for (const nested of Object.values(data)) {
232
+ const value = readGenerationActive(nested);
233
+ if (value !== undefined)
234
+ return value;
235
+ }
236
+ return undefined;
237
+ }
238
+ function hashText(value) {
239
+ let hash = 2166136261;
240
+ for (let index = 0; index < value.length; index += 1) {
241
+ hash ^= value.charCodeAt(index);
242
+ hash = Math.imul(hash, 16777619);
243
+ }
244
+ return (hash >>> 0).toString(16).padStart(8, "0");
245
+ }
121
246
  function threadRefFromContext(context) {
122
247
  const thread = {};
123
248
  if (context.url !== undefined)
@@ -1,5 +1,5 @@
1
1
  import type { ChatGPTRunItem, ChatGPTRunResult } from "./types.js";
2
- export type ChatGPTRunStreamEventName = "thread_opened" | "mode_selected" | "tool_selected" | "file_attached" | "message_submitted" | "message_completed" | "file_downloaded" | "run_blocked";
2
+ export type ChatGPTRunStreamEventName = "thread_opened" | "experience_opened" | "configuration_applied" | "mode_selected" | "tool_selected" | "file_attached" | "message_submitted" | "message_in_progress" | "message_completed" | "file_downloaded" | "run_blocked";
3
3
  export type ChatGPTRunStreamEvent = {
4
4
  type: "run_item_stream_event";
5
5
  name: ChatGPTRunStreamEventName;
@@ -47,6 +47,10 @@ function runItemEventName(item) {
47
47
  switch (item.type) {
48
48
  case "thread.opened":
49
49
  return "thread_opened";
50
+ case "experience.opened":
51
+ return "experience_opened";
52
+ case "configuration.applied":
53
+ return "configuration_applied";
50
54
  case "mode.selected":
51
55
  return "mode_selected";
52
56
  case "tool.selected":
@@ -55,6 +59,8 @@ function runItemEventName(item) {
55
59
  return "file_attached";
56
60
  case "message.submitted":
57
61
  return "message_submitted";
62
+ case "message.in_progress":
63
+ return "message_in_progress";
58
64
  case "message.completed":
59
65
  return "message_completed";
60
66
  case "file.downloaded":
@@ -1,4 +1,4 @@
1
- import type { AttachedFile, CommandResult, CommandStatus, CopyResponseArgs, DownloadLatestArgs, BootstrapArgs, ReadLatestArgs, ResponseCaptureFidelity, ResponseCaptureSource, ResponseFormat, SequencePlan, SelectToolArgs, SetModeArgs, ThreadTarget, WaitArgs } from "../types.js";
1
+ import type { AttachedFile, ChatGPTExperience, CommandResult, CommandStatus, CompletionState, ConfigurationSelection, CopyResponseArgs, DownloadLatestArgs, BootstrapArgs, ReadLatestArgs, ResponseCaptureFidelity, ResponseCaptureSource, ResponseFormat, SequencePlan, SelectToolArgs, SetModeArgs, SubmissionState, ThreadTarget, WaitArgs } from "../types.js";
2
2
  import type { RunReportOptions } from "../commands/reports.js";
3
3
  import type { UntrustedOutputReturnEnvelope } from "../safety/untrusted-output.js";
4
4
  import type { ChatGPTRunStream } from "./stream.js";
@@ -38,6 +38,10 @@ export type ChatGPTVisibleModePreference = SetModeArgs & {
38
38
  required?: boolean;
39
39
  ifUnavailable?: "continue" | "block" | "skip";
40
40
  };
41
+ export type ChatGPTVisibleConfigurationPreference = ConfigurationSelection & {
42
+ required?: boolean;
43
+ ifUnavailable?: "continue" | "block" | "skip";
44
+ };
41
45
  export type ChatGPTVisibleToolPreference = SelectToolArgs & {
42
46
  required?: boolean;
43
47
  ifUnavailable?: "continue" | "block" | "skip";
@@ -46,6 +50,8 @@ export type ChatGPTRunDefaults = {
46
50
  thread?: ChatGPTThreadSelector | ThreadTarget;
47
51
  existingTab?: BootstrapArgs["existingTab"];
48
52
  preferExistingTab?: boolean;
53
+ experience?: Exclude<ChatGPTExperience, "unknown">;
54
+ configuration?: ChatGPTVisibleConfigurationPreference;
49
55
  mode?: ChatGPTVisibleModePreference;
50
56
  wait?: boolean | WaitArgs;
51
57
  read?: boolean | ReadLatestArgs;
@@ -103,6 +109,8 @@ export type ChatGPTRunInput = string | {
103
109
  thread?: ChatGPTThreadSelector;
104
110
  existingTab?: BootstrapArgs["existingTab"];
105
111
  preferExistingTab?: boolean;
112
+ experience?: Exclude<ChatGPTExperience, "unknown">;
113
+ configuration?: ChatGPTVisibleConfigurationPreference;
106
114
  attachments?: ChatGPTAttachmentInput[];
107
115
  mode?: ChatGPTVisibleModePreference;
108
116
  tools?: ChatGPTVisibleToolPreference[];
@@ -126,6 +134,14 @@ export type DownloadedFileSummary = {
126
134
  export type ChatGPTRunItem = {
127
135
  type: "thread.opened";
128
136
  thread: ChatGPTThreadRef;
137
+ } | {
138
+ type: "experience.opened";
139
+ experience: Exclude<ChatGPTExperience, "unknown">;
140
+ changed?: boolean;
141
+ } | {
142
+ type: "configuration.applied";
143
+ requested?: ConfigurationSelection;
144
+ verified?: boolean;
129
145
  } | {
130
146
  type: "mode.selected";
131
147
  requested?: string;
@@ -144,6 +160,16 @@ export type ChatGPTRunItem = {
144
160
  role: "user";
145
161
  preview: string;
146
162
  redacted: boolean;
163
+ } | {
164
+ type: "message.in_progress";
165
+ role: "assistant";
166
+ output_text?: string;
167
+ preview: string;
168
+ format: ResponseFormat;
169
+ completionState?: CompletionState;
170
+ generationActive?: boolean;
171
+ textLength?: number;
172
+ textHash?: string;
147
173
  } | {
148
174
  type: "message.completed";
149
175
  role: "assistant";
@@ -216,6 +242,8 @@ export type ChatGPTRunState = {
216
242
  resumable: boolean;
217
243
  thread?: ChatGPTThreadRef;
218
244
  nextStepId?: string;
245
+ submissionState?: SubmissionState;
246
+ completionState?: CompletionState;
219
247
  };
220
248
  export type ChatGPTRunData<TOutput = string> = {
221
249
  finalOutput?: TOutput;
@@ -224,6 +252,9 @@ export type ChatGPTRunData<TOutput = string> = {
224
252
  thread?: ChatGPTThreadRef;
225
253
  downloads?: DownloadedFileSummary[];
226
254
  reportPath?: string;
255
+ submissionState?: SubmissionState;
256
+ completionState?: CompletionState;
257
+ generationActive?: boolean;
227
258
  };
228
259
  export type ChatGPTRunResult<TOutput = string> = CommandResult<ChatGPTRunData<TOutput>> & {
229
260
  finalOutput?: TOutput;
@@ -256,6 +287,9 @@ export type ChatGPTResponse = {
256
287
  resultStatus: CommandStatus;
257
288
  thread?: ChatGPTThreadRef;
258
289
  reportPath?: string;
290
+ submissionState?: SubmissionState;
291
+ completionState?: CompletionState;
292
+ generationActive?: boolean;
259
293
  untrustedOutput?: UntrustedOutputReturnEnvelope;
260
294
  unsupported?: UnsupportedField[];
261
295
  };
@@ -1,6 +1,15 @@
1
1
  export type RiskLevel = "low" | "medium" | "high";
2
2
  export declare const commandRisk: {
3
3
  readonly "session.bootstrap": "low";
4
+ readonly "experience.detect": "low";
5
+ readonly "experience.open": "medium";
6
+ readonly "configuration.inspect": "low";
7
+ readonly "configuration.apply": "medium";
8
+ readonly "work.start": "medium";
9
+ readonly "work.status": "medium";
10
+ readonly "work.wait": "low";
11
+ readonly "work.steer": "medium";
12
+ readonly "work.readLatest": "medium";
4
13
  readonly "threads.search": "medium";
5
14
  readonly "threads.open": "medium";
6
15
  readonly "threads.new": "low";
@@ -9,6 +18,7 @@ export declare const commandRisk: {
9
18
  readonly "messages.ask": "medium";
10
19
  readonly "messages.wait": "low";
11
20
  readonly "messages.readLatest": "medium";
21
+ readonly "messages.status": "medium";
12
22
  readonly "messages.waitAndRead": "medium";
13
23
  readonly "artifacts.listLatest": "medium";
14
24
  readonly "artifacts.wait": "low";
@@ -21,6 +31,7 @@ export declare const commandRisk: {
21
31
  readonly "projects.sources.add": "medium";
22
32
  readonly "response.copy": "medium";
23
33
  readonly "modes.set": "medium";
34
+ readonly "modes.get": "low";
24
35
  readonly "tools.select": "medium";
25
36
  readonly "threads.delete": "high";
26
37
  readonly "threads.archive": "high";
@@ -1,5 +1,14 @@
1
1
  export const commandRisk = {
2
2
  "session.bootstrap": "low",
3
+ "experience.detect": "low",
4
+ "experience.open": "medium",
5
+ "configuration.inspect": "low",
6
+ "configuration.apply": "medium",
7
+ "work.start": "medium",
8
+ "work.status": "medium",
9
+ "work.wait": "low",
10
+ "work.steer": "medium",
11
+ "work.readLatest": "medium",
3
12
  "threads.search": "medium",
4
13
  "threads.open": "medium",
5
14
  "threads.new": "low",
@@ -8,6 +17,7 @@ export const commandRisk = {
8
17
  "messages.ask": "medium",
9
18
  "messages.wait": "low",
10
19
  "messages.readLatest": "medium",
20
+ "messages.status": "medium",
11
21
  "messages.waitAndRead": "medium",
12
22
  "artifacts.listLatest": "medium",
13
23
  "artifacts.wait": "low",
@@ -20,6 +30,7 @@ export const commandRisk = {
20
30
  "projects.sources.add": "medium",
21
31
  "response.copy": "medium",
22
32
  "modes.set": "medium",
33
+ "modes.get": "low",
23
34
  "tools.select": "medium",
24
35
  "threads.delete": "high",
25
36
  "threads.archive": "high",
@@ -1 +1,7 @@
1
+ type IntelligenceModeOptionId = "instant" | "medium" | "high" | "extraHigh" | "pro";
1
2
  export declare function main(argv?: string[]): Promise<number>;
3
+ export declare function mergeCapture(source: string, labels: readonly string[], modeOptions: Partial<Record<IntelligenceModeOptionId, string[]>>, generationLabels?: {
4
+ stopControl?: readonly string[];
5
+ stoppedAssistant?: readonly string[];
6
+ }): string;
7
+ export {};
@@ -3,7 +3,17 @@ import { dirname, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { readLanguageCoverage } from "./locale-capture/language-coverage.js";
5
5
  const ENGLISH_MODE_LABELS = new Set(["Latest", "Instant", "Thinking", "Extended", "Medium", "High", "Extra High", "Pro"]);
6
- const UPDATE_NOTE = " * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.";
6
+ const ENGLISH_STOP_CONTROL = new Set(["stop generating", "stop streaming", "stop answering", "cancel"]);
7
+ const ENGLISH_STOPPED_ASSISTANT = new Set(["stopped thinking", "stopped answering", "generation stopped"]);
8
+ const INTELLIGENCE_MODE_OPTION_IDS = ["instant", "medium", "high", "extraHigh", "pro"];
9
+ const ENGLISH_INTELLIGENCE_MODE_OPTIONS = {
10
+ instant: "Instant",
11
+ medium: "Medium",
12
+ high: "High",
13
+ extraHigh: "Extra High",
14
+ pro: "Pro",
15
+ };
16
+ const UPDATE_NOTE = " * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.";
7
17
  class ApplyUsageError extends Error {
8
18
  exitCode;
9
19
  constructor(message, exitCode = 2) {
@@ -45,12 +55,18 @@ export async function main(argv = process.argv.slice(2)) {
45
55
  throw new ApplyUsageError(`Missing successful capture for ${language.bcp47}.`, 1);
46
56
  }
47
57
  const labels = observedNonEnglishLabels(record);
48
- if (labels.length === 0)
58
+ const modeOptions = observedNonEnglishModeOptions(record);
59
+ const stopControl = observedNonEnglishGenerationLabels(record.generationStopLabels, ENGLISH_STOP_CONTROL);
60
+ const stoppedAssistant = observedNonEnglishGenerationLabels(record.generationStoppedLabels, ENGLISH_STOPPED_ASSISTANT);
61
+ if (labels.length === 0 && Object.keys(modeOptions).length === 0 && stopControl.length === 0 && stoppedAssistant.length === 0)
49
62
  continue;
50
63
  planned.push({
51
64
  locale: language.bcp47,
52
65
  file: resolve(root, "src/dom/locale", `${language.bcp47}.ts`),
53
66
  labels,
67
+ modeOptions,
68
+ stopControl,
69
+ stoppedAssistant,
54
70
  });
55
71
  }
56
72
  if (!options.reviewed) {
@@ -62,10 +78,13 @@ export async function main(argv = process.argv.slice(2)) {
62
78
  }
63
79
  for (const change of planned) {
64
80
  const before = await readFile(change.file, "utf8");
65
- const after = mergeModeLabels(before, change.labels);
81
+ const after = mergeCapture(before, change.labels, change.modeOptions, {
82
+ stopControl: change.stopControl,
83
+ stoppedAssistant: change.stoppedAssistant,
84
+ });
66
85
  if (after !== before) {
67
86
  await writeFile(change.file, after, "utf8");
68
- console.log(`updated ${change.locale} labels=${change.labels.length}`);
87
+ console.log(`updated ${change.locale} labels=${change.labels.length} modeOptions=${Object.keys(change.modeOptions).length} stopControl=${change.stopControl.length} stoppedAssistant=${change.stoppedAssistant.length}`);
69
88
  }
70
89
  }
71
90
  return 0;
@@ -93,18 +112,46 @@ function observedNonEnglishLabels(record) {
93
112
  }
94
113
  return labels;
95
114
  }
96
- function mergeModeLabels(source, labels) {
97
- let text = updateComment(source);
98
- const existing = parseExistingModeLabels(text);
99
- const merged = dedupe([...existing, ...labels]);
100
- const line = ` modeLabels: [${merged.map(label => JSON.stringify(label)).join(", ")}],`;
101
- if (/^\s*modeLabels:\s*\[[^\]]*\],/m.test(text)) {
102
- return text.replace(/^\s*modeLabels:\s*\[[^\]]*\],/m, line);
115
+ function observedNonEnglishModeOptions(record) {
116
+ const labels = record.intelligenceLabels ?? [];
117
+ if (labels.length !== INTELLIGENCE_MODE_OPTION_IDS.length) {
118
+ throw new ApplyUsageError(`${record.requestedLocale} has ${labels.length} Intelligence labels; expected ${INTELLIGENCE_MODE_OPTION_IDS.length}.`, 1);
103
119
  }
104
- if (/^\s*copyResponse:\s*.*,\n/m.test(text)) {
105
- return text.replace(/^(\s*copyResponse:\s*.*,\n)/m, `$1${line}\n`);
120
+ const modeOptions = {};
121
+ for (let index = 0; index < INTELLIGENCE_MODE_OPTION_IDS.length; index += 1) {
122
+ const id = INTELLIGENCE_MODE_OPTION_IDS[index];
123
+ const label = labels[index];
124
+ if (label !== ENGLISH_INTELLIGENCE_MODE_OPTIONS[id]) {
125
+ modeOptions[id] = [label];
126
+ }
127
+ }
128
+ return modeOptions;
129
+ }
130
+ function observedNonEnglishGenerationLabels(values, englishValues) {
131
+ return dedupe((values ?? [])
132
+ .map(value => value.replace(/\s+/g, " ").trim())
133
+ .filter(value => value.length > 0 && !englishValues.has(value.toLowerCase())));
134
+ }
135
+ export function mergeCapture(source, labels, modeOptions, generationLabels = {}) {
136
+ let text = updateComment(source);
137
+ if (labels.length > 0) {
138
+ const existing = parseExistingModeLabels(text);
139
+ const merged = dedupe([...existing, ...labels]);
140
+ const line = ` modeLabels: [${merged.map(label => JSON.stringify(label)).join(", ")}],`;
141
+ if (/^\s*modeLabels:\s*\[[^\]]*\],/m.test(text)) {
142
+ text = text.replace(/^\s*modeLabels:\s*\[[^\]]*\],/m, line);
143
+ }
144
+ else if (/^\s*copyResponse:\s*.*,\n/m.test(text)) {
145
+ text = text.replace(/^(\s*copyResponse:\s*.*,\n)/m, `$1${line}\n`);
146
+ }
147
+ else {
148
+ text = text.replace(/^export const \w+ = \{\n/m, match => `${match}${line}\n`);
149
+ }
106
150
  }
107
- return text.replace(/^export const \w+ = \{\n/m, match => `${match}${line}\n`);
151
+ text = mergeModeOptions(text, modeOptions);
152
+ text = mergeStringArrayProperty(text, "stopControl", generationLabels.stopControl ?? []);
153
+ text = mergeStringArrayProperty(text, "stoppedAssistant", generationLabels.stoppedAssistant ?? []);
154
+ return text;
108
155
  }
109
156
  function parseExistingModeLabels(source) {
110
157
  const match = /^\s*modeLabels:\s*\[(?<body>[^\]]*)\],/m.exec(source);
@@ -117,6 +164,91 @@ function parseExistingModeLabels(source) {
117
164
  }
118
165
  return labels;
119
166
  }
167
+ function mergeModeOptions(source, modeOptions) {
168
+ const existing = parseExistingModeOptions(source);
169
+ const merged = {};
170
+ for (const id of INTELLIGENCE_MODE_OPTION_IDS) {
171
+ const values = dedupe([...(existing[id] ?? []), ...(modeOptions[id] ?? [])]);
172
+ if (values.length > 0) {
173
+ merged[id] = values;
174
+ }
175
+ }
176
+ const block = formatModeOptions(merged);
177
+ if (block === undefined) {
178
+ return source;
179
+ }
180
+ if (/^\s*modeOptions:\s*\{[\s\S]*?^\s*\},\n/m.test(source)) {
181
+ return source.replace(/^\s*modeOptions:\s*\{[\s\S]*?^\s*\},\n/m, `${block}\n`);
182
+ }
183
+ if (/^\s*modeLabels:\s*\[[^\]]*\],\n/m.test(source)) {
184
+ return source.replace(/^(\s*modeLabels:\s*\[[^\]]*\],\n)/m, `$1${block}\n`);
185
+ }
186
+ return source.replace(/^export const \w+ = \{\n/m, match => `${match}${block}\n`);
187
+ }
188
+ function parseExistingModeOptions(source) {
189
+ const options = {};
190
+ const blockMatch = /^\s*modeOptions:\s*\{(?<body>[\s\S]*?)^\s*\},/m.exec(source);
191
+ const body = blockMatch?.groups?.body;
192
+ if (body === undefined)
193
+ return options;
194
+ for (const id of INTELLIGENCE_MODE_OPTION_IDS) {
195
+ const lineMatch = new RegExp(`^\\s*${id}:\\s*\\[(?<body>[^\\]]*)\\],`, "m").exec(body);
196
+ const lineBody = lineMatch?.groups?.body;
197
+ if (lineBody === undefined)
198
+ continue;
199
+ const values = [];
200
+ for (const stringMatch of lineBody.matchAll(/"((?:\\"|[^"])*)"/g)) {
201
+ values.push(JSON.parse(`"${stringMatch[1]}"`));
202
+ }
203
+ if (values.length > 0) {
204
+ options[id] = values;
205
+ }
206
+ }
207
+ return options;
208
+ }
209
+ function formatModeOptions(modeOptions) {
210
+ const lines = INTELLIGENCE_MODE_OPTION_IDS
211
+ .map(id => {
212
+ const values = modeOptions[id];
213
+ return values === undefined || values.length === 0
214
+ ? undefined
215
+ : ` ${id}: [${values.map(value => JSON.stringify(value)).join(", ")}],`;
216
+ })
217
+ .filter((line) => line !== undefined);
218
+ if (lines.length === 0) {
219
+ return undefined;
220
+ }
221
+ return [" modeOptions: {", ...lines, " },"].join("\n");
222
+ }
223
+ function mergeStringArrayProperty(source, property, values) {
224
+ if (values.length === 0)
225
+ return source;
226
+ const existing = parseExistingStringArrayProperty(source, property);
227
+ const merged = dedupe([...existing, ...values]);
228
+ const line = ` ${property}: [${merged.map(value => JSON.stringify(value)).join(", ")}],`;
229
+ const propertyPattern = new RegExp(`^\\s*${property}:\\s*\\[[^\\]]*\\],`, "m");
230
+ if (propertyPattern.test(source)) {
231
+ return source.replace(propertyPattern, line);
232
+ }
233
+ if (/^\s*responseActions:\s*.*,\n/m.test(source)) {
234
+ return source.replace(/^(\s*responseActions:\s*.*,\n)/m, `$1${line}\n`);
235
+ }
236
+ if (/^\s*modeOptions:\s*\{[\s\S]*?^\s*\},\n/m.test(source)) {
237
+ return source.replace(/^(\s*modeOptions:\s*\{[\s\S]*?^\s*\},\n)/m, `$1${line}\n`);
238
+ }
239
+ return source.replace(/^export const \w+ = \{\n/m, match => `${match}${line}\n`);
240
+ }
241
+ function parseExistingStringArrayProperty(source, property) {
242
+ const match = new RegExp(`^\\s*${property}:\\s*\\[(?<body>[^\\]]*)\\],`, "m").exec(source);
243
+ const body = match?.groups?.body;
244
+ if (body === undefined)
245
+ return [];
246
+ const values = [];
247
+ for (const stringMatch of body.matchAll(/"((?:\\"|[^"])*)"/g)) {
248
+ values.push(JSON.parse(`"${stringMatch[1]}"`));
249
+ }
250
+ return values;
251
+ }
120
252
  function updateComment(source) {
121
253
  let text = source.replace(/\n \* Omitted because they match English case-insensitively: `modeLabels`[\s\S]*?blocker copy\.\n/g, "\n * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.\n");
122
254
  if (!text.includes(UPDATE_NOTE)) {
@@ -178,7 +310,7 @@ function requiredValue(argv, index, flag) {
178
310
  function packageRoot() {
179
311
  return resolve(dirname(fileURLToPath(import.meta.url)), "../..");
180
312
  }
181
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
313
+ if (typeof process !== "undefined" && process.argv[1] === fileURLToPath(import.meta.url)) {
182
314
  const exitCode = await main();
183
315
  process.exitCode = exitCode;
184
316
  }
@@ -9,6 +9,9 @@ type CaptureOptions = {
9
9
  limit: number | undefined;
10
10
  locales: string[] | undefined;
11
11
  openVersionSubmenu: boolean;
12
+ captureGenerationState: boolean;
13
+ generationPrompt: string;
14
+ generationCaptureTimeoutMs: number;
12
15
  restore: boolean;
13
16
  settleMs: number;
14
17
  switchTimeoutMs: number;