@piagent/platform 1.22.1 → 1.23.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.
- package/CHANGELOG.md +13 -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 +8 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +8 -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 +9 -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 +9 -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/fast-mode.mjs +66 -0
- package/packages/piagent-core/managed/helper-run.mjs +2 -1
- package/packages/piagent-core/managed/request-stream.mjs +10 -2
- package/packages/piagent-core/managed/session.mjs +8 -1
- package/packages/piagent-core/managed/tool-failures.mjs +91 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/managed-failure.mjs +3 -0
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-EmQ_ohQe.js → MarkdownMessage-lWHlCJq4.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-CDVdO4C7.js +231 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/managed-gateway.mjs +4 -2
- package/packages/piagent-webui/gateway/managed-projection.ts +3 -1
- package/packages/piagent-webui/gateway/runtime-session-controls.ts +9 -0
- package/packages/piagent-webui/gateway/session-catalog.ts +3 -2
- package/packages/piagent-webui/gateway/session-command-controller.ts +6 -3
- package/packages/piagent-webui/gateway/session-command-store.ts +3 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +6 -1
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +9 -9
- package/schemas/piagent-webui/session-catalog-v1.schema.json +1 -1
- package/schemas/piagent-webui/session-command-v1.schema.json +20 -5
- package/scripts/company-terminal.mjs +22 -0
- package/packages/piagent-webui/dist/client/assets/index-Posch2XH.js +0 -231
|
@@ -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.23.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,14 @@
|
|
|
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.23.0 · 2026-10-13</p>
|
|
109
|
+
<h2 id="v1-23-0">Fast mode for company conversations, failed tools reported to Studio</h2>
|
|
110
|
+
<p class="section-lead">Where the team's Harness in Agent Studio allows it, the dashboard shows a Fast switch in the conversation's header and the Terminal has <span class="inline-code">/fast on|off|status</span>. Every new conversation starts with it off. While on, the main agent's calls on a GPT (Codex) model ask for the priority tier: the same model and thinking level, answered faster, with Studio counting that call's tokens 2.5 times toward the key's quota. Subagents run as usual. On a Claude model or an API-key vendor model nothing is added and the turn runs at standard speed. If Studio or the provider refuses, the call is asked again once at standard speed and the conversation goes back to standard speed with a note. Needs Studio 0.26.0 and an Agent Watch that carries the Harness's <span class="inline-code">fast_mode</span>.</p>
|
|
111
|
+
<p class="section-lead">A tool that fails in a company run (Docker, a network command, a test…) is reported to Agent Studio (Logs → “Tool lỗi”) so an administrator can read why: the tool, a code, the command or path, the start and end of the error output, the OS and whether the machine has a Docker engine. Secrets are redacted and the home folder is written <span class="inline-code">~</span> before sending; no prompt, search query or subagent brief is sent, and a tool you stopped or declined is not reported.</p>
|
|
112
|
+
<p class="section-lead">Fix: after an administrator changed the Harness (for example the main agent from thinking level high to a fixed max), a new company conversation from the dashboard never sent its first message and showed “Pi received the request and the session is resyncing”. A new conversation now starts at the level its Harness allows.</p>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
107
115
|
<section class="section">
|
|
108
116
|
<p class="section-kicker">v1.22.1 · 2026-10-12</p>
|
|
109
117
|
<h2 id="v1-22-1">The dashboard no longer keeps loading when a project is on a slow drive</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.23.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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.23.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>
|
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.23.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.
|
|
97
|
+
<p class="eyebrow">Piagent 1.23.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.
|
|
111
|
+
<a class="button" href="/whats-new">Có gì mới trong 1.23.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.
|
|
187
|
+
<span class="card-kicker">Bản 1.23.0</span>
|
|
188
188
|
<h3>Có gì mới</h3>
|
|
189
|
-
<p>
|
|
189
|
+
<p>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.23.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>
|
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.23.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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.23.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>
|
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.23.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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.23.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>
|
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.23.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.
|
|
33
|
+
<span class="pill"><span class="pill-dot"></span>v1.23.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>
|
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.23.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>
|
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.23.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>
|
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.23.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>
|
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.23.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>
|
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.23.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,14 @@
|
|
|
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.23.0 · 2026-10-13</p>
|
|
109
|
+
<h2 id="v1-23-0">Fast mode cho cuộc trò chuyện công ty, tool lỗi được báo về Studio</h2>
|
|
110
|
+
<p class="section-lead">Khi Harness của team trong Agent Studio cho phép, dashboard có nút Fast ở đầu cuộc trò chuyện và Terminal có lệnh <span class="inline-code">/fast on|off|status</span>. Mỗi cuộc trò chuyện mới bắt đầu ở chế độ tắt. Khi bật, call của main agent trên model GPT (Codex) xin tier ưu tiên: cùng model và mức suy luận, trả lời nhanh hơn, và Studio tính token của call đó vào quota của key ở mức 2,5 lần. Subagent chạy như thường. Trên model Claude hay model của vendor dùng API key thì không thêm gì và lượt chạy ở tốc độ thường. Nếu Studio hay provider từ chối, call được gửi lại một lần ở tốc độ thường và cuộc trò chuyện quay về tốc độ thường kèm một ghi chú. Cần Studio 0.26.0 và Agent Watch có mang <span class="inline-code">fast_mode</span> của Harness.</p>
|
|
111
|
+
<p class="section-lead">Một tool chạy lỗi trong phiên công ty (Docker, lệnh mạng, test…) được báo về Agent Studio (Logs → “Tool lỗi”) để quản trị viên đọc được lý do: tool, mã lỗi, lệnh hoặc đường dẫn, phần đầu và cuối của output lỗi, hệ điều hành và máy có Docker engine hay không. Secret được che và thư mục home ghi là <span class="inline-code">~</span> trước khi gửi; không gửi prompt, câu tìm kiếm hay brief của subagent, và tool do bạn dừng hoặc từ chối thì không báo.</p>
|
|
112
|
+
<p class="section-lead">Sửa lỗi: sau khi quản trị viên đổi Harness (ví dụ main agent từ mức suy luận high sang max cố định), cuộc trò chuyện công ty mới tạo từ dashboard không gửi được tin nhắn đầu, chỉ hiện “Pi đã nhận yêu cầu và phiên đang đồng bộ lại”. Giờ cuộc trò chuyện mới bắt đầu ở mức mà Harness cho phép.</p>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
107
115
|
<section class="section">
|
|
108
116
|
<p class="section-kicker">v1.22.1 · 2026-10-12</p>
|
|
109
117
|
<h2 id="v1-22-1">Dashboard không còn quay mãi khi project nằm trên ổ chậm</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.23.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>
|
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.23.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":
|
|
260
|
-
"sha256": "
|
|
259
|
+
"fileCount": 538,
|
|
260
|
+
"sha256": "93173799932f8f9d88976e5ffad598645d01fb59f9545ed1dc783d2ca2b1a296"
|
|
261
261
|
}
|
|
262
262
|
],
|
|
263
263
|
"custodianComparisonRules": {
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Fast mode of a company conversation. The member turns it on per
|
|
2
|
+
// conversation, and only where the team's Harness in Studio allows it
|
|
3
|
+
// (harness.configuration.fast_mode); a conversation starts with it off. While
|
|
4
|
+
// on, the main agent's calls ask a Codex model for the priority service tier:
|
|
5
|
+
// the same model and thinking level, answered faster, and charged to the
|
|
6
|
+
// key's quota at a higher rate (Studio decides the rate and refuses the tier
|
|
7
|
+
// for a run whose Harness does not allow it). Subagents run as usual.
|
|
8
|
+
const ENTRY = 'agent-watch-fast';
|
|
9
|
+
export const NOTICE = 'agent-watch-fast-notice';
|
|
10
|
+
export const FAST_SERVICE_TIER = 'priority';
|
|
11
|
+
|
|
12
|
+
export const fastAllowed = manifest => manifest?.harness?.configuration?.fast_mode === true;
|
|
13
|
+
const recorded = manager => manager.getEntries().filter(e => e.type === 'custom' && e.customType === ENTRY).at(-1)?.data;
|
|
14
|
+
export function restoreFast(manager) { return recorded(manager)?.enabled === true; }
|
|
15
|
+
// The conversation records whether its Harness allows fast mode, so the
|
|
16
|
+
// dashboard shows the switch (or not) without the runtime; a Harness that
|
|
17
|
+
// stopped allowing it turns it off.
|
|
18
|
+
export function recordFast(owner, manager) {
|
|
19
|
+
const allowed = fastAllowed(owner.manifest), last = recorded(manager);
|
|
20
|
+
if (!allowed) owner.fast = false;
|
|
21
|
+
if ((last?.allowed === true) === allowed && (last?.enabled === true) === Boolean(owner.fast)) return;
|
|
22
|
+
manager.appendCustomEntry(ENTRY, { enabled: Boolean(owner.fast), allowed, at: new Date().toISOString() });
|
|
23
|
+
}
|
|
24
|
+
// "on" | "off" where the Harness allows fast mode, else null (no switch).
|
|
25
|
+
export const fastState = owner => fastAllowed(owner.manifest) ? owner.fast ? 'on' : 'off' : null;
|
|
26
|
+
export function fastSetter(owner, manager) {
|
|
27
|
+
return enabled => {
|
|
28
|
+
if (enabled && !fastAllowed(owner.manifest)) throw Error('managed-fast-unavailable');
|
|
29
|
+
if (Boolean(enabled) === Boolean(owner.fast)) return false;
|
|
30
|
+
owner.fast = Boolean(enabled); manager.appendCustomEntry(ENTRY, { enabled: owner.fast, allowed: fastAllowed(owner.manifest), at: new Date().toISOString() });
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Where fast mode is served, as Studio admits it (ledger.FastWeightMilli; a
|
|
35
|
+
// model missing here simply runs at standard speed):
|
|
36
|
+
// - Codex (Responses): service_tier "priority" on the models OpenAI's speed
|
|
37
|
+
// documentation names, checked 2026-10-13.
|
|
38
|
+
// - Claude (Messages): not served. Anthropic's fast mode is another protocol
|
|
39
|
+
// (speed "fast" with a beta header, Opus 5.5 / 5 / 4.8 only) and is billed
|
|
40
|
+
// to a subscription's usage credits, outside the plan.
|
|
41
|
+
// - API-key vendors (Chat Completions): none documents a fast tier.
|
|
42
|
+
const CODEX_FAST = new Set(['gpt-6.1-sol', 'gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna', 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5.6-terra', 'gpt-5.5']);
|
|
43
|
+
export const fastServed = route => route?.provider === 'codex' && CODEX_FAST.has(route.native);
|
|
44
|
+
// Studio's refusal of the tier (the Harness or the model list changed), or
|
|
45
|
+
// the provider's of a request carrying it: asked again once without it.
|
|
46
|
+
export const fastRefusal = code => code === 'policy_denied' || code === 'upstream_request_rejected';
|
|
47
|
+
// A fast request was refused before the model did any work: the conversation
|
|
48
|
+
// goes back to standard speed and says so, instead of failing the turn.
|
|
49
|
+
export function declineFast(owner, code) {
|
|
50
|
+
owner.fast = false;
|
|
51
|
+
try {
|
|
52
|
+
recordFast(owner, owner.session.sessionManager);
|
|
53
|
+
void owner.session.sendCustomMessage?.({ customType: NOTICE, display: true, details: { code },
|
|
54
|
+
content: `Fast mode was refused for this request [${code}]: this conversation continues at standard speed. Turn Fast on again to retry.` }, { triggerTurn: false })?.catch?.(() => {});
|
|
55
|
+
} catch { /* the request goes on at standard speed either way */ }
|
|
56
|
+
}
|
|
57
|
+
// The payload hook of one request: the main agent's call on a model that
|
|
58
|
+
// serves fast mode carries the tier while fast mode is on and still allowed
|
|
59
|
+
// (the Harness may have turned it off since the member turned it on).
|
|
60
|
+
export function fastPayload(owner, role, route, onPayload) {
|
|
61
|
+
if (role !== 'main' || !owner.fast || !fastAllowed(owner.manifest) || !fastServed(route)) return onPayload;
|
|
62
|
+
return async (payload, model) => {
|
|
63
|
+
const next = (onPayload ? await onPayload(payload, model) : undefined) ?? payload;
|
|
64
|
+
return next && typeof next === 'object' && !Array.isArray(next) ? { ...next, service_tier: FAST_SERVICE_TIER } : next;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -12,6 +12,7 @@ import { reviewParts, mergeReviews, REVIEW_PARALLEL, emptySnapshot, readBranchBa
|
|
|
12
12
|
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
|
+
import { trackToolFailures } from './tool-failures.mjs';
|
|
15
16
|
|
|
16
17
|
const textContent = result => result.content?.filter(p => p.type === 'text').map(p => p.text).join('\n') ?? '';
|
|
17
18
|
// A helper failure that another model of its role may not have: the model's
|
|
@@ -85,7 +86,7 @@ async function helperAttempt(managed, role, task, signal, harness, snapshot, par
|
|
|
85
86
|
sessionManager: managed.api.SessionManager.inMemory(managed.cwd), noTools: 'builtin', tools: [...READ_TOOLS, ...(setup.commands ? ['bash'] : []), ...(setup.web ? ['web_search', 'web_fetch'] : [])],
|
|
86
87
|
customTools: [...boundary.tools(managed.api).filter(tool => READ_TOOLS.includes(tool.name) || setup.commands && tool.name === 'bash').map(tool => countedCheck(managed, tool)),
|
|
87
88
|
...(setup.web ? managed.webTools(runtime, role) : [])], resourceLoader: managedResourceLoader(managed.api, { systemPrompt: setup.prompt }) });
|
|
88
|
-
sessions.add(session);
|
|
89
|
+
sessions.add(session); trackToolFailures(managed, session, role);
|
|
89
90
|
const abort = () => void session.abort(); signal?.addEventListener('abort', abort, { once: true });
|
|
90
91
|
try { if (signal?.aborted) throw Error('managed-helper-cancelled'); await session.prompt(anchorText(managed.session.sessionManager, harness) + task + checks + extra, { expandPromptTemplates: false }); }
|
|
91
92
|
finally { signal?.removeEventListener('abort', abort); }
|
|
@@ -10,6 +10,8 @@ import { describeFailure, failureCode, failureIsAdmissionRefusal, failureIsBrief
|
|
|
10
10
|
import { CLIENT_AGENT } from './client-agent.mjs';
|
|
11
11
|
import { shareGrant, releaseGrant } from './grant-share.mjs';
|
|
12
12
|
import { purposeHeaders } from './call-purpose.mjs';
|
|
13
|
+
import { sendToolFailures } from './tool-failures.mjs';
|
|
14
|
+
import { declineFast, fastPayload, fastRefusal } from './fast-mode.mjs';
|
|
13
15
|
|
|
14
16
|
// A caller may pass the provider's resolved auth headers (Pi's compaction and
|
|
15
17
|
// branch summaries do): for this provider that is the registered placeholder
|
|
@@ -63,13 +65,17 @@ export function wrapRoleStreams(owner, runtime, role, { provider, verifyGrant })
|
|
|
63
65
|
const grant = await share.grant.catch((error) => { throw refused ? Error(refused) : error; });
|
|
64
66
|
verifyGrant(grant, owner.manifest, role);
|
|
65
67
|
if (role === 'main') owner.grant = grant;
|
|
68
|
+
// Tool calls that failed since the last request (tool-failures.mjs).
|
|
69
|
+
void sendToolFailures(owner, grant.token);
|
|
66
70
|
const route = owner.routes.get(runtime);
|
|
67
71
|
if (!route || grant.provider_model_id !== route.native || grant.provider !== route.provider || grant.model_id !== route.model_id || model.id !== 'agent-watch-auto') throw Error('managed-route-changed');
|
|
68
72
|
const asked = Date.now();
|
|
69
73
|
// The main agent names its conversation, so every turn reaches the
|
|
70
74
|
// same company account and prompt cache; a helper names its role.
|
|
71
75
|
const conversation = role === 'main' ? conversationSession(owner) : null;
|
|
72
|
-
|
|
76
|
+
// Fast mode, where the member turned it on and the model serves it (fast-mode.mjs).
|
|
77
|
+
const onPayload = fastPayload(owner, role, route, options?.onPayload), fast = onPayload !== options?.onPayload;
|
|
78
|
+
const stream = original({ ...model, id: route.native }, context, { ...options, apiKey: grant.token, sessionId: conversation ?? grant.role_id, maxRetries: 0, onPayload,
|
|
73
79
|
headers: { ...withoutCredentials(options?.headers), ...purpose, 'User-Agent': CLIENT_AGENT, 'X-Session-Id': grant.role_id, ...(conversation ? { 'X-Claude-Code-Session-Id': conversation } : {}) } });
|
|
74
80
|
// Whether Studio admitted the request is known at its first event
|
|
75
81
|
// after "start": until then nothing is passed on, so a refused
|
|
@@ -79,11 +85,13 @@ export function wrapRoleStreams(owner, runtime, role, { provider, verifyGrant })
|
|
|
79
85
|
if (opening && event.type === 'start') { opening.push(event); continue; }
|
|
80
86
|
const code = event.type === 'error' ? failureCode(event.error?.errorMessage) : null;
|
|
81
87
|
if (opening && code && !signal?.aborted && (failureIsAdmissionRefusal(code) || failureIsBriefRefusal(code) && brief < BRIEF_ASKS
|
|
82
|
-
|| role === 'main' && code === ACCOUNT_GONE && rebinds < MAX_REBINDS)) { refusal = event.error; break; }
|
|
88
|
+
|| role === 'main' && code === ACCOUNT_GONE && rebinds < MAX_REBINDS || fast && fastRefusal(code))) { refusal = event.error; break; }
|
|
83
89
|
if (opening) { for (const held of opening) out.push(held); opening = null; wait = capacityWaitEnded(owner, wait, 'admitted'); letGo(); }
|
|
84
90
|
out.push(event.type === 'error' ? { ...event, ...(signal?.aborted ? { reason: 'aborted' } : {}), error: told(event.error) } : event);
|
|
85
91
|
}
|
|
86
92
|
if (!refusal) { for (const held of opening ?? []) out.push(held); out.end(told(await stream.result())); return; }
|
|
93
|
+
// Refused with the fast tier: asked again at standard speed.
|
|
94
|
+
if (fast && fastRefusal(failureCode(refusal.errorMessage))) { letGo(); declineFast(owner, failureCode(refusal.errorMessage)); continue; }
|
|
87
95
|
refused = String(refusal.errorMessage ?? '');
|
|
88
96
|
// A lapsed or replaced grant: the next ask renews it, not this share.
|
|
89
97
|
const stale = share; letGo();
|
|
@@ -14,6 +14,8 @@ import { nativeManagedModel, isVendor, piProvider, studioAPI, studioPath } from
|
|
|
14
14
|
import { fetchOriginTool } from './repository-operation.mjs';
|
|
15
15
|
import { describeFailure, failureCode, failureText } from '../runtime/managed-failure.mjs';
|
|
16
16
|
import { enableCompanyTurnRetry, wrapRoleStreams } from './request-stream.mjs';
|
|
17
|
+
import { trackToolFailures } from './tool-failures.mjs';
|
|
18
|
+
import { fastServed, fastSetter, fastState, recordFast, restoreFast } from './fast-mode.mjs';
|
|
17
19
|
import { runHelper } from './helper-run.mjs';
|
|
18
20
|
import { askTool } from './member-questions.mjs';
|
|
19
21
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
@@ -168,12 +170,15 @@ export class ManagedSession {
|
|
|
168
170
|
self.turnRetry = enableCompanyTurnRetry(self.session, settings);
|
|
169
171
|
// The member's access for this conversation: ask first, or Bypass (permission.mjs).
|
|
170
172
|
self.permission = restorePermission(manager); self.session.managedSetPermission = permissionSetter(self, manager);
|
|
173
|
+
// Fast mode, where the Harness allows it (fast-mode.mjs): off until the member turns it on.
|
|
174
|
+
self.fast = restoreFast(manager); recordFast(self, manager); self.session.managedSetFast = fastSetter(self, manager); self.session.managedFastState = () => fastState(self); // null before the first run
|
|
175
|
+
self.session.managedFastServed = () => { const route = self.routes.get(self.modelRuntime); return route ? fastServed(route) : null; };
|
|
171
176
|
// A helper's receipt (its tokens, the patch it read) is for the member's
|
|
172
177
|
// eyes: the agent has the helper's answer as the tool result, and a
|
|
173
178
|
// receipt in its context reads as a message from the member.
|
|
174
179
|
const context = self.session.agent.transformContext;
|
|
175
180
|
self.session.agent.transformContext = async (messages, signal) => (context ? await context(messages, signal) : messages)
|
|
176
|
-
.filter(m => !(m.role === 'custom' &&
|
|
181
|
+
.filter(m => !(m.role === 'custom' && ['agent-watch-helper-receipt', 'agent-watch-fast-notice'].includes(m.customType)));
|
|
177
182
|
// A call to a tool that does not exist (a model inventing a name) is
|
|
178
183
|
// answered by Pi before any hook; the run counts them for its report.
|
|
179
184
|
self.session.subscribe(event => {
|
|
@@ -181,6 +186,7 @@ export class ManagedSession {
|
|
|
181
186
|
});
|
|
182
187
|
await stageCompaction(self.session, self.api, self.sdk);
|
|
183
188
|
compactEarly(settings); backOffFailedCompaction(self.session); trackCompaction(self, self.session);
|
|
189
|
+
trackToolFailures(self, self.session);
|
|
184
190
|
self.publishHelpers();
|
|
185
191
|
const setThinking=self.session.setThinkingLevel.bind(self.session);
|
|
186
192
|
self.runThinking = setThinking;
|
|
@@ -353,6 +359,7 @@ export class ManagedSession {
|
|
|
353
359
|
this.blocked = this.scope && sameMember(this.scope, scope) ? null : 'managed-session-scope-changed';
|
|
354
360
|
if (this.blocked) throw Error(this.blocked);
|
|
355
361
|
if (!sameKey(this.scope, scope)) { this.session.sessionManager.appendCustomEntry('agent-watch-scope', scope); this.scope = scope; }
|
|
362
|
+
if (this.session) recordFast(this, this.session.sessionManager);
|
|
356
363
|
this.session?.setActiveToolsByName(this.session.getActiveToolNames()); // the new Harness workflow and helpers reach the prompt
|
|
357
364
|
}
|
|
358
365
|
async digest() { try { return await this.changeDigest(); } catch { return null; } }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Tool calls that failed in a company run, for Studio's logs (Logs → "Tool
|
|
2
|
+
// lỗi"). A failure happens on the member's machine and its output stays
|
|
3
|
+
// there: an administrator told "Docker does not work" had nothing to read.
|
|
4
|
+
// Each failed call is kept as the tool, a stable code, what it was asked to
|
|
5
|
+
// run (a command, path or address; never a search query or a brief), the end
|
|
6
|
+
// of its error output and what the machine is; secrets are redacted and the
|
|
7
|
+
// home folder is written "~". Studio keeps them 30 days.
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import { redactedText } from '../runtime/managed-failure.mjs';
|
|
11
|
+
import { CLIENT_AGENT } from './client-agent.mjs';
|
|
12
|
+
|
|
13
|
+
const TOOL = /^[a-z0-9_]{1,40}$/;
|
|
14
|
+
const INPUT_CHARS = 500, ERROR_HEAD = 1000, ERROR_TAIL = 2000;
|
|
15
|
+
// One request carries at most BATCH failures; KEEP wait for the next one.
|
|
16
|
+
const BATCH = 20, KEEP = 100, ATTEMPTS = 3;
|
|
17
|
+
// The member stopped the tool or declined to approve it: not a failure.
|
|
18
|
+
const NOT_FAILURES = new Set(['managed_tool_cancelled', 'managed_helper_cancelled', 'managed_operation_denied']);
|
|
19
|
+
|
|
20
|
+
const text = result => typeof result === 'string' ? result
|
|
21
|
+
: Array.isArray(result?.content) ? result.content.filter(part => part?.type === 'text').map(part => part.text ?? '').join('\n') : '';
|
|
22
|
+
function clean(value, home = os.homedir()) {
|
|
23
|
+
const redacted = redactedText(value);
|
|
24
|
+
return (home.length > 1 ? redacted.split(home).join('~') : redacted).replace(/\0/g, '');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// The runtime's own code when the error starts with one (managed-docker-
|
|
28
|
+
// unreachable, web-search-failed…), else how the command ended.
|
|
29
|
+
export function toolFailureCode(error) {
|
|
30
|
+
const named = /^((?:managed|web-search)-[a-z-]{1,56})\b/.exec(error)?.[1];
|
|
31
|
+
if (named) return named.replaceAll('-', '_');
|
|
32
|
+
const exit = /Command exited with code (\d{1,3})\s*$/.exec(error)?.[1];
|
|
33
|
+
if (exit) return `exit_${exit}`;
|
|
34
|
+
if (/Command timed out after \d+ seconds\s*$/.test(error)) return 'timeout';
|
|
35
|
+
if (/Command aborted\s*$/.test(error)) return 'managed_tool_cancelled';
|
|
36
|
+
return 'error';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// One failed call as Studio stores it; null when it is not a failure to keep.
|
|
40
|
+
export function toolFailure({ tool, role, args, result, startedAt, endedAt, engine, home }) {
|
|
41
|
+
const error = clean(text(result), home).trim(), code = toolFailureCode(error);
|
|
42
|
+
if (NOT_FAILURES.has(code)) return null;
|
|
43
|
+
const input = clean(args?.command ?? args?.path ?? args?.url ?? '', home).trim();
|
|
44
|
+
return { id: randomUUID(), tool: TOOL.test(String(tool)) ? tool : 'other', role, code, at: endedAt,
|
|
45
|
+
...(input ? { input: input.slice(0, INPUT_CHARS) } : {}),
|
|
46
|
+
// A long output keeps its start and its end: the end is where it failed.
|
|
47
|
+
error: error.length <= ERROR_HEAD + ERROR_TAIL ? error : `${error.slice(0, ERROR_HEAD)}\n[… ${error.length - ERROR_HEAD - ERROR_TAIL} characters left out …]\n${error.slice(-ERROR_TAIL)}`,
|
|
48
|
+
duration_ms: Math.max(0, Math.round(endedAt - (startedAt ?? endedAt))),
|
|
49
|
+
engine: tool === 'run_with_docker' ? 'used' : engine ? 'available' : 'none' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Sends what is waiting with a role's token. A run's token lives two minutes
|
|
53
|
+
// and each model request renews it, so the request after a failed tool
|
|
54
|
+
// (wrapRoleStreams) carries what a lapsed token could not. A Studio without
|
|
55
|
+
// this log (404) is not asked again; nothing here can fail a turn.
|
|
56
|
+
export function sendToolFailures(owner, token, { fetchImpl = fetch, now = Date.now } = {}) {
|
|
57
|
+
const log = owner.toolFailures;
|
|
58
|
+
if (!log || log.off || log.sending || !log.queue.length || !token) return null;
|
|
59
|
+
const batch = log.queue.splice(0, BATCH);
|
|
60
|
+
const again = () => { log.queue.unshift(...batch.filter(item => (item.attempts = (item.attempts ?? 0) + 1) < ATTEMPTS)); };
|
|
61
|
+
log.sending = (async () => {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetchImpl(`${owner.origin}/studio/v1/managed/tool-failures`, { method: 'POST', signal: AbortSignal.timeout(10_000),
|
|
64
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', 'User-Agent': CLIENT_AGENT },
|
|
65
|
+
body: JSON.stringify({ os: `${process.platform} ${os.release()} ${process.arch}`.slice(0, 80),
|
|
66
|
+
failures: batch.map(({ at, attempts, ...item }) => ({ ...item, age_ms: Math.max(0, now() - at) })) }) });
|
|
67
|
+
if (response.status === 404 || response.status === 405) { log.off = true; log.queue.length = 0; }
|
|
68
|
+
// A refused body (400) would be refused again.
|
|
69
|
+
else if (!response.ok && response.status !== 400) again();
|
|
70
|
+
} catch { again(); }
|
|
71
|
+
finally { log.sending = null; }
|
|
72
|
+
})();
|
|
73
|
+
return log.sending;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Keeps a session's failed tool calls on its owner (the main agent's session,
|
|
77
|
+
// or a helper's with its role) and tries to send each as it happens.
|
|
78
|
+
export function trackToolFailures(owner, session, role = 'main', now = Date.now) {
|
|
79
|
+
const log = owner.toolFailures ??= { queue: [], sending: null, off: false }, started = new Map();
|
|
80
|
+
session.subscribe(event => {
|
|
81
|
+
if (event?.type === 'tool_execution_start') started.set(event.toolCallId, { at: now(), args: event.args });
|
|
82
|
+
if (event?.type !== 'tool_execution_end') return;
|
|
83
|
+
const call = started.get(event.toolCallId); started.delete(event.toolCallId);
|
|
84
|
+
if (!event.isError || log.off) return;
|
|
85
|
+
let failure = null;
|
|
86
|
+
try { failure = toolFailure({ tool: event.toolName, role, args: call?.args, result: event.result, startedAt: call?.at, endedAt: now(), engine: owner.boundary?.engine }); } catch { /* the log never fails a tool */ }
|
|
87
|
+
if (!failure) return;
|
|
88
|
+
log.queue.push(failure); if (log.queue.length > KEEP) log.queue.shift();
|
|
89
|
+
void sendToolFailures(owner, owner.grant?.token);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -88,6 +88,7 @@ const CODES = {
|
|
|
88
88
|
search_unavailable: ['tool', 'no web search provider answered right now; try again shortly or use web_fetch with a known address'],
|
|
89
89
|
'managed-session-open-elsewhere': ['new-session', 'this conversation is running in the Piagent WebUI; continue it there or start a new one'],
|
|
90
90
|
'managed-session-lease-unavailable': ['failed', 'Piagent could not record which process runs this conversation'],
|
|
91
|
+
'managed-fast-unavailable': ['policy', "the team's Harness does not allow fast mode"],
|
|
91
92
|
'managed-plan-required': ['tool', 'for a complex task the Harness asks for a checklist (update_plan) before the first file edit'],
|
|
92
93
|
'managed-plan-invalid': ['tool', 'the checklist was not in the expected form'],
|
|
93
94
|
'managed-request-failed': ['failed', 'the request could not be sent'],
|
|
@@ -122,6 +123,8 @@ export function failureIsBriefRefusal(code) { return BRIEF_REFUSALS.has(code); }
|
|
|
122
123
|
export function unnamedFailureText(raw) {
|
|
123
124
|
return ['managed-request-failed', 'studio-request-failed'].includes(failureCode(raw)) ? redactSensitiveText(String(raw ?? '')).text.slice(0, 600) : null;
|
|
124
125
|
}
|
|
126
|
+
// Any text that leaves this machine for Studio's logs, with secrets redacted.
|
|
127
|
+
export function redactedText(raw) { return redactSensitiveText(String(raw ?? '')).text; }
|
|
125
128
|
// The sentence alone, for a status line that already says who is waiting.
|
|
126
129
|
export function failureReason(code) { return (CODES[code] ?? CODES['managed-request-failed'])[1]; }
|
|
127
130
|
// A failure Studio never saw: nothing was sent, so it is not a model request.
|