@piagent/platform 1.33.0 → 1.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/docs/command-reference-vietnamese.md +2 -2
- package/docs/distribution-standard.md +6 -6
- package/docs/languages.json +2 -2
- package/docs/operator-manual-vietnamese.md +7 -7
- package/docs/publishing-for-teams.md +1 -0
- package/docs/release-install-policy.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/content/en/index.html +4 -4
- package/docs-site/content/en/whats-new.html +6 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +6 -0
- package/docs-site/context-engine.html +1 -1
- package/docs-site/ecosystem.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/ecosystem.html +1 -1
- package/docs-site/en/index.html +5 -5
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +1 -1
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +7 -1
- package/docs-site/en/windows.html +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +5 -5
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +1 -1
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +7 -1
- package/docs-site/windows.html +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
- package/package.json +1 -1
- package/packages/piagent-core/managed/helper-run.mjs +13 -5
- package/packages/piagent-core/managed/helper-settlement.mjs +92 -0
- package/packages/piagent-core/managed/memory.mjs +9 -9
- package/packages/piagent-core/managed/session.mjs +4 -4
- package/packages/piagent-core/managed/studio-capabilities.mjs +60 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-dCzZ2b6f.js → MarkdownMessage-CfgyQQbW.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-HyzWKvDu.js +234 -0
- package/packages/piagent-webui/dist/client/assets/{index-B5XOKQy-.css → index-_0nGP2n3.css} +1 -1
- package/packages/piagent-webui/dist/client/index.html +2 -2
- package/packages/piagent-webui/gateway/agent-status-files.ts +77 -0
- package/packages/piagent-webui/gateway/gateway-service.ts +5 -1
- package/packages/piagent-webui/gateway/managed-projection.ts +7 -1
- package/packages/piagent-webui/gateway/session-catalog.ts +3 -2
- package/schemas/piagent-webui/session-catalog-v1.schema.json +5 -0
- package/packages/piagent-webui/dist/client/assets/index-CZ02zZjO.js +0 -232
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/en/team.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.34.1 · 2026-10-20</p>
|
|
109
|
+
<h2 id="v1-34-1">Helpers report how their work ended, Agent Watch tells you when you are needed</h2>
|
|
110
|
+
<p class="section-lead">Every subagent run reports how it ended: done, failed, stopped or unverifiable (for example a verify with no verdicts), and Studio shows it in Logs → Lượt agent. When it hands work to a subagent, the agent can state the target, the change, the constraints, what the subagent may touch and the evidence it needs. Agent Watch (macOS 0.15.9, Windows 0.1.15) tells you when a conversation waits for your approval or your answer, or when the agent is done; the state file holds only the conversation's title and project name. In Source Changes you can comment any line of the diff and send all comments to the agent as one message with each file and line; the review subagent's findings show under the line they are about. Piagent asks Studio what it serves before using a newer feature (Agent Studio 0.34).</p>
|
|
111
|
+
</section>
|
|
112
|
+
|
|
107
113
|
<section class="section">
|
|
108
114
|
<p class="section-kicker">v1.33.0 · 2026-10-20</p>
|
|
109
115
|
<h2 id="v1-33-0">Runs on Pi 1.3.0</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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/index.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.
|
|
97
|
+
<p class="eyebrow">Piagent 1.34.1</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.
|
|
111
|
+
<a class="button" href="/whats-new">Có gì mới trong 1.34.1</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.
|
|
187
|
+
<span class="card-kicker">Bản 1.34.1</span>
|
|
188
188
|
<h3>Có gì mới</h3>
|
|
189
|
-
<p>
|
|
189
|
+
<p>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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/mcp-auth.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/mcp.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/profiles.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/runtime.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/scope.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/security.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/team.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/whats-new.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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.34.1 · 2026-10-20</p>
|
|
109
|
+
<h2 id="v1-34-1">Helper báo kết quả, Agent Watch báo khi cần bạn</h2>
|
|
110
|
+
<p class="section-lead">Mỗi lần subagent chạy xong đều báo rõ kết quả: xong, lỗi, đã dừng hay chưa rõ (ví dụ verify không trả về kết luận), và Studio hiện kết quả đó trong Logs → Lượt agent. Khi giao việc cho subagent, agent có thể ghi rõ phạm vi, việc cần làm, ràng buộc, phần được phép đụng và bằng chứng cần có. Agent Watch (macOS 0.15.9, Windows 0.1.15) báo khi một cuộc trò chuyện đang chờ bạn duyệt, đang hỏi bạn, hoặc khi agent xong việc; file trạng thái chỉ có tên cuộc trò chuyện và tên project. Trong Source Changes, bạn bấm số dòng để comment trên diff rồi gửi tất cả cho agent thành một tin nhắn có đúng file và dòng; kết quả của subagent review hiện ngay dưới dòng nó nói tới. Piagent hỏi Studio những gì nó hỗ trợ trước khi dùng tính năng mới (cần Agent Studio 0.34).</p>
|
|
111
|
+
</section>
|
|
112
|
+
|
|
107
113
|
<section class="section">
|
|
108
114
|
<p class="section-kicker">v1.33.0 · 2026-10-20</p>
|
|
109
115
|
<h2 id="v1-33-0">Chạy trên Pi 1.3.0</h2>
|
package/docs-site/windows.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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>
|
package/docs-site/workflows.html
CHANGED
|
@@ -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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.34.1 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":
|
|
260
|
-
"sha256": "
|
|
259
|
+
"fileCount": 561,
|
|
260
|
+
"sha256": "ceda4bf84ab79c6b4c921c5827917351f38abe0524f76008ad224de1197fdada"
|
|
261
261
|
}
|
|
262
262
|
],
|
|
263
263
|
"custodianComparisonRules": {
|
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ 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
16
|
import { helperMemoryTools, renewer } from './memory.mjs';
|
|
17
|
+
import { answeredSettlement, failedSettlement, settleHelper, OUTCOME_WORDS } from './helper-settlement.mjs';
|
|
17
18
|
|
|
18
19
|
const textContent = result => result.content?.filter(p => p.type === 'text').map(p => p.text).join('\n') ?? '';
|
|
19
20
|
// A helper failure that another model of its role may not have: the model's
|
|
@@ -68,8 +69,8 @@ async function helperAttempt(managed, role, task, signal, harness, snapshot, par
|
|
|
68
69
|
try { verifyGrant(grant, managed.manifest, role); }
|
|
69
70
|
catch (error) { try { await managed.broker.request('close', { role }); } catch { /* closes with the run */ } throw error; }
|
|
70
71
|
} catch (error) { throw Error(`managed-helper-failed: ${describeFailure(role, failureText(error))}`); }
|
|
71
|
-
let boundary;
|
|
72
|
-
const sessions = new Set();
|
|
72
|
+
let boundary, report = null;
|
|
73
|
+
const sessions = new Set(), spec = managed.helperSpecs?.get(role) ?? {};
|
|
73
74
|
try {
|
|
74
75
|
const runtime = await managed.api.ModelRuntime.create({ credentials: new managed.ai.InMemoryCredentialStore(), modelsPath: null, refreshOnCreate: false, allowModelNetwork: false });
|
|
75
76
|
const model = managed.installModel(runtime, grant); managed.wrapStreams(runtime, role);
|
|
@@ -130,10 +131,17 @@ async function helperAttempt(managed, role, task, signal, harness, snapshot, par
|
|
|
130
131
|
managed.session.sessionManager.appendCustomEntry('agent-watch-review', details);
|
|
131
132
|
}
|
|
132
133
|
const verdicts = role === 'verify' ? recordVerdicts(managed.run, reply, harness, details) : '';
|
|
134
|
+
report = answeredSettlement(role, reply, details, spec); details.outcome = report.outcome;
|
|
133
135
|
await managed.session.sendCustomMessage({ customType: 'agent-watch-helper-receipt', display: true,
|
|
134
|
-
content: `${setup.label}: ${usage.toLocaleString('en-US')} tokens${verdicts}${review ? ` · ${parts.parts.length} parts` : ''}${snapshot ? (stale ? ' · review is stale' : ' · patch ' + snapshot.digest.slice(0, 12)) : ''}.`, details }, { triggerTurn: false });
|
|
136
|
+
content: `${setup.label}: ${usage.toLocaleString('en-US')} tokens${verdicts}${review ? ` · ${parts.parts.length} parts` : ''}${snapshot ? (stale ? ' · review is stale' : ' · patch ' + snapshot.digest.slice(0, 12)) : ''} · ${OUTCOME_WORDS[report.outcome]}.`, details }, { triggerTurn: false });
|
|
135
137
|
// An objection to the brief comes with the answer; a stale review has none to act on.
|
|
136
138
|
const objection = stale ? '' : await relayObjection(managed, role, reply, harness);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
const ending = `Outcome: ${report.outcome}${report.outcome === 'unverifiable' ? ` (${report.summary})` : ''}.\n\n`;
|
|
140
|
+
return { content: [{ type: 'text', text: ending + (stale ? 'Review is stale: the patch changed during review. Obtain a new review for the current patch.' : reply + objection) }], details };
|
|
141
|
+
} catch (error) { report = failedSettlement(role, error, signal, spec); throw error; }
|
|
142
|
+
finally {
|
|
143
|
+
for (const session of sessions) session.dispose(); await boundary?.dispose();
|
|
144
|
+
await settleHelper(managed, grant, report); // with the attempt's own token, before its role closes
|
|
145
|
+
await managed.broker.request('close', { role });
|
|
146
|
+
}
|
|
139
147
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// How a helper's attempt ended (Agent Studio 0.34+, POST /v1/managed/settle):
|
|
2
|
+
// one report per attempt, sent with the helper's own run token before its
|
|
3
|
+
// role closes. succeeded: the helper did its job and answered in the form its
|
|
4
|
+
// role needs (verify's verdicts, review's findings); failed: it could not
|
|
5
|
+
// (an error); cancelled: the member or the main agent stopped it;
|
|
6
|
+
// unverifiable: it answered without what its role needs, or its review went
|
|
7
|
+
// stale. Studio reads a helper role that closed without a report as
|
|
8
|
+
// unverifiable: absence is never success or failure.
|
|
9
|
+
import { CLIENT_AGENT } from './client-agent.mjs';
|
|
10
|
+
import { hasCapability } from './studio-capabilities.mjs';
|
|
11
|
+
import { HELPER_ROLES } from './helper-roles.mjs';
|
|
12
|
+
|
|
13
|
+
// The task as the main agent gives it: five optional fields beside the brief.
|
|
14
|
+
export const SPEC_FIELDS = Object.freeze({
|
|
15
|
+
target: 'The files, component or environment in scope.',
|
|
16
|
+
change: 'What the helper should find, check or judge.',
|
|
17
|
+
constraints: 'What must hold, and what is out of bounds.',
|
|
18
|
+
ownership: 'What the helper may read or run, and what is someone else\'s.',
|
|
19
|
+
acceptance: 'The evidence that answers the task (for example file:line facts, a passing check, a cited source).',
|
|
20
|
+
});
|
|
21
|
+
const field = description => ({ type: 'string', maxLength: 1000, description });
|
|
22
|
+
export function delegateParameters() {
|
|
23
|
+
return { type: 'object', properties: { role: { type: 'string', enum: HELPER_ROLES },
|
|
24
|
+
title: { type: 'string', maxLength: 120, description: 'One line naming the job, for the company logs (for example "Find where login tokens are stored").' },
|
|
25
|
+
task: { type: 'string', minLength: 1, maxLength: 12000 },
|
|
26
|
+
...Object.fromEntries(Object.entries(SPEC_FIELDS).map(([name, description]) => [name, field(description)])),
|
|
27
|
+
since: { type: 'string', maxLength: 200, description: 'Review only: the commit, branch or HEAD~N the work to review started from (work committed in earlier turns).' } },
|
|
28
|
+
required: ['role', 'task'], additionalProperties: false };
|
|
29
|
+
}
|
|
30
|
+
export const SPEC_PROMPT = ' Give a helper target and acceptance at least (also change, constraints and ownership when they matter): it answers against them, and its ending is recorded with them.';
|
|
31
|
+
|
|
32
|
+
// The delegate call as the helper reads it: the structured fields head the
|
|
33
|
+
// brief; the main agent's task follows. The spec is kept for the settlement.
|
|
34
|
+
export function delegateArgs(self, args) {
|
|
35
|
+
const spec = Object.fromEntries(Object.keys(SPEC_FIELDS).map(k => [k, typeof args?.[k] === 'string' ? args[k].trim().slice(0, 1000) : '']).filter(([, v]) => v));
|
|
36
|
+
(self.helperSpecs ??= new Map()).set(args?.role, spec);
|
|
37
|
+
if (!Object.keys(spec).length) return args;
|
|
38
|
+
const head = Object.entries(spec).map(([k, v]) => `${k[0].toUpperCase()}${k.slice(1)}: ${v}`).join('\n');
|
|
39
|
+
return { ...args, task: `${head}\n\n${args.task}`.slice(0, 12000) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const jsonBlock = reply => {
|
|
43
|
+
const blocks = [...String(reply).matchAll(/```json\s*([\s\S]*?)```/g)].map(m => m[1]);
|
|
44
|
+
for (const raw of blocks.reverse()) { try { const v = JSON.parse(raw); if (v && typeof v === 'object') return v; } catch { /* next */ } }
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
const clip = (s, n) => { const t = String(s ?? '').replace(/\s+/g, ' ').trim(); return t.length > n ? t.slice(0, n - 1) + '…' : t; };
|
|
48
|
+
const FILE_LINE = /(?:^|[\s`("'])((?:[\w@.-]+\/)*[\w@.-]+\.[A-Za-z0-9]+):(\d+)/g;
|
|
49
|
+
|
|
50
|
+
// The report of an attempt that answered.
|
|
51
|
+
export function answeredSettlement(role, reply, details = {}, spec = {}) {
|
|
52
|
+
const parsed = jsonBlock(reply);
|
|
53
|
+
const prose = String(reply).replace(/```[\s\S]*?```/g, ' ').split('\n').map(l => l.replace(/^[#>*\-\s]+/, '').trim()).find(l => l.length > 0) ?? '';
|
|
54
|
+
const summary = clip(typeof parsed?.summary === 'string' && parsed.summary.trim() ? parsed.summary : prose || `${role} answered.`, 600);
|
|
55
|
+
const evidence = [], files = new Set();
|
|
56
|
+
let outcome = 'succeeded';
|
|
57
|
+
if (role === 'verify') {
|
|
58
|
+
const verdicts = Array.isArray(parsed?.verdicts) ? parsed.verdicts : null;
|
|
59
|
+
if (!verdicts) outcome = 'unverifiable';
|
|
60
|
+
for (const v of verdicts ?? []) evidence.push(clip(`${v?.status ?? '?'}: ${v?.claim ?? ''} — ${v?.evidence ?? ''}`, 300));
|
|
61
|
+
} else if (role === 'review') {
|
|
62
|
+
if (details.stale) outcome = 'unverifiable';
|
|
63
|
+
else if (details.parsed === false) outcome = 'unverifiable';
|
|
64
|
+
for (const f of details.findings ?? []) { evidence.push(clip(`${f.severity} ${f.file ?? ''}:${f.line ?? ''} ${f.issue ?? ''}`, 300)); if (f.file) files.add(String(f.file)); }
|
|
65
|
+
}
|
|
66
|
+
for (const m of String(reply).matchAll(FILE_LINE)) {
|
|
67
|
+
files.add(m[1].replace(/^\.\//, ''));
|
|
68
|
+
if (role !== 'verify' && role !== 'review' && evidence.length < 8) evidence.push(clip(`${m[1]}:${m[2]}`, 300));
|
|
69
|
+
}
|
|
70
|
+
return { outcome, summary: details.stale ? 'The patch changed during review; this review is stale.' : summary,
|
|
71
|
+
evidence: [...new Set(evidence.filter(Boolean))].slice(0, 8), files: [...files].filter(f => f.length <= 300).slice(0, 50), ...(Object.keys(spec ?? {}).length ? { spec } : {}) };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// The report of an attempt that ended without an answer.
|
|
75
|
+
export function failedSettlement(role, error, signal, spec = {}) {
|
|
76
|
+
const cancelled = signal?.aborted || /managed-helper-cancelled/.test(String(error?.message));
|
|
77
|
+
return { outcome: cancelled ? 'cancelled' : 'failed', summary: clip(cancelled ? `The ${role} subagent was stopped.` : `The ${role} subagent failed: ${String(error?.message ?? 'unknown error').replace(/^managed-helper-failed:\s*/, '')}`, 600),
|
|
78
|
+
evidence: [], files: [], ...(Object.keys(spec ?? {}).length ? { spec } : {}) };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Sends the report with the attempt's own token; never fails the helper.
|
|
82
|
+
export async function settleHelper(managed, grant, report, fetchImpl = (...args) => globalThis.fetch(...args)) {
|
|
83
|
+
if (!report || !grant?.token || !hasCapability(managed, 'managed.settle.v1')) return false;
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetchImpl(`${managed.origin}/v1/managed/settle`, { method: 'POST', signal: AbortSignal.timeout(3_000),
|
|
86
|
+
headers: { Authorization: `Bearer ${grant.token}`, 'Content-Type': 'application/json', 'User-Agent': CLIENT_AGENT }, body: JSON.stringify(report) });
|
|
87
|
+
void response.body?.cancel?.().catch(() => {});
|
|
88
|
+
return response.ok;
|
|
89
|
+
} catch { return false; }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const OUTCOME_WORDS = { succeeded: 'done', failed: 'failed', cancelled: 'stopped', unverifiable: 'unverifiable' };
|
|
@@ -7,7 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
import { randomUUID } from 'node:crypto';
|
|
8
8
|
import { MemoryOutbox, memoryScope, scopeID } from './memory-outbox.mjs';
|
|
9
9
|
import { CheckLessons } from './memory-lessons.mjs';
|
|
10
|
-
import { CLIENT_AGENT } from './client-agent.mjs';
|
|
10
|
+
import { CLIENT_AGENT } from './client-agent.mjs'; import { knownCapabilities, refreshCapabilities } from './studio-capabilities.mjs';
|
|
11
11
|
import { redactedText } from '../runtime/managed-failure.mjs';
|
|
12
12
|
import { git, repoKey, repositoryAt, findRepositories, topDirs, eventFor, commitChanges } from './memory-repos.mjs';
|
|
13
13
|
import { PAGE_NAMES, renewer, memoryTools } from './memory-tools.mjs';
|
|
@@ -452,15 +452,15 @@ export function trackMemoryLessons(self) {
|
|
|
452
452
|
// Whether this conversation has team memory now: Studio answered memory for
|
|
453
453
|
// this key recently and has not switched it off since (the Terminal's header).
|
|
454
454
|
export const memoryOn = self => !!self?.memory?.stateDir && self.memory.active && TeamMemory.known(self.memory.stateDir, self.origin, memoryScope(self.origin, self.manifest));
|
|
455
|
-
// memory_ask is offered
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
// memory_search instead (ask() does not turn memory off on a 404).
|
|
459
|
-
const speaksAsk = () => true;
|
|
460
|
-
export const memoryToolsFor = (self, agentDir) => agentDir && TeamMemory.known(agentDir, self.origin, memoryScope(self.origin, self.manifest)) ? memoryTools(self, { ask: speaksAsk() }) : [];
|
|
455
|
+
// memory_ask is offered unless this Studio listed its capabilities without
|
|
456
|
+
// memory.ask.v1 (studio-capabilities.mjs). With no list heard (a Studio older
|
|
457
|
+
// than 0.34) it is offered; on a Studio without /v1/memory/ask the tool says
|
|
458
|
+
// to use memory_search instead (ask() does not turn memory off on a 404).
|
|
459
|
+
const speaksAsk = (self, agentDir) => knownCapabilities(agentDir ?? self.memory?.stateDir, self.origin, self.manifest)?.has('memory.ask.v1') ?? true;
|
|
460
|
+
export const memoryToolsFor = (self, agentDir) => agentDir && TeamMemory.known(agentDir, self.origin, memoryScope(self.origin, self.manifest)) ? memoryTools(self, { ask: speaksAsk(self, agentDir) }) : [];
|
|
461
461
|
// A helper (scout, research, verify, review) reads the team memory with its own run token; it never writes.
|
|
462
462
|
export const helperMemoryTools = (managed, grant, renew) => managed.memory?.stateDir && TeamMemory.known(managed.memory.stateDir, managed.origin, memoryScope(managed.origin, managed.manifest))
|
|
463
|
-
? memoryTools(managed, { pages: false, ask: speaksAsk(), grant, renew }) : [];
|
|
463
|
+
? memoryTools(managed, { pages: false, ask: speaksAsk(managed), grant, renew }) : [];
|
|
464
464
|
export async function openRound(self, manager, text) {
|
|
465
465
|
if (!self.memory) return;
|
|
466
466
|
self.memory.stats = {};
|
|
@@ -475,7 +475,7 @@ export async function openRound(self, manager, text) {
|
|
|
475
475
|
if (fresh && !scopedBrief(messages, self.memory.scopeHash)) {
|
|
476
476
|
await self.session.sendCustomMessage({ customType: MEMORY_ENTRY, content: fresh.content, details: fresh.details, display: false }, { triggerTurn: false });
|
|
477
477
|
}
|
|
478
|
-
self.memory.startBackground(self.grant);
|
|
478
|
+
self.memory.startBackground(self.grant); if (self.memory.stateDir) void refreshCapabilities(self, self.grant, { agentDir: self.memory.stateDir });
|
|
479
479
|
} catch { /* memory is optional */ }
|
|
480
480
|
}
|
|
481
481
|
export async function closeRound(self) {
|
|
@@ -24,7 +24,7 @@ import { readPatchDigest, readPatchSnapshot, readChangedFiles, readHead } from '
|
|
|
24
24
|
import { memberTurn } from './continuation.mjs';
|
|
25
25
|
import { workflowPolicy, repositoryChecks, planTool, currentPlan, PLAN_ENTRY, workflowPrompt, RunProcess, completionGate, pendingBaseline, pendingPaths, pendingHead, processEditTools } from './workflow.mjs';
|
|
26
26
|
import { beforeDelegate } from './objections.mjs';
|
|
27
|
-
import { HELPER_CALLS, HELPER_ROLES, helperRoles, helperPrompt, webPrompt, delegateDescription } from './helper-roles.mjs';
|
|
27
|
+
import { HELPER_CALLS, HELPER_ROLES, helperRoles, helperPrompt, webPrompt, delegateDescription } from './helper-roles.mjs'; import { delegateParameters, delegateArgs, SPEC_PROMPT } from './helper-settlement.mjs';
|
|
28
28
|
import { loadAgentResources } from './agent-skills.mjs';
|
|
29
29
|
import { restorePermission, permissionSetter } from './permission.mjs';
|
|
30
30
|
import { commandTools, servicesPrompt } from './command-tools.mjs'; import { attachMemory, memoryToolsFor, openRound, closeRound, prepareMemoryContext, trackMemoryLessons } from './memory.mjs';
|
|
@@ -123,9 +123,9 @@ export class ManagedSession {
|
|
|
123
123
|
// turn, names the Harness's current helpers; delegate refuses one the
|
|
124
124
|
// Harness does not have.
|
|
125
125
|
const roles = helperRoles(manifest);
|
|
126
|
-
if (roles.length) customTools.push({ name: 'delegate', label: 'Subagent', description: delegateDescription(roles),
|
|
127
|
-
parameters:
|
|
128
|
-
execute: (_id, args, signal) => self.delegate(args, signal) });
|
|
126
|
+
if (roles.length) customTools.push({ name: 'delegate', label: 'Subagent', description: delegateDescription(roles) + SPEC_PROMPT,
|
|
127
|
+
parameters: delegateParameters(), // the brief and its structured fields (helper-settlement.mjs)
|
|
128
|
+
execute: (_id, args, signal) => self.delegate(delegateArgs(self, args), signal) });
|
|
129
129
|
customTools.push(planTool(async plan => {
|
|
130
130
|
self.session.sessionManager.appendCustomEntry(PLAN_ENTRY, { ...plan, at: new Date().toISOString() });
|
|
131
131
|
if (self.run) self.run.planUpdated = true;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CLIENT_AGENT } from './client-agent.mjs';
|
|
4
|
+
import { memoryScope, scopeID } from './memory-outbox.mjs';
|
|
5
|
+
|
|
6
|
+
// What this Studio serves beyond the base API (GET /v1/capabilities, Agent
|
|
7
|
+
// Studio 0.34+): one name per contract and version, such as
|
|
8
|
+
// "requests.cancel.v1" or "memory.v3". Piagent uses a newer operation only
|
|
9
|
+
// when its name is listed. A Studio without the route (404, older than
|
|
10
|
+
// 0.34) has no list: callers keep their old behaviour for it. Names Piagent
|
|
11
|
+
// does not know are ignored; a failed read keeps the last answer this Mac
|
|
12
|
+
// had for the same Studio scope. Asked when a round
|
|
13
|
+
// starts, at most once an hour, kept in <agentDir>/studio-capabilities.json.
|
|
14
|
+
const FRESH_MS = 3_600_000, KEEP_MS = 7 * 24 * 3_600_000, TIMEOUT_MS = 3_000;
|
|
15
|
+
const NAME = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*\.v[0-9]+$/;
|
|
16
|
+
const FILE = 'studio-capabilities.json';
|
|
17
|
+
|
|
18
|
+
function read(stateDir, scope, now) {
|
|
19
|
+
try {
|
|
20
|
+
const k = JSON.parse(fs.readFileSync(path.join(stateDir, FILE), 'utf8'));
|
|
21
|
+
if (k.scope !== scopeID(scope) || !k.scope || (k.names !== null && !Array.isArray(k.names)) || now - k.at > KEEP_MS) return null;
|
|
22
|
+
return k;
|
|
23
|
+
} catch { return null; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// The capabilities last heard from this Studio scope on this Mac, or null
|
|
27
|
+
// when there is no list (none heard, long ago, or a Studio older than 0.34).
|
|
28
|
+
export function knownCapabilities(stateDir, origin, manifest, now = Date.now()) {
|
|
29
|
+
const scope = memoryScope(origin, manifest);
|
|
30
|
+
const k = stateDir && scope ? read(stateDir, scope, now) : null;
|
|
31
|
+
return k?.names ? new Set(k.names) : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const hasCapability = (self, name) => !!self?.capabilities?.has(name);
|
|
35
|
+
|
|
36
|
+
// Refreshes self.capabilities for the round's grant; never fails the round.
|
|
37
|
+
export async function refreshCapabilities(self, grant, { agentDir = self.agentDir, fetchImpl = (...args) => globalThis.fetch(...args), now = Date.now() } = {}) {
|
|
38
|
+
const scope = memoryScope(self.origin, self.manifest);
|
|
39
|
+
const known = agentDir && scope ? read(agentDir, scope, now) : null;
|
|
40
|
+
if (known) self.capabilities = known.names ? new Set(known.names) : null;
|
|
41
|
+
if (known && now - known.at < FRESH_MS) return self.capabilities ?? null;
|
|
42
|
+
if (!grant?.token || !self.origin) return self.capabilities ?? null;
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetchImpl(`${self.origin}/v1/capabilities`, { signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
45
|
+
headers: { Authorization: `Bearer ${grant.token}`, Accept: 'application/json', 'User-Agent': CLIENT_AGENT } });
|
|
46
|
+
let names;
|
|
47
|
+
if (response.status === 404) { void response.body?.cancel?.().catch(() => {}); names = null; }
|
|
48
|
+
else if (response.ok) {
|
|
49
|
+
const answer = await response.json();
|
|
50
|
+
names = Array.isArray(answer?.capabilities) ? answer.capabilities.filter(n => typeof n === 'string' && NAME.test(n)).slice(0, 200) : [];
|
|
51
|
+
} else { void response.body?.cancel?.().catch(() => {}); return self.capabilities ?? null; }
|
|
52
|
+
self.capabilities = names ? new Set(names) : null;
|
|
53
|
+
if (agentDir && scope) {
|
|
54
|
+
const file = path.join(agentDir, FILE), temporary = `${file}.${process.pid}.tmp`;
|
|
55
|
+
fs.writeFileSync(temporary, JSON.stringify({ scope: scopeID(scope), at: now, names }), { mode: 0o600 });
|
|
56
|
+
fs.renameSync(temporary, file);
|
|
57
|
+
}
|
|
58
|
+
} catch { /* the last answer, if any, stays */ }
|
|
59
|
+
return self.capabilities ?? null;
|
|
60
|
+
}
|