page-agent 0.0.0 → 0.0.2

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.
@@ -22,19 +22,18 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
22
22
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
23
23
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
24
24
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
25
- var _openai, _model, _bus, _wrapper, _indicator, _statusText, _historySection, _expandButton, _pauseButton, _stopButton, _inputSection, _taskInput, _bus2, _state, _isExpanded, _pageAgent, _userAnswerResolver, _isWaitingForUserAnswer, _Panel_instances, update_fn, show_fn, hide_fn, reset_fn, togglePause_fn, updatePauseButton_fn, stopAgent_fn, submitTask_fn, handleUserAnswer_fn, showInputArea_fn, hideInputArea_fn, shouldShowInputArea_fn, createWrapper_fn, setupEventListeners_fn, toggle_fn, expand_fn, collapse_fn, animateTextChange_fn, updateStatusIndicator_fn, updateHistory_fn, scrollToBottom_fn, createHistoryItem_fn, _cursor, _currentCursorX, _currentCursorY, _targetCursorX, _targetCursorY, _SimulatorMask_instances, createCursor_fn, moveCursorToTarget_fn, _llm, _totalWaitTime, _abortController, _PageAgent_instances, packMacroTool_fn, getSystemPrompt_fn, assembleUserPrompt_fn, onDone_fn, getBrowserState_fn, updateTree_fn;
26
- import { generateText, stepCountIs, tool } from "ai";
25
+ var _bus, _wrapper, _indicator, _statusText, _historySection, _expandButton, _pauseButton, _stopButton, _inputSection, _taskInput, _bus2, _state, _isExpanded, _pageAgent, _userAnswerResolver, _isWaitingForUserAnswer, _headerUpdateTimer, _pendingHeaderText, _isAnimating, _Panel_instances, update_fn, show_fn, hide_fn, reset_fn, togglePause_fn, updatePauseButton_fn, stopAgent_fn, submitTask_fn, handleUserAnswer_fn, showInputArea_fn, hideInputArea_fn, shouldShowInputArea_fn, createWrapper_fn, setupEventListeners_fn, toggle_fn, expand_fn, collapse_fn, startHeaderUpdateLoop_fn, stopHeaderUpdateLoop_fn, checkAndUpdateHeader_fn, animateTextChange_fn, updateStatusIndicator_fn, updateHistory_fn, scrollToBottom_fn, createHistoryItem_fn, _cursor, _currentCursorX, _currentCursorY, _targetCursorX, _targetCursorY, _SimulatorMask_instances, createCursor_fn, moveCursorToTarget_fn, _llm, _totalWaitTime, _abortController, _PageAgent_instances, packMacroTool_fn, getSystemPrompt_fn, assembleUserPrompt_fn, onDone_fn, getBrowserState_fn, updateTree_fn;
27
26
  import chalk from "chalk";
28
- import zod from "zod";
29
- import { createOpenAI } from "@ai-sdk/openai";
27
+ import zod, { z } from "zod";
30
28
  import { Motion } from "ai-motion";
31
29
  const VIEWPORT_EXPANSION = -1;
32
30
  const DEFAULT_MODEL_NAME = "gpt-41-mini-0414-global";
33
31
  const DEFAULT_API_KEY = "not-needed";
34
32
  const DEFAULT_BASE_URL = "http://localhost:3000/api/agent";
35
- const MACRO_TOOL_NAME = "AgentOutput";
36
33
  const LLM_MAX_RETRIES = 2;
37
34
  const MAX_STEPS = 20;
35
+ const DEFAULT_TEMPERATURE = 0.7;
36
+ const DEFAULT_MAX_TOKENS = 4096;
38
37
  const domTree = /* @__PURE__ */ __name((args = {
39
38
  doHighlightElements: true,
40
39
  focusHighlightIndex: -1,
@@ -1396,49 +1395,6 @@ function getPageInfo() {
1396
1395
  };
1397
1396
  }
1398
1397
  __name(getPageInfo, "getPageInfo");
1399
- const zhCN = {
1400
- ui: {
1401
- panel: {
1402
- ready: "准备就绪",
1403
- thinking: "正在思考...",
1404
- paused: "暂停中,稍后",
1405
- taskInput: "输入新任务,详细描述步骤,回车提交",
1406
- userAnswerPrompt: "请回答上面问题,回车提交",
1407
- taskTerminated: "任务已终止",
1408
- taskCompleted: "任务结束",
1409
- continueExecution: "继续执行",
1410
- userAnswer: "用户回答: {{input}}",
1411
- pause: "暂停",
1412
- continue: "继续",
1413
- stop: "终止",
1414
- expand: "展开历史",
1415
- collapse: "收起历史",
1416
- step: "步骤 {{number}} · {{time}}{{duration}}"
1417
- },
1418
- tools: {
1419
- clicking: "正在点击元素 [{{index}}]...",
1420
- inputting: "正在输入文本到元素 [{{index}}]...",
1421
- selecting: '正在选择选项 "{{text}}"...',
1422
- scrolling: "正在滚动页面...",
1423
- waiting: "等待 {{seconds}} 秒...",
1424
- done: "结束任务",
1425
- clicked: "🖱️ 已点击元素 [{{index}}]",
1426
- inputted: '⌨️ 已输入文本 "{{text}}"',
1427
- selected: '☑️ 已选择选项 "{{text}}"',
1428
- scrolled: "🛞 页面滚动完成",
1429
- waited: "⌛️ 等待完成",
1430
- executing: "正在执行 {{toolName}}..."
1431
- },
1432
- errors: {
1433
- elementNotFound: "未找到索引为 {{index}} 的交互元素",
1434
- taskRequired: "任务描述不能为空",
1435
- executionFailed: "任务执行失败",
1436
- notInputElement: "元素不是输入框或文本域",
1437
- notSelectElement: "元素不是选择框",
1438
- optionNotFound: '未找到选项 "{{text}}"'
1439
- }
1440
- }
1441
- };
1442
1398
  const enUS = {
1443
1399
  ui: {
1444
1400
  panel: {
@@ -1451,6 +1407,8 @@ const enUS = {
1451
1407
  taskCompleted: "Task completed",
1452
1408
  continueExecution: "Continue execution",
1453
1409
  userAnswer: "User answer: {{input}}",
1410
+ question: "Question: {{question}}",
1411
+ waitingPlaceholder: "Waiting for task to start...",
1454
1412
  pause: "Pause",
1455
1413
  continue: "Continue",
1456
1414
  stop: "Stop",
@@ -1470,7 +1428,10 @@ const enUS = {
1470
1428
  selected: '☑️ Selected option "{{text}}"',
1471
1429
  scrolled: "🛞 Page scrolled",
1472
1430
  waited: "⌛️ Wait completed",
1473
- executing: "正在执行 {{toolName}}..."
1431
+ executing: "Executing {{toolName}}...",
1432
+ resultSuccess: "success",
1433
+ resultFailure: "failed",
1434
+ resultError: "error"
1474
1435
  },
1475
1436
  errors: {
1476
1437
  elementNotFound: "No interactive element found at index {{index}}",
@@ -1482,9 +1443,57 @@ const enUS = {
1482
1443
  }
1483
1444
  }
1484
1445
  };
1446
+ const zhCN = {
1447
+ ui: {
1448
+ panel: {
1449
+ ready: "准备就绪",
1450
+ thinking: "正在思考...",
1451
+ paused: "暂停中,稍后",
1452
+ taskInput: "输入新任务,详细描述步骤,回车提交",
1453
+ userAnswerPrompt: "请回答上面问题,回车提交",
1454
+ taskTerminated: "任务已终止",
1455
+ taskCompleted: "任务结束",
1456
+ continueExecution: "继续执行",
1457
+ userAnswer: "用户回答: {{input}}",
1458
+ question: "询问: {{question}}",
1459
+ waitingPlaceholder: "等待任务开始...",
1460
+ pause: "暂停",
1461
+ continue: "继续",
1462
+ stop: "终止",
1463
+ expand: "展开历史",
1464
+ collapse: "收起历史",
1465
+ step: "步骤 {{number}} · {{time}}{{duration}}"
1466
+ },
1467
+ tools: {
1468
+ clicking: "正在点击元素 [{{index}}]...",
1469
+ inputting: "正在输入文本到元素 [{{index}}]...",
1470
+ selecting: '正在选择选项 "{{text}}"...',
1471
+ scrolling: "正在滚动页面...",
1472
+ waiting: "等待 {{seconds}} 秒...",
1473
+ done: "结束任务",
1474
+ clicked: "🖱️ 已点击元素 [{{index}}]",
1475
+ inputted: '⌨️ 已输入文本 "{{text}}"',
1476
+ selected: '☑️ 已选择选项 "{{text}}"',
1477
+ scrolled: "🛞 页面滚动完成",
1478
+ waited: "⌛️ 等待完成",
1479
+ executing: "正在执行 {{toolName}}...",
1480
+ resultSuccess: "成功",
1481
+ resultFailure: "失败",
1482
+ resultError: "错误"
1483
+ },
1484
+ errors: {
1485
+ elementNotFound: "未找到索引为 {{index}} 的交互元素",
1486
+ taskRequired: "任务描述不能为空",
1487
+ executionFailed: "任务执行失败",
1488
+ notInputElement: "元素不是输入框或文本域",
1489
+ notSelectElement: "元素不是选择框",
1490
+ optionNotFound: '未找到选项 "{{text}}"'
1491
+ }
1492
+ }
1493
+ };
1485
1494
  const locales = {
1486
- "zh-CN": zhCN,
1487
- "en-US": enUS
1495
+ "en-US": enUS,
1496
+ "zh-CN": zhCN
1488
1497
  };
1489
1498
  const _I18n = class _I18n {
1490
1499
  language;
@@ -1510,7 +1519,7 @@ const _I18n = class _I18n {
1510
1519
  }
1511
1520
  interpolate(template, params) {
1512
1521
  return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
1513
- return params[key]?.toString() || match;
1522
+ return params[key] != null ? params[key].toString() : match;
1514
1523
  });
1515
1524
  }
1516
1525
  getLanguage() {
@@ -1519,14 +1528,17 @@ const _I18n = class _I18n {
1519
1528
  };
1520
1529
  __name(_I18n, "I18n");
1521
1530
  let I18n = _I18n;
1522
- function assert(condition, message, silent) {
1523
- if (!condition) {
1524
- const errorMessage = message ?? "Assertion failed";
1525
- if (!silent) console.error(chalk.red(`❌ assert: ${errorMessage}`));
1526
- throw new Error(errorMessage);
1527
- }
1531
+ function parseLLMConfig(config) {
1532
+ return {
1533
+ baseURL: config.baseURL ?? DEFAULT_BASE_URL,
1534
+ apiKey: config.apiKey ?? DEFAULT_API_KEY,
1535
+ model: config.model ?? DEFAULT_MODEL_NAME,
1536
+ temperature: config.temperature ?? DEFAULT_TEMPERATURE,
1537
+ maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
1538
+ maxRetries: config.maxRetries ?? LLM_MAX_RETRIES
1539
+ };
1528
1540
  }
1529
- __name(assert, "assert");
1541
+ __name(parseLLMConfig, "parseLLMConfig");
1530
1542
  const _EventBus = class _EventBus extends EventTarget {
1531
1543
  /**
1532
1544
  * Listen to built-in events
@@ -1571,24 +1583,316 @@ function getEventBus(channel) {
1571
1583
  return bus;
1572
1584
  }
1573
1585
  __name(getEventBus, "getEventBus");
1586
+ const InvokeErrorType = {
1587
+ // Retryable
1588
+ NETWORK_ERROR: "network_error",
1589
+ // Network error, retry
1590
+ RATE_LIMIT: "rate_limit",
1591
+ // Rate limit, retry
1592
+ SERVER_ERROR: "server_error",
1593
+ // 5xx, retry
1594
+ NO_TOOL_CALL: "no_tool_call",
1595
+ // Model did not call tool
1596
+ INVALID_TOOL_ARGS: "invalid_tool_args",
1597
+ // Tool args don't match schema
1598
+ TOOL_EXECUTION_ERROR: "tool_execution_error",
1599
+ // Tool execution error
1600
+ UNKNOWN: "unknown",
1601
+ // Non-retryable
1602
+ AUTH_ERROR: "auth_error",
1603
+ // Authentication failed
1604
+ CONTEXT_LENGTH: "context_length",
1605
+ // Prompt too long
1606
+ CONTENT_FILTER: "content_filter"
1607
+ // Content filtered
1608
+ };
1609
+ const _InvokeError = class _InvokeError extends Error {
1610
+ type;
1611
+ retryable;
1612
+ statusCode;
1613
+ rawError;
1614
+ constructor(type, message, rawError) {
1615
+ super(message);
1616
+ this.name = "InvokeError";
1617
+ this.type = type;
1618
+ this.retryable = this.isRetryable(type);
1619
+ this.rawError = rawError;
1620
+ }
1621
+ isRetryable(type) {
1622
+ const retryableTypes = [
1623
+ InvokeErrorType.NETWORK_ERROR,
1624
+ InvokeErrorType.RATE_LIMIT,
1625
+ InvokeErrorType.SERVER_ERROR,
1626
+ InvokeErrorType.NO_TOOL_CALL,
1627
+ InvokeErrorType.INVALID_TOOL_ARGS,
1628
+ InvokeErrorType.TOOL_EXECUTION_ERROR,
1629
+ InvokeErrorType.UNKNOWN
1630
+ ];
1631
+ return retryableTypes.includes(type);
1632
+ }
1633
+ };
1634
+ __name(_InvokeError, "InvokeError");
1635
+ let InvokeError = _InvokeError;
1636
+ function zodToOpenAITool(name, tool2) {
1637
+ return {
1638
+ type: "function",
1639
+ function: {
1640
+ name,
1641
+ description: tool2.description,
1642
+ parameters: z.toJSONSchema(tool2.inputSchema, { target: "openapi-3.0" })
1643
+ }
1644
+ };
1645
+ }
1646
+ __name(zodToOpenAITool, "zodToOpenAITool");
1647
+ function lenientParseMacroToolCall(responseData, inputSchema) {
1648
+ const choice = responseData.choices?.[0];
1649
+ if (!choice) {
1650
+ throw new InvokeError(InvokeErrorType.UNKNOWN, "No choices in response", responseData);
1651
+ }
1652
+ switch (choice.finish_reason) {
1653
+ case "tool_calls":
1654
+ case "stop":
1655
+ break;
1656
+ case "length":
1657
+ throw new InvokeError(
1658
+ InvokeErrorType.CONTEXT_LENGTH,
1659
+ "Response truncated: max tokens reached"
1660
+ );
1661
+ case "content_filter":
1662
+ throw new InvokeError(InvokeErrorType.CONTENT_FILTER, "Content filtered by safety system");
1663
+ default:
1664
+ throw new InvokeError(
1665
+ InvokeErrorType.UNKNOWN,
1666
+ `Unexpected finish_reason: ${choice.finish_reason}`
1667
+ );
1668
+ }
1669
+ const actionSchema = inputSchema.shape.action;
1670
+ if (!actionSchema) {
1671
+ throw new Error('inputSchema must have an "action" field');
1672
+ }
1673
+ let arg = null;
1674
+ const toolCall = choice.message?.tool_calls?.[0]?.function;
1675
+ arg = toolCall?.arguments ?? null;
1676
+ if (arg && toolCall.name !== "AgentOutput") {
1677
+ console.log(chalk.yellow("lenientParseMacroToolCall: #1 fixing incorrect tool call"));
1678
+ let tmpArg;
1679
+ try {
1680
+ tmpArg = JSON.parse(arg);
1681
+ } catch (error2) {
1682
+ throw new InvokeError(
1683
+ InvokeErrorType.INVALID_TOOL_ARGS,
1684
+ "Failed to parse tool arguments as JSON",
1685
+ error2
1686
+ );
1687
+ }
1688
+ arg = JSON.stringify({ action: { [toolCall.name]: tmpArg } });
1689
+ }
1690
+ if (!arg) {
1691
+ arg = choice.message?.content.trim() || null;
1692
+ }
1693
+ if (!arg) {
1694
+ throw new InvokeError(
1695
+ InvokeErrorType.NO_TOOL_CALL,
1696
+ "No tool call or content found in response",
1697
+ responseData
1698
+ );
1699
+ }
1700
+ let parsedArgs;
1701
+ try {
1702
+ parsedArgs = JSON.parse(arg);
1703
+ } catch (error2) {
1704
+ throw new InvokeError(
1705
+ InvokeErrorType.INVALID_TOOL_ARGS,
1706
+ "Failed to parse tool arguments as JSON",
1707
+ error2
1708
+ );
1709
+ }
1710
+ if (parsedArgs.action || parsedArgs.evaluation_previous_goal || parsedArgs.next_goal) {
1711
+ if (!parsedArgs.action) {
1712
+ console.log(chalk.yellow("lenientParseMacroToolCall: #2 fixing incorrect tool call"));
1713
+ parsedArgs.action = {
1714
+ wait: { seconds: 1 }
1715
+ };
1716
+ }
1717
+ } else if (parsedArgs.type && parsedArgs.function) {
1718
+ if (parsedArgs.function.name !== "AgentOutput")
1719
+ throw new InvokeError(
1720
+ InvokeErrorType.INVALID_TOOL_ARGS,
1721
+ `Expected function name "AgentOutput", got "${parsedArgs.function.name}"`,
1722
+ null
1723
+ );
1724
+ console.log(chalk.yellow("lenientParseMacroToolCall: #3 fixing incorrect tool call"));
1725
+ parsedArgs = parsedArgs.function.arguments;
1726
+ } else if (parsedArgs.name && parsedArgs.arguments) {
1727
+ if (parsedArgs.name !== "AgentOutput")
1728
+ throw new InvokeError(
1729
+ InvokeErrorType.INVALID_TOOL_ARGS,
1730
+ `Expected function name "AgentOutput", got "${parsedArgs.name}"`,
1731
+ null
1732
+ );
1733
+ console.log(chalk.yellow("lenientParseMacroToolCall: #4 fixing incorrect tool call"));
1734
+ parsedArgs = parsedArgs.arguments;
1735
+ } else {
1736
+ console.log(chalk.yellow("lenientParseMacroToolCall: #5 fixing incorrect tool call"));
1737
+ parsedArgs = { action: parsedArgs };
1738
+ }
1739
+ if (typeof parsedArgs === "string") {
1740
+ console.log(chalk.yellow("lenientParseMacroToolCall: #6 fixing incorrect tool call"));
1741
+ try {
1742
+ parsedArgs = JSON.parse(parsedArgs);
1743
+ } catch (error2) {
1744
+ throw new InvokeError(
1745
+ InvokeErrorType.INVALID_TOOL_ARGS,
1746
+ "Failed to parse nested tool arguments as JSON",
1747
+ error2
1748
+ );
1749
+ }
1750
+ }
1751
+ const validation = inputSchema.safeParse(parsedArgs);
1752
+ if (validation.success) {
1753
+ return validation.data;
1754
+ } else {
1755
+ const action = parsedArgs.action ?? {};
1756
+ const actionName = Object.keys(action)[0] || "unknown";
1757
+ const actionArgs = JSON.stringify(action[actionName] || "unknown");
1758
+ throw new InvokeError(
1759
+ InvokeErrorType.INVALID_TOOL_ARGS,
1760
+ `Tool arguments validation failed: action "${actionName}" with args ${actionArgs}`,
1761
+ validation.error
1762
+ );
1763
+ }
1764
+ }
1765
+ __name(lenientParseMacroToolCall, "lenientParseMacroToolCall");
1766
+ function modelPatch(body) {
1767
+ const model = body.model || "";
1768
+ if (model.toLowerCase().startsWith("claude")) {
1769
+ body.tool_choice = { type: "tool", name: "AgentOutput" };
1770
+ body.thinking = { type: "disabled" };
1771
+ }
1772
+ if (model.toLowerCase().includes("grok")) {
1773
+ console.log("Applying Grok patch: removing tool_choice");
1774
+ delete body.tool_choice;
1775
+ console.log("Applying Grok patch: disable reasoning and thinking");
1776
+ body.thinking = { type: "disabled", effort: "minimal" };
1777
+ body.reasoning = { enabled: false, effort: "low" };
1778
+ }
1779
+ return body;
1780
+ }
1781
+ __name(modelPatch, "modelPatch");
1782
+ const _OpenAIClient = class _OpenAIClient {
1783
+ config;
1784
+ constructor(config) {
1785
+ this.config = config;
1786
+ }
1787
+ async invoke(messages, tools2, abortSignal) {
1788
+ const openaiTools = Object.entries(tools2).map(([name, tool22]) => zodToOpenAITool(name, tool22));
1789
+ let response;
1790
+ try {
1791
+ response = await fetch(`${this.config.baseURL}/chat/completions`, {
1792
+ method: "POST",
1793
+ headers: {
1794
+ "Content-Type": "application/json",
1795
+ Authorization: `Bearer ${this.config.apiKey}`
1796
+ },
1797
+ body: JSON.stringify(
1798
+ modelPatch({
1799
+ model: this.config.model,
1800
+ temperature: this.config.temperature,
1801
+ max_tokens: this.config.maxTokens,
1802
+ messages,
1803
+ tools: openaiTools,
1804
+ // tool_choice: 'required',
1805
+ tool_choice: { type: "function", function: { name: "AgentOutput" } },
1806
+ // model specific params
1807
+ // reasoning_effort: 'minimal',
1808
+ // verbosity: 'low',
1809
+ parallel_tool_calls: false
1810
+ })
1811
+ ),
1812
+ signal: abortSignal
1813
+ });
1814
+ } catch (error2) {
1815
+ throw new InvokeError(InvokeErrorType.NETWORK_ERROR, "Network request failed", error2);
1816
+ }
1817
+ if (!response.ok) {
1818
+ const errorData = await response.json().catch();
1819
+ const errorMessage = errorData.error?.message || response.statusText;
1820
+ if (response.status === 401 || response.status === 403) {
1821
+ throw new InvokeError(
1822
+ InvokeErrorType.AUTH_ERROR,
1823
+ `Authentication failed: ${errorMessage}`,
1824
+ errorData
1825
+ );
1826
+ }
1827
+ if (response.status === 429) {
1828
+ throw new InvokeError(
1829
+ InvokeErrorType.RATE_LIMIT,
1830
+ `Rate limit exceeded: ${errorMessage}`,
1831
+ errorData
1832
+ );
1833
+ }
1834
+ if (response.status >= 500) {
1835
+ throw new InvokeError(
1836
+ InvokeErrorType.SERVER_ERROR,
1837
+ `Server error: ${errorMessage}`,
1838
+ errorData
1839
+ );
1840
+ }
1841
+ throw new InvokeError(
1842
+ InvokeErrorType.UNKNOWN,
1843
+ `HTTP ${response.status}: ${errorMessage}`,
1844
+ errorData
1845
+ );
1846
+ }
1847
+ const data = await response.json();
1848
+ const tool2 = tools2.AgentOutput;
1849
+ const macroToolInput = lenientParseMacroToolCall(data, tool2.inputSchema);
1850
+ let toolResult;
1851
+ try {
1852
+ toolResult = await tool2.execute(macroToolInput);
1853
+ } catch (e) {
1854
+ throw new InvokeError(
1855
+ InvokeErrorType.TOOL_EXECUTION_ERROR,
1856
+ `Tool execution failed: ${e.message}`,
1857
+ e
1858
+ );
1859
+ }
1860
+ return {
1861
+ toolCall: {
1862
+ // id: toolCall.id,
1863
+ name: "AgentOutput",
1864
+ args: macroToolInput
1865
+ },
1866
+ toolResult,
1867
+ usage: {
1868
+ promptTokens: data.usage?.prompt_tokens ?? 0,
1869
+ completionTokens: data.usage?.completion_tokens ?? 0,
1870
+ totalTokens: data.usage?.total_tokens ?? 0,
1871
+ cachedTokens: data.usage?.prompt_tokens_details?.cached_tokens,
1872
+ reasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens
1873
+ },
1874
+ rawResponse: data
1875
+ };
1876
+ }
1877
+ };
1878
+ __name(_OpenAIClient, "OpenAIClient");
1879
+ let OpenAIClient = _OpenAIClient;
1574
1880
  const _LLM = class _LLM {
1575
1881
  constructor(config, id) {
1576
1882
  __publicField(this, "config");
1577
1883
  __publicField(this, "id");
1578
- __privateAdd(this, _openai);
1579
- __privateAdd(this, _model);
1884
+ __publicField(this, "client");
1580
1885
  __privateAdd(this, _bus);
1581
- this.config = {
1582
- baseURL: DEFAULT_BASE_URL,
1583
- apiKey: DEFAULT_API_KEY,
1584
- modelName: DEFAULT_MODEL_NAME,
1585
- maxRetries: LLM_MAX_RETRIES,
1586
- ...config
1587
- };
1886
+ this.config = parseLLMConfig(config);
1588
1887
  this.id = id;
1589
1888
  __privateSet(this, _bus, getEventBus(id));
1590
- __privateSet(this, _openai, createOpenAI({ baseURL: this.config.baseURL, apiKey: this.config.apiKey }));
1591
- __privateSet(this, _model, __privateGet(this, _openai).chat(this.config.modelName));
1889
+ this.client = new OpenAIClient({
1890
+ model: this.config.model,
1891
+ apiKey: this.config.apiKey,
1892
+ baseURL: this.config.baseURL,
1893
+ temperature: this.config.temperature,
1894
+ maxTokens: this.config.maxTokens
1895
+ });
1592
1896
  }
1593
1897
  /**
1594
1898
  * - call llm api *once*
@@ -1596,81 +1900,10 @@ const _LLM = class _LLM {
1596
1900
  * - return the result of the tool
1597
1901
  */
1598
1902
  async invoke(messages, tools2, abortSignal) {
1599
- const isClaude = this.config.modelName.slice(0, 8).includes("claude");
1600
- this.config.modelName.slice(0, 6).includes("qwen");
1601
- this.config.modelName.slice(0, 5).includes("gpt");
1602
1903
  return await withRetry(
1603
1904
  async () => {
1604
- const result = await generateText({
1605
- model: __privateGet(this, _model),
1606
- messages,
1607
- tools: tools2,
1608
- abortSignal,
1609
- /**
1610
- * 文档中没有说明,从源码看,@facts
1611
- * - 只会重试被识别为 retryable 的 API_CALL_ERROR
1612
- * - 返回无法解析的 json 应该不会重试
1613
- * - experimental_repairToolCall 只会执行一次,不算作重试
1614
- * @facts
1615
- * - 许多 proxy 过的 openAI 兼容接口返回的错误格式并不规范,通常不会被识别为 retryable
1616
- * @conclusion
1617
- * - 看起来并不实用,不如完全手工控制粗粒度重试
1618
- */
1619
- // maxRetries: this.config.maxRetries,
1620
- maxRetries: 0,
1621
- // toolChoice: 'required',
1622
- // @note incompatible to Claude
1623
- toolChoice: isClaude ? void 0 : { type: "tool", toolName: MACRO_TOOL_NAME },
1624
- /**
1625
- * controlled by main loop. our method only call api once
1626
- */
1627
- // stopWhen: [hasToolCall('done'), stepCountIs(100)],
1628
- stopWhen: [stepCountIs(1)],
1629
- // stopWhen: [hasToolCall('AgentOutput')],
1630
- providerOptions: {
1631
- openai: {
1632
- // @note this one needs all fields in tool schema must be `required`
1633
- // strictJsonSchema: true,
1634
- // This way only at most one tool can be called at a time
1635
- parallelToolCalls: false,
1636
- reasoningEffort: "minimal",
1637
- // @note not working
1638
- // serviceTier: 'priority',
1639
- textVerbosity: "low",
1640
- // @note Optimize OpenAI model caching, should be unique per user, currently has no effect
1641
- promptCacheKey: "page-agent:" + this.id
1642
- }
1643
- }
1644
- /**
1645
- * schema 出错时执行一次,不确定是否计入重试
1646
- * 目前看起来像是会直接抛错,被 withRetry 处理
1647
- * @note
1648
- * 如果不提供,则 ai-sdk 会把 tool-error 加入 message 中重新调用一次,
1649
- * 配合 stepCountIs 或者 hasToolCall 都会导致错误被 silent,toolResults 永远为 0
1650
- * 遗憾的是,这里没有办法抛错(抛错后回到默认逻辑),只要这里 repair 不好,就会导致 silent error
1651
- * 更糟糕的是,只要传入了 tools,无论 stopWhen 如何设置,都会被当作 multi-step,
1652
- * 本质上就和我们 single step 的逻辑冲突
1653
- * 长远来看必须删掉 ai-sdk,直接用 openAI API 实现
1654
- */
1655
- // experimental_repairToolCall: (options): Promise<LanguageModelV2ToolCall | null> => {
1656
- // console.error('hahhah', options)
1657
- // throw options.error
1658
- // },
1659
- });
1660
- console.log(chalk.blue.bold("LLM:invoke finished"), result);
1661
- const toolError = result.content.find((part) => part.type === "tool-error");
1662
- if (toolError) throw toolError.error;
1663
- assert(!result.text, "Model returned text without calling done tool", true);
1664
- assert(result.toolCalls.length === 1, "Model must call exactly one tool", true);
1665
- assert(result.toolResults.length === 1, "Step must have exactly one tool result", true);
1666
- const toolCall = result.toolCalls[0];
1667
- const toolResult = result.toolResults[0];
1668
- const usage = result.totalUsage;
1669
- return {
1670
- toolCall,
1671
- toolResult,
1672
- usage
1673
- };
1905
+ const result = await this.client.invoke(messages, tools2, abortSignal);
1906
+ return result;
1674
1907
  },
1675
1908
  // retry settings
1676
1909
  {
@@ -1691,8 +1924,6 @@ const _LLM = class _LLM {
1691
1924
  );
1692
1925
  }
1693
1926
  };
1694
- _openai = new WeakMap();
1695
- _model = new WeakMap();
1696
1927
  _bus = new WeakMap();
1697
1928
  __name(_LLM, "LLM");
1698
1929
  let LLM = _LLM;
@@ -1710,6 +1941,7 @@ async function withRetry(fn, settings) {
1710
1941
  console.error(error2);
1711
1942
  settings.onError(error2, retries < settings.maxRetries);
1712
1943
  if (error2?.name === "AbortError") throw error2;
1944
+ if (error2 instanceof InvokeError && !error2.retryable) throw error2;
1713
1945
  lastError = error2;
1714
1946
  retries++;
1715
1947
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -2049,6 +2281,10 @@ const utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
2049
2281
  waitFor
2050
2282
  }, Symbol.toStringTag, { value: "Module" }));
2051
2283
  window.utils = utils;
2284
+ function tool(options) {
2285
+ return options;
2286
+ }
2287
+ __name(tool, "tool");
2052
2288
  const tools = /* @__PURE__ */ new Map();
2053
2289
  tools.set(
2054
2290
  "done",
@@ -2058,7 +2294,8 @@ tools.set(
2058
2294
  text: zod.string(),
2059
2295
  success: zod.boolean().default(true)
2060
2296
  }),
2061
- execute: /* @__PURE__ */ __name(function(input2) {
2297
+ execute: /* @__PURE__ */ __name(async function(input2) {
2298
+ return Promise.resolve("Task completed");
2062
2299
  }, "execute")
2063
2300
  })
2064
2301
  );
@@ -2389,6 +2626,9 @@ const _Panel = class _Panel {
2389
2626
  __privateAdd(this, _pageAgent);
2390
2627
  __privateAdd(this, _userAnswerResolver, null);
2391
2628
  __privateAdd(this, _isWaitingForUserAnswer, false);
2629
+ __privateAdd(this, _headerUpdateTimer, null);
2630
+ __privateAdd(this, _pendingHeaderText, null);
2631
+ __privateAdd(this, _isAnimating, false);
2392
2632
  __privateSet(this, _pageAgent, pageAgent);
2393
2633
  __privateSet(this, _bus2, pageAgent.bus);
2394
2634
  __privateSet(this, _wrapper, __privateMethod(this, _Panel_instances, createWrapper_fn).call(this));
@@ -2401,6 +2641,7 @@ const _Panel = class _Panel {
2401
2641
  __privateSet(this, _inputSection, __privateGet(this, _wrapper).querySelector(`.${styles$1.inputSectionWrapper}`));
2402
2642
  __privateSet(this, _taskInput, __privateGet(this, _wrapper).querySelector(`.${styles$1.taskInput}`));
2403
2643
  __privateMethod(this, _Panel_instances, setupEventListeners_fn).call(this);
2644
+ __privateMethod(this, _Panel_instances, startHeaderUpdateLoop_fn).call(this);
2404
2645
  __privateMethod(this, _Panel_instances, showInputArea_fn).call(this);
2405
2646
  __privateGet(this, _bus2).on("panel:show", () => __privateMethod(this, _Panel_instances, show_fn).call(this));
2406
2647
  __privateGet(this, _bus2).on("panel:hide", () => __privateMethod(this, _Panel_instances, hide_fn).call(this));
@@ -2421,7 +2662,7 @@ const _Panel = class _Panel {
2421
2662
  __privateSet(this, _userAnswerResolver, resolve);
2422
2663
  __privateMethod(this, _Panel_instances, update_fn).call(this, {
2423
2664
  type: "output",
2424
- displayText: `询问: ${question}`
2665
+ displayText: __privateGet(this, _pageAgent).i18n.t("ui.panel.question", { question })
2425
2666
  });
2426
2667
  if (!__privateGet(this, _isExpanded)) {
2427
2668
  __privateMethod(this, _Panel_instances, expand_fn).call(this);
@@ -2434,6 +2675,7 @@ const _Panel = class _Panel {
2434
2675
  */
2435
2676
  dispose() {
2436
2677
  __privateSet(this, _isWaitingForUserAnswer, false);
2678
+ __privateMethod(this, _Panel_instances, stopHeaderUpdateLoop_fn).call(this);
2437
2679
  this.wrapper.remove();
2438
2680
  }
2439
2681
  };
@@ -2452,13 +2694,17 @@ _isExpanded = new WeakMap();
2452
2694
  _pageAgent = new WeakMap();
2453
2695
  _userAnswerResolver = new WeakMap();
2454
2696
  _isWaitingForUserAnswer = new WeakMap();
2697
+ _headerUpdateTimer = new WeakMap();
2698
+ _pendingHeaderText = new WeakMap();
2699
+ _isAnimating = new WeakMap();
2455
2700
  _Panel_instances = new WeakSet();
2456
- update_fn = /* @__PURE__ */ __name(async function(stepData) {
2701
+ /**
2702
+ * Update status
2703
+ */
2704
+ update_fn = /* @__PURE__ */ __name(function(stepData) {
2457
2705
  const step = __privateGet(this, _state).addStep(stepData);
2458
2706
  const headerText = truncate(step.displayText, 20);
2459
- if (__privateGet(this, _statusText).textContent !== headerText) {
2460
- await __privateMethod(this, _Panel_instances, animateTextChange_fn).call(this, headerText);
2461
- }
2707
+ __privateSet(this, _pendingHeaderText, headerText);
2462
2708
  __privateMethod(this, _Panel_instances, updateStatusIndicator_fn).call(this, step.type);
2463
2709
  __privateMethod(this, _Panel_instances, updateHistory_fn).call(this);
2464
2710
  if (step.type === "completed" || step.type === "error") {
@@ -2482,7 +2728,7 @@ show_fn = /* @__PURE__ */ __name(function() {
2482
2728
  this.wrapper.style.transform = "translateX(-50%) translateY(0)";
2483
2729
  }, "#show");
2484
2730
  /**
2485
- * 隐藏面板
2731
+ * Hide panel
2486
2732
  */
2487
2733
  hide_fn = /* @__PURE__ */ __name(function() {
2488
2734
  this.wrapper.style.opacity = "0";
@@ -2490,7 +2736,7 @@ hide_fn = /* @__PURE__ */ __name(function() {
2490
2736
  this.wrapper.style.display = "none";
2491
2737
  }, "#hide");
2492
2738
  /**
2493
- * 重置状态
2739
+ * Reset state
2494
2740
  */
2495
2741
  reset_fn = /* @__PURE__ */ __name(function() {
2496
2742
  __privateGet(this, _state).reset();
@@ -2511,39 +2757,39 @@ togglePause_fn = /* @__PURE__ */ __name(function() {
2511
2757
  __privateGet(this, _pageAgent).paused = !__privateGet(this, _pageAgent).paused;
2512
2758
  __privateMethod(this, _Panel_instances, updatePauseButton_fn).call(this);
2513
2759
  if (__privateGet(this, _pageAgent).paused) {
2514
- __privateGet(this, _statusText).textContent = "暂停中,稍后";
2760
+ __privateGet(this, _statusText).textContent = __privateGet(this, _pageAgent).i18n.t("ui.panel.paused");
2515
2761
  __privateMethod(this, _Panel_instances, updateStatusIndicator_fn).call(this, "thinking");
2516
2762
  } else {
2517
- __privateGet(this, _statusText).textContent = "继续执行";
2763
+ __privateGet(this, _statusText).textContent = __privateGet(this, _pageAgent).i18n.t("ui.panel.continueExecution");
2518
2764
  __privateMethod(this, _Panel_instances, updateStatusIndicator_fn).call(this, "tool_executing");
2519
2765
  }
2520
2766
  }, "#togglePause");
2521
2767
  /**
2522
- * 更新暂停按钮状态
2768
+ * Update pause button state
2523
2769
  */
2524
2770
  updatePauseButton_fn = /* @__PURE__ */ __name(function() {
2525
2771
  if (__privateGet(this, _pageAgent).paused) {
2526
2772
  __privateGet(this, _pauseButton).textContent = "▶";
2527
- __privateGet(this, _pauseButton).title = "继续";
2773
+ __privateGet(this, _pauseButton).title = __privateGet(this, _pageAgent).i18n.t("ui.panel.continue");
2528
2774
  __privateGet(this, _pauseButton).classList.add(styles$1.paused);
2529
2775
  } else {
2530
2776
  __privateGet(this, _pauseButton).textContent = "⏸︎";
2531
- __privateGet(this, _pauseButton).title = "暂停";
2777
+ __privateGet(this, _pauseButton).title = __privateGet(this, _pageAgent).i18n.t("ui.panel.pause");
2532
2778
  __privateGet(this, _pauseButton).classList.remove(styles$1.paused);
2533
2779
  }
2534
2780
  }, "#updatePauseButton");
2535
2781
  /**
2536
- * 终止 Agent
2782
+ * Stop Agent
2537
2783
  */
2538
2784
  stopAgent_fn = /* @__PURE__ */ __name(function() {
2539
2785
  __privateMethod(this, _Panel_instances, update_fn).call(this, {
2540
2786
  type: "error",
2541
- displayText: "任务已终止"
2787
+ displayText: __privateGet(this, _pageAgent).i18n.t("ui.panel.taskTerminated")
2542
2788
  });
2543
2789
  __privateGet(this, _pageAgent).dispose();
2544
2790
  }, "#stopAgent");
2545
2791
  /**
2546
- * 提交任务
2792
+ * Submit task
2547
2793
  */
2548
2794
  submitTask_fn = /* @__PURE__ */ __name(function() {
2549
2795
  const input2 = __privateGet(this, _taskInput).value.trim();
@@ -2556,12 +2802,12 @@ submitTask_fn = /* @__PURE__ */ __name(function() {
2556
2802
  }
2557
2803
  }, "#submitTask");
2558
2804
  /**
2559
- * 处理用户回答
2805
+ * Handle user answer
2560
2806
  */
2561
2807
  handleUserAnswer_fn = /* @__PURE__ */ __name(function(input2) {
2562
2808
  __privateMethod(this, _Panel_instances, update_fn).call(this, {
2563
2809
  type: "input",
2564
- displayText: `用户回答: ${input2}`
2810
+ displayText: __privateGet(this, _pageAgent).i18n.t("ui.panel.userAnswer", { input: input2 })
2565
2811
  });
2566
2812
  __privateSet(this, _isWaitingForUserAnswer, false);
2567
2813
  if (__privateGet(this, _userAnswerResolver)) {
@@ -2570,24 +2816,24 @@ handleUserAnswer_fn = /* @__PURE__ */ __name(function(input2) {
2570
2816
  }
2571
2817
  }, "#handleUserAnswer");
2572
2818
  /**
2573
- * 显示输入区域
2819
+ * Show input area
2574
2820
  */
2575
2821
  showInputArea_fn = /* @__PURE__ */ __name(function(placeholder) {
2576
2822
  __privateGet(this, _taskInput).value = "";
2577
- __privateGet(this, _taskInput).placeholder = placeholder || "输入新任务,详细描述步骤,回车提交";
2823
+ __privateGet(this, _taskInput).placeholder = placeholder || __privateGet(this, _pageAgent).i18n.t("ui.panel.taskInput");
2578
2824
  __privateGet(this, _inputSection).classList.remove(styles$1.hidden);
2579
2825
  setTimeout(() => {
2580
2826
  __privateGet(this, _taskInput).focus();
2581
2827
  }, 100);
2582
2828
  }, "#showInputArea");
2583
2829
  /**
2584
- * 隐藏输入区域
2830
+ * Hide input area
2585
2831
  */
2586
2832
  hideInputArea_fn = /* @__PURE__ */ __name(function() {
2587
2833
  __privateGet(this, _inputSection).classList.add(styles$1.hidden);
2588
2834
  }, "#hideInputArea");
2589
2835
  /**
2590
- * 检查是否应该显示输入区域
2836
+ * Check if input area should be shown
2591
2837
  */
2592
2838
  shouldShowInputArea_fn = /* @__PURE__ */ __name(function() {
2593
2839
  if (__privateGet(this, _isWaitingForUserAnswer)) return true;
@@ -2612,23 +2858,23 @@ createWrapper_fn = /* @__PURE__ */ __name(function() {
2612
2858
  stepNumber: 0,
2613
2859
  timestamp: /* @__PURE__ */ new Date(),
2614
2860
  type: "thinking",
2615
- displayText: "等待任务开始..."
2861
+ displayText: __privateGet(this, _pageAgent).i18n.t("ui.panel.waitingPlaceholder")
2616
2862
  })}
2617
2863
  </div>
2618
2864
  </div>
2619
2865
  <div class="${styles$1.header}">
2620
2866
  <div class="${styles$1.statusSection}">
2621
2867
  <div class="${styles$1.indicator} ${styles$1.thinking}"></div>
2622
- <div class="${styles$1.statusText}">准备就绪</div>
2868
+ <div class="${styles$1.statusText}">${__privateGet(this, _pageAgent).i18n.t("ui.panel.ready")}</div>
2623
2869
  </div>
2624
2870
  <div class="${styles$1.controls}">
2625
- <button class="${styles$1.controlButton} ${styles$1.expandButton}" title="展开历史">
2871
+ <button class="${styles$1.controlButton} ${styles$1.expandButton}" title="${__privateGet(this, _pageAgent).i18n.t("ui.panel.expand")}">
2626
2872
 
2627
2873
  </button>
2628
- <button class="${styles$1.controlButton} ${styles$1.pauseButton}" title="暂停">
2874
+ <button class="${styles$1.controlButton} ${styles$1.pauseButton}" title="${__privateGet(this, _pageAgent).i18n.t("ui.panel.pause")}">
2629
2875
  ⏸︎
2630
2876
  </button>
2631
- <button class="${styles$1.controlButton} ${styles$1.stopButton}" title="终止">
2877
+ <button class="${styles$1.controlButton} ${styles$1.stopButton}" title="${__privateGet(this, _pageAgent).i18n.t("ui.panel.stop")}">
2632
2878
  X
2633
2879
  </button>
2634
2880
  </div>
@@ -2696,19 +2942,53 @@ collapse_fn = /* @__PURE__ */ __name(function() {
2696
2942
  this.wrapper.classList.add(styles$1.collapsed);
2697
2943
  __privateGet(this, _expandButton).textContent = "▼";
2698
2944
  }, "#collapse");
2699
- animateTextChange_fn = /* @__PURE__ */ __name(async function(newText) {
2700
- return new Promise((resolve) => {
2701
- __privateGet(this, _statusText).classList.add(styles$1.fadeOut);
2945
+ /**
2946
+ * Start periodic header update loop
2947
+ */
2948
+ startHeaderUpdateLoop_fn = /* @__PURE__ */ __name(function() {
2949
+ __privateSet(this, _headerUpdateTimer, setInterval(() => {
2950
+ __privateMethod(this, _Panel_instances, checkAndUpdateHeader_fn).call(this);
2951
+ }, 450));
2952
+ }, "#startHeaderUpdateLoop");
2953
+ /**
2954
+ * Stop periodic header update loop
2955
+ */
2956
+ stopHeaderUpdateLoop_fn = /* @__PURE__ */ __name(function() {
2957
+ if (__privateGet(this, _headerUpdateTimer)) {
2958
+ clearInterval(__privateGet(this, _headerUpdateTimer));
2959
+ __privateSet(this, _headerUpdateTimer, null);
2960
+ }
2961
+ }, "#stopHeaderUpdateLoop");
2962
+ /**
2963
+ * Check if header needs update and trigger animation if not currently animating
2964
+ */
2965
+ checkAndUpdateHeader_fn = /* @__PURE__ */ __name(function() {
2966
+ if (!__privateGet(this, _pendingHeaderText) || __privateGet(this, _isAnimating)) {
2967
+ return;
2968
+ }
2969
+ if (__privateGet(this, _statusText).textContent === __privateGet(this, _pendingHeaderText)) {
2970
+ __privateSet(this, _pendingHeaderText, null);
2971
+ return;
2972
+ }
2973
+ const textToShow = __privateGet(this, _pendingHeaderText);
2974
+ __privateSet(this, _pendingHeaderText, null);
2975
+ __privateMethod(this, _Panel_instances, animateTextChange_fn).call(this, textToShow);
2976
+ }, "#checkAndUpdateHeader");
2977
+ /**
2978
+ * Animate text change with fade out/in effect
2979
+ */
2980
+ animateTextChange_fn = /* @__PURE__ */ __name(function(newText) {
2981
+ __privateSet(this, _isAnimating, true);
2982
+ __privateGet(this, _statusText).classList.add(styles$1.fadeOut);
2983
+ setTimeout(() => {
2984
+ __privateGet(this, _statusText).textContent = newText;
2985
+ __privateGet(this, _statusText).classList.remove(styles$1.fadeOut);
2986
+ __privateGet(this, _statusText).classList.add(styles$1.fadeIn);
2702
2987
  setTimeout(() => {
2703
- __privateGet(this, _statusText).textContent = newText;
2704
- __privateGet(this, _statusText).classList.remove(styles$1.fadeOut);
2705
- __privateGet(this, _statusText).classList.add(styles$1.fadeIn);
2706
- setTimeout(() => {
2707
- __privateGet(this, _statusText).classList.remove(styles$1.fadeIn);
2708
- resolve();
2709
- }, 300);
2710
- }, 150);
2711
- });
2988
+ __privateGet(this, _statusText).classList.remove(styles$1.fadeIn);
2989
+ __privateSet(this, _isAnimating, false);
2990
+ }, 300);
2991
+ }, 150);
2712
2992
  }, "#animateTextChange");
2713
2993
  updateStatusIndicator_fn = /* @__PURE__ */ __name(function(type) {
2714
2994
  __privateGet(this, _indicator).className = styles$1.indicator;
@@ -2716,7 +2996,7 @@ updateStatusIndicator_fn = /* @__PURE__ */ __name(function(type) {
2716
2996
  }, "#updateStatusIndicator");
2717
2997
  updateHistory_fn = /* @__PURE__ */ __name(function() {
2718
2998
  const steps = __privateGet(this, _state).getAllSteps();
2719
- __privateGet(this, _historySection).innerHTML = steps.slice(-10).map((step) => __privateMethod(this, _Panel_instances, createHistoryItem_fn).call(this, step)).join("");
2999
+ __privateGet(this, _historySection).innerHTML = steps.map((step) => __privateMethod(this, _Panel_instances, createHistoryItem_fn).call(this, step)).join("");
2720
3000
  __privateMethod(this, _Panel_instances, scrollToBottom_fn).call(this);
2721
3001
  }, "#updateHistory");
2722
3002
  scrollToBottom_fn = /* @__PURE__ */ __name(function() {
@@ -2735,7 +3015,9 @@ createHistoryItem_fn = /* @__PURE__ */ __name(function(step) {
2735
3015
  let statusIcon2 = "";
2736
3016
  if (step.type === "completed") {
2737
3017
  if (step.toolName === "done") {
2738
- const isSuccess = !step.toolResult || !step.toolResult.includes("失败") && !step.toolResult.includes("错误");
3018
+ const failureKeyword = __privateGet(this, _pageAgent).i18n.t("ui.tools.resultFailure");
3019
+ const errorKeyword = __privateGet(this, _pageAgent).i18n.t("ui.tools.resultError");
3020
+ const isSuccess = !step.toolResult || !step.toolResult.includes(failureKeyword) && !step.toolResult.includes(errorKeyword);
2739
3021
  typeClass = isSuccess ? styles$1.doneSuccess : styles$1.doneError;
2740
3022
  statusIcon2 = isSuccess ? "🎉" : "❌";
2741
3023
  } else {
@@ -2759,6 +3041,13 @@ createHistoryItem_fn = /* @__PURE__ */ __name(function(step) {
2759
3041
  } else {
2760
3042
  statusIcon2 = "🧠";
2761
3043
  }
3044
+ const durationText = step.duration ? ` · ${step.duration}ms` : "";
3045
+ const stepLabel = __privateGet(this, _pageAgent).i18n.t("ui.panel.step", {
3046
+ number: step.stepNumber.toString(),
3047
+ time,
3048
+ duration: durationText || ""
3049
+ // Explicitly pass empty string to replace template
3050
+ });
2762
3051
  return `
2763
3052
  <div class="${styles$1.historyItem} ${typeClass}">
2764
3053
  <div class="${styles$1.historyContent}">
@@ -2766,8 +3055,7 @@ createHistoryItem_fn = /* @__PURE__ */ __name(function(step) {
2766
3055
  <span>${step.displayText}</span>
2767
3056
  </div>
2768
3057
  <div class="${styles$1.historyMeta}">
2769
- 步骤 ${step.stepNumber} · ${time}
2770
- ${step.duration ? ` · ${step.duration}ms` : ""}
3058
+ ${stepLabel}
2771
3059
  </div>
2772
3060
  </div>
2773
3061
  `;
@@ -3028,6 +3316,14 @@ moveCursorToTarget_fn = /* @__PURE__ */ __name(function() {
3028
3316
  }, "#moveCursorToTarget");
3029
3317
  __name(_SimulatorMask, "SimulatorMask");
3030
3318
  let SimulatorMask = _SimulatorMask;
3319
+ function assert(condition, message, silent) {
3320
+ if (!condition) {
3321
+ const errorMessage = message ?? "Assertion failed";
3322
+ console.error(chalk.red(`❌ assert: ${errorMessage}`));
3323
+ throw new Error(errorMessage);
3324
+ }
3325
+ }
3326
+ __name(assert, "assert");
3031
3327
  const _PageAgent = class _PageAgent extends EventTarget {
3032
3328
  constructor(config = {}) {
3033
3329
  super();
@@ -3036,6 +3332,7 @@ const _PageAgent = class _PageAgent extends EventTarget {
3036
3332
  __publicField(this, "id", uid());
3037
3333
  __publicField(this, "bus", getEventBus(this.id));
3038
3334
  __publicField(this, "i18n");
3335
+ __publicField(this, "panel");
3039
3336
  __publicField(this, "paused", false);
3040
3337
  __publicField(this, "disposed", false);
3041
3338
  __publicField(this, "task", "");
@@ -3059,13 +3356,12 @@ const _PageAgent = class _PageAgent extends EventTarget {
3059
3356
  __publicField(this, "tools", new Map(tools));
3060
3357
  /** Fullscreen mask */
3061
3358
  __publicField(this, "mask", new SimulatorMask());
3062
- /** Interactive panel */
3063
- __publicField(this, "panel", new Panel(this));
3064
3359
  /** History records */
3065
3360
  __publicField(this, "history", []);
3066
3361
  this.config = config;
3067
3362
  __privateSet(this, _llm, new LLM(this.config, this.id));
3068
3363
  this.i18n = new I18n(this.config.language);
3364
+ this.panel = new Panel(this);
3069
3365
  patchReact();
3070
3366
  }
3071
3367
  /**
@@ -3108,18 +3404,16 @@ const _PageAgent = class _PageAgent extends EventTarget {
3108
3404
  content: __privateMethod(this, _PageAgent_instances, assembleUserPrompt_fn).call(this)
3109
3405
  }
3110
3406
  ],
3111
- // tools,
3112
- __privateMethod(this, _PageAgent_instances, packMacroTool_fn).call(this),
3407
+ { AgentOutput: __privateMethod(this, _PageAgent_instances, packMacroTool_fn).call(this) },
3113
3408
  __privateGet(this, _abortController).signal
3114
3409
  );
3115
- const toolResult = result.toolResult;
3116
- const input2 = toolResult.input;
3117
- const output2 = toolResult.output;
3410
+ const macroResult = result.toolResult;
3411
+ const input2 = macroResult.input;
3412
+ const output2 = macroResult.output;
3118
3413
  const brain = {
3119
- thinking: input2.thinking,
3120
- evaluation_previous_goal: input2.evaluation_previous_goal,
3121
- memory: input2.memory,
3122
- next_goal: input2.next_goal
3414
+ evaluation_previous_goal: input2.evaluation_previous_goal || "",
3415
+ memory: input2.memory || "",
3416
+ next_goal: input2.next_goal || ""
3123
3417
  };
3124
3418
  const actionName = Object.keys(input2.action)[0];
3125
3419
  const action = {
@@ -3144,8 +3438,8 @@ const _PageAgent = class _PageAgent extends EventTarget {
3144
3438
  };
3145
3439
  }
3146
3440
  if (actionName === "done") {
3147
- const success = action.input.success || false;
3148
- const text = action.input.text || "no text provided";
3441
+ const success = action.input?.success ?? false;
3442
+ const text = action.input?.text || "no text provided";
3149
3443
  console.log(chalk.green.bold("Task completed"), success, text);
3150
3444
  __privateMethod(this, _PageAgent_instances, onDone_fn).call(this, text, success);
3151
3445
  return {
@@ -3190,42 +3484,6 @@ _PageAgent_instances = new WeakSet();
3190
3484
  * - next_goal: string
3191
3485
  * - action: { toolName: toolInput }
3192
3486
  * where action must be selected from tools defined in this.tools
3193
- *
3194
- * @topic 要不要合并成一个 tool?
3195
- * @facts
3196
- * - 我们需要模型每步返回 evaluation/memory/goal 等思考过程
3197
- * - browser use 合并成一个巨大的 tool
3198
- * ```json
3199
- * {
3200
- * "memory": "...",
3201
- * "goal": "...",
3202
- * "actions": [
3203
- * {
3204
- * "name": "...",
3205
- * "args": "..."
3206
- * }
3207
- * // ...
3208
- * ]
3209
- * }
3210
- * ```
3211
- * - qwen 目前必须指定 function name 来确保 tool call
3212
- * @reasoning
3213
- * - 不能为了 qwen 的缺陷而设计系统
3214
- * - 更复杂的 tool 更容易出错
3215
- * - 分散的 tool 更容易利用 ai-sdk 的重试机制,也更容易处理错误
3216
- * - 不能用额外的步骤生成这些数据,不仅性能过差,而且 goal 之类的必须和 call 一起生成
3217
- * @options
3218
- * - Plan @A
3219
- * - 和 browser use 使用完全一致的做法,合并成一个大 tool,要求每次调用
3220
- * - 会把 tool 定义变得非常复杂,增加出错率
3221
- * - Plan @B
3222
- * - 每次调用两个 tool,其中一个用来输出思考
3223
- * - 很难用提示词 enforce 这么复杂的规则
3224
- * - Plan @C
3225
- * - 自动为每个 tool 增加固定的 reasoning/memory/goal 等输入,并自动拦截提取这些数据
3226
- * - 会让 tool 定义变得很长
3227
- * @conclusion
3228
- * - 使用 @A
3229
3487
  */
3230
3488
  packMacroTool_fn = /* @__PURE__ */ __name(function() {
3231
3489
  const tools2 = this.tools;
@@ -3234,70 +3492,75 @@ packMacroTool_fn = /* @__PURE__ */ __name(function() {
3234
3492
  [toolName]: tool2.inputSchema
3235
3493
  });
3236
3494
  });
3237
- const actionSchema = zod.union(actionSchemas);
3495
+ const actionSchema = zod.union(
3496
+ actionSchemas
3497
+ );
3498
+ const macroToolSchema = zod.object({
3499
+ // thinking: zod.string().optional(),
3500
+ evaluation_previous_goal: zod.string().optional(),
3501
+ memory: zod.string().optional(),
3502
+ next_goal: zod.string().optional(),
3503
+ action: actionSchema
3504
+ });
3238
3505
  return {
3239
- [MACRO_TOOL_NAME]: tool({
3240
- // description: 'Output the result of the agent',
3241
- inputSchema: zod.object({
3242
- // thinking: zod.string().optional(),
3243
- evaluation_previous_goal: zod.string().optional(),
3244
- memory: zod.string().optional(),
3245
- next_goal: zod.string().optional(),
3246
- action: actionSchema
3247
- }),
3248
- execute: /* @__PURE__ */ __name(async (input2, options) => {
3249
- if (__privateGet(this, _abortController).signal.aborted) throw new Error("AbortError");
3250
- await waitUntil(() => !this.paused);
3251
- console.log(chalk.blue.bold("MacroTool execute"), input2);
3252
- const action = input2.action;
3253
- const toolName = Object.keys(action)[0];
3254
- const toolInput = action[toolName];
3255
- const brain = trimLines(`✅: ${input2.evaluation_previous_goal}
3506
+ // name: MACRO_TOOL_NAME,
3507
+ // description: 'Execute agent action', // @todo remote
3508
+ inputSchema: macroToolSchema,
3509
+ execute: /* @__PURE__ */ __name(async (input2) => {
3510
+ if (__privateGet(this, _abortController).signal.aborted) throw new Error("AbortError");
3511
+ await waitUntil(() => !this.paused);
3512
+ console.log(chalk.blue.bold("MacroTool execute"), input2);
3513
+ const action = input2.action;
3514
+ const toolName = Object.keys(action)[0];
3515
+ const toolInput = action[toolName];
3516
+ const brain = trimLines(`✅: ${input2.evaluation_previous_goal}
3256
3517
  💾: ${input2.memory}
3257
3518
  🎯: ${input2.next_goal}
3258
3519
  `);
3259
- console.log(brain);
3260
- this.bus.emit("panel:update", {
3261
- type: "thinking",
3262
- displayText: brain
3263
- });
3264
- const tool2 = tools2.get(toolName);
3265
- assert(tool2, `Tool ${toolName} not found. (@note should have been caught before this!!!)`);
3266
- console.log(chalk.blue.bold(`Executing tool: ${toolName}`), toolInput, options);
3520
+ console.log(brain);
3521
+ this.bus.emit("panel:update", {
3522
+ type: "thinking",
3523
+ displayText: brain
3524
+ });
3525
+ const tool2 = tools2.get(toolName);
3526
+ assert(tool2, `Tool ${toolName} not found. (@note should have been caught before this!!!)`);
3527
+ console.log(chalk.blue.bold(`Executing tool: ${toolName}`), toolInput);
3528
+ this.bus.emit("panel:update", {
3529
+ type: "tool_executing",
3530
+ toolName,
3531
+ toolArgs: toolInput,
3532
+ displayText: getToolExecutingText(toolName, toolInput, this.i18n)
3533
+ });
3534
+ const startTime = Date.now();
3535
+ let result = await tool2.execute.bind(this)(toolInput);
3536
+ const duration = Date.now() - startTime;
3537
+ console.log(chalk.green.bold(`Tool (${toolName}) executed for ${duration}ms`), result);
3538
+ if (toolName === "wait") {
3539
+ __privateSet(this, _totalWaitTime, __privateGet(this, _totalWaitTime) + Math.round(toolInput.seconds + duration / 1e3));
3540
+ result += `
3541
+ <sys> You have waited ${__privateGet(this, _totalWaitTime)} seconds accumulatively.`;
3542
+ if (__privateGet(this, _totalWaitTime) >= 3)
3543
+ result += "\nDo NOT wait any longer unless you have a good reason.\n";
3544
+ result += "</sys>";
3545
+ } else {
3546
+ __privateSet(this, _totalWaitTime, 0);
3547
+ }
3548
+ const displayResult = getToolCompletedText(toolName, toolInput, this.i18n);
3549
+ if (displayResult)
3267
3550
  this.bus.emit("panel:update", {
3268
3551
  type: "tool_executing",
3269
3552
  toolName,
3270
3553
  toolArgs: toolInput,
3271
- displayText: getToolExecutingText(toolName, toolInput, this.i18n)
3554
+ toolResult: result,
3555
+ displayText: displayResult,
3556
+ duration
3272
3557
  });
3273
- const startTime = Date.now();
3274
- let result = await tool2.execute.bind(this)(toolInput, options);
3275
- const duration = Date.now() - startTime;
3276
- console.log(chalk.green.bold(`Tool (${toolName}) executed for ${duration}ms`), result);
3277
- if (toolName === "wait") {
3278
- __privateSet(this, _totalWaitTime, __privateGet(this, _totalWaitTime) + Math.round(toolInput.seconds + duration / 1e3));
3279
- result += `
3280
- <sys> You have waited ${__privateGet(this, _totalWaitTime)} seconds accumulatively.`;
3281
- if (__privateGet(this, _totalWaitTime) >= 3)
3282
- result += "\nDo NOT wait any longer unless you have a good reason.\n";
3283
- result += "</sys>";
3284
- } else {
3285
- __privateSet(this, _totalWaitTime, 0);
3286
- }
3287
- const displayResult = getToolCompletedText(toolName, toolInput, this.i18n);
3288
- if (displayResult)
3289
- this.bus.emit("panel:update", {
3290
- type: "tool_executing",
3291
- toolName,
3292
- toolArgs: toolInput,
3293
- toolResult: result,
3294
- displayText: displayResult,
3295
- duration
3296
- });
3297
- await new Promise((resolve) => setTimeout(resolve, 100));
3298
- return result;
3299
- }, "execute")
3300
- })
3558
+ await new Promise((resolve) => setTimeout(resolve, 100));
3559
+ return {
3560
+ input: input2,
3561
+ output: result
3562
+ };
3563
+ }, "execute")
3301
3564
  };
3302
3565
  }, "#packMacroTool");
3303
3566
  /**