codex-chatgpt-control 0.1.0-alpha.1 → 0.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +55 -0
  3. package/contracts/v1/fixtures/backend-capabilities.json +7 -0
  4. package/contracts/v1/fixtures/blocker-explanation-profiles.json +114 -0
  5. package/contracts/v1/fixtures/command-descriptors.json +192 -15
  6. package/contracts/v1/fixtures/doctor-bridge-upload.json +8 -0
  7. package/contracts/v1/fixtures/doctor-scenario-preflight.json +108 -0
  8. package/contracts/v1/fixtures/existing-tab-diagnostics-blocker.json +57 -0
  9. package/contracts/v1/fixtures/files-preflight-success.json +32 -0
  10. package/contracts/v1/fixtures/help-root.json +1 -1
  11. package/contracts/v1/fixtures/output-json-parse-success.json +17 -2
  12. package/contracts/v1/fixtures/project-sources-plan-add.json +71 -0
  13. package/contracts/v1/fixtures/read-latest-clipped.json +31 -0
  14. package/contracts/v1/fixtures/reports-create-redacted.json +15 -0
  15. package/contracts/v1/fixtures/responses-basic-success.json +11 -0
  16. package/contracts/v1/fixtures/run-basic-success.json +15 -0
  17. package/contracts/v1/fixtures/run-browser-bridge-blocker.json +2 -2
  18. package/contracts/v1/fixtures/run-timeout-partial.json +2 -2
  19. package/contracts/v1/fixtures/runner-budget-blocker.json +1 -1
  20. package/contracts/v1/fixtures/stream-blocked.ndjson +1 -1
  21. package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
  22. package/contracts/v1/manifest.json +30 -0
  23. package/contracts/v1/parity-suite.json +72 -6
  24. package/contracts/v1/schemas/backend-request.schema.json +3 -0
  25. package/contracts/v1/schemas/command-result.schema.json +48 -0
  26. package/contracts/v1/schemas/response.schema.json +28 -0
  27. package/contracts/v1/schemas/run-result.schema.json +84 -0
  28. package/contracts/v1/surface-drift-policy.json +210 -0
  29. package/dist/codex-chatgpt-control-backend.mjs +5137 -488
  30. package/dist/codex-chatgpt-control.bundle.mjs +5001 -286
  31. package/dist/src/backend/client.d.ts +6 -1
  32. package/dist/src/backend/client.js +5 -0
  33. package/dist/src/backend/protocol.d.ts +1 -1
  34. package/dist/src/backend/protocol.js +7 -0
  35. package/dist/src/backend/session.js +14 -0
  36. package/dist/src/browser/attach.js +128 -20
  37. package/dist/src/browser/downloads.js +2 -1
  38. package/dist/src/browser/page-state.js +6 -3
  39. package/dist/src/client.d.ts +16 -1
  40. package/dist/src/client.js +86 -7
  41. package/dist/src/commands/artifacts.d.ts +5 -0
  42. package/dist/src/commands/artifacts.js +432 -0
  43. package/dist/src/commands/context.js +5 -2
  44. package/dist/src/commands/doctor.d.ts +10 -2
  45. package/dist/src/commands/doctor.js +242 -14
  46. package/dist/src/commands/files.d.ts +2 -1
  47. package/dist/src/commands/files.js +352 -18
  48. package/dist/src/commands/messages.d.ts +2 -1
  49. package/dist/src/commands/messages.js +232 -40
  50. package/dist/src/commands/modes.js +202 -20
  51. package/dist/src/commands/project-sources.d.ts +10 -0
  52. package/dist/src/commands/project-sources.js +643 -0
  53. package/dist/src/commands/registry.js +76 -14
  54. package/dist/src/commands/reports.d.ts +9 -0
  55. package/dist/src/commands/reports.js +83 -6
  56. package/dist/src/commands/response-actions.js +33 -0
  57. package/dist/src/commands/sequence.js +14 -0
  58. package/dist/src/commands/threads.js +2 -1
  59. package/dist/src/commands/timeouts.d.ts +2 -0
  60. package/dist/src/commands/timeouts.js +18 -0
  61. package/dist/src/diagnostics/blockers.d.ts +54 -0
  62. package/dist/src/diagnostics/blockers.js +299 -0
  63. package/dist/src/dom/artifacts.d.ts +7 -0
  64. package/dist/src/dom/artifacts.js +198 -0
  65. package/dist/src/dom/generation-state.d.ts +8 -0
  66. package/dist/src/dom/generation-state.js +103 -0
  67. package/dist/src/dom/locale/am.d.ts +28 -0
  68. package/dist/src/dom/locale/am.js +28 -0
  69. package/dist/src/dom/locale/ar.d.ts +28 -0
  70. package/dist/src/dom/locale/ar.js +28 -0
  71. package/dist/src/dom/locale/bg.d.ts +28 -0
  72. package/dist/src/dom/locale/bg.js +28 -0
  73. package/dist/src/dom/locale/bn.d.ts +28 -0
  74. package/dist/src/dom/locale/bn.js +28 -0
  75. package/dist/src/dom/locale/bs.d.ts +28 -0
  76. package/dist/src/dom/locale/bs.js +28 -0
  77. package/dist/src/dom/locale/ca.d.ts +28 -0
  78. package/dist/src/dom/locale/ca.js +28 -0
  79. package/dist/src/dom/locale/cs.d.ts +28 -0
  80. package/dist/src/dom/locale/cs.js +28 -0
  81. package/dist/src/dom/locale/da.d.ts +28 -0
  82. package/dist/src/dom/locale/da.js +28 -0
  83. package/dist/src/dom/locale/de.d.ts +30 -0
  84. package/dist/src/dom/locale/de.js +30 -0
  85. package/dist/src/dom/locale/el.d.ts +28 -0
  86. package/dist/src/dom/locale/el.js +28 -0
  87. package/dist/src/dom/locale/en.d.ts +50 -0
  88. package/dist/src/dom/locale/en.js +56 -0
  89. package/dist/src/dom/locale/es-419.d.ts +28 -0
  90. package/dist/src/dom/locale/es-419.js +28 -0
  91. package/dist/src/dom/locale/es-ES.d.ts +28 -0
  92. package/dist/src/dom/locale/es-ES.js +28 -0
  93. package/dist/src/dom/locale/et.d.ts +28 -0
  94. package/dist/src/dom/locale/et.js +28 -0
  95. package/dist/src/dom/locale/fa.d.ts +28 -0
  96. package/dist/src/dom/locale/fa.js +28 -0
  97. package/dist/src/dom/locale/fi.d.ts +28 -0
  98. package/dist/src/dom/locale/fi.js +28 -0
  99. package/dist/src/dom/locale/fr-CA.d.ts +29 -0
  100. package/dist/src/dom/locale/fr-CA.js +29 -0
  101. package/dist/src/dom/locale/fr-FR.d.ts +30 -0
  102. package/dist/src/dom/locale/fr-FR.js +30 -0
  103. package/dist/src/dom/locale/gu.d.ts +28 -0
  104. package/dist/src/dom/locale/gu.js +28 -0
  105. package/dist/src/dom/locale/hi.d.ts +28 -0
  106. package/dist/src/dom/locale/hi.js +28 -0
  107. package/dist/src/dom/locale/hr.d.ts +28 -0
  108. package/dist/src/dom/locale/hr.js +28 -0
  109. package/dist/src/dom/locale/hu.d.ts +28 -0
  110. package/dist/src/dom/locale/hu.js +28 -0
  111. package/dist/src/dom/locale/hy.d.ts +28 -0
  112. package/dist/src/dom/locale/hy.js +28 -0
  113. package/dist/src/dom/locale/id.d.ts +28 -0
  114. package/dist/src/dom/locale/id.js +28 -0
  115. package/dist/src/dom/locale/index.d.ts +79 -0
  116. package/dist/src/dom/locale/index.js +202 -0
  117. package/dist/src/dom/locale/is.d.ts +28 -0
  118. package/dist/src/dom/locale/is.js +28 -0
  119. package/dist/src/dom/locale/it.d.ts +27 -0
  120. package/dist/src/dom/locale/it.js +27 -0
  121. package/dist/src/dom/locale/ja.d.ts +27 -0
  122. package/dist/src/dom/locale/ja.js +27 -0
  123. package/dist/src/dom/locale/ka.d.ts +28 -0
  124. package/dist/src/dom/locale/ka.js +28 -0
  125. package/dist/src/dom/locale/kk.d.ts +28 -0
  126. package/dist/src/dom/locale/kk.js +28 -0
  127. package/dist/src/dom/locale/kn.d.ts +28 -0
  128. package/dist/src/dom/locale/kn.js +28 -0
  129. package/dist/src/dom/locale/ko.d.ts +28 -0
  130. package/dist/src/dom/locale/ko.js +28 -0
  131. package/dist/src/dom/locale/lt.d.ts +28 -0
  132. package/dist/src/dom/locale/lt.js +28 -0
  133. package/dist/src/dom/locale/lv.d.ts +28 -0
  134. package/dist/src/dom/locale/lv.js +28 -0
  135. package/dist/src/dom/locale/mk.d.ts +28 -0
  136. package/dist/src/dom/locale/mk.js +28 -0
  137. package/dist/src/dom/locale/ml.d.ts +28 -0
  138. package/dist/src/dom/locale/ml.js +28 -0
  139. package/dist/src/dom/locale/mn.d.ts +28 -0
  140. package/dist/src/dom/locale/mn.js +28 -0
  141. package/dist/src/dom/locale/mr.d.ts +28 -0
  142. package/dist/src/dom/locale/mr.js +28 -0
  143. package/dist/src/dom/locale/ms.d.ts +28 -0
  144. package/dist/src/dom/locale/ms.js +28 -0
  145. package/dist/src/dom/locale/my.d.ts +28 -0
  146. package/dist/src/dom/locale/my.js +28 -0
  147. package/dist/src/dom/locale/nb.d.ts +28 -0
  148. package/dist/src/dom/locale/nb.js +28 -0
  149. package/dist/src/dom/locale/nl.d.ts +28 -0
  150. package/dist/src/dom/locale/nl.js +28 -0
  151. package/dist/src/dom/locale/pa.d.ts +28 -0
  152. package/dist/src/dom/locale/pa.js +28 -0
  153. package/dist/src/dom/locale/pl.d.ts +28 -0
  154. package/dist/src/dom/locale/pl.js +28 -0
  155. package/dist/src/dom/locale/pt-BR.d.ts +28 -0
  156. package/dist/src/dom/locale/pt-BR.js +28 -0
  157. package/dist/src/dom/locale/pt-PT.d.ts +28 -0
  158. package/dist/src/dom/locale/pt-PT.js +28 -0
  159. package/dist/src/dom/locale/ro.d.ts +28 -0
  160. package/dist/src/dom/locale/ro.js +28 -0
  161. package/dist/src/dom/locale/ru.d.ts +28 -0
  162. package/dist/src/dom/locale/ru.js +28 -0
  163. package/dist/src/dom/locale/sk.d.ts +28 -0
  164. package/dist/src/dom/locale/sk.js +28 -0
  165. package/dist/src/dom/locale/sl.d.ts +28 -0
  166. package/dist/src/dom/locale/sl.js +28 -0
  167. package/dist/src/dom/locale/so.d.ts +28 -0
  168. package/dist/src/dom/locale/so.js +28 -0
  169. package/dist/src/dom/locale/sq.d.ts +28 -0
  170. package/dist/src/dom/locale/sq.js +28 -0
  171. package/dist/src/dom/locale/sr.d.ts +28 -0
  172. package/dist/src/dom/locale/sr.js +28 -0
  173. package/dist/src/dom/locale/sv.d.ts +28 -0
  174. package/dist/src/dom/locale/sv.js +28 -0
  175. package/dist/src/dom/locale/sw.d.ts +28 -0
  176. package/dist/src/dom/locale/sw.js +28 -0
  177. package/dist/src/dom/locale/ta.d.ts +28 -0
  178. package/dist/src/dom/locale/ta.js +28 -0
  179. package/dist/src/dom/locale/te.d.ts +28 -0
  180. package/dist/src/dom/locale/te.js +28 -0
  181. package/dist/src/dom/locale/th.d.ts +28 -0
  182. package/dist/src/dom/locale/th.js +28 -0
  183. package/dist/src/dom/locale/tl.d.ts +28 -0
  184. package/dist/src/dom/locale/tl.js +28 -0
  185. package/dist/src/dom/locale/tr.d.ts +28 -0
  186. package/dist/src/dom/locale/tr.js +28 -0
  187. package/dist/src/dom/locale/types.d.ts +45 -0
  188. package/dist/src/dom/locale/types.js +1 -0
  189. package/dist/src/dom/locale/uk.d.ts +28 -0
  190. package/dist/src/dom/locale/uk.js +28 -0
  191. package/dist/src/dom/locale/ur.d.ts +28 -0
  192. package/dist/src/dom/locale/ur.js +28 -0
  193. package/dist/src/dom/locale/vi.d.ts +28 -0
  194. package/dist/src/dom/locale/vi.js +28 -0
  195. package/dist/src/dom/locale/zh-HK.d.ts +28 -0
  196. package/dist/src/dom/locale/zh-HK.js +28 -0
  197. package/dist/src/dom/locale/zh-Hans.d.ts +28 -0
  198. package/dist/src/dom/locale/zh-Hans.js +28 -0
  199. package/dist/src/dom/locale/zh-TW.d.ts +29 -0
  200. package/dist/src/dom/locale/zh-TW.js +29 -0
  201. package/dist/src/dom/locale-labels.d.ts +26 -0
  202. package/dist/src/dom/locale-labels.js +26 -0
  203. package/dist/src/dom/menus.d.ts +6 -0
  204. package/dist/src/dom/menus.js +34 -8
  205. package/dist/src/dom/message-format.d.ts +2 -1
  206. package/dist/src/dom/message-format.js +67 -10
  207. package/dist/src/dom/messages.d.ts +2 -1
  208. package/dist/src/dom/messages.js +2 -4
  209. package/dist/src/dom/selectors.d.ts +1 -0
  210. package/dist/src/dom/selectors.js +42 -15
  211. package/dist/src/index.d.ts +5 -0
  212. package/dist/src/index.js +5 -0
  213. package/dist/src/platform/local-paths.d.ts +4 -0
  214. package/dist/src/platform/local-paths.js +20 -0
  215. package/dist/src/runner/interruptions.js +15 -4
  216. package/dist/src/runner/responses.js +18 -1
  217. package/dist/src/runner/result.js +15 -0
  218. package/dist/src/runner/resume.js +1 -0
  219. package/dist/src/runner/types.d.ts +3 -0
  220. package/dist/src/safety/risk.d.ts +7 -0
  221. package/dist/src/safety/risk.js +7 -0
  222. package/dist/src/safety/untrusted-output.d.ts +73 -0
  223. package/dist/src/safety/untrusted-output.js +199 -0
  224. package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +1 -0
  225. package/dist/src/scripts/apply-intelligence-locale-captures.js +184 -0
  226. package/dist/src/scripts/capture-intelligence-locales.d.ts +25 -0
  227. package/dist/src/scripts/capture-intelligence-locales.js +669 -0
  228. package/dist/src/scripts/check-surface-drift.d.ts +1 -0
  229. package/dist/src/scripts/check-surface-drift.js +11 -0
  230. package/dist/src/scripts/live-smoke/scenarios.js +84 -0
  231. package/dist/src/scripts/locale-capture/language-coverage.d.ts +10 -0
  232. package/dist/src/scripts/locale-capture/language-coverage.js +39 -0
  233. package/dist/src/testing/surface-drift.d.ts +66 -0
  234. package/dist/src/testing/surface-drift.js +363 -0
  235. package/dist/src/types.d.ts +201 -2
  236. package/package.json +2 -1
  237. package/references/backend-protocol.md +93 -1
  238. package/references/language-coverage.md +142 -0
  239. package/references/localization.md +247 -0
  240. package/references/python-parity.md +63 -1
  241. package/references/safety.md +2 -0
  242. package/references/troubleshooting.md +58 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Urdu (ur). Captured 2026-06-09 against a live chatgpt.com session
3
+ * (html lang=ur, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export const ur = {
10
+ composerTextbox: ["کوئی بھی چیز پوچھیں۔۔۔"],
11
+ sendButton: ["پرامپٹ بھیجیں"],
12
+ searchChatsButton: ["چیٹس تلاش کریں"],
13
+ searchChatsPlaceholder: ["چیٹس تلاش کریں..."],
14
+ newChat: ["نئی چیٹ"],
15
+ addFilesButton: ["فائلیں وغیرہ اپ لوڈ کریں"],
16
+ addFilesOpenerCandidates: ["فائلیں وغیرہ اپ لوڈ کریں"],
17
+ addPhotosFilesMenuItem: ["تصویریں اور فائلیں شامل کریں"],
18
+ copyResponse: ["جواب کاپی کریں"],
19
+ modeLabels: ["فوری", "اوسط", "اعلیٰ", "انتہائی اعلیٰ"],
20
+ modeOpenerExtra: ["کنفیگر کریں..."],
21
+ tools: {
22
+ web_search: ["ویب پر تلاش"],
23
+ deep_research: ["ڈیپ ریسرچ"],
24
+ create_image: ["تصویر بنائیں"],
25
+ },
26
+ signedInMarkers: ["نئی چیٹ", "چیٹس تلاش کریں", "حالیہ", "چیٹ ہسٹری", "پراجیکٹس", "کوئی بھی چیز پوچھیں۔۔۔"],
27
+ responseActions: ["جواب کاپی کریں"],
28
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Vietnamese (vi-VN). Captured 2026-06-09 against a live chatgpt.com session
3
+ * (html lang=vi-VN, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export declare const vi: {
10
+ composerTextbox: string[];
11
+ sendButton: string[];
12
+ searchChatsButton: string[];
13
+ searchChatsPlaceholder: string[];
14
+ newChat: string[];
15
+ addFilesButton: string[];
16
+ addFilesOpenerCandidates: string[];
17
+ addPhotosFilesMenuItem: string[];
18
+ copyResponse: string[];
19
+ modeLabels: string[];
20
+ modeOpenerExtra: string[];
21
+ tools: {
22
+ web_search: string[];
23
+ deep_research: string[];
24
+ create_image: string[];
25
+ };
26
+ signedInMarkers: string[];
27
+ responseActions: string[];
28
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Vietnamese (vi-VN). Captured 2026-06-09 against a live chatgpt.com session
3
+ * (html lang=vi-VN, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export const vi = {
10
+ composerTextbox: ["Trò chuyện với ChatGPT"],
11
+ sendButton: ["Gửi lời nhắc"],
12
+ searchChatsButton: ["Tìm kiếm đoạn chat"],
13
+ searchChatsPlaceholder: ["Tìm kiếm đoạn chat..."],
14
+ newChat: ["Đoạn chat mới"],
15
+ addFilesButton: ["Thêm tệp và nhiều tính năng khác"],
16
+ addFilesOpenerCandidates: ["Thêm tệp và nhiều tính năng khác"],
17
+ addPhotosFilesMenuItem: ["Thêm ảnh và tệp"],
18
+ copyResponse: ["Sao chép phản hồi"],
19
+ modeLabels: ["Tức thì", "Trung bình", "Cao", "Rất cao"],
20
+ modeOpenerExtra: ["Định cấu hình"],
21
+ tools: {
22
+ web_search: ["Tìm kiếm trên mạng"],
23
+ deep_research: ["Nghiên cứu chuyên sâu"],
24
+ create_image: ["Tạo hình ảnh"],
25
+ },
26
+ signedInMarkers: ["Đoạn chat mới", "Tìm kiếm đoạn chat", "Gần đây", "Thư viện", "Dự án", "Trò chuyện với ChatGPT"],
27
+ responseActions: ["Sao chép phản hồi"],
28
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Chinese — Traditional, Hong Kong (zh-HK). Captured 2026-06-09 against a live chatgpt.com
3
+ * session (html lang=zh-HK, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export declare const zhHK: {
10
+ composerTextbox: string[];
11
+ sendButton: string[];
12
+ searchChatsButton: string[];
13
+ searchChatsPlaceholder: string[];
14
+ newChat: string[];
15
+ addFilesButton: string[];
16
+ addFilesOpenerCandidates: string[];
17
+ addPhotosFilesMenuItem: string[];
18
+ copyResponse: string[];
19
+ modeLabels: string[];
20
+ modeOpenerExtra: string[];
21
+ tools: {
22
+ web_search: string[];
23
+ deep_research: string[];
24
+ create_image: string[];
25
+ };
26
+ signedInMarkers: string[];
27
+ responseActions: string[];
28
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Chinese — Traditional, Hong Kong (zh-HK). Captured 2026-06-09 against a live chatgpt.com
3
+ * session (html lang=zh-HK, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export const zhHK = {
10
+ composerTextbox: ["與 ChatGPT 對話"],
11
+ sendButton: ["傳送提示"],
12
+ searchChatsButton: ["搜尋對話"],
13
+ searchChatsPlaceholder: ["搜尋對話…"],
14
+ newChat: ["新對話"],
15
+ addFilesButton: ["上載檔案和其他内容"],
16
+ addFilesOpenerCandidates: ["上載檔案和其他内容"],
17
+ addPhotosFilesMenuItem: ["加入相片和檔案"],
18
+ copyResponse: ["複製回覆"],
19
+ modeLabels: ["即時", "均衡", "高", "極高", "專業"],
20
+ modeOpenerExtra: ["設定"],
21
+ tools: {
22
+ web_search: ["網絡搜尋"],
23
+ deep_research: ["深度研究"],
24
+ create_image: ["創作圖像"],
25
+ },
26
+ signedInMarkers: ["新對話", "搜尋對話", "最近對話", "圖庫", "項目", "與 ChatGPT 對話"],
27
+ responseActions: ["複製回覆"],
28
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Chinese Simplified (zh-Hans / zh-CN). Captured 2026-06-09 against a live chatgpt.com session
3
+ * (html lang=zh-CN, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export declare const zhHans: {
10
+ composerTextbox: string[];
11
+ sendButton: string[];
12
+ searchChatsButton: string[];
13
+ searchChatsPlaceholder: string[];
14
+ newChat: string[];
15
+ addFilesButton: string[];
16
+ addFilesOpenerCandidates: string[];
17
+ addPhotosFilesMenuItem: string[];
18
+ copyResponse: string[];
19
+ modeLabels: string[];
20
+ modeOpenerExtra: string[];
21
+ tools: {
22
+ web_search: string[];
23
+ deep_research: string[];
24
+ create_image: string[];
25
+ };
26
+ signedInMarkers: string[];
27
+ responseActions: string[];
28
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Chinese Simplified (zh-Hans / zh-CN). Captured 2026-06-09 against a live chatgpt.com session
3
+ * (html lang=zh-CN, Google Translate confirmed off).
4
+ *
5
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
+ *
7
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
8
+ */
9
+ export const zhHans = {
10
+ composerTextbox: ["有问题,尽管问"],
11
+ sendButton: ["发送提示"],
12
+ searchChatsButton: ["搜索聊天"],
13
+ searchChatsPlaceholder: ["搜索聊天…"],
14
+ newChat: ["新聊天"],
15
+ addFilesButton: ["添加文件等"],
16
+ addFilesOpenerCandidates: ["添加文件等"],
17
+ addPhotosFilesMenuItem: ["添加照片和文件"],
18
+ copyResponse: ["复制回复"],
19
+ modeLabels: ["极速", "均衡", "高级", "超高", "专业"],
20
+ modeOpenerExtra: ["配置…"],
21
+ tools: {
22
+ web_search: ["网页搜索"],
23
+ deep_research: ["深度研究"],
24
+ create_image: ["创建图片"],
25
+ },
26
+ signedInMarkers: ["新聊天", "搜索聊天", "最近", "历史聊天记录", "项目", "有问题,尽管问"],
27
+ responseActions: ["复制回复"],
28
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Chinese — Traditional, Taiwan (zh-TW). Captured 2026-06-09 against a live chatgpt.com
3
+ * session (html lang=zh-TW, Google Translate confirmed off). Distinct from zh-HK
4
+ * (e.g. 傳送提示詞 vs 傳送提示, 網頁搜尋 vs 網絡搜尋, 複製回應 vs 複製回覆).
5
+ *
6
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
7
+ *
8
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
9
+ */
10
+ export declare const zhTW: {
11
+ composerTextbox: string[];
12
+ sendButton: string[];
13
+ searchChatsButton: string[];
14
+ searchChatsPlaceholder: string[];
15
+ newChat: string[];
16
+ addFilesButton: string[];
17
+ addFilesOpenerCandidates: string[];
18
+ addPhotosFilesMenuItem: string[];
19
+ copyResponse: string[];
20
+ modeLabels: string[];
21
+ modeOpenerExtra: string[];
22
+ tools: {
23
+ web_search: string[];
24
+ deep_research: string[];
25
+ create_image: string[];
26
+ };
27
+ signedInMarkers: string[];
28
+ responseActions: string[];
29
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Chinese — Traditional, Taiwan (zh-TW). Captured 2026-06-09 against a live chatgpt.com
3
+ * session (html lang=zh-TW, Google Translate confirmed off). Distinct from zh-HK
4
+ * (e.g. 傳送提示詞 vs 傳送提示, 網頁搜尋 vs 網絡搜尋, 複製回應 vs 複製回覆).
5
+ *
6
+ * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
7
+ *
8
+ * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
9
+ */
10
+ export const zhTW = {
11
+ composerTextbox: ["與 ChatGPT 對話"],
12
+ sendButton: ["傳送提示詞"],
13
+ searchChatsButton: ["搜尋對話"],
14
+ searchChatsPlaceholder: ["搜尋聊天..."],
15
+ newChat: ["新對話"],
16
+ addFilesButton: ["新增檔案等更多功能"],
17
+ addFilesOpenerCandidates: ["新增檔案等更多功能"],
18
+ addPhotosFilesMenuItem: ["新增照片和檔案"],
19
+ copyResponse: ["複製回應"],
20
+ modeLabels: ["即時", "中等", "高", "超高", "專業"],
21
+ modeOpenerExtra: ["設定"],
22
+ tools: {
23
+ web_search: ["網頁搜尋"],
24
+ deep_research: ["深入研究"],
25
+ create_image: ["創作圖像"],
26
+ },
27
+ signedInMarkers: ["新對話", "搜尋對話", "最近的對話", "圖庫", "專案", "與 ChatGPT 對話"],
28
+ responseActions: ["複製回應"],
29
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Centralized, locale-sensitive ChatGPT UI strings — barrel re-export.
3
+ *
4
+ * Every entry is an ORDERED list of candidate strings. English is canonical and MUST
5
+ * stay first: it is also the public API contract for mode/tool selection (callers pass
6
+ * `effort: "Thinking"`, `tool: "web_search"`, etc.). Matchers iterate the whole list,
7
+ * so localizing the SDK is just appending verified strings to these arrays — no selector
8
+ * or command code needs to change.
9
+ *
10
+ * RULES
11
+ * - Do NOT add unverified translations. A locale string is only valid once it has been
12
+ * observed in a real localized ChatGPT session. Guesses are worse than nothing because
13
+ * they mask the `selector_drift` blocker that is the designed recovery path.
14
+ * - Keys are stable logical ids. The localized display text goes in the array; the key
15
+ * never changes.
16
+ * - These are the visible/accessible-name surfaces only. Structural anchors (roles,
17
+ * element ids like `#composer-plus-btn`, `a[href^='/c/']`, `data-message-author-role`)
18
+ * are language-agnostic and live in the selectors directly.
19
+ *
20
+ * Structural-only blocker patterns stay literal in `safety/blockers.ts` on purpose:
21
+ * numeric/HTTP codes (`404`), our own bridge error fragments (`fileChooser.setFiles`,
22
+ * `permission denied`), and the modal heuristic are not ChatGPT-localized UI text.
23
+ *
24
+ * To add a new language, see `src/dom/locale/index.ts`.
25
+ */
26
+ export { localeLabels, anyLabelPattern, escapeRegExp } from "./locale/index.js";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Centralized, locale-sensitive ChatGPT UI strings — barrel re-export.
3
+ *
4
+ * Every entry is an ORDERED list of candidate strings. English is canonical and MUST
5
+ * stay first: it is also the public API contract for mode/tool selection (callers pass
6
+ * `effort: "Thinking"`, `tool: "web_search"`, etc.). Matchers iterate the whole list,
7
+ * so localizing the SDK is just appending verified strings to these arrays — no selector
8
+ * or command code needs to change.
9
+ *
10
+ * RULES
11
+ * - Do NOT add unverified translations. A locale string is only valid once it has been
12
+ * observed in a real localized ChatGPT session. Guesses are worse than nothing because
13
+ * they mask the `selector_drift` blocker that is the designed recovery path.
14
+ * - Keys are stable logical ids. The localized display text goes in the array; the key
15
+ * never changes.
16
+ * - These are the visible/accessible-name surfaces only. Structural anchors (roles,
17
+ * element ids like `#composer-plus-btn`, `a[href^='/c/']`, `data-message-author-role`)
18
+ * are language-agnostic and live in the selectors directly.
19
+ *
20
+ * Structural-only blocker patterns stay literal in `safety/blockers.ts` on purpose:
21
+ * numeric/HTTP codes (`404`), our own bridge error fragments (`fileChooser.setFiles`,
22
+ * `permission denied`), and the modal heuristic are not ChatGPT-localized UI text.
23
+ *
24
+ * To add a new language, see `src/dom/locale/index.ts`.
25
+ */
26
+ export { localeLabels, anyLabelPattern, escapeRegExp } from "./locale/index.js";
@@ -2,6 +2,12 @@ import type { PageLike } from "../types.js";
2
2
  export type MenuItem = {
3
3
  label: string;
4
4
  normalized: string;
5
+ role?: string;
6
+ checked?: boolean;
7
+ expanded?: boolean;
8
+ hasPopup?: boolean;
9
+ testId?: string;
10
+ ariaLabel?: string;
5
11
  };
6
12
  export declare function extractMenuItemsFromText(text: string): MenuItem[];
7
13
  export declare function enumerateVisibleMenuItems(page: PageLike): Promise<MenuItem[]>;
@@ -9,23 +9,49 @@ export function extractMenuItemsFromText(text) {
9
9
  export async function enumerateVisibleMenuItems(page) {
10
10
  if (typeof page.evaluate === "function") {
11
11
  const labels = await page.evaluate(() => {
12
+ const toItem = (node) => {
13
+ const element = node;
14
+ const label = (element.innerText ?? element.textContent ?? "").replace(/\s+/g, " ").trim();
15
+ const item = { label };
16
+ const role = element.getAttribute("role");
17
+ if (role !== null)
18
+ item.role = role;
19
+ const checked = element.getAttribute("aria-checked");
20
+ if (checked === "true")
21
+ item.checked = true;
22
+ if (checked === "false")
23
+ item.checked = false;
24
+ const expanded = element.getAttribute("aria-expanded");
25
+ if (expanded === "true")
26
+ item.expanded = true;
27
+ if (expanded === "false")
28
+ item.expanded = false;
29
+ if (element.getAttribute("aria-haspopup") === "menu")
30
+ item.hasPopup = true;
31
+ const testId = element.getAttribute("data-testid");
32
+ if (testId !== null)
33
+ item.testId = testId;
34
+ const ariaLabel = element.getAttribute("aria-label");
35
+ if (ariaLabel !== null)
36
+ item.ariaLabel = ariaLabel;
37
+ return item;
38
+ };
12
39
  const roleItems = Array.from(document.querySelectorAll("[role='menuitem'], [role='menuitemradio'], [role='option']"))
13
- .map(node => node.innerText ?? node.textContent ?? "")
14
- .filter(Boolean);
40
+ .map(toItem)
41
+ .filter(item => item.label.length > 0);
15
42
  if (roleItems.length > 0) {
16
- return { labels: roleItems, split: false };
43
+ return { items: roleItems, labels: [], split: false };
17
44
  }
18
45
  const menus = Array.from(document.querySelectorAll("[role='menu'], [role='listbox'], [data-radix-popper-content-wrapper]"))
19
46
  .map(node => node.innerText ?? node.textContent ?? "")
20
47
  .filter(Boolean);
21
- return { labels: menus, split: true };
48
+ return { items: [], labels: menus, split: true };
22
49
  });
23
50
  return labels.split
24
51
  ? labels.labels.flatMap(label => extractMenuItemsFromText(label))
25
- : labels.labels
26
- .map(label => normalizeWhitespace(label))
27
- .filter(Boolean)
28
- .map(label => ({ label, normalized: normalizeLabel(label) }));
52
+ : labels.items
53
+ .map(item => ({ ...item, label: normalizeWhitespace(item.label), normalized: normalizeLabel(item.label) }))
54
+ .filter(item => item.label.length > 0);
29
55
  }
30
56
  return [];
31
57
  }
@@ -1,10 +1,11 @@
1
- import type { ResponseAction, ResponseBlock, ResponseBranchState, ResponseCaptureFidelity, ResponseCaptureSource, ResponseCitation, ResponseCodeBlock, ResponseFormat, ResponseTable } from "../types.js";
1
+ import type { ResponseAction, ResponseBlock, ResponseBranchState, ResponseCaptureLimit, ResponseCaptureFidelity, ResponseCaptureSource, ResponseCitation, ResponseCodeBlock, ResponseFormat, ResponseTable } from "../types.js";
2
2
  export type NormalizedResponseFormat = Exclude<ResponseFormat, "text">;
3
3
  export type FormattedMessageContent = {
4
4
  text: string;
5
5
  format: NormalizedResponseFormat;
6
6
  source?: ResponseCaptureSource;
7
7
  fidelity?: ResponseCaptureFidelity;
8
+ captureLimit?: ResponseCaptureLimit;
8
9
  warnings?: string[];
9
10
  markdown?: string;
10
11
  visibleText?: string;
@@ -51,20 +51,36 @@ export function formatMessageHtml(html, requestedFormat = "markdown", maxChars,
51
51
  const root = parseHtmlFragment(html);
52
52
  const meaningfulChildren = stripIgnorableNodes(root.children);
53
53
  const blocks = extractBlocks(meaningfulChildren);
54
- const markdown = clamp(blocksToMarkdown(blocks), maxChars);
55
- const visibleText = clamp(blocksToPlainText(blocks), maxChars);
56
- const normalizedText = clamp(normalizeWhitespace(visibleText), maxChars);
54
+ const rawMarkdown = blocksToMarkdown(blocks);
55
+ const rawVisibleText = blocksToPlainText(blocks);
56
+ const rawNormalizedText = normalizeWhitespace(rawVisibleText);
57
+ const markdownCapture = applyCaptureLimit(rawMarkdown, maxChars);
58
+ const visibleTextCapture = applyCaptureLimit(rawVisibleText, maxChars);
59
+ const normalizedTextCapture = applyCaptureLimit(rawNormalizedText, maxChars);
60
+ const markdown = markdownCapture.text;
61
+ const visibleText = visibleTextCapture.text;
62
+ const normalizedText = normalizedTextCapture.text;
57
63
  const citations = collectCitations(meaningfulChildren);
58
64
  const codeBlocks = blocks.flatMap(block => block.type === "code" ? [codeBlockFromBlock(block)] : []);
59
65
  const tables = blocks.flatMap(block => block.type === "table" ? [tableFromBlock(block)] : []);
60
66
  const metadata = extractResponseMetadata(metadataHtml ?? html);
67
+ const captureLimit = captureLimitForFormat(format, {
68
+ markdown: markdownCapture.captureLimit,
69
+ visibleText: visibleTextCapture.captureLimit,
70
+ normalizedText: normalizedTextCapture.captureLimit,
71
+ html: applyCaptureLimit(html, maxChars).captureLimit
72
+ });
61
73
  const content = {
62
74
  text: textForFormat(format, { markdown, visibleText, normalizedText, html }),
63
75
  format,
64
76
  source: "semantic_dom",
65
77
  fidelity: fidelityForDomFormat(format)
66
78
  };
79
+ if (captureLimit !== undefined)
80
+ content.captureLimit = captureLimit;
67
81
  const warnings = warningsForDomFormat(format);
82
+ if (captureLimit?.clipped === true)
83
+ warnings.push(captureLimitWarning(captureLimit));
68
84
  if (warnings.length > 0)
69
85
  content.warnings = warnings;
70
86
  if (format === "markdown" || format === "all")
@@ -98,15 +114,29 @@ export function formatMessageHtml(html, requestedFormat = "markdown", maxChars,
98
114
  }
99
115
  export function formatClipboardMarkdown(text, maxChars, requestedFormat = "markdown") {
100
116
  const format = normalizeResponseFormat(requestedFormat);
101
- const markdown = clamp(normalizeLineBreaks(text).trim(), maxChars);
117
+ const rawMarkdown = normalizeLineBreaks(text).trim();
118
+ const rawNormalizedText = normalizeWhitespace(rawMarkdown);
119
+ const markdownCapture = applyCaptureLimit(rawMarkdown, maxChars);
120
+ const normalizedTextCapture = applyCaptureLimit(rawNormalizedText, maxChars);
121
+ const markdown = markdownCapture.text;
102
122
  const visibleText = markdown;
103
- const normalizedText = clamp(normalizeWhitespace(markdown), maxChars);
123
+ const normalizedText = normalizedTextCapture.text;
124
+ const captureLimit = captureLimitForFormat(format, {
125
+ markdown: markdownCapture.captureLimit,
126
+ visibleText: markdownCapture.captureLimit,
127
+ normalizedText: normalizedTextCapture.captureLimit,
128
+ html: markdownCapture.captureLimit
129
+ });
104
130
  const content = {
105
131
  text: textForFormat(format, { markdown, visibleText, normalizedText, html: markdown }),
106
132
  format,
107
133
  source: "clipboard",
108
134
  fidelity: "clipboard_markdown"
109
135
  };
136
+ if (captureLimit !== undefined)
137
+ content.captureLimit = captureLimit;
138
+ if (captureLimit?.clipped === true)
139
+ content.warnings = [captureLimitWarning(captureLimit)];
110
140
  if (format === "markdown" || format === "all")
111
141
  content.markdown = markdown;
112
142
  if (format === "visible_text" || format === "all")
@@ -137,6 +167,38 @@ function warningsForDomFormat(format) {
137
167
  }
138
168
  return ["Markdown was reconstructed from visible DOM semantics; use response.copy for clipboard Markdown when exact copy fidelity is required."];
139
169
  }
170
+ function applyCaptureLimit(text, maxChars) {
171
+ if (maxChars === undefined) {
172
+ return { text };
173
+ }
174
+ const captureLimit = {
175
+ maxChars,
176
+ originalChars: text.length,
177
+ clipped: text.length > maxChars
178
+ };
179
+ return {
180
+ text: captureLimit.clipped ? text.slice(0, maxChars) : text,
181
+ captureLimit
182
+ };
183
+ }
184
+ function captureLimitForFormat(format, limits) {
185
+ switch (format) {
186
+ case "markdown":
187
+ return limits.markdown;
188
+ case "visible_text":
189
+ return limits.visibleText;
190
+ case "normalized_text":
191
+ return limits.normalizedText;
192
+ case "html":
193
+ return limits.html;
194
+ case "blocks":
195
+ case "all":
196
+ return limits.markdown;
197
+ }
198
+ }
199
+ function captureLimitWarning(captureLimit) {
200
+ return `Response captured text was clipped by maxChars=${captureLimit.maxChars} from ${captureLimit.originalChars} characters.`;
201
+ }
140
202
  function textForFormat(format, values) {
141
203
  switch (format) {
142
204
  case "markdown":
@@ -574,11 +636,6 @@ function normalizeInline(text) {
574
636
  .replace(/\s+([.,;:!?])/g, "$1")
575
637
  .trim();
576
638
  }
577
- function clamp(text, maxChars) {
578
- if (maxChars === undefined || text.length <= maxChars)
579
- return text;
580
- return text.slice(0, Math.max(0, maxChars));
581
- }
582
639
  function escapeMarkdownLinkText(text) {
583
640
  return text.replace(/]/g, "\\]");
584
641
  }
@@ -1,4 +1,4 @@
1
- import type { PageLike, ResponseAction, ResponseBlock, ResponseBranchState, ResponseCaptureFidelity, ResponseCaptureSource, ResponseCitation, ResponseCodeBlock, ResponseFormat, ResponseTable } from "../types.js";
1
+ import type { PageLike, ResponseAction, ResponseBlock, ResponseBranchState, ResponseCaptureLimit, ResponseCaptureFidelity, ResponseCaptureSource, ResponseCitation, ResponseCodeBlock, ResponseFormat, ResponseTable } from "../types.js";
2
2
  export type MessageRole = "user" | "assistant";
3
3
  export type ExtractedMessage = {
4
4
  role: MessageRole;
@@ -6,6 +6,7 @@ export type ExtractedMessage = {
6
6
  format: Exclude<ResponseFormat, "text">;
7
7
  source?: ResponseCaptureSource;
8
8
  fidelity?: ResponseCaptureFidelity;
9
+ captureLimit?: ResponseCaptureLimit;
9
10
  warnings?: string[];
10
11
  markdown?: string;
11
12
  visibleText?: string;
@@ -1,4 +1,5 @@
1
1
  import { extractRoleMessageHtml, formatMessageHtml, normalizeResponseFormat } from "./message-format.js";
2
+ import { localeLabels } from "./locale-labels.js";
2
3
  import { normalizeWhitespace } from "./visible-text.js";
3
4
  export function extractMessagesFromHtml(html, args = {}) {
4
5
  return extractRoleMessageHtml(html)
@@ -98,10 +99,7 @@ export function isTransientAssistantText(text) {
98
99
  .replace(/[.。…]+$/g, "")
99
100
  .trim()
100
101
  .toLowerCase();
101
- return normalized === "thinking"
102
- || normalized === "reasoning"
103
- || normalized === "searching"
104
- || normalized === "searching the web"
102
+ return localeLabels.transientAssistant.some(phrase => normalized === phrase.toLowerCase())
105
103
  || /^analyzing (?:the )?images?$/.test(normalized)
106
104
  || /^processing (?:the )?images?$/.test(normalized)
107
105
  || /^reading (?:the )?images?$/.test(normalized);
@@ -6,6 +6,7 @@ export declare const cssSelectors: {
6
6
  readonly conversationTurns: "[data-testid^='conversation-turn']";
7
7
  readonly hiddenFileInputs: "input[type='file']";
8
8
  readonly downloadControls: string;
9
+ readonly generatedArtifactDownloadControls: string;
9
10
  };
10
11
  export declare function composerTextbox(page: PageLike): LocatorLike;
11
12
  export declare function sendButton(page: PageLike): LocatorLike;