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
@@ -0,0 +1,338 @@
1
+ import { countPageMessages } from "../dom/messages.js";
2
+ import { localeLabels } from "../dom/locale-labels.js";
3
+ import { resultError, resultOk } from "../errors.js";
4
+ import { listLatestArtifacts } from "./artifacts.js";
5
+ import { applyConfiguration } from "./configuration.js";
6
+ import { contextFromPage } from "./context.js";
7
+ import { openExperience, detectExperience } from "./experience.js";
8
+ import { attachFiles } from "./files.js";
9
+ import { askMessage, composeMessage, messageStatus, readLatest, submitMessage, waitForMessage } from "./messages.js";
10
+ import { ensurePage } from "./session.js";
11
+ const NEW_WORK_LABELS = localeLabels.newWork;
12
+ export async function startWork(env, args) {
13
+ const prompt = args.prompt.trim();
14
+ if (prompt.length === 0) {
15
+ return {
16
+ ok: false,
17
+ status: "unsupported",
18
+ warnings: [],
19
+ blocker: {
20
+ kind: "selector_drift",
21
+ code: "empty_work_prompt",
22
+ fieldPath: "prompt",
23
+ message: "work.start requires a non-empty visible prompt.",
24
+ resumable: false
25
+ },
26
+ context: { timestamp: new Date().toISOString() }
27
+ };
28
+ }
29
+ const boot = await ensurePage(env);
30
+ if (!boot.ok) {
31
+ return boot;
32
+ }
33
+ const page = env.page;
34
+ try {
35
+ const surface = await openExperience(env, {
36
+ experience: "work",
37
+ ...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs })
38
+ });
39
+ if (!surface.ok) {
40
+ return forwardCommandFailure(surface);
41
+ }
42
+ if (args.newTask !== false) {
43
+ const fresh = await ensureBlankWorkTask(env, args.timeoutMs);
44
+ if (!fresh.ok) {
45
+ return forwardCommandFailure(fresh);
46
+ }
47
+ }
48
+ const baselineTurnCount = await countPageMessages(page).catch(() => undefined);
49
+ const baselineAssistantTurnCount = await countPageMessages(page, "assistant").catch(() => undefined);
50
+ let configuration;
51
+ if (args.configuration !== undefined) {
52
+ const applied = await applyConfiguration(env, {
53
+ experience: "work",
54
+ desired: args.configuration,
55
+ strict: true,
56
+ ...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs })
57
+ });
58
+ if (!applied.ok || applied.data === undefined) {
59
+ return forwardCommandFailure(applied);
60
+ }
61
+ configuration = applied.data;
62
+ }
63
+ if ((args.files?.length ?? 0) > 0) {
64
+ const attach = await attachFiles(env, {
65
+ paths: args.files ?? [],
66
+ ...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs })
67
+ });
68
+ if (!attach.ok) {
69
+ return forwardCommandFailure(attach);
70
+ }
71
+ }
72
+ const compose = await composeMessage(env, {
73
+ text: prompt,
74
+ mode: "replace",
75
+ ...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs })
76
+ });
77
+ if (!compose.ok) {
78
+ return forwardCommandFailure(compose);
79
+ }
80
+ const submitArgs = {
81
+ text: prompt,
82
+ ...(baselineTurnCount === undefined ? {} : { previousTurnCount: baselineTurnCount }),
83
+ ...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs })
84
+ };
85
+ const submit = await submitMessage(env, submitArgs);
86
+ if (!submit.ok || submit.data === undefined) {
87
+ return forwardCommandFailure(submit);
88
+ }
89
+ const task = await workTaskRef(env, baselineTurnCount, baselineAssistantTurnCount);
90
+ const data = { task, submitted: submit.data };
91
+ if (configuration !== undefined)
92
+ data.configuration = configuration;
93
+ let waitResult;
94
+ if (args.wait === true || typeof args.wait === "object") {
95
+ const waitArgs = typeof args.wait === "object" ? { ...args.wait } : {};
96
+ if (baselineTurnCount !== undefined)
97
+ waitArgs.afterTurnCount = baselineTurnCount;
98
+ if (baselineAssistantTurnCount !== undefined)
99
+ waitArgs.afterAssistantTurnCount = baselineAssistantTurnCount;
100
+ waitResult = await waitForMessage(env, waitArgs);
101
+ if (waitResult.data !== undefined)
102
+ data.wait = waitResult.data;
103
+ if (!waitResult.ok && waitResult.status !== "partial") {
104
+ return forwardWorkFailure(waitResult, data);
105
+ }
106
+ }
107
+ if (args.read === true || typeof args.read === "object") {
108
+ const read = await readLatest(env, typeof args.read === "object" ? args.read : {});
109
+ if (!read.ok || read.data === undefined) {
110
+ return forwardWorkFailure(read, data);
111
+ }
112
+ data.response = read.data;
113
+ }
114
+ if (waitResult !== undefined && !waitResult.ok) {
115
+ const partial = {
116
+ ok: false,
117
+ status: "partial",
118
+ data,
119
+ warnings: [
120
+ ...waitResult.warnings,
121
+ "The Work task was submitted exactly once, but completion was not verified."
122
+ ],
123
+ context: await workContext(env)
124
+ };
125
+ const outputText = data.response?.text ?? waitResult.output_text;
126
+ if (outputText !== undefined)
127
+ partial.output_text = outputText;
128
+ return partial;
129
+ }
130
+ return resultOk(data, await workContext(env, surface.data?.selectorProfile), ["Work task submission uses matching-turn recovery and will not blindly resubmit the prompt."]);
131
+ }
132
+ catch (error) {
133
+ return resultError(error instanceof Error ? error : new Error(String(error)), await workContext(env));
134
+ }
135
+ }
136
+ export async function workStatus(env, args = {}) {
137
+ const ready = await requireWork(env);
138
+ if (!ready.ok)
139
+ return forwardCommandFailure(ready);
140
+ const message = await messageStatus(env, args);
141
+ if (!message.ok || message.data === undefined) {
142
+ return forwardCommandFailure(message);
143
+ }
144
+ const data = {
145
+ experience: "work",
146
+ task: await workTaskRef(env, undefined, undefined),
147
+ message: message.data
148
+ };
149
+ const warnings = [...message.warnings];
150
+ if (args.includeArtifacts === true) {
151
+ const artifacts = await listLatestArtifacts(env, {});
152
+ if (artifacts.ok && artifacts.data !== undefined) {
153
+ data.artifacts = artifacts.data;
154
+ warnings.push(...artifacts.warnings);
155
+ }
156
+ else {
157
+ warnings.push(`Work artifact status was unavailable: ${artifacts.blocker?.message ?? artifacts.error?.message ?? artifacts.status}`);
158
+ }
159
+ }
160
+ return resultOk(data, {
161
+ ...message.context,
162
+ experience: "work",
163
+ ...(ready.data?.selectorProfile === undefined
164
+ ? {}
165
+ : { selectorProfile: ready.data.selectorProfile })
166
+ }, warnings);
167
+ }
168
+ export async function waitForWork(env, args = {}) {
169
+ const ready = await requireWork(env);
170
+ if (!ready.ok)
171
+ return forwardCommandFailure(ready);
172
+ return markWorkResult(await waitForMessage(env, args), ready.data?.selectorProfile);
173
+ }
174
+ export async function steerWork(env, args) {
175
+ const ready = await requireWork(env);
176
+ if (!ready.ok)
177
+ return forwardCommandFailure(ready);
178
+ return markWorkResult(await askMessage(env, {
179
+ text: args.prompt,
180
+ wait: args.wait ?? false,
181
+ read: args.read ?? false,
182
+ ...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs })
183
+ }), ready.data?.selectorProfile);
184
+ }
185
+ export async function readLatestWork(env, args = {}) {
186
+ const ready = await requireWork(env);
187
+ if (!ready.ok)
188
+ return forwardCommandFailure(ready);
189
+ return markWorkResult(await readLatest(env, args), ready.data?.selectorProfile);
190
+ }
191
+ async function ensureBlankWorkTask(env, timeoutMs) {
192
+ const page = env.page;
193
+ const currentTurns = await countPageMessages(page).catch(() => 0);
194
+ if (currentTurns === 0) {
195
+ return resultOk({ fresh: true }, await workContext(env));
196
+ }
197
+ if (await clickNewWorkControl(page)) {
198
+ const started = Date.now();
199
+ const timeout = timeoutMs ?? 30000;
200
+ while (Date.now() - started < timeout) {
201
+ await page.waitForTimeout?.(150);
202
+ if (await countPageMessages(page).catch(() => currentTurns) === 0) {
203
+ return resultOk({ fresh: true }, await workContext(env));
204
+ }
205
+ }
206
+ return {
207
+ ok: false,
208
+ status: "blocked",
209
+ warnings: [],
210
+ blocker: {
211
+ kind: "selector_drift",
212
+ code: "work_new_task_unverified",
213
+ message: "The new Work task control was clicked, but the visible conversation did not reset to a blank task.",
214
+ resumable: true
215
+ },
216
+ context: await workContext(env)
217
+ };
218
+ }
219
+ return {
220
+ ok: false,
221
+ status: "blocked",
222
+ warnings: [],
223
+ blocker: {
224
+ kind: "selector_drift",
225
+ code: "work_new_task_control_not_found",
226
+ message: "A current Work task is loaded and no unique new-task control was found. Pass newTask: false only when intentionally steering the current task.",
227
+ candidates: NEW_WORK_LABELS.map(label => ({ label })),
228
+ resumable: true
229
+ },
230
+ context: await workContext(env)
231
+ };
232
+ }
233
+ async function clickNewWorkControl(page) {
234
+ for (const label of NEW_WORK_LABELS) {
235
+ for (const role of ["button", "link"]) {
236
+ if (await clickIfUnique(page.getByRole?.(role, { name: label, exact: true }))) {
237
+ return true;
238
+ }
239
+ }
240
+ }
241
+ return false;
242
+ }
243
+ async function requireWork(env) {
244
+ const detected = await detectExperience(env);
245
+ if (detected.ok && detected.data?.experience === "work") {
246
+ return resultOk({
247
+ experience: "work",
248
+ selectorProfile: detected.data.selectorProfile
249
+ }, detected.context);
250
+ }
251
+ return {
252
+ ok: false,
253
+ status: "unsupported",
254
+ warnings: detected.warnings,
255
+ blocker: {
256
+ kind: "selector_drift",
257
+ code: "work_surface_required",
258
+ fieldPath: "experience",
259
+ message: `This command requires the visible Work surface; detected ${detected.data?.experience ?? "unknown"}.`,
260
+ resumable: true
261
+ },
262
+ context: detected.context
263
+ };
264
+ }
265
+ async function workTaskRef(env, baselineTurnCount, baselineAssistantTurnCount) {
266
+ const context = await workContext(env);
267
+ const task = {};
268
+ if (context.url !== undefined)
269
+ task.url = context.url;
270
+ if (context.conversationId !== undefined)
271
+ task.conversationId = context.conversationId;
272
+ if (context.title !== undefined)
273
+ task.title = context.title;
274
+ if (baselineTurnCount !== undefined)
275
+ task.baselineTurnCount = baselineTurnCount;
276
+ if (baselineAssistantTurnCount !== undefined)
277
+ task.baselineAssistantTurnCount = baselineAssistantTurnCount;
278
+ return task;
279
+ }
280
+ async function workContext(env, selectorProfile) {
281
+ return contextFromPage(env.page, {
282
+ experience: "work",
283
+ ...(selectorProfile === undefined ? {} : { selectorProfile })
284
+ });
285
+ }
286
+ function markWorkResult(result, selectorProfile) {
287
+ return {
288
+ ...result,
289
+ context: {
290
+ ...result.context,
291
+ experience: "work",
292
+ ...(selectorProfile === undefined ? {} : { selectorProfile })
293
+ }
294
+ };
295
+ }
296
+ function forwardWorkFailure(result, data) {
297
+ const forwarded = {
298
+ ok: false,
299
+ status: result.status,
300
+ data,
301
+ warnings: result.warnings,
302
+ context: result.context
303
+ };
304
+ if (result.output_text !== undefined)
305
+ forwarded.output_text = result.output_text;
306
+ if (result.blocker !== undefined)
307
+ forwarded.blocker = result.blocker;
308
+ if (result.error !== undefined)
309
+ forwarded.error = result.error;
310
+ return forwarded;
311
+ }
312
+ function forwardCommandFailure(result) {
313
+ const forwarded = {
314
+ ok: false,
315
+ status: result.status,
316
+ warnings: result.warnings,
317
+ context: result.context
318
+ };
319
+ if (result.output_text !== undefined)
320
+ forwarded.output_text = result.output_text;
321
+ if (result.reportPath !== undefined)
322
+ forwarded.reportPath = result.reportPath;
323
+ if (result.blocker !== undefined)
324
+ forwarded.blocker = result.blocker;
325
+ if (result.error !== undefined)
326
+ forwarded.error = result.error;
327
+ if (result.steps !== undefined)
328
+ forwarded.steps = result.steps;
329
+ return forwarded;
330
+ }
331
+ async function clickIfUnique(locator) {
332
+ if (locator?.count === undefined || locator.click === undefined)
333
+ return false;
334
+ if (await locator.count().catch(() => 0) !== 1)
335
+ return false;
336
+ await locator.click();
337
+ return true;
338
+ }
@@ -4,5 +4,12 @@ export type AssistantGenerationState = {
4
4
  stopped: boolean;
5
5
  signals: string[];
6
6
  };
7
+ /**
8
+ * Neutral fallback used when generation state cannot be inspected.
9
+ *
10
+ * This means "no active/stopped signal observed"; it is not evidence that a
11
+ * response is complete.
12
+ */
13
+ export declare const EMPTY_GENERATION_STATE: AssistantGenerationState;
7
14
  export declare function readAssistantGenerationState(page: PageLike): Promise<AssistantGenerationState>;
8
15
  export declare function latestAssistantTurnHasResponseActions(page: PageLike): Promise<boolean>;
@@ -1,6 +1,12 @@
1
1
  import { copyResponseButtons } from "./selectors.js";
2
2
  import { localeLabels } from "./locale-labels.js";
3
- const EMPTY_GENERATION_STATE = {
3
+ /**
4
+ * Neutral fallback used when generation state cannot be inspected.
5
+ *
6
+ * This means "no active/stopped signal observed"; it is not evidence that a
7
+ * response is complete.
8
+ */
9
+ export const EMPTY_GENERATION_STATE = {
4
10
  active: false,
5
11
  stopped: false,
6
12
  signals: []
@@ -0,0 +1,3 @@
1
+ export declare function normalizeForLabelMatch(text: string): string;
2
+ export declare function visibleLabelMatches(label: string, wanted: string): boolean;
3
+ export declare function isShortLatinToken(value: string): boolean;
@@ -0,0 +1,25 @@
1
+ import { normalizeWhitespace } from "./visible-text.js";
2
+ export function normalizeForLabelMatch(text) {
3
+ return normalizeWhitespace(text.normalize("NFKC")).toLocaleLowerCase();
4
+ }
5
+ export function visibleLabelMatches(label, wanted) {
6
+ const normalizedLabel = normalizeForLabelMatch(label);
7
+ const normalizedWanted = normalizeForLabelMatch(wanted);
8
+ if (normalizedWanted.length === 0) {
9
+ return false;
10
+ }
11
+ if (normalizedLabel === normalizedWanted) {
12
+ return true;
13
+ }
14
+ if (isShortLatinToken(normalizedWanted)) {
15
+ return new RegExp(`(^|[^\\p{L}\\p{N}])${escapeRegExp(normalizedWanted)}([^\\p{L}\\p{N}]|$)`, "iu")
16
+ .test(normalizedLabel);
17
+ }
18
+ return normalizedLabel.includes(normalizedWanted);
19
+ }
20
+ export function isShortLatinToken(value) {
21
+ return value.length <= 3 && /^[a-z0-9]+$/i.test(value);
22
+ }
23
+ function escapeRegExp(value) {
24
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25
+ }
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export declare const am: {
10
10
  composerTextbox: string[];
@@ -17,6 +17,12 @@ export declare const am: {
17
17
  addPhotosFilesMenuItem: string[];
18
18
  copyResponse: string[];
19
19
  modeLabels: string[];
20
+ modeOptions: {
21
+ instant: string[];
22
+ medium: string[];
23
+ high: string[];
24
+ extraHigh: string[];
25
+ };
20
26
  modeOpenerExtra: string[];
21
27
  tools: {
22
28
  web_search: string[];
@@ -25,4 +31,5 @@ export declare const am: {
25
31
  };
26
32
  signedInMarkers: string[];
27
33
  responseActions: string[];
34
+ stopControl: string[];
28
35
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export const am = {
10
10
  composerTextbox: ["ከChatGPT ጋር ይወያዩ"],
@@ -17,6 +17,12 @@ export const am = {
17
17
  addPhotosFilesMenuItem: ["ፎቶዎችን እና ፋይሎችን ያክሉ"],
18
18
  copyResponse: ["ምላሹን ይቅዱ"],
19
19
  modeLabels: ["ፈጣን", "መካከለኛ", "ከፍተኛ", "እጅግ ከፍተኛ"],
20
+ modeOptions: {
21
+ instant: ["ፈጣን"],
22
+ medium: ["መካከለኛ"],
23
+ high: ["ከፍተኛ"],
24
+ extraHigh: ["እጅግ ከፍተኛ"],
25
+ },
20
26
  modeOpenerExtra: ["ያዋቅሩ"],
21
27
  tools: {
22
28
  web_search: ["የድር ፍለጋ"],
@@ -25,4 +31,5 @@ export const am = {
25
31
  },
26
32
  signedInMarkers: ["አዲስ ውይይት", "ውይይቶችን ፈልግ", "የቅርብ ጊዜዎች", "ላይብረሪ", "ፕሮጀክቶች", "ከChatGPT ጋር ይወያዩ"],
27
33
  responseActions: ["ምላሹን ይቅዱ"],
34
+ stopControl: ["መልስ መስጠት አቁም"],
28
35
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export declare const ar: {
10
10
  composerTextbox: string[];
@@ -17,6 +17,13 @@ export declare const ar: {
17
17
  addPhotosFilesMenuItem: string[];
18
18
  copyResponse: string[];
19
19
  modeLabels: string[];
20
+ modeOptions: {
21
+ instant: string[];
22
+ medium: string[];
23
+ high: string[];
24
+ extraHigh: string[];
25
+ pro: string[];
26
+ };
20
27
  modeOpenerExtra: string[];
21
28
  tools: {
22
29
  web_search: string[];
@@ -25,4 +32,5 @@ export declare const ar: {
25
32
  };
26
33
  signedInMarkers: string[];
27
34
  responseActions: string[];
35
+ stopControl: string[];
28
36
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export const ar = {
10
10
  composerTextbox: ["الدردشة مع ChatGPT"],
@@ -17,6 +17,13 @@ export const ar = {
17
17
  addPhotosFilesMenuItem: ["إضافة صور وملفات"],
18
18
  copyResponse: ["نسخ إجابة"],
19
19
  modeLabels: ["فوري", "متوسط", "عالي", "مكثف جدًا", "احترافي"],
20
+ modeOptions: {
21
+ instant: ["فوري"],
22
+ medium: ["متوسط"],
23
+ high: ["عالي"],
24
+ extraHigh: ["مكثف جدًا"],
25
+ pro: ["احترافي"],
26
+ },
20
27
  modeOpenerExtra: ["تكوين"],
21
28
  tools: {
22
29
  web_search: ["البحث في الويب"],
@@ -25,4 +32,5 @@ export const ar = {
25
32
  },
26
33
  signedInMarkers: ["دردشة جديدة", "البحث في الدردشات", "المحادثات الأخيرة", "المكتبة", "المشروعات", "الدردشة مع ChatGPT"],
27
34
  responseActions: ["نسخ إجابة"],
35
+ stopControl: ["إيقاف الرد"],
28
36
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export declare const bg: {
10
10
  composerTextbox: string[];
@@ -17,6 +17,13 @@ export declare const bg: {
17
17
  addPhotosFilesMenuItem: string[];
18
18
  copyResponse: string[];
19
19
  modeLabels: string[];
20
+ modeOptions: {
21
+ instant: string[];
22
+ medium: string[];
23
+ high: string[];
24
+ extraHigh: string[];
25
+ pro: string[];
26
+ };
20
27
  modeOpenerExtra: string[];
21
28
  tools: {
22
29
  web_search: string[];
@@ -25,4 +32,5 @@ export declare const bg: {
25
32
  };
26
33
  signedInMarkers: string[];
27
34
  responseActions: string[];
35
+ stopControl: string[];
28
36
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export const bg = {
10
10
  composerTextbox: ["Чат с ChatGPT"],
@@ -17,6 +17,13 @@ export const bg = {
17
17
  addPhotosFilesMenuItem: ["Добавяне на снимки и файлове"],
18
18
  copyResponse: ["Копирайте отговора"],
19
19
  modeLabels: ["Мигновен", "Среден", "Висок", "Много високо", "Про"],
20
+ modeOptions: {
21
+ instant: ["Мигновен"],
22
+ medium: ["Среден"],
23
+ high: ["Висок"],
24
+ extraHigh: ["Много високо"],
25
+ pro: ["Про"],
26
+ },
20
27
  modeOpenerExtra: ["Конфигурирайте"],
21
28
  tools: {
22
29
  web_search: ["Търсене в интернет"],
@@ -25,4 +32,5 @@ export const bg = {
25
32
  },
26
33
  signedInMarkers: ["Нов чат", "Търсене на чатове", "Скорошни чатове", "Каталог", "Проекти", "Чат с ChatGPT"],
27
34
  responseActions: ["Копирайте отговора"],
35
+ stopControl: ["Спри отговора"],
28
36
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export declare const bn: {
10
10
  composerTextbox: string[];
@@ -17,6 +17,13 @@ export declare const bn: {
17
17
  addPhotosFilesMenuItem: string[];
18
18
  copyResponse: string[];
19
19
  modeLabels: string[];
20
+ modeOptions: {
21
+ instant: string[];
22
+ medium: string[];
23
+ high: string[];
24
+ extraHigh: string[];
25
+ pro: string[];
26
+ };
20
27
  modeOpenerExtra: string[];
21
28
  tools: {
22
29
  web_search: string[];
@@ -25,4 +32,5 @@ export declare const bn: {
25
32
  };
26
33
  signedInMarkers: string[];
27
34
  responseActions: string[];
35
+ stopControl: string[];
28
36
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export const bn = {
10
10
  composerTextbox: ["যে কোন কিছু জিজ্ঞেস করুন…"],
@@ -17,6 +17,13 @@ export const bn = {
17
17
  addPhotosFilesMenuItem: ["ফটো এবং ফাইল আপলোড করুন"],
18
18
  copyResponse: ["উত্তর কপি করুন"],
19
19
  modeLabels: ["তাৎক্ষণিক", "মাঝারি", "উচ্চ", "অতি উচ্চ", "প্রো"],
20
+ modeOptions: {
21
+ instant: ["তাৎক্ষণিক"],
22
+ medium: ["মাঝারি"],
23
+ high: ["উচ্চ"],
24
+ extraHigh: ["অতি উচ্চ"],
25
+ pro: ["প্রো"],
26
+ },
20
27
  modeOpenerExtra: ["কনফিগার করুন..."],
21
28
  tools: {
22
29
  web_search: ["ওয়েব সন্ধান"],
@@ -25,4 +32,5 @@ export const bn = {
25
32
  },
26
33
  signedInMarkers: ["নতুন চ্যাট", "চ্যাট খুঁজুন", "সাম্প্রতিক", "চ্যাটের ইতিহাস", "প্রোজেক্ট", "যে কোন কিছু জিজ্ঞেস করুন…"],
27
34
  responseActions: ["উত্তর কপি করুন"],
35
+ stopControl: ["উত্তর থামান"],
28
36
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export declare const bs: {
10
10
  composerTextbox: string[];
@@ -17,6 +17,12 @@ export declare const bs: {
17
17
  addPhotosFilesMenuItem: string[];
18
18
  copyResponse: string[];
19
19
  modeLabels: string[];
20
+ modeOptions: {
21
+ instant: string[];
22
+ medium: string[];
23
+ high: string[];
24
+ extraHigh: string[];
25
+ };
20
26
  modeOpenerExtra: string[];
21
27
  tools: {
22
28
  web_search: string[];
@@ -25,4 +31,5 @@ export declare const bs: {
25
31
  };
26
32
  signedInMarkers: string[];
27
33
  responseActions: string[];
34
+ stopControl: string[];
28
35
  };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Some non-Intelligence surfaces may still fall back to English + `selector_drift`.
6
6
  *
7
- * Intelligence picker labels updated 2026-06-10 from a visible ChatGPT Pro session.
7
+ * Intelligence picker labels updated 2026-06-10 and stop-control labels updated 2026-06-15 from visible ChatGPT Pro sessions.
8
8
  */
9
9
  export const bs = {
10
10
  composerTextbox: ["Razgovarajte pomoću ChatGPT-a"],
@@ -17,6 +17,12 @@ export const bs = {
17
17
  addPhotosFilesMenuItem: ["Dodaj slike i datoteke"],
18
18
  copyResponse: ["Kopiraj odgovor"],
19
19
  modeLabels: ["Brzo", "Srednji", "Visoko", "Vrlo visoko"],
20
+ modeOptions: {
21
+ instant: ["Brzo"],
22
+ medium: ["Srednji"],
23
+ high: ["Visoko"],
24
+ extraHigh: ["Vrlo visoko"],
25
+ },
20
26
  modeOpenerExtra: ["Podesi"],
21
27
  tools: {
22
28
  web_search: ["Internet pretraga"],
@@ -25,4 +31,5 @@ export const bs = {
25
31
  },
26
32
  signedInMarkers: ["Novi razgovor", "Pretraži razgovore", "Nedavno", "Biblioteka", "Projekti", "Razgovarajte pomoću ChatGPT-a"],
27
33
  responseActions: ["Kopiraj odgovor"],
34
+ stopControl: ["Zaustavi odgovaranje"],
28
35
  };