@piagent/platform 1.11.1 → 1.12.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 +25 -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 +12 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +12 -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 +13 -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 +13 -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/extensions/piagent-guard.ts +11 -5
- package/packages/piagent-core/extensions/source-checkout-paths.ts +31 -0
- package/packages/piagent-core/managed/agent-skills.mjs +44 -0
- package/packages/piagent-core/managed/doctor.mjs +23 -2
- package/packages/piagent-core/managed/language-environment.mjs +118 -0
- package/packages/piagent-core/managed/resource-loader.mjs +5 -4
- package/packages/piagent-core/managed/sandbox-diagnostic.mjs +4 -1
- package/packages/piagent-core/managed/session.mjs +15 -3
- package/packages/piagent-core/managed/tool-boundary.mjs +110 -28
- package/packages/piagent-core/managed/tool-worker.mjs +3 -3
- package/packages/piagent-core/managed/toolchain.mjs +10 -8
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/registration/agent-resources.ts +12 -0
- package/packages/piagent-core/runtime/resources/agent-resources.d.mts +6 -0
- package/packages/piagent-core/runtime/resources/agent-resources.mjs +140 -0
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-D70xkf0d.js → MarkdownMessage-FWfOWZjV.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-BuIJR5rd.js → index-DFKHLVYk.js} +14 -14
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/gateway-service.ts +16 -0
- package/packages/piagent-webui/gateway/path-suggestions.ts +118 -0
- package/packages/piagent-webui/server/loopback-server.ts +2 -0
- package/packages/piagent-webui/server/member-routes.ts +22 -3
- package/packages/piagent-webui/server/transcript-projection.ts +7 -1
|
@@ -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.12.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>
|
|
@@ -103,6 +103,18 @@
|
|
|
103
103
|
<h1>1.9.0: ask in plain language, the team's Harness checks</h1>
|
|
104
104
|
<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>
|
|
105
105
|
</div>
|
|
106
|
+
<section class="section">
|
|
107
|
+
<p class="section-kicker">v1.12.0 · 2026-10-06</p>
|
|
108
|
+
<h2 id="v1-12-0">@ any folder, skills from /, and tests in the company sandbox</h2>
|
|
109
|
+
<p class="section-lead">Point the agent at exactly the file or folder to read, reuse the skills and commands you already keep, and let the agent run the project's unit and integration tests.</p>
|
|
110
|
+
<div class="grid cols-2">
|
|
111
|
+
<article class="card"><h3 id="at-mentions">@ files and folders</h3><p>Type <code>@</code> in the composer to pick the project's files and folders; type <code>~/</code> or <code>/</code> for any other folder on the Mac. The agent reads folders outside the project for reference, read-only; hidden home files, <code>~/Library</code> and credential files stay closed.</p></article>
|
|
112
|
+
<article class="card"><h3 id="kept-drafts">Unsent messages are kept</h3><p>A message typed in a conversation or in the new chat is still there after opening another conversation or reloading the page, with its attached files.</p></article>
|
|
113
|
+
<article class="card"><h3 id="skills-commands">Skills and commands</h3><p>Skills and commands in the project's and your <code>.claude</code>, <code>.codex</code>, <code>.agents</code> and <code>.pi</code> folders work as they are: the model sees the skills, and a <code>/</code> at the start of a message offers <code>/command</code> and <code>/skill:name</code>.</p></article>
|
|
114
|
+
<article class="card"><h3 id="tests-in-sandbox">Tests in the company sandbox</h3><p>Package caches kept per project (npm, pnpm, pip, uv, Go, Cargo, Gradle, Maven, NuGet…), servers and databases on localhost, the selected Xcode, JDKs, .NET. Run <code>piagent studio --doctor</code> to see which languages are ready. Docker and the iOS Simulator are not available in the sandbox yet.</p></article>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
106
118
|
<section class="section">
|
|
107
119
|
<p class="section-kicker">v1.11.1 · 2026-10-05</p>
|
|
108
120
|
<h2 id="v1-11-1">An update ends on the new release's dashboard</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.12.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.12.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>
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
<main>
|
|
94
94
|
<div class="content-shell" data-article>
|
|
95
95
|
<section class="hero">
|
|
96
|
-
<p class="eyebrow">Piagent 1.
|
|
96
|
+
<p class="eyebrow">Piagent 1.12.0</p>
|
|
97
97
|
<h1>Coding agent cho cả team, kiểm tra từng bước.</h1>
|
|
98
98
|
<p>
|
|
99
99
|
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.
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
<div class="hero-actions">
|
|
108
108
|
<a class="button primary" href="/quickstart">Bắt đầu</a>
|
|
109
109
|
<a class="button" href="/ecosystem">Hệ sinh thái</a>
|
|
110
|
-
<a class="button" href="/whats-new">Có gì mới trong 1.
|
|
110
|
+
<a class="button" href="/whats-new">Có gì mới trong 1.12.0</a>
|
|
111
111
|
<a class="button" href="/commands">Xem command</a>
|
|
112
112
|
</div>
|
|
113
113
|
</section>
|
|
@@ -183,9 +183,9 @@ piagent-setup</code></pre>
|
|
|
183
183
|
<h2 id="bat-dau-tu-dau">Bắt đầu từ đâu</h2>
|
|
184
184
|
<div class="card-grid">
|
|
185
185
|
<a class="doc-card" href="/whats-new">
|
|
186
|
-
<span class="card-kicker">Bản 1.
|
|
186
|
+
<span class="card-kicker">Bản 1.12.0</span>
|
|
187
187
|
<h3>Có gì mới</h3>
|
|
188
|
-
<p
|
|
188
|
+
<p>@ chọn file và folder trên máy, giữ tin nhắn đang soạn, skill và command dùng bằng /, sandbox công ty chạy được test của nhiều ngôn ngữ.</p>
|
|
189
189
|
</a>
|
|
190
190
|
<a class="doc-card" href="/ecosystem">
|
|
191
191
|
<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.12.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.12.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.12.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.12.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.12.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.12.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.12.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.12.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.12.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.12.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.12.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.12.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>
|
|
@@ -103,6 +103,18 @@
|
|
|
103
103
|
<h1>Bản 1.9.0: nói việc bằng lời, Harness của team kiểm tra</h1>
|
|
104
104
|
<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>
|
|
105
105
|
</div>
|
|
106
|
+
<section class="section">
|
|
107
|
+
<p class="section-kicker">v1.12.0 · 2026-10-06</p>
|
|
108
|
+
<h2 id="v1-12-0">@ tới mọi folder, skill dùng bằng /, và test chạy được trong sandbox công ty</h2>
|
|
109
|
+
<p class="section-lead">Chỉ cho agent đúng file hay folder cần đọc, dùng lại skill và command đã có, và để agent tự chạy unit test lẫn integration test của project.</p>
|
|
110
|
+
<div class="grid cols-2">
|
|
111
|
+
<article class="card"><h3 id="at-mentions">@ file và folder</h3><p>Gõ <code>@</code> trong khung chat để chọn file, folder của project; gõ <code>~/</code> hoặc <code>/</code> để chọn folder khác trên máy. Agent đọc được folder ngoài project, chỉ đọc; file ẩn trong home, <code>~/Library</code> và file chứa credential vẫn bị chặn.</p></article>
|
|
112
|
+
<article class="card"><h3 id="kept-drafts">Giữ tin nhắn đang soạn</h3><p>Tin nhắn gõ dở ở mỗi cuộc trò chuyện và ở khung chat mới vẫn còn khi chuyển sang cuộc trò chuyện khác hay tải lại trang, cùng file đã đính kèm.</p></article>
|
|
113
|
+
<article class="card"><h3 id="skills-commands">Skill và command</h3><p>Skill và command trong <code>.claude</code>, <code>.codex</code>, <code>.agents</code> và <code>.pi</code> của project và trên máy dùng được ngay: model thấy danh sách skill, gõ <code>/</code> ở đầu tin nhắn để chọn <code>/command</code> hoặc <code>/skill:tên</code>.</p></article>
|
|
114
|
+
<article class="card"><h3 id="tests-in-sandbox">Test trong sandbox công ty</h3><p>Cache package giữ theo project (npm, pnpm, pip, uv, Go, Cargo, Gradle, Maven, NuGet…), server và database trên localhost, Xcode đã chọn, JDK, .NET. Chạy <code>piagent studio --doctor</code> để xem ngôn ngữ nào sẵn sàng. Docker và iOS Simulator chưa dùng được trong sandbox.</p></article>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
106
118
|
<section class="section">
|
|
107
119
|
<p class="section-kicker">v1.11.1 · 2026-10-05</p>
|
|
108
120
|
<h2 id="v1-11-1">Cập nhật xong là dashboard chạy bản mới</h2>
|
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.12.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": 521,
|
|
260
|
+
"sha256": "a070df51c583ffb57a918ece6e2afa9347e9f5a2fe895882232e9da6623be8d1"
|
|
261
261
|
}
|
|
262
262
|
],
|
|
263
263
|
"custodianComparisonRules": {
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
resolveRepositoryPathCandidate, shellGlobTargetsProtectedPath,
|
|
19
19
|
unresolvedExpansionReason
|
|
20
20
|
} from "./shell-reach.ts";
|
|
21
|
-
import { grantedSourceCheckoutRootForPath, shellTouchesGrantedSourceCheckout } from "./source-checkout-paths.ts";
|
|
21
|
+
import { grantedSourceCheckoutRootForPath, piToolPathInput, referenceReadRootForPath, shellTouchesGrantedSourceCheckout } from "./source-checkout-paths.ts";
|
|
22
22
|
import { experimentLoopDecision } from "./experiment-loop-policy.ts";
|
|
23
23
|
import { extractShellWritePathCandidates } from "./shell-write-targets.js";
|
|
24
24
|
import { isProjectMutatingShellCommand } from "./readonly-inline-inspection.ts";
|
|
@@ -163,6 +163,8 @@ import { finalGateConfig } from "./acceptance-diagnostic-policy.ts";
|
|
|
163
163
|
import { fallbackBasePolicy } from "./fallback-base-policy.ts";
|
|
164
164
|
import { prefixCompletions, registerPiagentTool as registerToolDefinition, registerRuntimeCommand, registerRuntimeTool } from "../runtime/registration/extension-registration.ts";
|
|
165
165
|
import { registerTaskEvidenceTools } from "../runtime/registration/task-evidence-tools.ts";
|
|
166
|
+
import { registerAgentResources } from "../runtime/registration/agent-resources.ts";
|
|
167
|
+
import { userSkillFolders } from "../runtime/resources/agent-resources.mjs";
|
|
166
168
|
import { FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP, ONBOARDING_COMMAND_ACTIONS, WORKFLOW_COMMAND_EXCLUSIONS } from "../runtime/registration/operator-catalogs.ts";
|
|
167
169
|
import { registerPiagentStatusCommand } from "../runtime/registration/runtime-model-status.ts";
|
|
168
170
|
import { registerTaskPreflightCommand } from "../runtime/registration/task-preflight.ts";
|
|
@@ -348,9 +350,10 @@ const DEFAULT_POLICY = fallbackBasePolicy(CONTEXT_INDEX_FILE, DEFAULT_ORCHESTRAT
|
|
|
348
350
|
// so it is resolved once here rather than threaded through every profile load.
|
|
349
351
|
const PLATFORM_ROOT = findPlatformRoot(path.dirname(fileURLToPath(import.meta.url)));
|
|
350
352
|
// The skills Pi lists for the model are read where they are installed: this
|
|
351
|
-
// package's and the user's
|
|
353
|
+
// package's and the user's (also those kept for other coding agents, such as
|
|
354
|
+
// ~/.claude/skills). Reading them is allowed; writes stay in the project.
|
|
352
355
|
const SKILL_READ_ROOTS = [path.join(PLATFORM_ROOT, "packages", "piagent-core", "skills"),
|
|
353
|
-
path.join(process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent"), "skills")]
|
|
356
|
+
path.join(process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent"), "skills"), ...userSkillFolders()]
|
|
354
357
|
.flatMap((root) => { try { return [fs.realpathSync.native(root)]; } catch { return []; } });
|
|
355
358
|
const UPDATE_CHECK_MODULE = fileURLToPath(new URL("./update-check.js", import.meta.url));
|
|
356
359
|
// The installed version, read from the package this file ships in. A maintainer
|
|
@@ -924,11 +927,12 @@ function evaluatePathLikeToolAccess(
|
|
|
924
927
|
sourceCheckoutReadRoots?: string[];
|
|
925
928
|
} = {}
|
|
926
929
|
): { block: boolean; reason?: string } {
|
|
927
|
-
const
|
|
930
|
+
const piFileTool = ["read", "write", "edit", "grep", "find", "ls"].includes(toolName);
|
|
931
|
+
const scopeAwareTool = options.forceScopeAware === true || piFileTool
|
|
928
932
|
|| /(?:^|[_-])(?:fs|filesystem)(?:[_-]|$)/i.test(toolName);
|
|
929
933
|
const inspection = inspectPathInputsFromInput(
|
|
930
934
|
cwd,
|
|
931
|
-
input,
|
|
935
|
+
piFileTool ? piToolPathInput(input) : input,
|
|
932
936
|
usesFilesystemContentFields(toolName, options.allowAmbiguousFilesystemContentFields !== false)
|
|
933
937
|
);
|
|
934
938
|
if (inspection.maxDepthExceeded) {
|
|
@@ -966,6 +970,7 @@ function evaluatePathLikeToolAccess(
|
|
|
966
970
|
const fieldAccessMode = filesystemFieldAccessMode(toolName, item.field, writesFilesystem);
|
|
967
971
|
const sourceCheckoutReadRoot = fieldAccessMode === "read"
|
|
968
972
|
? grantedSourceCheckoutRootForPath(cwd, item.path, options.sourceCheckoutReadRoots ?? [])
|
|
973
|
+
?? (piFileTool && !writesFilesystem ? referenceReadRootForPath(cwd, item.path) : undefined)
|
|
969
974
|
: undefined;
|
|
970
975
|
if (scopeAwareTool && isFilesystemScopeField(item.field)) {
|
|
971
976
|
const boundary = inspectRepositoryPathBoundary(cwd, item.path);
|
|
@@ -4144,5 +4149,6 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4144
4149
|
// registers. The completion module holds only task-bound tools (memory
|
|
4145
4150
|
// citations need a task id), so it is no longer registered at all.
|
|
4146
4151
|
registerTaskEvidenceTools(pi, registrationDeps);
|
|
4152
|
+
registerAgentResources(pi);
|
|
4147
4153
|
registerSessionCommands(pi, { ...registrationDeps, ...contextCommandApi });
|
|
4148
4154
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
import { extractShellPathCandidates, normalizePathCandidate } from "./policy-core.js";
|
|
@@ -24,6 +25,36 @@ export function grantedSourceCheckoutRootForPath(cwd: string, candidate: string,
|
|
|
24
25
|
return roots.find((root) => inside(root, canonical));
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
// Folders a member points the agent to for reference (an @ mention, "read
|
|
29
|
+
// ~/Documents/old-shop"): their own files, external drives and the shared
|
|
30
|
+
// folder, read with read, grep, find or ls. Hidden entries of the home folder
|
|
31
|
+
// (shell files, histories, tool configs) and ~/Library (app data, mail,
|
|
32
|
+
// browser profiles) stay closed; a link is judged by where it leads.
|
|
33
|
+
export function referenceReadRootForPath(cwd: string, candidate: string): string | undefined {
|
|
34
|
+
let target: string, home: string;
|
|
35
|
+
try {
|
|
36
|
+
target = fs.realpathSync.native(path.resolve(cwd, normalizePathCandidate(candidate)));
|
|
37
|
+
home = fs.realpathSync.native(os.homedir());
|
|
38
|
+
} catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
if (inside(home, target)) {
|
|
42
|
+
const first = path.relative(home, target).split(path.sep)[0];
|
|
43
|
+
return first.startsWith(".") || first === "Library" ? undefined : home;
|
|
44
|
+
}
|
|
45
|
+
return ["/Volumes", "/Users/Shared", "/private/tmp"].find((root) => inside(root, target));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Pi's own file tools drop a leading @ (an @path mention) and expand ~ to the
|
|
49
|
+
// home folder before they open a path: check the path they will open.
|
|
50
|
+
export function piToolPathInput(input: Record<string, unknown>): Record<string, unknown> {
|
|
51
|
+
const value = input.path;
|
|
52
|
+
if (typeof value !== "string") return input;
|
|
53
|
+
const bare = value.startsWith("@") ? value.slice(1) : value;
|
|
54
|
+
const opened = bare === "~" ? os.homedir() : bare.startsWith("~/") ? path.join(os.homedir(), bare.slice(2)) : bare;
|
|
55
|
+
return opened === value ? input : { ...input, path: opened };
|
|
56
|
+
}
|
|
57
|
+
|
|
27
58
|
// A shell command can name a file it is about to create. realpath fails for that
|
|
28
59
|
// file, so resolve its nearest existing parent instead; otherwise a new file in
|
|
29
60
|
// the cache, spelled through an alias such as /var for /private/var, escapes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { agentResourcePaths } from '../runtime/resources/agent-resources.mjs';
|
|
5
|
+
|
|
6
|
+
// The skills and commands of the project and of the member (see
|
|
7
|
+
// agent-resources.mjs), loaded with Pi's own loaders: skills are listed to
|
|
8
|
+
// the model by name and description, commands become /name. Also the folders
|
|
9
|
+
// the sandbox opens for reading: a skill's SKILL.md and the files it bundles,
|
|
10
|
+
// where the skill is installed.
|
|
11
|
+
export async function loadAgentResources(api, sdk, cwd, home) {
|
|
12
|
+
// A folder that cannot be read never keeps the conversation from opening.
|
|
13
|
+
try { return await load(api, sdk, cwd, home); }
|
|
14
|
+
catch { return { skills: [], prompts: [], readable: [], expand: (text) => text }; }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function load(api, sdk, cwd, home) {
|
|
18
|
+
const { skillPaths, promptPaths } = agentResourcePaths({ cwd, ...(home ? { home } : {}) });
|
|
19
|
+
// Defaults off: no personal Pi folder is consulted, only these paths.
|
|
20
|
+
const skills = skillPaths.length ? api.loadSkills({ cwd, agentDir: cwd, skillPaths, includeDefaults: false }).skills : [];
|
|
21
|
+
const templates = await import(pathToFileURL(path.join(sdk, 'dist/core/prompt-templates.js')));
|
|
22
|
+
const prompts = promptPaths.length ? templates.loadPromptTemplates({ cwd, agentDir: cwd, promptPaths, includeDefaults: false }).templates : [];
|
|
23
|
+
const real = (dir) => { try { return fs.realpathSync(dir); } catch { return null; } };
|
|
24
|
+
const readable = [...new Set([...skillPaths, ...skills.map((skill) => real(skill.baseDir))].filter(Boolean))];
|
|
25
|
+
return { skills, prompts, readable, expand: (text) => expandCommand(text, skills, prompts, api.stripFrontmatter, templates.expandPromptTemplate) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A member's /skill:name, /command or /skill-name (as other agents call a
|
|
29
|
+
// skill), expanded as Pi does: a command's prompt with its arguments, or the
|
|
30
|
+
// skill's instructions followed by what the member asked. Anything else, a
|
|
31
|
+
// path such as /tmp/x included, is left as typed.
|
|
32
|
+
export function expandCommand(text, skills, prompts, stripFrontmatter, expandPromptTemplate) {
|
|
33
|
+
if (typeof text !== 'string' || !text.startsWith('/')) return text;
|
|
34
|
+
const space = text.search(/\s/), head = space < 0 ? text.slice(1) : text.slice(1, space);
|
|
35
|
+
const args = space < 0 ? '' : text.slice(space + 1).trim();
|
|
36
|
+
const explicit = head.startsWith('skill:'), name = explicit ? head.slice(6) : head;
|
|
37
|
+
if (!explicit && prompts.some((prompt) => prompt.name === name)) return expandPromptTemplate(text, prompts);
|
|
38
|
+
const skill = skills.find((item) => item.name === name);
|
|
39
|
+
if (!skill) return text;
|
|
40
|
+
let body;
|
|
41
|
+
try { body = stripFrontmatter(fs.readFileSync(skill.filePath, 'utf8')).trim(); } catch { return text; }
|
|
42
|
+
const block = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
|
|
43
|
+
return args ? `${block}\n\n${args}` : block;
|
|
44
|
+
}
|
|
@@ -15,6 +15,12 @@ import { fetchPublicPage } from './web-fetch.mjs';
|
|
|
15
15
|
const text = result => (result?.content ?? []).filter(part => part.type === 'text').map(part => part.text).join('\n').trim();
|
|
16
16
|
const first = value => String(value ?? '').split('\n')[0].slice(0, 160);
|
|
17
17
|
|
|
18
|
+
const LANGUAGES = [['pnpm', 'pnpm --version'], ['yarn', 'yarn --version'], ['bun', 'bun --version'], ['deno', 'deno --version'], ['uv', 'uv --version'],
|
|
19
|
+
['poetry', 'poetry --version'], ['go', 'go version'], ['rust (cargo)', 'cargo --version'], ['java', 'javac -version', 'test -n "$JAVA_HOME"'], ['maven', 'mvn -v'],
|
|
20
|
+
['gradle', 'gradle --version --offline'], ['kotlin', 'kotlinc -version'], ['.NET', 'dotnet --version'], ['php', 'php --version'], ['composer', 'composer --version'],
|
|
21
|
+
['ruby', 'ruby --version'], ['bundler', 'bundle --version'], ['swift', 'swift --version'], ['dart', 'dart --version'], ['flutter', 'flutter --version --suppress-analytics'],
|
|
22
|
+
['elixir', 'elixir --version']];
|
|
23
|
+
|
|
18
24
|
export async function runDoctor({ sdkRoot, origin, broker, network = true }) {
|
|
19
25
|
const results = [];
|
|
20
26
|
const check = async (label, required, run) => {
|
|
@@ -47,10 +53,12 @@ export async function runDoctor({ sdkRoot, origin, broker, network = true }) {
|
|
|
47
53
|
await boundary.invoke('write', { path: 'Dự án mới/ghi chú.md', content: 'ổn\n' });
|
|
48
54
|
if (await bash("cat 'Dự án mới/ghi chú.md'") !== 'ổn') throw Error('content changed'); return 'ok';
|
|
49
55
|
});
|
|
50
|
-
await check('
|
|
56
|
+
await check('folders outside the project are read-only', true, async () => {
|
|
51
57
|
let refused = false; try { await boundary.invoke('write', { path: '../piagent-doctor-outside.txt', content: 'x' }); } catch { refused = true; }
|
|
52
58
|
if (!refused) { fs.rmSync(path.join(path.dirname(project), 'piagent-doctor-outside.txt'), { force: true }); throw Error('a write outside the project was allowed'); }
|
|
53
|
-
|
|
59
|
+
// A folder the member points to for reference (@~/…) is readable.
|
|
60
|
+
await boundary.invoke('ls', { path: '~' });
|
|
61
|
+
return 'read, write refused';
|
|
54
62
|
});
|
|
55
63
|
await check('commands have no network unless approved', true, async () => {
|
|
56
64
|
let refused = false; try { await bash('curl -sS -m 5 https://example.com/ >/dev/null'); } catch { refused = true; }
|
|
@@ -70,6 +78,19 @@ export async function runDoctor({ sdkRoot, origin, broker, network = true }) {
|
|
|
70
78
|
await bash('cc main.c -o main && ./main'); return first(await bash('cc --version'));
|
|
71
79
|
});
|
|
72
80
|
await check('make', false, () => bash('make --version'));
|
|
81
|
+
// Tests that start a server or use a local database (tool-boundary.mjs).
|
|
82
|
+
await check('tests reach servers on this Mac (localhost)', false, async () => {
|
|
83
|
+
await boundary.invoke('write', { path: 'loopback.mjs', content: "import http from 'node:http'; const s = http.createServer((q, r) => r.end('ok')); s.listen(0, '127.0.0.1', async () => { console.log(await (await fetch(`http://127.0.0.1:${s.address().port}/`)).text()); s.close(); });\n" });
|
|
84
|
+
try { return await bash('node loopback.mjs'); }
|
|
85
|
+
catch (error) { throw Error(/loopback-blocked/.test(String(error?.message)) ? 'a local proxy listens on this Mac: tests that use localhost need run_with_network' : first(String(error?.message))); }
|
|
86
|
+
});
|
|
87
|
+
await check('package caches kept for the project', false, async () => { await bash('mkdir -p "$npm_config_cache" && touch "$npm_config_cache/.piagent-doctor"'); return boundary.packageCache; });
|
|
88
|
+
// The languages installed on this Mac, each started once in the sandbox.
|
|
89
|
+
// (macOS ships stubs for some, such as javac without a JDK: a probe decides.)
|
|
90
|
+
for (const [label, command, probe = `command -v ${command.split(' ')[0]} >/dev/null`] of LANGUAGES) {
|
|
91
|
+
if (await bash(`${probe} && echo yes || true`) !== 'yes') continue;
|
|
92
|
+
await check(label, false, async () => first(await bash(`${command} 2>&1`)));
|
|
93
|
+
}
|
|
73
94
|
if (network) await check('web_fetch reads a public page', false, async () => `status ${(await fetchPublicPage('https://example.com/', { maxChars: 1000 })).status}`);
|
|
74
95
|
} finally { await boundary?.dispose(); fs.rmSync(project, { recursive: true, force: true }); }
|
|
75
96
|
if (broker) {
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
// What test runners and package managers of the common languages need in the
|
|
7
|
+
// company sandbox beyond the toolchains themselves (toolchain.mjs):
|
|
8
|
+
// - package caches that persist for the project between conversations,
|
|
9
|
+
// outside the project and private to it: the first install asks for
|
|
10
|
+
// network (run_with_network), later runs work offline;
|
|
11
|
+
// - the member's own dependency caches as read-only seeds, where the tool can
|
|
12
|
+
// layer one (Go, Gradle, Maven), so what is already downloaded is reused;
|
|
13
|
+
// - runtimes installed in places the sandbox otherwise keeps closed (JDKs
|
|
14
|
+
// under ~/Library/Java, the Android SDK, the system gem folder, MacPorts);
|
|
15
|
+
// - SwiftPM and xcodebuild, which cannot start their own sandbox inside this
|
|
16
|
+
// one: their manifest sandbox is off; this sandbox confines the same
|
|
17
|
+
// processes.
|
|
18
|
+
const KEEP_DAYS = 30;
|
|
19
|
+
const existing = dir => { try { const real = fs.realpathSync(dir); return fs.statSync(real).isDirectory() ? real : null; } catch { return null; } };
|
|
20
|
+
|
|
21
|
+
// Unused project caches are removed after a month.
|
|
22
|
+
function prune(root, keep) {
|
|
23
|
+
let entries = [];
|
|
24
|
+
try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { return; }
|
|
25
|
+
const limit = Date.now() - KEEP_DAYS * 24 * 60 * 60 * 1000;
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const dir = path.join(root, entry.name);
|
|
28
|
+
try { if (entry.isDirectory() && dir !== keep && fs.statSync(dir).mtimeMs < limit) fs.rmSync(dir, { recursive: true, force: true }); } catch { /* in use or gone */ }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function javaHome(userHome) {
|
|
33
|
+
try {
|
|
34
|
+
const home = execFileSync('/usr/libexec/java_home', [], { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'], env: { HOME: userHome, PATH: '/usr/bin:/bin' } }).trim();
|
|
35
|
+
return existing(home);
|
|
36
|
+
} catch { return null; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// SwiftPM and xcodebuild start sandbox-exec for package manifests and
|
|
40
|
+
// plugins; inside this sandbox that fails ("sandbox_apply: Operation not
|
|
41
|
+
// permitted"). These wrappers, first on PATH, pass the switch that turns it
|
|
42
|
+
// off, then run the member's own swift/xcodebuild found further on PATH.
|
|
43
|
+
const SWIFT_WRAPPER = `#!/bin/sh
|
|
44
|
+
self=$(cd "$(dirname "$0")" && pwd -P)
|
|
45
|
+
real=; IFS=:; for dir in $PATH; do [ "$dir" = "$self" ] && continue; [ -x "$dir/$(basename "$0")" ] && { real="$dir/$(basename "$0")"; break; }; done; unset IFS
|
|
46
|
+
[ -n "$real" ] || { echo "$(basename "$0"): not installed" >&2; exit 127; }
|
|
47
|
+
case "$(basename "$0"):$1" in
|
|
48
|
+
swift:build|swift:test|swift:run) sub=$1; shift; exec "$real" "$sub" --disable-sandbox "$@" ;;
|
|
49
|
+
swift:package) shift; exec "$real" package --disable-sandbox "$@" ;;
|
|
50
|
+
xcodebuild:*)
|
|
51
|
+
# Build products go to the project's cache (~/Library/Developer is closed).
|
|
52
|
+
derived=; case " $* " in *" -derivedDataPath "*) ;; *" -scheme "*|*" -xctestrun "*|*" -testProductsPath "*) derived="-derivedDataPath $PIAGENT_DERIVED_DATA" ;; esac
|
|
53
|
+
exec "$real" -IDEPackageSupportDisableManifestSandbox=YES -IDEPackageSupportDisablePluginExecutionSandbox=YES $derived "$@" ;;
|
|
54
|
+
*) exec "$real" "$@" ;;
|
|
55
|
+
esac
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
export function languageEnvironment({ userHome, repositoryTop, home }) {
|
|
59
|
+
const root = path.join(userHome, 'Library/Caches/Piagent/sandbox');
|
|
60
|
+
const cache = path.join(root, createHash('sha256').update(repositoryTop).digest('hex').slice(0, 24));
|
|
61
|
+
fs.mkdirSync(cache, { recursive: true, mode: 0o700 });
|
|
62
|
+
fs.chmodSync(cache, 0o700);
|
|
63
|
+
const now = new Date(); try { fs.utimesSync(cache, now, now); } catch { /* best effort */ }
|
|
64
|
+
prune(root, cache);
|
|
65
|
+
const at = name => path.join(cache, name);
|
|
66
|
+
|
|
67
|
+
// Read-only: runtimes and the member's caches used as seeds.
|
|
68
|
+
const seeds = {
|
|
69
|
+
gradle: existing(path.join(userHome, '.gradle/caches')),
|
|
70
|
+
maven: existing(path.join(userHome, '.m2/repository')),
|
|
71
|
+
go: existing(path.join(process.env.GOMODCACHE || path.join(userHome, 'go/pkg/mod'), 'cache/download')),
|
|
72
|
+
// .NET restores over TLS through the macOS Security framework, closed
|
|
73
|
+
// here: packages the member restored are read from their own folder.
|
|
74
|
+
nuget: existing(path.join(userHome, '.nuget/packages')),
|
|
75
|
+
};
|
|
76
|
+
const java = javaHome(userHome), android = existing(path.join(userHome, 'Library/Android/sdk'));
|
|
77
|
+
const readRoots = [...new Set([...Object.values(seeds), java, android, existing(path.join(userHome, 'Library/Java/JavaVirtualMachines')),
|
|
78
|
+
existing('/Library/Ruby'), existing('/opt/local')].filter(Boolean))];
|
|
79
|
+
|
|
80
|
+
const bin = path.join(home, '.piagent/bin');
|
|
81
|
+
fs.mkdirSync(bin, { recursive: true, mode: 0o700 });
|
|
82
|
+
for (const name of ['swift', 'xcodebuild']) fs.writeFileSync(path.join(bin, name), SWIFT_WRAPPER, { mode: 0o755 });
|
|
83
|
+
// pnpm's store: its own settings file, which npm does not read (an npm_config_
|
|
84
|
+
// variable would make every npm command warn about an unknown setting).
|
|
85
|
+
const pnpm = path.join(home, 'Library/Preferences/pnpm');
|
|
86
|
+
fs.mkdirSync(pnpm, { recursive: true, mode: 0o700 });
|
|
87
|
+
fs.writeFileSync(path.join(pnpm, 'rc'), `store-dir=${at('pnpm-store')}\n`, { mode: 0o600 });
|
|
88
|
+
|
|
89
|
+
const common = {
|
|
90
|
+
npm_config_cache: at('npm'), YARN_CACHE_FOLDER: at('yarn'), BUN_INSTALL_CACHE_DIR: at('bun'), DENO_DIR: at('deno'),
|
|
91
|
+
PIP_CACHE_DIR: at('pip'), UV_CACHE_DIR: at('uv'), POETRY_CACHE_DIR: at('poetry'), XDG_CACHE_HOME: at('xdg'),
|
|
92
|
+
GOPATH: at('go'), GOMODCACHE: at('go/pkg/mod'), GOCACHE: at('go/build'), GOFLAGS: '-modcacherw',
|
|
93
|
+
CARGO_HOME: at('cargo'), GRADLE_USER_HOME: at('gradle'), NUGET_PACKAGES: at('nuget'), COMPOSER_CACHE_DIR: at('composer'),
|
|
94
|
+
PUB_CACHE: at('pub'), GEM_HOME: at('gem'), BUNDLE_PATH: at('bundle'), CP_HOME_DIR: at('cocoapods'), PIAGENT_DERIVED_DATA: at('DerivedData'),
|
|
95
|
+
DOTNET_CLI_TELEMETRY_OPTOUT: '1', DOTNET_NOLOGO: '1', DOTNET_SKIP_FIRST_TIME_EXPERIENCE: '1',
|
|
96
|
+
MAVEN_OPTS: [`-Dmaven.repo.local=${at('m2/repository')}`, ...(seeds.maven ? [`-Dmaven.repo.local.tail=${seeds.maven}`] : [])].join(' '),
|
|
97
|
+
...(seeds.gradle ? { GRADLE_RO_DEP_CACHE: seeds.gradle } : {}),
|
|
98
|
+
...(seeds.nuget ? { NUGET_FALLBACK_PACKAGES: seeds.nuget } : {}),
|
|
99
|
+
...(java ? { JAVA_HOME: java } : {}),
|
|
100
|
+
...(android ? { ANDROID_HOME: android, ANDROID_SDK_ROOT: android } : {}),
|
|
101
|
+
};
|
|
102
|
+
// Go reads modules the member already has from their own cache first; an
|
|
103
|
+
// approved command then goes on to the usual proxies.
|
|
104
|
+
const seed = seeds.go ? `file://${seeds.go},` : '';
|
|
105
|
+
// .NET keeps its named mutexes (NuGet's among them) as files in
|
|
106
|
+
// /tmp/.dotnet, whatever TMPDIR says.
|
|
107
|
+
const dotnet = [path.join(userHome, '.dotnet/dotnet'), '/usr/local/share/dotnet/dotnet', '/opt/homebrew/bin/dotnet'].some(file => fs.existsSync(file));
|
|
108
|
+
if (dotnet) try { fs.mkdirSync('/private/tmp/.dotnet', { recursive: true, mode: 0o777 }); } catch { /* another user's */ }
|
|
109
|
+
return {
|
|
110
|
+
cache, readRoots, writeRoots: [cache, ...(dotnet && existing('/private/tmp/.dotnet') ? ['/private/tmp/.dotnet'] : [])], bin,
|
|
111
|
+
env: {
|
|
112
|
+
// .NET's dual-stack sockets reach 127.0.0.1 as ::ffff:127.0.0.1, which
|
|
113
|
+
// the loopback rule does not name (the test host would not connect).
|
|
114
|
+
offline: { ...common, GOPROXY: `${seed}off`, npm_config_prefer_offline: 'true', DOTNET_SYSTEM_NET_DISABLEIPV6: '1' },
|
|
115
|
+
network: { ...common, GOPROXY: `${seed}${process.env.GOPROXY || 'https://proxy.golang.org,direct'}` },
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -22,14 +22,15 @@ export function projectInstructions(cwd, top = cwd) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// Managed sessions never consult the personal trust store or execute resources
|
|
25
|
-
// discovered in a project.
|
|
26
|
-
|
|
25
|
+
// discovered in a project. Skills and commands are text (agent-skills.mjs):
|
|
26
|
+
// what they ask for runs with the session's own tools.
|
|
27
|
+
export function managedResourceLoader(api, { systemPrompt, extensions = [], agentsFiles = [], skills = [], prompts = [] } = {}) {
|
|
27
28
|
const runtime = api.createExtensionRuntime();
|
|
28
29
|
const empty = Object.freeze([]);
|
|
29
30
|
return Object.freeze({
|
|
30
31
|
getExtensions: () => ({ extensions, errors: empty, runtime }),
|
|
31
|
-
getSkills: () => ({ skills:
|
|
32
|
-
getPrompts: () => ({ prompts:
|
|
32
|
+
getSkills: () => ({ skills: [...skills], diagnostics: empty }),
|
|
33
|
+
getPrompts: () => ({ prompts: [...prompts], diagnostics: empty }),
|
|
33
34
|
getThemes: () => ({ themes: empty, diagnostics: empty }),
|
|
34
35
|
getAgentsFiles: () => ({ agentsFiles: agentsFiles.map((file) => ({ ...file })) }),
|
|
35
36
|
getSystemPrompt: () => typeof systemPrompt === 'function' ? systemPrompt() : systemPrompt,
|