@piagent/platform 1.16.1 → 1.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/README.vi.md +1 -1
  4. package/docs/command-reference-vietnamese.md +4 -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 +13 -0
  16. package/docs-site/content/index.html +4 -4
  17. package/docs-site/content/whats-new.html +13 -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 +14 -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 +14 -1
  53. package/docs-site/windows.html +1 -1
  54. package/docs-site/workflows.html +1 -1
  55. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  56. package/package.json +1 -1
  57. package/packages/piagent-core/extensions/piagent-guard.ts +3 -0
  58. package/packages/piagent-core/managed/resource-loader.mjs +14 -2
  59. package/packages/piagent-core/managed/session.mjs +7 -5
  60. package/packages/piagent-core/package.json +1 -1
  61. package/packages/piagent-core/runtime/update/terminal-update-offer.d.mts +7 -0
  62. package/packages/piagent-core/runtime/update/terminal-update-offer.mjs +114 -0
  63. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-sL0LOqY0.js → MarkdownMessage-BDdLJYnZ.js} +1 -1
  64. package/packages/piagent-webui/dist/client/assets/{index-Bs8l37SR.js → index-DJGByyfL.js} +12 -12
  65. package/packages/piagent-webui/dist/client/index.html +1 -1
  66. package/packages/piagent-webui/shared/company-copy.ts +99 -0
  67. package/packages/piagent-webui/shared/process-copy.ts +64 -0
  68. package/scripts/company-terminal.mjs +104 -0
  69. package/scripts/piagent-studio.mjs +13 -4
@@ -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.16.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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,19 @@
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.16.2 · 2026-10-08</p>
109
+ <h2 id="v1-16-2">The Terminal says what the dashboard says</h2>
110
+ <p class="section-lead">When a new Piagent is out, the Terminal (<span class="inline-code">pi</span> and <span class="inline-code">piagent studio</span>) asks <strong>Update now</strong> or <strong>Later</strong> (asked again after an hour), only between turns, with a footer line; <span class="inline-code">/piagent-update</span> checks at once.</p>
111
+ <ul>
112
+ <li><span class="inline-code">/bypass</span> in <span class="inline-code">piagent studio</span> switches Bypass for the conversation, like the dashboard's switch; the footer shows "Bypass" while it is on.</li>
113
+ <li>A network command is confirmed with its reason, why Bypass still asks, and what allowing or declining does.</li>
114
+ <li>Failed requests, a company runtime that cannot start and the Harness's notes (checks, review, a subagent's objection, "your call") read in Vietnamese as on the dashboard.</li>
115
+ <li>With Agent Watch 0.15.2: when a Watch update needs Keychain access again, Watch asks in a dialog and refreshes Piagent's setup at once.</li>
116
+ </ul>
117
+ <p>This release changes the company launcher: Agent Watch refreshes the setup on its next sync (minutes). If <span class="inline-code">piagent studio</span> says the setup changed, open Agent Watch → Studio and import it for Piagent.</p>
118
+ </section>
119
+
107
120
  <section class="section">
108
121
  <p class="section-kicker">v1.16.1 · 2026-10-07</p>
109
122
  <h2 id="v1-16-1">New releases are announced, checked every hour</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.16.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1</p>
97
+ <p class="eyebrow">Piagent 1.16.2</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.16.1</a>
111
+ <a class="button" href="/whats-new">Có gì mới trong 1.16.2</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.16.1</span>
187
+ <span class="card-kicker">Bản 1.16.2</span>
188
188
  <h3>Có gì mới</h3>
189
- <p>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>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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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,19 @@
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.16.2 · 2026-10-08</p>
109
+ <h2 id="v1-16-2">Terminal nói giống dashboard</h2>
110
+ <p class="section-lead">Có bản Piagent mới thì Terminal (<span class="inline-code">pi</span> và <span class="inline-code">piagent studio</span>) hỏi <strong>Cập nhật ngay</strong> hoặc <strong>Để sau</strong> (nhắc lại sau 1 giờ), chỉ giữa các lượt, kèm một dòng dưới footer; <span class="inline-code">/piagent-update</span> kiểm tra ngay.</p>
111
+ <ul>
112
+ <li><span class="inline-code">/bypass</span> trong <span class="inline-code">piagent studio</span> bật/tắt Bypass cho cuộc trò chuyện, giống công tắc trên dashboard; footer hiện "Bypass" khi đang bật.</li>
113
+ <li>Lệnh cần internet được hỏi kèm lý do, lý do Bypass vẫn hỏi và hậu quả khi đồng ý hay từ chối.</li>
114
+ <li>Lỗi khi gửi, lỗi khi mở phiên công ty và ghi chú của Harness (check, review, phản biện của subagent, "bạn quyết định") hiện bằng tiếng Việt như trên dashboard.</li>
115
+ <li>Với Agent Watch 0.15.2: sau khi Watch cập nhật mà cần cấp lại quyền Keychain, Watch hỏi bằng dialog và cập nhật lại cấu hình Piagent ngay.</li>
116
+ </ul>
117
+ <p>Bản này đổi trình khởi chạy công ty: Agent Watch tự cập nhật cấu hình ở lần đồng bộ kế tiếp (vài phút). Nếu <span class="inline-code">piagent studio</span> báo cấu hình đã đổi, mở Agent Watch → Studio và nhập lại cấu hình cho Piagent.</p>
118
+ </section>
119
+
107
120
  <section class="section">
108
121
  <p class="section-kicker">v1.16.1 · 2026-10-07</p>
109
122
  <h2 id="v1-16-1">Báo bản mới rõ ràng, kiểm tra mỗi giờ</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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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.16.1 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.16.2 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>
@@ -256,8 +256,8 @@
256
256
  },
257
257
  {
258
258
  "path": "tests",
259
- "fileCount": 525,
260
- "sha256": "c9ef8c449f71514ed8f32161be6d5280ea4ccd702341cc5ca645c9e51f70fb7f"
259
+ "fileCount": 526,
260
+ "sha256": "01827d63b0b67a55437e498d6eb0a5ffa43806c88c02eb687b6bba945d37aa59"
261
261
  }
262
262
  ],
263
263
  "custodianComparisonRules": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/platform",
3
- "version": "1.16.1",
3
+ "version": "1.16.2",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -164,6 +164,7 @@ import { fallbackBasePolicy } from "./fallback-base-policy.ts";
164
164
  import { prefixCompletions, registerPiagentTool as registerToolDefinition, registerRuntimeCommand, registerRuntimeTool } from "../runtime/registration/extension-registration.ts";
165
165
  import { registerTaskEvidenceTools } from "../runtime/registration/task-evidence-tools.ts";
166
166
  import { registerAgentResources } from "../runtime/registration/agent-resources.ts";
167
+ import { registerTerminalUpdateOffer } from "../runtime/update/terminal-update-offer.mjs";
167
168
  import { userSkillFolders } from "../runtime/resources/agent-resources.mjs";
168
169
  import { FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP, ONBOARDING_COMMAND_ACTIONS, WORKFLOW_COMMAND_EXCLUSIONS } from "../runtime/registration/operator-catalogs.ts";
169
170
  import { registerPiagentStatusCommand } from "../runtime/registration/runtime-model-status.ts";
@@ -4159,5 +4160,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4159
4160
  // citations need a task id), so it is no longer registered at all.
4160
4161
  registerTaskEvidenceTools(pi, registrationDeps);
4161
4162
  registerAgentResources(pi);
4163
+ // A new release is asked about in the Terminal, as the dashboard does.
4164
+ registerTerminalUpdateOffer(pi, { installed: installedPlatformVersion(), packageRoot: PLATFORM_ROOT, restart: "pi" });
4162
4165
  registerSessionCommands(pi, { ...registrationDeps, ...contextCommandApi });
4163
4166
  }
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
3
4
 
4
5
  const INSTRUCTION_FILES = ['AGENTS.override.md', 'AGENTS.md', 'AGENTS.MD', 'CLAUDE.md', 'CLAUDE.MD'];
5
6
  // Project instructions from the project folder up to its repository root, as
@@ -24,8 +25,7 @@ export function projectInstructions(cwd, top = cwd) {
24
25
  // Managed sessions never consult the personal trust store or execute resources
25
26
  // discovered in a project. Skills and commands are text (agent-skills.mjs):
26
27
  // what they ask for runs with the session's own tools.
27
- export function managedResourceLoader(api, { systemPrompt, extensions = [], agentsFiles = [], skills = [], prompts = [] } = {}) {
28
- const runtime = api.createExtensionRuntime();
28
+ export function managedResourceLoader(api, { systemPrompt, extensions = [], runtime = api.createExtensionRuntime(), agentsFiles = [], skills = [], prompts = [] } = {}) {
29
29
  const empty = Object.freeze([]);
30
30
  return Object.freeze({
31
31
  getExtensions: () => ({ extensions, errors: empty, runtime }),
@@ -41,3 +41,15 @@ export function managedResourceLoader(api, { systemPrompt, extensions = [], agen
41
41
  reload: async () => {},
42
42
  });
43
43
  }
44
+
45
+ // Extensions the company Terminal adds itself (its commands and how Harness
46
+ // notes read), built the way Pi builds an inline extension. Never project code.
47
+ export async function inlineExtensions(api, sdkRoot, cwd, factories = []) {
48
+ const runtime = api.createExtensionRuntime();
49
+ if (!factories.length) return { extensions: [], runtime };
50
+ const load = (file) => import(pathToFileURL(path.join(sdkRoot, 'dist', ...file.split('/'))).href);
51
+ const [{ loadExtensionFromFactory }, { createEventBus }] = await Promise.all([load('core/extensions/loader.js'), load('core/event-bus.js')]);
52
+ const bus = createEventBus(), extensions = [];
53
+ for (const [index, factory] of factories.entries()) extensions.push(await loadExtensionFromFactory(factory, cwd, bus, runtime, `<inline:company-terminal-${index + 1}>`));
54
+ return { extensions, runtime };
55
+ }
@@ -5,7 +5,7 @@ import { randomUUID, createHash } from 'node:crypto';
5
5
  import { pathToFileURL, fileURLToPath } from 'node:url';
6
6
  import { ManagedToolBoundary } from './tool-boundary.mjs';
7
7
  import { ensureSearchTools, trustSystemCertificates } from './toolchain.mjs';
8
- import { managedResourceLoader, projectInstructions } from './resource-loader.mjs';
8
+ import { inlineExtensions, managedResourceLoader, projectInstructions } from './resource-loader.mjs';
9
9
  import { fetchPublicPage, fetchLimits } from './web-fetch.mjs';
10
10
  import { searchForRole } from './web-search.mjs';
11
11
  import { shareGrant, releaseGrant } from './grant-share.mjs';
@@ -84,7 +84,7 @@ export class ManagedSession {
84
84
  // renewBroker/bindingRevision (optional) let a long-lived conversation follow
85
85
  // a key or harness change: Agent Watch rewrites the binding's revision, and
86
86
  // the conversation enrolls again before its next run.
87
- static async create({ sdkRoot, cwd, origin, broker, protectedRoots = [], agentDir, sessionManager, renewBroker = null, bindingRevision = null }) {
87
+ static async create({ sdkRoot, cwd, origin, broker, protectedRoots = [], agentDir, sessionManager, renewBroker = null, bindingRevision = null, terminalExtensions = [] }) {
88
88
  const sdk = fs.realpathSync(sdkRoot);
89
89
  if (JSON.parse(fs.readFileSync(path.join(sdk, 'package.json'))).version !== '0.87.1') throw Error('managed-sdk-version-unqualified');
90
90
  await trustSystemCertificates();
@@ -116,7 +116,7 @@ export class ManagedSession {
116
116
  action:{kind:'external-provider-action',preconditionClass:'runtime-only',toolName:'fetch_origin',rawAction:plan,
117
117
  targetPaths:[self.cwd],provider:'github',urlOrigin:'https://github.com',requestedScope:'fetch-origin-once',
118
118
  reason:`Fetch origin branches from ${plan.repository}`,riskClass:'low',allowConsequence:'Download origin branches once; keep the current branch and working files unchanged.',denyConsequence:'No network request or credential read.'},
119
- terminalConfirm:()=>ctx?.ui?.confirm?.('Fetch origin',`Fetch ${plan.repository} into ${self.cwd}? No working files will be changed.`)??Promise.resolve(false),
119
+ terminalConfirm:()=>ctx?.ui?.confirm?.('Tải nhánh từ origin',`Tải các nhánh của ${plan.repository} về ${self.cwd} một lần.\n\nĐồng ý: chỉ tải nhánh; nhánh hiện tại và file đang làm không đổi.\nTừ chối: không gửi request mạng, không đọc thông tin đăng nhập.`)??Promise.resolve(false),
120
120
  unavailableFallback:'terminal-confirm',recheck:()=>!signal?.aborted&&Boolean(self.grant)});
121
121
  if(!decision.allowed||!decision.consume()||signal?.aborted)throw Error('managed-operation-denied');
122
122
  return executeRepositoryFetch(self.boundary,plan,signal);
@@ -133,7 +133,7 @@ export class ManagedSession {
133
133
  action:{kind:'external-provider-action',preconditionClass:'runtime-only',toolName:'run_with_network',rawAction:{command:args.command},commandPreview:String(args.command),
134
134
  targetPaths:[self.cwd],provider:'network',urlOrigin:null,requestedScope:'network-command-once',reason:(confirm==='ask'?String(args.reason):`Bypass still asks: ${confirm}. ${args.reason}`).slice(0,300),riskClass:'medium',
135
135
  allowConsequence:'Run this exact command once, with internet access; a server it starts accepts connections while it runs.',denyConsequence:'The command does not run; the agent is told you declined.'},
136
- terminalConfirm:()=>ctx?.ui?.confirm?.('Run with network',`Allow internet access for: ${args.command}`)??Promise.resolve(false),
136
+ terminalConfirm:()=>ctx?.ui?.confirm?.('Chạy lệnh có internet',`${args.command}\n\nLý do: ${args.reason}${confirm==='ask'?'':`\nBypass vẫn hỏi: ${confirm}`}\n\nĐồng ý: chạy đúng lệnh này một lần, có internet; server nó mở nhận kết nối trong lúc chạy.\nTừ chối: lệnh không chạy, agent được báo bạn đã từ chối.`)??Promise.resolve(false),
137
137
  unavailableFallback:'terminal-confirm',recheck:()=>!signal?.aborted&&Boolean(self.grant)});
138
138
  if(!decision.allowed||!decision.consume()||signal?.aborted)throw Error('managed-operation-denied');
139
139
  let ok=false;
@@ -161,7 +161,7 @@ export class ManagedSession {
161
161
  self.checks = repositoryChecks(agentsFiles, self.cwd, self.boundary.repositoryTop);
162
162
  // The Harness workflow (possibly changed on a later enrollment) adds its process to the prompt.
163
163
  const loader = managedResourceLoader(api, { systemPrompt: () => BASE_PROMPT + referencePrompt(self.boundary.userHome) + TEST_PROMPT + webPrompt(helperRoles(manifest)) + helperPrompt(helperRoles(self.manifest)) + workflowPrompt(workflowPolicy(self.manifest), self.checks), agentsFiles,
164
- skills: self.resources.skills, prompts: self.resources.prompts });
164
+ skills: self.resources.skills, prompts: self.resources.prompts, ...await inlineExtensions(api, sdk, self.cwd, terminalExtensions) });
165
165
  const settings = api.SettingsManager.inMemory({ retry: { enabled: false, provider: { maxRetries: 0 } }, cacheWarming: 'off', enableInstallTelemetry: false, enableAnalytics: false, enableSkillCommands: true });
166
166
  const manager = sessionManager ?? api.SessionManager.inMemory(self.cwd);
167
167
  const scope = scopeOf(self.origin, manifest);
@@ -229,6 +229,8 @@ export class ManagedSession {
229
229
  };
230
230
  const prompt = self.session.prompt.bind(self.session);
231
231
  self.session.prompt = async (text, options) => {
232
+ // An extension command (/bypass, /piagent-update) runs here, never as a turn.
233
+ if (/^\/\S/.test(text) && self.session.extensionRunner?.getCommand(text.slice(1).split(/\s/)[0])) return prompt(text, options);
232
234
  // /skill:name, /command: the run sees what the member asked for in full.
233
235
  text = self.resources.expand(text);
234
236
  if (self.starting) await self.starting;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/core",
3
- "version": "1.16.1",
3
+ "version": "1.16.2",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -0,0 +1,7 @@
1
+ export const SNOOZE_MS: number;
2
+ export function readSnooze(home?: string): { version: string | undefined; until: number };
3
+ export function snoozeUpdate(version: string, options?: { home?: string; now?: number }): void;
4
+ export function pendingUpdate(options: { installed: string | undefined; home?: string; now?: number }): string | null;
5
+ export function probeRegistry(options?: { wait?: boolean; spawnImpl?: unknown }): Promise<boolean>;
6
+ export function runPiagentUpdate(options: { packageRoot: string; version: string; home?: string; spawnImpl?: unknown }): Promise<{ ok: boolean; log: string }>;
7
+ export function registerTerminalUpdateOffer(pi: unknown, options: { installed: string | undefined; packageRoot: string; restart: string; home?: string; afterUpdate?: () => string | null; runUpdate?: (options: { packageRoot: string; version: string; home?: string }) => Promise<{ ok: boolean; log: string }>; probe?: (options?: { wait?: boolean }) => Promise<boolean> }): void;
@@ -0,0 +1,114 @@
1
+ // A new Piagent release, said in the Terminal the way the dashboard says it:
2
+ // a question with "Update now" and "Later", asked when the Terminal is idle,
3
+ // and a footer line until the machine is updated. "Later" asks again after an
4
+ // hour, per release. The registry is asked in the background every hour
5
+ // (update-check.js keeps the cache); nothing here waits on the network.
6
+ import fs from "node:fs";
7
+ import os from "node:os";
8
+ import path from "node:path";
9
+ import { spawn } from "node:child_process";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ import { compareReleaseVersions, evaluateUpdateCheck, readUpdateCache, releaseVersion } from "../../extensions/update-check.js";
13
+
14
+ const PROBE_MODULE = fileURLToPath(new URL("../../extensions/update-check.js", import.meta.url));
15
+
16
+ export const SNOOZE_MS = 60 * 60 * 1000;
17
+ const CHECK_EVERY_MS = 10 * 60 * 1000;
18
+ const SNOOZE_FILE = "piagent-update-snooze.json", LOG_FILE = "piagent-update-terminal.log";
19
+ const STATUS_KEY = "piagent-update";
20
+ // The dashboard's own runtime UI: the dashboard asks with its dialog instead.
21
+ const GATEWAY_UI = Symbol.for("piagent.webui.gateway-runtime-ui.v1");
22
+ const UPDATE_NOW = "Cập nhật ngay", LATER = "Để sau (nhắc lại sau 1 giờ)";
23
+
24
+ const stateFile = (home, name) => path.join(home ?? os.homedir(), ".pi", name);
25
+
26
+ export function readSnooze(home) {
27
+ try { const value = JSON.parse(fs.readFileSync(stateFile(home, SNOOZE_FILE), "utf8"));
28
+ return { version: releaseVersion(value?.version), until: Number.isFinite(value?.until) ? value.until : 0 }; }
29
+ catch { return { version: undefined, until: 0 }; }
30
+ }
31
+ export function snoozeUpdate(version, { home, now = Date.now() } = {}) {
32
+ const file = stateFile(home, SNOOZE_FILE);
33
+ fs.mkdirSync(path.dirname(file), { recursive: true });
34
+ fs.writeFileSync(file, `${JSON.stringify({ version, until: now + SNOOZE_MS })}\n`, { mode: 0o600 });
35
+ }
36
+
37
+ // The release to offer now: newer than the installed one and not put off.
38
+ export function pendingUpdate({ installed, home, now = Date.now() }) {
39
+ const latest = releaseVersion(readUpdateCache(home).latest), current = releaseVersion(installed);
40
+ if (!latest || !current || compareReleaseVersions(latest, current) <= 0) return null;
41
+ const snooze = readSnooze(home);
42
+ return snooze.version === latest && now < snooze.until ? null : latest;
43
+ }
44
+
45
+ // The company Terminal runs with a bare PATH: npm sits next to this Node.
46
+ const npmPath = () => [path.dirname(process.execPath), "/usr/local/bin", "/opt/homebrew/bin", process.env.PATH ?? "/usr/bin:/bin"].join(path.delimiter);
47
+
48
+ // Asks the registry (`npm view`) and refreshes the cache; `wait` for an answer
49
+ // the member asked for, otherwise detached so nothing waits on the network.
50
+ export function probeRegistry({ wait = false, spawnImpl = spawn } = {}) {
51
+ return new Promise((resolve) => {
52
+ let child;
53
+ try { child = spawnImpl(process.execPath, [PROBE_MODULE, "--probe"], { detached: !wait, stdio: "ignore", windowsHide: true, env: { ...process.env, PATH: npmPath() } }); }
54
+ catch { resolve(false); return; }
55
+ if (!wait) { child.unref?.(); resolve(true); return; }
56
+ const timer = setTimeout(() => { child.kill?.(); resolve(false); }, 20_000);
57
+ child.once("error", () => { clearTimeout(timer); resolve(false); });
58
+ child.once("exit", (code) => { clearTimeout(timer); resolve(code === 0); });
59
+ });
60
+ }
61
+
62
+ // `piagent-update --version X`, as the dashboard's update runs it.
63
+ export function runPiagentUpdate({ packageRoot, version, home, spawnImpl = spawn }) {
64
+ const log = stateFile(home, LOG_FILE);
65
+ fs.mkdirSync(path.dirname(log), { recursive: true });
66
+ const out = fs.openSync(log, "w", 0o600);
67
+ const PATH = npmPath();
68
+ return new Promise((resolve) => {
69
+ let child;
70
+ try {
71
+ child = spawnImpl(process.execPath, [path.join(packageRoot, "scripts", "update-global.mjs"), "--version", version],
72
+ { stdio: ["ignore", out, out], env: { ...process.env, PATH, PIAGENT_NO_UPDATE_CHECK: "1" }, windowsHide: true });
73
+ } catch { fs.closeSync(out); resolve({ ok: false, log }); return; }
74
+ child.once("error", () => { fs.closeSync(out); resolve({ ok: false, log }); });
75
+ child.once("exit", (code) => { fs.closeSync(out); resolve({ ok: code === 0, log }); });
76
+ });
77
+ }
78
+
79
+ // `restart`: what the member runs to use the new release (`pi`, `piagent studio`).
80
+ export function registerTerminalUpdateOffer(pi, { installed, packageRoot, restart, home, afterUpdate = () => null, runUpdate = runPiagentUpdate, probe: askRegistry = probeRegistry }) {
81
+ if (!releaseVersion(installed) || process.env.PIAGENT_NO_UPDATE_CHECK?.trim()) return;
82
+ let asking = false, ui = null, updated = null;
83
+ const probe = () => { if (evaluateUpdateCheck({ installed, cache: readUpdateCache(home), now: Date.now() }).probe) void askRegistry(); };
84
+ const status = (version) => ui?.setStatus?.(STATUS_KEY, version ? `⬆ Piagent ${version} · /piagent-update` : undefined);
85
+ const update = async (ctx, version) => {
86
+ ctx.ui.notify(`Đang cập nhật Piagent ${installed} → ${version}…`, "info");
87
+ const result = await runUpdate({ packageRoot, version, home });
88
+ if (!result.ok) { ctx.ui.notify(`Chưa cập nhật được Piagent. Chi tiết trong ${result.log}. Có thể chạy \`piagent-update\` trong terminal.`, "error"); return; }
89
+ updated = version; status(undefined);
90
+ const extra = afterUpdate();
91
+ ctx.ui.notify(`Đã cập nhật Piagent ${installed} → ${version}. Thoát rồi mở lại \`${restart}\` để dùng bản mới.${extra ? ` ${extra}` : ""}`, "info");
92
+ };
93
+ const offer = async (ctx, { force = false } = {}) => {
94
+ if (asking || updated || !ctx?.hasUI || ctx.ui?.[GATEWAY_UI] || !ctx.ui?.select) return;
95
+ ui = ctx.ui;
96
+ const version = force ? releaseVersion(readUpdateCache(home).latest) : pendingUpdate({ installed, home });
97
+ const newer = version && compareReleaseVersions(version, installed) > 0;
98
+ status(newer ? version : undefined);
99
+ if (!newer) { if (force) ctx.ui.notify(`Piagent ${installed} là bản mới nhất.`, "info"); return; }
100
+ asking = true;
101
+ try {
102
+ const choice = await ctx.ui.select(`Có bản Piagent mới: ${version} (đang dùng ${installed})`, [UPDATE_NOW, LATER]);
103
+ if (choice === UPDATE_NOW) await update(ctx, version);
104
+ else snoozeUpdate(version, { home });
105
+ } finally { asking = false; }
106
+ };
107
+ pi.on("session_start", async (_event, ctx) => { probe(); await offer(ctx); });
108
+ // Only between turns: a question never interrupts the agent.
109
+ pi.on("agent_end", async (_event, ctx) => { probe(); await offer(ctx); });
110
+ const timer = setInterval(probe, CHECK_EVERY_MS); timer.unref?.();
111
+ pi.on("session_shutdown", () => clearInterval(timer));
112
+ pi.registerCommand("piagent-update", { description: "Kiểm tra và cập nhật Piagent lên bản mới nhất",
113
+ handler: async (_args, ctx) => { ctx.ui.notify("Đang kiểm tra bản Piagent mới…", "info"); await askRegistry({ wait: true }); await offer(ctx, { force: true }); } });
114
+ }