@piagent/platform 1.26.0 → 1.27.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 (64) hide show
  1. package/CHANGELOG.md +26 -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 +2 -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 +12 -0
  16. package/docs-site/content/index.html +4 -4
  17. package/docs-site/content/whats-new.html +12 -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 +13 -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 +13 -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/managed/helper-run.mjs +4 -3
  58. package/packages/piagent-core/managed/memory.mjs +221 -0
  59. package/packages/piagent-core/managed/session.mjs +5 -5
  60. package/packages/piagent-core/package.json +1 -1
  61. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-DcFq8SRy.js → MarkdownMessage-BpNzOP6f.js} +1 -1
  62. package/packages/piagent-webui/dist/client/assets/{index-B4PFrM4y.js → index-DWkb1mpZ.js} +3 -3
  63. package/packages/piagent-webui/dist/client/index.html +1 -1
  64. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +2 -2
@@ -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.26.0 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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,18 @@
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.27.0 · 2026-10-17</p>
109
+ <h2 id="v1-27-0">Team memory: company agents remember what the team settled in each repository</h2>
110
+ <p class="section-lead">With team memory turned on in Agent Studio 0.27.0, a new company conversation opens with a short brief from the team's memory of the repository (decisions, rules, work in progress from earlier conversations and commits). Every round is kept for the team (secrets redacted); the agent and its helpers search it with <span class="inline-code">memory_search</span>. A memory outage or a team without memory never fails or slows a turn.</p>
111
+ </section>
112
+
113
+ <section class="section">
114
+ <p class="section-kicker">v1.26.1 · 2026-10-16</p>
115
+ <h2 id="v1-26-1">A conversation is renamed while its turn runs</h2>
116
+ <p class="section-lead">Renaming a conversation from its menu while a turn ran (a company conversation in particular) showed <span class="inline-code">session-revision-stale</span> and kept the old name. The new name now lands mid-turn; when a rename, fork or archive still fails, the dialog says why in words.</p>
117
+ </section>
118
+
107
119
  <section class="section">
108
120
  <p class="section-kicker">v1.26.0 · 2026-10-15</p>
109
121
  <h2 id="v1-26-0">Dashboard / commands and shortcuts, /undo, dev servers across turns</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.26.0 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · EN</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0</p>
97
+ <p class="eyebrow">Piagent 1.27.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.26.0</a>
111
+ <a class="button" href="/whats-new">Có gì mới trong 1.27.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.26.0</span>
187
+ <span class="card-kicker">Bản 1.27.0</span>
188
188
  <h3>Có gì mới</h3>
189
- <p>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>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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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,18 @@
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.27.0 · 2026-10-17</p>
109
+ <h2 id="v1-27-0">Team memory: agent công ty nhớ những gì team đã chốt trong từng repo</h2>
110
+ <p class="section-lead">Khi team bật memory trên Agent Studio 0.27.0, cuộc trò chuyện công ty mới mở đầu bằng một bản tóm tắt ngắn từ memory của team về repo (quyết định, quy tắc, việc đang làm, rút từ hội thoại và commit trước đó). Mỗi lượt được lưu lại cho team (đã lọc secret); agent và subagent tìm được trong memory bằng <span class="inline-code">memory_search</span>. Memory lỗi hay tắt không bao giờ làm hỏng hay chậm một lượt.</p>
111
+ </section>
112
+
113
+ <section class="section">
114
+ <p class="section-kicker">v1.26.1 · 2026-10-16</p>
115
+ <h2 id="v1-26-1">Đổi tên cuộc trò chuyện được cả khi lượt đang chạy</h2>
116
+ <p class="section-lead">Đổi tên từ menu của cuộc trò chuyện trong lúc lượt đang chạy (nhất là cuộc trò chuyện công ty) từng hiện <span class="inline-code">session-revision-stale</span> và giữ tên cũ. Giờ tên mới được ghi ngay giữa lượt; nếu đổi tên, tạo nhánh hay lưu trữ vẫn không được, hộp thoại nói lý do bằng lời.</p>
117
+ </section>
118
+
107
119
  <section class="section">
108
120
  <p class="section-kicker">v1.26.0 · 2026-10-15</p>
109
121
  <h2 id="v1-26-0">Lệnh / và phím tắt trên dashboard, /undo, dev server chạy qua nhiều lượt</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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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.26.0 docs · VI</span>
33
+ <span class="pill"><span class="pill-dot"></span>v1.27.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>
@@ -256,8 +256,8 @@
256
256
  },
257
257
  {
258
258
  "path": "tests",
259
- "fileCount": 547,
260
- "sha256": "845f7c2abbea50453be655a8c88710ca49a8f62f2e58d0a02e29ab5c7319f92e"
259
+ "fileCount": 549,
260
+ "sha256": "f3d91ca724e78ae57b53be7433fa432d4b5d13844aab81b5635a39564ca133bf"
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.26.0",
3
+ "version": "1.27.0",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -13,6 +13,7 @@ import { reviewFindings, briefIssues } from './workflow.mjs';
13
13
  import { anchorText, relayObjection, recordVerdicts } from './objections.mjs';
14
14
  import { HELPER_CALLS, HELPER_SETUP, READ_TOOLS, countedCheck } from './helper-roles.mjs';
15
15
  import { trackToolFailures } from './tool-failures.mjs';
16
+ import { helperMemoryTools } from './memory.mjs';
16
17
 
17
18
  const textContent = result => result.content?.filter(p => p.type === 'text').map(p => p.text).join('\n') ?? '';
18
19
  // A helper failure that another model of its role may not have: the model's
@@ -74,7 +75,7 @@ async function helperAttempt(managed, role, task, signal, harness, snapshot, par
74
75
  const model = managed.installModel(runtime, grant); managed.wrapStreams(runtime, role);
75
76
  // Studio's logs name the job by its title (the brief's first line without one).
76
77
  (managed.jobTitles ??= new WeakMap()).set(runtime, jobTitle(task, title));
77
- const setup = HELPER_SETUP[role];
78
+ const setup = HELPER_SETUP[role], memory = helperMemoryTools(managed, () => grant);
78
79
  boundary = new ManagedToolBoundary({ cwd: managed.cwd, sdkRoot: managed.sdk, protectedRoots: managed.protectedRoots, readOnly: true, commands: setup.commands });
79
80
  const checks = role === 'verify' ? `\n\nRepository checks: ${managed.checks.commands.length ? managed.checks.commands.map(c => '`' + c + '`').join(', ') : 'none declared or detected; choose the tests, type check or build that cover the claims'}.` : '';
80
81
  // One helper session answers the brief (with one part of the patch for a
@@ -83,9 +84,9 @@ async function helperAttempt(managed, role, task, signal, harness, snapshot, par
83
84
  const ask = async extra => {
84
85
  const { session } = await managed.api.createAgentSession({ cwd: managed.cwd, model, modelRuntime: runtime, thinkingLevel: grant.effort || 'off',
85
86
  settingsManager: managed.api.SettingsManager.inMemory({ retry: { enabled: false, provider: { maxRetries: 0 } }, cacheWarming: 'off' }),
86
- sessionManager: managed.api.SessionManager.inMemory(managed.cwd), noTools: 'builtin', tools: [...READ_TOOLS, ...(setup.commands ? ['bash'] : []), ...(setup.web ? ['web_search', 'web_fetch'] : [])],
87
+ sessionManager: managed.api.SessionManager.inMemory(managed.cwd), noTools: 'builtin', tools: [...READ_TOOLS, ...(setup.commands ? ['bash'] : []), ...(setup.web ? ['web_search', 'web_fetch'] : []), ...memory.map(tool => tool.name)],
87
88
  customTools: [...boundary.tools(managed.api).filter(tool => READ_TOOLS.includes(tool.name) || setup.commands && tool.name === 'bash').map(tool => countedCheck(managed, tool)),
88
- ...(setup.web ? managed.webTools(runtime, role) : [])], resourceLoader: managedResourceLoader(managed.api, { systemPrompt: setup.prompt }) });
89
+ ...(setup.web ? managed.webTools(runtime, role) : []), ...memory], resourceLoader: managedResourceLoader(managed.api, { systemPrompt: setup.prompt }) });
89
90
  sessions.add(session); trackToolFailures(managed, session, role);
90
91
  const abort = () => void session.abort(); signal?.addEventListener('abort', abort, { once: true });
91
92
  try { if (signal?.aborted) throw Error('managed-helper-cancelled'); await session.prompt(anchorText(managed.session.sessionManager, harness) + task + checks + extra, { expandPromptTemplates: false }); }
@@ -0,0 +1,221 @@
1
+ // Team memory for company conversations (Studio docs/team-memory/PLAN.md):
2
+ // Studio keeps one memory per team and repository. On a conversation's first
3
+ // round the agent gets a brief of what the team's earlier conversations and
4
+ // commits say about the member's request; every round is sent back to grow
5
+ // it; new commits are sent when HEAD moved. Everything goes through Studio on
6
+ // the round's run token, and nothing here may fail or slow a turn: every call
7
+ // is bounded, a failed write waits in a small outbox for the next round, and
8
+ // Studio's 204 (memory off for the team) turns memory off for a while.
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
+ import { execFile } from 'node:child_process';
12
+ import { CLIENT_AGENT } from './client-agent.mjs';
13
+ import { redactedText } from '../runtime/managed-failure.mjs';
14
+
15
+ export const MEMORY_ENTRY = 'agent-watch-memory';
16
+ const BRIEF_MS = 5_000, WRITE_MS = 4_000, GIT_MS = 6_000, TOOL_MS = 10_000;
17
+ const OFF_MS = 30 * 60_000, KNOWN_MS = 7 * 24 * 3600_000;
18
+ const MAX_TURN = 30 * 1024, MAX_OUTBOX = 2 * 1024 * 1024, MAX_COMMITS = 300, MAX_GIT_BODY = 60 * 1024, MAX_COMMIT_BODY = 2_000;
19
+
20
+ // The repository's identity: its origin remote without scheme, user, port
21
+ // or ".git", lower case ("github.com/acme/shop"). null: no memory here.
22
+ export function repoKey(remote) {
23
+ let s = String(remote ?? '').trim();
24
+ if (!s) return null;
25
+ const scp = /^[\w.-]+@([^:/]+):(.+)$/.exec(s); // git@github.com:acme/shop.git
26
+ if (scp) s = `${scp[1]}/${scp[2]}`;
27
+ else {
28
+ try { const u = new URL(s); if (!['http:', 'https:', 'ssh:', 'git:'].includes(u.protocol)) return null; s = `${u.hostname}${u.pathname}`; }
29
+ catch { return null; }
30
+ }
31
+ s = s.toLowerCase().replace(/\.git\/?$/, '').replace(/\/+$/, '').replace(/\/{2,}/g, '/');
32
+ return /^[a-z0-9][a-z0-9._-]*\/[a-z0-9._/-]+$/.test(s) && s.length <= 200 && !s.includes('..') ? s : null;
33
+ }
34
+
35
+ const git = (cwd, args, timeout = 3_000) => new Promise(resolve => execFile('git', ['-C', cwd, ...args], { timeout, maxBuffer: 4 << 20, encoding: 'utf8' },
36
+ (error, stdout) => resolve(error ? null : stdout)));
37
+
38
+ const textOf = content => typeof content === 'string' ? content : Array.isArray(content) ? content.filter(p => p?.type === 'text').map(p => p.text).join('\n') : '';
39
+
40
+ // One round as memory keeps it: what the member asked, the agent's final
41
+ // answer and the files the conversation changed. Tool calls and their output
42
+ // are left out.
43
+ export function turnRecord(messages, changedFiles = []) {
44
+ const asked = messages.filter(m => m.role === 'user').map(m => textOf(m.content).trim()).filter(Boolean);
45
+ const answer = textOf(messages.findLast(m => m.role === 'assistant' && textOf(m.content).trim())?.content).trim();
46
+ if (!asked.length && !answer) return '';
47
+ const parts = [];
48
+ for (const a of asked) parts.push(`Member: ${a}`);
49
+ if (answer) parts.push(`${asked.length ? 'Agent' : 'Agent (continuing)'}: ${answer}`);
50
+ const files = [...changedFiles].slice(0, 40);
51
+ if (files.length) parts.push(`Files changed in this conversation: ${files.join(', ')}`);
52
+ // Studio redacts again; its own keys are not in the general patterns.
53
+ let text = redactedText(parts.join('\n\n')).replace(/\bas_(?:live|run)_[A-Za-z0-9_-]{8,}/g, '[REDACTED_SECRET]');
54
+ return Buffer.byteLength(text) > MAX_TURN ? Buffer.from(text).subarray(0, MAX_TURN - 3).toString('utf8').replace(/�*$/, '') + '…' : text;
55
+ }
56
+
57
+ export class TeamMemory {
58
+ constructor({ origin, cwd, stateDir, conversationId, fetchImpl = fetch, now = () => Date.now() }) {
59
+ Object.assign(this, { origin, cwd, stateDir, conversationId, fetchImpl, now, repo: undefined, offUntil: 0, retain: true, pendingGit: null, briefed: false });
60
+ this.outboxPath = stateDir ? path.join(stateDir, 'memory-outbox.jsonl') : null;
61
+ this.knownPath = stateDir ? path.join(stateDir, 'memory-known.json') : null;
62
+ }
63
+
64
+ // Memory answered for this Studio recently: the conversation gets the
65
+ // memory tools (their schema is fixed when it opens).
66
+ static known(stateDir, origin, now = Date.now()) {
67
+ try { const k = JSON.parse(fs.readFileSync(path.join(stateDir, 'memory-known.json'), 'utf8')); return k.origin === origin && now - k.at < KNOWN_MS; }
68
+ catch { return false; }
69
+ }
70
+ #remember(on) {
71
+ if (!this.knownPath) return;
72
+ try { if (on) fs.writeFileSync(this.knownPath, JSON.stringify({ origin: this.origin, at: this.now() })); else fs.rmSync(this.knownPath, { force: true }); } catch { /* best effort */ }
73
+ }
74
+
75
+ async repository() {
76
+ if (this.repo === undefined) this.repo = repoKey(await git(this.cwd, ['remote', 'get-url', 'origin']));
77
+ return this.repo;
78
+ }
79
+ get active() { return this.now() >= this.offUntil; }
80
+
81
+ async #post(grant, op, body, timeout) {
82
+ const response = await this.fetchImpl(`${this.origin}/v1/memory/${op}`, { method: 'POST', signal: AbortSignal.timeout(timeout),
83
+ headers: { Authorization: `Bearer ${grant.token}`, 'Content-Type': 'application/json', 'User-Agent': CLIENT_AGENT, 'X-Session-Id': grant.role_id },
84
+ body: JSON.stringify(body) });
85
+ // 204: memory is off for the team; 404: a Studio without team memory.
86
+ if (response.status === 204 || response.status === 404) { this.offUntil = this.now() + OFF_MS; this.#remember(false); return null; }
87
+ if (!response.ok) throw Error(`memory-${op}-${response.status}`);
88
+ this.#remember(true);
89
+ return response.json();
90
+ }
91
+
92
+ // The round's start: send what waited, and on a conversation's first
93
+ // round the brief (returned for the caller to add to the conversation).
94
+ async beforeRound(grant, prompt, hasBrief) {
95
+ if (!grant || !this.active || !(await this.repository())) return null;
96
+ await this.#drain(grant);
97
+ if (hasBrief || this.briefed || !this.active) return null;
98
+ this.briefed = true;
99
+ let brief;
100
+ try { brief = await this.#post(grant, 'brief', { repo_key: this.repo, prompt: String(prompt ?? '').slice(0, 2000) }, BRIEF_MS); }
101
+ catch { return null; }
102
+ if (!brief) return null;
103
+ this.retain = brief.retain_sessions !== false;
104
+ this.pendingGit = this.#sendGit(grant, brief.git_head ?? null).catch(() => {});
105
+ return { text: typeof brief.text === 'string' ? brief.text : '', facts: brief.facts ?? 0, pages: Array.isArray(brief.pages) ? brief.pages : [] };
106
+ }
107
+
108
+ async #sendGit(grant, known) {
109
+ const head = (await git(this.cwd, ['rev-parse', 'HEAD']))?.trim();
110
+ if (!/^[0-9a-f]{40}$/.test(head ?? '') || head === known) return;
111
+ const range = known && (await git(this.cwd, ['merge-base', '--is-ancestor', known, head]).then(out => out !== null)) ? [`${known}..${head}`] : [head];
112
+ const log = await git(this.cwd, ['log', `-n${MAX_COMMITS}`, '--no-merges', '--format=%H%x1f%cI%x1f%s%x1f%b%x1e', ...range], 5_000);
113
+ if (!log) return;
114
+ const commits = log.split('\x1e').map(r => r.trim()).filter(Boolean).map(r => { const [sha, date, subject, body = ''] = r.split('\x1f'); return { sha, date, subject, body: body.trim().slice(0, MAX_COMMIT_BODY) }; })
115
+ .filter(c => /^[0-9a-f]{40}$/.test(c.sha) && c.subject?.trim() && c.date);
116
+ if (!commits.length) return;
117
+ // Oldest first, in calls under the body cap; the head is recorded with the last.
118
+ commits.reverse();
119
+ for (let i = 0; i < commits.length;) {
120
+ const batch = []; let bytes = 0;
121
+ while (i < commits.length && (bytes += Buffer.byteLength(JSON.stringify(commits[i]))) < MAX_GIT_BODY) batch.push(commits[i++]);
122
+ if (!batch.length) batch.push(commits[i++]);
123
+ const last = i >= commits.length;
124
+ if (!(await this.#post(grant, 'git', { repo_key: this.repo, head: last ? head : batch.at(-1).sha, commits: batch }, GIT_MS))) return;
125
+ }
126
+ }
127
+
128
+ // The round's end, before its run closes: the round goes to memory, or to
129
+ // the outbox when Studio did not take it.
130
+ async afterRound(grant, messages, changedFiles) {
131
+ if (this.pendingGit) { await this.pendingGit; this.pendingGit = null; }
132
+ if (!grant || !this.active || !this.retain || !(await this.repository())) return;
133
+ const text = turnRecord(messages, changedFiles);
134
+ if (!text) return;
135
+ const item = { repo_key: this.repo, conversation_id: this.conversationId, text };
136
+ try { await this.#post(grant, 'retain', item, WRITE_MS); }
137
+ catch { this.#queue(item); }
138
+ }
139
+
140
+ #queue(item) {
141
+ if (!this.outboxPath) return;
142
+ try {
143
+ const size = fs.existsSync(this.outboxPath) ? fs.statSync(this.outboxPath).size : 0;
144
+ if (size + Buffer.byteLength(JSON.stringify(item)) > MAX_OUTBOX) return;
145
+ fs.mkdirSync(path.dirname(this.outboxPath), { recursive: true });
146
+ fs.appendFileSync(this.outboxPath, JSON.stringify(item) + '\n', { mode: 0o600 });
147
+ } catch { /* memory is optional */ }
148
+ }
149
+
150
+ async #drain(grant) {
151
+ if (!this.outboxPath || !fs.existsSync(this.outboxPath)) return;
152
+ let items;
153
+ try { items = fs.readFileSync(this.outboxPath, 'utf8').split('\n').filter(Boolean).map(l => JSON.parse(l)); fs.rmSync(this.outboxPath, { force: true }); }
154
+ catch { return; }
155
+ const left = [];
156
+ for (const [i, item] of items.entries()) {
157
+ if (i >= 5 || !this.active) { left.push(item); continue; }
158
+ try { await this.#post(grant, 'retain', item, WRITE_MS); } catch { left.push(item); }
159
+ }
160
+ for (const item of left) if (this.active) this.#queue(item);
161
+ }
162
+
163
+ async search(grant, query) {
164
+ if (!grant || !(await this.repository())) return 'Team memory is not available for this folder (no origin remote).';
165
+ try { return (await this.#post(grant, 'search', { repo_key: this.repo, query }, TOOL_MS))?.text ?? 'Team memory is off for this team.'; }
166
+ catch { return 'Team memory did not answer; go on without it.'; }
167
+ }
168
+ async page(grant, name) {
169
+ if (!grant || !(await this.repository())) return 'Team memory is not available for this folder (no origin remote).';
170
+ try {
171
+ const page = await this.#post(grant, 'page', { repo_key: this.repo, name }, TOOL_MS);
172
+ return page ? (page.markdown?.trim() || `The page "${name}" has no content yet.`) : 'Team memory is off for this team.';
173
+ } catch { return `No knowledge page named "${name}", or team memory did not answer.`; }
174
+ }
175
+ }
176
+
177
+ export const PAGE_NAMES = ['Component map', 'Core concepts', 'Conventions and patterns', 'Key decisions and rationale', 'Initiatives and work in progress'];
178
+
179
+ // The agent's memory tools; helpers get only the search, on their own run.
180
+ export function memoryTools(self, { pages = true, grant = () => self.grant } = {}) {
181
+ const tools = [{ name: 'memory_search', label: 'Team memory',
182
+ description: 'Search the team memory of this repository: decisions, rules and conventions from the team\'s earlier conversations and commits, and its knowledge pages. Use it before asking the member something the team may already have settled. Memory is a record of the past: check what it says against the code.',
183
+ parameters: { type: 'object', properties: { query: { type: 'string', minLength: 1, maxLength: 2000 } }, required: ['query'], additionalProperties: false },
184
+ execute: async (_id, args) => ({ content: [{ type: 'text', text: await self.memory.search(grant(), args.query) }], details: {} }) }];
185
+ if (pages) tools.push({ name: 'memory_page', label: 'Team knowledge page',
186
+ description: `Read one knowledge page the team memory keeps for this repository: ${PAGE_NAMES.join(', ')}.`,
187
+ parameters: { type: 'object', properties: { name: { type: 'string', enum: PAGE_NAMES } }, required: ['name'], additionalProperties: false },
188
+ execute: async (_id, args) => ({ content: [{ type: 'text', text: await self.memory.page(grant(), args.name) }], details: {} }) });
189
+ return tools;
190
+ }
191
+
192
+ // The brief as the conversation keeps it: a custom message the member does
193
+ // not see; the model reads it as context before the member's message.
194
+ export function briefMessage(brief) {
195
+ const pages = brief.pages.length ? `\n\nKnowledge pages (read with memory_page): ${brief.pages.join(', ')}.` : '';
196
+ return { customType: MEMORY_ENTRY, content: brief.text + pages, display: false, details: { facts: brief.facts } };
197
+ }
198
+ export const hasBrief = manager => manager.getEntries().some(e => (e.type === 'custom_message' || e.type === 'custom') && e.customType === MEMORY_ENTRY);
199
+
200
+ // The managed session's three hooks (session.mjs): the memory and its tools
201
+ // when the conversation opens, a round's start once its run is granted, and
202
+ // a round's end while its run token is still valid. None can fail a turn.
203
+ export function attachMemory(self, manager, agentDir) {
204
+ self.memory = new TeamMemory({ origin: self.origin, cwd: self.cwd, stateDir: agentDir ?? null, conversationId: manager.getSessionId() });
205
+ return self.memory;
206
+ }
207
+ export const memoryToolsFor = (self, agentDir) => agentDir && TeamMemory.known(agentDir, self.origin) ? memoryTools(self) : [];
208
+ // A helper (scout, research, verify, review) reads the team memory with its own run token; it never writes.
209
+ export const helperMemoryTools = (managed, grant) => managed.memory?.stateDir && TeamMemory.known(managed.memory.stateDir, managed.origin)
210
+ ? memoryTools(managed, { pages: false, grant }) : [];
211
+ export async function openRound(self, manager, text) {
212
+ self.roundStart = self.session.messages.length;
213
+ try {
214
+ const brief = await self.memory?.beforeRound(self.grant, text, hasBrief(manager));
215
+ if (brief?.text) await self.session.sendCustomMessage(briefMessage(brief), { triggerTurn: false });
216
+ } catch { /* memory is optional */ }
217
+ }
218
+ export async function closeRound(self) {
219
+ if (!self.grant || self.preflight || !self.memory) return;
220
+ try { await self.memory.afterRound(self.grant, self.session.messages.slice(self.roundStart ?? 0), self.ownPaths); } catch { /* memory is optional */ }
221
+ }
@@ -27,7 +27,7 @@ import { beforeDelegate } from './objections.mjs';
27
27
  import { HELPER_CALLS, HELPER_ROLES, helperRoles, helperPrompt, webPrompt, delegateDescription } from './helper-roles.mjs';
28
28
  import { loadAgentResources } from './agent-skills.mjs';
29
29
  import { restorePermission, permissionSetter } from './permission.mjs';
30
- import { commandTools, servicesPrompt } from './command-tools.mjs';
30
+ import { commandTools, servicesPrompt } from './command-tools.mjs'; import { attachMemory, memoryToolsFor, openRound, closeRound } from './memory.mjs';
31
31
 
32
32
  const PROVIDER = 'agent_watch_managed';
33
33
  export const HELPER_BEAT_MS = 60_000;
@@ -132,14 +132,14 @@ export class ManagedSession {
132
132
  return { content: [{ type: 'text', text: `Plan updated: ${plan.plan.filter(p => p.status === 'completed').length}/${plan.plan.length} steps completed.` }], details: { plan: plan.plan } };
133
133
  }));
134
134
  // The member decides what the request leaves open: numbered options, or their own words.
135
- customTools.push(askTool(self));
135
+ customTools.push(askTool(self), ...memoryToolsFor(self, agentDir)); // team memory's tools, once Studio answered memory here (memory.mjs)
136
136
  const agentsFiles = projectInstructions(self.cwd, self.boundary.repositoryTop);
137
137
  self.checks = repositoryChecks(agentsFiles, self.cwd, self.boundary.repositoryTop);
138
138
  // The Harness workflow (possibly changed on a later enrollment) adds its process to the prompt.
139
139
  const loader = managedResourceLoader(api, { systemPrompt: () => BASE_PROMPT + referencePrompt(self.boundary.userHome) + TEST_PROMPT + servicesPrompt(self.boundary) + webPrompt(helperRoles(manifest)) + helperPrompt(helperRoles(self.manifest)) + workflowPrompt(workflowPolicy(self.manifest), self.checks), agentsFiles,
140
140
  skills: self.resources.skills, prompts: self.resources.prompts, ...await inlineExtensions(api, sdk, self.cwd, terminalExtensions) });
141
141
  const settings = api.SettingsManager.inMemory({ retry: { enabled: false, provider: { maxRetries: 0 } }, cacheWarming: 'off', enableInstallTelemetry: false, enableAnalytics: false, enableSkillCommands: true });
142
- const manager = sessionManager ?? api.SessionManager.inMemory(self.cwd);
142
+ const manager = sessionManager ?? api.SessionManager.inMemory(self.cwd); attachMemory(self, manager, agentDir);
143
143
  const scope = scopeOf(self.origin, manifest);
144
144
  const prior = manager.getEntries().filter(e => e.type === 'custom' && e.customType === 'agent-watch-scope').at(-1)?.data;
145
145
  // Under another member or another Studio a conversation still opens and
@@ -263,7 +263,7 @@ export class ManagedSession {
263
263
  const pending = pendingBaseline(manager);
264
264
  self.ownPaths = new Set(pending !== null ? pendingPaths(manager) : []);
265
265
  self.base = (pending !== null ? pendingHead(manager) : null) ?? await readHead(self.boundary);
266
- self.run.startDigest = pending ?? await self.digest();
266
+ self.run.startDigest = pending ?? await self.digest(); await openRound(self, manager, text); // sends what waited; a new conversation gets the team brief
267
267
  } catch (error) { self.preflight = describeFailure('main', failureText(error)); }
268
268
  self.activePrompt = true;
269
269
  })();
@@ -286,7 +286,7 @@ export class ManagedSession {
286
286
  // The round's model work is over: a member message now waits for the next round.
287
287
  self.between = true;
288
288
  Object.assign(round, { planUpdated: !!self.run?.planUpdated, changed: !!self.run?.changed });
289
- await Promise.allSettled([...self.helpers.values()]);
289
+ await Promise.allSettled([...self.helpers.values()]); await closeRound(self); // the round goes to team memory while its run token is valid
290
290
  // What a command left running in the background ends with the turn.
291
291
  try { await self.boundary.stopStrays(); } catch { /* best effort; the boundary stops them on close */ }
292
292
  await self.refreshReview();