@piagent/platform 1.36.1 → 1.37.0

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 (82) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -1
  3. package/README.vi.md +1 -1
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +6 -6
  6. package/docs/languages.json +2 -2
  7. package/docs/operator-manual-vietnamese.md +7 -7
  8. package/docs/publishing-for-teams.md +1 -0
  9. package/docs/release-install-policy.md +2 -2
  10. package/docs/team-onboarding.md +2 -2
  11. package/docs-site/architecture.html +1 -1
  12. package/docs-site/benchmark.html +1 -1
  13. package/docs-site/commands.html +1 -1
  14. package/docs-site/content/en/index.html +4 -4
  15. package/docs-site/content/en/whats-new.html +6 -0
  16. package/docs-site/content/index.html +4 -4
  17. package/docs-site/content/whats-new.html +6 -0
  18. package/docs-site/context-engine.html +1 -1
  19. package/docs-site/ecosystem.html +1 -1
  20. package/docs-site/en/architecture.html +1 -1
  21. package/docs-site/en/benchmark.html +1 -1
  22. package/docs-site/en/commands.html +1 -1
  23. package/docs-site/en/context-engine.html +1 -1
  24. package/docs-site/en/ecosystem.html +1 -1
  25. package/docs-site/en/index.html +5 -5
  26. package/docs-site/en/mcp-approval.html +1 -1
  27. package/docs-site/en/mcp-auth.html +1 -1
  28. package/docs-site/en/mcp-servers.html +1 -1
  29. package/docs-site/en/mcp.html +1 -1
  30. package/docs-site/en/permissions.html +1 -1
  31. package/docs-site/en/profiles.html +1 -1
  32. package/docs-site/en/quickstart.html +1 -1
  33. package/docs-site/en/runtime.html +1 -1
  34. package/docs-site/en/scope.html +1 -1
  35. package/docs-site/en/security.html +1 -1
  36. package/docs-site/en/team.html +1 -1
  37. package/docs-site/en/whats-new.html +7 -1
  38. package/docs-site/en/windows.html +1 -1
  39. package/docs-site/en/workflows.html +1 -1
  40. package/docs-site/index.html +5 -5
  41. package/docs-site/mcp-approval.html +1 -1
  42. package/docs-site/mcp-auth.html +1 -1
  43. package/docs-site/mcp-servers.html +1 -1
  44. package/docs-site/mcp.html +1 -1
  45. package/docs-site/permissions.html +1 -1
  46. package/docs-site/profiles.html +1 -1
  47. package/docs-site/quickstart.html +1 -1
  48. package/docs-site/runtime.html +1 -1
  49. package/docs-site/scope.html +1 -1
  50. package/docs-site/security.html +1 -1
  51. package/docs-site/team.html +1 -1
  52. package/docs-site/whats-new.html +7 -1
  53. package/docs-site/windows.html +1 -1
  54. package/docs-site/workflows.html +1 -1
  55. package/evals/long-task-soak-v1/README.md +96 -0
  56. package/evals/long-task-soak-v1/lib/judge.mjs +67 -0
  57. package/evals/long-task-soak-v1/lib/persona.mjs +63 -0
  58. package/evals/long-task-soak-v1/lib/report.mjs +35 -0
  59. package/evals/long-task-soak-v1/lib/session-loop.mjs +165 -0
  60. package/evals/long-task-soak-v1/lib/workspace.mjs +115 -0
  61. package/evals/long-task-soak-v1/runner.mjs +89 -0
  62. package/evals/long-task-soak-v1/scenarios/autonomous-node-inventory.json +15 -0
  63. package/evals/long-task-soak-v1/scenarios/business-python-billing.json +34 -0
  64. package/evals/long-task-soak-v1/scenarios/framework-go-docker.json +40 -0
  65. package/evals/long-task-soak-v1/scenarios/framework-playwright-e2e.json +15 -0
  66. package/evals/long-task-soak-v1/scenarios/harness-guardrails-secrets.json +23 -0
  67. package/evals/long-task-soak-v1/scenarios/long-horizon-polyglot-rename.json +30 -0
  68. package/evals/long-task-soak-v1/scenarios/long-node-inventory-platform.json +64 -0
  69. package/evals/long-task-soak-v1/scenarios/long-polyglot-platform.json +288 -0
  70. package/evals/long-task-soak-v1/scenarios/long-postgres-booking-platform.json +210 -0
  71. package/evals/long-task-soak-v1/scenarios/long-python-billing-platform.json +84 -0
  72. package/evals/long-task-soak-v1/scenarios/mixed-requirement-change.json +20 -0
  73. package/evals/long-task-soak-v1/scenarios/stability-flaky-slow-suite.json +22 -0
  74. package/evals/long-task-soak-v1/scenarios/thirdparty-postgres-integration.json +30 -0
  75. package/evals/long-task-soak-v1/surfaces/terminal-rpc.mjs +150 -0
  76. package/evals/long-task-soak-v1/surfaces/webui-gateway.mjs +214 -0
  77. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  78. package/package.json +2 -1
  79. package/packages/piagent-core/package.json +1 -1
  80. package/packages/piagent-core/runtime/managed-failure.mjs +5 -0
  81. package/scripts/company-terminal.mjs +2 -1
  82. package/scripts/piagent-studio.mjs +13 -3
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · EN</span>
34
34
  <nav class="language-switch" aria-label="Language">
35
35
  <a class="language-option" href="/security" hreflang="vi" lang="vi">VI</a>
36
36
  <a class="language-option current" href="/en/security" hreflang="en" lang="en" aria-current="true">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · EN</span>
34
34
  <nav class="language-switch" aria-label="Language">
35
35
  <a class="language-option" href="/team" hreflang="vi" lang="vi">VI</a>
36
36
  <a class="language-option current" href="/en/team" hreflang="en" lang="en" aria-current="true">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · EN</span>
34
34
  <nav class="language-switch" aria-label="Language">
35
35
  <a class="language-option" href="/whats-new" hreflang="vi" lang="vi">VI</a>
36
36
  <a class="language-option current" href="/en/whats-new" hreflang="en" lang="en" aria-current="true">EN</a>
@@ -104,6 +104,12 @@
104
104
  <h1>1.9.0: ask in plain language, the team's Harness checks</h1>
105
105
  <p>The task contract and workflow commands are gone. Company mode asks for checks and a review on its own; Stop, network commands on approval and clearer approval cards.</p>
106
106
  </div>
107
+ <section class="section">
108
+ <p class="section-kicker">v1.37.0 · 2026-10-22</p>
109
+ <h2 id="v1-37-0">An answer cut mid-stream no longer stops a long task</h2>
110
+ <p class="section-lead">When the way from Studio to the model is cut while an answer is streaming, Studio says "Request interrupted"; Piagent used to take it as "the request could not be sent" and stopped the whole task. Now it asks again and goes on by itself. New <code>piagent studio --rpc</code> lets a script drive a company conversation over Pi's RPC protocol (several messages, answering the agent's questions).</p>
111
+ </section>
112
+
107
113
  <section class="section">
108
114
  <p class="section-kicker">v1.36.1 · 2026-10-22</p>
109
115
  <h2 id="v1-36-1">A moment when Agent Watch cannot reach Studio no longer stops a task</h2>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · EN</span>
34
34
  <nav class="language-switch" aria-label="Language">
35
35
  <a class="language-option" href="/windows" hreflang="vi" lang="vi">VI</a>
36
36
  <a class="language-option current" href="/en/windows" hreflang="en" lang="en" aria-current="true">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · EN</span>
34
34
  <nav class="language-switch" aria-label="Language">
35
35
  <a class="language-option" href="/workflows" hreflang="vi" lang="vi">VI</a>
36
36
  <a class="language-option current" href="/en/workflows" hreflang="en" lang="en" aria-current="true">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/" hreflang="en" lang="en">EN</a>
@@ -94,7 +94,7 @@
94
94
  <main>
95
95
  <div class="content-shell" data-article>
96
96
  <section class="hero">
97
- <p class="eyebrow">Piagent 1.36.1</p>
97
+ <p class="eyebrow">Piagent 1.37.0</p>
98
98
  <h1>Coding agent cho cả team, kiểm tra từng bước.</h1>
99
99
  <p>
100
100
  Một dashboard cho Terminal và trình duyệt. Dùng model bằng tài khoản của bạn, hoặc model của công ty qua Agent Watch và Agent Studio. Guard kiểm tra từng tool call; ở chế độ công ty, Harness của team tự yêu cầu chạy check, cho subagent review và báo kết quả ngay dưới câu trả lời.
@@ -108,7 +108,7 @@
108
108
  <div class="hero-actions">
109
109
  <a class="button primary" href="/quickstart">Bắt đầu</a>
110
110
  <a class="button" href="/ecosystem">Hệ sinh thái</a>
111
- <a class="button" href="/whats-new">Có gì mới trong 1.36.1</a>
111
+ <a class="button" href="/whats-new">Có gì mới trong 1.37.0</a>
112
112
  <a class="button" href="/commands">Xem command</a>
113
113
  </div>
114
114
  </section>
@@ -184,9 +184,9 @@ piagent-setup</code></pre>
184
184
  <h2 id="bat-dau-tu-dau">Bắt đầu từ đâu</h2>
185
185
  <div class="card-grid">
186
186
  <a class="doc-card" href="/whats-new">
187
- <span class="card-kicker">Bản 1.36.1</span>
187
+ <span class="card-kicker">Bản 1.37.0</span>
188
188
  <h3>Có gì mới</h3>
189
- <p>Agent Watch mất kết nối tới Studio trong chốc lát không còn làm dừng task; ở 1.36.0: team memory học từ chỗ làm sai: Studio chấm những gì memory đã đưa, lệnh lỗi lặp lại thành bài học; ở 1.35: task dài không dừng khi Studio cập nhật hay model báo lỗi tạm thời; ở 1.34: helper báo kết quả, Agent Watch báo khi cần bạn; ở 1.33.0: chạy trên Pi 1.3.0; ở 1.32.1: Bypass được giữ sau khi tải lại, tab đang dùng không còn bị đăng xuất sau 8 giờ; ở 1.32.0: lượt lỗi hoặc bấm Stop được dừng cả ở Studio; ở 1.31.1: <code>memory_ask</code> có ngay sau khi cập nhật; ở 1.31.0: Memory của team đi theo branch và luật của team, có <code>memory_ask</code>; ở 1.30.1: Terminal công ty không còn hiện tên model, mở <code>piagent studio</code> ở thư mục home báo rõ cần vào thư mục dự án; ở 1.30.0: context lớn đi qua mạng chậm, Terminal công ty gọn như dashboard; ở 1.29.0: Terminal công ty có giao diện riêng, dừng lượt không còn làm hỏng cuộc trò chuyện; ở 1.28.3: Agent Watch tự cập nhật không còn làm gián đoạn Terminal công ty; ở 1.28.2: không còn lỗi “binding changed” ngay sau khi cập nhật; ở 1.28.1: Terminal công ty và Pi cá nhân không còn giẫm chân nhau (không nạp Piagent hai lần, dọn cấu hình công ty cũ, cuộc trò chuyện công ty chỉ mở bằng piagent studio); ở 1.28.0: team memory v2; ở 1.27.1: team memory chạy ổn cả khi agent đang gọi model; ở 1.27.0: team memory, agent công ty nhớ quyết định và quy tắc của team trong từng repo; ở 1.26.1: đổi tên cuộc trò chuyện được cả khi lượt đang chạy; ở 1.26.0: lệnh / và phím tắt của Terminal ngay trên dashboard, /undo hoàn tác lượt trước của agent, dev server chạy qua nhiều lượt; ở 1.25.0: thiếu môi trường thì agent xin quyền và chạy được sau khi bạn duyệt (run_on_host ngoài sandbox, start_docker mở Docker ngay trong cuộc trò chuyện); ở 1.24.0: chọn Fast ngay trên trang tạo cuộc trò chuyện công ty, nút Fast đổi màu vàng khi đang bật; ở 1.23.1: câu hỏi của agent luôn nằm cuối cuộc trò chuyện, ngay trên ô nhập, và dễ đọc hơn; ở 1.23.0: nút Fast cho cuộc trò chuyện công ty khi Harness cho phép, tool lỗi được báo về Studio (Logs → Tool lỗi), cuộc trò chuyện mới gửi được tin nhắn đầu sau khi Harness đổi; ở 1.22.1: dashboard không còn quay mãi khi project nằm trên ổ Windows nhìn từ WSL (/mnt/d…) hoặc phiên công ty không trả lời; ở 1.22.0: đính kèm được mọi file code và văn bản (.html, .css, .ts, .py, Dockerfile…); ở 1.21.1: bật Bypass được cả khi lượt đang chạy và được giữ sau khi tải lại; ở 1.21.0: câu hỏi và yêu cầu duyệt hiện từng cái một (main agent trước), thông báo góc phải khi cuộc trò chuyện khác xong hay đang chờ bạn, Studio ghi lại mỗi lần compact; ở 1.20.0: cài package trong sandbox công ty không phải duyệt (npm, PyPI, Go, Maven, Gradle… qua proxy chỉ cho registry); ở 1.19.0: task công ty có checklist được làm đến hết, cuối mỗi lượt báo rõ xong, dừng giữa chừng hay lỗi, Docker trong sandbox công ty, folder chứa nhiều repo; ở 1.18.0: đổi nhánh Git trên dashboard, tự thử lại khi lỗi tạm thời, Claude Haiku 5.5; ở 1.17.0: dashboard hiện nhánh Git của project; ở 1.16.3: dashboard cập nhật xong vẫn tự kiểm tra bản mới; ở 1.16.2: Terminal nói giống dashboard: hỏi cập nhật, /bypass, lỗi và ghi chú Harness bằng tiếng Việt; ở 1.16.1: bản mới được báo bằng dialog và kiểm tra mỗi giờ, tab dashboard không còn mất đăng nhập; ở 1.16.0: Bypass cho cuộc trò chuyện công ty, đổi Harness không làm gián đoạn; ở 1.15.2: cuộc trò chuyện dài tự thu gọn trở lại; ở 1.15.1: Studio thấy mỗi call của model dùng để làm gì, nhiều cuộc trò chuyện trong một folder không nhận nhầm thay đổi của nhau; ở 1.15.0: subagent bị từ chối nói đúng lý do; ở 1.14: cập nhật giữ model bạn đã chọn; ở 1.13: cuộc trò chuyện giữ chỗ đang đọc, chữ của agent có định dạng, Piagent trên Windows qua WSL2: sandbox công ty bằng bubblewrap và Agent Watch cho Windows; ở 1.12: @ chọn file và folder trên máy, skill và command dùng bằng /, nhánh cuộc trò chuyện công ty gửi được tin nhắn.</p>
189
+ <p>Câu trả lời bị cắt giữa chừng không còn làm dừng task dài, có <code>piagent studio --rpc</code>; ở 1.36.1: Agent Watch mất kết nối tới Studio trong chốc lát không còn làm dừng task; ở 1.36.0: team memory học từ chỗ làm sai: Studio chấm những gì memory đã đưa, lệnh lỗi lặp lại thành bài học; ở 1.35: task dài không dừng khi Studio cập nhật hay model báo lỗi tạm thời; ở 1.34: helper báo kết quả, Agent Watch báo khi cần bạn; ở 1.33.0: chạy trên Pi 1.3.0; ở 1.32.1: Bypass được giữ sau khi tải lại, tab đang dùng không còn bị đăng xuất sau 8 giờ; ở 1.32.0: lượt lỗi hoặc bấm Stop được dừng cả ở Studio; ở 1.31.1: <code>memory_ask</code> có ngay sau khi cập nhật; ở 1.31.0: Memory của team đi theo branch và luật của team, có <code>memory_ask</code>; ở 1.30.1: Terminal công ty không còn hiện tên model, mở <code>piagent studio</code> ở thư mục home báo rõ cần vào thư mục dự án; ở 1.30.0: context lớn đi qua mạng chậm, Terminal công ty gọn như dashboard; ở 1.29.0: Terminal công ty có giao diện riêng, dừng lượt không còn làm hỏng cuộc trò chuyện; ở 1.28.3: Agent Watch tự cập nhật không còn làm gián đoạn Terminal công ty; ở 1.28.2: không còn lỗi “binding changed” ngay sau khi cập nhật; ở 1.28.1: Terminal công ty và Pi cá nhân không còn giẫm chân nhau (không nạp Piagent hai lần, dọn cấu hình công ty cũ, cuộc trò chuyện công ty chỉ mở bằng piagent studio); ở 1.28.0: team memory v2; ở 1.27.1: team memory chạy ổn cả khi agent đang gọi model; ở 1.27.0: team memory, agent công ty nhớ quyết định và quy tắc của team trong từng repo; ở 1.26.1: đổi tên cuộc trò chuyện được cả khi lượt đang chạy; ở 1.26.0: lệnh / và phím tắt của Terminal ngay trên dashboard, /undo hoàn tác lượt trước của agent, dev server chạy qua nhiều lượt; ở 1.25.0: thiếu môi trường thì agent xin quyền và chạy được sau khi bạn duyệt (run_on_host ngoài sandbox, start_docker mở Docker ngay trong cuộc trò chuyện); ở 1.24.0: chọn Fast ngay trên trang tạo cuộc trò chuyện công ty, nút Fast đổi màu vàng khi đang bật; ở 1.23.1: câu hỏi của agent luôn nằm cuối cuộc trò chuyện, ngay trên ô nhập, và dễ đọc hơn; ở 1.23.0: nút Fast cho cuộc trò chuyện công ty khi Harness cho phép, tool lỗi được báo về Studio (Logs → Tool lỗi), cuộc trò chuyện mới gửi được tin nhắn đầu sau khi Harness đổi; ở 1.22.1: dashboard không còn quay mãi khi project nằm trên ổ Windows nhìn từ WSL (/mnt/d…) hoặc phiên công ty không trả lời; ở 1.22.0: đính kèm được mọi file code và văn bản (.html, .css, .ts, .py, Dockerfile…); ở 1.21.1: bật Bypass được cả khi lượt đang chạy và được giữ sau khi tải lại; ở 1.21.0: câu hỏi và yêu cầu duyệt hiện từng cái một (main agent trước), thông báo góc phải khi cuộc trò chuyện khác xong hay đang chờ bạn, Studio ghi lại mỗi lần compact; ở 1.20.0: cài package trong sandbox công ty không phải duyệt (npm, PyPI, Go, Maven, Gradle… qua proxy chỉ cho registry); ở 1.19.0: task công ty có checklist được làm đến hết, cuối mỗi lượt báo rõ xong, dừng giữa chừng hay lỗi, Docker trong sandbox công ty, folder chứa nhiều repo; ở 1.18.0: đổi nhánh Git trên dashboard, tự thử lại khi lỗi tạm thời, Claude Haiku 5.5; ở 1.17.0: dashboard hiện nhánh Git của project; ở 1.16.3: dashboard cập nhật xong vẫn tự kiểm tra bản mới; ở 1.16.2: Terminal nói giống dashboard: hỏi cập nhật, /bypass, lỗi và ghi chú Harness bằng tiếng Việt; ở 1.16.1: bản mới được báo bằng dialog và kiểm tra mỗi giờ, tab dashboard không còn mất đăng nhập; ở 1.16.0: Bypass cho cuộc trò chuyện công ty, đổi Harness không làm gián đoạn; ở 1.15.2: cuộc trò chuyện dài tự thu gọn trở lại; ở 1.15.1: Studio thấy mỗi call của model dùng để làm gì, nhiều cuộc trò chuyện trong một folder không nhận nhầm thay đổi của nhau; ở 1.15.0: subagent bị từ chối nói đúng lý do; ở 1.14: cập nhật giữ model bạn đã chọn; ở 1.13: cuộc trò chuyện giữ chỗ đang đọc, chữ của agent có định dạng, Piagent trên Windows qua WSL2: sandbox công ty bằng bubblewrap và Agent Watch cho Windows; ở 1.12: @ chọn file và folder trên máy, skill và command dùng bằng /, nhánh cuộc trò chuyện công ty gửi được tin nhắn.</p>
190
190
  </a>
191
191
  <a class="doc-card" href="/ecosystem">
192
192
  <span class="card-kicker">Get started</span>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/mcp-approval" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/mcp-approval" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/mcp-auth" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/mcp-auth" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/mcp-servers" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/mcp-servers" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/mcp" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/mcp" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/permissions" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/permissions" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/profiles" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/profiles" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/quickstart" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/quickstart" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/runtime" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/runtime" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/scope" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/scope" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/security" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/security" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/team" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/team" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/whats-new" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/whats-new" hreflang="en" lang="en">EN</a>
@@ -104,6 +104,12 @@
104
104
  <h1>Bản 1.9.0: nói việc bằng lời, Harness của team kiểm tra</h1>
105
105
  <p>Bỏ task contract và lệnh workflow. Chế độ công ty tự yêu cầu check và review; nút Dừng, lệnh có mạng theo lần duyệt và thẻ duyệt dễ đọc hơn.</p>
106
106
  </div>
107
+ <section class="section">
108
+ <p class="section-kicker">v1.37.0 · 2026-10-22</p>
109
+ <h2 id="v1-37-0">Câu trả lời bị cắt giữa chừng không còn làm dừng task dài</h2>
110
+ <p class="section-lead">Khi đường từ Studio tới model bị cắt lúc câu trả lời đang chạy, Studio báo "Request interrupted"; trước đây Piagent coi đó là "không gửi được yêu cầu" và dừng cả task. Giờ Piagent hỏi lại và tự làm tiếp. Thêm <code>piagent studio --rpc</code> để một script điều khiển cuộc trò chuyện công ty qua giao thức RPC của Pi (nhiều tin nhắn, trả lời câu hỏi của agent).</p>
111
+ </section>
112
+
107
113
  <section class="section">
108
114
  <p class="section-kicker">v1.36.1 · 2026-10-22</p>
109
115
  <h2 id="v1-36-1">Agent Watch mất kết nối tới Studio trong chốc lát không còn làm dừng task</h2>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/windows" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/windows" hreflang="en" lang="en">EN</a>
@@ -30,7 +30,7 @@
30
30
  <span>Pi Agent Platform</span>
31
31
  </a>
32
32
  <div class="top-actions">
33
- <span class="pill"><span class="pill-dot"></span>v1.36.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.37.0 docs · VI</span>
34
34
  <nav class="language-switch" aria-label="Ngôn ngữ">
35
35
  <a class="language-option current" href="/workflows" hreflang="vi" lang="vi" aria-current="true">VI</a>
36
36
  <a class="language-option" href="/en/workflows" hreflang="en" lang="en">EN</a>
@@ -0,0 +1,96 @@
1
+ # Long-task soak lane v1
2
+
3
+ Members leave when a long task stops part way and they have to keep pushing
4
+ it. This lane measures exactly that, on the real company agent with real
5
+ models: a simulated member gives a long task in a real repository, answers
6
+ only the business decisions it is asked about, presses Stop, adds a rule
7
+ mid-task or reopens the conversation like a person would, and writes
8
+ "tiếp tục" when the agent stops before the work is done. Hidden acceptance
9
+ checks, not the agent's own words, decide whether the work is done.
10
+
11
+ It costs real model tokens through Studio. There is no provider-free mode:
12
+ the deterministic lifecycle evidence lives in `evals/long-horizon-v1` and the
13
+ `tests/managed-*` suites.
14
+
15
+ ## Surfaces
16
+
17
+ - `terminal`: `piagent studio --rpc --project <repo>` (Pi's RPC protocol;
18
+ `ask_user` arrives as an extension UI select). Needs a Piagent release that
19
+ has `--rpc` (after 1.35.1).
20
+ - `webui`: the company WebUI's Gateway used the way the browser app uses it
21
+ (launch link → cookie + CSRF, `sessions.command` over the WebSocket,
22
+ questions and approvals over HTTP). Conversations appear in the member's
23
+ WebUI under the soak projects.
24
+
25
+ ## Run
26
+
27
+ ```bash
28
+ node evals/long-task-soak-v1/runner.mjs --list
29
+ node evals/long-task-soak-v1/runner.mjs --self-check # oracles: fail on the fixture, pass on the reference
30
+ node evals/long-task-soak-v1/runner.mjs --surface webui --scenario autonomous-node-inventory
31
+ node evals/long-task-soak-v1/runner.mjs --surface both --category business,third-party --repeat 2
32
+ ```
33
+
34
+ Runs go to `~/piagent-soak/runs/<time>/<scenario>-<surface>-<n>/` (`project/`,
35
+ `events.jsonl`, `result.json`); reports to `~/piagent-soak/reports/` as JSON
36
+ and Markdown. `--work-root` moves both.
37
+
38
+ ## Verdicts
39
+
40
+ - `smooth`: the checks pass and the member never had to push.
41
+ - `recovered`: the checks pass after nudges, a Stop on a stall, or a reopen
42
+ after a crash, within the scenario's `maxNudges`.
43
+ - `abandoned`: the checks fail, or the member had to push more than
44
+ `maxNudges` times. This is the number to drive down.
45
+
46
+ Defects recorded per turn: `stopped_midway`, `asked_permission_to_continue`,
47
+ `claimed_done_but_checks_fail`, `ended_without_answer`, `turn_failed`,
48
+ `stalled_no_activity`, `process_crashed`, `kept_asking` (more questions than
49
+ the scenario's decisions need, each counted as a push); per run:
50
+ `business_decision_not_asked`, `unneeded_question`, `protected_file_changed`,
51
+ `time_budget_exceeded`.
52
+
53
+ ## Scenario format (`scenarios/<id>.json`)
54
+
55
+ ```jsonc
56
+ {
57
+ "id": "business-python-billing",
58
+ "title": "…",
59
+ "category": ["business", "framework"], // free tags used by --category
60
+ "language": "vi" | "en" | "ja", // the member's language (nudges, replies)
61
+ "fixture": "python-billing", // fixtures/<name>/
62
+ "requires": ["docker", "uv", "network"], // missing → skipped
63
+ "prompt": "…", // the member's first message
64
+ "questionsExpected": false, // questions no decision covers are defects
65
+ "decisions": [{ // what only the member can decide
66
+ "id": "rounding", "mustAsk": true,
67
+ "topic": ["round", "làm tròn"], // regexes matched against a question
68
+ "option": "half.?even|banker", // ask_user option to pick (regex on label+description)
69
+ "reply": "Round half-even to 2 decimals." // free-text answer otherwise
70
+ }],
71
+ "approvals": "allow" | "deny",
72
+ "userActions": [{ "when": { "toolCalls": 20 } | { "minutes": 10 },
73
+ "do": "steer" | "follow_up" | "stop_then_continue" | "restart", "text": "…" }],
74
+ "budget": { "maxMinutes": 90, "maxNudges": 3, "stallMinutes": 15 },
75
+ "oracle": { "hidden": "hidden", "checks": [{ "name": "…", "run": "shell", "timeoutSec": 600 }] },
76
+ "reference": "reference", "referenceSetup": ["…"], "setup": ["…"], "protect": [".env"]
77
+ }
78
+ ```
79
+
80
+ A fixture is `fixtures/<name>/` with `project/` (what the member's repository
81
+ holds), `fixture.json` (`setup` commands such as dependency installs,
82
+ `protect`ed files), one or more hidden overlays (acceptance tests the agent
83
+ never sees) and reference overlays (a solution, used only by
84
+ `--self-check`). Oracle checks run on a copy of the project with the hidden
85
+ overlay laid over it; `$SOAK_RUN` names the run, for unique container names.
86
+ Every scenario must pass `--self-check` before it is used.
87
+
88
+ Self-check proves the reference passes, not that the hidden checks accept
89
+ every reasonable solution. The first long soak (2026-10-21) lost five runs to
90
+ checks stricter than their spec: a shortfall shown as 0 instead of negative,
91
+ a cancelled seed booking not counted, `sqlite3.Row` instead of tuples, and a
92
+ phase-2 regression check that broke on phase 3's new fields. Hidden checks
93
+ assert only what the spec says, read rows and objects loosely, and earlier
94
+ phases' checks must tolerate later phases' legitimate additions. When a run
95
+ fails a check, run the phase oracle on its `project/` and read the failure
96
+ before blaming the agent.
@@ -0,0 +1,67 @@
1
+ // How a turn ended, seen the way a member reading the screen would see it:
2
+ // the work is done, the agent asks something the member must decide, or it
3
+ // stopped part way (the "pause" that makes members give up). The oracle, not
4
+ // the agent's own words, says whether the work is done.
5
+
6
+ const QUESTION_WORDS = [
7
+ // vi
8
+ /\b(?:anh|chị|bạn|em)\s+(?:muốn|chọn|thích|có muốn|cho phép|đồng ý|xác nhận)/i, /\b(?:có nên|nên chọn|hay là|phương án nào|lựa chọn nào|xác nhận giúp)\b/i,
9
+ // en
10
+ /\b(?:should I|shall I|do you want|would you like|which (?:option|one|approach)|please confirm|can you confirm|let me know (?:if|which|whether)|your call|prefer)\b/i,
11
+ // ja
12
+ /(?:どちら|しますか|よろしいですか|いかがですか|確認してください|選んでください)/,
13
+ ];
14
+ const PERMISSION_TO_GO_ON = [
15
+ /\b(?:có muốn|muốn) (?:tôi|em|mình) (?:tiếp tục|làm tiếp|tiếp)/i, /\btiếp tục (?:không|chứ|nhé)\s*\?/i,
16
+ /\b(?:shall|should) I (?:continue|proceed|go on|keep going)\b/i, /\b(?:want|like) me to (?:continue|proceed|keep going|go ahead)\b/i,
17
+ /続けますか|続行しますか|進めてもよろしいですか/,
18
+ ];
19
+ const MIDWAY_WORDS = [
20
+ /(?:tiếp theo (?:tôi|em|mình) sẽ|(?:tôi|em|mình) sẽ (?:tiếp tục|làm tiếp)|phần còn lại|các mục còn lại|chưa xong|chưa hoàn thành|bước tiếp theo)/i,
21
+ /\b(?:next,? I(?:'ll| will)|I(?:'ll| will) (?:continue|now|next)|remaining (?:steps|items|work)|not (?:yet )?(?:done|finished|complete)|still (?:need|to do)|TODO)\b/i,
22
+ /(?:次に|続いて|残りの|未完了)/,
23
+ ];
24
+ const DONE_WORDS = [/(?:đã (?:làm )?xong|làm xong|xong cả|xong hết|hoàn thành|hoàn tất|xong rồi|(?:test|kiểm thử) (?:đều )?(?:pass|qua)|qua \d+\/\d+)/i, /\b(?:pass(?:ed|es)? \d+\/\d+|\d+\/\d+ (?:tests? )?pass)/i, /\b(?:done|completed|finished|all (?:tests|checks) pass(?:ed)?|implemented)\b/i, /(?:完了|終わりました|すべてのテストが通)/];
25
+
26
+ const tail = (text, n = 700) => String(text ?? '').trim().slice(-n);
27
+
28
+ // Does the agent's last text hand the member a question to answer?
29
+ export function asksMember(text) {
30
+ const end = tail(text);
31
+ if (!end) return false;
32
+ const lastParagraph = end.split(/\n\s*\n/).filter(Boolean).at(-1) ?? end;
33
+ return /[??]\s*(?:[*_`)\]]*)\s*$/.test(lastParagraph) || /[??]/.test(lastParagraph) && QUESTION_WORDS.some(re => re.test(lastParagraph))
34
+ || QUESTION_WORDS.some(re => re.test(lastParagraph)) && /\n\s*(?:\d+[.)]|[-*•])\s+\S/.test(end);
35
+ }
36
+ export const asksPermissionToGoOn = text => PERMISSION_TO_GO_ON.some(re => re.test(tail(text, 400)));
37
+ export const saysMidway = text => MIDWAY_WORDS.some(re => re.test(tail(text, 600)));
38
+ export const claimsDone = text => DONE_WORDS.some(re => re.test(tail(text, 900)));
39
+
40
+ // One turn end → what the member sees and what the simulated member does next.
41
+ // turn: { lastText, turnEnd: {state}|null, error: string|null, stalled, crashed }
42
+ // oracle: { pass } | null (null when not run because the agent asked)
43
+ export function classifyTurn(turn, oracle) {
44
+ if (turn.crashed) return { kind: 'crashed', defect: 'process_crashed' };
45
+ if (turn.stalled) return { kind: 'stalled', defect: 'stalled_no_activity' };
46
+ if (oracle?.pass) return { kind: 'done', defect: null };
47
+ if (turn.error || turn.turnEnd?.state === 'failed') return { kind: 'failed', defect: 'turn_failed' };
48
+ if (turn.turnEnd?.state === 'cancelled') return { kind: 'cancelled', defect: null };
49
+ const text = turn.lastText ?? '';
50
+ if (asksPermissionToGoOn(text)) return { kind: 'permission_pause', defect: 'asked_permission_to_continue' };
51
+ if (asksMember(text)) return { kind: 'asked', defect: null };
52
+ if (turn.turnEnd?.state === 'midway' || saysMidway(text)) return { kind: 'midway', defect: 'stopped_midway' };
53
+ // The Harness itself closed the turn as finished: whatever the wording,
54
+ // the agent considered the work done.
55
+ if (claimsDone(text) || turn.turnEnd?.state === 'done') return { kind: 'false_done', defect: 'claimed_done_but_checks_fail' };
56
+ if (!text.trim()) return { kind: 'silent', defect: 'ended_without_answer' };
57
+ return { kind: 'midway', defect: 'stopped_midway' };
58
+ }
59
+
60
+ // Would a real member still be here? smooth: done with no help; recovered:
61
+ // done after the member pushed (a nudge, a Stop on a stall, a reopen after a
62
+ // crash); abandoned: not done, or more pushing than a member gives.
63
+ export function verdict({ pass, nudges, stalls, crashes, maxNudges }) {
64
+ const pushes = nudges + stalls + crashes;
65
+ if (!pass || pushes > maxNudges) return 'abandoned';
66
+ return pushes === 0 ? 'smooth' : 'recovered';
67
+ }
@@ -0,0 +1,63 @@
1
+ // The simulated member: answers the business decisions a scenario leaves
2
+ // open (only when asked), pushes the agent on when it stops part way, and
3
+ // says so in the scenario's language, as a real member would.
4
+
5
+ const NUDGES = {
6
+ vi: ['tiếp tục', 'Tiếp tục đi, làm cho xong hết các mục nhé.', 'Vẫn chưa xong, em làm tiếp đến khi kiểm tra đều qua.'],
7
+ en: ['continue', 'Please keep going until everything is finished.', 'It is still not done. Continue until all checks pass.'],
8
+ ja: ['続けてください', '最後まで続けてください。', 'まだ終わっていません。すべてのチェックが通るまで続けてください。'],
9
+ };
10
+ const GO_ON = { vi: 'Có, cứ làm tiếp, không cần hỏi lại.', en: 'Yes, go ahead without asking again.', ja: 'はい、確認なしで続けてください。' };
11
+ const STILL_FAILING = { vi: 'Bên anh kiểm tra vẫn chưa qua:', en: 'On my side these checks still fail:', ja: 'こちらではまだ次のチェックが失敗しています:' };
12
+ const WHAT_FAILS = [/(?:mục|phần|test|check|kiểm tra) nào (?:vẫn )?(?:chưa|còn|fail|lỗi)/i, /\b(?:which|what) (?:check|test|item|part)s? (?:is |are )?(?:still )?(?:fail|not)/i, /\bwhat(?:'s| is) (?:still )?(?:failing|missing|wrong)/i, /(?:どの|何が).*(?:失敗|通って)/];
13
+ export const asksWhatFails = text => WHAT_FAILS.some(re => re.test(String(text ?? '').slice(-600)));
14
+ const YOUR_CALL = { vi: 'Em tự quyết theo hợp lý nhất, ghi rõ giả định.', en: 'Your call: pick the most reasonable option and state the assumption.', ja: 'お任せします。妥当な選択をして前提を明記してください。' };
15
+
16
+ const matches = (text, topic) => (topic ?? []).some(word => new RegExp(word, 'i').test(text));
17
+ const optionText = option => typeof option === 'string' ? option : `${option.label ?? ''} ${option.description ?? ''}`;
18
+
19
+ export class Persona {
20
+ constructor(scenario) {
21
+ this.language = NUDGES[scenario.language] ? scenario.language : 'en';
22
+ this.decisions = scenario.decisions ?? [];
23
+ this.asked = new Map(); // decision id → how it was asked: 'tool' | 'prose'
24
+ this.unexpected = []; // questions no decision covers
25
+ this.nudges = 0;
26
+ }
27
+
28
+ nudge(kind, failing = []) {
29
+ if (kind === 'permission_pause') return GO_ON[this.language];
30
+ const lines = NUDGES[this.language], line = lines[Math.min(this.nudges++, lines.length - 1)];
31
+ // Told it is done, the member tries it and says what still fails, as a
32
+ // member reads a red check, not the hidden tests themselves.
33
+ return kind === 'false_done' && failing.length ? `${line}\n${this.failingText(failing)}` : line;
34
+ }
35
+ failingText(failing) { return [STILL_FAILING[this.language], ...failing.map(f => `- ${f}`)].join('\n'); }
36
+ // A Stop on a stall, then the member writes again: counts as its own push.
37
+ afterStall() { return NUDGES[this.language][0]; }
38
+
39
+ #decisionFor(text) { return this.decisions.find(d => matches(text, d.topic)); }
40
+
41
+ // One structured question (ask_user): {question, header, options[], multiSelect}
42
+ // → {selected: [index]} | {other: text} | {skipped: true}
43
+ answerStructured(q) {
44
+ const text = `${q.header ?? ''} ${q.question}`;
45
+ const decision = this.#decisionFor(text);
46
+ if (!decision) { this.unexpected.push(q.question); return { skipped: true }; }
47
+ this.asked.set(decision.id, this.asked.get(decision.id) ?? 'tool');
48
+ const pick = new RegExp(decision.option ?? '^$', 'i');
49
+ const index = (q.options ?? []).findIndex(o => pick.test(optionText(o)));
50
+ return index >= 0 && decision.option ? { selected: [index] } : { other: decision.reply };
51
+ }
52
+
53
+ // A question written in the answer text: reply to every decision it touches.
54
+ answerProse(text) {
55
+ const hit = this.decisions.filter(d => matches(text, d.topic));
56
+ if (!hit.length) { this.unexpected.push(String(text).slice(-300)); return YOUR_CALL[this.language]; }
57
+ for (const d of hit) this.asked.set(d.id, this.asked.get(d.id) ?? 'prose');
58
+ return hit.map(d => d.reply).join('\n');
59
+ }
60
+
61
+ // Decisions the member had to make that the agent never asked about.
62
+ notAsked() { return this.decisions.filter(d => d.mustAsk && !this.asked.has(d.id)).map(d => d.id); }
63
+ }
@@ -0,0 +1,35 @@
1
+ // The report a maintainer reads after a night's soak: would members have
2
+ // stayed, and what made them push.
3
+ const cell = v => String(v ?? '').replaceAll('|', '\\|').replaceAll('\n', ' ');
4
+
5
+ export function markdownReport(report) {
6
+ const lines = [`# Long-task soak ${report.startedAt}`, ''];
7
+ if (report.mode === 'self-check') {
8
+ lines.push('| Scenario | Oracle | Baseline | Reference |', '|---|---|---|---|');
9
+ for (const r of report.results) lines.push(`| ${r.scenario} | ${r.skipped ? `skipped (${r.skipped.join(', ')})` : r.ok ? 'ok' : 'BROKEN'} | ${cell(r.baseline?.map(c => `${c.name}:${c.pass ? 'pass' : 'fail'}`).join(' '))} | ${cell(r.reference?.map(c => `${c.name}:${c.pass ? 'pass' : 'fail'}`).join(' ') ?? r.error)} |`);
10
+ return lines.join('\n') + '\n';
11
+ }
12
+ const runs = report.results.filter(r => !r.skipped);
13
+ const count = v => runs.filter(r => r.verdict === v).length;
14
+ lines.push(`Runs: ${runs.length} · smooth ${count('smooth')} · recovered ${count('recovered')} · abandoned ${count('abandoned')} · harness errors ${count('harness_error')}`, '');
15
+ const defects = {};
16
+ for (const r of runs) for (const d of r.defects ?? []) defects[d.defect] = (defects[d.defect] ?? 0) + 1;
17
+ if (Object.keys(defects).length) {
18
+ lines.push('## What made members push', '', '| Defect | Runs |', '|---|---|');
19
+ for (const [d, n] of Object.entries(defects).sort((a, b) => b[1] - a[1])) lines.push(`| ${d} | ${n} |`);
20
+ lines.push('');
21
+ }
22
+ lines.push('## Runs', '', '| Scenario | Surface | Verdict | Phases | Min | Turns | Nudges | Stalls | Crashes | Questions | Tools (err) | Retries | Compactions | Stops/restarts | Defects |', '|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|');
23
+ for (const r of runs) {
24
+ const s = r.stats ?? {};
25
+ lines.push(`| ${r.scenario} | ${r.surface} | **${r.verdict}** | ${r.phases ?? ''} | ${r.minutes ?? ''} | ${s.turns ?? ''} | ${s.nudges ?? ''} | ${s.stalls ?? ''} | ${s.crashes ?? ''} | ${s.questions ?? ''} | ${s.toolCalls ?? ''} (${s.toolErrors ?? ''}) | ${s.providerRetries ?? ''} | ${s.compactions ?? ''} | ${s.memberStops ?? ''}/${s.restarts ?? ''} | ${cell((r.defects ?? []).map(d => d.defect).join(', ') || r.error?.split('\n')[0])} |`);
26
+ }
27
+ for (const r of runs.filter(r => r.verdict !== 'smooth')) {
28
+ lines.push('', `### ${r.scenario} on ${r.surface}: ${r.verdict}`, '', `Run folder: \`${r.runDir}\``, '');
29
+ for (const t of r.turns ?? []) lines.push(`- turn ${t.n} [${t.phase ?? 'main'}] (${t.minutes} min): ${t.kind}${t.defect ? ` — ${t.defect}` : ''}${t.oracle ? ` · checks ${t.oracle.pass ? 'pass' : `fail: ${t.oracle.failed.join(', ')}`}` : ''}`);
30
+ if (r.lastText) lines.push('', '> ' + cell(r.lastText.slice(-600)));
31
+ if (r.error) lines.push('', '```', r.error.slice(0, 1200), '```');
32
+ }
33
+ for (const r of report.results.filter(r => r.skipped)) lines.push('', `Skipped ${r.scenario}: missing ${r.skipped.join(', ')}`);
34
+ return lines.join('\n') + '\n';
35
+ }