@tea-agent/loop-agent 0.43.0-next.9 → 0.44.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +108 -2308
  3. package/README.md +7 -0
  4. package/dist/application/dag/args.js +8 -0
  5. package/dist/application/task-lifecycle/advance.js +38 -7
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/cli/command-definitions.js +2 -0
  8. package/dist/cli/program.js +3 -1
  9. package/dist/cli/update/init-surface-notifier.js +2 -0
  10. package/dist/cli/update/policy.js +2 -0
  11. package/dist/commands/client-recovery.js +20 -55
  12. package/dist/commands/cursor-prompt.js +2 -0
  13. package/dist/commands/dag-approve.js +2 -0
  14. package/dist/commands/dag-final-verification.js +2 -0
  15. package/dist/commands/dag-init-hybrid.js +2 -0
  16. package/dist/commands/dag-reconcile-tasks.js +2 -0
  17. package/dist/commands/dag-reject.js +2 -0
  18. package/dist/commands/dag-report.js +2 -0
  19. package/dist/commands/dag-rerun.js +2 -0
  20. package/dist/commands/dag-resume.js +2 -0
  21. package/dist/commands/dag-workflow-compile.js +2 -0
  22. package/dist/commands/dag-workflow-plan.js +2 -0
  23. package/dist/commands/dag-workflow-validate.js +2 -0
  24. package/dist/commands/delegate.js +2 -0
  25. package/dist/commands/examples.js +2 -0
  26. package/dist/commands/import-prd.js +2 -0
  27. package/dist/commands/init-model-catalog.js +2 -0
  28. package/dist/commands/init.js +19 -13
  29. package/dist/commands/instructions.js +2 -0
  30. package/dist/commands/knowledge.js +2 -0
  31. package/dist/commands/loop-benchmark.js +2 -0
  32. package/dist/commands/pi-prompt.js +2 -0
  33. package/dist/commands/pi-reuse-benchmark.js +2 -0
  34. package/dist/commands/promote-run.js +2 -0
  35. package/dist/commands/stats.js +1 -1
  36. package/dist/commands/study-init.js +2 -0
  37. package/dist/commands/task-advance.js +34 -0
  38. package/dist/commands/task-source-prepare.js +2 -0
  39. package/dist/commands/worktree-create.js +2 -0
  40. package/dist/commands/worktree-remove.js +2 -0
  41. package/dist/executors/dag-pi-executor.js +1650 -198
  42. package/dist/executors/pi-executor.js +85 -3
  43. package/dist/executors/pi-sdk-executor.js +18 -0
  44. package/dist/executors/shell-executor.js +93 -7
  45. package/dist/executors/shell-verification.js +3 -0
  46. package/dist/executors/shell-write-guard.js +27 -32
  47. package/dist/governance/exec-plans.js +6 -3
  48. package/dist/infrastructure/console/app-data.js +6 -0
  49. package/dist/shared/artifacts-core.js +2 -0
  50. package/dist/shared/backend-dogfood-preflight.js +47 -0
  51. package/dist/shared/dag-failure-category.js +3 -0
  52. package/dist/shared/git-progress.js +2 -0
  53. package/dist/shared/one-shot-prompt-args.js +2 -0
  54. package/dist/shared/operator/capabilities.js +180 -0
  55. package/dist/shared/package-metadata.js +6 -4
  56. package/dist/shared/pi-context-pressure/extension.js +26 -16
  57. package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
  58. package/dist/shared/pi-provider-recovery.js +182 -0
  59. package/dist/shared/pi-retry-settings.js +40 -0
  60. package/dist/shared/reference-context.js +2 -0
  61. package/dist/task/config-types.js +29 -0
  62. package/dist/task/contract/project.js +9 -0
  63. package/dist/task/contract/recover.js +6 -0
  64. package/dist/task/contract/schema.js +17 -0
  65. package/dist/task/source-prepare/artifact-meta.js +9 -2
  66. package/dist/task/source-prepare/build-draft.js +60 -0
  67. package/dist/task/source-prepare/fragment-inventory.js +4 -1
  68. package/dist/task/source-prepare/parse-intent.js +36 -11
  69. package/dist/task/task-demand-routing.js +5 -2
  70. package/dist/worker/console/chat/browser-automation.js +523 -0
  71. package/dist/worker/console/chat/browser-policy.js +6 -36
  72. package/dist/worker/console/chat/browser-routes.js +188 -56
  73. package/dist/worker/console/chat/explore-tools.js +5 -2
  74. package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
  75. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
  76. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +184 -0
  77. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
  78. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
  79. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +94 -0
  80. package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
  81. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
  82. package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
  83. package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
  84. package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
  85. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
  86. package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
  87. package/dist/worker/console/chat/pi-runtime.js +208 -1138
  88. package/dist/worker/console/chat/routes.js +27 -7
  89. package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
  90. package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
  91. package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
  92. package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
  93. package/dist/worker/console/chat/session-mode-view.js +5 -4
  94. package/dist/worker/console/chat/session-mode.js +18 -12
  95. package/dist/worker/console/chat/session-store.js +42 -9
  96. package/dist/worker/console/chat/tool-preview.js +115 -0
  97. package/dist/worker/console/chat/tools.js +10 -0
  98. package/dist/worker/console/chat/turn-order.js +13 -0
  99. package/dist/worker/console/chat/turn-process.js +32 -30
  100. package/dist/worker/console/console-update-and-init.js +1 -19
  101. package/dist/worker/console/console-update-runtime.js +5 -20
  102. package/dist/worker/console/operator-actions.js +63 -1
  103. package/dist/worker/console/prd-intake-bridge.js +54 -1
  104. package/dist/worker/console/routes.js +8 -0
  105. package/dist/worker/console/scheduled-goal-host.js +98 -0
  106. package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
  107. package/dist/worker/console/server.js +24 -1
  108. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D_fNbWc_.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
  109. package/dist/worker/console/static/assets/{arc-_HuqLW6m.js → arc-eoQiMvuk.js} +1 -1
  110. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-Djy2qY1m.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
  111. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-bB-1TSyY.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
  112. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-C5Hmghrz.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
  113. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
  114. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Duyl39oy.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
  115. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-x6mdnSyf.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
  116. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-WlBuFj7W.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
  117. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CTF429HX.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
  118. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Dwd6r-SF.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
  119. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C-Br82D1.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
  120. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CuGp0DeE.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
  121. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CJhFX6Lh.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
  122. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DpGOa_jT.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
  123. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DXJaTynd.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
  124. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
  125. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
  126. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CRBZWjE4.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
  127. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BdXcxtlb.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
  128. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-07UKsOts.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
  129. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-D38p9__S.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
  130. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-nSiBNNjH.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
  131. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BFOTot5D.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
  132. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DypKaZ6k.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
  133. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-B6yoOQeW.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
  134. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BHYS6viX.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
  135. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-D2VD5Snj.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
  136. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-E98mWxN1.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
  137. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CT1zxhqP.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
  138. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-6GcngNcy.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
  139. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DCL6NvKe.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
  140. package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
  141. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +469 -0
  142. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C0627e-9.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
  143. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C_u3P6Ik.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
  144. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DPDPv9HL.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
  145. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-D3OHdWiO.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
  146. package/dist/worker/console/static/assets/{linear-DI88zD7n.js → linear-BDZ9riWi.js} +1 -1
  147. package/dist/worker/console/static/assets/{mermaid.core-p2l8IrWY.js → mermaid.core-7pKqYtpZ.js} +5 -5
  148. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-B1bAX-oS.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
  149. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DwOnWxdD.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
  150. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-xRkTXnJC.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
  151. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-_9UC5Zio.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
  152. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CgEUirWJ.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
  153. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Czbpwiyz.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
  154. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-_j7_KiqY.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
  155. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BNJa6Hyi.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
  156. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DedpKtOc.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
  157. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BBvthr7c.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
  158. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
  159. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Ds7umgEM.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
  160. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
  161. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKa-p1nF.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
  162. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-LoUNR56o.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
  163. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bqbl6ym7.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
  164. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B8dUg4lf.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
  165. package/dist/worker/console/static/index.html +2 -2
  166. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
  167. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
  168. package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
  169. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
  170. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
  171. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
  172. package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
  173. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +92 -38
  174. package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
  175. package/dist/worker/console/static-src/operator-chat/useChatThread.js +38 -55
  176. package/dist/worker/console/static-src/operator-chat/useComposer.js +182 -69
  177. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
  178. package/dist/worker/console/static-src/prd-file-import.js +3 -1
  179. package/dist/worker/console/workspace-context.js +22 -0
  180. package/dist/worker/console/workspace-initialization.js +209 -0
  181. package/dist/worker/delivery/git-transaction.js +2 -0
  182. package/dist/worker/feature/fullstack-validate.js +2 -0
  183. package/dist/worker/loop-agent/loop-agent-client.js +8 -2
  184. package/dist/worker/materialize/harness-task-materializer.js +4 -1
  185. package/dist/worker/metrics/projector.js +1 -1
  186. package/dist/worker/observability/event-store.js +4 -1
  187. package/dist/worker/observability/read-model.js +4 -1
  188. package/dist/worker/observe/node-transparency.js +23 -1
  189. package/dist/worker/observe/routes.js +4 -0
  190. package/dist/worker/observe/static/operator-chrome.js +3 -1
  191. package/dist/worker/observe/static/styles.css +7 -4
  192. package/dist/worker/observe/static/views/dag-inspector.js +5 -19
  193. package/dist/worker/outcomes/declared-artifacts.js +2 -0
  194. package/dist/worker/preflight.js +3 -0
  195. package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
  196. package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
  197. package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
  198. package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
  199. package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
  200. package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
  201. package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
  202. package/dist/worker/task-spec/validate.js +4 -1
  203. package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
  204. package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
  205. package/dist/workflows/dag/dag-retry-schema.js +11 -0
  206. package/dist/workflows/dag/decision-envelope.js +2 -0
  207. package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
  208. package/dist/workflows/dag/frontend-closeout.js +3 -1
  209. package/dist/workflows/dag/frontend-committed-facts.js +461 -0
  210. package/dist/workflows/dag/frontend-durable-tools.js +15 -3
  211. package/dist/workflows/dag/frontend-implementation-contract.js +369 -12
  212. package/dist/workflows/dag/frontend-plan-canary.js +53 -0
  213. package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
  214. package/dist/workflows/dag/frontend-plan-render.js +0 -2
  215. package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
  216. package/dist/workflows/dag/frontend-recovery-run.js +57 -0
  217. package/dist/workflows/dag/frontend-repair.js +55 -76
  218. package/dist/workflows/dag/frontend-review-context.js +42 -68
  219. package/dist/workflows/dag/frontend-review-scopes.js +2 -2
  220. package/dist/workflows/dag/frontend-risk.js +92 -10
  221. package/dist/workflows/dag/frontend-session-budget.js +117 -3
  222. package/dist/workflows/dag/frontend-shape.js +11 -55
  223. package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
  224. package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
  225. package/dist/workflows/dag/frontend-verification-trace.js +33 -54
  226. package/dist/workflows/dag/frontend-writer-admission.js +3 -47
  227. package/dist/workflows/dag/frontend-writer-status.js +0 -23
  228. package/dist/workflows/dag/init-hybrid.js +47 -19
  229. package/dist/workflows/dag/interrupt-request.js +2 -0
  230. package/dist/workflows/dag/lifecycle.js +11 -2
  231. package/dist/workflows/dag/node-execution.js +50 -13
  232. package/dist/workflows/dag/reconcile-run.js +2 -0
  233. package/dist/workflows/dag/repair-artifact.js +3 -1
  234. package/dist/workflows/dag/report.js +2 -0
  235. package/dist/workflows/dag/rerun-plan.js +10 -0
  236. package/dist/workflows/dag/rerun-task.js +79 -1
  237. package/dist/workflows/dag/retry-policy.js +18 -0
  238. package/dist/workflows/dag/scheduler.js +2 -4
  239. package/dist/workflows/dag/types.js +44 -6
  240. package/dist/workflows/dag/validate.js +4 -0
  241. package/docs/README.md +1 -0
  242. package/docs/architecture/runtime-boundaries.md +3 -3
  243. package/docs/governance/README.md +1 -0
  244. package/docs/init-surface.manifest.json +1 -0
  245. package/docs/operations/README.md +2 -0
  246. package/docs/templates/README.md +1 -1
  247. package/docs/templates/agent-dag.schema.json +2 -2
  248. package/docs/templates/backend-test-dag.json +14 -10
  249. package/docs/templates/frontend-implementation-contract.schema.json +0 -7
  250. package/package.json +9 -3
  251. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  252. package/skills/frontend-contract/references/contract-protocol.md +3 -1
  253. package/skills/frontend-plan/SKILL.md +8 -6
  254. package/skills/frontend-plan/references/decision-contract.md +3 -3
  255. package/skills/frontend-review/SKILL.md +18 -28
  256. package/skills/frontend-review/references/review-findings.md +8 -3
  257. package/dist/commands/new-task.js +0 -5
  258. package/dist/commands/status.js +0 -57
  259. package/dist/commands/task-contract.js +0 -269
  260. package/dist/shared/prompts.js +0 -26
  261. package/dist/task/config.js +0 -2
  262. package/dist/task/contract/validate-draft.js +0 -106
  263. package/dist/task/goal.js +0 -3
  264. package/dist/task/index.js +0 -12
  265. package/dist/task/lifecycle.js +0 -1
  266. package/dist/task/operator/capabilities.js +0 -6
  267. package/dist/task/operator/envelope.js +0 -2
  268. package/dist/task/operator/index.js +0 -5
  269. package/dist/task/operator/registry.js +0 -2
  270. package/dist/task/operator/types.js +0 -1
  271. package/dist/task/paths.js +0 -1
  272. package/dist/task/source-state.js +0 -1
  273. package/dist/task/subagent-guidance.js +0 -1
  274. package/dist/worker/console/chat/sift-bridge.js +0 -1
  275. package/dist/worker/console/index.js +0 -20
  276. package/dist/worker/console/static/assets/channel-C0A6UEIa.js +0 -1
  277. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DzguNppy.js +0 -1
  278. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DzguNppy.js +0 -1
  279. package/dist/worker/console/static/assets/index-24jp3aBh.js +0 -468
  280. package/dist/worker/console/static/assets/index-CY6y6I8M.css +0 -1
  281. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BABVQnfM.js +0 -1
  282. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D0qsLaxR.js +0 -8
  283. package/dist/worker/scheduler/index.js +0 -25
  284. package/dist/workflows/dag/facts.js +0 -4
  285. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
  286. package/dist/workflows/dag/index.js +0 -6
  287. package/dist/workflows/dynamic/index.js +0 -7
@@ -0,0 +1,523 @@
1
+ /**
2
+ * Workspace-scoped Playwright browser used by both Operator Chat tools and
3
+ * the Browser panel. The browser profile lives outside the repository and is
4
+ * reused across Console restarts, so login state and downloads remain
5
+ * isolated from the user's normal browser without contaminating the repo.
6
+ */
7
+ import { randomUUID } from "node:crypto";
8
+ import { mkdir } from "node:fs/promises";
9
+ import path from "node:path";
10
+ import { chromium, } from "playwright-core";
11
+ import { normalizeBrowserUrl } from "./browser-policy.js";
12
+ export const BROWSER_VIEWPORT_WIDTH = 1280;
13
+ export const BROWSER_VIEWPORT_HEIGHT = 900;
14
+ const SEARCH_INPUT_SELECTORS = [
15
+ "input[name='wd']",
16
+ "input[name='q']",
17
+ "input[name='query']",
18
+ "input[type='search']",
19
+ "input[type='text']",
20
+ ];
21
+ const SEARCH_SUBMIT_SELECTORS = [
22
+ "button[type='submit']",
23
+ "input[type='submit']",
24
+ "button:not([type])",
25
+ ];
26
+ const DEFAULT_SEARCH_ENGINE_URL = "https://www.baidu.com/";
27
+ const SEARCH_TYPING_DELAY_MS = 45;
28
+ const DEFAULT_ACTION_TIMEOUT_MS = 15_000;
29
+ const MAX_SNAPSHOT_CHARS = 24_000;
30
+ const MAX_SCREENSHOT_BYTES = 4 * 1024 * 1024;
31
+ export class BrowserAutomationService {
32
+ options;
33
+ browserPromise;
34
+ activePage;
35
+ tabs = new Map();
36
+ nextTabId = 1;
37
+ disposed = false;
38
+ dialogQueue = [];
39
+ downloadQueue = [];
40
+ constructor(options) {
41
+ this.options = options;
42
+ }
43
+ async navigate(rawUrl) {
44
+ const normalized = normalizeBrowserUrl(rawUrl);
45
+ if (normalized.kind !== "ok") {
46
+ return {
47
+ ok: false,
48
+ code: normalized.kind === "blocked" ? "BAD_SCHEME" : "INVALID_URL",
49
+ error: normalized.kind === "blocked"
50
+ ? "browser urls must use http(s)"
51
+ : "invalid browser url",
52
+ };
53
+ }
54
+ try {
55
+ const page = await this.page();
56
+ await page.goto(normalized.url, { waitUntil: "domcontentloaded", timeout: 30_000 });
57
+ const snapshot = await this.snapshot({ screenshot: false });
58
+ return { ok: true, page: snapshot };
59
+ }
60
+ catch (error) {
61
+ return { ok: false, code: "NAVIGATION_FAILED", error: errorMessage(error) };
62
+ }
63
+ }
64
+ async reload() {
65
+ const page = await this.page();
66
+ await page.reload({ waitUntil: "domcontentloaded", timeout: 30_000 });
67
+ return this.snapshot({ screenshot: false });
68
+ }
69
+ async goBack() {
70
+ const page = await this.page();
71
+ await page.goBack({ waitUntil: "domcontentloaded", timeout: 30_000 });
72
+ return this.snapshot({ screenshot: false });
73
+ }
74
+ async goForward() {
75
+ const page = await this.page();
76
+ await page.goForward({ waitUntil: "domcontentloaded", timeout: 30_000 });
77
+ return this.snapshot({ screenshot: false });
78
+ }
79
+ async snapshot(options) {
80
+ const page = await this.page();
81
+ const dialog = this.dialogFor(page);
82
+ if (dialog) {
83
+ return {
84
+ url: page.url(),
85
+ title: "浏览器弹窗",
86
+ tabs: await this.tabSummaries(),
87
+ dialog,
88
+ ariaSnapshot: "",
89
+ ...(options?.screenshot === false
90
+ ? {}
91
+ : { screenshot: await this.captureScreenshot(page) }),
92
+ };
93
+ }
94
+ const [title, aria] = await Promise.all([
95
+ page.title(),
96
+ page.ariaSnapshot({ mode: "ai", timeout: 10_000 }),
97
+ ]);
98
+ const download = await this.nextDownloadResult();
99
+ const screenshot = options?.screenshot === false
100
+ ? undefined
101
+ : await this.captureScreenshot(page);
102
+ return {
103
+ url: page.url(),
104
+ title,
105
+ tabs: await this.tabSummaries(),
106
+ dialog,
107
+ ariaSnapshot: truncateText(aria, MAX_SNAPSHOT_CHARS),
108
+ ...(screenshot ? { screenshot } : {}),
109
+ ...(download ? { download } : {}),
110
+ };
111
+ }
112
+ /**
113
+ * Lightweight state for iframe-driven UI. Avoids aria capture and PNG
114
+ * encoding on every panel poll.
115
+ */
116
+ async state() {
117
+ if (!this.browserPromise && !this.disposed)
118
+ return emptyBrowserState();
119
+ const page = await this.page();
120
+ const state = {
121
+ running: true,
122
+ url: page.url(),
123
+ title: await page.title().catch(() => ""),
124
+ tabs: await this.tabSummaries(),
125
+ dialog: this.dialogFor(page),
126
+ ariaSnapshot: "",
127
+ };
128
+ return state;
129
+ }
130
+ async search(rawQuery, rawUrl) {
131
+ const query = rawQuery.trim();
132
+ if (!query)
133
+ throw new BrowserAutomationError("EMPTY_QUERY", "search query is empty");
134
+ const target = rawUrl?.trim() || DEFAULT_SEARCH_ENGINE_URL;
135
+ const normalized = normalizeBrowserUrl(target);
136
+ if (normalized.kind !== "ok") {
137
+ throw new BrowserAutomationError("INVALID_URL", "search engine url must use http(s)");
138
+ }
139
+ const page = await this.page();
140
+ await page.bringToFront();
141
+ await page.goto(normalized.url, { waitUntil: "domcontentloaded", timeout: 30_000 });
142
+ const input = await findSearchInput(page);
143
+ if (!input) {
144
+ throw new BrowserAutomationError("SEARCH_INPUT_NOT_FOUND", "the search page did not expose a recognizable search input");
145
+ }
146
+ await input.fill("", { timeout: DEFAULT_ACTION_TIMEOUT_MS });
147
+ await input.pressSequentially(query, {
148
+ delay: SEARCH_TYPING_DELAY_MS,
149
+ timeout: DEFAULT_ACTION_TIMEOUT_MS,
150
+ });
151
+ await Promise.all([
152
+ page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 30_000 }),
153
+ submitSearchForm(page, input),
154
+ ]);
155
+ return this.snapshot({ screenshot: false });
156
+ }
157
+ async act(request) {
158
+ const page = await this.page();
159
+ const ref = request.ref.trim();
160
+ if (!/^(f\d+)?e\d+$/.test(ref)) {
161
+ throw new BrowserAutomationError("INVALID_REF", "element ref must come from a fresh browser snapshot");
162
+ }
163
+ const locator = page.locator(`aria-ref=${ref}`);
164
+ try {
165
+ if (request.type === "click") {
166
+ await locator.click({ timeout: DEFAULT_ACTION_TIMEOUT_MS });
167
+ }
168
+ else if (request.type === "fill") {
169
+ await locator.fill(request.value, { timeout: DEFAULT_ACTION_TIMEOUT_MS });
170
+ }
171
+ else if (request.type === "press") {
172
+ await locator.press(request.key, { timeout: DEFAULT_ACTION_TIMEOUT_MS });
173
+ }
174
+ else {
175
+ await locator.hover({ timeout: DEFAULT_ACTION_TIMEOUT_MS });
176
+ }
177
+ }
178
+ catch (error) {
179
+ if (error instanceof Error && /ref .* not found/i.test(error.message)) {
180
+ throw new BrowserAutomationError("STALE_REF", "element ref is stale; take a fresh browser snapshot", { cause: error });
181
+ }
182
+ throw new BrowserAutomationError("ACTION_FAILED", errorMessage(error), { cause: error });
183
+ }
184
+ await page.waitForLoadState("domcontentloaded", { timeout: 3_000 }).catch(() => undefined);
185
+ return { action: request.type, ref, page: await this.snapshot({ screenshot: false }) };
186
+ }
187
+ async scroll(input) {
188
+ const page = await this.page();
189
+ const amount = clamp(input.amount ?? 600, 50, 3000);
190
+ await page.mouse.wheel(0, input.direction === "down" ? amount : -amount);
191
+ await page.waitForTimeout(150);
192
+ return this.snapshot({ screenshot: false });
193
+ }
194
+ async pressKey(key) {
195
+ const page = await this.page();
196
+ await page.keyboard.press(key);
197
+ await page.waitForLoadState("domcontentloaded", { timeout: 3_000 }).catch(() => undefined);
198
+ return this.snapshot({ screenshot: false });
199
+ }
200
+ async selectTab(tabId) {
201
+ const page = this.tabs.get(tabId);
202
+ if (!page)
203
+ throw new BrowserAutomationError("TAB_NOT_FOUND", `tab ${tabId} does not exist`);
204
+ this.activePage = page;
205
+ await page.bringToFront();
206
+ return this.snapshot({ screenshot: false });
207
+ }
208
+ async newTab(rawUrl) {
209
+ const context = await this.context();
210
+ const page = await context.newPage();
211
+ this.trackPage(page);
212
+ this.activePage = page;
213
+ if (rawUrl) {
214
+ const result = await this.navigate(rawUrl);
215
+ if (!result.ok)
216
+ throw new BrowserAutomationError(result.code, result.error);
217
+ return result.page;
218
+ }
219
+ return this.snapshot({ screenshot: false });
220
+ }
221
+ async closeTab(tabId) {
222
+ const page = tabId === undefined ? await this.page() : this.tabs.get(tabId);
223
+ if (!page)
224
+ throw new BrowserAutomationError("TAB_NOT_FOUND", `tab ${tabId} does not exist`);
225
+ this.tabs.delete(tabIdForValue(this.tabs, page) ?? 0);
226
+ await page.close();
227
+ if (this.activePage === page)
228
+ this.activePage = undefined;
229
+ return this.snapshot({ screenshot: false });
230
+ }
231
+ async handleDialog(action, promptText) {
232
+ const page = await this.page();
233
+ const index = this.dialogQueue.findIndex((entry) => entry.page === page);
234
+ const entry = index >= 0 ? this.dialogQueue[index] : undefined;
235
+ if (!entry)
236
+ throw new BrowserAutomationError("NO_DIALOG", "the active page has no pending dialog");
237
+ this.dialogQueue.splice(index, 1);
238
+ if (action === "accept")
239
+ await entry.dialog.accept(promptText);
240
+ else
241
+ await entry.dialog.dismiss();
242
+ return this.snapshot({ screenshot: false });
243
+ }
244
+ async dispatchPointer(input) {
245
+ const page = await this.page();
246
+ if (input.type === "mousemove")
247
+ await page.mouse.move(input.x, input.y);
248
+ else if (input.type === "wheel")
249
+ await page.mouse.wheel(input.deltaX, input.deltaY);
250
+ else if (input.type === "mousedown")
251
+ await page.mouse.down({ button: input.button ?? "left" });
252
+ else if (input.type === "mouseup")
253
+ await page.mouse.up({ button: input.button ?? "left" });
254
+ return { ok: true };
255
+ }
256
+ async dispatchKeyboard(input) {
257
+ const page = await this.page();
258
+ if (input.type === "keydown")
259
+ await page.keyboard.down(input.key);
260
+ else
261
+ await page.keyboard.up(input.key);
262
+ return { ok: true };
263
+ }
264
+ async inputText(text) {
265
+ const page = await this.page();
266
+ await page.keyboard.type(text);
267
+ return { ok: true };
268
+ }
269
+ async screenshot() {
270
+ return this.snapshot({ screenshot: true });
271
+ }
272
+ async focus() {
273
+ const page = await this.page();
274
+ await page.bringToFront();
275
+ return { ok: true };
276
+ }
277
+ async waitFor(ms) {
278
+ const page = await this.page();
279
+ await page.waitForTimeout(clamp(ms, 0, 10_000));
280
+ return this.snapshot({ screenshot: false });
281
+ }
282
+ async dispose() {
283
+ if (this.disposed)
284
+ return;
285
+ this.disposed = true;
286
+ const pending = this.browserPromise;
287
+ this.browserPromise = undefined;
288
+ this.tabs.clear();
289
+ this.activePage = undefined;
290
+ if (!pending)
291
+ return;
292
+ const context = await pending.catch(() => undefined);
293
+ await context?.close().catch(() => undefined);
294
+ }
295
+ async page() {
296
+ const context = await this.context();
297
+ if (this.activePage && !this.activePage.isClosed())
298
+ return this.activePage;
299
+ const existing = context.pages().find((page) => this.tabs.has(tabIdForValue(this.tabs, page) ?? -1));
300
+ if (existing) {
301
+ this.activePage = existing;
302
+ return existing;
303
+ }
304
+ const page = await context.newPage();
305
+ this.trackPage(page);
306
+ this.activePage = page;
307
+ return page;
308
+ }
309
+ async context() {
310
+ if (this.disposed)
311
+ throw new BrowserAutomationError("BROWSER_CLOSED", "browser service is disposed");
312
+ this.browserPromise ??= (async () => {
313
+ await Promise.all([
314
+ mkdir(this.options.profileDir, { recursive: true }),
315
+ mkdir(this.options.downloadsDir, { recursive: true }),
316
+ mkdir(this.options.screenshotsDir, { recursive: true }),
317
+ ]);
318
+ const browser = await this.launchBrowser();
319
+ this.wireContext(browser);
320
+ const page = browser.pages()[0] ?? await browser.newPage();
321
+ this.trackPage(page);
322
+ this.activePage = page;
323
+ return browser;
324
+ })();
325
+ try {
326
+ return await this.browserPromise;
327
+ }
328
+ catch (error) {
329
+ this.browserPromise = undefined;
330
+ throw new BrowserAutomationError("BROWSER_START_FAILED", errorMessage(error), {
331
+ cause: error,
332
+ });
333
+ }
334
+ }
335
+ wireContext(context) {
336
+ context.on("page", (page) => {
337
+ this.trackPage(page);
338
+ if (!this.activePage || this.activePage.isClosed())
339
+ this.activePage = page;
340
+ });
341
+ context.on("close", () => {
342
+ if (this.browserPromise) {
343
+ void this.browserPromise.then((current) => {
344
+ if (current === context) {
345
+ this.browserPromise = undefined;
346
+ this.activePage = undefined;
347
+ this.tabs.clear();
348
+ }
349
+ });
350
+ }
351
+ });
352
+ context.on("dialog", (dialog) => {
353
+ const page = dialog.page();
354
+ if (page)
355
+ this.dialogQueue.push({ page, dialog });
356
+ });
357
+ context.on("download", (download) => {
358
+ this.downloadQueue.push({ download, consumed: false });
359
+ });
360
+ }
361
+ async launchBrowser() {
362
+ const candidates = chromiumLaunchCandidates();
363
+ const launchOptions = chromiumLaunchOptions(this.options.headless);
364
+ let lastError;
365
+ for (const candidate of candidates) {
366
+ try {
367
+ return await chromium.launchPersistentContext(this.options.profileDir, { ...launchOptions, ...candidate });
368
+ }
369
+ catch (error) {
370
+ lastError = error;
371
+ }
372
+ }
373
+ if (candidates.length === 0) {
374
+ return await chromium.launchPersistentContext(this.options.profileDir, launchOptions);
375
+ }
376
+ throw lastError instanceof Error
377
+ ? lastError
378
+ : new Error("no Chromium executable was available");
379
+ }
380
+ trackPage(page) {
381
+ for (const [id, existing] of this.tabs) {
382
+ if (existing === page)
383
+ return id;
384
+ }
385
+ const id = this.nextTabId;
386
+ this.nextTabId += 1;
387
+ this.tabs.set(id, page);
388
+ page.on("close", () => {
389
+ this.tabs.delete(id);
390
+ if (this.activePage === page)
391
+ this.activePage = undefined;
392
+ });
393
+ return id;
394
+ }
395
+ dialogFor(page) {
396
+ const index = this.dialogQueue.findIndex((entry) => entry.page === page);
397
+ const dialog = index >= 0 ? this.dialogQueue[index]?.dialog : undefined;
398
+ if (!dialog)
399
+ return null;
400
+ return {
401
+ type: dialog.type(),
402
+ message: dialog.message(),
403
+ ...(dialog.defaultValue() ? { defaultText: dialog.defaultValue() } : {}),
404
+ };
405
+ }
406
+ async tabSummaries() {
407
+ return Promise.all([...this.tabs.entries()].map(async ([id, page]) => ({
408
+ id,
409
+ url: page.url(),
410
+ title: page.isClosed() ? "" : await page.title().catch(() => ""),
411
+ active: !page.isClosed() && page === this.activePage,
412
+ })));
413
+ }
414
+ async captureScreenshot(page) {
415
+ const buffer = await page.screenshot({
416
+ type: "png",
417
+ fullPage: false,
418
+ timeout: 15_000,
419
+ });
420
+ if (buffer.byteLength > MAX_SCREENSHOT_BYTES)
421
+ return undefined;
422
+ return `data:image/png;base64,${buffer.toString("base64")}`;
423
+ }
424
+ async nextDownloadResult() {
425
+ const index = this.downloadQueue.findIndex((entry) => !entry.consumed);
426
+ if (index < 0)
427
+ return undefined;
428
+ const entry = this.downloadQueue[index];
429
+ if (!entry)
430
+ return undefined;
431
+ entry.consumed = true;
432
+ const download = entry.download;
433
+ const target = path.join(this.options.downloadsDir, `${randomUUID()}-${sanitizeFilename(download.suggestedFilename())}`);
434
+ try {
435
+ await download.saveAs(target);
436
+ this.downloadQueue.splice(index, 1);
437
+ return {
438
+ url: download.url(),
439
+ suggestedFilename: download.suggestedFilename(),
440
+ path: target,
441
+ };
442
+ }
443
+ catch (error) {
444
+ this.downloadQueue.splice(index, 1);
445
+ throw error;
446
+ }
447
+ }
448
+ }
449
+ function emptyBrowserState() {
450
+ return {
451
+ running: false,
452
+ url: "about:blank",
453
+ title: "",
454
+ tabs: [],
455
+ dialog: null,
456
+ ariaSnapshot: "",
457
+ };
458
+ }
459
+ function chromiumLaunchOptions(headless) {
460
+ return {
461
+ headless: headless ?? false,
462
+ viewport: undefined,
463
+ acceptDownloads: true,
464
+ locale: "zh-CN",
465
+ timezoneId: "Asia/Shanghai",
466
+ args: ["--start-maximized", "--disable-blink-features=AutomationControlled"],
467
+ ignoreDefaultArgs: ["--enable-automation"],
468
+ };
469
+ }
470
+ async function findSearchInput(page) {
471
+ for (const selector of SEARCH_INPUT_SELECTORS) {
472
+ const locator = page.locator(selector).first();
473
+ if (await locator.isVisible().catch(() => false))
474
+ return locator;
475
+ }
476
+ return undefined;
477
+ }
478
+ async function submitSearchForm(page, input) {
479
+ for (const selector of SEARCH_SUBMIT_SELECTORS) {
480
+ const locator = page.locator(selector).first();
481
+ if (!await locator.isVisible().catch(() => false))
482
+ continue;
483
+ await locator.click({ timeout: DEFAULT_ACTION_TIMEOUT_MS });
484
+ return;
485
+ }
486
+ await input.press("Enter", { timeout: DEFAULT_ACTION_TIMEOUT_MS });
487
+ }
488
+ export class BrowserAutomationError extends Error {
489
+ code;
490
+ constructor(code, message, options) {
491
+ super(message, options);
492
+ this.code = code;
493
+ this.name = "BrowserAutomationError";
494
+ }
495
+ }
496
+ export function chromiumLaunchCandidates(env = process.env, platform = process.platform) {
497
+ const executablePath = env.LOOP_BROWSER_CHROMIUM_PATH?.trim();
498
+ if (executablePath)
499
+ return [{ executablePath }];
500
+ if (platform === "win32")
501
+ return [{ channel: "chrome" }, { channel: "msedge" }];
502
+ if (platform === "darwin")
503
+ return [{ channel: "chrome" }];
504
+ return [];
505
+ }
506
+ function tabIdForValue(map, page) {
507
+ for (const [id, value] of map)
508
+ if (value === page)
509
+ return id;
510
+ return undefined;
511
+ }
512
+ function truncateText(value, max) {
513
+ return value.length <= max ? value : `${value.slice(0, max)}\n... [snapshot truncated]`;
514
+ }
515
+ function clamp(value, min, max) {
516
+ return Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : min;
517
+ }
518
+ function sanitizeFilename(value) {
519
+ return value.replace(/[\\/:*?"<>|]/g, "_").slice(0, 180) || "download";
520
+ }
521
+ function errorMessage(error) {
522
+ return error instanceof Error ? error.message : String(error);
523
+ }
@@ -1,15 +1,11 @@
1
1
  /**
2
2
  * Pure URL policy for the Operator Chat browser panel: normalize user input
3
- * into an http(s) URL and refuse destinations that would be dangerous to
4
- * embed in the panel iframe. Dependency-free so it is unit-testable.
3
+ * into an http(s) URL. Dependency-free so it is unit-testable.
5
4
  *
6
- * The iframe sandbox (opaque origin no allow-same-origin, no top
7
- * navigation) is the primary security boundary; this module is the
8
- * address-bar gate on top of it: only http/https may be navigated, and
9
- * loopback addresses are refused so a browsed page cannot probe local
10
- * services by user action. The Console's OWN origin is explicitly allowed —
11
- * the sandbox still renders it in an opaque origin with no same-origin
12
- * privileges, exactly like any other site.
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.
13
9
  */
14
10
  /**
15
11
  * Extract the `frame-ancestors` source list of a Content-Security-Policy
@@ -47,16 +43,6 @@ export function embeddabilityOf(probe) {
47
43
  }
48
44
  return "embeddable";
49
45
  }
50
- /** A loopback hostname (localhost, IPv6 ::1, 127.0.0.0/8, 0.0.0.0). */
51
- export function isLoopbackHostname(hostname) {
52
- const host = hostname.replace(/^\[|\]$/g, "").toLowerCase();
53
- if (host === "localhost" || host === "::1" || host === "0.0.0.0")
54
- return true;
55
- const parts = host.split(".");
56
- return (parts.length === 4 &&
57
- parts[0] === "127" &&
58
- parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255));
59
- }
60
46
  /**
61
47
  * Schemes that must never reach the iframe even without `//` (javascript:,
62
48
  * data:, file:, ...). Host:port lookalikes (example.com:8080) are NOT here —
@@ -88,12 +74,8 @@ const FORBIDDEN_SCHEMES = new Set([
88
74
  /**
89
75
  * Normalize one address-bar input against the navigation policy.
90
76
  * @param input raw user text.
91
- * @param selfOrigin the Console's own origin (window.location.origin). The
92
- * Console itself may be browsed in the panel (the sandbox keeps it opaque),
93
- * so it is let through BEFORE the loopback check — its host is normally
94
- * loopback.
95
77
  */
96
- export function normalizeBrowserUrl(input, selfOrigin) {
78
+ export function normalizeBrowserUrl(input) {
97
79
  const trimmed = input.trim();
98
80
  if (trimmed === "")
99
81
  return { kind: "invalid" };
@@ -127,17 +109,5 @@ export function normalizeBrowserUrl(input, selfOrigin) {
127
109
  if (url.protocol !== "http:" && url.protocol !== "https:") {
128
110
  return { kind: "blocked", reason: "scheme" };
129
111
  }
130
- // The Console's own origin is allowed (browsed in an opaque origin like
131
- // any other site); checked before the loopback gate because its host is
132
- // normally loopback.
133
- try {
134
- if (url.origin === new URL(selfOrigin).origin)
135
- return { kind: "ok", url: url.href };
136
- }
137
- catch {
138
- // Unparsable selfOrigin (never in practice): fall through to the gate.
139
- }
140
- if (isLoopbackHostname(url.hostname))
141
- return { kind: "blocked", reason: "loopback" };
142
112
  return { kind: "ok", url: url.href };
143
113
  }