ai-browser 0.2.3 → 0.2.5
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.
- package/README.md +62 -5
- package/dist/agent/agent-loop.d.ts +8 -2
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +138 -86
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/config.d.ts +5 -0
- package/dist/agent/config.d.ts.map +1 -1
- package/dist/agent/config.js +5 -0
- package/dist/agent/config.js.map +1 -1
- package/dist/agent/content-budget.d.ts +11 -0
- package/dist/agent/content-budget.d.ts.map +1 -0
- package/dist/agent/content-budget.js +129 -0
- package/dist/agent/content-budget.js.map +1 -0
- package/dist/agent/conversation-manager.d.ts +48 -0
- package/dist/agent/conversation-manager.d.ts.map +1 -0
- package/dist/agent/conversation-manager.js +157 -0
- package/dist/agent/conversation-manager.js.map +1 -0
- package/dist/agent/error-recovery.d.ts +29 -0
- package/dist/agent/error-recovery.d.ts.map +1 -0
- package/dist/agent/error-recovery.js +72 -0
- package/dist/agent/error-recovery.js.map +1 -0
- package/dist/agent/page-state-cache.d.ts +22 -0
- package/dist/agent/page-state-cache.d.ts.map +1 -0
- package/dist/agent/page-state-cache.js +71 -0
- package/dist/agent/page-state-cache.js.map +1 -0
- package/dist/agent/progress-estimator.d.ts +17 -0
- package/dist/agent/progress-estimator.d.ts.map +1 -0
- package/dist/agent/progress-estimator.js +67 -0
- package/dist/agent/progress-estimator.js.map +1 -0
- package/dist/agent/prompt.d.ts +1 -1
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js +83 -48
- package/dist/agent/prompt.js.map +1 -1
- package/dist/agent/task-agent.d.ts +89 -0
- package/dist/agent/task-agent.d.ts.map +1 -0
- package/dist/agent/task-agent.js +448 -0
- package/dist/agent/task-agent.js.map +1 -0
- package/dist/agent/token-tracker.d.ts +22 -0
- package/dist/agent/token-tracker.d.ts.map +1 -0
- package/dist/agent/token-tracker.js +29 -0
- package/dist/agent/token-tracker.js.map +1 -0
- package/dist/agent/tool-usage-tracker.d.ts +45 -0
- package/dist/agent/tool-usage-tracker.d.ts.map +1 -0
- package/dist/agent/tool-usage-tracker.js +145 -0
- package/dist/agent/tool-usage-tracker.js.map +1 -0
- package/dist/agent/types.d.ts +24 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +305 -1
- package/dist/api/routes.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/ai-markdown.d.ts +2 -0
- package/dist/mcp/ai-markdown.d.ts.map +1 -0
- package/dist/mcp/ai-markdown.js +1739 -0
- package/dist/mcp/ai-markdown.js.map +1 -0
- package/dist/mcp/browser-mcp-server.d.ts.map +1 -1
- package/dist/mcp/browser-mcp-server.js +279 -49
- package/dist/mcp/browser-mcp-server.js.map +1 -1
- package/dist/mcp/task-tools.d.ts.map +1 -1
- package/dist/mcp/task-tools.js +107 -13
- package/dist/mcp/task-tools.js.map +1 -1
- package/dist/task/tool-actions.d.ts +4 -0
- package/dist/task/tool-actions.d.ts.map +1 -1
- package/dist/task/tool-actions.js +72 -0
- package/dist/task/tool-actions.js.map +1 -1
- package/package.json +5 -2
- package/public/index.html +593 -41
- package/public/task-result.html +135 -0
- package/public/tasks.html +126 -0
|
@@ -0,0 +1,1739 @@
|
|
|
1
|
+
const AI_SCHEMA_VERSION = '1.0';
|
|
2
|
+
const DETAIL_LEVEL_ENV = 'AI_MARKDOWN_DETAIL_LEVEL';
|
|
3
|
+
const ADAPTIVE_DETAIL_ENV = 'AI_MARKDOWN_ADAPTIVE_POLICY';
|
|
4
|
+
const DELTA_CACHE_LIMIT = 512;
|
|
5
|
+
const deltaSnapshotCache = new Map();
|
|
6
|
+
export function enrichWithAiMarkdown(toolName, data) {
|
|
7
|
+
if (!isObject(data))
|
|
8
|
+
return data;
|
|
9
|
+
if (typeof data.aiMarkdown === 'string')
|
|
10
|
+
return data;
|
|
11
|
+
const normalizedTool = normalizeToolName(toolName);
|
|
12
|
+
if (!normalizedTool)
|
|
13
|
+
return data;
|
|
14
|
+
const detailDecision = resolveDetailDecision(normalizedTool, data);
|
|
15
|
+
const detailLevel = detailDecision.level;
|
|
16
|
+
const dataWithDetail = { ...data, aiDetailLevel: detailLevel };
|
|
17
|
+
const aiMarkdown = buildAiMarkdown(normalizedTool, dataWithDetail);
|
|
18
|
+
if (!aiMarkdown)
|
|
19
|
+
return data;
|
|
20
|
+
const aiSummary = buildAiSummary(normalizedTool, dataWithDetail);
|
|
21
|
+
const aiHints = buildAiHints(normalizedTool, dataWithDetail);
|
|
22
|
+
const nextActions = buildNextActions(normalizedTool, dataWithDetail, detailLevel);
|
|
23
|
+
const deltaSummary = buildDeltaSummary(normalizedTool, dataWithDetail);
|
|
24
|
+
const schemaRepairGuidance = buildSchemaRepairGuidance(normalizedTool, dataWithDetail);
|
|
25
|
+
return {
|
|
26
|
+
...data,
|
|
27
|
+
aiSchemaVersion: AI_SCHEMA_VERSION,
|
|
28
|
+
aiDetailLevel: detailLevel,
|
|
29
|
+
aiDetailPolicy: {
|
|
30
|
+
mode: detailDecision.mode,
|
|
31
|
+
source: detailDecision.source,
|
|
32
|
+
reason: detailDecision.reason,
|
|
33
|
+
},
|
|
34
|
+
aiSummary,
|
|
35
|
+
aiMarkdown,
|
|
36
|
+
aiHints,
|
|
37
|
+
nextActions,
|
|
38
|
+
...(deltaSummary ? { deltaSummary } : {}),
|
|
39
|
+
...(schemaRepairGuidance ? { schemaRepairGuidance } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function normalizeToolName(toolName) {
|
|
43
|
+
const direct = [
|
|
44
|
+
'create_session',
|
|
45
|
+
'close_session',
|
|
46
|
+
'navigate',
|
|
47
|
+
'get_page_info',
|
|
48
|
+
'get_page_content',
|
|
49
|
+
'find_element',
|
|
50
|
+
'click',
|
|
51
|
+
'type_text',
|
|
52
|
+
'press_key',
|
|
53
|
+
'wait',
|
|
54
|
+
'wait_for_stable',
|
|
55
|
+
'scroll',
|
|
56
|
+
'go_back',
|
|
57
|
+
'select_option',
|
|
58
|
+
'hover',
|
|
59
|
+
'set_value',
|
|
60
|
+
'switch_tab',
|
|
61
|
+
'close_tab',
|
|
62
|
+
'create_tab',
|
|
63
|
+
'list_tabs',
|
|
64
|
+
'screenshot',
|
|
65
|
+
'execute_javascript',
|
|
66
|
+
'handle_dialog',
|
|
67
|
+
'get_dialog_info',
|
|
68
|
+
'get_network_logs',
|
|
69
|
+
'get_console_logs',
|
|
70
|
+
'upload_file',
|
|
71
|
+
'get_downloads',
|
|
72
|
+
'list_task_templates',
|
|
73
|
+
'run_task_template',
|
|
74
|
+
'get_task_run',
|
|
75
|
+
'list_task_runs',
|
|
76
|
+
'cancel_task_run',
|
|
77
|
+
'get_artifact',
|
|
78
|
+
'get_runtime_profile',
|
|
79
|
+
'fill_form',
|
|
80
|
+
'click_and_wait',
|
|
81
|
+
'navigate_and_extract',
|
|
82
|
+
];
|
|
83
|
+
return direct.includes(toolName) ? toolName : null;
|
|
84
|
+
}
|
|
85
|
+
function buildAiMarkdown(toolName, data) {
|
|
86
|
+
switch (toolName) {
|
|
87
|
+
case 'create_session':
|
|
88
|
+
return formatCreateSession(data);
|
|
89
|
+
case 'close_session':
|
|
90
|
+
return formatCloseSession(data);
|
|
91
|
+
case 'navigate':
|
|
92
|
+
return formatNavigate(data);
|
|
93
|
+
case 'get_page_info':
|
|
94
|
+
return formatPageInfo(data);
|
|
95
|
+
case 'get_page_content':
|
|
96
|
+
return formatPageContent(data);
|
|
97
|
+
case 'find_element':
|
|
98
|
+
return formatFindElement(data);
|
|
99
|
+
case 'click':
|
|
100
|
+
case 'type_text':
|
|
101
|
+
case 'press_key':
|
|
102
|
+
case 'wait':
|
|
103
|
+
case 'wait_for_stable':
|
|
104
|
+
case 'scroll':
|
|
105
|
+
case 'go_back':
|
|
106
|
+
case 'select_option':
|
|
107
|
+
case 'hover':
|
|
108
|
+
case 'set_value':
|
|
109
|
+
case 'switch_tab':
|
|
110
|
+
case 'close_tab':
|
|
111
|
+
case 'handle_dialog':
|
|
112
|
+
case 'upload_file':
|
|
113
|
+
return formatActionResult(toolName, data);
|
|
114
|
+
case 'create_tab':
|
|
115
|
+
return formatCreateTab(data);
|
|
116
|
+
case 'list_tabs':
|
|
117
|
+
return formatTabList(data);
|
|
118
|
+
case 'screenshot':
|
|
119
|
+
return formatScreenshotMeta(data);
|
|
120
|
+
case 'execute_javascript':
|
|
121
|
+
return formatExecuteJavascript(data);
|
|
122
|
+
case 'get_dialog_info':
|
|
123
|
+
return formatDialogInfo(data);
|
|
124
|
+
case 'get_network_logs':
|
|
125
|
+
return formatNetworkLogs(data);
|
|
126
|
+
case 'get_console_logs':
|
|
127
|
+
return formatConsoleLogs(data);
|
|
128
|
+
case 'get_downloads':
|
|
129
|
+
return formatDownloads(data);
|
|
130
|
+
case 'list_task_templates':
|
|
131
|
+
return formatTemplateList(data);
|
|
132
|
+
case 'run_task_template':
|
|
133
|
+
return formatRunSubmit(data);
|
|
134
|
+
case 'get_task_run':
|
|
135
|
+
return formatRunStatus(data);
|
|
136
|
+
case 'list_task_runs':
|
|
137
|
+
return formatRunList(data);
|
|
138
|
+
case 'cancel_task_run':
|
|
139
|
+
return formatCancelTaskRun(data);
|
|
140
|
+
case 'get_artifact':
|
|
141
|
+
return formatArtifact(data);
|
|
142
|
+
case 'get_runtime_profile':
|
|
143
|
+
return formatRuntimeProfile(data);
|
|
144
|
+
case 'fill_form':
|
|
145
|
+
return formatFillForm(data);
|
|
146
|
+
case 'click_and_wait':
|
|
147
|
+
return formatClickAndWait(data);
|
|
148
|
+
case 'navigate_and_extract':
|
|
149
|
+
return formatNavigateAndExtract(data);
|
|
150
|
+
default:
|
|
151
|
+
return '';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function buildAiSummary(toolName, data) {
|
|
155
|
+
switch (toolName) {
|
|
156
|
+
case 'create_session':
|
|
157
|
+
return `Session created: ${asString(data.sessionId) || 'unknown-session'}`;
|
|
158
|
+
case 'close_session':
|
|
159
|
+
return `Session close result: success=${boolText(data.success)}${data.kept ? ', kept=headful' : ''}`;
|
|
160
|
+
case 'navigate': {
|
|
161
|
+
const pageTitle = asString(data.page?.title) || 'Untitled';
|
|
162
|
+
const pageUrl = asString(data.page?.url) || 'unknown-url';
|
|
163
|
+
const partial = Boolean(data.partial);
|
|
164
|
+
return partial
|
|
165
|
+
? `Navigation partially completed: ${pageTitle} (${pageUrl})`
|
|
166
|
+
: `Navigation completed: ${pageTitle} (${pageUrl})`;
|
|
167
|
+
}
|
|
168
|
+
case 'get_page_info': {
|
|
169
|
+
const count = Array.isArray(data.elements) ? data.elements.length : 0;
|
|
170
|
+
const total = numberOrNull(data.totalElements);
|
|
171
|
+
const title = asString(data.page?.title) || 'Untitled';
|
|
172
|
+
if (typeof total === 'number') {
|
|
173
|
+
return `Collected ${count}/${total} interactive elements from ${title}`;
|
|
174
|
+
}
|
|
175
|
+
return `Collected ${count} interactive elements from ${title}`;
|
|
176
|
+
}
|
|
177
|
+
case 'get_page_content': {
|
|
178
|
+
const sections = Array.isArray(data.sections) ? data.sections.length : 0;
|
|
179
|
+
const title = asString(data.title) || 'Page content';
|
|
180
|
+
return `Extracted ${sections} content sections from ${title}`;
|
|
181
|
+
}
|
|
182
|
+
case 'find_element': {
|
|
183
|
+
const candidates = Array.isArray(data.candidates) ? data.candidates.length : 0;
|
|
184
|
+
return `Found ${candidates} candidates for query: ${asString(data.query) || '-'}`;
|
|
185
|
+
}
|
|
186
|
+
case 'click':
|
|
187
|
+
case 'type_text':
|
|
188
|
+
case 'press_key':
|
|
189
|
+
case 'wait':
|
|
190
|
+
case 'wait_for_stable':
|
|
191
|
+
case 'scroll':
|
|
192
|
+
case 'go_back':
|
|
193
|
+
case 'select_option':
|
|
194
|
+
case 'hover':
|
|
195
|
+
case 'set_value':
|
|
196
|
+
case 'switch_tab':
|
|
197
|
+
case 'close_tab':
|
|
198
|
+
case 'handle_dialog':
|
|
199
|
+
case 'upload_file':
|
|
200
|
+
return `${toolName} executed: success=${boolText(data.success)}`;
|
|
201
|
+
case 'create_tab':
|
|
202
|
+
return `Created tab: ${asString(data.tabId) || 'unknown-tab'}${data.partial ? ' (partial navigation)' : ''}`;
|
|
203
|
+
case 'list_tabs': {
|
|
204
|
+
const tabs = Array.isArray(data.tabs) ? data.tabs.length : 0;
|
|
205
|
+
const active = asString(data.activeTabId) || 'unknown';
|
|
206
|
+
return `Listed ${tabs} tabs (active: ${active})`;
|
|
207
|
+
}
|
|
208
|
+
case 'screenshot':
|
|
209
|
+
return `Screenshot captured: ${asString(data.title) || asString(data.url) || 'current page'}`;
|
|
210
|
+
case 'execute_javascript': {
|
|
211
|
+
const truncated = Boolean(data.truncated);
|
|
212
|
+
return truncated ? 'JavaScript executed (result truncated)' : 'JavaScript executed';
|
|
213
|
+
}
|
|
214
|
+
case 'get_dialog_info': {
|
|
215
|
+
const pending = data.pendingDialog ? 'yes' : 'no';
|
|
216
|
+
const history = Array.isArray(data.dialogHistory) ? data.dialogHistory.length : 0;
|
|
217
|
+
return `Dialog status: pending=${pending}, history=${history}`;
|
|
218
|
+
}
|
|
219
|
+
case 'get_network_logs': {
|
|
220
|
+
const count = Array.isArray(data.logs) ? data.logs.length : 0;
|
|
221
|
+
const total = numberOrNull(data.totalCount);
|
|
222
|
+
if (typeof total === 'number')
|
|
223
|
+
return `Network logs: returned ${count}/${total}`;
|
|
224
|
+
return `Network logs: returned ${count}`;
|
|
225
|
+
}
|
|
226
|
+
case 'get_console_logs': {
|
|
227
|
+
const logs = Array.isArray(data.logs) ? data.logs : [];
|
|
228
|
+
const errors = logs.filter((l) => asString(l?.level) === 'error').length;
|
|
229
|
+
const warns = logs.filter((l) => asString(l?.level) === 'warn').length;
|
|
230
|
+
return `Console logs: ${logs.length} entries (error=${errors}, warn=${warns})`;
|
|
231
|
+
}
|
|
232
|
+
case 'get_downloads': {
|
|
233
|
+
const downloads = Array.isArray(data.downloads) ? data.downloads.length : 0;
|
|
234
|
+
return `Downloads listed: ${downloads}`;
|
|
235
|
+
}
|
|
236
|
+
case 'list_task_templates': {
|
|
237
|
+
const templates = Array.isArray(data.templates) ? data.templates.length : 0;
|
|
238
|
+
return `Listed ${templates} task templates`;
|
|
239
|
+
}
|
|
240
|
+
case 'run_task_template':
|
|
241
|
+
return `Task run submitted: ${asString(data.runId) || 'unknown-run'} (${asString(data.status) || 'unknown-status'})`;
|
|
242
|
+
case 'get_task_run': {
|
|
243
|
+
const verification = extractVerificationSnapshot(data);
|
|
244
|
+
if (verification && !verification.pass) {
|
|
245
|
+
const miss = verification.missingFields.length;
|
|
246
|
+
const mismatch = verification.typeMismatches.length;
|
|
247
|
+
return `Task run status: ${asString(data.runId) || 'unknown-run'} -> ${asString(data.status) || 'unknown-status'} (schema gaps: missing=${miss}, type=${mismatch})`;
|
|
248
|
+
}
|
|
249
|
+
return `Task run status: ${asString(data.runId) || 'unknown-run'} -> ${asString(data.status) || 'unknown-status'}`;
|
|
250
|
+
}
|
|
251
|
+
case 'list_task_runs': {
|
|
252
|
+
const runs = Array.isArray(data.runs) ? data.runs.length : 0;
|
|
253
|
+
return `Listed ${runs} task runs`;
|
|
254
|
+
}
|
|
255
|
+
case 'cancel_task_run':
|
|
256
|
+
return `Cancel task result: success=${boolText(data.success)}`;
|
|
257
|
+
case 'get_artifact':
|
|
258
|
+
return `Artifact chunk: ${asString(data.artifactId) || 'unknown-artifact'} (${numberOrNull(data.length) ?? 0} bytes)`;
|
|
259
|
+
case 'get_runtime_profile':
|
|
260
|
+
return `Runtime profile: maxConcurrentRuns=${numberOrNull(data.maxConcurrentRuns) ?? '-'}, trustLevel=${asString(data.trustLevel) || '-'}`;
|
|
261
|
+
case 'fill_form': {
|
|
262
|
+
const results = Array.isArray(data.results) ? data.results : [];
|
|
263
|
+
const ok = results.filter((r) => r?.success).length;
|
|
264
|
+
const submitOk = data.submitResult ? boolText(data.submitResult.success) : 'skipped';
|
|
265
|
+
return `Form filled: ${ok}/${results.length} fields succeeded, submit=${submitOk}`;
|
|
266
|
+
}
|
|
267
|
+
case 'click_and_wait': {
|
|
268
|
+
const clickOk = boolText(data.clickResult?.success);
|
|
269
|
+
const waitOk = boolText(data.waitResult?.success);
|
|
270
|
+
return `Click+wait: click=${clickOk}, wait=${waitOk}`;
|
|
271
|
+
}
|
|
272
|
+
case 'navigate_and_extract': {
|
|
273
|
+
const navOk = boolText(data.navigateResult?.success);
|
|
274
|
+
const title = asString(data.navigateResult?.page?.title) || '-';
|
|
275
|
+
return `Navigate+extract: nav=${navOk}, page=${title}`;
|
|
276
|
+
}
|
|
277
|
+
default:
|
|
278
|
+
return `${toolName} completed`;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function buildAiHints(toolName, data) {
|
|
282
|
+
switch (toolName) {
|
|
283
|
+
case 'create_session':
|
|
284
|
+
return ['Use the returned sessionId for follow-up actions.'];
|
|
285
|
+
case 'close_session':
|
|
286
|
+
return data.kept
|
|
287
|
+
? ['Session is preserved because it is headful; set force=true if closure is required.']
|
|
288
|
+
: ['Create a new session if more actions are needed.'];
|
|
289
|
+
case 'navigate': {
|
|
290
|
+
const hints = ['Call get_page_info next to discover actionable elements.'];
|
|
291
|
+
if (Boolean(data.partial)) {
|
|
292
|
+
hints.unshift('Page load was partial; consider wait_for_stable before interacting.');
|
|
293
|
+
}
|
|
294
|
+
return hints;
|
|
295
|
+
}
|
|
296
|
+
case 'get_page_info': {
|
|
297
|
+
const hints = ['Use element IDs from the table for click/type_text operations.'];
|
|
298
|
+
if (Boolean(data.truncated)) {
|
|
299
|
+
hints.push('Results are truncated; rerun with a larger maxElements if needed.');
|
|
300
|
+
}
|
|
301
|
+
if (data.pendingDialog) {
|
|
302
|
+
hints.push('A dialog is pending; use handle_dialog before further actions.');
|
|
303
|
+
}
|
|
304
|
+
return hints;
|
|
305
|
+
}
|
|
306
|
+
case 'get_page_content':
|
|
307
|
+
return ['If key information is missing, scroll and call get_page_content again.'];
|
|
308
|
+
case 'find_element':
|
|
309
|
+
return ['Pick the highest score candidate first; fallback to next candidate on failure.'];
|
|
310
|
+
case 'scroll':
|
|
311
|
+
return ['After scrolling, call get_page_info or get_page_content to refresh visible information.'];
|
|
312
|
+
case 'go_back':
|
|
313
|
+
return ['Use get_page_info to inspect the previous page state after navigation.'];
|
|
314
|
+
case 'select_option':
|
|
315
|
+
return ['If selection has downstream effects, wait briefly and fetch updated page info.'];
|
|
316
|
+
case 'hover':
|
|
317
|
+
return ['Hover may reveal menus/tooltips; call get_page_info to capture newly visible controls.'];
|
|
318
|
+
case 'set_value':
|
|
319
|
+
return ['After setting value, submit form via press_key Enter or click the submit control.'];
|
|
320
|
+
case 'switch_tab':
|
|
321
|
+
return ['Continue operations in the active tab or list_tabs to confirm context.'];
|
|
322
|
+
case 'close_tab':
|
|
323
|
+
return ['Call list_tabs to verify remaining tabs and active context.'];
|
|
324
|
+
case 'create_tab':
|
|
325
|
+
return ['If partial=true, call wait_for_stable then inspect page info.'];
|
|
326
|
+
case 'list_tabs':
|
|
327
|
+
return ['Switch context with switch_tab before interacting with a non-active tab.'];
|
|
328
|
+
case 'screenshot':
|
|
329
|
+
return ['Use get_page_info/get_page_content for structured reasoning instead of image-only analysis.'];
|
|
330
|
+
case 'execute_javascript':
|
|
331
|
+
return ['Prefer deterministic return values; avoid relying on console.log output.'];
|
|
332
|
+
case 'handle_dialog':
|
|
333
|
+
return data.success
|
|
334
|
+
? ['Dialog handled; continue the original interaction flow.']
|
|
335
|
+
: ['No dialog was handled; call get_dialog_info to inspect current dialog state.'];
|
|
336
|
+
case 'get_dialog_info':
|
|
337
|
+
return data.pendingDialog
|
|
338
|
+
? ['A dialog is pending; call handle_dialog before further page actions.']
|
|
339
|
+
: ['No blocking dialog now; continue normal page interaction flow.'];
|
|
340
|
+
case 'get_network_logs':
|
|
341
|
+
return ['Use filter=failed or filter=slow to quickly isolate problematic requests.'];
|
|
342
|
+
case 'get_console_logs':
|
|
343
|
+
return ['Focus on error/warn entries first; rerun with level=all only if needed.'];
|
|
344
|
+
case 'upload_file':
|
|
345
|
+
return ['Call get_page_info to confirm upload-related UI state changes.'];
|
|
346
|
+
case 'get_downloads':
|
|
347
|
+
return ['Use the latest download entry to confirm completion or inspect error fields.'];
|
|
348
|
+
case 'list_task_templates':
|
|
349
|
+
return ['Pick a templateId and call run_task_template with validated inputs.'];
|
|
350
|
+
case 'run_task_template':
|
|
351
|
+
return ['Use get_task_run with runId to monitor progress until terminal status.'];
|
|
352
|
+
case 'get_task_run': {
|
|
353
|
+
const status = asString(data.status);
|
|
354
|
+
const verification = extractVerificationSnapshot(data);
|
|
355
|
+
if (verification && !verification.pass) {
|
|
356
|
+
const hints = [];
|
|
357
|
+
if (verification.missingFields.length > 0) {
|
|
358
|
+
hints.push(`Schema missing fields: ${verification.missingFields.slice(0, 8).join(', ')}.`);
|
|
359
|
+
}
|
|
360
|
+
if (verification.typeMismatches.length > 0) {
|
|
361
|
+
hints.push(`Schema type mismatches: ${verification.typeMismatches.slice(0, 8).join(', ')}.`);
|
|
362
|
+
}
|
|
363
|
+
hints.push('Collect stronger evidence before retrying; prioritize missing fields first.');
|
|
364
|
+
return hints;
|
|
365
|
+
}
|
|
366
|
+
if (status === 'running' || status === 'queued') {
|
|
367
|
+
return ['Continue polling get_task_run until terminal status.'];
|
|
368
|
+
}
|
|
369
|
+
if (status === 'succeeded' || status === 'partial_success') {
|
|
370
|
+
return ['If artifactIds exist, call get_artifact to fetch evidence chunks.'];
|
|
371
|
+
}
|
|
372
|
+
return ['Check error details and adjust inputs before retrying.'];
|
|
373
|
+
}
|
|
374
|
+
case 'list_task_runs':
|
|
375
|
+
return ['Select a runId and call get_task_run for full details.'];
|
|
376
|
+
case 'cancel_task_run':
|
|
377
|
+
return ['If cancellation fails due to terminal status, inspect run details with get_task_run.'];
|
|
378
|
+
case 'get_artifact':
|
|
379
|
+
return ['If complete=false, increase offset and call get_artifact again.'];
|
|
380
|
+
case 'get_runtime_profile':
|
|
381
|
+
return ['Use runtime limits to tune task batch size, mode, and polling strategy.'];
|
|
382
|
+
case 'fill_form':
|
|
383
|
+
return [
|
|
384
|
+
'Check individual field results for failures; retry failed fields individually with type_text.',
|
|
385
|
+
'If submit was skipped, click the submit button or use press_key Enter.',
|
|
386
|
+
];
|
|
387
|
+
case 'click_and_wait':
|
|
388
|
+
return [
|
|
389
|
+
'Call get_page_info to inspect the page after click+wait completes.',
|
|
390
|
+
'If waitResult failed, try wait_for_stable manually with a longer timeout.',
|
|
391
|
+
];
|
|
392
|
+
case 'navigate_and_extract':
|
|
393
|
+
return [
|
|
394
|
+
'Review extracted content; if incomplete, call get_page_content with scroll.',
|
|
395
|
+
'Use get_page_info if you need interactive elements instead of text content.',
|
|
396
|
+
];
|
|
397
|
+
default:
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function resolveDetailDecision(toolName, data) {
|
|
402
|
+
const fromData = parseDetailLevel(data?.aiDetailLevel);
|
|
403
|
+
if (fromData) {
|
|
404
|
+
return {
|
|
405
|
+
level: fromData,
|
|
406
|
+
source: 'data',
|
|
407
|
+
mode: 'fixed',
|
|
408
|
+
reason: 'detail level explicitly provided in payload',
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const fromEnv = parseDetailLevel(process.env[DETAIL_LEVEL_ENV]);
|
|
412
|
+
const baseLevel = fromEnv ?? 'normal';
|
|
413
|
+
const source = fromEnv ? 'env' : 'default';
|
|
414
|
+
if (!parseBooleanEnv(process.env[ADAPTIVE_DETAIL_ENV])) {
|
|
415
|
+
return {
|
|
416
|
+
level: baseLevel,
|
|
417
|
+
source,
|
|
418
|
+
mode: 'fixed',
|
|
419
|
+
reason: source === 'env' ? 'detail level provided by environment' : 'default detail level',
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const adaptive = computeAdaptiveDetail(toolName, data, baseLevel);
|
|
423
|
+
if (adaptive.level === baseLevel) {
|
|
424
|
+
return {
|
|
425
|
+
level: baseLevel,
|
|
426
|
+
source,
|
|
427
|
+
mode: 'fixed',
|
|
428
|
+
reason: adaptive.reason,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
level: adaptive.level,
|
|
433
|
+
source,
|
|
434
|
+
mode: 'adaptive',
|
|
435
|
+
reason: adaptive.reason,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function resolveDetailLevel(data) {
|
|
439
|
+
const fromData = parseDetailLevel(data?.aiDetailLevel);
|
|
440
|
+
if (fromData)
|
|
441
|
+
return fromData;
|
|
442
|
+
const fromEnv = parseDetailLevel(process.env[DETAIL_LEVEL_ENV]);
|
|
443
|
+
return fromEnv ?? 'normal';
|
|
444
|
+
}
|
|
445
|
+
function computeAdaptiveDetail(toolName, data, baseLevel) {
|
|
446
|
+
if (baseLevel === 'full') {
|
|
447
|
+
return { level: baseLevel, reason: 'adaptive policy keeps full detail unchanged' };
|
|
448
|
+
}
|
|
449
|
+
const status = asString(data?.status);
|
|
450
|
+
switch (toolName) {
|
|
451
|
+
case 'get_task_run':
|
|
452
|
+
if (status === 'queued' || status === 'running') {
|
|
453
|
+
return { level: 'brief', reason: 'adaptive policy: non-terminal task polling prefers brief detail' };
|
|
454
|
+
}
|
|
455
|
+
if (status === 'failed' || status === 'canceled') {
|
|
456
|
+
return { level: 'full', reason: 'adaptive policy: terminal failure escalates detail for debugging' };
|
|
457
|
+
}
|
|
458
|
+
return { level: baseLevel, reason: 'adaptive policy: keep base detail for terminal success states' };
|
|
459
|
+
case 'list_task_runs':
|
|
460
|
+
if (Boolean(data?.hasMore)) {
|
|
461
|
+
return { level: 'brief', reason: 'adaptive policy: paginated run listing prefers concise summary' };
|
|
462
|
+
}
|
|
463
|
+
return { level: baseLevel, reason: 'adaptive policy: keep base detail for short run lists' };
|
|
464
|
+
case 'get_network_logs':
|
|
465
|
+
case 'get_console_logs':
|
|
466
|
+
if (Boolean(data?.hasMore) || Boolean(data?.truncated)) {
|
|
467
|
+
return { level: 'brief', reason: 'adaptive policy: log polling prefers concise change-focused output' };
|
|
468
|
+
}
|
|
469
|
+
return { level: baseLevel, reason: 'adaptive policy: keep base detail for complete log snapshots' };
|
|
470
|
+
case 'get_downloads': {
|
|
471
|
+
const downloads = Array.isArray(data?.downloads) ? data?.downloads : [];
|
|
472
|
+
const hasPending = downloads.some((item) => item && item.completed === false);
|
|
473
|
+
if (hasPending) {
|
|
474
|
+
return { level: 'brief', reason: 'adaptive policy: pending downloads prefer concise polling output' };
|
|
475
|
+
}
|
|
476
|
+
return { level: baseLevel, reason: 'adaptive policy: keep base detail for stable download lists' };
|
|
477
|
+
}
|
|
478
|
+
case 'get_artifact':
|
|
479
|
+
if (data?.complete === false) {
|
|
480
|
+
return { level: 'brief', reason: 'adaptive policy: chunked artifact retrieval prefers concise continuation guidance' };
|
|
481
|
+
}
|
|
482
|
+
return { level: baseLevel, reason: 'adaptive policy: keep base detail for completed artifact payloads' };
|
|
483
|
+
default:
|
|
484
|
+
return { level: baseLevel, reason: 'adaptive policy: tool not in adaptive set, keep base detail' };
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function parseDetailLevel(value) {
|
|
488
|
+
if (typeof value !== 'string')
|
|
489
|
+
return null;
|
|
490
|
+
const normalized = value.toLowerCase();
|
|
491
|
+
if (normalized === 'brief' || normalized === 'normal' || normalized === 'full') {
|
|
492
|
+
return normalized;
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
function parseBooleanEnv(value) {
|
|
497
|
+
if (typeof value !== 'string')
|
|
498
|
+
return false;
|
|
499
|
+
const normalized = value.trim().toLowerCase();
|
|
500
|
+
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
501
|
+
}
|
|
502
|
+
function pickByDetail(detail, limits) {
|
|
503
|
+
if (detail === 'brief')
|
|
504
|
+
return limits.brief;
|
|
505
|
+
if (detail === 'full')
|
|
506
|
+
return limits.full;
|
|
507
|
+
return limits.normal;
|
|
508
|
+
}
|
|
509
|
+
function buildNextActions(toolName, data, detailLevel) {
|
|
510
|
+
const actions = [];
|
|
511
|
+
const add = (action) => {
|
|
512
|
+
actions.push(action);
|
|
513
|
+
};
|
|
514
|
+
switch (toolName) {
|
|
515
|
+
case 'create_session':
|
|
516
|
+
add({ tool: 'navigate', reason: 'Open target page after creating session', priority: 'high' });
|
|
517
|
+
break;
|
|
518
|
+
case 'navigate':
|
|
519
|
+
add({ tool: 'get_page_info', reason: 'Inspect actionable elements on the loaded page', priority: 'high' });
|
|
520
|
+
if (Boolean(data.partial)) {
|
|
521
|
+
add({ tool: 'wait_for_stable', reason: 'Page load is partial, wait for stability first', priority: 'high' });
|
|
522
|
+
}
|
|
523
|
+
break;
|
|
524
|
+
case 'get_page_info': {
|
|
525
|
+
const recommendations = collectIntentRecommendations(data);
|
|
526
|
+
if (recommendations.length > 0) {
|
|
527
|
+
const first = recommendations[0];
|
|
528
|
+
add({
|
|
529
|
+
tool: 'click',
|
|
530
|
+
args: { element_id: first.suggestedElementIds[0] },
|
|
531
|
+
reason: `Try intent '${first.intent}' using recommended element`,
|
|
532
|
+
priority: 'high',
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
add({ tool: 'find_element', reason: 'Use semantic fallback if target control is still unclear', priority: 'medium' });
|
|
536
|
+
if (Boolean(data.truncated)) {
|
|
537
|
+
add({ tool: 'get_page_info', args: { maxElements: Math.max(numberOrNull(data.elements?.length) ?? 50, 100) }, reason: 'Increase maxElements to inspect more controls', priority: 'medium' });
|
|
538
|
+
}
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
case 'find_element': {
|
|
542
|
+
const first = Array.isArray(data.candidates) ? data.candidates[0] : null;
|
|
543
|
+
if (first?.id) {
|
|
544
|
+
const candidateId = asString(first.id);
|
|
545
|
+
add({ tool: 'click', args: { element_id: candidateId }, reason: 'Try top-ranked candidate', priority: 'high' });
|
|
546
|
+
add({ tool: 'type_text', args: { element_id: candidateId, text: '<value>' }, reason: 'If candidate is an input, provide value via type_text', priority: 'medium' });
|
|
547
|
+
}
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
case 'list_tabs': {
|
|
551
|
+
const active = asString(data.activeTabId);
|
|
552
|
+
const tabs = Array.isArray(data.tabs) ? data.tabs : [];
|
|
553
|
+
const alternative = tabs.find((t) => asString(t?.id) && asString(t?.id) !== active);
|
|
554
|
+
if (alternative?.id) {
|
|
555
|
+
add({ tool: 'switch_tab', args: { tabId: asString(alternative.id) }, reason: 'Switch to a non-active tab for further actions', priority: 'high' });
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case 'get_dialog_info':
|
|
560
|
+
if (data.pendingDialog) {
|
|
561
|
+
add({ tool: 'handle_dialog', args: { action: 'accept' }, reason: 'Resolve pending dialog before other actions', priority: 'high' });
|
|
562
|
+
}
|
|
563
|
+
break;
|
|
564
|
+
case 'get_network_logs': {
|
|
565
|
+
const cursor = asCursor(data.nextCursor);
|
|
566
|
+
const cursorArgs = buildMaxEntriesCursorArgs(cursor);
|
|
567
|
+
if (Boolean(data.hasMore) && cursorArgs) {
|
|
568
|
+
add({ tool: 'get_network_logs', args: cursorArgs, reason: 'Continue retrieving older network logs', priority: 'high' });
|
|
569
|
+
}
|
|
570
|
+
add({ tool: 'get_network_logs', args: { filter: 'failed' }, reason: 'Focus on failed requests first', priority: 'medium' });
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
case 'get_console_logs': {
|
|
574
|
+
const cursor = asCursor(data.nextCursor);
|
|
575
|
+
const cursorArgs = buildMaxEntriesCursorArgs(cursor);
|
|
576
|
+
if (Boolean(data.hasMore) && cursorArgs) {
|
|
577
|
+
add({ tool: 'get_console_logs', args: cursorArgs, reason: 'Continue retrieving older console logs', priority: 'high' });
|
|
578
|
+
}
|
|
579
|
+
add({ tool: 'get_console_logs', args: { level: 'all' }, reason: 'Expand log level only if needed after error/warn analysis', priority: 'medium' });
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
case 'get_downloads': {
|
|
583
|
+
const downloads = Array.isArray(data.downloads) ? data.downloads : [];
|
|
584
|
+
const pending = downloads.find((d) => d && d.completed === false);
|
|
585
|
+
if (pending) {
|
|
586
|
+
add({ tool: 'wait', args: { milliseconds: 1000 }, reason: 'Wait for pending download completion', priority: 'medium' });
|
|
587
|
+
}
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
case 'run_task_template':
|
|
591
|
+
add({ tool: 'get_task_run', args: { runId: asString(data.runId) || '<runId>' }, reason: 'Poll task run status until terminal state', priority: 'high' });
|
|
592
|
+
break;
|
|
593
|
+
case 'get_task_run': {
|
|
594
|
+
const status = asString(data.status);
|
|
595
|
+
const verification = extractVerificationSnapshot(data);
|
|
596
|
+
const sessionId = asString(data.sessionId);
|
|
597
|
+
if (status === 'queued' || status === 'running') {
|
|
598
|
+
add({ tool: 'get_task_run', args: { runId: asString(data.runId) || '<runId>' }, reason: 'Continue polling until completion', priority: 'high' });
|
|
599
|
+
}
|
|
600
|
+
else if (Array.isArray(data.artifactIds) && data.artifactIds.length > 0) {
|
|
601
|
+
add({ tool: 'get_artifact', args: { artifactId: asString(data.artifactIds[0]) }, reason: 'Fetch first evidence artifact', priority: 'high' });
|
|
602
|
+
}
|
|
603
|
+
if (verification && !verification.pass) {
|
|
604
|
+
if (verification.missingFields.length > 0 && sessionId) {
|
|
605
|
+
add({ tool: 'get_page_content', args: { sessionId }, reason: 'Collect richer textual evidence for schema-missing fields', priority: 'high' });
|
|
606
|
+
}
|
|
607
|
+
if (verification.typeMismatches.length > 0 && sessionId) {
|
|
608
|
+
add({ tool: 'get_page_info', args: { sessionId, maxElements: 120 }, reason: 'Collect structured element state to repair type mismatches', priority: 'medium' });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
case 'list_task_runs': {
|
|
614
|
+
const runs = Array.isArray(data.runs) ? data.runs : [];
|
|
615
|
+
const first = runs[0];
|
|
616
|
+
if (first?.runId) {
|
|
617
|
+
add({ tool: 'get_task_run', args: { runId: asString(first.runId) }, reason: 'Open latest run details', priority: 'high' });
|
|
618
|
+
}
|
|
619
|
+
const cursor = asCursor(data.nextCursor);
|
|
620
|
+
const cursorArgs = buildListRunsCursorArgs(cursor);
|
|
621
|
+
if (Boolean(data.hasMore) && cursorArgs) {
|
|
622
|
+
add({ tool: 'list_task_runs', args: cursorArgs, reason: 'Continue listing older runs with nextCursor', priority: 'medium' });
|
|
623
|
+
}
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
case 'get_artifact':
|
|
627
|
+
if (!Boolean(data.complete)) {
|
|
628
|
+
add({
|
|
629
|
+
tool: 'get_artifact',
|
|
630
|
+
args: { artifactId: asString(data.artifactId), offset: (numberOrNull(data.offset) ?? 0) + (numberOrNull(data.length) ?? 0) },
|
|
631
|
+
reason: 'Continue fetching remaining artifact chunks',
|
|
632
|
+
priority: 'high',
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
break;
|
|
636
|
+
case 'fill_form': {
|
|
637
|
+
const fieldResults = Array.isArray(data.results) ? data.results : [];
|
|
638
|
+
const failed = fieldResults.filter((r) => !r?.success);
|
|
639
|
+
if (failed.length > 0) {
|
|
640
|
+
add({ tool: 'type_text', reason: 'Retry failed fields individually with type_text', priority: 'high' });
|
|
641
|
+
}
|
|
642
|
+
if (!data.submitResult) {
|
|
643
|
+
add({ tool: 'click', reason: 'Submit the form by clicking the submit button', priority: 'high' });
|
|
644
|
+
}
|
|
645
|
+
add({ tool: 'get_page_info', reason: 'Inspect page state after form submission', priority: 'medium' });
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
case 'click_and_wait':
|
|
649
|
+
add({ tool: 'get_page_info', reason: 'Inspect page elements after click+wait', priority: 'high' });
|
|
650
|
+
if (!data.waitResult?.success) {
|
|
651
|
+
add({ tool: 'wait_for_stable', reason: 'Wait failed; retry with explicit stability check', priority: 'medium' });
|
|
652
|
+
}
|
|
653
|
+
break;
|
|
654
|
+
case 'navigate_and_extract':
|
|
655
|
+
if (data.extractResult?.sections?.length === 0) {
|
|
656
|
+
add({ tool: 'get_page_content', reason: 'No content extracted; try scrolling and re-extracting', priority: 'high' });
|
|
657
|
+
}
|
|
658
|
+
add({ tool: 'get_page_info', reason: 'Inspect interactive elements if interaction is needed', priority: 'medium' });
|
|
659
|
+
break;
|
|
660
|
+
default:
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
return finalizeNextActions(actions, detailLevel);
|
|
664
|
+
}
|
|
665
|
+
function extractVerificationSnapshot(data) {
|
|
666
|
+
const direct = normalizeVerification(data?.verification);
|
|
667
|
+
if (direct)
|
|
668
|
+
return direct;
|
|
669
|
+
const nested = normalizeVerification(data?.result?.verification);
|
|
670
|
+
if (nested)
|
|
671
|
+
return nested;
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
function normalizeVerification(value) {
|
|
675
|
+
if (!isObject(value))
|
|
676
|
+
return null;
|
|
677
|
+
if (typeof value.pass !== 'boolean')
|
|
678
|
+
return null;
|
|
679
|
+
return {
|
|
680
|
+
pass: value.pass,
|
|
681
|
+
score: typeof value.score === 'number' ? value.score : undefined,
|
|
682
|
+
missingFields: Array.isArray(value.missingFields)
|
|
683
|
+
? value.missingFields.map((item) => asString(item) || String(item)).filter(Boolean)
|
|
684
|
+
: [],
|
|
685
|
+
typeMismatches: Array.isArray(value.typeMismatches)
|
|
686
|
+
? value.typeMismatches.map((item) => asString(item) || String(item)).filter(Boolean)
|
|
687
|
+
: [],
|
|
688
|
+
reason: typeof value.reason === 'string' ? value.reason : undefined,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function buildSchemaRepairGuidance(toolName, data) {
|
|
692
|
+
if (toolName !== 'get_task_run')
|
|
693
|
+
return null;
|
|
694
|
+
const verification = extractVerificationSnapshot(data);
|
|
695
|
+
if (!verification || verification.pass)
|
|
696
|
+
return null;
|
|
697
|
+
const recommendedChecks = [];
|
|
698
|
+
if (verification.missingFields.length > 0) {
|
|
699
|
+
recommendedChecks.push(`Re-collect fields: ${verification.missingFields.slice(0, 8).join(', ')}`);
|
|
700
|
+
}
|
|
701
|
+
if (verification.typeMismatches.length > 0) {
|
|
702
|
+
recommendedChecks.push(`Normalize value types for: ${verification.typeMismatches.slice(0, 8).join(', ')}`);
|
|
703
|
+
}
|
|
704
|
+
if (Array.isArray(data.schemaRepairHints) && data.schemaRepairHints.length > 0) {
|
|
705
|
+
recommendedChecks.push(...data.schemaRepairHints.slice(0, 4).map((item) => singleLine(String(item))));
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
status: 'schema_failed',
|
|
709
|
+
missingFields: verification.missingFields,
|
|
710
|
+
typeMismatches: verification.typeMismatches,
|
|
711
|
+
recommendedChecks: recommendedChecks.slice(0, 8),
|
|
712
|
+
retryAdvice: 'Retry only after collecting evidence for missing/type-mismatched fields.',
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function collectIntentRecommendations(data) {
|
|
716
|
+
const raw = Array.isArray(data.recommendedByIntent) ? data.recommendedByIntent : [];
|
|
717
|
+
return raw
|
|
718
|
+
.map((item) => ({
|
|
719
|
+
intent: asString(item?.intent),
|
|
720
|
+
suggestedElementIds: Array.isArray(item?.suggestedElementIds)
|
|
721
|
+
? item.suggestedElementIds.map((id) => asString(id)).filter(Boolean)
|
|
722
|
+
: [],
|
|
723
|
+
}))
|
|
724
|
+
.filter((item) => item.intent && item.suggestedElementIds.length > 0);
|
|
725
|
+
}
|
|
726
|
+
function finalizeNextActions(actions, detailLevel) {
|
|
727
|
+
const normalized = [];
|
|
728
|
+
const seen = new Set();
|
|
729
|
+
for (const action of actions) {
|
|
730
|
+
const normalizedAction = normalizeNextAction(action);
|
|
731
|
+
if (!normalizedAction)
|
|
732
|
+
continue;
|
|
733
|
+
const dedupeKey = `${normalizedAction.tool}:${stableJson(normalizedAction.args ?? {})}`;
|
|
734
|
+
if (seen.has(dedupeKey))
|
|
735
|
+
continue;
|
|
736
|
+
seen.add(dedupeKey);
|
|
737
|
+
normalized.push(normalizedAction);
|
|
738
|
+
}
|
|
739
|
+
const maxActions = pickByDetail(detailLevel, { brief: 1, normal: 3, full: 5 });
|
|
740
|
+
return normalized.slice(0, maxActions);
|
|
741
|
+
}
|
|
742
|
+
function normalizeNextAction(action) {
|
|
743
|
+
const tool = singleLine(asString(action.tool));
|
|
744
|
+
const reasonRaw = singleLine(asString(action.reason));
|
|
745
|
+
if (!tool || !reasonRaw)
|
|
746
|
+
return null;
|
|
747
|
+
const args = isObject(action.args) ? sanitizeActionArgs(action.args) : undefined;
|
|
748
|
+
const priority = normalizePriority(action.priority);
|
|
749
|
+
const reason = normalizeReason(reasonRaw);
|
|
750
|
+
return {
|
|
751
|
+
tool,
|
|
752
|
+
...(args ? { args } : {}),
|
|
753
|
+
reason,
|
|
754
|
+
priority,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function sanitizeActionArgs(args) {
|
|
758
|
+
const entries = Object.entries(args).filter(([, value]) => value !== undefined);
|
|
759
|
+
if (entries.length === 0)
|
|
760
|
+
return undefined;
|
|
761
|
+
return Object.fromEntries(entries);
|
|
762
|
+
}
|
|
763
|
+
function normalizePriority(priority) {
|
|
764
|
+
if (priority === 'high' || priority === 'medium' || priority === 'low')
|
|
765
|
+
return priority;
|
|
766
|
+
return 'medium';
|
|
767
|
+
}
|
|
768
|
+
function normalizeReason(reason) {
|
|
769
|
+
const compact = compactText(reason, 140);
|
|
770
|
+
return /[.!?]$/.test(compact) ? compact : `${compact}.`;
|
|
771
|
+
}
|
|
772
|
+
function stableJson(value) {
|
|
773
|
+
if (Array.isArray(value)) {
|
|
774
|
+
return `[${value.map((item) => stableJson(item)).join(',')}]`;
|
|
775
|
+
}
|
|
776
|
+
if (isObject(value)) {
|
|
777
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
|
|
778
|
+
}
|
|
779
|
+
return JSON.stringify(value);
|
|
780
|
+
}
|
|
781
|
+
function formatBriefStatusResultBlock(options) {
|
|
782
|
+
const blocker = options.blocker && options.blocker.trim().length > 0 ? options.blocker : 'none';
|
|
783
|
+
return [
|
|
784
|
+
`## ${options.title}`,
|
|
785
|
+
'',
|
|
786
|
+
`- Status: ${options.status}`,
|
|
787
|
+
`- Result: ${options.result}`,
|
|
788
|
+
`- Blocker: ${blocker}`,
|
|
789
|
+
'',
|
|
790
|
+
'### Recommended Next Step',
|
|
791
|
+
'',
|
|
792
|
+
`- ${options.next}`,
|
|
793
|
+
].join('\n');
|
|
794
|
+
}
|
|
795
|
+
function buildDeltaSummary(toolName, data) {
|
|
796
|
+
switch (toolName) {
|
|
797
|
+
case 'get_task_run': {
|
|
798
|
+
const runId = asString(data.runId);
|
|
799
|
+
if (!runId)
|
|
800
|
+
return null;
|
|
801
|
+
const key = `get_task_run:${runId}`;
|
|
802
|
+
const snapshot = {
|
|
803
|
+
status: asString(data.status) || '-',
|
|
804
|
+
doneSteps: asNumber(data.progress?.doneSteps),
|
|
805
|
+
totalSteps: asNumber(data.progress?.totalSteps),
|
|
806
|
+
artifactCount: Array.isArray(data.artifactIds) ? data.artifactIds.length : 0,
|
|
807
|
+
};
|
|
808
|
+
const changes = diffSnapshots(deltaSnapshotCache.get(key), snapshot, {
|
|
809
|
+
status: (prev, next) => `status changed: ${prev} -> ${next}`,
|
|
810
|
+
doneSteps: (prev, next, curr) => {
|
|
811
|
+
const prevText = `${prev ?? 0}/${curr.totalSteps ?? '-'}`;
|
|
812
|
+
const nextText = `${next ?? 0}/${curr.totalSteps ?? '-'}`;
|
|
813
|
+
return `progress changed: ${prevText} -> ${nextText}`;
|
|
814
|
+
},
|
|
815
|
+
totalSteps: (_prev, next, curr) => `progress denominator now: ${curr.doneSteps ?? 0}/${next ?? '-'}`,
|
|
816
|
+
artifactCount: (prev, next) => `artifact count changed: ${prev ?? 0} -> ${next ?? 0}`,
|
|
817
|
+
});
|
|
818
|
+
putDeltaSnapshot(key, snapshot);
|
|
819
|
+
return { key, changes };
|
|
820
|
+
}
|
|
821
|
+
case 'list_task_runs': {
|
|
822
|
+
const first = Array.isArray(data.runs) && data.runs.length > 0 ? data.runs[0] : null;
|
|
823
|
+
const key = 'list_task_runs:default';
|
|
824
|
+
const snapshot = {
|
|
825
|
+
firstRunId: asString(first?.runId) || '-',
|
|
826
|
+
firstStatus: asString(first?.status) || '-',
|
|
827
|
+
count: Array.isArray(data.runs) ? data.runs.length : 0,
|
|
828
|
+
hasMore: Boolean(data.hasMore),
|
|
829
|
+
};
|
|
830
|
+
const changes = diffSnapshots(deltaSnapshotCache.get(key), snapshot, {
|
|
831
|
+
firstRunId: (prev, next) => `latest run changed: ${prev} -> ${next}`,
|
|
832
|
+
firstStatus: (prev, next) => `latest run status changed: ${prev} -> ${next}`,
|
|
833
|
+
count: (prev, next) => `run count changed: ${prev ?? 0} -> ${next ?? 0}`,
|
|
834
|
+
hasMore: (_prev, next) => `hasMore is now ${boolText(next)}`,
|
|
835
|
+
});
|
|
836
|
+
putDeltaSnapshot(key, snapshot);
|
|
837
|
+
return { key, changes };
|
|
838
|
+
}
|
|
839
|
+
case 'get_network_logs':
|
|
840
|
+
case 'get_console_logs': {
|
|
841
|
+
const key = `${toolName}:default`;
|
|
842
|
+
const snapshot = {
|
|
843
|
+
returned: Array.isArray(data.logs) ? data.logs.length : 0,
|
|
844
|
+
truncated: Boolean(data.truncated),
|
|
845
|
+
topIssue: Array.isArray(data.topIssues) && data.topIssues.length > 0
|
|
846
|
+
? `${asString(data.topIssues[0]?.kind)}:${numberOrNull(data.topIssues[0]?.count) ?? 0}`
|
|
847
|
+
: '-',
|
|
848
|
+
};
|
|
849
|
+
const changes = diffSnapshots(deltaSnapshotCache.get(key), snapshot, {
|
|
850
|
+
returned: (prev, next) => `returned entries changed: ${prev ?? 0} -> ${next ?? 0}`,
|
|
851
|
+
truncated: (_prev, next) => `truncated is now ${boolText(next)}`,
|
|
852
|
+
topIssue: (prev, next) => `top issue changed: ${prev} -> ${next}`,
|
|
853
|
+
});
|
|
854
|
+
putDeltaSnapshot(key, snapshot);
|
|
855
|
+
return { key, changes };
|
|
856
|
+
}
|
|
857
|
+
default:
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function diffSnapshots(previous, current, formatters) {
|
|
862
|
+
if (!previous) {
|
|
863
|
+
return ['initial snapshot'];
|
|
864
|
+
}
|
|
865
|
+
const changes = [];
|
|
866
|
+
for (const [field, formatter] of Object.entries(formatters)) {
|
|
867
|
+
const prev = previous[field];
|
|
868
|
+
const next = current[field];
|
|
869
|
+
if (stableJson(prev) === stableJson(next))
|
|
870
|
+
continue;
|
|
871
|
+
changes.push(formatter(prev, next, current));
|
|
872
|
+
}
|
|
873
|
+
return changes.length > 0 ? changes : ['no significant change'];
|
|
874
|
+
}
|
|
875
|
+
function putDeltaSnapshot(key, snapshot) {
|
|
876
|
+
if (deltaSnapshotCache.has(key)) {
|
|
877
|
+
deltaSnapshotCache.delete(key);
|
|
878
|
+
}
|
|
879
|
+
deltaSnapshotCache.set(key, snapshot);
|
|
880
|
+
if (deltaSnapshotCache.size <= DELTA_CACHE_LIMIT)
|
|
881
|
+
return;
|
|
882
|
+
const oldestKey = deltaSnapshotCache.keys().next().value;
|
|
883
|
+
if (oldestKey) {
|
|
884
|
+
deltaSnapshotCache.delete(oldestKey);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
function formatCreateSession(data) {
|
|
888
|
+
const lines = [
|
|
889
|
+
'## Session Created',
|
|
890
|
+
'',
|
|
891
|
+
`- Session ID: ${asString(data.sessionId) || '-'}`,
|
|
892
|
+
'',
|
|
893
|
+
'### Recommended Next Step',
|
|
894
|
+
'',
|
|
895
|
+
'- Use this `sessionId` in subsequent tool calls.',
|
|
896
|
+
];
|
|
897
|
+
return lines.join('\n');
|
|
898
|
+
}
|
|
899
|
+
function formatCloseSession(data) {
|
|
900
|
+
const lines = [
|
|
901
|
+
'## Session Close Result',
|
|
902
|
+
'',
|
|
903
|
+
`- Success: ${boolText(data.success)}`,
|
|
904
|
+
];
|
|
905
|
+
if (typeof data.kept === 'boolean') {
|
|
906
|
+
lines.push(`- Kept: ${boolText(data.kept)}`);
|
|
907
|
+
}
|
|
908
|
+
if (data.reason) {
|
|
909
|
+
lines.push(`- Reason: ${singleLine(asString(data.reason) || '')}`);
|
|
910
|
+
}
|
|
911
|
+
lines.push('', '### Recommended Next Step', '');
|
|
912
|
+
if (data.kept) {
|
|
913
|
+
lines.push('- Session is still active; set `force=true` to close headful sessions.');
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
lines.push('- Create a new session when further browser actions are required.');
|
|
917
|
+
}
|
|
918
|
+
return lines.join('\n');
|
|
919
|
+
}
|
|
920
|
+
function formatCreateTab(data) {
|
|
921
|
+
const lines = [
|
|
922
|
+
'## Tab Created',
|
|
923
|
+
'',
|
|
924
|
+
`- Success: ${boolText(Boolean(data.tabId) || data.success)}`,
|
|
925
|
+
`- Tab ID: ${asString(data.tabId) || '-'}`,
|
|
926
|
+
`- URL: ${asString(data.url) || '-'}`,
|
|
927
|
+
`- Partial Navigation: ${boolText(data.partial)}`,
|
|
928
|
+
'',
|
|
929
|
+
'### Recommended Next Step',
|
|
930
|
+
'',
|
|
931
|
+
'- Call `get_page_info` in the new active tab before interaction.',
|
|
932
|
+
];
|
|
933
|
+
return lines.join('\n');
|
|
934
|
+
}
|
|
935
|
+
function formatScreenshotMeta(data) {
|
|
936
|
+
const lines = [
|
|
937
|
+
'## Screenshot Captured',
|
|
938
|
+
'',
|
|
939
|
+
`- Captured: ${boolText(data.captured)}`,
|
|
940
|
+
`- URL: ${asString(data.url) || '-'}`,
|
|
941
|
+
`- Title: ${asString(data.title) || '-'}`,
|
|
942
|
+
`- Full Page: ${boolText(data.fullPage)}`,
|
|
943
|
+
`- Element Target: ${asString(data.element) || '-'}`,
|
|
944
|
+
'',
|
|
945
|
+
'### Recommended Next Step',
|
|
946
|
+
'',
|
|
947
|
+
'- Use `get_page_info` / `get_page_content` to extract structured information.',
|
|
948
|
+
];
|
|
949
|
+
return lines.join('\n');
|
|
950
|
+
}
|
|
951
|
+
function formatExecuteJavascript(data) {
|
|
952
|
+
const resultPreview = compactText(singleLine(toDisplayText(data.result)), 220);
|
|
953
|
+
const lines = [
|
|
954
|
+
'## JavaScript Execution Result',
|
|
955
|
+
'',
|
|
956
|
+
`- Truncated: ${boolText(data.truncated)}`,
|
|
957
|
+
`- Result Preview: ${resultPreview || '-'}`,
|
|
958
|
+
];
|
|
959
|
+
if (data.hint) {
|
|
960
|
+
lines.push(`- Hint: ${singleLine(asString(data.hint) || '')}`);
|
|
961
|
+
}
|
|
962
|
+
lines.push('', '### Recommended Next Step', '', '- Return structured values (object/array) for easier downstream parsing.');
|
|
963
|
+
return lines.join('\n');
|
|
964
|
+
}
|
|
965
|
+
function formatDownloads(data) {
|
|
966
|
+
const detail = resolveDetailLevel(data);
|
|
967
|
+
const downloads = Array.isArray(data.downloads) ? data.downloads : [];
|
|
968
|
+
const top = downloads.slice(0, pickByDetail(detail, { brief: 10, normal: 20, full: 40 }));
|
|
969
|
+
const lines = [
|
|
970
|
+
'## Downloads',
|
|
971
|
+
'',
|
|
972
|
+
`- Returned Downloads: ${downloads.length}`,
|
|
973
|
+
'',
|
|
974
|
+
'| id | filename | completed | size | error |',
|
|
975
|
+
'|---|---|---|---:|---|',
|
|
976
|
+
];
|
|
977
|
+
if (top.length === 0) {
|
|
978
|
+
lines.push('| - | - | - | - | No downloads |');
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
for (const item of top) {
|
|
982
|
+
lines.push(`| ${tableSafe(asString(item?.id) || '-')} | ${tableSafe(compactText(asString(item?.filename) || '-', 80))} | ${boolText(item?.completed)} | ${numberOrNull(item?.size) ?? '-'} | ${tableSafe(compactText(asString(item?.error) || '-', 60))} |`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
lines.push('', '### Recommended Next Step', '', '- Use `completed=true` entries first for downstream file processing.');
|
|
986
|
+
return lines.join('\n');
|
|
987
|
+
}
|
|
988
|
+
function formatTemplateList(data) {
|
|
989
|
+
const detail = resolveDetailLevel(data);
|
|
990
|
+
const templates = Array.isArray(data.templates) ? data.templates : [];
|
|
991
|
+
const top = templates.slice(0, pickByDetail(detail, { brief: 10, normal: 20, full: 40 }));
|
|
992
|
+
const lines = [
|
|
993
|
+
'## Task Template List',
|
|
994
|
+
'',
|
|
995
|
+
`- Returned Templates: ${templates.length}`,
|
|
996
|
+
'',
|
|
997
|
+
'| templateId | version | mode | trust levels |',
|
|
998
|
+
'|---|---|---|---|',
|
|
999
|
+
];
|
|
1000
|
+
if (top.length === 0) {
|
|
1001
|
+
lines.push('| - | - | - | - |');
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
for (const tpl of top) {
|
|
1005
|
+
const trust = Array.isArray(tpl?.trustLevelSupport) ? tpl.trustLevelSupport.join(', ') : '-';
|
|
1006
|
+
lines.push(`| ${tableSafe(asString(tpl?.templateId) || '-')} | ${tableSafe(asString(tpl?.version) || '-')} | ${tableSafe(asString(tpl?.executionMode) || '-')} | ${tableSafe(trust)} |`);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
lines.push('', '### Recommended Next Step', '', '- Select a `templateId` and validate required inputs before execution.');
|
|
1010
|
+
return lines.join('\n');
|
|
1011
|
+
}
|
|
1012
|
+
function formatCancelTaskRun(data) {
|
|
1013
|
+
const lines = [
|
|
1014
|
+
'## Cancel Task Run',
|
|
1015
|
+
'',
|
|
1016
|
+
`- Success: ${boolText(data.success)}`,
|
|
1017
|
+
`- Run ID: ${asString(data.runId) || '-'}`,
|
|
1018
|
+
];
|
|
1019
|
+
if (data.reason) {
|
|
1020
|
+
lines.push(`- Reason: ${singleLine(asString(data.reason) || '')}`);
|
|
1021
|
+
}
|
|
1022
|
+
lines.push('', '### Recommended Next Step', '', '- Call `get_task_run` to confirm latest status.');
|
|
1023
|
+
return lines.join('\n');
|
|
1024
|
+
}
|
|
1025
|
+
function formatRuntimeProfile(data) {
|
|
1026
|
+
const lines = [
|
|
1027
|
+
'## Runtime Profile',
|
|
1028
|
+
'',
|
|
1029
|
+
`- maxConcurrentRuns: ${numberOrNull(data.maxConcurrentRuns) ?? '-'}`,
|
|
1030
|
+
`- maxUrls: ${numberOrNull(data.maxUrls) ?? '-'}`,
|
|
1031
|
+
`- maxTabsPerSession: ${numberOrNull(data.maxTabsPerSession) ?? '-'}`,
|
|
1032
|
+
`- syncTimeoutMs: ${numberOrNull(data.syncTimeoutMs) ?? '-'}`,
|
|
1033
|
+
`- asyncTimeoutMs: ${numberOrNull(data.asyncTimeoutMs) ?? '-'}`,
|
|
1034
|
+
`- artifactMaxChunkSize: ${numberOrNull(data.artifactMaxChunkSize) ?? '-'}`,
|
|
1035
|
+
`- artifactTtlMs: ${numberOrNull(data.artifactTtlMs) ?? '-'}`,
|
|
1036
|
+
`- runTtlMs: ${numberOrNull(data.runTtlMs) ?? '-'}`,
|
|
1037
|
+
`- trustLevel: ${asString(data.trustLevel) || '-'}`,
|
|
1038
|
+
];
|
|
1039
|
+
if (Array.isArray(data.supportedModes) && data.supportedModes.length > 0) {
|
|
1040
|
+
lines.push(`- supportedModes: ${data.supportedModes.join(', ')}`);
|
|
1041
|
+
}
|
|
1042
|
+
lines.push('', '### Recommended Next Step', '', '- Adjust batch size/mode to stay within runtime limits.');
|
|
1043
|
+
return lines.join('\n');
|
|
1044
|
+
}
|
|
1045
|
+
function formatFillForm(data) {
|
|
1046
|
+
const results = Array.isArray(data.results) ? data.results : [];
|
|
1047
|
+
const lines = [
|
|
1048
|
+
'## Fill Form Result',
|
|
1049
|
+
'',
|
|
1050
|
+
`- Fields Attempted: ${results.length}`,
|
|
1051
|
+
`- Succeeded: ${results.filter((r) => r?.success).length}`,
|
|
1052
|
+
`- Failed: ${results.filter((r) => !r?.success).length}`,
|
|
1053
|
+
];
|
|
1054
|
+
if (results.length > 0) {
|
|
1055
|
+
lines.push('', '| element_id | success | error |', '|---|---|---|');
|
|
1056
|
+
for (const r of results) {
|
|
1057
|
+
const id = asString(r?.elementId) || '-';
|
|
1058
|
+
const ok = boolText(r?.success);
|
|
1059
|
+
const err = asString(r?.error) || '-';
|
|
1060
|
+
lines.push(`| \`${tableSafe(id)}\` | ${ok} | ${tableSafe(compactText(err, 80))} |`);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
if (data.submitResult) {
|
|
1064
|
+
lines.push('', '### Submit Result', '');
|
|
1065
|
+
lines.push(`- Success: ${boolText(data.submitResult.success)}`);
|
|
1066
|
+
if (data.submitResult.page) {
|
|
1067
|
+
lines.push(`- URL: ${asString(data.submitResult.page.url) || '-'}`);
|
|
1068
|
+
lines.push(`- Title: ${asString(data.submitResult.page.title) || '-'}`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
lines.push('', '### Recommended Next Step', '', '- Call `get_page_info` to verify form submission result.');
|
|
1072
|
+
return lines.join('\n');
|
|
1073
|
+
}
|
|
1074
|
+
function formatClickAndWait(data) {
|
|
1075
|
+
const lines = [
|
|
1076
|
+
'## Click and Wait Result',
|
|
1077
|
+
'',
|
|
1078
|
+
`- Click Success: ${boolText(data.clickResult?.success)}`,
|
|
1079
|
+
`- Wait Success: ${boolText(data.waitResult?.success)}`,
|
|
1080
|
+
];
|
|
1081
|
+
if (data.clickResult?.page) {
|
|
1082
|
+
lines.push(`- URL: ${asString(data.clickResult.page.url) || '-'}`);
|
|
1083
|
+
lines.push(`- Title: ${asString(data.clickResult.page.title) || '-'}`);
|
|
1084
|
+
}
|
|
1085
|
+
if (data.clickResult?.newTabCreated) {
|
|
1086
|
+
lines.push(`- New Tab Created: ${asString(data.clickResult.newTabCreated)}`);
|
|
1087
|
+
}
|
|
1088
|
+
if (data.waitResult && !data.waitResult.success) {
|
|
1089
|
+
lines.push(`- Wait Reason: ${asString(data.waitResult.reason) || 'timeout or instability'}`);
|
|
1090
|
+
}
|
|
1091
|
+
lines.push('', '### Recommended Next Step', '', '- Call `get_page_info` to inspect the page after click+wait.');
|
|
1092
|
+
return lines.join('\n');
|
|
1093
|
+
}
|
|
1094
|
+
function formatNavigateAndExtract(data) {
|
|
1095
|
+
const lines = [
|
|
1096
|
+
'## Navigate and Extract Result',
|
|
1097
|
+
'',
|
|
1098
|
+
];
|
|
1099
|
+
// Navigation part
|
|
1100
|
+
if (data.navigateResult) {
|
|
1101
|
+
lines.push(`- Nav Success: ${boolText(data.navigateResult.success)}`);
|
|
1102
|
+
lines.push(`- URL: ${asString(data.navigateResult.page?.url) || '-'}`);
|
|
1103
|
+
lines.push(`- Title: ${asString(data.navigateResult.page?.title) || '-'}`);
|
|
1104
|
+
if (typeof data.navigateResult.statusCode === 'number') {
|
|
1105
|
+
lines.push(`- HTTP Status: ${data.navigateResult.statusCode}`);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
// Extract part
|
|
1109
|
+
if (data.extractResult) {
|
|
1110
|
+
const sections = Array.isArray(data.extractResult.sections) ? data.extractResult.sections : [];
|
|
1111
|
+
// Sort by attention descending so the most relevant content is shown first
|
|
1112
|
+
const sorted = [...sections].sort((a, b) => (numberOrNull(b?.attention) ?? 0) - (numberOrNull(a?.attention) ?? 0));
|
|
1113
|
+
const topSections = sorted.slice(0, 20);
|
|
1114
|
+
lines.push(`- Sections Extracted: ${sections.length}`);
|
|
1115
|
+
if (topSections.length > 0) {
|
|
1116
|
+
lines.push('', '### Key Text Blocks', '');
|
|
1117
|
+
for (const section of topSections) {
|
|
1118
|
+
const attention = numberOrNull(section?.attention);
|
|
1119
|
+
const level = typeof attention === 'number' ? `[attention=${attention.toFixed(2)}]` : '';
|
|
1120
|
+
lines.push(`- ${level} ${compactText(asString(section?.text) || '-', 400)}`);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (data.elementsResult) {
|
|
1125
|
+
const elements = Array.isArray(data.elementsResult.elements) ? data.elementsResult.elements : [];
|
|
1126
|
+
lines.push(`- Elements Found: ${elements.length}`);
|
|
1127
|
+
}
|
|
1128
|
+
lines.push('', '### Recommended Next Step', '', '- If content is incomplete, scroll and call `get_page_content` again.');
|
|
1129
|
+
return lines.join('\n');
|
|
1130
|
+
}
|
|
1131
|
+
function formatNavigate(data) {
|
|
1132
|
+
const lines = [
|
|
1133
|
+
'## Navigation Result',
|
|
1134
|
+
'',
|
|
1135
|
+
`- Success: ${boolText(data.success)}`,
|
|
1136
|
+
`- Partial: ${boolText(data.partial)}`,
|
|
1137
|
+
`- URL: ${asString(data.page?.url) || '-'}`,
|
|
1138
|
+
`- Title: ${asString(data.page?.title) || '-'}`,
|
|
1139
|
+
];
|
|
1140
|
+
if (typeof data.statusCode === 'number') {
|
|
1141
|
+
lines.push(`- HTTP Status: ${data.statusCode}`);
|
|
1142
|
+
}
|
|
1143
|
+
if (data.dialog) {
|
|
1144
|
+
lines.push(`- Pending Dialog: ${asString(data.dialog.type) || 'yes'}`);
|
|
1145
|
+
}
|
|
1146
|
+
lines.push('', '### Recommended Next Step', '', '- To extract text content: call `get_page_content`.', '- To interact with elements: call `get_page_info`.');
|
|
1147
|
+
return lines.join('\n');
|
|
1148
|
+
}
|
|
1149
|
+
function formatPageInfo(data) {
|
|
1150
|
+
const detail = resolveDetailLevel(data);
|
|
1151
|
+
const elements = Array.isArray(data.elements) ? data.elements : [];
|
|
1152
|
+
const topElements = elements.slice(0, pickByDetail(detail, { brief: 10, normal: 20, full: 40 }));
|
|
1153
|
+
const intents = Array.isArray(data.intents) ? data.intents.slice(0, 6) : [];
|
|
1154
|
+
if (detail === 'brief') {
|
|
1155
|
+
const recommended = collectIntentRecommendations(data);
|
|
1156
|
+
const topIds = topElements.slice(0, 5).map((item) => asString(item?.id)).filter(Boolean);
|
|
1157
|
+
const blocker = data.pendingDialog
|
|
1158
|
+
? `pending dialog ${asString(data.pendingDialog.type) || 'unknown'}`
|
|
1159
|
+
: (Boolean(data.truncated) ? 'element list truncated' : 'none');
|
|
1160
|
+
const resultParts = [
|
|
1161
|
+
`elements=${elements.length}`,
|
|
1162
|
+
topIds.length > 0 ? `topIds=${topIds.join(', ')}` : 'topIds=-',
|
|
1163
|
+
recommended.length > 0 ? `intent=${recommended[0].intent}` : null,
|
|
1164
|
+
].filter(Boolean);
|
|
1165
|
+
return formatBriefStatusResultBlock({
|
|
1166
|
+
title: 'Page Interaction Snapshot',
|
|
1167
|
+
status: `${asString(data.page?.type) || 'unknown'} @ ${asString(data.page?.title) || '-'}`,
|
|
1168
|
+
result: resultParts.join('; '),
|
|
1169
|
+
blocker,
|
|
1170
|
+
next: data.pendingDialog ? 'Call `handle_dialog` before interacting with page elements.' : 'Use `click` / `type_text` with top element IDs.',
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
const lines = [
|
|
1174
|
+
'## Page Interaction Snapshot',
|
|
1175
|
+
'',
|
|
1176
|
+
`- URL: ${asString(data.page?.url) || '-'}`,
|
|
1177
|
+
`- Title: ${asString(data.page?.title) || '-'}`,
|
|
1178
|
+
`- Page Type: ${asString(data.page?.type) || '-'}`,
|
|
1179
|
+
`- Summary: ${singleLine(asString(data.page?.summary) || '-')}`,
|
|
1180
|
+
`- Elements Returned: ${elements.length}`,
|
|
1181
|
+
];
|
|
1182
|
+
if (typeof data.totalElements === 'number') {
|
|
1183
|
+
lines.push(`- Total Elements (before truncation): ${data.totalElements}`);
|
|
1184
|
+
}
|
|
1185
|
+
if (typeof data.truncated === 'boolean') {
|
|
1186
|
+
lines.push(`- Truncated: ${boolText(data.truncated)}`);
|
|
1187
|
+
}
|
|
1188
|
+
if (intents.length > 0) {
|
|
1189
|
+
lines.push(`- Detected Intents: ${intents.map((v) => singleLine(String(v))).join(', ')}`);
|
|
1190
|
+
}
|
|
1191
|
+
const recommended = collectIntentRecommendations(data);
|
|
1192
|
+
if (recommended.length > 0) {
|
|
1193
|
+
const compact = recommended
|
|
1194
|
+
.map((entry) => `${entry.intent}: ${entry.suggestedElementIds.slice(0, 3).join(', ')}`)
|
|
1195
|
+
.join(' ; ');
|
|
1196
|
+
lines.push(`- Recommended By Intent: ${compact || '-'}`);
|
|
1197
|
+
}
|
|
1198
|
+
if (data.stability) {
|
|
1199
|
+
lines.push(`- Stability: load=${asString(data.stability.loadState) || '-'}, networkPending=${numberOrNull(data.stability.networkPending) ?? '-'}`);
|
|
1200
|
+
}
|
|
1201
|
+
if (data.pendingDialog) {
|
|
1202
|
+
lines.push(`- Pending Dialog: ${asString(data.pendingDialog.type) || 'yes'}`);
|
|
1203
|
+
}
|
|
1204
|
+
lines.push('', '### Top Actionable Elements', '', '| id | type | label | state |', '|---|---|---|---|');
|
|
1205
|
+
if (topElements.length === 0) {
|
|
1206
|
+
lines.push('| - | - | No actionable elements detected | - |');
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
for (const el of topElements) {
|
|
1210
|
+
const id = tableSafe(asString(el?.id) || '-');
|
|
1211
|
+
const type = tableSafe(asString(el?.type) || '-');
|
|
1212
|
+
const label = tableSafe(compactText(asString(el?.label) || '-', 80));
|
|
1213
|
+
const state = tableSafe(compactState(el?.state));
|
|
1214
|
+
lines.push(`| \`${id}\` | ${type} | ${label} | ${state} |`);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
lines.push('', '### Recommended Next Step', '', '- Use `click` / `type_text` with element IDs from the table.');
|
|
1218
|
+
if (Boolean(data.pendingDialog)) {
|
|
1219
|
+
lines.push('- Handle the dialog first with `handle_dialog`.');
|
|
1220
|
+
}
|
|
1221
|
+
if (Boolean(data.truncated)) {
|
|
1222
|
+
lines.push('- Re-run with larger `maxElements` if needed.');
|
|
1223
|
+
}
|
|
1224
|
+
return lines.join('\n');
|
|
1225
|
+
}
|
|
1226
|
+
function formatPageContent(data) {
|
|
1227
|
+
const detail = resolveDetailLevel(data);
|
|
1228
|
+
const sections = Array.isArray(data.sections) ? data.sections : [];
|
|
1229
|
+
// Sort by attention descending so the most relevant content is shown first
|
|
1230
|
+
const sorted = [...sections].sort((a, b) => (numberOrNull(b?.attention) ?? 0) - (numberOrNull(a?.attention) ?? 0));
|
|
1231
|
+
const topSections = sorted.slice(0, pickByDetail(detail, { brief: 6, normal: 20, full: 30 }));
|
|
1232
|
+
if (detail === 'brief') {
|
|
1233
|
+
const topSnippet = topSections.slice(0, 2).map((section) => compactText(asString(section?.text) || '-', 80)).join(' | ');
|
|
1234
|
+
return formatBriefStatusResultBlock({
|
|
1235
|
+
title: 'Page Content Snapshot',
|
|
1236
|
+
status: `sections=${sections.length}`,
|
|
1237
|
+
result: topSnippet || 'no text extracted',
|
|
1238
|
+
blocker: sections.length === 0 ? 'no content extracted' : 'none',
|
|
1239
|
+
next: 'If required fields are missing, scroll and call `get_page_content` again.',
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
const lines = [
|
|
1243
|
+
'## Page Content Snapshot',
|
|
1244
|
+
'',
|
|
1245
|
+
`- Title: ${asString(data.title) || '-'}`,
|
|
1246
|
+
`- URL: ${asString(data.url) || '-'}`,
|
|
1247
|
+
`- Sections: ${sections.length}`,
|
|
1248
|
+
'',
|
|
1249
|
+
'### Key Text Blocks',
|
|
1250
|
+
'',
|
|
1251
|
+
];
|
|
1252
|
+
if (topSections.length === 0) {
|
|
1253
|
+
lines.push('- No sections extracted. Consider scrolling or using execute_javascript as fallback.');
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
for (const section of topSections) {
|
|
1257
|
+
const attention = numberOrNull(section?.attention);
|
|
1258
|
+
const level = typeof attention === 'number' ? `[attention=${attention.toFixed(2)}]` : '[attention=-]';
|
|
1259
|
+
lines.push(`- ${level} ${compactText(asString(section?.text) || '-', 400)}`);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
lines.push('', '### Recommended Next Step', '', '- If required fields are missing, scroll and call `get_page_content` again.');
|
|
1263
|
+
return lines.join('\n');
|
|
1264
|
+
}
|
|
1265
|
+
function formatFindElement(data) {
|
|
1266
|
+
const detail = resolveDetailLevel(data);
|
|
1267
|
+
const candidates = Array.isArray(data.candidates) ? data.candidates.slice(0, pickByDetail(detail, { brief: 5, normal: 10, full: 20 })) : [];
|
|
1268
|
+
const lines = [
|
|
1269
|
+
'## Element Search Result',
|
|
1270
|
+
'',
|
|
1271
|
+
`- Query: ${asString(data.query) || '-'}`,
|
|
1272
|
+
`- Candidates: ${Array.isArray(data.candidates) ? data.candidates.length : 0}`,
|
|
1273
|
+
'',
|
|
1274
|
+
'| rank | id | label | score | reason |',
|
|
1275
|
+
'|---:|---|---|---:|---|',
|
|
1276
|
+
];
|
|
1277
|
+
if (candidates.length === 0) {
|
|
1278
|
+
lines.push('| 1 | - | No matches | 0 | - |');
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
candidates.forEach((c, index) => {
|
|
1282
|
+
lines.push(`| ${index + 1} | \`${tableSafe(asString(c.id) || '-')}\` | ${tableSafe(compactText(asString(c.label) || '-', 70))} | ${numberText(c.score)} | ${tableSafe(compactText(asString(c.matchReason) || '-', 60))} |`);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
lines.push('', '### Recommended Next Step', '', '- Try the highest-score candidate first with `click` or `type_text`.');
|
|
1286
|
+
return lines.join('\n');
|
|
1287
|
+
}
|
|
1288
|
+
function formatActionResult(toolName, data) {
|
|
1289
|
+
const titleMap = {
|
|
1290
|
+
click: 'Click Result',
|
|
1291
|
+
type_text: 'Type Text Result',
|
|
1292
|
+
press_key: 'Press Key Result',
|
|
1293
|
+
wait: 'Wait Result',
|
|
1294
|
+
wait_for_stable: 'Stability Result',
|
|
1295
|
+
scroll: 'Scroll Result',
|
|
1296
|
+
go_back: 'Back Navigation Result',
|
|
1297
|
+
select_option: 'Select Option Result',
|
|
1298
|
+
hover: 'Hover Result',
|
|
1299
|
+
set_value: 'Set Value Result',
|
|
1300
|
+
switch_tab: 'Switch Tab Result',
|
|
1301
|
+
close_tab: 'Close Tab Result',
|
|
1302
|
+
handle_dialog: 'Handle Dialog Result',
|
|
1303
|
+
upload_file: 'Upload File Result',
|
|
1304
|
+
};
|
|
1305
|
+
const lines = [
|
|
1306
|
+
`## ${titleMap[toolName] || 'Action Result'}`,
|
|
1307
|
+
'',
|
|
1308
|
+
`- Success: ${boolText(data.success)}`,
|
|
1309
|
+
];
|
|
1310
|
+
if (data.page) {
|
|
1311
|
+
lines.push(`- URL: ${asString(data.page.url) || '-'}`);
|
|
1312
|
+
lines.push(`- Title: ${asString(data.page.title) || '-'}`);
|
|
1313
|
+
}
|
|
1314
|
+
if (toolName === 'wait_for_stable') {
|
|
1315
|
+
lines.push(`- Stable: ${boolText(data.stable)}`);
|
|
1316
|
+
lines.push(`- DOM Stable: ${boolText(data.domStable)}`);
|
|
1317
|
+
lines.push(`- Network Pending: ${numberOrNull(data.networkPending) ?? '-'}`);
|
|
1318
|
+
}
|
|
1319
|
+
if (data.newTabCreated) {
|
|
1320
|
+
lines.push(`- New Tab Created: ${asString(data.newTabCreated)}`);
|
|
1321
|
+
}
|
|
1322
|
+
if (data.dialog) {
|
|
1323
|
+
lines.push(`- Dialog Type: ${asString(data.dialog.type) || 'yes'}`);
|
|
1324
|
+
if (asString(data.dialog.message)) {
|
|
1325
|
+
lines.push(`- Dialog Message: ${compactText(asString(data.dialog.message), 180)}`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
if (data.filePath) {
|
|
1329
|
+
lines.push(`- File Path: ${asString(data.filePath)}`);
|
|
1330
|
+
}
|
|
1331
|
+
if (data.reason) {
|
|
1332
|
+
lines.push(`- Reason: ${singleLine(asString(data.reason))}`);
|
|
1333
|
+
}
|
|
1334
|
+
lines.push('', '### Recommended Next Step', '', '- Re-check page state via `get_page_info` or `get_page_content` if needed.');
|
|
1335
|
+
return lines.join('\n');
|
|
1336
|
+
}
|
|
1337
|
+
function formatTabList(data) {
|
|
1338
|
+
const detail = resolveDetailLevel(data);
|
|
1339
|
+
const tabs = Array.isArray(data.tabs) ? data.tabs : [];
|
|
1340
|
+
const topTabs = tabs.slice(0, pickByDetail(detail, { brief: 10, normal: 20, full: 40 }));
|
|
1341
|
+
const activeTabId = asString(data.activeTabId) || '-';
|
|
1342
|
+
const lines = [
|
|
1343
|
+
'## Tab List',
|
|
1344
|
+
'',
|
|
1345
|
+
`- Active Tab ID: ${activeTabId}`,
|
|
1346
|
+
`- Returned Tabs: ${tabs.length}`,
|
|
1347
|
+
'',
|
|
1348
|
+
'| id | active | title | url |',
|
|
1349
|
+
'|---|---|---|---|',
|
|
1350
|
+
];
|
|
1351
|
+
if (topTabs.length === 0) {
|
|
1352
|
+
lines.push('| - | - | - | - |');
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
for (const tab of topTabs) {
|
|
1356
|
+
const id = asString(tab?.id) || '-';
|
|
1357
|
+
const active = id === activeTabId ? 'yes' : 'no';
|
|
1358
|
+
const title = compactText(asString(tab?.title) || '-', 80);
|
|
1359
|
+
const url = compactText(asString(tab?.url) || '-', 120);
|
|
1360
|
+
lines.push(`| ${tableSafe(id)} | ${active} | ${tableSafe(title)} | ${tableSafe(url)} |`);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
lines.push('', '### Recommended Next Step', '', '- Use `switch_tab` with target tab ID before interacting.');
|
|
1364
|
+
return lines.join('\n');
|
|
1365
|
+
}
|
|
1366
|
+
function formatDialogInfo(data) {
|
|
1367
|
+
const pending = isObject(data.pendingDialog) ? data.pendingDialog : null;
|
|
1368
|
+
const detail = resolveDetailLevel(data);
|
|
1369
|
+
const history = Array.isArray(data.dialogHistory) ? data.dialogHistory : [];
|
|
1370
|
+
const recent = history.slice(-pickByDetail(detail, { brief: 5, normal: 10, full: 20 })).reverse();
|
|
1371
|
+
const lines = [
|
|
1372
|
+
'## Dialog Status',
|
|
1373
|
+
'',
|
|
1374
|
+
`- Pending Dialog: ${pending ? 'yes' : 'no'}`,
|
|
1375
|
+
`- History Entries: ${history.length}`,
|
|
1376
|
+
];
|
|
1377
|
+
if (pending) {
|
|
1378
|
+
lines.push(`- Pending Type: ${asString(pending.type) || '-'}`);
|
|
1379
|
+
lines.push(`- Pending Message: ${compactText(asString(pending.message) || '-', 180)}`);
|
|
1380
|
+
}
|
|
1381
|
+
lines.push('', '### Recent Dialog History', '', '| type | handled | message |', '|---|---|---|');
|
|
1382
|
+
if (recent.length === 0) {
|
|
1383
|
+
lines.push('| - | - | No dialog history |');
|
|
1384
|
+
}
|
|
1385
|
+
else {
|
|
1386
|
+
for (const item of recent) {
|
|
1387
|
+
lines.push(`| ${tableSafe(asString(item?.type) || '-')} | ${boolText(item?.handled)} | ${tableSafe(compactText(asString(item?.message) || '-', 100))} |`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
lines.push('', '### Recommended Next Step', '');
|
|
1391
|
+
if (pending) {
|
|
1392
|
+
lines.push('- Call `handle_dialog` before other page interactions.');
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
lines.push('- No blocking native dialog; continue normal browsing flow.');
|
|
1396
|
+
}
|
|
1397
|
+
return lines.join('\n');
|
|
1398
|
+
}
|
|
1399
|
+
function formatNetworkLogs(data) {
|
|
1400
|
+
const detail = resolveDetailLevel(data);
|
|
1401
|
+
const logs = Array.isArray(data.logs) ? data.logs : [];
|
|
1402
|
+
const topLogs = logs.slice(-pickByDetail(detail, { brief: 10, normal: 20, full: 40 })).reverse();
|
|
1403
|
+
const total = numberOrNull(data.totalCount);
|
|
1404
|
+
const failed = logs.filter((l) => Boolean(l?.error) || ((asNumber(l?.status) ?? 0) >= 400)).length;
|
|
1405
|
+
if (detail === 'brief') {
|
|
1406
|
+
const topIssues = Array.isArray(data.topIssues) ? data.topIssues : [];
|
|
1407
|
+
const issue = topIssues[0];
|
|
1408
|
+
const blocker = issue
|
|
1409
|
+
? `${asString(issue?.kind) || 'issue'} x${numberOrNull(issue?.count) ?? 0}`
|
|
1410
|
+
: (failed > 0 ? `failed=${failed}` : 'none');
|
|
1411
|
+
return formatBriefStatusResultBlock({
|
|
1412
|
+
title: 'Network Logs',
|
|
1413
|
+
status: `returned=${logs.length}${typeof total === 'number' ? `/${total}` : ''}; truncated=${boolText(data.truncated)}`,
|
|
1414
|
+
result: `failed=${failed}`,
|
|
1415
|
+
blocker,
|
|
1416
|
+
next: Boolean(data.hasMore) ? 'Call `get_network_logs` with `nextCursor`/higher `maxEntries`.' : 'Use `filter=failed` or `urlPattern` to narrow issues.',
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
const lines = [
|
|
1420
|
+
'## Network Logs',
|
|
1421
|
+
'',
|
|
1422
|
+
`- Returned Entries: ${logs.length}`,
|
|
1423
|
+
`- Total Before Truncation: ${typeof total === 'number' ? total : '-'}`,
|
|
1424
|
+
`- Failed Entries: ${failed}`,
|
|
1425
|
+
`- Truncated: ${boolText(data.truncated)}`,
|
|
1426
|
+
];
|
|
1427
|
+
const topIssues = Array.isArray(data.topIssues) ? data.topIssues : [];
|
|
1428
|
+
if (topIssues.length > 0) {
|
|
1429
|
+
const issueText = topIssues
|
|
1430
|
+
.slice(0, pickByDetail(detail, { brief: 2, normal: 3, full: 5 }))
|
|
1431
|
+
.map((issue) => `${asString(issue?.kind) || 'issue'} x${numberOrNull(issue?.count) ?? 0}`)
|
|
1432
|
+
.join(' ; ');
|
|
1433
|
+
lines.push(`- Top Issues: ${issueText}`);
|
|
1434
|
+
}
|
|
1435
|
+
lines.push('', '| method | status | type | duration(ms) | url |', '|---|---:|---|---:|---|');
|
|
1436
|
+
if (topLogs.length === 0) {
|
|
1437
|
+
lines.push('| - | - | - | - | No network logs |');
|
|
1438
|
+
}
|
|
1439
|
+
else {
|
|
1440
|
+
for (const log of topLogs) {
|
|
1441
|
+
const method = asString(log?.method) || '-';
|
|
1442
|
+
const status = asNumber(log?.status);
|
|
1443
|
+
const type = asString(log?.resourceType) || '-';
|
|
1444
|
+
const duration = asNumber(log?.timing?.duration);
|
|
1445
|
+
const url = compactText(asString(log?.url) || '-', 100);
|
|
1446
|
+
lines.push(`| ${tableSafe(method)} | ${status ?? '-'} | ${tableSafe(type)} | ${duration !== null ? duration.toFixed(0) : '-'} | ${tableSafe(url)} |`);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
lines.push('', '### Recommended Next Step', '', '- Use `filter=failed` or `urlPattern` to quickly narrow issues.');
|
|
1450
|
+
return lines.join('\n');
|
|
1451
|
+
}
|
|
1452
|
+
function formatConsoleLogs(data) {
|
|
1453
|
+
const detail = resolveDetailLevel(data);
|
|
1454
|
+
const logs = Array.isArray(data.logs) ? data.logs : [];
|
|
1455
|
+
const topLogs = logs.slice(-pickByDetail(detail, { brief: 15, normal: 30, full: 60 })).reverse();
|
|
1456
|
+
const counts = {
|
|
1457
|
+
error: logs.filter((l) => asString(l?.level) === 'error').length,
|
|
1458
|
+
warn: logs.filter((l) => asString(l?.level) === 'warn').length,
|
|
1459
|
+
info: logs.filter((l) => asString(l?.level) === 'info').length,
|
|
1460
|
+
log: logs.filter((l) => asString(l?.level) === 'log').length,
|
|
1461
|
+
debug: logs.filter((l) => asString(l?.level) === 'debug').length,
|
|
1462
|
+
};
|
|
1463
|
+
if (detail === 'brief') {
|
|
1464
|
+
const topIssues = Array.isArray(data.topIssues) ? data.topIssues : [];
|
|
1465
|
+
const issue = topIssues[0];
|
|
1466
|
+
const blocker = issue
|
|
1467
|
+
? `${asString(issue?.kind) || 'issue'} x${numberOrNull(issue?.count) ?? 0}`
|
|
1468
|
+
: (counts.error > 0 || counts.warn > 0 ? `error=${counts.error}, warn=${counts.warn}` : 'none');
|
|
1469
|
+
return formatBriefStatusResultBlock({
|
|
1470
|
+
title: 'Console Logs',
|
|
1471
|
+
status: `entries=${logs.length}; truncated=${boolText(data.truncated)}`,
|
|
1472
|
+
result: `error=${counts.error}, warn=${counts.warn}, info=${counts.info}`,
|
|
1473
|
+
blocker,
|
|
1474
|
+
next: Boolean(data.hasMore) ? 'Call `get_console_logs` with `nextCursor`/higher `maxEntries`.' : 'Fix error/warn first; widen to `level=all` only when needed.',
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
const lines = [
|
|
1478
|
+
'## Console Logs',
|
|
1479
|
+
'',
|
|
1480
|
+
`- Returned Entries: ${logs.length}`,
|
|
1481
|
+
`- error: ${counts.error}, warn: ${counts.warn}, info: ${counts.info}, log: ${counts.log}, debug: ${counts.debug}`,
|
|
1482
|
+
`- Truncated: ${boolText(data.truncated)}`,
|
|
1483
|
+
];
|
|
1484
|
+
const topIssues = Array.isArray(data.topIssues) ? data.topIssues : [];
|
|
1485
|
+
if (topIssues.length > 0) {
|
|
1486
|
+
const issueText = topIssues
|
|
1487
|
+
.slice(0, pickByDetail(detail, { brief: 2, normal: 3, full: 5 }))
|
|
1488
|
+
.map((issue) => `${asString(issue?.kind) || 'issue'} x${numberOrNull(issue?.count) ?? 0}`)
|
|
1489
|
+
.join(' ; ');
|
|
1490
|
+
lines.push(`- Top Issues: ${issueText}`);
|
|
1491
|
+
}
|
|
1492
|
+
lines.push('', '| level | timestamp | text |', '|---|---|---|');
|
|
1493
|
+
if (topLogs.length === 0) {
|
|
1494
|
+
lines.push('| - | - | No console logs |');
|
|
1495
|
+
}
|
|
1496
|
+
else {
|
|
1497
|
+
for (const log of topLogs) {
|
|
1498
|
+
const ts = asNumber(log?.timestamp);
|
|
1499
|
+
const tsText = ts === null ? '-' : new Date(ts).toISOString();
|
|
1500
|
+
lines.push(`| ${tableSafe(asString(log?.level) || '-')} | ${tableSafe(tsText)} | ${tableSafe(compactText(asString(log?.text) || '-', 120))} |`);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
lines.push('', '### Recommended Next Step', '', '- Fix error/warn first; use `level=all` only when deeper debugging is required.');
|
|
1504
|
+
return lines.join('\n');
|
|
1505
|
+
}
|
|
1506
|
+
function formatRunSubmit(data) {
|
|
1507
|
+
const lines = [
|
|
1508
|
+
'## Task Run Submitted',
|
|
1509
|
+
'',
|
|
1510
|
+
`- Run ID: ${asString(data.runId) || '-'}`,
|
|
1511
|
+
`- Session ID: ${asString(data.sessionId) || '-'}`,
|
|
1512
|
+
`- Status: ${asString(data.status) || '-'}`,
|
|
1513
|
+
`- Mode: ${asString(data.mode) || '-'}`,
|
|
1514
|
+
'',
|
|
1515
|
+
'### Recommended Next Step',
|
|
1516
|
+
'',
|
|
1517
|
+
'- Call `get_task_run` with this runId until terminal status.',
|
|
1518
|
+
];
|
|
1519
|
+
return lines.join('\n');
|
|
1520
|
+
}
|
|
1521
|
+
function formatRunStatus(data) {
|
|
1522
|
+
const detail = resolveDetailLevel(data);
|
|
1523
|
+
const done = asNumber(data.progress?.doneSteps);
|
|
1524
|
+
const total = asNumber(data.progress?.totalSteps);
|
|
1525
|
+
if (detail === 'brief') {
|
|
1526
|
+
const status = asString(data.status) || '-';
|
|
1527
|
+
const progressText = done !== null && total !== null ? `${done}/${total}` : '-';
|
|
1528
|
+
const verification = extractVerificationSnapshot(data);
|
|
1529
|
+
const resultParts = [
|
|
1530
|
+
asString(data.resultSummary) ? compactText(asString(data.resultSummary), 100) : null,
|
|
1531
|
+
Array.isArray(data.artifactIds) ? `artifacts=${data.artifactIds.length}` : null,
|
|
1532
|
+
`progress=${progressText}`,
|
|
1533
|
+
verification && !verification.pass
|
|
1534
|
+
? `schema(missing=${verification.missingFields.length}, type=${verification.typeMismatches.length})`
|
|
1535
|
+
: null,
|
|
1536
|
+
].filter(Boolean);
|
|
1537
|
+
const blocker = verification && !verification.pass
|
|
1538
|
+
? `schema verification failed${verification.reason ? `: ${verification.reason}` : ''}`
|
|
1539
|
+
: (data.error ? compactText(singleLine(JSON.stringify(data.error)), 120) : 'none');
|
|
1540
|
+
const next = status === 'queued' || status === 'running'
|
|
1541
|
+
? 'Continue polling `get_task_run` until terminal status.'
|
|
1542
|
+
: (Array.isArray(data.artifactIds) && data.artifactIds.length > 0)
|
|
1543
|
+
? 'Fetch evidence with `get_artifact`.'
|
|
1544
|
+
: 'Inspect `result` and decide whether retry is needed.';
|
|
1545
|
+
return formatBriefStatusResultBlock({
|
|
1546
|
+
title: 'Task Run Status',
|
|
1547
|
+
status: `${status}; runId=${asString(data.runId) || '-'}`,
|
|
1548
|
+
result: resultParts.join('; ') || 'no summary',
|
|
1549
|
+
blocker,
|
|
1550
|
+
next,
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
const lines = [
|
|
1554
|
+
'## Task Run Status',
|
|
1555
|
+
'',
|
|
1556
|
+
`- Run ID: ${asString(data.runId) || '-'}`,
|
|
1557
|
+
`- Template: ${asString(data.templateId) || '-'}`,
|
|
1558
|
+
`- Status: ${asString(data.status) || '-'}`,
|
|
1559
|
+
`- Progress: ${done !== null && total !== null ? `${done}/${total}` : '-'}`,
|
|
1560
|
+
];
|
|
1561
|
+
if (Array.isArray(data.artifactIds) && data.artifactIds.length > 0) {
|
|
1562
|
+
lines.push(`- Artifact IDs: ${data.artifactIds.join(', ')}`);
|
|
1563
|
+
}
|
|
1564
|
+
if (asString(data.resultSummary)) {
|
|
1565
|
+
lines.push(`- Result Summary: ${compactText(asString(data.resultSummary), 220)}`);
|
|
1566
|
+
}
|
|
1567
|
+
if (Array.isArray(data.evidenceRefs) && data.evidenceRefs.length > 0) {
|
|
1568
|
+
const refs = data.evidenceRefs
|
|
1569
|
+
.slice(0, 6)
|
|
1570
|
+
.map((ref) => `${asString(ref?.artifactId) || '-'}(${asString(ref?.reason) || 'evidence'})`)
|
|
1571
|
+
.join(', ');
|
|
1572
|
+
lines.push(`- Evidence Refs: ${refs}`);
|
|
1573
|
+
}
|
|
1574
|
+
const verification = extractVerificationSnapshot(data);
|
|
1575
|
+
if (verification && !verification.pass) {
|
|
1576
|
+
lines.push(`- Verification: pass=false, missing=${verification.missingFields.length}, type=${verification.typeMismatches.length}`);
|
|
1577
|
+
if (verification.missingFields.length > 0) {
|
|
1578
|
+
lines.push(`- Missing Fields: ${verification.missingFields.slice(0, 12).join(', ')}`);
|
|
1579
|
+
}
|
|
1580
|
+
if (verification.typeMismatches.length > 0) {
|
|
1581
|
+
lines.push(`- Type Mismatches: ${verification.typeMismatches.slice(0, 12).join(', ')}`);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (Array.isArray(data.schemaRepairHints) && data.schemaRepairHints.length > 0) {
|
|
1585
|
+
lines.push(`- Schema Repair Hints: ${data.schemaRepairHints.slice(0, 4).map((item) => singleLine(String(item))).join(' ; ')}`);
|
|
1586
|
+
}
|
|
1587
|
+
if (data.error) {
|
|
1588
|
+
lines.push(`- Error: ${singleLine(JSON.stringify(data.error))}`);
|
|
1589
|
+
}
|
|
1590
|
+
lines.push('', '### Recommended Next Step', '');
|
|
1591
|
+
if (data.status === 'queued' || data.status === 'running') {
|
|
1592
|
+
lines.push('- Continue polling `get_task_run`.');
|
|
1593
|
+
}
|
|
1594
|
+
else if (Array.isArray(data.artifactIds) && data.artifactIds.length > 0) {
|
|
1595
|
+
lines.push('- Retrieve artifacts with `get_artifact`.');
|
|
1596
|
+
}
|
|
1597
|
+
else {
|
|
1598
|
+
lines.push('- Inspect `result` and decide whether a retry is needed.');
|
|
1599
|
+
}
|
|
1600
|
+
return lines.join('\n');
|
|
1601
|
+
}
|
|
1602
|
+
function formatRunList(data) {
|
|
1603
|
+
const detail = resolveDetailLevel(data);
|
|
1604
|
+
const runs = Array.isArray(data.runs) ? data.runs.slice(0, pickByDetail(detail, { brief: 10, normal: 20, full: 40 })) : [];
|
|
1605
|
+
if (detail === 'brief') {
|
|
1606
|
+
const first = runs[0];
|
|
1607
|
+
const firstStatus = first ? `${asString(first?.runId) || '-'}:${asString(first?.status) || '-'}` : 'none';
|
|
1608
|
+
return formatBriefStatusResultBlock({
|
|
1609
|
+
title: 'Task Run List',
|
|
1610
|
+
status: `returned=${Array.isArray(data.runs) ? data.runs.length : 0}; total=${numberOrNull(data.total) ?? '-'}`,
|
|
1611
|
+
result: `latest=${firstStatus}`,
|
|
1612
|
+
blocker: Boolean(data.hasMore) ? 'more runs available via nextCursor' : 'none',
|
|
1613
|
+
next: first ? 'Call `get_task_run` for the latest run.' : 'Submit a new task run if no records exist.',
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
const lines = [
|
|
1617
|
+
'## Task Run List',
|
|
1618
|
+
'',
|
|
1619
|
+
`- Returned Runs: ${Array.isArray(data.runs) ? data.runs.length : 0}`,
|
|
1620
|
+
`- Total (filtered): ${numberOrNull(data.total) ?? '-'}`,
|
|
1621
|
+
'',
|
|
1622
|
+
'| runId | templateId | status | progress |',
|
|
1623
|
+
'|---|---|---|---|',
|
|
1624
|
+
];
|
|
1625
|
+
if (runs.length === 0) {
|
|
1626
|
+
lines.push('| - | - | - | - |');
|
|
1627
|
+
}
|
|
1628
|
+
else {
|
|
1629
|
+
for (const run of runs) {
|
|
1630
|
+
const done = asNumber(run?.progress?.doneSteps);
|
|
1631
|
+
const total = asNumber(run?.progress?.totalSteps);
|
|
1632
|
+
const progress = done !== null && total !== null ? `${done}/${total}` : '-';
|
|
1633
|
+
lines.push(`| ${tableSafe(asString(run?.runId) || '-')} | ${tableSafe(asString(run?.templateId) || '-')} | ${tableSafe(asString(run?.status) || '-')} | ${progress} |`);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
lines.push('', '### Recommended Next Step', '', '- Pick a runId and call `get_task_run` for details.');
|
|
1637
|
+
return lines.join('\n');
|
|
1638
|
+
}
|
|
1639
|
+
function formatArtifact(data) {
|
|
1640
|
+
const lines = [
|
|
1641
|
+
'## Artifact Chunk',
|
|
1642
|
+
'',
|
|
1643
|
+
`- Artifact ID: ${asString(data.artifactId) || '-'}`,
|
|
1644
|
+
`- MIME Type: ${asString(data.mimeType) || '-'}`,
|
|
1645
|
+
`- Offset: ${numberOrNull(data.offset) ?? '-'}`,
|
|
1646
|
+
`- Length: ${numberOrNull(data.length) ?? '-'}`,
|
|
1647
|
+
`- Total Size: ${numberOrNull(data.totalSize) ?? '-'}`,
|
|
1648
|
+
`- Complete: ${boolText(data.complete)}`,
|
|
1649
|
+
'',
|
|
1650
|
+
'### Recommended Next Step',
|
|
1651
|
+
'',
|
|
1652
|
+
data.complete
|
|
1653
|
+
? '- Artifact fully retrieved.'
|
|
1654
|
+
: '- Continue with a larger offset in `get_artifact` to fetch remaining chunks.',
|
|
1655
|
+
];
|
|
1656
|
+
return lines.join('\n');
|
|
1657
|
+
}
|
|
1658
|
+
function toDisplayText(value) {
|
|
1659
|
+
if (value === null || value === undefined)
|
|
1660
|
+
return '';
|
|
1661
|
+
if (typeof value === 'string')
|
|
1662
|
+
return value;
|
|
1663
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
1664
|
+
return String(value);
|
|
1665
|
+
try {
|
|
1666
|
+
return JSON.stringify(value);
|
|
1667
|
+
}
|
|
1668
|
+
catch {
|
|
1669
|
+
return String(value);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
function compactState(state) {
|
|
1673
|
+
if (!isObject(state))
|
|
1674
|
+
return '-';
|
|
1675
|
+
const importantKeys = ['value', 'checked', 'selected', 'disabled', 'expanded'];
|
|
1676
|
+
const picked = importantKeys
|
|
1677
|
+
.filter((key) => key in state)
|
|
1678
|
+
.map((key) => `${key}:${singleLine(String(state[key]))}`);
|
|
1679
|
+
if (picked.length === 0)
|
|
1680
|
+
return '-';
|
|
1681
|
+
return compactText(picked.join(', '), 60);
|
|
1682
|
+
}
|
|
1683
|
+
function boolText(value) {
|
|
1684
|
+
return Boolean(value) ? 'yes' : 'no';
|
|
1685
|
+
}
|
|
1686
|
+
function asString(value) {
|
|
1687
|
+
return typeof value === 'string' ? value : '';
|
|
1688
|
+
}
|
|
1689
|
+
function asNumber(value) {
|
|
1690
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
1691
|
+
}
|
|
1692
|
+
function numberOrNull(value) {
|
|
1693
|
+
return asNumber(value);
|
|
1694
|
+
}
|
|
1695
|
+
function asCursor(value) {
|
|
1696
|
+
return isObject(value) ? value : null;
|
|
1697
|
+
}
|
|
1698
|
+
function buildMaxEntriesCursorArgs(cursor) {
|
|
1699
|
+
if (!cursor)
|
|
1700
|
+
return null;
|
|
1701
|
+
const suggestedMaxEntries = numberOrNull(cursor.suggestedMaxEntries);
|
|
1702
|
+
if (suggestedMaxEntries === null)
|
|
1703
|
+
return null;
|
|
1704
|
+
return { maxEntries: suggestedMaxEntries };
|
|
1705
|
+
}
|
|
1706
|
+
function buildListRunsCursorArgs(cursor) {
|
|
1707
|
+
if (!cursor)
|
|
1708
|
+
return null;
|
|
1709
|
+
const offset = numberOrNull(cursor.offset);
|
|
1710
|
+
const limit = numberOrNull(cursor.limit);
|
|
1711
|
+
if (offset === null && limit === null)
|
|
1712
|
+
return null;
|
|
1713
|
+
const args = {};
|
|
1714
|
+
if (offset !== null)
|
|
1715
|
+
args.offset = offset;
|
|
1716
|
+
if (limit !== null)
|
|
1717
|
+
args.limit = limit;
|
|
1718
|
+
return args;
|
|
1719
|
+
}
|
|
1720
|
+
function numberText(value) {
|
|
1721
|
+
const n = asNumber(value);
|
|
1722
|
+
return n === null ? '-' : n.toFixed(3);
|
|
1723
|
+
}
|
|
1724
|
+
function compactText(value, maxLen) {
|
|
1725
|
+
const oneLine = singleLine(value);
|
|
1726
|
+
if (oneLine.length <= maxLen)
|
|
1727
|
+
return oneLine;
|
|
1728
|
+
return `${oneLine.slice(0, Math.max(0, maxLen - 1))}…`;
|
|
1729
|
+
}
|
|
1730
|
+
function singleLine(value) {
|
|
1731
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
1732
|
+
}
|
|
1733
|
+
function tableSafe(value) {
|
|
1734
|
+
return value.replace(/\|/g, '\\|');
|
|
1735
|
+
}
|
|
1736
|
+
function isObject(value) {
|
|
1737
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1738
|
+
}
|
|
1739
|
+
//# sourceMappingURL=ai-markdown.js.map
|