@agentrhq/webcmd 0.2.5 → 0.3.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 (200) hide show
  1. package/README.md +13 -2
  2. package/cli-manifest.json +217 -13
  3. package/clis/_shared/site-auth.js +3 -3
  4. package/clis/_shared/site-auth.test.js +4 -4
  5. package/clis/chatgpt/ask.js +1 -1
  6. package/clis/chatgpt/ask.test.js +11 -0
  7. package/clis/chatgpt/commands.test.js +102 -1
  8. package/clis/chatgpt/deep-research-result.js +45 -3
  9. package/clis/chatgpt/image.js +22 -2
  10. package/clis/chatgpt/project-file-add.js +24 -1
  11. package/clis/chatgpt/utils.js +258 -52
  12. package/clis/chatgpt/utils.test.js +259 -1
  13. package/clis/claude/ask.js +21 -1
  14. package/clis/facebook/feed.js +110 -24
  15. package/clis/facebook/feed.test.js +62 -0
  16. package/clis/facebook/search.js +185 -38
  17. package/clis/facebook/search.test.js +95 -50
  18. package/clis/instagram/explore.js +30 -12
  19. package/clis/instagram/explore.test.js +77 -0
  20. package/clis/instagram/post.js +14 -1
  21. package/clis/instagram/reel.js +13 -1
  22. package/clis/linkedin/company.js +153 -0
  23. package/clis/linkedin/company.test.js +111 -0
  24. package/clis/linkedin/connections.js +135 -0
  25. package/clis/linkedin/connections.test.js +141 -0
  26. package/clis/mercury/reimbursement-draft.js +12 -1
  27. package/clis/slock/whoami.test.js +2 -2
  28. package/clis/twitter/article-evaluate.test.js +27 -0
  29. package/clis/twitter/article.js +73 -16
  30. package/clis/twitter/article.test.js +209 -0
  31. package/clis/twitter/download.js +6 -1
  32. package/clis/twitter/post.js +14 -1
  33. package/clis/twitter/profile.js +1 -1
  34. package/clis/twitter/profile.test.js +8 -0
  35. package/clis/twitter/quote.js +11 -1
  36. package/clis/twitter/reply.js +11 -1
  37. package/dist/src/browser/base-page.d.ts +26 -0
  38. package/dist/src/browser/base-page.js +23 -8
  39. package/dist/src/browser/base-page.test.js +45 -12
  40. package/dist/src/browser/command-catalog.d.ts +6 -0
  41. package/dist/src/browser/command-catalog.js +259 -0
  42. package/dist/src/browser/command-catalog.test.d.ts +1 -0
  43. package/dist/src/browser/command-catalog.test.js +79 -0
  44. package/dist/src/browser/daemon-client.d.ts +2 -1
  45. package/dist/src/browser/runtime/local-cloak/actions.js +4 -1
  46. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.d.ts +17 -0
  47. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.js +106 -0
  48. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.d.ts +1 -0
  49. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.js +65 -0
  50. package/dist/src/browser/runtime/local-cloak/provider.test.js +36 -0
  51. package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +10 -2
  52. package/dist/src/browser/runtime/local-cloak/session-manager.js +25 -9
  53. package/dist/src/browser/runtime/local-cloak/session-manager.test.js +36 -0
  54. package/dist/src/browser/target-resolver.d.ts +1 -0
  55. package/dist/src/browser/target-resolver.js +99 -4
  56. package/dist/src/browser/target-resolver.test.js +141 -1
  57. package/dist/src/build-manifest.d.ts +6 -0
  58. package/dist/src/build-manifest.js +18 -1
  59. package/dist/src/build-manifest.test.js +57 -1
  60. package/dist/src/builtin-command-surface.d.ts +9 -0
  61. package/dist/src/builtin-command-surface.js +16 -0
  62. package/dist/src/check-hosted-contract.test.d.ts +1 -0
  63. package/dist/src/check-hosted-contract.test.js +56 -0
  64. package/dist/src/cli.js +63 -110
  65. package/dist/src/cli.test.js +82 -6
  66. package/dist/src/command-presentation.d.ts +95 -0
  67. package/dist/src/command-presentation.js +486 -0
  68. package/dist/src/command-presentation.test.d.ts +1 -0
  69. package/dist/src/command-presentation.test.js +97 -0
  70. package/dist/src/command-surface.d.ts +43 -0
  71. package/dist/src/command-surface.js +205 -0
  72. package/dist/src/command-surface.test.d.ts +1 -0
  73. package/dist/src/command-surface.test.js +406 -0
  74. package/dist/src/commanderAdapter.d.ts +5 -1
  75. package/dist/src/commanderAdapter.js +12 -55
  76. package/dist/src/commanderAdapter.test.js +15 -3
  77. package/dist/src/commands/auth.js +3 -2
  78. package/dist/src/commands/auth.test.js +6 -6
  79. package/dist/src/community-plugin-sync.d.ts +11 -0
  80. package/dist/src/community-plugin-sync.js +138 -0
  81. package/dist/src/community-plugin-sync.test.d.ts +1 -0
  82. package/dist/src/community-plugin-sync.test.js +123 -0
  83. package/dist/src/completion-fast.d.ts +4 -4
  84. package/dist/src/completion-fast.js +15 -34
  85. package/dist/src/completion-shared.d.ts +4 -0
  86. package/dist/src/completion-shared.js +38 -0
  87. package/dist/src/completion.js +3 -27
  88. package/dist/src/completion.test.js +31 -2
  89. package/dist/src/docs-sync-review-cli.test.d.ts +1 -0
  90. package/dist/src/docs-sync-review-cli.test.js +322 -0
  91. package/dist/src/docs-sync-review.d.ts +117 -0
  92. package/dist/src/docs-sync-review.js +475 -0
  93. package/dist/src/docs-sync-review.test.d.ts +1 -0
  94. package/dist/src/docs-sync-review.test.js +324 -0
  95. package/dist/src/errors.d.ts +12 -3
  96. package/dist/src/errors.js +11 -7
  97. package/dist/src/errors.test.js +14 -1
  98. package/dist/src/execution.d.ts +1 -2
  99. package/dist/src/execution.js +4 -45
  100. package/dist/src/generate-release-notes-cli.test.js +11 -1
  101. package/dist/src/help.d.ts +4 -0
  102. package/dist/src/help.js +50 -255
  103. package/dist/src/help.test.js +27 -1
  104. package/dist/src/hosted/args.d.ts +4 -0
  105. package/dist/src/hosted/args.js +4 -0
  106. package/dist/src/hosted/args.test.d.ts +1 -0
  107. package/dist/src/hosted/args.test.js +49 -0
  108. package/dist/src/hosted/availability.d.ts +13 -0
  109. package/dist/src/hosted/availability.js +16 -0
  110. package/dist/src/hosted/availability.test.d.ts +1 -0
  111. package/dist/src/hosted/availability.test.js +180 -0
  112. package/dist/src/hosted/browser-args.d.ts +18 -0
  113. package/dist/src/hosted/browser-args.js +152 -0
  114. package/dist/src/hosted/browser-args.test.d.ts +1 -0
  115. package/dist/src/hosted/browser-args.test.js +182 -0
  116. package/dist/src/hosted/client.d.ts +59 -0
  117. package/dist/src/hosted/client.js +510 -0
  118. package/dist/src/hosted/client.test.d.ts +1 -0
  119. package/dist/src/hosted/client.test.js +738 -0
  120. package/dist/src/hosted/config.d.ts +55 -0
  121. package/dist/src/hosted/config.js +112 -0
  122. package/dist/src/hosted/config.test.d.ts +1 -0
  123. package/dist/src/hosted/config.test.js +81 -0
  124. package/dist/src/hosted/contract.d.ts +94 -0
  125. package/dist/src/hosted/contract.js +208 -0
  126. package/dist/src/hosted/contract.test.d.ts +1 -0
  127. package/dist/src/hosted/contract.test.js +361 -0
  128. package/dist/src/hosted/credentials.d.ts +38 -0
  129. package/dist/src/hosted/credentials.js +248 -0
  130. package/dist/src/hosted/credentials.test.d.ts +1 -0
  131. package/dist/src/hosted/credentials.test.js +93 -0
  132. package/dist/src/hosted/file-contract.test.d.ts +1 -0
  133. package/dist/src/hosted/file-contract.test.js +174 -0
  134. package/dist/src/hosted/files.d.ts +29 -0
  135. package/dist/src/hosted/files.js +296 -0
  136. package/dist/src/hosted/files.test.d.ts +1 -0
  137. package/dist/src/hosted/files.test.js +231 -0
  138. package/dist/src/hosted/main-lifecycle.test.d.ts +1 -0
  139. package/dist/src/hosted/main-lifecycle.test.js +213 -0
  140. package/dist/src/hosted/manifest.d.ts +14 -0
  141. package/dist/src/hosted/manifest.js +47 -0
  142. package/dist/src/hosted/manifest.test.d.ts +1 -0
  143. package/dist/src/hosted/manifest.test.js +164 -0
  144. package/dist/src/hosted/output-parity.test.d.ts +1 -0
  145. package/dist/src/hosted/output-parity.test.js +108 -0
  146. package/dist/src/hosted/root-command-surface.test.d.ts +1 -0
  147. package/dist/src/hosted/root-command-surface.test.js +646 -0
  148. package/dist/src/hosted/runner.d.ts +19 -0
  149. package/dist/src/hosted/runner.js +705 -0
  150. package/dist/src/hosted/runner.test.d.ts +1 -0
  151. package/dist/src/hosted/runner.test.js +1607 -0
  152. package/dist/src/hosted/setup.d.ts +10 -0
  153. package/dist/src/hosted/setup.js +79 -0
  154. package/dist/src/hosted/setup.test.d.ts +1 -0
  155. package/dist/src/hosted/setup.test.js +195 -0
  156. package/dist/src/hosted/types.d.ts +175 -0
  157. package/dist/src/hosted/types.js +1 -0
  158. package/dist/src/main.js +123 -97
  159. package/dist/src/manifest-types.d.ts +2 -0
  160. package/dist/src/output.d.ts +17 -2
  161. package/dist/src/output.js +88 -81
  162. package/dist/src/output.test.js +141 -51
  163. package/dist/src/pipeline/executor.test.js +1 -0
  164. package/dist/src/pipeline/steps/download.test.js +1 -0
  165. package/dist/src/plugin-create-cli.test.d.ts +1 -0
  166. package/dist/src/plugin-create-cli.test.js +37 -0
  167. package/dist/src/plugin-manifest.d.ts +12 -0
  168. package/dist/src/plugin-manifest.js +16 -0
  169. package/dist/src/plugin-manifest.test.js +19 -1
  170. package/dist/src/plugin-scaffold.d.ts +4 -1
  171. package/dist/src/plugin-scaffold.js +4 -1
  172. package/dist/src/plugin-scaffold.test.js +23 -8
  173. package/dist/src/plugin.js +4 -1
  174. package/dist/src/plugin.test.js +13 -0
  175. package/dist/src/registry.d.ts +2 -0
  176. package/dist/src/release-notes.d.ts +6 -1
  177. package/dist/src/release-notes.js +192 -4
  178. package/dist/src/release-notes.test.js +143 -4
  179. package/dist/src/root-command-surface.d.ts +31 -0
  180. package/dist/src/root-command-surface.js +106 -0
  181. package/dist/src/serialization.d.ts +1 -16
  182. package/dist/src/serialization.js +5 -55
  183. package/dist/src/stream-write.d.ts +12 -0
  184. package/dist/src/stream-write.js +91 -0
  185. package/dist/src/stream-write.test.d.ts +1 -0
  186. package/dist/src/stream-write.test.js +186 -0
  187. package/dist/src/types.d.ts +5 -0
  188. package/dist/src/utils.d.ts +1 -1
  189. package/dist/src/utils.js +2 -8
  190. package/dist/src/utils.test.js +50 -0
  191. package/hosted-contract.json +36748 -0
  192. package/package.json +6 -1
  193. package/scripts/check-hosted-contract.mjs +108 -0
  194. package/scripts/docs-sync-review.ts +332 -0
  195. package/scripts/generate-release-notes.ts +1 -1
  196. package/scripts/sync-community-plugins.ts +14 -0
  197. package/skills/smart-search/SKILL.md +14 -3
  198. package/skills/webcmd-autofix/SKILL.md +26 -37
  199. package/skills/webcmd-usage/SKILL.md +21 -7
  200. package/clis/antigravity/SKILL.md +0 -38
@@ -28,7 +28,30 @@ export const projectFileAddCommand = cli({
28
28
  siteSession: 'persistent',
29
29
  navigateBefore: false,
30
30
  args: [
31
- { name: 'file', positional: true, required: true, help: 'Local file path(s) to upload; comma-separated paths are supported' },
31
+ {
32
+ name: 'file',
33
+ positional: true,
34
+ required: true,
35
+ help: 'Local file path(s) to upload; comma-separated paths are supported',
36
+ file: {
37
+ direction: 'input',
38
+ pathKind: 'file',
39
+ multiple: true,
40
+ separator: ',',
41
+ contentTypes: [
42
+ 'application/pdf',
43
+ 'text/plain',
44
+ 'text/markdown',
45
+ 'text/csv',
46
+ 'application/json',
47
+ 'image/jpeg',
48
+ 'image/png',
49
+ 'image/gif',
50
+ 'image/webp',
51
+ ],
52
+ maxBytes: 26_214_400,
53
+ },
54
+ },
32
55
  { name: 'id', required: true, help: 'Project ID or /g/g-p-<id> URL' },
33
56
  ],
34
57
  columns: ['Status', 'File'],
@@ -1059,8 +1059,10 @@ export async function sendChatGPTMessage(page, text) {
1059
1059
  return true;
1060
1060
  }
1061
1061
 
1062
- export async function getVisibleMessages(page) {
1062
+ export async function getVisibleMessages(page, { textOnly = false } = {}) {
1063
+ const includeHtml = textOnly ? 'false' : 'true';
1063
1064
  const result = requireArrayEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
1065
+ const includeHtml = ${includeHtml};
1064
1066
  const isVisible = (el) => {
1065
1067
  if (!(el instanceof HTMLElement)) return false;
1066
1068
  const style = window.getComputedStyle(el);
@@ -1097,8 +1099,11 @@ export async function getVisibleMessages(page) {
1097
1099
  || node.querySelector('.markdown')
1098
1100
  || node.querySelector('[data-message-author-role]')
1099
1101
  || node;
1100
- const html = contentNode instanceof HTMLElement ? (contentNode.innerHTML || '') : '';
1101
- const text = normalize(contentNode instanceof HTMLElement ? (contentNode.innerText || contentNode.textContent || '') : '');
1102
+ const html = includeHtml && contentNode instanceof HTMLElement ? (contentNode.innerHTML || '') : '';
1103
+ const rawText = contentNode instanceof HTMLElement
1104
+ ? (includeHtml ? (contentNode.innerText || contentNode.textContent || '') : (contentNode.textContent || ''))
1105
+ : '';
1106
+ const text = normalize(rawText);
1102
1107
  if (!text) continue;
1103
1108
  const key = role + '\\n' + text;
1104
1109
  if (seen.has(key)) continue;
@@ -1169,7 +1174,7 @@ export async function waitForChatGPTDetailRows(page, { wantMarkdown = false, tim
1169
1174
  lastKey = key;
1170
1175
  stableStartedAt = 0;
1171
1176
  }
1172
- await page.wait(3);
1177
+ await page.sleep(3);
1173
1178
  }
1174
1179
 
1175
1180
  throw new TimeoutError(
@@ -1255,25 +1260,171 @@ function extractDeepResearchSourcesFromReportMessage(reportMessage) {
1255
1260
  return [...byUrl.values()].slice(0, 200);
1256
1261
  }
1257
1262
 
1258
- function extractDeepResearchFromWidgetState(widgetState, source = 'conversation-widget-state') {
1259
- const state = parseJsonMaybe(widgetState);
1260
- if (!state || typeof state !== 'object') return null;
1261
- const reportMessage = state.report_message || state.reportMessage || null;
1262
- const parts = Array.isArray(reportMessage?.content?.parts) ? reportMessage.content.parts : [];
1263
- const report = normalizeDeepResearchText(parts.filter((part) => typeof part === 'string').join('\n\n'));
1264
- if (!looksLikeDeepResearchReport(report)) return null;
1263
+ function pickFirstObject(...values) {
1264
+ for (const value of values) {
1265
+ const parsed = parseJsonMaybe(value);
1266
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed;
1267
+ }
1268
+ return {};
1269
+ }
1270
+
1271
+ function stringOrEmpty(value) {
1272
+ return value === undefined || value === null ? '' : String(value);
1273
+ }
1274
+
1275
+ function compactDeepResearchPlanSteps(plan, stepStatusesByPlan) {
1276
+ const steps = Array.isArray(plan?.steps) ? plan.steps : [];
1277
+ return steps.slice(0, 50).map((step, index) => {
1278
+ const id = stringOrEmpty(step?.id || step?.step_id || step?.plan_step_id || step?.key || index);
1279
+ return {
1280
+ id,
1281
+ title: stringOrEmpty(step?.title || step?.name || step?.summary || step?.description),
1282
+ status: stringOrEmpty(step?.status || step?.step_status || stepStatusesByPlan?.[id] || ''),
1283
+ };
1284
+ }).filter((step) => step.id || step.title || step.status);
1285
+ }
1286
+
1287
+ function deepResearchProgressStatus(progress) {
1288
+ const venusStatus = String(progress.venusStatus || '').toLowerCase();
1289
+ if (/waiting_for_user|user_response/.test(venusStatus)) return 'waiting_for_user';
1290
+ if (/needs_user|user_action|action_required|requires_action/.test(venusStatus)) return 'needs_user_action';
1291
+ if (/running|in_progress|loading|generating|researching|queued|started|processing/.test(venusStatus)) return 'running';
1292
+
1293
+ const venusMessageType = String(progress.venusMessageType || '').toLowerCase();
1294
+ if (/loading|running|generating|research|progress/.test(venusMessageType)) return 'running';
1295
+
1296
+ if (progress.asyncTaskConversationId
1297
+ || progress.widgetSessionId
1298
+ || progress.asyncStatus !== undefined
1299
+ || progress.venusStatus
1300
+ || progress.planId
1301
+ || progress.planTitle) {
1302
+ return 'not_ready';
1303
+ }
1304
+ return '';
1305
+ }
1306
+
1307
+ function buildDeepResearchProgressResult(state, responseMetadata, source) {
1308
+ const widgetState = state && typeof state === 'object' ? state : {};
1309
+ const response = responseMetadata && typeof responseMetadata === 'object' ? responseMetadata : {};
1310
+ const plan = pickFirstObject(widgetState.plan, widgetState.current_plan, widgetState.research_plan);
1311
+ const stepStatusesByPlan = pickFirstObject(
1312
+ widgetState.step_statuses_by_plan,
1313
+ widgetState.stepStatusesByPlan,
1314
+ widgetState.step_statuses,
1315
+ );
1316
+ const progress = {
1317
+ asyncTaskConversationId: stringOrEmpty(
1318
+ response.async_task_conversation_id
1319
+ || response.asyncTaskConversationId
1320
+ || response['openai/asyncTaskConversationId'],
1321
+ ),
1322
+ widgetSessionId: stringOrEmpty(
1323
+ response['openai/widgetSessionId']
1324
+ || response.widget_session_id
1325
+ || response.widgetSessionId,
1326
+ ),
1327
+ asyncStatus: response['openai/asyncStatus'] ?? response.async_status ?? response.asyncStatus,
1328
+ venusMessageType: stringOrEmpty(response.venus_message_type || response.venusMessageType),
1329
+ venusStatus: stringOrEmpty(widgetState.status || widgetState.venus_status || widgetState.venusStatus),
1330
+ waitingForUserUntil: stringOrEmpty(
1331
+ widgetState.waiting_for_user_response_on_plan_until
1332
+ || widgetState.waitingForUserResponseOnPlanUntil
1333
+ || widgetState.waiting_for_user_until,
1334
+ ),
1335
+ planId: stringOrEmpty(plan.plan_id || plan.planId || plan.id),
1336
+ planTitle: stringOrEmpty(plan.title || plan.name),
1337
+ planSteps: compactDeepResearchPlanSteps(plan, stepStatusesByPlan),
1338
+ stepStatusesByPlan,
1339
+ };
1340
+ const status = deepResearchProgressStatus(progress);
1341
+ if (!status) return null;
1342
+
1343
+ if (/^completed$/i.test(progress.venusStatus)
1344
+ && !progress.asyncTaskConversationId
1345
+ && !progress.widgetSessionId
1346
+ && progress.asyncStatus === undefined
1347
+ && !progress.venusMessageType
1348
+ && !progress.planId
1349
+ && !progress.planTitle
1350
+ && !progress.planSteps.length
1351
+ && !Object.keys(progress.stepStatusesByPlan || {}).length) {
1352
+ return null;
1353
+ }
1354
+
1265
1355
  return {
1266
- status: 'completed',
1267
- report,
1356
+ status,
1357
+ report: '',
1268
1358
  html: '',
1269
- method: source,
1270
- sources: extractDeepResearchSourcesFromReportMessage(reportMessage),
1271
- widgetStatus: String(state.status || ''),
1272
- reportMessageId: String(reportMessage?.id || ''),
1273
- reportLength: report.length,
1359
+ method: source.includes('widget-state') ? source.replace('widget-state', 'widget-progress') : `${source}-progress`,
1360
+ sources: [],
1361
+ progress,
1362
+ asyncTaskConversationId: progress.asyncTaskConversationId,
1363
+ widgetSessionId: progress.widgetSessionId,
1364
+ asyncStatus: progress.asyncStatus,
1365
+ venusMessageType: progress.venusMessageType,
1366
+ venusStatus: progress.venusStatus,
1367
+ waitingForUserUntil: progress.waitingForUserUntil,
1368
+ planId: progress.planId,
1369
+ planTitle: progress.planTitle,
1274
1370
  };
1275
1371
  }
1276
1372
 
1373
+ function deepResearchCandidateScore(candidate) {
1374
+ if (!candidate) return 0;
1375
+ if (candidate.status === 'completed') return 1000000 + (candidate.reportLength || candidate.report?.length || 0);
1376
+ if (candidate.status === 'waiting_for_user' || candidate.status === 'needs_user_action') return 500000;
1377
+ if (candidate.status === 'running') return 400000;
1378
+ if (candidate.status === 'not_ready') return 300000;
1379
+ return 1;
1380
+ }
1381
+
1382
+ function selectDeepResearchCandidate(candidates, payload, mapping) {
1383
+ if (!candidates.length) return null;
1384
+ const lineage = new Set();
1385
+ let nodeId = String(payload.current_node || payload.currentNode || '');
1386
+ while (nodeId && !lineage.has(nodeId)) {
1387
+ lineage.add(nodeId);
1388
+ nodeId = String(mapping[nodeId]?.parent || '');
1389
+ }
1390
+ const currentBranch = lineage.size
1391
+ ? candidates.filter((candidate) => lineage.has(candidate.conversationMessageId))
1392
+ : [];
1393
+ const pool = currentBranch.length ? currentBranch : candidates;
1394
+ pool.sort((a, b) => {
1395
+ const createdDelta = b._createdAt - a._createdAt;
1396
+ if (createdDelta) return createdDelta;
1397
+ const orderDelta = b._candidateOrder - a._candidateOrder;
1398
+ if (currentBranch.length && orderDelta) return orderDelta;
1399
+ const scoreDelta = deepResearchCandidateScore(b) - deepResearchCandidateScore(a);
1400
+ return scoreDelta || orderDelta;
1401
+ });
1402
+ const { _createdAt, _candidateOrder, ...selected } = pool[0];
1403
+ return selected;
1404
+ }
1405
+
1406
+ function extractDeepResearchFromWidgetState(widgetState, source = 'conversation-widget-state', responseMetadata = null) {
1407
+ const state = parseJsonMaybe(widgetState);
1408
+ if ((!state || typeof state !== 'object') && !responseMetadata) return null;
1409
+ const widgetStateObject = state && typeof state === 'object' ? state : {};
1410
+ const reportMessage = widgetStateObject.report_message || widgetStateObject.reportMessage || null;
1411
+ const parts = Array.isArray(reportMessage?.content?.parts) ? reportMessage.content.parts : [];
1412
+ const report = normalizeDeepResearchText(parts.filter((part) => typeof part === 'string').join('\n\n'));
1413
+ if (looksLikeDeepResearchReport(report)) {
1414
+ return {
1415
+ status: 'completed',
1416
+ report,
1417
+ html: '',
1418
+ method: source,
1419
+ sources: extractDeepResearchSourcesFromReportMessage(reportMessage),
1420
+ widgetStatus: String(widgetStateObject.status || ''),
1421
+ reportMessageId: String(reportMessage?.id || ''),
1422
+ reportLength: report.length,
1423
+ };
1424
+ }
1425
+ return buildDeepResearchProgressResult(widgetStateObject, pickFirstObject(responseMetadata), source);
1426
+ }
1427
+
1277
1428
  function extractDeepResearchFromConversationPayload(payload, { expectedConversationId = '' } = {}) {
1278
1429
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
1279
1430
  throw new CommandExecutionError('Malformed ChatGPT conversation payload for Deep Research extraction.');
@@ -1291,24 +1442,33 @@ function extractDeepResearchFromConversationPayload(payload, { expectedConversat
1291
1442
  const candidates = [];
1292
1443
  for (const [messageId, node] of Object.entries(mapping)) {
1293
1444
  const message = node?.message || {};
1294
- const sdk = message?.metadata?.chatgpt_sdk;
1445
+ const metadata = message?.metadata || {};
1446
+ const sdk = metadata?.chatgpt_sdk || {};
1447
+ const responseMetadata = pickFirstObject(
1448
+ sdk?.response_metadata,
1449
+ sdk?.responseMetadata,
1450
+ metadata?.response_metadata,
1451
+ metadata?.responseMetadata,
1452
+ );
1295
1453
  for (const widgetState of [
1296
1454
  sdk?.widget_state,
1297
1455
  sdk?.widgetState,
1298
- message?.metadata?.widget_state,
1299
- message?.metadata?.widgetState,
1456
+ metadata?.widget_state,
1457
+ metadata?.widgetState,
1300
1458
  ]) {
1301
- const extracted = extractDeepResearchFromWidgetState(widgetState);
1459
+ if (widgetState === undefined || widgetState === null) continue;
1460
+ const extracted = extractDeepResearchFromWidgetState(widgetState, 'conversation-widget-state', responseMetadata);
1302
1461
  if (extracted) {
1303
1462
  candidates.push({
1304
1463
  ...extracted,
1305
1464
  conversationMessageId: messageId,
1465
+ _createdAt: Number(message.create_time || message.createTime || metadata.create_time || 0) || 0,
1466
+ _candidateOrder: candidates.length,
1306
1467
  });
1307
1468
  }
1308
1469
  }
1309
1470
  }
1310
- candidates.sort((a, b) => b.report.length - a.report.length);
1311
- return candidates[0] || null;
1471
+ return selectDeepResearchCandidate(candidates, payload, mapping);
1312
1472
  }
1313
1473
 
1314
1474
  function conversationIdFromBackendConversationUrl(url) {
@@ -1331,12 +1491,14 @@ function extractDeepResearchFromNetworkEntries(entries, { expectedConversationId
1331
1491
  if (extracted) {
1332
1492
  candidates.push({
1333
1493
  ...extracted,
1334
- method: 'network-conversation-widget-state',
1494
+ method: extracted.status === 'completed'
1495
+ ? 'network-conversation-widget-state'
1496
+ : 'network-conversation-widget-progress',
1335
1497
  networkUrl: url,
1336
1498
  });
1337
1499
  }
1338
1500
  }
1339
- candidates.sort((a, b) => b.report.length - a.report.length);
1501
+ candidates.sort((a, b) => deepResearchCandidateScore(b) - deepResearchCandidateScore(a));
1340
1502
  return candidates[0] || null;
1341
1503
  }
1342
1504
 
@@ -1570,6 +1732,7 @@ export async function getChatGPTDeepResearchResult(page, { conversationId = '',
1570
1732
  );
1571
1733
  }
1572
1734
  }
1735
+ let progressCandidate = null;
1573
1736
  const diagnostics = {
1574
1737
  iframeCount: Array.isArray(iframeState.iframes) ? iframeState.iframes.length : 0,
1575
1738
  iframe: iframe ? {
@@ -1605,19 +1768,26 @@ export async function getChatGPTDeepResearchResult(page, { conversationId = '',
1605
1768
  const extracted = extractDeepResearchFromNetworkEntries(relevantEntries, { expectedConversationId: conversationId });
1606
1769
  if (extracted) {
1607
1770
  diagnostics.networkConversation = {
1608
- foundReport: true,
1771
+ foundReport: extracted.status === 'completed',
1772
+ foundProgress: extracted.status !== 'completed',
1773
+ status: extracted.status,
1609
1774
  reportLength: extracted.reportLength || extracted.report.length,
1610
1775
  sourceCount: Array.isArray(extracted.sources) ? extracted.sources.length : 0,
1776
+ venusStatus: extracted.venusStatus || '',
1777
+ asyncTaskConversationId: extracted.asyncTaskConversationId || '',
1611
1778
  };
1612
- return {
1613
- status: 'completed',
1614
- report: extracted.report,
1615
- html: '',
1616
- url: iframeState.url,
1617
- method: extracted.method,
1618
- sources: extracted.sources || [],
1619
- diagnostics,
1620
- };
1779
+ if (extracted.status === 'completed') {
1780
+ return {
1781
+ status: 'completed',
1782
+ report: extracted.report,
1783
+ html: '',
1784
+ url: iframeState.url,
1785
+ method: extracted.method,
1786
+ sources: extracted.sources || [],
1787
+ diagnostics,
1788
+ };
1789
+ }
1790
+ progressCandidate = extracted;
1621
1791
  }
1622
1792
  } catch (error) {
1623
1793
  if (error instanceof CommandExecutionError) throw error;
@@ -1640,12 +1810,16 @@ export async function getChatGPTDeepResearchResult(page, { conversationId = '',
1640
1810
  status: conversation?.status || 0,
1641
1811
  contentType: conversation?.contentType || '',
1642
1812
  transport: conversation?.transport || '',
1643
- foundReport: !!extracted,
1813
+ foundReport: extracted?.status === 'completed',
1814
+ foundProgress: !!extracted && extracted.status !== 'completed',
1815
+ deepResearchStatus: extracted?.status || '',
1644
1816
  reportLength: extracted?.reportLength || 0,
1645
1817
  widgetStatus: extracted?.widgetStatus || '',
1818
+ venusStatus: extracted?.venusStatus || '',
1819
+ asyncTaskConversationId: extracted?.asyncTaskConversationId || '',
1646
1820
  sourceCount: Array.isArray(extracted?.sources) ? extracted.sources.length : 0,
1647
1821
  };
1648
- if (extracted) {
1822
+ if (extracted?.status === 'completed') {
1649
1823
  return {
1650
1824
  status: 'completed',
1651
1825
  report: extracted.report,
@@ -1656,6 +1830,7 @@ export async function getChatGPTDeepResearchResult(page, { conversationId = '',
1656
1830
  diagnostics,
1657
1831
  };
1658
1832
  }
1833
+ if (extracted) progressCandidate = extracted;
1659
1834
  }
1660
1835
  } catch (error) {
1661
1836
  if (error instanceof CommandExecutionError) throw error;
@@ -1663,6 +1838,14 @@ export async function getChatGPTDeepResearchResult(page, { conversationId = '',
1663
1838
  }
1664
1839
  }
1665
1840
 
1841
+ if (progressCandidate) {
1842
+ return {
1843
+ ...progressCandidate,
1844
+ url: iframeState.url,
1845
+ diagnostics,
1846
+ };
1847
+ }
1848
+
1666
1849
  if (iframe?.text && looksLikeDeepResearchReport(iframe.text)) {
1667
1850
  return {
1668
1851
  status: 'completed',
@@ -1820,8 +2003,10 @@ export async function waitForChatGPTDeepResearchResult(page, { conversationId =
1820
2003
  lastReport = result.report;
1821
2004
  stableStartedAt = Date.now();
1822
2005
  }
2006
+ } else if (result.status === 'waiting_for_user' || result.status === 'needs_user_action') {
2007
+ return result;
1823
2008
  }
1824
- await page.wait(3);
2009
+ await page.sleep(3);
1825
2010
  }
1826
2011
 
1827
2012
  throw new TimeoutError(
@@ -1912,7 +2097,7 @@ export async function waitForChatGPTResponse(page, baselineCount, prompt, timeou
1912
2097
  const baselinePairCounts = normalizeBaselinePairCounts(options);
1913
2098
 
1914
2099
  while (Date.now() - startTime < timeoutSeconds * 1000) {
1915
- await page.wait(3);
2100
+ await page.sleep(3);
1916
2101
  if (options.conversationUrl) {
1917
2102
  const currentUrl = await currentChatGPTUrl(page);
1918
2103
  if (currentUrl && !isSameChatGPTConversation(currentUrl, options.conversationUrl)) {
@@ -1926,7 +2111,7 @@ export async function waitForChatGPTResponse(page, baselineCount, prompt, timeou
1926
2111
  continue;
1927
2112
  }
1928
2113
 
1929
- const messages = await getVisibleMessages(page);
2114
+ const messages = await getVisibleMessages(page, { textOnly: true });
1930
2115
  const candidate = findLatestNewAssistantResponse(messages, prompt, baselinePairCounts);
1931
2116
  if (!candidate || candidate === String(prompt || '').trim()) continue;
1932
2117
 
@@ -2056,7 +2241,7 @@ export async function prepareChatGPTImagePaths(imagePaths) {
2056
2241
  async function waitForChatGPTUploadPreview(page, fileNames) {
2057
2242
  const namesJson = JSON.stringify(fileNames);
2058
2243
  for (let attempt = 0; attempt < 10; attempt += 1) {
2059
- await page.wait(1);
2244
+ await page.sleep(1);
2060
2245
  const ready = requireBooleanEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
2061
2246
  (() => {
2062
2247
  const names = ${namesJson};
@@ -2170,15 +2355,36 @@ export async function uploadChatGPTImages(page, imagePaths) {
2170
2355
  export async function isGenerating(page) {
2171
2356
  return requireBooleanEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
2172
2357
  (() => {
2173
- const text = (document.body?.innerText || '').replace(/\\s+/g, ' ');
2174
- if (/Thinking|Stop generating|Thinking/.test(text)) return true;
2175
- return Array.from(document.querySelectorAll('button')).some(b => {
2176
- const label = b.getAttribute('aria-label') || '';
2177
- return label === 'Stop generating'
2178
- || label.includes('Thinking')
2179
- || label.includes('Stop generating')
2180
- || label.includes('Thinking');
2181
- });
2358
+ if (document.querySelector('[data-testid="stop-button"]')) return true;
2359
+
2360
+ const controls = Array.from(document.querySelectorAll('button, [role="button"], [aria-label]'));
2361
+ for (const control of controls) {
2362
+ const label = control.getAttribute('aria-label') || '';
2363
+ if (label.includes('Stop generating')) return true;
2364
+ }
2365
+
2366
+ const scopes = [];
2367
+ const turns = document.querySelectorAll('article[data-testid*="conversation-turn"]');
2368
+ const messages = turns.length ? turns : document.querySelectorAll('[data-message-author-role]');
2369
+ if (messages.length) {
2370
+ scopes.push([messages[messages.length - 1], /Thinking|Stop generating/]);
2371
+ }
2372
+ const composer = document.querySelector('#prompt-textarea, [aria-label="Chat with ChatGPT"]');
2373
+ if (composer) {
2374
+ let root = composer;
2375
+ for (let i = 0; i < 4 && root.parentElement; i += 1) root = root.parentElement;
2376
+ scopes.push([root, /Stop generating/]);
2377
+ }
2378
+
2379
+ for (const [scope, pattern] of scopes) {
2380
+ for (const el of [scope, ...scope.querySelectorAll('*')]) {
2381
+ if (el.children.length) continue;
2382
+ if (el.closest('.markdown, pre, code')) continue;
2383
+ const text = (el.textContent || '').trim();
2384
+ if (text && text.length <= 40 && pattern.test(text)) return true;
2385
+ }
2386
+ }
2387
+ return false;
2182
2388
  })()
2183
2389
  `)), 'chatgpt generation state');
2184
2390
  }
@@ -2319,7 +2525,7 @@ export async function waitForChatGPTImages(page, beforeUrls, timeoutSeconds, con
2319
2525
  let stableCount = 0;
2320
2526
 
2321
2527
  for (let i = 0; i < maxPolls; i++) {
2322
- await page.wait(i === 0 ? 3 : pollIntervalSeconds);
2528
+ await page.sleep(i === 0 ? 3 : pollIntervalSeconds);
2323
2529
 
2324
2530
  let currentUrl = '';
2325
2531
  if (convUrl && convUrl.includes('/c/')) {