@tea-agent/loop-agent 0.43.0-next.6 → 0.43.0-next.7

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 (79) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/dist/build-stamp.json +3 -3
  3. package/dist/worker/console/chat/browser-policy.js +143 -0
  4. package/dist/worker/console/chat/browser-routes.js +148 -0
  5. package/dist/worker/console/chat/routes.js +5 -0
  6. package/dist/worker/console/chat/scm-routes.js +25 -1
  7. package/dist/worker/console/chat/scm-service.js +30 -0
  8. package/dist/worker/console/chat/session-catalog.js +31 -0
  9. package/dist/worker/console/chat/session-mode-view.js +3 -3
  10. package/dist/worker/console/server.js +2 -28
  11. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BuyrlONK.js → abnfDiagram-N423BO3Z-0z1FF_Mr.js} +1 -1
  12. package/dist/worker/console/static/assets/{arc-BvCl_wpi.js → arc-5Rtc6XV_.js} +1 -1
  13. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BkBpBaRq.js → architectureDiagram-T3A2C74G-BkywIxBo.js} +1 -1
  14. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-yve4Mcb_.js → blockDiagram-VBNYF7ZC-g1wALcrC.js} +1 -1
  15. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-9yLGboBx.js → c4Diagram-5PPSVZJV-CtFfJvWJ.js} +1 -1
  16. package/dist/worker/console/static/assets/channel-Ln3PalCI.js +1 -0
  17. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BKP69b8r.js → chunk-2GRJ4B5K-D5PQz8yM.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-NWFRnXju.js → chunk-2Q5K7J3B-DgV_JJPi.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BiweqLEc.js → chunk-5RXB4S5H-xwfF7A20.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CP9GHmRY.js → chunk-5VM5RSS4-CGqPeDdw.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BiGDkBtj.js → chunk-6Q2QTUOP-0wVqzhBr.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DkJ4ZjQS.js → chunk-GF5L2VYU-CMbMHcFc.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B5jjn7zq.js → chunk-JWPE2WC7-BHizoNTm.js} +1 -1
  24. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-C6KoK9O0.js → chunk-KBJHAD2P-PS3GrTTl.js} +1 -1
  25. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-BvNIfyn-.js → chunk-RYQCIY6F-igvsougt.js} +1 -1
  26. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BZ33iX4W.js → chunk-XXDRQBXY-DtyvbRKY.js} +1 -1
  27. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BwEIBfqD.js +1 -0
  28. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BwEIBfqD.js +1 -0
  29. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CP8eIuZd.js → cose-bilkent-JH36ORCC-Y3cTrz6x.js} +1 -1
  30. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DZECjpOe.js → cynefin-VYW2F7L2-CNz317P7.js} +1 -1
  31. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-CvVJoFqf.js → cynefinDiagram-MW4NZA55-CqJD0Eda.js} +1 -1
  32. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-D_JTqdAn.js → dagre-VZM6K2ZE-DB9xkqs_.js} +1 -1
  33. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Db7AQ0z0.js → diagram-7IWD3JNH-BvMd4WO1.js} +1 -1
  34. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-AZPVo5CZ.js → diagram-B4RE2ZJO-kzIbfP-r.js} +1 -1
  35. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BgXqI3E6.js → diagram-LBJQPF4R-D7DuDLA1.js} +1 -1
  36. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BYjFn3Ba.js → diagram-Q27KOJAE-DWYiiEqR.js} +1 -1
  37. package/dist/worker/console/static/assets/{diagram-UB23O5K3-B4rJgGGO.js → diagram-UB23O5K3-BRnBFo_0.js} +1 -1
  38. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-C6XWZhpw.js → ebnfDiagram-BXEA7PRR-DeTE8_JP.js} +1 -1
  39. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-1YdQRIQW.js → erDiagram-JOGREHBK-DJIc6Ji-.js} +1 -1
  40. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-l3a0AwN0.js → flowDiagram-UKHOOZJN-DHYWItNm.js} +1 -1
  41. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CQL96F2Z.js → ganttDiagram-PKOTCBZU-BtwhbiNv.js} +1 -1
  42. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BxCvafRb.js → gitGraphDiagram-DS77QQ5N-R21xagUM.js} +1 -1
  43. package/dist/worker/console/static/assets/index-Bf7dxfm-.css +1 -0
  44. package/dist/worker/console/static/assets/index-DHrt_6jk.js +468 -0
  45. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BUipIeDO.js → infoDiagram-6WML65LV-xn9vhN0g.js} +1 -1
  46. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CRfomVMX.js → ishikawaDiagram-WSZJBQD7-B1MMrk5S.js} +1 -1
  47. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-_UFvyviX.js → journeyDiagram-NVQOT4AX-Bt_eha4U.js} +1 -1
  48. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CKIRoqKv.js → kanban-definition-27J2QSJJ-CPIEKJzB.js} +1 -1
  49. package/dist/worker/console/static/assets/{linear-CjttJDCM.js → linear-CT6doEH4.js} +1 -1
  50. package/dist/worker/console/static/assets/{mermaid.core-DPvHX155.js → mermaid.core-_8ilrIeK.js} +5 -5
  51. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-Qmj-qwBe.js → mindmap-definition-FAOFIHXS-xzLuFnQq.js} +1 -1
  52. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Auw6DRgq.js → pegDiagram-VL7TDLO6-BUK6JRUg.js} +1 -1
  53. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CqMrfD4c.js → pieDiagram-7S7Q4E2Y-ChFOxDhD.js} +1 -1
  54. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BAddeggH.js → quadrantDiagram-CIZ2JOQS-BeoCJ_wm.js} +1 -1
  55. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DG1JuOu2.js → railroadDiagram-AXF67PYL-CaSV6rVf.js} +1 -1
  56. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DLYGMKlC.js → requirementDiagram-LRYGKXZP-CogY5162.js} +1 -1
  57. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-g68SbLM9.js → sankeyDiagram-W5VNT64P-CmTAqVmS.js} +1 -1
  58. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CmszLKv8.js → sequenceDiagram-SI44F4Z6-DtmgJPSM.js} +1 -1
  59. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-D2tEKUCj.js → sizeCapture-X5ZJPWSS-DY-e-ucu.js} +1 -1
  60. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Syc5E_Jf.js → stateDiagram-OKZ733FA-C0GKqasS.js} +1 -1
  61. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BmOaJuhY.js +1 -0
  62. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Ibn0IRCa.js → swimlanes-SLNWSIFB-CHwJlgUA.js} +2 -2
  63. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DOCjPMNz.js +8 -0
  64. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-2ZW9W3iS.js → timeline-definition-Z64GVDOM-leVMWmSe.js} +1 -1
  65. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-v50HsKUh.js → vennDiagram-T6HMQDX7-Dh-LA6pq.js} +1 -1
  66. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DqCXEoDH.js → wardleyDiagram-T6FBY63Y-jBwm-krQ.js} +1 -1
  67. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-vIP8Vo9d.js → xychartDiagram-ELKLHX3M-CFkAWroq.js} +1 -1
  68. package/dist/worker/console/static/index.html +2 -2
  69. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +12 -15
  70. package/dist/workflows/dag/frontend-implementation-contract.js +4 -0
  71. package/dist/workflows/dag/frontend-review-scopes.js +32 -10
  72. package/package.json +1 -1
  73. package/dist/worker/console/static/assets/channel-BRahIhjw.js +0 -1
  74. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-GOC80mhq.js +0 -1
  75. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-GOC80mhq.js +0 -1
  76. package/dist/worker/console/static/assets/index-4wCGsXuU.js +0 -465
  77. package/dist/worker/console/static/assets/index-CiF9WBoR.css +0 -1
  78. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CwWwuPI-.js +0 -1
  79. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-A7fqemr6.js +0 -8
package/CHANGELOG.md CHANGED
@@ -4,14 +4,18 @@
4
4
 
5
5
  ### 新增
6
6
 
7
+ - Operator Chat 浏览器面板(DSH 风格):Console 右侧辅助栏新增「浏览器」tab——地址栏(自动补 https、仅 http/https、禁止 javascript/data/file 等危险协议与本机回环地址)、后退/前进/刷新导航栈、系统浏览器跳转;页面运行在无 same-origin、无 top-navigation 的沙箱 iframe 中(访问的页面拿不到 Console 的存储与 API),沙箱状态条支持单页面临时解锁(红色警示 + 一键恢复);对拒绝被嵌入的站点(X-Frame-Options / frame-ancestors)给出明确解释面板与「在系统浏览器打开 / 仍要加载」出口,探测端点仅返回响应头、仅限 http(s) 非回环、8 秒硬超时。
7
8
  - Operator Chat 源代码管理(DSH 风格):模型获得 `scm` 工具(status/diff/stage/unstage/discard/commit/log/branch/push/pull,固定参数 git 无 shell、限时限量输出);Console 右侧辅助栏新增「源码」面板(DSH GitView 布局——分支下拉切换、已暂存/更改分组与全部暂存/取消暂存、单行提交框 Ctrl+Enter、内联分页历史带 refs 徽标与相对时间、点击文件看 diff、右键菜单放弃更改/复制路径);模型与面板共用同一工作区级 ScmService。
8
9
  - Operator Chat 在具备 Aimax 凭据资格时,向对话进程注入 `$AIMAX_AUTH_TOKEN`(明文或 `sm4:v1:` 解密后的 ticket),供 skills 读取;文件缺失、无法解析或解密失败时不注入该变量。
9
10
  - Operator Chat 终端管理(DSH 风格):模型获得 `terminal` 持久 PTY 工具(跨平台 shell 目录——Windows 提供 powershell/bash,macOS/Linux 提供 bash/zsh,pwsh 7 装了即用;cwd/环境变量/后台任务跨调用保留,完成标记协议 + 超时与截断保护,枚举与默认值按平台动态生成);Console 右侧辅助栏新增「终端」面板(xterm.js 渲染,实时查看/接管模型的 shell,按平台动态展示可创建的 shell,支持发送输入、Ctrl+C、终止并关闭);模型 shell 与用户面板共用同一会话注册表并开在工作区仓库根目录,命名区分来源。
10
11
 
11
12
  ### 改进
12
13
 
13
- - 修复模型续跑取消后带部分文字仍被标记成功的问题;前端 Plan 的取消、认证、限流及策略失败优先于容量恢复,精确格式错误保留原修复路径。完整性检查按需求建立单次索引,减少大计划重复扫描,同时拆分纯检查与恢复策略模块。
14
+ - 修复前端契约可选字段未填写时,评审分片索引引用不存在字段、导致设计评审启动前失败的问题。分片生成跳过 undefined;读取端明确拒绝缺失字段、无效引用、重复或非法范围及未索引内容,保留合法空数组和 JSON 空值。
14
15
 
16
+ - Operator Chat 侧栏「添加工作区」图标改为与 DSH 一致的文件夹加号,不再用纯加号。
17
+ - Operator Chat 侧栏改为单一「新会话」;空白会话在输入框上方用芯片选择工作区和模式(工作流 / Pi),有历史后芯片收起。不再用落地页双卡或「新 Pi」按钮;换模式会复用或新建对应空白会话,不会改已有会话的模式。
18
+ - 修复模型续跑取消后带部分文字仍被标记成功的问题;前端 Plan 的取消、认证、限流及策略失败优先于容量恢复,精确格式错误保留原修复路径。完整性检查按需求建立单次索引,减少大计划重复扫描,同时拆分纯检查与恢复策略模块。
15
19
  - 优化 Operator Chat 长会话的流式渲染:完成回合、消息行、工具卡和用户输入锚点建立稳定的 memo 边界,流式文本更新不再重复渲染整段历史或重装锚点测量监听;缓存会在历史消息或工具实际更新时正确失效;无新增折叠回合时不再提交空状态及重复写入本地存储,工具参数与结果格式化按真实输入变化缓存。
16
20
  - 对话页「过程折叠」对齐 DSH 细节:折叠后的内容仍保留在页面中,浏览器 Ctrl+F 可直接命中并自动展开;折叠行展开时不再多出底部空隙;箭头改为 16px、颜色更淡的固定图标(靠旋转而非换图标),并补齐系统「减少动态效果」偏好;汇总文案新增「N 个 subagent」,内置 Explore 子代理不再计入普通工具调用。
17
21
  - 恢复前端 Plan 实时事实分页读取、逐需求 UX 遗漏检查与已完成范围复用;超限时补齐剩余验证目标,并接通 DAG 到 SDK 的原会话续跑。局部状态修改保留其他需求的关联,沿用耐久账本、显式替换和冻结分片边界。
@@ -33,6 +37,7 @@
33
37
 
34
38
  ### 修复
35
39
 
40
+ - 修复点「新 Pi」后一发消息侧栏就变成「工作流」:全局会话列表和点进会话时漏了 `sessionMode`,缺省被当成工作流。
36
41
  - 修复对话页存在运行活动时,收起状态的右侧辅助栏展开按钮覆盖「运行活动 N」及数字徽标的问题;中大尺寸布局现在保留 8px 操作间距,窄屏布局不变。
37
42
  - 修复 Operator Chat 消息流的六处实际使用问题:
38
43
  - **回复被吞**:断线/重连(含每 25 秒的 SSE 事件窗口轮转)后,保留事件里已经生成的正文会在事件流恢复时重新应用到对应回合,不再被静默丢弃;仅当该回合已有更新内容时才跳过,避免重复气泡。
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.43.0-next.6",
4
- "gitSha": "086f94db72547618b8dceb905a3972164acf56dd",
5
- "builtAt": "2026-09-09T06:46:29.301Z"
3
+ "version": "0.43.0-next.7",
4
+ "gitSha": "55437ad2cc676289ad970390a991cf28f47584d2",
5
+ "builtAt": "2026-09-09T08:12:25.724Z"
6
6
  }
@@ -0,0 +1,143 @@
1
+ /**
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.
5
+ *
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.
13
+ */
14
+ /**
15
+ * Extract the `frame-ancestors` source list of a Content-Security-Policy
16
+ * header, or undefined when the directive is absent (or empty).
17
+ */
18
+ export function extractFrameAncestors(csp) {
19
+ if (csp === null)
20
+ return undefined;
21
+ for (const directive of csp.split(";")) {
22
+ const parts = directive.trim().split(/\s+/);
23
+ if (parts[0] === "frame-ancestors") {
24
+ const sources = parts.slice(1).filter((source) => source !== "");
25
+ return sources.length === 0 ? undefined : sources;
26
+ }
27
+ }
28
+ return undefined;
29
+ }
30
+ /**
31
+ * Decide whether a site can render inside the panel iframe. The signals are
32
+ * exactly the ones the browser enforces when it refuses an iframe load:
33
+ * X-Frame-Options DENY/SAMEORIGIN, or a frame-ancestors directive that does
34
+ * not allow `*` ('self' means the SITE's own origin — never ours, so it also
35
+ * blocks the panel). A site we could not reach yields 'unknown' and the
36
+ * plain iframe stays.
37
+ */
38
+ export function embeddabilityOf(probe) {
39
+ if (probe.reachable !== true)
40
+ return "unknown";
41
+ const xfo = probe.xFrameOptions?.trim().toUpperCase();
42
+ if (xfo === "DENY" || xfo === "SAMEORIGIN")
43
+ return "blocked";
44
+ if (probe.frameAncestors !== undefined &&
45
+ !probe.frameAncestors.some((source) => source === "*")) {
46
+ return "blocked";
47
+ }
48
+ return "embeddable";
49
+ }
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
+ /**
61
+ * Schemes that must never reach the iframe even without `//` (javascript:,
62
+ * data:, file:, ...). Host:port lookalikes (example.com:8080) are NOT here —
63
+ * they parse as hosts below.
64
+ */
65
+ const FORBIDDEN_SCHEMES = new Set([
66
+ "javascript",
67
+ "data",
68
+ "file",
69
+ "about",
70
+ "vbscript",
71
+ "blob",
72
+ "mailto",
73
+ "tel",
74
+ "ftp",
75
+ "ftps",
76
+ "ws",
77
+ "wss",
78
+ "sftp",
79
+ "ssh",
80
+ "chrome",
81
+ "chrome-extension",
82
+ "moz-extension",
83
+ "edge",
84
+ "opera",
85
+ "resource",
86
+ "view-source",
87
+ ]);
88
+ /**
89
+ * Normalize one address-bar input against the navigation policy.
90
+ * @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
+ */
96
+ export function normalizeBrowserUrl(input, selfOrigin) {
97
+ const trimmed = input.trim();
98
+ if (trimmed === "")
99
+ return { kind: "invalid" };
100
+ // Distinguish an explicit scheme from a bare host:port. "example.com:8080"
101
+ // would match a naive scheme regex (dots are legal in schemes), so a
102
+ // scheme prefix is only honored when it is http(s) or a known-forbidden
103
+ // scheme; anything else is treated as a host and gets https://.
104
+ const schemeMatch = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed);
105
+ let withScheme;
106
+ if (schemeMatch === null) {
107
+ withScheme = `https://${trimmed}`;
108
+ }
109
+ else {
110
+ const scheme = schemeMatch[1]?.toLowerCase() ?? "";
111
+ if (scheme === "http" || scheme === "https")
112
+ withScheme = trimmed;
113
+ else if (FORBIDDEN_SCHEMES.has(scheme))
114
+ return { kind: "blocked", reason: "scheme" };
115
+ else
116
+ withScheme = `https://${trimmed}`;
117
+ }
118
+ let url;
119
+ try {
120
+ url = new URL(withScheme);
121
+ }
122
+ catch {
123
+ return { kind: "invalid" };
124
+ }
125
+ // Protocol backstop: any URL that still parses to a non-http(s) scheme
126
+ // (ftp://, ws:// — which carry `//` and skip the list) is refused here.
127
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
128
+ return { kind: "blocked", reason: "scheme" };
129
+ }
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
+ return { kind: "ok", url: url.href };
143
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * HTTP routes for the Operator Chat browser panel: one read-only probe
3
+ * endpoint the panel uses to explain an iframe refusal (X-Frame-Options /
4
+ * CSP frame-ancestors are exactly the signals the browser enforces when it
5
+ * refuses to embed a site). The probe is display-only (headers back to the
6
+ * caller), restricted to http(s) non-loopback URLs, with a hard timeout.
7
+ */
8
+ import { embeddabilityOf, extractFrameAncestors, isLoopbackHostname, } from "./browser-policy.js";
9
+ function workspaceIdOf(req) {
10
+ try {
11
+ return new URL(req.url ?? "/", "http://localhost").searchParams.get("workspaceId") ?? undefined;
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ }
17
+ function sendJson(res, status, body) {
18
+ res.writeHead(status, {
19
+ "content-type": "application/json; charset=utf-8",
20
+ "cache-control": "no-store",
21
+ });
22
+ res.end(JSON.stringify(body));
23
+ }
24
+ /** Route dispatcher; returns true when the request was handled. */
25
+ export async function handleBrowserRequest(req, res, deps, pathname) {
26
+ if (!pathname.startsWith("/api/operator/v1/chat/browser"))
27
+ return false;
28
+ const method = (req.method ?? "GET").toUpperCase();
29
+ const rest = pathname.slice("/api/operator/v1/chat/browser".length);
30
+ const parts = rest.split("/").filter(Boolean);
31
+ const sub = parts[0] ?? "";
32
+ try {
33
+ if (method === "GET" && sub === "probe" && parts.length === 1) {
34
+ // Fail-closed: the probe needs a workspaceId like every chat API.
35
+ if (deps.requireWorkspace) {
36
+ const ws = deps.requireWorkspace(req);
37
+ if (!ws.ok) {
38
+ sendJson(res, 400, {
39
+ ok: false,
40
+ error: { code: "WORKSPACE_REQUIRED", message: "workspaceId is required" },
41
+ });
42
+ return true;
43
+ }
44
+ }
45
+ else if (!workspaceIdOf(req)) {
46
+ sendJson(res, 400, {
47
+ ok: false,
48
+ error: { code: "WORKSPACE_REQUIRED", message: "workspaceId is required" },
49
+ });
50
+ return true;
51
+ }
52
+ const url = new URL(req.url ?? "/", "http://localhost");
53
+ const raw = url.searchParams.get("url") ?? "";
54
+ let parsed;
55
+ try {
56
+ parsed = new URL(raw);
57
+ }
58
+ catch {
59
+ sendJson(res, 400, {
60
+ ok: false,
61
+ error: { code: "INVALID_URL", message: "invalid url" },
62
+ });
63
+ return true;
64
+ }
65
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
66
+ sendJson(res, 400, {
67
+ ok: false,
68
+ error: { code: "BAD_SCHEME", message: "only http/https urls can be probed" },
69
+ });
70
+ return true;
71
+ }
72
+ // Mirror the address-bar policy: loopback stays unreachable from
73
+ // the panel, so probing it would leak nothing the panel could use.
74
+ if (isLoopbackHostname(parsed.hostname)) {
75
+ sendJson(res, 400, {
76
+ ok: false,
77
+ error: { code: "LOOPBACK_REFUSED", message: "local addresses are not probed" },
78
+ });
79
+ return true;
80
+ }
81
+ const controller = new AbortController();
82
+ const timer = setTimeout(() => controller.abort(), 8000);
83
+ try {
84
+ let response = await fetch(parsed, {
85
+ method: "HEAD",
86
+ redirect: "follow",
87
+ signal: controller.signal,
88
+ });
89
+ // Some servers answer HEAD with 405/501; retry once as GET
90
+ // (the body is discarded — only the headers matter).
91
+ if (response.status === 405 || response.status === 501) {
92
+ response = await fetch(parsed, {
93
+ method: "GET",
94
+ redirect: "follow",
95
+ signal: controller.signal,
96
+ });
97
+ }
98
+ const csp = response.headers.get("content-security-policy");
99
+ const frameAncestors = extractFrameAncestors(csp);
100
+ const xFrameOptions = response.headers.get("x-frame-options");
101
+ const probe = {
102
+ reachable: true,
103
+ url: response.url,
104
+ status: response.status,
105
+ ...(xFrameOptions !== null ? { xFrameOptions } : {}),
106
+ ...(frameAncestors !== undefined ? { frameAncestors } : {}),
107
+ };
108
+ sendJson(res, 200, {
109
+ ok: true,
110
+ data: { probe, verdict: embeddabilityOf(probe) },
111
+ });
112
+ return true;
113
+ }
114
+ catch {
115
+ // DNS / TLS / connection / timeout: nothing to judge — the
116
+ // client keeps the plain iframe.
117
+ sendJson(res, 200, {
118
+ ok: true,
119
+ data: { probe: { reachable: false }, verdict: "unknown" },
120
+ });
121
+ return true;
122
+ }
123
+ finally {
124
+ clearTimeout(timer);
125
+ }
126
+ }
127
+ sendJson(res, 404, {
128
+ ok: false,
129
+ error: { code: "NOT_FOUND", message: `unknown browser route: ${method} ${pathname}` },
130
+ });
131
+ return true;
132
+ }
133
+ catch (error) {
134
+ if (!res.headersSent) {
135
+ sendJson(res, 500, {
136
+ ok: false,
137
+ error: {
138
+ code: "BROWSER_INTERNAL",
139
+ message: error instanceof Error ? error.message : String(error),
140
+ },
141
+ });
142
+ }
143
+ else {
144
+ res.end();
145
+ }
146
+ return true;
147
+ }
148
+ }
@@ -48,6 +48,7 @@ import { SddDataAlignmentError, } from "./sdd-data-alignment.js";
48
48
  import { TerminalSessionRegistry } from "./terminal-sessions.js";
49
49
  import { handleTerminalRequest } from "./terminal-routes.js";
50
50
  import { handleScmRequest } from "./scm-routes.js";
51
+ import { handleBrowserRequest } from "./browser-routes.js";
51
52
  function projectBrowserChatSession(record) {
52
53
  const { telemetrySessionId, ...projected } = record;
53
54
  void telemetrySessionId;
@@ -4267,6 +4268,10 @@ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
4267
4268
  return true;
4268
4269
  }
4269
4270
  }
4271
+ // Browser probe API (read-only; explains iframe refusals to the panel).
4272
+ if (await handleBrowserRequest(req, res, {}, pathname)) {
4273
+ return true;
4274
+ }
4270
4275
  if (!pathname.startsWith("/api/operator/v1/chat/") &&
4271
4276
  !pathname.startsWith("/api/operator/v1/pi/"))
4272
4277
  return false;
@@ -25,7 +25,7 @@ function sendJson(res, status, body) {
25
25
  res.end(JSON.stringify(body));
26
26
  }
27
27
  function mapScmError(res, error) {
28
- const status = error.code === "NO_PATHS" || error.code === "EMPTY_MESSAGE" || error.code === "INVALID_NAME"
28
+ const status = error.code === "NO_PATHS" || error.code === "EMPTY_MESSAGE" || error.code === "INVALID_NAME" || error.code === "INVALID_HASH"
29
29
  ? 400
30
30
  : error.code === "GIT_TIMEOUT"
31
31
  ? 504
@@ -89,6 +89,30 @@ export async function handleScmRequest(req, res, deps, pathname) {
89
89
  sendJson(res, 200, { ok: true, data: r });
90
90
  return true;
91
91
  }
92
+ if (method === "GET" && sub === "commit-diff" && parts.length === 1) {
93
+ const hash = url.searchParams.get("hash") ?? "";
94
+ if (!hash) {
95
+ sendJson(res, 400, { ok: false, error: { code: "INVALID_INPUT", message: "hash is required" } });
96
+ return true;
97
+ }
98
+ const r = await deps.scm.commitDiff(hash);
99
+ if (!r.ok)
100
+ return mapScmError(res, r), true;
101
+ sendJson(res, 200, { ok: true, data: r });
102
+ return true;
103
+ }
104
+ if (method === "GET" && sub === "untracked-diff" && parts.length === 1) {
105
+ const path = url.searchParams.get("path") ?? "";
106
+ if (!path) {
107
+ sendJson(res, 400, { ok: false, error: { code: "NO_PATHS", message: "path is required" } });
108
+ return true;
109
+ }
110
+ const r = await deps.scm.untrackedDiff(path);
111
+ if (!r.ok)
112
+ return mapScmError(res, r), true;
113
+ sendJson(res, 200, { ok: true, data: r });
114
+ return true;
115
+ }
92
116
  if (method === "GET" && sub === "log" && parts.length === 1) {
93
117
  const limit = Number(url.searchParams.get("limit") ?? 30);
94
118
  const r = await deps.scm.log(Number.isFinite(limit) ? Math.min(Math.max(limit, 1), 200) : 30);
@@ -53,6 +53,9 @@ export class ScmService {
53
53
  ? `git ${args[0]} timed out`
54
54
  : err.message ?? String(e),
55
55
  stderr: typeof err.stderr === "string" ? err.stderr.slice(0, 2000) : undefined,
56
+ // `git diff --no-index` exits 1 when a diff exists; callers that
57
+ // expect that carry the partial stdout through.
58
+ stdout: typeof err.stdout === "string" ? err.stdout : undefined,
56
59
  };
57
60
  }
58
61
  }
@@ -149,6 +152,33 @@ export class ScmService {
149
152
  return r;
150
153
  return { ok: true, diff: r.stdout };
151
154
  }
155
+ /** Full patch of one commit (`git show` without the commit header). */
156
+ async commitDiff(hash) {
157
+ if (!/^[0-9a-f]{4,40}$/i.test(hash)) {
158
+ return { ok: false, code: "INVALID_HASH", message: "invalid commit hash" };
159
+ }
160
+ const r = await this.git(["show", "--format=", "--no-color", hash]);
161
+ if (!r.ok)
162
+ return r;
163
+ return { ok: true, diff: r.stdout };
164
+ }
165
+ /**
166
+ * Untracked-file content as a full-addition diff (`git diff --no-index
167
+ * /dev/null <path>`): git diff never lists untracked files, and this
168
+ * renders them like any other change in the diff view.
169
+ */
170
+ async untrackedDiff(path) {
171
+ if (path.includes("\0") || path.includes("..")) {
172
+ return { ok: false, code: "NO_PATHS", message: "invalid path" };
173
+ }
174
+ const r = await this.git(["diff", "--no-index", "--no-color", "-U3", "--", "/dev/null", path]);
175
+ // `git diff --no-index` exits 1 when there IS a diff — carry stdout.
176
+ if (!r.ok && r.code === "GIT_FAILED")
177
+ return { ok: true, diff: r.stdout ?? "" };
178
+ if (!r.ok)
179
+ return r;
180
+ return { ok: true, diff: r.stdout };
181
+ }
152
182
  /** Stage paths (`git add --` / `git add -A --` for "."). */
153
183
  async stage(paths, all = false) {
154
184
  if (!all && paths.length === 0) {
@@ -1,5 +1,36 @@
1
1
  import { isBlankChatSession } from "./session-store.js";
2
2
  import { projectDeferredTurnSummaries, projectDeferredTurnSummary, } from "./deferred-turn.js";
3
+ /** Sidebar / global GET /chat/sessions projection. Must include sessionMode. */
4
+ export function projectChatSessionCatalogListItem(entry, active) {
5
+ return {
6
+ sessionId: entry.sessionId,
7
+ ...(entry.workspaceId ? { workspaceId: entry.workspaceId } : {}),
8
+ state: entry.state,
9
+ createdAt: entry.createdAt,
10
+ updatedAt: entry.updatedAt,
11
+ ...(entry.title !== undefined ? { title: entry.title } : {}),
12
+ ...(entry.displayTitle !== undefined
13
+ ? { displayTitle: entry.displayTitle }
14
+ : {}),
15
+ ...(entry.modelProvider !== undefined
16
+ ? { modelProvider: entry.modelProvider }
17
+ : {}),
18
+ ...(entry.modelId !== undefined ? { modelId: entry.modelId } : {}),
19
+ ...(entry.thinkingLevel !== undefined
20
+ ? { thinkingLevel: entry.thinkingLevel }
21
+ : {}),
22
+ sessionMode: entry.sessionMode,
23
+ activeTurnId: active?.turnId,
24
+ turnPhase: active?.abortRequestedAt
25
+ ? "stopping"
26
+ : active
27
+ ? "running"
28
+ : "idle",
29
+ deferredTurn: entry.deferredTurn,
30
+ deferredTurns: entry.deferredTurns,
31
+ isBlank: entry.isBlank && !active,
32
+ };
33
+ }
3
34
  /**
4
35
  * Process-wide Session baseline, mirroring DSH's independent session.list.
5
36
  * Full records stay in their fingerprint-isolated stores; this catalog only
@@ -16,9 +16,9 @@ export const SESSION_MODE_COPY = {
16
16
  bannerTitle: "工作流",
17
17
  bannerBody: "用自然语言编排 loop-agent / agent-worker 的全部能力。这是 Operator 面:默认具备完整 Pi 工具与受策略约束的 operator action。软约束:大改与高风险变更优先走受治 DAG。",
18
18
  landingTitle: "开始一段新的对话",
19
- landingBody: "用自然语言编排 loop-agent:查询状态、诊断问题、创建与恢复任务。可先点选下方建议,或选择工作流 / Pi 模式新建会话。",
19
+ landingBody: "用自然语言编排 loop-agent:查询状态、诊断问题、创建与恢复任务。可先点选下方建议,或在输入框描述目标。工作流或 Pi 在输入框上方选择。",
20
20
  emptyTitle: "今天想推进什么?",
21
- emptyBody: "会话已就绪。点选建议快速开场,或在下方输入框直接描述目标。业务写入仍经受治理 DAG。",
21
+ emptyBody: "会话已就绪。输入框上方可切换工作区或工作流 / Pi。点选建议快速开场,或直接描述目标。业务写入仍经受治理 DAG。",
22
22
  },
23
23
  pi: {
24
24
  id: "pi",
@@ -31,7 +31,7 @@ export const SESSION_MODE_COPY = {
31
31
  landingTitle: "开始一段 Pi 协作",
32
32
  landingBody: "Pi 模式保留压缩、Explore、认证与 SDD 接入,取消工作流业务注入。适合有人监督的直接编码与调试。",
33
33
  emptyTitle: "直接协作编码",
34
- emptyBody: "会话已就绪。使用 Pi 工具读写仓库;Explore 负责只读深挖。本会话不加载任务 / DAG 工作流工具。",
34
+ emptyBody: "会话已就绪。输入框上方可切换工作区或模式。使用 Pi 工具读写仓库;Explore 负责只读深挖。本会话不加载任务 / DAG 工作流工具。",
35
35
  },
36
36
  };
37
37
  /** Slash actions that only exist in workflow sessions. Browser-safe: pure
@@ -9,7 +9,7 @@ import { DAG_ABSOLUTE_MAX_WALL_CLOCK_MS } from "../../shared/timeout-policy.js";
9
9
  import { openConsoleAppData } from "../../infrastructure/console/app-data.js";
10
10
  import { isLoopbackHost } from "./loopback.js";
11
11
  import { ChatSessionStore } from "./chat/session-store.js";
12
- import { ChatSessionCatalog } from "./chat/session-catalog.js";
12
+ import { ChatSessionCatalog, projectChatSessionCatalogListItem, } from "./chat/session-catalog.js";
13
13
  import { matchRepoHtmlPreviewRoute } from "./chat/repo-browser.js";
14
14
  import { handleConsoleHealthRequest, handleWorkspaceRoutes, sendJson, serveConsoleStatic, } from "./routes.js";
15
15
  import { issueBootCapabilityToken, injectConfirmationTokenScript, } from "./security.js";
@@ -430,33 +430,7 @@ export async function createConsoleServer(options) {
430
430
  .get(entry.workspaceId)
431
431
  ?.chatRouteDeps.events.getActiveTurn(entry.sessionId)
432
432
  : undefined;
433
- return {
434
- sessionId: entry.sessionId,
435
- ...(entry.workspaceId ? { workspaceId: entry.workspaceId } : {}),
436
- state: entry.state,
437
- createdAt: entry.createdAt,
438
- updatedAt: entry.updatedAt,
439
- ...(entry.title !== undefined ? { title: entry.title } : {}),
440
- ...(entry.displayTitle !== undefined
441
- ? { displayTitle: entry.displayTitle }
442
- : {}),
443
- ...(entry.modelProvider !== undefined
444
- ? { modelProvider: entry.modelProvider }
445
- : {}),
446
- ...(entry.modelId !== undefined ? { modelId: entry.modelId } : {}),
447
- ...(entry.thinkingLevel !== undefined
448
- ? { thinkingLevel: entry.thinkingLevel }
449
- : {}),
450
- activeTurnId: active?.turnId,
451
- turnPhase: active?.abortRequestedAt
452
- ? "stopping"
453
- : active
454
- ? "running"
455
- : "idle",
456
- deferredTurn: entry.deferredTurn,
457
- deferredTurns: entry.deferredTurns,
458
- isBlank: entry.isBlank && !active,
459
- };
433
+ return projectChatSessionCatalogListItem(entry, active);
460
434
  });
461
435
  sendJson(res, 200, {
462
436
  ok: true,
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-BiGDkBtj.js";import{p as f}from"./chunk-JWPE2WC7-B5jjn7zq.js";import{_ as n,l as o}from"./mermaid.core-DPvHX155.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-DZECjpOe.js";import"./index-4wCGsXuU.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-0wVqzhBr.js";import{p as f}from"./chunk-JWPE2WC7-BHizoNTm.js";import{_ as n,l as o}from"./mermaid.core-_8ilrIeK.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CNz317P7.js";import"./index-DHrt_6jk.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-DPvHX155.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-_8ilrIeK.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};