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
@@ -52,9 +52,9 @@ workflow selector.
52
52
 
53
53
  Two rules that never change regardless of language:
54
54
 
55
- - **API keys stay English.** Callers pass `model: "Pro"`,
56
- `intelligence: "Pro"`, `modelVersion: "5.4"`, `effort: "Thinking"`, or
57
- `tool: "web_search"`. Only the *matched DOM text* is localized. You add the
55
+ - **API keys stay English.** Callers pass `experience: "work"`,
56
+ `model: "GPT-5.6 Sol"`, `intelligence: "Pro"`, `effort: "High"`,
57
+ `speed: "Standard"`, or `tool: "web_search"`. Only the *matched DOM text* is localized. You add the
58
58
  German label to the registry array; the caller-facing key is untouched.
59
59
  - **Structural anchors are language-agnostic and are not in this file.** Element ids
60
60
  (`#composer-plus-btn`, `#upload-files`), `data-message-author-role`,
@@ -68,6 +68,11 @@ Localized — lives in `src/dom/locale/en.ts` (English) and per-locale files, sa
68
68
  | Registry key | Surface | Capture from |
69
69
  |---|---|---|
70
70
  | `composerTextbox` | composer textbox | `aria-label` |
71
+ | `workComposerTextbox` | Work composer textbox | `aria-label` / placeholder |
72
+ | `newWork` | start-another-Work-task control | visible button or link text |
73
+ | `experienceOptions.chat` / `.work` | Chat/Work switch controls | visible button, tab, link, or menu text |
74
+ | `configurationAxes.*` | model/intelligence/effort/speed/advanced rows | visible row text |
75
+ | `configurationOptions.*` | Chat and Work configuration values | visible menu text |
71
76
  | `sendButton` | send button | `aria-label` |
72
77
  | `searchChatsButton` | search-chats button | `aria-label` |
73
78
  | `searchChatsPlaceholder` | search input | `placeholder` (mind the ellipsis — see caveats) |
@@ -77,7 +82,8 @@ Localized — lives in `src/dom/locale/en.ts` (English) and per-locale files, sa
77
82
  | `projectSourcesTab` / `projectSourcesAddSource` / `projectSourcesUploadFiles` | Project Sources tab and append-add flow | visible tab/button/menu text |
78
83
  | `copyResponse` | copy-response button | `aria-label` |
79
84
  | `download` / `downloadImage` / `imageContainerHint` | download affordances | `aria-label` / container hint |
80
- | `modeLabels` / `modeOpenerExtra` | model/effort switcher | visible button + menu text |
85
+ | `modeLabels` / `modeOpenerExtra` | model/effort switcher recognition and openers | visible button + menu text |
86
+ | `modeOptions.<semantic-id>` | selectable model/intelligence mode options | visible picker rows, keyed by stable ids such as `high`, `extraHigh`, and `pro` |
81
87
  | `tools.web_search` / `tools.deep_research` / `tools.create_image` | tool menu items | visible menu text |
82
88
  | `signedInMarkers` | signed-in detection | sidebar/shell words |
83
89
  | `transientAssistant` | streaming placeholder filter | assistant streaming text ("Thinking", etc.) |
@@ -100,6 +106,27 @@ Localized — lives in `src/dom/locale/en.ts` (English) and per-locale files, sa
100
106
  The type definitions for `LocaleStrings` (complete) and `LocaleContribution` (partial, for
101
107
  non-English files) live in [`src/dom/locale/types.ts`](../src/dom/locale/types.ts).
102
108
 
109
+ The first Chat/Work profile fixtures are English evidence. The nested locale
110
+ combiner lets existing locale files omit the new groups without breaking
111
+ compilation, but that is not proof that Work configuration is localized in
112
+ those languages. Add observed labels and a sanitized surface-profile fixture
113
+ before claiming support for a new locale/profile combination.
114
+
115
+ Create a read-only draft from an already-open authorized ChatGPT tab:
116
+
117
+ ```bash
118
+ npm run capture:surface-profile -- \
119
+ --id work-basic-de \
120
+ --locale de-DE
121
+ ```
122
+
123
+ The command defaults region, plan, account, and workspace metadata to
124
+ `not-recorded`, defaults support state to `unverified`, strips conversation
125
+ identifiers, and excludes prompt/response/sidebar text. Review the draft under
126
+ `outputs/surface-profiles/`, add only verified localized labels to the registry,
127
+ then move a sanitized fixture into `contracts/v1/fixtures/` and update the
128
+ manifest/parity matrix.
129
+
103
130
  ## Adding a new language
104
131
 
105
132
  ### Step 1 — put a real ChatGPT session into the target language
@@ -141,7 +168,13 @@ import type { LocaleContribution } from "./types.js";
141
168
 
142
169
  export const de = {
143
170
  sendButton: ["Nachricht senden"],
144
- modeLabels: ["Neueste", "Schnell", "Denken", "Erweitert", "Pro"],
171
+ modeLabels: ["Sofort", "Mittel", "Hoch", "Extra hoch"],
172
+ modeOptions: {
173
+ instant: ["Sofort"],
174
+ medium: ["Mittel"],
175
+ high: ["Hoch"],
176
+ extraHigh: ["Extra hoch"],
177
+ },
145
178
  tools: {
146
179
  web_search: ["Websuche"],
147
180
  },
@@ -150,11 +183,14 @@ export const de = {
150
183
  ```
151
184
 
152
185
  Leave the canonical English first (it comes from `en.ts`), and leave the API keys
153
- (`web_search`, the `effort` values) unchanged.
186
+ (`web_search`, `modeOptions.pro`, `modeOptions.high`, and the other semantic ids)
187
+ unchanged.
154
188
 
155
189
  Newer ChatGPT rollouts may expose `Medium`, `High`, `Extra High`, and `Pro`
156
190
  under an `Intelligence` picker. Add localized equivalents only after observing
157
- those exact labels in the target locale.
191
+ those exact labels in the target locale. Put broad picker/opening labels in
192
+ `modeLabels`, but put selectable labels under `modeOptions.<semantic-id>` so
193
+ short labels such as `Pro` cannot match unrelated rows like `Move to project`.
158
194
 
159
195
  Then open [`src/dom/locale/index.ts`](../src/dom/locale/index.ts) and register the new
160
196
  locale:
@@ -239,9 +275,11 @@ Notes:
239
275
  - All consumer import paths (`from "../dom/locale-labels.js"`) are unchanged — the barrel
240
276
  `locale-labels.ts` re-exports everything from `locale/index.ts`.
241
277
  - `doctor({ check: ["localization"] })` verifies that the registry is populated and
242
- canonical English values are present. It does not yet prove full localized selector
243
- coverage; treat localized workflow failures as selector-wiring work unless the registry
244
- is missing the observed labels.
278
+ canonical English values are present. It also reports running-state localization coverage
279
+ separately from the flattened selector list, because `stopControl` and
280
+ `stoppedAssistant` require a live mid-generation capture. It does not yet prove full
281
+ localized selector coverage; treat localized workflow failures as selector-wiring work
282
+ unless the registry is missing the observed labels.
245
283
  - The public-export plugin bundles under `plugins/codex-chatgpt-control/runtime/node/` are
246
284
  produced by a separate pipeline (`tools/public-export/export-public.mjs`) and are not
247
285
  updated by the sync above.
@@ -26,9 +26,14 @@ Wire fields stay TypeScript-compatible. Python exposes idiomatic aliases:
26
26
  | `totalBytes` | `total_bytes` |
27
27
  | `projectUrl` | `project_url` |
28
28
  | `displayPath` | `display_path` |
29
+ | `selectorProfile` | `selector_profile` |
30
+ | `newTask` | `new_task` |
31
+ | `includeArtifacts` | `include_artifacts` |
29
32
 
30
33
  Incomplete response capture is also shared contract behavior. Python must preserve `status == "partial"`, `output_text`, warnings, and any nested `data.captureLimit` dictionaries exactly as the TypeScript backend returns them. `partial` is not a protocol error: callers should inspect `data.complete` and run another wait/read on the same thread when they need final output.
31
34
 
35
+ For long-answer polling, Python forwards `response_content="metadata"` to the shared wire field `responseContent: "metadata"` on `messages.wait`. The TypeScript backend then omits assistant text from wait results and returns compact metadata such as `data.responseChars` and `data.responseSha256`; Python must preserve those fields without trying to reconstruct omitted content.
36
+
32
37
  Generated-image behavior stays owned by the TypeScript runtime. Python exposes
33
38
  the same backend commands through `chatgpt.artifacts.list_latest(...)`,
34
39
  `chatgpt.artifacts.wait(...)`, and `chatgpt.artifacts.download_latest(...)`.
@@ -39,6 +44,13 @@ claimed conversation in a temporary bridge-owned tab and exporting through
39
44
  `pageAssets`, Python observes the same command result through the backend
40
45
  protocol without any Python-side browser logic.
41
46
 
47
+ Chat/Work behavior follows the same authority boundary. Python exposes matching
48
+ sync and async `experience`, `configuration`, and `work` groups, but surface
49
+ detection, selector profiles, configuration state machines, submit-once Work
50
+ semantics, and artifact extraction remain owned by TypeScript. Nested Python
51
+ dictionaries and model instances are recursively converted from snake_case to
52
+ the camelCase wire contract.
53
+
42
54
  Blocker explainability follows the same rule. TypeScript owns blocker creation,
43
55
  runner interruption decisions, and existing-tab diagnostics. Python exposes
44
56
  `explain_blocker(...)` over the backend blocker dictionary and is checked against
@@ -49,7 +61,7 @@ the shared `blocker-explanation-profiles.json` and
49
61
 
50
62
  Python does not reinterpret attachment paths. It sends the path string to the Node backend, and the backend validates the path against its own host operating system. Attachment paths must be absolute on the backend host. On macOS/Linux/WSL backends, use POSIX paths such as `/example/user/file.pdf` or `/mnt/c/example/user/file.pdf`. On Windows backends, use fully qualified paths such as `C:\Users\you\file.pdf` or UNC paths such as `\\server\share\file.pdf`. Drive-relative paths, root-relative paths, and Windows-looking paths sent to a POSIX backend are rejected before filesystem access.
51
63
 
52
- Python exposes the backend-visible `files.preflight` command as `chatgpt.files.preflight(...)`. It returns the same `CommandResult` as TypeScript and can be decoded with `FilePreflightData` when callers want typed metadata. The command validates paths, readability, file-vs-directory status, size limits, duplicate basenames, duplicate resolved paths, zero-byte files, and extension-based MIME/category guesses without opening ChatGPT or reading file contents for MIME detection.
64
+ Python exposes the backend-visible `files.preflight` command as `chatgpt.files.preflight(...)`. It returns the same `CommandResult` as TypeScript and can be decoded with `FilePreflightData` when callers want typed metadata. The command validates paths, readability, file-vs-directory status, size limits, duplicate basenames, duplicate resolved paths, zero-byte files, and extension-based MIME/category guesses without opening ChatGPT or reading file contents for MIME detection. Zero-byte files are blocked before browser interaction. Optional `include_hashes=True` / wire `includeHashes: true` adds SHA-256 metadata to `FilePreflightFile.sha256` for local diagnostics; file contents are never returned.
53
65
 
54
66
  ## Project Sources
55
67
 
@@ -121,7 +133,7 @@ Report results may include `metaPath` plus `integrity` metadata. Python exposes
121
133
 
122
134
  ## Streaming
123
135
 
124
- `stream-*.ndjson` fixtures are milestone streams. They are not token streams. The final `completed` event contains a normal `ChatGPTRunResult` wire object, including blockers when the run cannot proceed.
136
+ `stream-*.ndjson` fixtures are milestone streams. They are not token streams. The final `completed` event contains a normal `ChatGPTRunResult` wire object, including blockers when the run cannot proceed. Running partial assistant text uses `message.in_progress` / `message_in_progress`; completion-confirmed assistant output uses `message.completed` / `message_completed`.
125
137
 
126
138
  ```python
127
139
  from codex_chatgpt_control import ChatGPTStreamEvent
@@ -161,7 +173,7 @@ Python is a native SDK facade over the local backend protocol. The initial brows
161
173
 
162
174
  - `dist/codex-chatgpt-control-backend.mjs` is the stdio backend bundle.
163
175
  - `BackendClient` and `StdioBackendTransport` keep Python backend calls long-lived.
164
- - `NodeSidecarTransport.run(...)` remains as a compatibility wrapper over backend `runner.run`.
176
+ - `NodeSidecarTransport.run(...)` remains as a compatibility wrapper over backend `runner.run`. By default each call spawns and tears down its own backend subprocess; use it as a context manager (or call `open()`/`close()`) to reuse one persistent backend process across calls in multi-command workflows. Transport-level failures close the persistent session; protocol-level errors keep it open.
165
177
  - Ordinary-shell smoke passes when browser-required calls return structured `browser_bridge_unavailable`.
166
178
  - Browser-bridge runtime smoke remains explicitly gated because it can operate a real ChatGPT session.
167
179
 
@@ -199,7 +211,7 @@ python scripts/live_smoke.py --mode browser-bridge
199
211
  When the live backend is hosted inside the Codex Chrome plugin runtime, do not test bridge availability from a normal shell or an unbootstrapped Node REPL. First initialize the Chrome runtime:
200
212
 
201
213
  ```js
202
- const { setupBrowserRuntime } = await import("/example/user/.codex/plugins/cache/openai-bundled/chrome/26.602.40724/scripts/browser-client.mjs");
214
+ const { setupBrowserRuntime } = await import("/example/user/.codex/plugins/cache/openai-bundled/chrome/latest/scripts/browser-client.mjs");
203
215
  await setupBrowserRuntime({ globals: globalThis });
204
216
  globalThis.browser = await agent.browsers.get("extension");
205
217
  ```
@@ -6,8 +6,12 @@ Accepted fields:
6
6
 
7
7
  - `input`
8
8
  - `thread`
9
+ - `existingTab`
10
+ - `preferExistingTab`
11
+ - `experience`
12
+ - `configuration`
9
13
  - `attachments`
10
- - `mode`
14
+ - `mode` (legacy compatibility)
11
15
  - `tools`
12
16
  - `text.format`
13
17
  - `stream: false`
@@ -20,9 +24,16 @@ Rejected API-only fields return `status: "unsupported"` before any prompt is sub
20
24
  const response = await chatgpt.responses.create({
21
25
  input: "Summarize the latest plan.",
22
26
  thread: { type: "conversationId", conversationId: "abc-123" },
27
+ experience: "chat",
28
+ configuration: { intelligence: "High" },
23
29
  text: { format: "markdown" },
24
30
  stream: false
25
31
  });
26
32
  ```
27
33
 
34
+ `experience` and `configuration` represent visible product controls, not API
35
+ model selection. Configuration is strict through the runner plan and must
36
+ verify the visible postcondition. Existing callers may continue to pass
37
+ `mode`; new callers should prefer the surface-aware fields.
38
+
28
39
  Use `chatgpt.runner.run()` for lower-level browser-control workflows, multi-step command planning, attachments, downloads, reports, and explicit interruption handling.
@@ -33,7 +33,7 @@ Use `chatgpt.explainBlocker(result)` or Python `explain_blocker(result)` when re
33
33
  Do not conclude that Chrome or the extension is broken from a plain shell result, or from checking `globalThis.agent` before the Chrome plugin runtime is initialized. For a true Codex Chrome-plugin live run, bootstrap the runtime first:
34
34
 
35
35
  ```js
36
- const { setupBrowserRuntime } = await import("/example/user/.codex/plugins/cache/openai-bundled/chrome/26.602.40724/scripts/browser-client.mjs");
36
+ const { setupBrowserRuntime } = await import("/example/user/.codex/plugins/cache/openai-bundled/chrome/latest/scripts/browser-client.mjs");
37
37
  await setupBrowserRuntime({ globals: globalThis });
38
38
  globalThis.browser = await agent.browsers.get("extension");
39
39
  ```
@@ -69,6 +69,17 @@ The ChatGPT UI changed or the page loaded an unexpected surface. Return visible
69
69
 
70
70
  Runner results surface this as `interruptions[0].type === "selector_drift"` with `blocker.candidates` when visible candidates were available. Do not retry automatically; ask the user or update selectors.
71
71
 
72
+ For Chat/Work drift, capture only scoped capability evidence: composer label,
73
+ main controls, configuration rows/options, locale, URL shape, selector profile,
74
+ and observation date. Do not retain sidebar thread titles or conversation
75
+ content. An unknown profile, missing axis, or unverified postcondition is a
76
+ normal blocker during staged account/region/workspace rollouts.
77
+
78
+ If `work.start` returns `work_new_task_control_not_found`, a task is already
79
+ loaded and the SDK refused to append. Pass `newTask: false` only when the user
80
+ intends to continue that exact task. If a Work submission returns partial or
81
+ timeout, use `work.status`, `work.wait`, or `work.readLatest`; do not resubmit.
82
+
72
83
  ## Existing Tab Not Found Or Ambiguous
73
84
 
74
85
  When `existingTab` targeting cannot select one already-open tab, inspect `blocker.diagnostics.existingTab` or the rendered blocker explanation. Diagnostics are metadata-only: requested target, whether user-open tabs were available, candidate tab IDs, URLs, titles, conversation IDs, omitted candidate count, and mismatch reason. They must not include page text or chat content.
@@ -90,9 +101,24 @@ Agent-facing remediation text should name both settings:
90
101
 
91
102
  Attachment paths are validated against the backend host's operating system. If a Windows-looking path is rejected on macOS/Linux, do not retry with the same string. Convert it to the backend host's real path, for example `/mnt/c/example/user/file.pdf` for a WSL/Linux backend. Drive-relative paths like `C:Users\you\file.pdf`, root-relative paths like `\tmp\file.pdf`, and empty or relative paths are always rejected.
92
103
 
104
+ ## Empty Or Stripped Attachments
105
+
106
+ Zero-byte files are blocked by `files.preflight` before browser upload because
107
+ ChatGPT rejects empty attachments with a generic help-center error. If a user
108
+ reports that manual upload works but automated upload fails, compare local and
109
+ browser-side metadata instead of guessing:
110
+
111
+ 1. Run `files.preflight({ paths, includeHashes: true })` and inspect `bytes`
112
+ plus `sha256` for the backend-visible file.
113
+ 2. Run `files.attach({ paths, includeDiagnostics: true, includeHashes: true })`
114
+ and inspect `data.diagnostics.browserInput.files[].size` when available.
115
+ 3. If preflight `bytes` is zero, investigate the source path, generation race,
116
+ cloud placeholder, or host/container path mapping. If preflight bytes are
117
+ nonzero but browser-side size is zero, investigate the Chrome handoff.
118
+
93
119
  ## Clipboard Unavailable
94
120
 
95
- `response.copy` falls back to DOM text extraction when the macOS system clipboard does not change.
121
+ `response.copy` falls back to DOM text extraction when the system clipboard does not change. Clipboard reads use `pbpaste` on macOS, PowerShell `Get-Clipboard` on Windows, and `xclip`/`xsel`/`wl-paste` on Linux; hosts without any of these tools always use the DOM fallback.
96
122
 
97
123
  ## Flattened Or Unreadable Response Capture
98
124
 
@@ -100,16 +126,33 @@ Use `readLatest({ format: "markdown" })`, `copyLatest()`, or the default SDK `re
100
126
 
101
127
  ## Long Responses Return `partial`
102
128
 
103
- Long Pro, Thinking, Deep Research, or file-backed answers can take longer than the default wait window. Treat `status: "partial"` and `data.complete: false` as an incomplete capture even when `output_text` is non-empty. Re-run `messages.wait(...)` on the same thread with a larger timeout, then call `readLatest(...)` or `copyLatest(...)` only after completion is confirmed.
129
+ Long Pro, Thinking, Deep Research, or file-backed answers can take longer than the default wait window. Treat `status: "partial"` and `data.complete: false` as an incomplete capture even when `output_text` is non-empty. Check `data.completionState` and `data.generationActive`; `completionState: "generating"` or `generationActive: true` means ChatGPT is still running and the prompt must not be resubmitted. For repeated polling, prefer `messages.status({ maxPreviewChars })` for a cheap snapshot, or `messages.wait({ responseContent: "metadata", ... })` so Codex receives compact status metadata instead of the same growing partial answer body. Re-run `messages.wait(...)` on the same thread (with a larger timeout if needed) until completion is confirmed, then call `readLatest(...)` or `copyLatest(...)` once.
104
130
 
105
131
  Recommended long-answer wait:
106
132
 
107
133
  ```ts
108
- wait: { timeoutMs: 600000, stableMs: 8000, pollMs: 1000 }
134
+ await chatgpt.messages.wait({
135
+ timeoutMs: 45_000,
136
+ stableMs: 2_000,
137
+ pollMs: 1_000,
138
+ mode: "deep_research",
139
+ responseContent: "metadata"
140
+ });
141
+
142
+ const final = await chatgpt.messages.readLatest({ format: "markdown" });
109
143
  ```
110
144
 
111
145
  Active generation may appear as a visible or accessible-name control such as `Stop answering`, `Stop generating`, or `Stop streaming`. Stopped generation may appear as `Stopped thinking`. Treat all of those as incomplete states.
112
146
 
147
+ When the outer host tool-call ceiling is short, poll in bounded chunks instead of issuing a single very long wait:
148
+
149
+ ```ts
150
+ const status = await chatgpt.messages.status({ maxPreviewChars: 400 });
151
+ if (status.data?.completionState === "generating" || status.data?.generationActive) {
152
+ await chatgpt.messages.wait({ timeoutMs: 30000, stableMs: 8000, pollMs: 1000 });
153
+ }
154
+ ```
155
+
113
156
  `maxChars` limits captured text returned by the SDK. It does not control ChatGPT generation. When set and clipping occurs, inspect result warnings and `data.captureLimit`, then rerun without `maxChars` for full capture.
114
157
 
115
158
  ## Response Branch Ambiguity
@@ -144,6 +187,6 @@ Doctor also supports opt-in scenario checks:
144
187
 
145
188
  - `existing_tab`: claims only the requested already-open tab target by default and reports `existing_tab_not_found` / `existing_tab_ambiguous` diagnostics without opening a replacement tab unless `existingTab.ifMissing` explicitly allows that.
146
189
  - `artifacts`: verifies current-page artifact selector/download/asset support without requesting generation.
147
- - `file_preflight`: validates supplied local file paths without opening ChatGPT or attempting upload. It reports path count, total bytes, duplicate/zero-byte warnings, and extension-based MIME/category metadata; fatal local file problems map to the same structured blockers as `files.preflight`.
148
- - `localization`: checks locale-label registry readiness and English canonical labels without changing the ChatGPT account language; it is not yet proof of full localized selector coverage.
190
+ - `file_preflight`: validates supplied local file paths without opening ChatGPT or attempting upload. It reports path count, total bytes, duplicate warnings, zero-byte blockers, and extension-based MIME/category metadata; fatal local file problems map to the same structured blockers as `files.preflight`.
191
+ - `localization`: checks locale-label registry readiness and English canonical labels without changing the ChatGPT account language. It reports running-state label coverage (`stopControl` and `stoppedAssistant`) separately so English-only, partial, and complete live-capture coverage are visible. It is not yet proof of full localized selector coverage.
149
192
  - `reports`: checks redacted-report policy and existing destination writability when possible without writing a report.