codex-chatgpt-control 0.3.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 (214) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +42 -2
  3. package/contracts/v1/fixtures/backend-capabilities.json +10 -0
  4. package/contracts/v1/fixtures/command-descriptors.json +257 -6
  5. package/contracts/v1/fixtures/describe-runner-run.json +5 -2
  6. package/contracts/v1/fixtures/doctor-scenario-preflight.json +34 -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/run-timeout-partial.json +15 -4
  10. package/contracts/v1/fixtures/stream-in-progress.ndjson +3 -0
  11. package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
  12. package/contracts/v1/fixtures/surface-chat-legacy.json +35 -0
  13. package/contracts/v1/fixtures/surface-chat-simplified.json +37 -0
  14. package/contracts/v1/fixtures/surface-sidebar-false-positive.json +29 -0
  15. package/contracts/v1/fixtures/surface-work-advanced.json +45 -0
  16. package/contracts/v1/fixtures/surface-work-basic.json +38 -0
  17. package/contracts/v1/fixtures/workflow-ask-success.json +8 -0
  18. package/contracts/v1/manifest.json +32 -1
  19. package/contracts/v1/parity-suite.json +113 -0
  20. package/contracts/v1/schemas/backend-request.schema.json +15 -0
  21. package/contracts/v1/schemas/manifest.schema.json +6 -3
  22. package/contracts/v1/schemas/surface-profile.schema.json +166 -0
  23. package/dist/codex-chatgpt-control-backend.mjs +2045 -227
  24. package/dist/codex-chatgpt-control.bundle.mjs +3817 -1960
  25. package/dist/src/backend/client.d.ts +26 -1
  26. package/dist/src/backend/client.js +23 -1
  27. package/dist/src/backend/protocol.d.ts +1 -1
  28. package/dist/src/backend/protocol.js +10 -0
  29. package/dist/src/backend/session.js +20 -0
  30. package/dist/src/browser/attach.d.ts +1 -0
  31. package/dist/src/browser/attach.js +3 -3
  32. package/dist/src/browser/page-state.js +5 -2
  33. package/dist/src/client.d.ts +31 -1
  34. package/dist/src/client.js +69 -14
  35. package/dist/src/commands/artifacts.js +1 -7
  36. package/dist/src/commands/configuration.d.ts +15 -0
  37. package/dist/src/commands/configuration.js +674 -0
  38. package/dist/src/commands/doctor.js +38 -1
  39. package/dist/src/commands/experience.d.ts +12 -0
  40. package/dist/src/commands/experience.js +288 -0
  41. package/dist/src/commands/files.js +1 -7
  42. package/dist/src/commands/messages.d.ts +2 -1
  43. package/dist/src/commands/messages.js +138 -16
  44. package/dist/src/commands/modes.js +28 -14
  45. package/dist/src/commands/registry.js +69 -7
  46. package/dist/src/commands/response-actions.js +1 -7
  47. package/dist/src/commands/sequence.js +24 -1
  48. package/dist/src/commands/session.d.ts +2 -0
  49. package/dist/src/commands/session.js +50 -1
  50. package/dist/src/commands/threads.js +1 -7
  51. package/dist/src/commands/work.d.ts +6 -0
  52. package/dist/src/commands/work.js +338 -0
  53. package/dist/src/dom/locale/am.d.ts +2 -1
  54. package/dist/src/dom/locale/am.js +2 -1
  55. package/dist/src/dom/locale/ar.d.ts +2 -1
  56. package/dist/src/dom/locale/ar.js +2 -1
  57. package/dist/src/dom/locale/bg.d.ts +2 -1
  58. package/dist/src/dom/locale/bg.js +2 -1
  59. package/dist/src/dom/locale/bn.d.ts +2 -1
  60. package/dist/src/dom/locale/bn.js +2 -1
  61. package/dist/src/dom/locale/bs.d.ts +2 -1
  62. package/dist/src/dom/locale/bs.js +2 -1
  63. package/dist/src/dom/locale/ca.d.ts +2 -1
  64. package/dist/src/dom/locale/ca.js +2 -1
  65. package/dist/src/dom/locale/cs.d.ts +2 -1
  66. package/dist/src/dom/locale/cs.js +2 -1
  67. package/dist/src/dom/locale/da.d.ts +2 -1
  68. package/dist/src/dom/locale/da.js +2 -1
  69. package/dist/src/dom/locale/de.d.ts +4 -3
  70. package/dist/src/dom/locale/de.js +4 -3
  71. package/dist/src/dom/locale/el.d.ts +2 -1
  72. package/dist/src/dom/locale/el.js +2 -1
  73. package/dist/src/dom/locale/en.d.ts +27 -2
  74. package/dist/src/dom/locale/en.js +28 -2
  75. package/dist/src/dom/locale/es-419.d.ts +2 -1
  76. package/dist/src/dom/locale/es-419.js +2 -1
  77. package/dist/src/dom/locale/es-ES.d.ts +2 -1
  78. package/dist/src/dom/locale/es-ES.js +2 -1
  79. package/dist/src/dom/locale/et.d.ts +2 -1
  80. package/dist/src/dom/locale/et.js +2 -1
  81. package/dist/src/dom/locale/fa.d.ts +2 -1
  82. package/dist/src/dom/locale/fa.js +2 -1
  83. package/dist/src/dom/locale/fi.d.ts +2 -1
  84. package/dist/src/dom/locale/fi.js +2 -1
  85. package/dist/src/dom/locale/fr-CA.d.ts +2 -1
  86. package/dist/src/dom/locale/fr-CA.js +2 -1
  87. package/dist/src/dom/locale/fr-FR.d.ts +3 -3
  88. package/dist/src/dom/locale/fr-FR.js +3 -3
  89. package/dist/src/dom/locale/gu.d.ts +2 -1
  90. package/dist/src/dom/locale/gu.js +2 -1
  91. package/dist/src/dom/locale/hi.d.ts +2 -1
  92. package/dist/src/dom/locale/hi.js +2 -1
  93. package/dist/src/dom/locale/hr.d.ts +2 -1
  94. package/dist/src/dom/locale/hr.js +2 -1
  95. package/dist/src/dom/locale/hu.d.ts +2 -1
  96. package/dist/src/dom/locale/hu.js +2 -1
  97. package/dist/src/dom/locale/hy.d.ts +2 -1
  98. package/dist/src/dom/locale/hy.js +2 -1
  99. package/dist/src/dom/locale/id.d.ts +2 -1
  100. package/dist/src/dom/locale/id.js +2 -1
  101. package/dist/src/dom/locale/index.d.ts +17 -2
  102. package/dist/src/dom/locale/index.js +65 -0
  103. package/dist/src/dom/locale/is.d.ts +2 -1
  104. package/dist/src/dom/locale/is.js +2 -1
  105. package/dist/src/dom/locale/it.d.ts +2 -1
  106. package/dist/src/dom/locale/it.js +2 -1
  107. package/dist/src/dom/locale/ja.d.ts +2 -1
  108. package/dist/src/dom/locale/ja.js +2 -1
  109. package/dist/src/dom/locale/ka.d.ts +2 -1
  110. package/dist/src/dom/locale/ka.js +2 -1
  111. package/dist/src/dom/locale/kk.d.ts +2 -1
  112. package/dist/src/dom/locale/kk.js +2 -1
  113. package/dist/src/dom/locale/kn.d.ts +2 -1
  114. package/dist/src/dom/locale/kn.js +2 -1
  115. package/dist/src/dom/locale/ko.d.ts +2 -1
  116. package/dist/src/dom/locale/ko.js +2 -1
  117. package/dist/src/dom/locale/lt.d.ts +2 -1
  118. package/dist/src/dom/locale/lt.js +2 -1
  119. package/dist/src/dom/locale/lv.d.ts +2 -1
  120. package/dist/src/dom/locale/lv.js +2 -1
  121. package/dist/src/dom/locale/mk.d.ts +2 -1
  122. package/dist/src/dom/locale/mk.js +2 -1
  123. package/dist/src/dom/locale/ml.d.ts +2 -1
  124. package/dist/src/dom/locale/ml.js +2 -1
  125. package/dist/src/dom/locale/mn.d.ts +2 -1
  126. package/dist/src/dom/locale/mn.js +2 -1
  127. package/dist/src/dom/locale/mr.d.ts +2 -1
  128. package/dist/src/dom/locale/mr.js +2 -1
  129. package/dist/src/dom/locale/ms.d.ts +2 -1
  130. package/dist/src/dom/locale/ms.js +2 -1
  131. package/dist/src/dom/locale/my.d.ts +2 -1
  132. package/dist/src/dom/locale/my.js +2 -1
  133. package/dist/src/dom/locale/nb.d.ts +2 -1
  134. package/dist/src/dom/locale/nb.js +2 -1
  135. package/dist/src/dom/locale/nl.d.ts +2 -1
  136. package/dist/src/dom/locale/nl.js +2 -1
  137. package/dist/src/dom/locale/pa.d.ts +2 -1
  138. package/dist/src/dom/locale/pa.js +2 -1
  139. package/dist/src/dom/locale/pl.d.ts +2 -1
  140. package/dist/src/dom/locale/pl.js +2 -1
  141. package/dist/src/dom/locale/pt-BR.d.ts +2 -1
  142. package/dist/src/dom/locale/pt-BR.js +2 -1
  143. package/dist/src/dom/locale/pt-PT.d.ts +2 -1
  144. package/dist/src/dom/locale/pt-PT.js +2 -1
  145. package/dist/src/dom/locale/ro.d.ts +2 -1
  146. package/dist/src/dom/locale/ro.js +2 -1
  147. package/dist/src/dom/locale/ru.d.ts +2 -1
  148. package/dist/src/dom/locale/ru.js +2 -1
  149. package/dist/src/dom/locale/sk.d.ts +2 -1
  150. package/dist/src/dom/locale/sk.js +2 -1
  151. package/dist/src/dom/locale/sl.d.ts +2 -1
  152. package/dist/src/dom/locale/sl.js +2 -1
  153. package/dist/src/dom/locale/so.d.ts +2 -1
  154. package/dist/src/dom/locale/so.js +2 -1
  155. package/dist/src/dom/locale/sq.d.ts +2 -1
  156. package/dist/src/dom/locale/sq.js +2 -1
  157. package/dist/src/dom/locale/sr.d.ts +2 -1
  158. package/dist/src/dom/locale/sr.js +2 -1
  159. package/dist/src/dom/locale/sv.d.ts +2 -1
  160. package/dist/src/dom/locale/sv.js +2 -1
  161. package/dist/src/dom/locale/sw.d.ts +2 -1
  162. package/dist/src/dom/locale/sw.js +2 -1
  163. package/dist/src/dom/locale/ta.d.ts +2 -1
  164. package/dist/src/dom/locale/ta.js +2 -1
  165. package/dist/src/dom/locale/te.d.ts +2 -1
  166. package/dist/src/dom/locale/te.js +2 -1
  167. package/dist/src/dom/locale/th.d.ts +2 -1
  168. package/dist/src/dom/locale/th.js +2 -1
  169. package/dist/src/dom/locale/tl.d.ts +4 -5
  170. package/dist/src/dom/locale/tl.js +4 -5
  171. package/dist/src/dom/locale/tr.d.ts +2 -1
  172. package/dist/src/dom/locale/tr.js +2 -1
  173. package/dist/src/dom/locale/types.d.ts +18 -1
  174. package/dist/src/dom/locale/uk.d.ts +2 -1
  175. package/dist/src/dom/locale/uk.js +2 -1
  176. package/dist/src/dom/locale/ur.d.ts +2 -1
  177. package/dist/src/dom/locale/ur.js +2 -1
  178. package/dist/src/dom/locale/vi.d.ts +2 -1
  179. package/dist/src/dom/locale/vi.js +2 -1
  180. package/dist/src/dom/locale/zh-HK.d.ts +2 -1
  181. package/dist/src/dom/locale/zh-HK.js +4 -3
  182. package/dist/src/dom/locale/zh-Hans.d.ts +2 -1
  183. package/dist/src/dom/locale/zh-Hans.js +2 -1
  184. package/dist/src/dom/locale/zh-TW.d.ts +2 -1
  185. package/dist/src/dom/locale/zh-TW.js +2 -1
  186. package/dist/src/dom/selectors.js +6 -1
  187. package/dist/src/index.d.ts +3 -0
  188. package/dist/src/index.js +3 -0
  189. package/dist/src/runner/responses.d.ts +3 -1
  190. package/dist/src/runner/responses.js +16 -1
  191. package/dist/src/runner/result.js +129 -4
  192. package/dist/src/runner/stream.d.ts +1 -1
  193. package/dist/src/runner/stream.js +6 -0
  194. package/dist/src/runner/types.d.ts +35 -1
  195. package/dist/src/safety/risk.d.ts +10 -0
  196. package/dist/src/safety/risk.js +10 -0
  197. package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +6 -0
  198. package/dist/src/scripts/apply-intelligence-locale-captures.js +53 -7
  199. package/dist/src/scripts/capture-intelligence-locales.d.ts +3 -0
  200. package/dist/src/scripts/capture-intelligence-locales.js +372 -3
  201. package/dist/src/scripts/capture-surface-profile.d.ts +23 -0
  202. package/dist/src/scripts/capture-surface-profile.js +304 -0
  203. package/dist/src/scripts/live-smoke/scenarios.js +34 -1
  204. package/dist/src/scripts/live-smoke.js +1 -0
  205. package/dist/src/types.d.ts +245 -2
  206. package/package.json +3 -2
  207. package/references/2026-07-16-chat-work-surfaces.md +87 -0
  208. package/references/agents-runner.md +13 -2
  209. package/references/backend-protocol.md +15 -5
  210. package/references/language-coverage.md +40 -3
  211. package/references/localization.md +34 -6
  212. package/references/python-parity.md +11 -1
  213. package/references/responses-adapter.md +12 -1
  214. package/references/troubleshooting.md +22 -2
@@ -1,4 +1,4 @@
1
- import { attachChatGPTBrowser } from "../browser/attach.js";
1
+ import { attachChatGPTBrowser, tabIdFromPage } from "../browser/attach.js";
2
2
  import { readPageState } from "../browser/page-state.js";
3
3
  import { resultError, resultOk } from "../errors.js";
4
4
  import { contextFromPage } from "./context.js";
@@ -7,6 +7,9 @@ export async function bootstrap(env, args = {}) {
7
7
  const attached = await attachChatGPTBrowser(env, args);
8
8
  env.browser = attached.browser;
9
9
  env.page = attached.page;
10
+ if (attached.tabId !== undefined) {
11
+ env.expectedTabId = attached.tabId;
12
+ }
10
13
  const state = await readPageState(attached.page);
11
14
  const data = {
12
15
  browserName: attached.browserName,
@@ -23,3 +26,49 @@ export async function bootstrap(env, args = {}) {
23
26
  return resultError(error instanceof Error ? error : new Error(String(error)));
24
27
  }
25
28
  }
29
+ export async function ensurePage(env) {
30
+ if (env.page === undefined) {
31
+ return bootstrap(env, { preferExistingTab: true });
32
+ }
33
+ const affinity = await verifyTabAffinity(env);
34
+ if (affinity !== undefined) {
35
+ return affinity;
36
+ }
37
+ return resultOk({}, await contextFromPage(env.page, tabContext(env)));
38
+ }
39
+ export async function verifyTabAffinity(env) {
40
+ if (env.expectedTabId === undefined || env.page === undefined) {
41
+ return undefined;
42
+ }
43
+ const actualTabId = tabIdFromPage(env.page);
44
+ if (actualTabId === env.expectedTabId) {
45
+ return undefined;
46
+ }
47
+ const code = actualTabId === undefined ? "tab_affinity_unverifiable" : "tab_affinity_lost";
48
+ const message = actualTabId === undefined
49
+ ? `ChatGPT command cannot verify it is still attached to expected tab ${env.expectedTabId}.`
50
+ : `ChatGPT command would run on tab ${actualTabId}, but the workflow expected tab ${env.expectedTabId}.`;
51
+ return {
52
+ ok: false,
53
+ status: "blocked",
54
+ warnings: [],
55
+ blocker: {
56
+ kind: "selector_drift",
57
+ code,
58
+ message,
59
+ remediation: [
60
+ {
61
+ label: "Reclaim the intended tab",
62
+ instruction: "Run session.bootstrap again with an exact existingTab target, or pass the correct page/tab to createChatGPT before retrying.",
63
+ userActionRequired: false
64
+ }
65
+ ],
66
+ resumable: false
67
+ },
68
+ context: await contextFromPage(env.page, tabContext(env, actualTabId))
69
+ };
70
+ }
71
+ function tabContext(env, actualTabId = tabIdFromPage(env.page)) {
72
+ const tabId = actualTabId ?? env.expectedTabId;
73
+ return tabId === undefined ? {} : { tabId };
74
+ }
@@ -5,7 +5,7 @@ import { anyLabelPattern, localeLabels } from "../dom/locale-labels.js";
5
5
  import { normalizeWhitespace, stripTags } from "../dom/visible-text.js";
6
6
  import { contextFromPage } from "./context.js";
7
7
  import { ensureConversationTarget } from "./conversation.js";
8
- import { bootstrap } from "./session.js";
8
+ import { ensurePage } from "./session.js";
9
9
  const CHATGPT_HOME = "https://chatgpt.com/";
10
10
  export function extractThreadSearchResultsFromHtml(html) {
11
11
  const anchors = html.matchAll(/<a\b(?<attrs>[^>]*\bhref=["'](?<href>\/c\/[^"']+)["'][^>]*)>(?<body>[\s\S]*?)<\/a>/gi);
@@ -108,12 +108,6 @@ export async function openThread(env, args, previousResults) {
108
108
  return resultError(error instanceof Error ? error : new Error(String(error)), await contextFromPage(page));
109
109
  }
110
110
  }
111
- async function ensurePage(env) {
112
- if (env.page !== undefined) {
113
- return resultOk({}, await contextFromPage(env.page));
114
- }
115
- return bootstrap(env, { preferExistingTab: true });
116
- }
117
111
  async function resolveOpenTarget(env, args, previousResults) {
118
112
  if (args.url !== undefined) {
119
113
  return { url: args.url };
@@ -0,0 +1,6 @@
1
+ import type { CommandResult, ReadWorkLatestArgs, ReadWorkLatestData, RuntimeEnv, StartWorkArgs, StartWorkData, SteerWorkArgs, SteerWorkData, WorkStatusArgs, WorkStatusData, WorkWaitArgs, WorkWaitData } from "../types.js";
2
+ export declare function startWork(env: RuntimeEnv, args: StartWorkArgs): Promise<CommandResult<StartWorkData>>;
3
+ export declare function workStatus(env: RuntimeEnv, args?: WorkStatusArgs): Promise<CommandResult<WorkStatusData>>;
4
+ export declare function waitForWork(env: RuntimeEnv, args?: WorkWaitArgs): Promise<CommandResult<WorkWaitData>>;
5
+ export declare function steerWork(env: RuntimeEnv, args: SteerWorkArgs): Promise<CommandResult<SteerWorkData>>;
6
+ export declare function readLatestWork(env: RuntimeEnv, args?: ReadWorkLatestArgs): Promise<CommandResult<ReadWorkLatestData>>;
@@ -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,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[];
@@ -31,4 +31,5 @@ export declare const am: {
31
31
  };
32
32
  signedInMarkers: string[];
33
33
  responseActions: string[];
34
+ stopControl: string[];
34
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 ጋር ይወያዩ"],
@@ -31,4 +31,5 @@ export const am = {
31
31
  },
32
32
  signedInMarkers: ["አዲስ ውይይት", "ውይይቶችን ፈልግ", "የቅርብ ጊዜዎች", "ላይብረሪ", "ፕሮጀክቶች", "ከChatGPT ጋር ይወያዩ"],
33
33
  responseActions: ["ምላሹን ይቅዱ"],
34
+ stopControl: ["መልስ መስጠት አቁም"],
34
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[];
@@ -32,4 +32,5 @@ export declare const ar: {
32
32
  };
33
33
  signedInMarkers: string[];
34
34
  responseActions: string[];
35
+ stopControl: string[];
35
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"],
@@ -32,4 +32,5 @@ export const ar = {
32
32
  },
33
33
  signedInMarkers: ["دردشة جديدة", "البحث في الدردشات", "المحادثات الأخيرة", "المكتبة", "المشروعات", "الدردشة مع ChatGPT"],
34
34
  responseActions: ["نسخ إجابة"],
35
+ stopControl: ["إيقاف الرد"],
35
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[];
@@ -32,4 +32,5 @@ export declare const bg: {
32
32
  };
33
33
  signedInMarkers: string[];
34
34
  responseActions: string[];
35
+ stopControl: string[];
35
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"],
@@ -32,4 +32,5 @@ export const bg = {
32
32
  },
33
33
  signedInMarkers: ["Нов чат", "Търсене на чатове", "Скорошни чатове", "Каталог", "Проекти", "Чат с ChatGPT"],
34
34
  responseActions: ["Копирайте отговора"],
35
+ stopControl: ["Спри отговора"],
35
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[];
@@ -32,4 +32,5 @@ export declare const bn: {
32
32
  };
33
33
  signedInMarkers: string[];
34
34
  responseActions: string[];
35
+ stopControl: string[];
35
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: ["যে কোন কিছু জিজ্ঞেস করুন…"],
@@ -32,4 +32,5 @@ export const bn = {
32
32
  },
33
33
  signedInMarkers: ["নতুন চ্যাট", "চ্যাট খুঁজুন", "সাম্প্রতিক", "চ্যাটের ইতিহাস", "প্রোজেক্ট", "যে কোন কিছু জিজ্ঞেস করুন…"],
34
34
  responseActions: ["উত্তর কপি করুন"],
35
+ stopControl: ["উত্তর থামান"],
35
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[];
@@ -31,4 +31,5 @@ export declare const bs: {
31
31
  };
32
32
  signedInMarkers: string[];
33
33
  responseActions: string[];
34
+ stopControl: string[];
34
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"],
@@ -31,4 +31,5 @@ export const bs = {
31
31
  },
32
32
  signedInMarkers: ["Novi razgovor", "Pretraži razgovore", "Nedavno", "Biblioteka", "Projekti", "Razgovarajte pomoću ChatGPT-a"],
33
33
  responseActions: ["Kopiraj odgovor"],
34
+ stopControl: ["Zaustavi odgovaranje"],
34
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 ca: {
10
10
  composerTextbox: string[];
@@ -31,4 +31,5 @@ export declare const ca: {
31
31
  };
32
32
  signedInMarkers: string[];
33
33
  responseActions: string[];
34
+ stopControl: string[];
34
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 ca = {
10
10
  composerTextbox: ["Xateja amb el ChatGPT"],
@@ -31,4 +31,5 @@ export const ca = {
31
31
  },
32
32
  signedInMarkers: ["Xat nou", "Cerca xats", "Recents", "Història de xats", "Projectes", "Xateja amb el ChatGPT"],
33
33
  responseActions: ["Copia la resposta"],
34
+ stopControl: ["Atura la resposta"],
34
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 cs: {
10
10
  composerTextbox: string[];
@@ -31,4 +31,5 @@ export declare const cs: {
31
31
  };
32
32
  signedInMarkers: string[];
33
33
  responseActions: string[];
34
+ stopControl: string[];
34
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 cs = {
10
10
  composerTextbox: ["Chatovat s ChatGPT"],
@@ -31,4 +31,5 @@ export const cs = {
31
31
  },
32
32
  signedInMarkers: ["Nový chat", "Hledat chaty", "Nedávné", "Historie chatu", "Projekty", "Chatovat s ChatGPT"],
33
33
  responseActions: ["Zkopírovat odpověď"],
34
+ stopControl: ["Zastavit odpovídání"],
34
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 da: {
10
10
  composerTextbox: string[];
@@ -30,4 +30,5 @@ export declare const da: {
30
30
  };
31
31
  signedInMarkers: string[];
32
32
  responseActions: string[];
33
+ stopControl: string[];
33
34
  };