@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/CHANGELOG.md +9 -140
  2. package/dist/application/task-lifecycle/advance.js +4 -17
  3. package/dist/application/task-lifecycle/observe.js +29 -43
  4. package/dist/application/task-lifecycle/plan-transitions.js +4 -5
  5. package/dist/build-stamp.json +3 -3
  6. package/dist/commands/client-recovery.js +55 -20
  7. package/dist/commands/new-task.js +5 -0
  8. package/dist/commands/status.js +57 -0
  9. package/dist/commands/task-contract.js +269 -0
  10. package/dist/executors/dag-pi-executor.js +230 -269
  11. package/dist/executors/pi-executor.js +0 -2
  12. package/dist/executors/pi-sdk-executor.js +39 -77
  13. package/dist/executors/shell-executor.js +2 -3
  14. package/dist/executors/shell-write-guard.js +32 -27
  15. package/dist/shared/pi-context-pressure/extension.js +16 -26
  16. package/dist/shared/pi-retry-settings.js +0 -40
  17. package/dist/shared/prompts.js +26 -0
  18. package/dist/task/config.js +2 -0
  19. package/dist/task/contract/validate-draft.js +106 -0
  20. package/dist/task/goal.js +3 -0
  21. package/dist/task/index.js +12 -0
  22. package/dist/task/lifecycle.js +1 -0
  23. package/dist/task/operator/capabilities.js +6 -0
  24. package/dist/task/operator/envelope.js +2 -0
  25. package/dist/task/operator/index.js +5 -0
  26. package/dist/task/operator/registry.js +2 -0
  27. package/dist/task/operator/types.js +1 -0
  28. package/dist/task/paths.js +1 -0
  29. package/dist/task/source-prepare/source-fidelity-pi.js +2 -2
  30. package/dist/task/source-references.js +23 -48
  31. package/dist/task/source-state.js +1 -0
  32. package/dist/task/subagent-guidance.js +1 -0
  33. package/dist/worker/console/chat/assistant-content.js +2 -23
  34. package/dist/worker/console/chat/explore-tools.js +0 -13
  35. package/dist/worker/console/chat/pi-runtime.js +1182 -221
  36. package/dist/worker/console/chat/routes.js +71 -213
  37. package/dist/worker/console/chat/sdd-data-alignment.js +9 -65
  38. package/dist/worker/console/chat/session-catalog.js +0 -32
  39. package/dist/worker/console/chat/session-store.js +6 -22
  40. package/dist/worker/console/chat/shortcuts.js +0 -6
  41. package/dist/worker/console/chat/sift-bridge.js +1 -0
  42. package/dist/worker/console/chat/tool-preview.js +0 -11
  43. package/dist/worker/console/chat/tools.js +0 -6
  44. package/dist/worker/console/console-update-and-init.js +19 -1
  45. package/dist/worker/console/console-update-runtime.js +20 -5
  46. package/dist/worker/console/index.js +20 -0
  47. package/dist/worker/console/operator-actions.js +1 -13
  48. package/dist/worker/console/routes.js +0 -8
  49. package/dist/worker/console/server.js +29 -11
  50. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-hUmM_GRf.js} +1 -1
  51. package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-Bt-xuieW.js} +1 -1
  52. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-DTqkL4DJ.js} +1 -1
  53. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-Cy0MNnzV.js} +1 -1
  54. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-DDdlT0Pd.js} +1 -1
  55. package/dist/worker/console/static/assets/channel-BpzRnZmU.js +1 -0
  56. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-3JawwNZ6.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-DoTp7Cri.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-CcirhZaS.js} +1 -1
  59. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-VrG21gnP.js} +1 -1
  60. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DDOgIS70.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CBjmgvJ_.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-CCQSht8M.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-BpGMkUUV.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-DNDT41QL.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-euXnDFbn.js} +1 -1
  66. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlMVqCc.js +1 -0
  67. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlMVqCc.js +1 -0
  68. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-BJ6NAeql.js} +1 -1
  69. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-F5JKIvIq.js} +1 -1
  70. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-Cwmkl_Gi.js} +1 -1
  71. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-BmCZ8815.js} +1 -1
  72. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-zWA3hUnX.js} +1 -1
  73. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-BzA-888F.js} +1 -1
  74. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-tPNheoch.js} +1 -1
  75. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-Cdi7Fyqp.js} +1 -1
  76. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-DFK9VBf1.js} +1 -1
  77. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-xCUr1kpg.js} +1 -1
  78. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-BANQHY_A.js} +1 -1
  79. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-VHJYEMkP.js} +1 -1
  80. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-DiyZQ0C6.js} +1 -1
  81. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-CZtIeBBa.js} +1 -1
  82. package/dist/worker/console/static/assets/index-BlN6ytLr.css +1 -0
  83. package/dist/worker/console/static/assets/index-CA-jfWlE.js +449 -0
  84. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-BuSspC0X.js} +1 -1
  85. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DRJZyPFy.js} +1 -1
  86. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-DIfXIVuE.js} +1 -1
  87. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-CT10_7Lt.js} +1 -1
  88. package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-ufemj74H.js} +1 -1
  89. package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-DEQe8-_m.js} +5 -5
  90. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-BRLUrbrC.js} +1 -1
  91. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BgibKtiF.js} +1 -1
  92. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-D3eyfdV9.js} +1 -1
  93. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DXeTk5Px.js} +1 -1
  94. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-BgXdFEe7.js} +1 -1
  95. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-CseC2A7R.js} +1 -1
  96. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-D80PFzfJ.js} +1 -1
  97. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BWzHW7LS.js} +1 -1
  98. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-xWQYTLAn.js} +1 -1
  99. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-CLxtpZeL.js} +1 -1
  100. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CiVoq4G-.js +1 -0
  101. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-DwugtmWV.js} +2 -2
  102. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BkdgdpfR.js +8 -0
  103. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-DHEOwHIe.js} +1 -1
  104. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-pmbZKlBK.js} +1 -1
  105. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-CivHoM5q.js} +1 -1
  106. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BfHIE7o6.js} +1 -1
  107. package/dist/worker/console/static/index.html +2 -2
  108. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +10 -122
  109. package/dist/worker/console/static-src/operator-chat/compaction-message.js +0 -7
  110. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +15 -35
  111. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +62 -136
  112. package/dist/worker/console/static-src/operator-chat/useChatStream.js +68 -159
  113. package/dist/worker/console/static-src/operator-chat/useChatThread.js +56 -75
  114. package/dist/worker/console/static-src/operator-chat/useComposer.js +78 -206
  115. package/dist/worker/console/workspace-context.js +1 -21
  116. package/dist/worker/observe/static/router.js +1 -1
  117. package/dist/worker/observe/static/styles.css +4 -7
  118. package/dist/worker/observe/static/views/dag-inspector.js +19 -12
  119. package/dist/worker/observe/static/views/dag.js +2 -2
  120. package/dist/worker/scheduler/index.js +25 -0
  121. package/dist/workflows/dag/facts.js +4 -0
  122. package/dist/workflows/dag/frontend-closeout.js +1 -1
  123. package/dist/workflows/dag/frontend-design-policy.js +1 -2
  124. package/dist/workflows/dag/frontend-implementation-contract.js +4 -8
  125. package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
  126. package/dist/workflows/dag/frontend-repair.js +76 -55
  127. package/dist/workflows/dag/frontend-review-context.js +1 -2
  128. package/dist/workflows/dag/frontend-review-scopes.js +12 -34
  129. package/dist/workflows/dag/frontend-shape-facts.js +2 -12
  130. package/dist/workflows/dag/frontend-verification-trace.js +42 -8
  131. package/dist/workflows/dag/index.js +6 -0
  132. package/dist/workflows/dag/init-hybrid.js +160 -97
  133. package/dist/workflows/dynamic/index.js +7 -0
  134. package/docs/architecture/runtime-boundaries.md +3 -3
  135. package/docs/templates/frontend-implementation-dag.json +8 -9
  136. package/package.json +3 -7
  137. package/skills/frontend-bounded-implement/SKILL.md +3 -4
  138. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  139. package/skills/frontend-contract/references/contract-protocol.md +1 -3
  140. package/skills/frontend-design-review/SKILL.md +7 -12
  141. package/skills/frontend-design-review/references/review-checklist.md +7 -7
  142. package/skills/frontend-plan/SKILL.md +0 -19
  143. package/skills/frontend-plan/references/decision-contract.md +19 -26
  144. package/skills/frontend-review/SKILL.md +18 -12
  145. package/skills/frontend-review/references/review-findings.md +43 -24
  146. package/dist/shared/pi-provider-recovery.js +0 -182
  147. package/dist/worker/console/chat/browser-automation.js +0 -363
  148. package/dist/worker/console/chat/browser-policy.js +0 -113
  149. package/dist/worker/console/chat/browser-routes.js +0 -162
  150. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +0 -44
  151. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +0 -89
  152. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +0 -202
  153. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +0 -79
  154. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +0 -86
  155. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +0 -38
  156. package/dist/worker/console/chat/pi-runtime/inspection-types.js +0 -28
  157. package/dist/worker/console/chat/pi-runtime/inspection.js +0 -439
  158. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +0 -153
  159. package/dist/worker/console/chat/pi-runtime/sdk-session.js +0 -5
  160. package/dist/worker/console/chat/scm-routes.js +0 -217
  161. package/dist/worker/console/chat/scm-service.js +0 -283
  162. package/dist/worker/console/chat/scm-tools.js +0 -111
  163. package/dist/worker/console/chat/session-mode-view.js +0 -49
  164. package/dist/worker/console/chat/session-mode.js +0 -220
  165. package/dist/worker/console/chat/terminal-routes.js +0 -216
  166. package/dist/worker/console/chat/terminal-sessions.js +0 -348
  167. package/dist/worker/console/chat/terminal-tools.js +0 -211
  168. package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
  169. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
  170. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
  171. package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
  172. package/dist/worker/console/static/assets/index-wXlQohNs.js +0 -469
  173. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
  174. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
  175. package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +0 -10
  176. package/dist/worker/console/static-src/operator-chat/chat-link.js +0 -94
  177. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +0 -24
  178. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +0 -192
  179. package/dist/worker/console/static-src/operator-chat/process-label.js +0 -41
  180. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +0 -13
  181. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +0 -10
  182. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +0 -51
  183. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +0 -21
  184. package/dist/worker/console/workspace-initialization.js +0 -209
  185. package/dist/workflows/dag/frontend-capacity.js +0 -9
  186. package/dist/workflows/dag/frontend-plan-completeness.js +0 -186
  187. package/dist/workflows/dag/frontend-plan-recovery-policy.js +0 -18
@@ -1,182 +0,0 @@
1
- import { buildGeneratedOverflowClassifierSource, isContextOverflow, } from "./pi-context-pressure/overflow.js";
2
- export const NORMALIZED_TRANSIENT_PROVIDER_PREFIX = "provider returned error:";
3
- const ERROR_TEXT_LIMIT = 1_500;
4
- const PERMANENT_ERROR_PATTERNS = [
5
- /\b(?:401|403|429)\b|unauthori[sz]ed|forbidden|authentication|invalid api[-_ ]?key|认证错误|鉴权失败/i,
6
- /\b(?:quota|billing|insufficient.?credit|out of budget)\b|余额不足|配额(?:不足|耗尽)/i,
7
- /rate.?limit|too many requests|请求.{0,8}(?:频繁|过多)|频率.{0,8}限制|限流/i,
8
- /aborted by user|user cancel|cancelled|canceled|用户取消/i,
9
- /business validation|invalid task|schema validation|业务校验/i,
10
- ];
11
- export const BUILT_IN_TRANSIENT_PROVIDER_RULES = Object.freeze([
12
- Object.freeze({
13
- id: "wrapped-transient-provider-error",
14
- all: Object.freeze([
15
- /\b(?:UnknownError|TypeValidationError)\b|Type validation failed/i,
16
- ]),
17
- any: Object.freeze([
18
- /\b502\b|\bcode[:\s]*502\b/i,
19
- /\bLLMRequestError\b/i,
20
- /network fluctuation|timeout|model processing timeout/i,
21
- /网络波动|模型处理超时|当前请求超时/i,
22
- /transient provider failure|non-standard (?:payload|response)|upstream returned/i,
23
- ]),
24
- }),
25
- Object.freeze({
26
- id: "internal-llm-request-error",
27
- all: Object.freeze([/\bLLMRequestError\b/i]),
28
- any: Object.freeze([
29
- /\b502\b|\bcode[:\s]*502\b/i,
30
- /network fluctuation|timeout|model processing timeout/i,
31
- /网络波动|模型处理超时|当前请求超时/i,
32
- /non-standard (?:payload|response)|upstream returned/i,
33
- ]),
34
- }),
35
- Object.freeze({
36
- id: "internal-provider-fluctuation",
37
- any: Object.freeze([/网络波动|模型处理超时|当前请求超时/i]),
38
- }),
39
- ]);
40
- function stringifyErrorPart(value) {
41
- if (typeof value === "string")
42
- return value;
43
- if (value == null)
44
- return "";
45
- try {
46
- return JSON.stringify(value);
47
- }
48
- catch {
49
- return String(value);
50
- }
51
- }
52
- function errorText(error) {
53
- if (typeof error === "string")
54
- return error;
55
- if (error == null)
56
- return "";
57
- if (typeof error !== "object")
58
- return String(error);
59
- const record = error;
60
- return [
61
- stringifyErrorPart(record.name),
62
- stringifyErrorPart(record.message),
63
- stringifyErrorPart(record.status),
64
- stringifyErrorPart(record.code),
65
- stringifyErrorPart(record.data),
66
- stringifyErrorPart(error),
67
- ]
68
- .filter(Boolean)
69
- .join(" ");
70
- }
71
- function patternMatches(pattern, text) {
72
- pattern.lastIndex = 0;
73
- const matched = pattern.test(text);
74
- pattern.lastIndex = 0;
75
- return matched;
76
- }
77
- function ruleMatches(rule, text) {
78
- const all = rule.all ?? [];
79
- const any = rule.any ?? [];
80
- if (all.length === 0 && any.length === 0)
81
- return false;
82
- if (!all.every((pattern) => patternMatches(pattern, text)))
83
- return false;
84
- return any.length === 0 || any.some((pattern) => patternMatches(pattern, text));
85
- }
86
- function isPermanentProviderError(error, text) {
87
- if (isContextOverflow(error))
88
- return true;
89
- return PERMANENT_ERROR_PATTERNS.some((pattern) => patternMatches(pattern, text));
90
- }
91
- export function classifyTransientProviderError(error, options = {}) {
92
- const text = errorText(error);
93
- if (!text || isPermanentProviderError(error, text)) {
94
- return { retryable: false, reason: "permanent" };
95
- }
96
- const rules = [
97
- ...BUILT_IN_TRANSIENT_PROVIDER_RULES,
98
- ...(options.additionalRules ?? []),
99
- ];
100
- const matched = rules.find((rule) => ruleMatches(rule, text));
101
- return matched
102
- ? { retryable: true, ruleId: matched.id }
103
- : { retryable: false, reason: "no-match" };
104
- }
105
- export function normalizeTransientProviderErrorMessage(current, options = {}) {
106
- if (!current || current.startsWith(NORMALIZED_TRANSIENT_PROVIDER_PREFIX)) {
107
- return undefined;
108
- }
109
- if (!classifyTransientProviderError(current, options).retryable)
110
- return undefined;
111
- const bounded = current.length <= ERROR_TEXT_LIMIT
112
- ? current
113
- : `${current.slice(0, ERROR_TEXT_LIMIT - 1)}…`;
114
- return `${NORMALIZED_TRANSIENT_PROVIDER_PREFIX} ${bounded}`;
115
- }
116
- export function shouldRewriteAssistantTransientProviderError(message, options = {}) {
117
- if (!message || typeof message !== "object")
118
- return undefined;
119
- const record = message;
120
- if (record.role !== "assistant")
121
- return undefined;
122
- if (record.stopReason !== undefined && record.stopReason !== "error") {
123
- return undefined;
124
- }
125
- const current = typeof record.errorMessage === "string" ? record.errorMessage : "";
126
- const rewritten = normalizeTransientProviderErrorMessage(current, options);
127
- return rewritten
128
- ? { message: { ...record, errorMessage: rewritten } }
129
- : undefined;
130
- }
131
- function serializePatterns(patterns) {
132
- return JSON.stringify(patterns.map((pattern) => ({ source: pattern.source, flags: pattern.flags })));
133
- }
134
- /** Generate the pure predicate embedded in the project-level OpenCode plugin. */
135
- export function buildGeneratedTransientProviderClassifierSource() {
136
- const rules = BUILT_IN_TRANSIENT_PROVIDER_RULES.map((rule) => ({
137
- id: rule.id,
138
- all: (rule.all ?? []).map((pattern) => ({
139
- source: pattern.source,
140
- flags: pattern.flags,
141
- })),
142
- any: (rule.any ?? []).map((pattern) => ({
143
- source: pattern.source,
144
- flags: pattern.flags,
145
- })),
146
- }));
147
- return `${buildGeneratedOverflowClassifierSource()}
148
- const TRANSIENT_PROVIDER_PERMANENT_PATTERNS = ${serializePatterns(PERMANENT_ERROR_PATTERNS)};
149
- const TRANSIENT_PROVIDER_RULES = ${JSON.stringify(rules)};
150
- function transientProviderStringify(value) {
151
- if (typeof value === "string") return value;
152
- if (value == null) return "";
153
- try { return JSON.stringify(value); } catch { return String(value); }
154
- }
155
- function transientProviderText(error) {
156
- if (typeof error === "string") return error;
157
- if (error == null) return "";
158
- if (typeof error !== "object") return String(error);
159
- return [
160
- transientProviderStringify(error.name),
161
- transientProviderStringify(error.message),
162
- transientProviderStringify(error.status),
163
- transientProviderStringify(error.code),
164
- transientProviderStringify(error.data),
165
- transientProviderStringify(error),
166
- ].filter(Boolean).join(" ");
167
- }
168
- function transientProviderPatternMatches(pattern, text) {
169
- return new RegExp(pattern.source, pattern.flags).test(text);
170
- }
171
- function transientProviderRuleMatches(rule, text) {
172
- if (rule.all.length === 0 && rule.any.length === 0) return false;
173
- if (!rule.all.every((pattern) => transientProviderPatternMatches(pattern, text))) return false;
174
- return rule.any.length === 0 || rule.any.some((pattern) => transientProviderPatternMatches(pattern, text));
175
- }
176
- function isKnownTransientProviderError(error) {
177
- const text = transientProviderText(error);
178
- if (!text || isOverflowMessage(text)) return false;
179
- if (TRANSIENT_PROVIDER_PERMANENT_PATTERNS.some((pattern) => transientProviderPatternMatches(pattern, text))) return false;
180
- return TRANSIENT_PROVIDER_RULES.some((rule) => transientProviderRuleMatches(rule, text));
181
- }`;
182
- }
@@ -1,363 +0,0 @@
1
- /**
2
- * Server-side implementation for the model-callable sidebar browser tools.
3
- *
4
- * Navigation is delivered to the visible BrowserPanel through a small pending
5
- * command queue. Page inspection uses the same http(s) URL loaded by that
6
- * panel and parses the server-visible HTML. Static GET forms are submitted as
7
- * real navigations; dynamic scripts and POST forms are intentionally refused.
8
- */
9
- import { normalizeBrowserUrl } from "./browser-policy.js";
10
- export class BrowserAutomationService {
11
- fetchImpl;
12
- pending = [];
13
- sequence = 0;
14
- constructor(fetchImpl = fetch) {
15
- this.fetchImpl = fetchImpl;
16
- }
17
- async navigate(rawUrl, options) {
18
- const normalized = normalizeBrowserUrl(rawUrl);
19
- if (normalized.kind !== "ok") {
20
- return {
21
- ok: false,
22
- error: normalized.kind === "invalid"
23
- ? "invalid browser url"
24
- : "browser urls must use http(s)",
25
- };
26
- }
27
- let snapshot;
28
- const snapshotSource = rewriteBaiduSearchUrlForSnapshot(normalized.url);
29
- if (options?.includeSnapshot) {
30
- try {
31
- snapshot = await this.inspect(snapshotSource);
32
- }
33
- catch {
34
- // Navigation itself can still succeed when the panel cannot
35
- // fetch the page server-side; the iframe remains the fallback.
36
- }
37
- }
38
- const displayUrl = snapshotSource === normalized.url ? normalized.url : snapshotSource;
39
- const command = {
40
- kind: "navigate",
41
- id: this.nextId(),
42
- url: displayUrl,
43
- ...(snapshot ? { snapshot } : {}),
44
- createdAt: new Date().toISOString(),
45
- };
46
- this.pending = [command];
47
- return { ok: true, command };
48
- }
49
- reload() {
50
- const command = {
51
- kind: "reload",
52
- id: this.nextId(),
53
- createdAt: new Date().toISOString(),
54
- };
55
- this.pending = [command];
56
- return { ok: true, command };
57
- }
58
- take() {
59
- const commands = this.pending;
60
- this.pending = [];
61
- return commands;
62
- }
63
- async inspect(rawUrl) {
64
- const normalized = normalizeBrowserUrl(rawUrl);
65
- if (normalized.kind !== "ok")
66
- throw new BrowserAutomationError("INVALID_URL", "invalid browser url");
67
- const controller = new AbortController();
68
- const timer = setTimeout(() => controller.abort(), 12_000);
69
- try {
70
- const response = await this.fetchImpl(normalized.url, {
71
- redirect: "follow",
72
- signal: controller.signal,
73
- headers: { accept: "text/html,application/xhtml+xml" },
74
- });
75
- const contentType = response.headers.get("content-type") ?? "";
76
- if (!contentType.includes("html")) {
77
- throw new BrowserAutomationError("NOT_HTML", "the browser page did not return an HTML document");
78
- }
79
- const html = await response.text();
80
- if (html.length > 3_000_000) {
81
- throw new BrowserAutomationError("PAGE_TOO_LARGE", "page exceeds 3MB");
82
- }
83
- return parseBrowserPage(html, response.url || normalized.url);
84
- }
85
- catch (error) {
86
- if (error instanceof BrowserAutomationError)
87
- throw error;
88
- throw new BrowserAutomationError("FETCH_FAILED", "could not load the page", {
89
- cause: error,
90
- });
91
- }
92
- finally {
93
- clearTimeout(timer);
94
- }
95
- }
96
- async search(rawQuery, rawUrl) {
97
- const query = rawQuery.trim();
98
- if (!query)
99
- throw new BrowserAutomationError("EMPTY_QUERY", "search query is empty");
100
- let target = rawUrl?.trim() ? rawUrl.trim() : "https://m.baidu.com/s?wd=";
101
- const normalized = normalizeBrowserUrl(target);
102
- if (normalized.kind !== "ok") {
103
- throw new BrowserAutomationError("INVALID_URL", "search engine url must use http(s)");
104
- }
105
- const url = new URL(normalized.url);
106
- url.searchParams.set(url.searchParams.has("q") ? "q" : "wd", query);
107
- const result = await this.navigate(url.href, { includeSnapshot: true });
108
- if (!result.ok)
109
- throw new BrowserAutomationError("INVALID_URL", result.error);
110
- return result.command.kind === "navigate" && result.command.snapshot
111
- ? result.command.snapshot
112
- : await this.inspect(url.href);
113
- }
114
- async act(rawUrl, request) {
115
- const snapshot = await this.inspect(rawUrl);
116
- const element = selectBrowserElement(snapshot, request);
117
- if (!element) {
118
- throw new BrowserAutomationError("ELEMENT_NOT_FOUND", "selector did not match a clickable element or form field");
119
- }
120
- if (request.type === "fill") {
121
- if (element.tag !== "input" && element.tag !== "textarea") {
122
- throw new BrowserAutomationError("NOT_FILLABLE", "matched element is not fillable");
123
- }
124
- const submitted = this.submitForm(snapshot, element, request.value);
125
- const submittedSnapshot = await this.inspect(submitted.url);
126
- const navigation = await this.navigate(submitted.url, { includeSnapshot: true });
127
- if (!navigation.ok)
128
- throw new BrowserAutomationError("INVALID_URL", navigation.error);
129
- return {
130
- ...submittedSnapshot,
131
- action: { type: "fill", selector: request.selector, value: request.value, url: submitted.url },
132
- };
133
- }
134
- const formSubmit = element.clickable && element.formIndex !== undefined && !element.href
135
- ? this.submitForm(snapshot, element)
136
- : undefined;
137
- const targetUrl = formSubmit?.url ?? element.href;
138
- if (targetUrl === undefined) {
139
- throw new BrowserAutomationError("NOT_CLICKABLE", "matched element has no safe click target");
140
- }
141
- const navigation = await this.navigate(targetUrl, { includeSnapshot: true });
142
- if (!navigation.ok)
143
- throw new BrowserAutomationError("INVALID_URL", navigation.error);
144
- return {
145
- ...snapshot,
146
- action: {
147
- type: "click",
148
- selector: request.selector,
149
- url: targetUrl,
150
- },
151
- };
152
- }
153
- submitForm(snapshot, target, valueOverride) {
154
- if (target.formIndex === undefined) {
155
- throw new BrowserAutomationError("FORM_NOT_FOUND", "matched field is not inside a form");
156
- }
157
- if ((target.formMethod ?? "get") !== "get") {
158
- throw new BrowserAutomationError("FORM_METHOD_NOT_SUPPORTED", "only GET forms can be automated without browser scripts");
159
- }
160
- const url = new URL(target.formAction ?? snapshot.url);
161
- for (const element of snapshot.elements) {
162
- if (element.formIndex !== target.formIndex || !element.name)
163
- continue;
164
- if (element.tag === "button")
165
- continue;
166
- if (element.tag === "input" && ["submit", "button", "reset", "image"].includes(element.type ?? "")) {
167
- continue;
168
- }
169
- const value = element.selector === target.selector && valueOverride !== undefined
170
- ? valueOverride
171
- : element.value ?? "";
172
- url.searchParams.set(element.name, value);
173
- }
174
- return { url: url.href };
175
- }
176
- nextId() {
177
- this.sequence += 1;
178
- return `browser-${Date.now().toString(36)}-${this.sequence}`;
179
- }
180
- }
181
- export class BrowserAutomationError extends Error {
182
- code;
183
- constructor(code, message, options) {
184
- super(message, options);
185
- this.code = code;
186
- this.name = "BrowserAutomationError";
187
- }
188
- }
189
- export function rewriteBaiduSearchUrlForSnapshot(rawUrl) {
190
- try {
191
- const url = new URL(rawUrl);
192
- if (url.protocol !== "https:" || url.hostname !== "www.baidu.com")
193
- return rawUrl;
194
- if (url.pathname.replace(/\/+$/, "") !== "/s")
195
- return rawUrl;
196
- const query = url.searchParams.get("wd") ?? url.searchParams.get("word");
197
- if (query === null)
198
- return rawUrl;
199
- const mobile = new URL("https://m.baidu.com/s");
200
- mobile.searchParams.set("wd", query);
201
- return mobile.href;
202
- }
203
- catch {
204
- return rawUrl;
205
- }
206
- }
207
- const SKIP_TAGS = new Set(["script", "style", "template", "noscript"]);
208
- const INTERACTIVE_TAGS = new Set(["a", "button", "input", "select", "textarea"]);
209
- export function parseBrowserPage(html, baseUrl) {
210
- const state = { snapshot: { url: baseUrl, title: "", elements: [] }, nextFormIndex: 0 };
211
- const tokenizer = /<!--[\s\S]*?-->|<\/?([a-zA-Z][a-zA-Z0-9:-]*)([^>]*)>/g;
212
- const openTags = [];
213
- let cursor = 0;
214
- let match;
215
- while ((match = tokenizer.exec(html)) !== null) {
216
- const text = decodeEntities(html.slice(cursor, match.index)).replace(/\s+/g, " ").trim();
217
- const parent = openTags[openTags.length - 1];
218
- if (parent && !SKIP_TAGS.has(parent.tag))
219
- appendText(parent, text);
220
- cursor = match.index + match[0].length;
221
- if (match[0].startsWith("<!--"))
222
- continue;
223
- const tag = match[1]?.toLowerCase() ?? "";
224
- const attrs = parseAttributes(match[2] ?? "");
225
- if (match[0].startsWith("</")) {
226
- const index = findOpenTag(openTags, tag);
227
- if (index >= 0) {
228
- const closed = openTags[index];
229
- openTags.length = index;
230
- closeElement(state, closed, baseUrl);
231
- }
232
- continue;
233
- }
234
- const parentForForm = openTags[openTags.length - 1];
235
- let form = parentForForm?.form;
236
- if (tag === "form" && !form) {
237
- form = {
238
- index: state.nextFormIndex,
239
- action: attrs.action,
240
- method: attrs.method?.toLowerCase(),
241
- };
242
- state.nextFormIndex += 1;
243
- }
244
- const element = { tag, attrs, text: "", form };
245
- if (/\/>$/.test(match[0]) || isVoidTag(tag)) {
246
- closeElement(state, element, baseUrl);
247
- }
248
- else {
249
- openTags.push(element);
250
- }
251
- }
252
- if (state.snapshot.elements.length > 300)
253
- state.snapshot.elements.length = 300;
254
- return state.snapshot;
255
- }
256
- function parseAttributes(raw) {
257
- const attrs = {};
258
- const pattern = /([:@a-zA-Z_#][^\s"'<>`=]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/g;
259
- let match;
260
- while ((match = pattern.exec(raw)) !== null) {
261
- attrs[match[1]?.toLowerCase() ?? ""] = decodeEntities(match[2] ?? match[3] ?? match[4] ?? "");
262
- }
263
- return attrs;
264
- }
265
- function isVoidTag(tag) {
266
- return tag === "input" || tag === "img" || tag === "br" || tag === "hr" || tag === "meta" || tag === "link";
267
- }
268
- function findOpenTag(stack, tag) {
269
- for (let index = stack.length - 1; index >= 0; index -= 1) {
270
- if (stack[index]?.tag === tag)
271
- return index;
272
- }
273
- return -1;
274
- }
275
- function appendText(target, text) {
276
- target.text = target.text ? `${target.text} ${text}` : text;
277
- }
278
- function closeElement(state, element, baseUrl) {
279
- const { tag, attrs, text } = element;
280
- if (tag === "title") {
281
- state.snapshot.title = text.slice(0, 300);
282
- return;
283
- }
284
- if (!INTERACTIVE_TAGS.has(tag))
285
- return;
286
- let href;
287
- if (tag === "a" && attrs.href && !/^(javascript|mailto|tel):/i.test(attrs.href.trim())) {
288
- try {
289
- href = new URL(attrs.href, baseUrl).href;
290
- }
291
- catch {
292
- href = undefined;
293
- }
294
- }
295
- const id = attrs.id;
296
- const name = attrs.name;
297
- const selector = buildSelector(tag, id, name, attrs.class, state.snapshot.elements.length);
298
- const rawText = text || attrs.value || attrs.placeholder || attrs["aria-label"] || attrs.title || "";
299
- state.snapshot.elements.push({
300
- tag,
301
- ...(id ? { id } : {}),
302
- text: rawText.replace(/\s+/g, " ").trim().slice(0, 240),
303
- ...(href ? { href } : {}),
304
- ...(name ? { name } : {}),
305
- ...(attrs.type ? { type: attrs.type } : {}),
306
- ...(attrs.value ? { value: attrs.value } : {}),
307
- ...(attrs.placeholder ? { placeholder: attrs.placeholder } : {}),
308
- ...(element.form ? { formIndex: element.form.index } : {}),
309
- ...(element.form ? {
310
- formAction: element.form.action === undefined
311
- ? baseUrl
312
- : new URL(element.form.action, baseUrl).href,
313
- formMethod: element.form.method ?? "get",
314
- } : {}),
315
- selector,
316
- clickable: tag === "a" || tag === "button" || (tag === "input" && ["submit", "button", "radio", "checkbox"].includes(attrs.type ?? "")),
317
- });
318
- }
319
- function buildSelector(tag, id, name, className, index) {
320
- if (id)
321
- return `#${escapeSelectorValue(id)}`;
322
- if (name)
323
- return `${tag}[name="${escapeSelectorValue(name)}"]`;
324
- if (className?.trim()) {
325
- const firstClass = className.trim().split(/\s+/)[0];
326
- if (firstClass)
327
- return `${tag}.${escapeSelectorValue(firstClass)}`;
328
- }
329
- return `${tag}:nth-of-type(${index + 1})`;
330
- }
331
- function escapeSelectorValue(value) {
332
- return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
333
- }
334
- export function selectBrowserElement(snapshot, request) {
335
- const selector = request.selector.trim();
336
- if (!selector)
337
- return undefined;
338
- return snapshot.elements.find((element) => element.selector === selector)
339
- ?? snapshot.elements.find((element) => element.id === selector.slice(1))
340
- ?? snapshot.elements.find((element) => element.name === selector)
341
- ?? snapshot.elements.find((element) => element.text.toLowerCase().includes(selector.toLowerCase()) && selector.length >= 2);
342
- }
343
- const ENTITY_MAP = {
344
- amp: "&",
345
- lt: "<",
346
- gt: ">",
347
- quot: "\"",
348
- apos: "'",
349
- nbsp: " ",
350
- };
351
- function decodeEntities(value) {
352
- return value.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (whole, entity) => {
353
- if (entity.startsWith("#x") || entity.startsWith("#X")) {
354
- const code = Number.parseInt(entity.slice(2), 16);
355
- return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
356
- }
357
- if (entity.startsWith("#")) {
358
- const code = Number.parseInt(entity.slice(1), 10);
359
- return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
360
- }
361
- return ENTITY_MAP[entity.toLowerCase()] ?? whole;
362
- });
363
- }
@@ -1,113 +0,0 @@
1
- /**
2
- * Pure URL policy for the Operator Chat browser panel: normalize user input
3
- * into an http(s) URL. Dependency-free so it is unit-testable.
4
- *
5
- * Only the scheme is gated: javascript:, data:, file: and friends must never
6
- * reach the iframe. Local addresses (localhost / 127.0.0.1 / ::1 / LAN
7
- * hosts) are deliberately allowed — the panel is a development console and
8
- * its users routinely browse local dev servers.
9
- */
10
- /**
11
- * Extract the `frame-ancestors` source list of a Content-Security-Policy
12
- * header, or undefined when the directive is absent (or empty).
13
- */
14
- export function extractFrameAncestors(csp) {
15
- if (csp === null)
16
- return undefined;
17
- for (const directive of csp.split(";")) {
18
- const parts = directive.trim().split(/\s+/);
19
- if (parts[0] === "frame-ancestors") {
20
- const sources = parts.slice(1).filter((source) => source !== "");
21
- return sources.length === 0 ? undefined : sources;
22
- }
23
- }
24
- return undefined;
25
- }
26
- /**
27
- * Decide whether a site can render inside the panel iframe. The signals are
28
- * exactly the ones the browser enforces when it refuses an iframe load:
29
- * X-Frame-Options DENY/SAMEORIGIN, or a frame-ancestors directive that does
30
- * not allow `*` ('self' means the SITE's own origin — never ours, so it also
31
- * blocks the panel). A site we could not reach yields 'unknown' and the
32
- * plain iframe stays.
33
- */
34
- export function embeddabilityOf(probe) {
35
- if (probe.reachable !== true)
36
- return "unknown";
37
- const xfo = probe.xFrameOptions?.trim().toUpperCase();
38
- if (xfo === "DENY" || xfo === "SAMEORIGIN")
39
- return "blocked";
40
- if (probe.frameAncestors !== undefined &&
41
- !probe.frameAncestors.some((source) => source === "*")) {
42
- return "blocked";
43
- }
44
- return "embeddable";
45
- }
46
- /**
47
- * Schemes that must never reach the iframe even without `//` (javascript:,
48
- * data:, file:, ...). Host:port lookalikes (example.com:8080) are NOT here —
49
- * they parse as hosts below.
50
- */
51
- const FORBIDDEN_SCHEMES = new Set([
52
- "javascript",
53
- "data",
54
- "file",
55
- "about",
56
- "vbscript",
57
- "blob",
58
- "mailto",
59
- "tel",
60
- "ftp",
61
- "ftps",
62
- "ws",
63
- "wss",
64
- "sftp",
65
- "ssh",
66
- "chrome",
67
- "chrome-extension",
68
- "moz-extension",
69
- "edge",
70
- "opera",
71
- "resource",
72
- "view-source",
73
- ]);
74
- /**
75
- * Normalize one address-bar input against the navigation policy.
76
- * @param input raw user text.
77
- */
78
- export function normalizeBrowserUrl(input) {
79
- const trimmed = input.trim();
80
- if (trimmed === "")
81
- return { kind: "invalid" };
82
- // Distinguish an explicit scheme from a bare host:port. "example.com:8080"
83
- // would match a naive scheme regex (dots are legal in schemes), so a
84
- // scheme prefix is only honored when it is http(s) or a known-forbidden
85
- // scheme; anything else is treated as a host and gets https://.
86
- const schemeMatch = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed);
87
- let withScheme;
88
- if (schemeMatch === null) {
89
- withScheme = `https://${trimmed}`;
90
- }
91
- else {
92
- const scheme = schemeMatch[1]?.toLowerCase() ?? "";
93
- if (scheme === "http" || scheme === "https")
94
- withScheme = trimmed;
95
- else if (FORBIDDEN_SCHEMES.has(scheme))
96
- return { kind: "blocked", reason: "scheme" };
97
- else
98
- withScheme = `https://${trimmed}`;
99
- }
100
- let url;
101
- try {
102
- url = new URL(withScheme);
103
- }
104
- catch {
105
- return { kind: "invalid" };
106
- }
107
- // Protocol backstop: any URL that still parses to a non-http(s) scheme
108
- // (ftp://, ws:// — which carry `//` and skip the list) is refused here.
109
- if (url.protocol !== "http:" && url.protocol !== "https:") {
110
- return { kind: "blocked", reason: "scheme" };
111
- }
112
- return { kind: "ok", url: url.href };
113
- }