@piagent/platform 1.8.0 → 1.9.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/AGENTS.md +7 -0
- package/CHANGELOG.md +132 -2
- package/README.md +45 -70
- package/README.vi.md +34 -61
- package/architecture/layers.json +16 -3
- package/catalog/capabilities.json +14 -7
- package/catalog/claude-model-additions.json +42 -0
- package/docs/auto-delegation-policy.md +2 -0
- package/docs/command-reference-vietnamese.md +68 -90
- package/docs/context-window-policy.md +5 -5
- package/docs/distribution-standard.md +13 -16
- package/docs/en/architecture.md +9 -1
- package/docs/herdr-workflow.md +2 -3
- package/docs/languages.json +4 -4
- package/docs/managed-local-acceptance.md +403 -0
- package/docs/mcp-and-tools.md +5 -7
- package/docs/memory-policy.md +3 -3
- package/docs/model-options.md +2 -2
- package/docs/oauth-providers.md +1 -1
- package/docs/operator-manual-vietnamese.md +82 -127
- package/docs/package-architecture-notes.md +4 -4
- package/docs/piagent-core-improvement-plan.md +3 -5
- package/docs/piagent-core-plan-checkpoint-2026-09-06.json +2 -13
- package/docs/piagent-runtime-benchmark-execution-plan.md +10 -8
- package/docs/project-adapters.md +3 -3
- package/docs/project-onboarding.md +15 -16
- package/docs/publishing-for-teams.md +3 -1
- package/docs/quality-benchmark.md +2 -0
- package/docs/quickstart-vietnamese.md +23 -24
- package/docs/release-install-policy.md +3 -2
- package/docs/research/harness-next-evidence-review.md +7 -7
- package/docs/runtime-harness-standard.md +2 -0
- package/docs/runtime-policy-design.md +2 -0
- package/docs/runtime-quality-baseline.md +2 -0
- package/docs/subagent-orchestration-capabilities.md +1 -1
- package/docs/subagents-and-multiagent.md +10 -8
- package/docs/task-implementation-contract.md +2 -0
- package/docs/task-lifecycle-vietnamese.md +2 -0
- package/docs/team-onboarding.md +28 -59
- package/docs/usage-observability.md +2 -2
- package/docs/vi/architecture.md +9 -1
- package/docs/workflow-recipes.md +6 -10
- package/docs-site/architecture.html +4 -3
- package/docs-site/assets/docs.css +80 -0
- package/docs-site/benchmark.html +7 -1
- package/docs-site/commands.html +37 -23
- package/docs-site/content/architecture.html +2 -2
- package/docs-site/content/benchmark.html +5 -0
- package/docs-site/content/commands.html +35 -22
- package/docs-site/content/ecosystem.html +144 -0
- package/docs-site/content/en/architecture.html +1 -1
- package/docs-site/content/en/benchmark.html +5 -0
- package/docs-site/content/en/commands.html +30 -13
- package/docs-site/content/en/ecosystem.html +144 -0
- package/docs-site/content/en/index.html +24 -9
- package/docs-site/content/en/quickstart.html +10 -12
- package/docs-site/content/en/security.html +1 -1
- package/docs-site/content/en/team.html +2 -2
- package/docs-site/content/en/whats-new.html +92 -1
- package/docs-site/content/en/workflows.html +29 -31
- package/docs-site/content/index.html +24 -9
- package/docs-site/content/quickstart.html +10 -12
- package/docs-site/content/security.html +2 -2
- package/docs-site/content/team.html +2 -2
- package/docs-site/content/whats-new.html +92 -1
- package/docs-site/content/workflows.html +36 -38
- package/docs-site/context-engine.html +2 -1
- package/docs-site/ecosystem.html +283 -0
- package/docs-site/en/architecture.html +3 -2
- package/docs-site/en/benchmark.html +7 -1
- package/docs-site/en/commands.html +32 -14
- package/docs-site/en/context-engine.html +2 -1
- package/docs-site/en/ecosystem.html +283 -0
- package/docs-site/en/index.html +30 -14
- package/docs-site/en/mcp-approval.html +2 -1
- package/docs-site/en/mcp-auth.html +2 -1
- package/docs-site/en/mcp-servers.html +2 -1
- package/docs-site/en/mcp.html +2 -1
- package/docs-site/en/permissions.html +2 -1
- package/docs-site/en/profiles.html +2 -1
- package/docs-site/en/quickstart.html +17 -18
- package/docs-site/en/runtime.html +2 -1
- package/docs-site/en/scope.html +2 -1
- package/docs-site/en/security.html +6 -5
- package/docs-site/en/team.html +4 -3
- package/docs-site/en/whats-new.html +100 -8
- package/docs-site/en/workflows.html +34 -35
- package/docs-site/index.html +30 -14
- package/docs-site/mcp-approval.html +2 -1
- package/docs-site/mcp-auth.html +2 -1
- package/docs-site/mcp-servers.html +2 -1
- package/docs-site/mcp.html +2 -1
- package/docs-site/permissions.html +2 -1
- package/docs-site/profiles.html +2 -1
- package/docs-site/quickstart.html +17 -18
- package/docs-site/runtime.html +2 -1
- package/docs-site/scope.html +2 -1
- package/docs-site/security.html +7 -6
- package/docs-site/team.html +4 -3
- package/docs-site/whats-new.html +100 -8
- package/docs-site/workflows.html +41 -42
- package/evals/fs5-causal-arm.v1.json +2 -2
- package/evals/private-holdout-v1/CURRENT_BOUNDARY.md +17 -0
- package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
- package/package.json +8 -7
- package/packages/piagent-core/README.md +15 -23
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +2 -0
- package/packages/piagent-core/extensions/experiment-loop-policy.ts +75 -0
- package/packages/piagent-core/extensions/local-state-path.js +15 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +108 -960
- package/packages/piagent-core/extensions/policy-core.js +2 -2
- package/packages/piagent-core/extensions/source-checkout-paths.ts +54 -0
- package/packages/piagent-core/managed/broker-client.mjs +49 -0
- package/packages/piagent-core/managed/capabilities.mjs +28 -0
- package/packages/piagent-core/managed/client-agent.mjs +6 -0
- package/packages/piagent-core/managed/compaction.mjs +63 -0
- package/packages/piagent-core/managed/doctor.mjs +107 -0
- package/packages/piagent-core/managed/helper-roles.mjs +57 -0
- package/packages/piagent-core/managed/native-catalog.mjs +42 -0
- package/packages/piagent-core/managed/patch-snapshot.mjs +48 -0
- package/packages/piagent-core/managed/repository-operation.mjs +84 -0
- package/packages/piagent-core/managed/request-stream.mjs +130 -0
- package/packages/piagent-core/managed/resource-loader.mjs +42 -0
- package/packages/piagent-core/managed/review-snapshot-worker.mjs +27 -0
- package/packages/piagent-core/managed/sandbox-diagnostic.mjs +12 -0
- package/packages/piagent-core/managed/search-fallback.mjs +93 -0
- package/packages/piagent-core/managed/session.mjs +493 -0
- package/packages/piagent-core/managed/store.d.mts +3 -0
- package/packages/piagent-core/managed/store.mjs +64 -0
- package/packages/piagent-core/managed/tool-boundary.mjs +275 -0
- package/packages/piagent-core/managed/tool-worker.mjs +36 -0
- package/packages/piagent-core/managed/toolchain.mjs +120 -0
- package/packages/piagent-core/managed/web-fetch.mjs +105 -0
- package/packages/piagent-core/managed/web-search.mjs +131 -0
- package/packages/piagent-core/managed/workflow.mjs +326 -0
- package/packages/piagent-core/package.json +2 -5
- package/packages/piagent-core/policies/base-policy.json +80 -26
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +3 -3
- package/packages/piagent-core/runtime/hooks/input-hook.ts +35 -196
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +2 -0
- package/packages/piagent-core/runtime/inspection/approval-broker.ts +9 -1
- package/packages/piagent-core/runtime/managed-failure.d.mts +10 -0
- package/packages/piagent-core/runtime/managed-failure.mjs +166 -0
- package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +8 -29
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +1 -1
- package/packages/piagent-core/runtime/policy/block-remedy.ts +5 -3
- package/packages/piagent-core/runtime/registration/context-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/knowledge-tools.ts +7 -7
- package/packages/piagent-core/runtime/registration/policy-tools.ts +2 -2
- package/packages/piagent-core/runtime/registration/session-commands.ts +11 -26
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +1 -1
- package/packages/piagent-core/runtime/session/system-prompt.ts +6 -7
- package/packages/piagent-core/runtime/workflows/webui-runtime-command.ts +6 -6
- package/packages/piagent-core/runtime/workflows/webui-workflow.ts +6 -11
- package/packages/piagent-core/security/sensitive-text.js +9 -0
- package/packages/piagent-core/skills/piagent-ops/SKILL.md +8 -10
- package/packages/piagent-core/skills/test-audit/SKILL.md +101 -0
- package/packages/piagent-core/subagents/piagent-planner.md +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-DMU7Xe_O.js → MarkdownMessage-BRPIMCRh.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DNJ1vH5d.js +228 -0
- package/packages/piagent-webui/dist/client/assets/{index-DVahUVWy.css → index-DgSmcnP8.css} +1 -1
- package/packages/piagent-webui/dist/client/index.html +2 -2
- package/packages/piagent-webui/extension/piagent-webui.ts +11 -11
- package/packages/piagent-webui/extension/session-stream-adapter.ts +3 -1
- package/packages/piagent-webui/extension/terminal-session-adapter.ts +6 -1
- package/packages/piagent-webui/gateway/company-relay.ts +308 -0
- package/packages/piagent-webui/gateway/control-socket.ts +39 -12
- package/packages/piagent-webui/gateway/gateway-service.ts +109 -28
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +58 -2
- package/packages/piagent-webui/gateway/managed-gateway.mjs +41 -0
- package/packages/piagent-webui/gateway/managed-launch.ts +145 -0
- package/packages/piagent-webui/gateway/managed-projection.ts +20 -0
- package/packages/piagent-webui/gateway/pi-host.ts +22 -4
- package/packages/piagent-webui/gateway/runtime-session-controls.ts +5 -2
- package/packages/piagent-webui/gateway/session-approval-authority.ts +28 -0
- package/packages/piagent-webui/gateway/session-catalog.ts +11 -3
- package/packages/piagent-webui/gateway/session-command-controller.ts +8 -2
- package/packages/piagent-webui/gateway/session-file-facts.ts +25 -0
- package/packages/piagent-webui/gateway/session-inspection-registry.ts +40 -9
- package/packages/piagent-webui/gateway/session-list-cache.ts +68 -0
- package/packages/piagent-webui/gateway/session-runtime-ownership.ts +8 -1
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +20 -27
- package/packages/piagent-webui/ownership/managed-terminal-lease.ts +27 -0
- package/packages/piagent-webui/ownership/profile-state.ts +2 -0
- package/packages/piagent-webui/ownership/session-lease-store.ts +22 -1
- package/packages/piagent-webui/server/approval-projection.ts +9 -0
- package/packages/piagent-webui/server/core-inspection-provider.ts +4 -9
- package/packages/piagent-webui/server/loopback-server.ts +37 -1
- package/packages/piagent-webui/server/message-stop-reason.ts +11 -0
- package/packages/piagent-webui/server/tool-call-summary.ts +185 -0
- package/packages/piagent-webui/server/transcript-projection.ts +86 -3
- package/packs/engineering-base/pack.json +4 -0
- package/schemas/piagent-webui/runtime-command-v1.schema.json +1 -2
- package/schemas/piagent-webui/session-catalog-v1.schema.json +8 -0
- package/schemas/piagent-webui/transcript-v1.schema.json +94 -1
- package/scripts/build-docs-site.mjs +23 -12
- package/scripts/check-public-wording.mjs +5 -6
- package/scripts/check-runtime-advisories.mjs +23 -6
- package/scripts/claude-model-additions.mjs +33 -0
- package/scripts/configure-model-scope.sh +30 -6
- package/scripts/configure-subagents.sh +1 -1
- package/scripts/explain-command.mjs +5 -6
- package/scripts/init-project.sh +0 -2
- package/scripts/install-global.sh +3 -2
- package/scripts/model-scope-prune.mjs +75 -0
- package/scripts/phase-tools-evaluation.mjs +9 -4
- package/scripts/piagent-cli.mjs +51 -2
- package/scripts/piagent-dashboard.mjs +27 -4
- package/scripts/piagent-installations.mjs +26 -0
- package/scripts/piagent-studio.mjs +155 -0
- package/scripts/runtime-advisory-exceptions.json +29 -0
- package/scripts/setup.sh +10 -6
- package/scripts/team-doctor.sh +20 -1
- package/scripts/typescript-loader.mjs +6 -0
- package/scripts/verify-local.sh +2 -2
- package/templates/global/settings.json +3 -1
- package/templates/project/.pi/project-context.md +3 -3
- package/templates/project/AGENTS.md +4 -5
- package/docs/piagent-core-reasoning-research-2026-09-06.md +0 -225
- package/docs/piagent-core-research-sources-2026-09-06.json +0 -416
- package/packages/piagent-webui/dist/client/assets/index-Cui9AAV_.js +0 -176
|
@@ -55,19 +55,25 @@ const NAV = [
|
|
|
55
55
|
href: "/",
|
|
56
56
|
nav: "Overview",
|
|
57
57
|
title: "Pi Agent Platform",
|
|
58
|
-
lead: "
|
|
58
|
+
lead: "Coding agent cho cả team: một dashboard cho Terminal và trình duyệt, model cá nhân hoặc của công ty, guard kiểm tra mọi tool call."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
slug: "ecosystem",
|
|
62
|
+
nav: "Hệ sinh thái",
|
|
63
|
+
title: "Piagent, Agent Watch và Agent Studio",
|
|
64
|
+
lead: "Một dashboard, hai cách dùng model: tài khoản của bạn, hoặc model của công ty qua Agent Watch và Agent Studio, với Harness tự kiểm tra từng lượt."
|
|
59
65
|
},
|
|
60
66
|
{
|
|
61
67
|
slug: "quickstart",
|
|
62
68
|
nav: "Quickstart",
|
|
63
69
|
title: "Step by step cho thành viên mới",
|
|
64
|
-
lead: "Hai lệnh để cài,
|
|
70
|
+
lead: "Hai lệnh để cài, mở repo, chọn profile và quyền, rồi giao việc bằng lời thường; guard kiểm tra từng tool call."
|
|
65
71
|
},
|
|
66
72
|
{
|
|
67
73
|
slug: "whats-new",
|
|
68
74
|
nav: "Có gì mới",
|
|
69
|
-
title: "
|
|
70
|
-
lead: "
|
|
75
|
+
title: "Bản 1.9.0: nói việc bằng lời, Harness của team kiểm tra",
|
|
76
|
+
lead: "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."
|
|
71
77
|
},
|
|
72
78
|
{
|
|
73
79
|
slug: "scope",
|
|
@@ -102,7 +108,7 @@ const NAV = [
|
|
|
102
108
|
slug: "workflows",
|
|
103
109
|
nav: "Workflows",
|
|
104
110
|
title: "Workflow thay đổi thế nào sau update",
|
|
105
|
-
lead: "
|
|
111
|
+
lead: "Từ bản 1.9.0 không còn launcher workflow: nói việc cần làm bằng lời thường. Các command xem thêm chỉ để quan sát và không bao giờ mở lượt model."
|
|
106
112
|
}
|
|
107
113
|
]
|
|
108
114
|
},
|
|
@@ -142,7 +148,7 @@ const NAV = [
|
|
|
142
148
|
slug: "security",
|
|
143
149
|
nav: "Security model",
|
|
144
150
|
title: "Policy enforcement layer, không phải OS sandbox",
|
|
145
|
-
lead: "Platform ưu tiên fail-closed trong bề mặt tool được kiểm soát: khi lock sai, profile sai hoặc
|
|
151
|
+
lead: "Platform ưu tiên fail-closed trong bề mặt tool được kiểm soát: khi lock sai, profile sai hoặc quyền không đủ thì chặn, không đoán."
|
|
146
152
|
},
|
|
147
153
|
{
|
|
148
154
|
slug: "architecture",
|
|
@@ -182,17 +188,22 @@ const EN_PAGE_COPY = {
|
|
|
182
188
|
index: {
|
|
183
189
|
nav: "Overview",
|
|
184
190
|
title: "Pi Agent Platform",
|
|
185
|
-
lead: "
|
|
191
|
+
lead: "A coding agent for the whole team: one dashboard for Terminal and the browser, personal or company models, and a guard that checks every tool call."
|
|
192
|
+
},
|
|
193
|
+
ecosystem: {
|
|
194
|
+
nav: "Ecosystem",
|
|
195
|
+
title: "Piagent, Agent Watch and Agent Studio",
|
|
196
|
+
lead: "One dashboard, two ways to use models: your own account, or the company's models through Agent Watch and Agent Studio, with a Harness that checks every turn."
|
|
186
197
|
},
|
|
187
198
|
quickstart: {
|
|
188
199
|
nav: "Quickstart",
|
|
189
200
|
title: "Step by step for new team members",
|
|
190
|
-
lead: "Install with two commands, open a repository,
|
|
201
|
+
lead: "Install with two commands, open a repository, choose its profile and permissions, then ask in plain language; the guard checks every tool call."
|
|
191
202
|
},
|
|
192
203
|
"whats-new": {
|
|
193
204
|
nav: "What’s new",
|
|
194
|
-
title: "
|
|
195
|
-
lead: "
|
|
205
|
+
title: "1.9.0: ask in plain language, the team's Harness checks",
|
|
206
|
+
lead: "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."
|
|
196
207
|
},
|
|
197
208
|
scope: {
|
|
198
209
|
nav: "Platform scope",
|
|
@@ -217,7 +228,7 @@ const EN_PAGE_COPY = {
|
|
|
217
228
|
workflows: {
|
|
218
229
|
nav: "Workflows",
|
|
219
230
|
title: "How workflows change after the update",
|
|
220
|
-
lead: "
|
|
231
|
+
lead: "Since 1.9.0 there are no workflow launchers: say what you need in plain language. The optional inspection commands only observe and never start a model turn."
|
|
221
232
|
},
|
|
222
233
|
mcp: {
|
|
223
234
|
nav: "MCP overview",
|
|
@@ -242,7 +253,7 @@ const EN_PAGE_COPY = {
|
|
|
242
253
|
security: {
|
|
243
254
|
nav: "Security model",
|
|
244
255
|
title: "Policy enforcement, not an OS sandbox",
|
|
245
|
-
lead: "Controlled tool surfaces fail closed when profiles, integrity locks
|
|
256
|
+
lead: "Controlled tool surfaces fail closed when profiles, integrity locks or permissions are invalid."
|
|
246
257
|
},
|
|
247
258
|
architecture: {
|
|
248
259
|
nav: "Architecture",
|
|
@@ -7,10 +7,10 @@ const root = path.resolve(import.meta.dirname, "..");
|
|
|
7
7
|
const surfaces = ["README.md", "docs", "docs-site/content", "scripts/build-docs-site.mjs",
|
|
8
8
|
"packages/piagent-core/README.md", "packages/piagent-core/prompts", "packages/piagent-core/mcp",
|
|
9
9
|
"packages/piagent-core/extensions", "packages/piagent-core/runtime", "templates/project/AGENTS.md"];
|
|
10
|
-
// Explicit reviewed research records, never a marker supplied by arbitrary docs.
|
|
11
|
-
const researchRecords = new Set(["docs/piagent-core-improvement-plan.md",
|
|
12
|
-
"docs/piagent-core-reasoning-research-2026-09-06.md", "docs/piagent-core-research-sources-2026-09-06.json"]);
|
|
13
10
|
const sourceNames = ["Codex CLI", "Claude CLI", "Claude Code"];
|
|
11
|
+
// Other agent products and projects. Public text describes a technique in its
|
|
12
|
+
// own words and names none of them; whole words only ("declined" is not one).
|
|
13
|
+
const otherAgents = /\b(?:Aider|SWE-agent|OpenHands|Oh My Pi|oh-my-pi|OpenClaw|OpenClaude|LangGraph|LangChain|Windsurf|Cline|Roo Code|Kilo Code|autoresearch)\b/g;
|
|
14
14
|
const prohibited = ["platform-migration", "codex-migration", "codex-parity", "benchmark-parity",
|
|
15
15
|
"harness-migration", "agent-stuff", "mitsuhiko", "claude mcp", "codex mcp",
|
|
16
16
|
"Codex-inspired", "Codex-grade", "Pi vs Codex", "vs Claude", "reference repo",
|
|
@@ -18,9 +18,8 @@ const prohibited = ["platform-migration", "codex-migration", "codex-parity", "be
|
|
|
18
18
|
const escaped = value => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
19
19
|
|
|
20
20
|
export function publicWordingViolations(file, text) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
return [...text.matchAll(pattern)].map(match => ({ file,
|
|
21
|
+
const pattern = new RegExp([...prohibited, ...sourceNames].map(escaped).join("|"), "gi");
|
|
22
|
+
return [...text.matchAll(pattern), ...text.matchAll(otherAgents)].sort((a, b) => a.index - b.index).map(match => ({ file,
|
|
24
23
|
line: text.slice(0, match.index).split("\n").length, keyword: match[0] }));
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
//
|
|
5
5
|
// The rule is "no moderate, high, or critical advisory in the pinned Pi host
|
|
6
6
|
// and add-on tree".
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// The only exceptions are listed in runtime-advisory-exceptions.json, each an
|
|
8
|
+
// owner decision for one advisory at one package path in the tree, with a day
|
|
9
|
+
// it stops applying. The same advisory anywhere else, any other advisory, and
|
|
10
|
+
// an exception past its day all block. Its tests check every entry.
|
|
11
|
+
import fs from "node:fs";
|
|
10
12
|
|
|
11
13
|
function readStdin() {
|
|
12
14
|
return new Promise((resolve, reject) => {
|
|
@@ -43,7 +45,11 @@ if (report.auditReportVersion !== 2) {
|
|
|
43
45
|
process.exit(1);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const
|
|
48
|
+
const { exceptions } = JSON.parse(fs.readFileSync(new URL("./runtime-advisory-exceptions.json", import.meta.url), "utf8"));
|
|
49
|
+
// A test seam for the day exceptions are judged on; the gate uses today (UTC).
|
|
50
|
+
const today = process.env.PIAGENT_ADVISORY_TODAY ?? new Date().toISOString().slice(0, 10);
|
|
51
|
+
|
|
52
|
+
const blocking = [], accepted = [];
|
|
47
53
|
for (const [name, vulnerability] of Object.entries(report.vulnerabilities ?? {})) {
|
|
48
54
|
if (!["moderate", "high", "critical"].includes(vulnerability.severity)) continue;
|
|
49
55
|
const ids = advisoryIdsFor(vulnerability);
|
|
@@ -51,13 +57,22 @@ for (const [name, vulnerability] of Object.entries(report.vulnerabilities ?? {})
|
|
|
51
57
|
blocking.push({ name, severity: vulnerability.severity, id: "(no advisory id)" });
|
|
52
58
|
continue;
|
|
53
59
|
}
|
|
60
|
+
const nodes = Array.isArray(vulnerability.nodes) ? vulnerability.nodes : [];
|
|
54
61
|
for (const id of ids) {
|
|
55
|
-
|
|
62
|
+
const exception = exceptions.find((entry) => entry.id === id && entry.package === name
|
|
63
|
+
&& nodes.length > 0 && nodes.every((node) => node === entry.node));
|
|
64
|
+
if (exception && today <= exception.until) accepted.push({ name, severity: vulnerability.severity, id, exception });
|
|
65
|
+
else blocking.push({ name, severity: vulnerability.severity, id, expired: exception?.until });
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
for (const entry of accepted) {
|
|
70
|
+
console.log(`ACCEPTED: ${entry.severity} advisory ${entry.id} in ${entry.name} at ${entry.exception.node} until ${entry.exception.until} (owner decision ${entry.exception.decided})`);
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
const failures = blocking.map(
|
|
60
74
|
(entry) => `${entry.severity} advisory ${entry.id} in ${entry.name} blocks the supported runtime`
|
|
75
|
+
+ (entry.expired ? ` (its exception ended ${entry.expired})` : "")
|
|
61
76
|
);
|
|
62
77
|
|
|
63
78
|
if (failures.length > 0) {
|
|
@@ -67,4 +82,6 @@ if (failures.length > 0) {
|
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
const counts = report.metadata?.vulnerabilities ?? {};
|
|
70
|
-
console.log(
|
|
85
|
+
console.log(accepted.length
|
|
86
|
+
? `PASS: no unaccepted moderate, high, or critical advisory (${accepted.length} accepted, each until its end day)`
|
|
87
|
+
: `PASS: no moderate, high, or critical advisory (${counts.moderate ?? 0} moderate, ${counts.high ?? 0} high, ${counts.critical ?? 0} critical)`);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const additions = JSON.parse(fs.readFileSync(new URL("../catalog/claude-model-additions.json", import.meta.url), "utf8"));
|
|
5
|
+
const object = value => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6
|
+
|
|
7
|
+
// Explicit model-scope setup only. Keep existing models, endpoints and auth;
|
|
8
|
+
// this metadata is not evidence that an account has access to a model.
|
|
9
|
+
export function prepareClaudeModelAdditions(file) {
|
|
10
|
+
if (fs.existsSync(file) && (!fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink())) throw new Error("Unsafe model configuration path");
|
|
11
|
+
const before = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : null;
|
|
12
|
+
const data = before === null ? {} : JSON.parse(before);
|
|
13
|
+
if (!object(data) || (data.providers !== undefined && !object(data.providers))) throw new Error("Unsupported model configuration");
|
|
14
|
+
data.providers ??= {};
|
|
15
|
+
const provider = data.providers.anthropic ?? {};
|
|
16
|
+
if (!object(provider) || (provider.models !== undefined && !Array.isArray(provider.models))) throw new Error("Unsupported Anthropic model configuration");
|
|
17
|
+
// A custom endpoint is controlled by its operator; Studio supplies its own catalog.
|
|
18
|
+
if (provider.baseUrl && provider.baseUrl !== "https://api.anthropic.com") return null;
|
|
19
|
+
const models = provider.models ?? [];
|
|
20
|
+
const missing = additions.models.filter(model => !models.some(existing => existing.id === model.id));
|
|
21
|
+
if (missing.length === 0) return null;
|
|
22
|
+
data.providers.anthropic = { ...provider, models: [...models, ...missing] };
|
|
23
|
+
return { apply() {
|
|
24
|
+
const current = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : null;
|
|
25
|
+
if (current !== before) throw new Error("Model configuration changed during setup");
|
|
26
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
27
|
+
const temporary = `${file}.piagent-${process.pid}`;
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(temporary, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600, flag: "wx" });
|
|
30
|
+
fs.renameSync(temporary, file);
|
|
31
|
+
} finally { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); }
|
|
32
|
+
} };
|
|
33
|
+
}
|
|
@@ -12,6 +12,8 @@ Options:
|
|
|
12
12
|
Default model (default: openai-codex/gpt-6-sol:high)
|
|
13
13
|
--settings <path> Pi settings file (default: ~/.pi/agent/settings.json)
|
|
14
14
|
--dry-run Print resulting settings JSON without writing
|
|
15
|
+
--prune Only remove enabledModels patterns that match no model
|
|
16
|
+
Pi knows (Pi warns about each on every start); keep the rest
|
|
15
17
|
-h, --help
|
|
16
18
|
|
|
17
19
|
Purpose:
|
|
@@ -33,6 +35,7 @@ PRESET="full"
|
|
|
33
35
|
DEFAULT_MODEL="openai-codex/gpt-6-sol:high"
|
|
34
36
|
SETTINGS_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/settings.json"
|
|
35
37
|
DRY_RUN=false
|
|
38
|
+
PRUNE=false
|
|
36
39
|
|
|
37
40
|
# A flag whose value is missing swallows the next flag instead. `--settings
|
|
38
41
|
# --dry-run` used to set the settings path to the string "--dry-run", leave
|
|
@@ -68,6 +71,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
68
71
|
DRY_RUN=true
|
|
69
72
|
shift
|
|
70
73
|
;;
|
|
74
|
+
--prune)
|
|
75
|
+
PRUNE=true
|
|
76
|
+
shift
|
|
77
|
+
;;
|
|
71
78
|
-h|--help)
|
|
72
79
|
usage
|
|
73
80
|
exit 0
|
|
@@ -80,6 +87,13 @@ while [[ $# -gt 0 ]]; do
|
|
|
80
87
|
esac
|
|
81
88
|
done
|
|
82
89
|
|
|
90
|
+
if [[ "$PRUNE" == true ]]; then
|
|
91
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
92
|
+
exec node "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/model-scope-prune.mjs" --settings "$SETTINGS_PATH"
|
|
93
|
+
fi
|
|
94
|
+
exec node "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/model-scope-prune.mjs" --settings "$SETTINGS_PATH" --fix
|
|
95
|
+
fi
|
|
96
|
+
|
|
83
97
|
case "$PRESET" in
|
|
84
98
|
full|codex|claude) ;;
|
|
85
99
|
*)
|
|
@@ -88,17 +102,17 @@ case "$PRESET" in
|
|
|
88
102
|
;;
|
|
89
103
|
esac
|
|
90
104
|
|
|
91
|
-
node --input-type=module - "$SETTINGS_PATH" "$PRESET" "$DEFAULT_MODEL" "$DRY_RUN" <<'NODE'
|
|
105
|
+
PIAGENT_SCOPE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" node --input-type=module - "$SETTINGS_PATH" "$PRESET" "$DEFAULT_MODEL" "$DRY_RUN" <<'NODE'
|
|
92
106
|
import fs from "node:fs";
|
|
93
107
|
import path from "node:path";
|
|
108
|
+
import { pathToFileURL } from "node:url";
|
|
109
|
+
const { prepareClaudeModelAdditions } = await import(pathToFileURL(process.env.PIAGENT_SCOPE_ROOT + "/scripts/claude-model-additions.mjs"));
|
|
94
110
|
|
|
95
111
|
const [settingsPath, preset, defaultModelInput, dryRunRaw] = process.argv.slice(2);
|
|
96
112
|
const dryRun = dryRunRaw === "true";
|
|
97
113
|
|
|
98
114
|
const codexModels = [
|
|
99
115
|
"openai-codex/gpt-5.3-codex-spark:minimal",
|
|
100
|
-
"openai-codex/gpt-5.4-mini:minimal",
|
|
101
|
-
"openai-codex/gpt-5.4:xhigh",
|
|
102
116
|
"openai-codex/gpt-5.5:high",
|
|
103
117
|
"openai-codex/gpt-5.6-luna:medium",
|
|
104
118
|
"openai-codex/gpt-5.6-terra:high",
|
|
@@ -115,6 +129,8 @@ const claudeModels = [
|
|
|
115
129
|
"anthropic/claude-sonnet-4-5:high",
|
|
116
130
|
"anthropic/claude-sonnet-4-6:max",
|
|
117
131
|
"anthropic/claude-sonnet-5:xhigh",
|
|
132
|
+
"anthropic/claude-sonnet-5-5:high",
|
|
133
|
+
"anthropic/claude-opus-5-5:high",
|
|
118
134
|
"anthropic/claude-opus-4-5:xhigh",
|
|
119
135
|
"anthropic/claude-opus-4-6:max",
|
|
120
136
|
"anthropic/claude-opus-4-7:xhigh",
|
|
@@ -141,21 +157,29 @@ function parseDefaultModel(input) {
|
|
|
141
157
|
}
|
|
142
158
|
|
|
143
159
|
const parsedDefault = parseDefaultModel(defaultModelInput);
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
: {};
|
|
160
|
+
const settingsBefore = fs.existsSync(settingsPath) ? fs.readFileSync(settingsPath, "utf8") : null;
|
|
161
|
+
const settings = settingsBefore === null ? {} : JSON.parse(settingsBefore);
|
|
147
162
|
|
|
148
163
|
settings.defaultProvider = parsedDefault.provider;
|
|
149
164
|
settings.defaultModel = parsedDefault.model;
|
|
150
165
|
settings.defaultThinkingLevel = parsedDefault.thinking;
|
|
151
166
|
settings.enabledModels = enabledModels;
|
|
152
167
|
|
|
168
|
+
const catalogEdit = preset === "codex" ? null : prepareClaudeModelAdditions(path.join(path.dirname(settingsPath), "models.json"));
|
|
153
169
|
const output = `${JSON.stringify(settings, null, 2)}\n`;
|
|
154
170
|
if (dryRun) {
|
|
155
171
|
process.stdout.write(output);
|
|
156
172
|
} else {
|
|
157
173
|
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
158
174
|
fs.writeFileSync(settingsPath, output);
|
|
175
|
+
try { if (catalogEdit) catalogEdit.apply(); }
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (fs.readFileSync(settingsPath, "utf8") === output) {
|
|
178
|
+
if (settingsBefore === null) fs.unlinkSync(settingsPath);
|
|
179
|
+
else fs.writeFileSync(settingsPath, settingsBefore);
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
159
183
|
console.log(`Configured Pi model scope: ${settingsPath}`);
|
|
160
184
|
console.log(` preset: ${preset}`);
|
|
161
185
|
console.log(` default: ${parsedDefault.provider}/${parsedDefault.model}:${parsedDefault.thinking}`);
|
|
@@ -27,7 +27,7 @@ After installing/configuring:
|
|
|
27
27
|
pi
|
|
28
28
|
/subagents-doctor
|
|
29
29
|
/subagents-models
|
|
30
|
-
# Piagent
|
|
30
|
+
# Piagent delegates useful bounded read-only work without a token-saving threshold.
|
|
31
31
|
USAGE
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -67,19 +67,18 @@ function parseArgs(argv) {
|
|
|
67
67
|
// What the operator should do next. The guard states 74 refusals and only three
|
|
68
68
|
// of them say this, which is why a denial so often ends the conversation.
|
|
69
69
|
const REMEDY = {
|
|
70
|
-
"protected-path": "
|
|
70
|
+
"protected-path": "This path is protected because it can hold credentials. Open it yourself or share the non-secret part; only the operator can change the protected-path policy, and no request or scope overrides it.",
|
|
71
71
|
"protected-glob": "Name the file you mean instead of a pattern; a pattern is refused when any name it can match is protected.",
|
|
72
72
|
"resolved-protected": "The path resolves to a protected file through a link or a parent directory. Use the real path you intend.",
|
|
73
73
|
"unresolved": "Write the path out, or put the expansion in its own argument, so the guard can see what is being opened.",
|
|
74
74
|
"exec-policy": "This is refused by an exec-policy rule, not by a path. Change the command, or ask the operator to run it.",
|
|
75
|
-
"runtime-required": "Run the command through the current Pi session. Only that runtime owns the
|
|
75
|
+
"runtime-required": "Run the command through the current Pi session. Only that runtime owns the permission, approval, and budget facts needed for a final decision."
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
// Task contracts, task scope and task lifecycle control were retired on
|
|
79
|
+
// 2026-09-30; the live guard no longer applies them.
|
|
78
80
|
const RUNTIME_GATES = [
|
|
79
|
-
"task-contract",
|
|
80
|
-
"task-scope",
|
|
81
81
|
"permission-profile",
|
|
82
|
-
"lifecycle-control",
|
|
83
82
|
"approval-state",
|
|
84
83
|
"context-budget"
|
|
85
84
|
];
|
|
@@ -144,7 +143,7 @@ function explain(command, protectedPaths, policy, cwd) {
|
|
|
144
143
|
staticDecision,
|
|
145
144
|
reason: staticDecision === "confirm"
|
|
146
145
|
? `Static preflight requires confirmation (${exec.reasons.join("; ")}), but the live runtime may still block it.`
|
|
147
|
-
: "Static preflight found no refusal. A final decision requires live session
|
|
146
|
+
: "Static preflight found no refusal. A final decision requires live session state.",
|
|
148
147
|
remainingGates: RUNTIME_GATES,
|
|
149
148
|
steps, exec
|
|
150
149
|
};
|
package/scripts/init-project.sh
CHANGED
|
@@ -315,8 +315,6 @@ echo "Next:"
|
|
|
315
315
|
echo " cd \"$PROJECT_PATH\""
|
|
316
316
|
echo " pi"
|
|
317
317
|
echo " /login # first time only"
|
|
318
|
-
echo " /onboard # open onboarding menu/status"
|
|
319
|
-
echo " /onboard run # first project-read after model selection"
|
|
320
318
|
echo " /mcp # MCP servers come from the global install, not from this project"
|
|
321
319
|
echo " /context index # inspect compact project context index"
|
|
322
320
|
echo " /memory # inspect project memory policy when needed"
|
|
@@ -780,5 +780,6 @@ echo " /scoped-models # optional: edit Ctrl+P model cycle scope"
|
|
|
780
780
|
if [[ "$WITH_MCP" == true ]]; then
|
|
781
781
|
echo " /mcp # inspect MCP servers; authenticate approved remote servers only when needed"
|
|
782
782
|
fi
|
|
783
|
-
|
|
784
|
-
echo " /
|
|
783
|
+
if [[ "$WITH_SUBAGENTS" == true ]]; then
|
|
784
|
+
echo " /subagents-doctor"
|
|
785
|
+
fi
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Patterns in Pi's enabledModels that match no model Pi knows. Pi prints
|
|
3
|
+
// "Warning: No models match pattern" for each one on every start, and a model
|
|
4
|
+
// that left the catalog never comes back by itself. Matching uses Pi's own
|
|
5
|
+
// resolver over every model Pi knows (built-in and models.json), signed in or
|
|
6
|
+
// not, so a provider that is only signed out keeps its patterns.
|
|
7
|
+
//
|
|
8
|
+
// Usage: model-scope-prune.mjs [--settings <path>] [--fix] [--json]
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
|
+
import { pathToFileURL } from "node:url";
|
|
13
|
+
|
|
14
|
+
// The Pi install `pi` runs from.
|
|
15
|
+
export function piPackageRoot(environment = process.env) {
|
|
16
|
+
try {
|
|
17
|
+
const bin = execFileSync("/usr/bin/which", ["pi"], { encoding: "utf8", env: environment }).trim();
|
|
18
|
+
let dir = path.dirname(fs.realpathSync(bin));
|
|
19
|
+
for (let depth = 0; depth < 4; depth += 1, dir = path.dirname(dir)) {
|
|
20
|
+
const manifest = path.join(dir, "package.json");
|
|
21
|
+
if (fs.existsSync(manifest) && JSON.parse(fs.readFileSync(manifest, "utf8")).name === "@earendil-works/pi-coding-agent") return dir;
|
|
22
|
+
}
|
|
23
|
+
} catch { /* no pi on PATH */ }
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Every model Pi knows, without network or credentials.
|
|
28
|
+
export async function knownModels(piRoot, agentDir) {
|
|
29
|
+
const { ModelRuntime } = await import(pathToFileURL(path.join(piRoot, "dist/core/model-runtime.js")).href);
|
|
30
|
+
const { InMemoryCredentialStore } = await import(pathToFileURL(path.join(piRoot, "dist/index.js")).href).catch(() => ({}));
|
|
31
|
+
const previous = process.env.PI_OFFLINE; process.env.PI_OFFLINE = "1";
|
|
32
|
+
try {
|
|
33
|
+
const runtime = await ModelRuntime.create({ refreshOnCreate: false, modelsPath: path.join(agentDir, "models.json"),
|
|
34
|
+
...(InMemoryCredentialStore ? { credentials: new InMemoryCredentialStore() } : {}) });
|
|
35
|
+
return runtime.getProviders().flatMap((provider) => runtime.getModels(provider.id ?? provider));
|
|
36
|
+
} finally { if (previous === undefined) delete process.env.PI_OFFLINE; else process.env.PI_OFFLINE = previous; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function stalePatterns(patterns, models, piRoot) {
|
|
40
|
+
const { resolveModelScopeFromModels } = await import(pathToFileURL(path.join(piRoot, "dist/core/model-resolver.js")).href);
|
|
41
|
+
const { diagnostics } = resolveModelScopeFromModels(patterns, models);
|
|
42
|
+
return [...new Set(diagnostics.filter((item) => item.code === "no-match").map((item) => item.pattern))];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Removes only the stale patterns; everything else in the file stays as written.
|
|
46
|
+
export function pruneSettings(settingsPath, stale) {
|
|
47
|
+
const text = fs.readFileSync(settingsPath, "utf8"), settings = JSON.parse(text);
|
|
48
|
+
const kept = settings.enabledModels.filter((pattern) => !stale.includes(pattern));
|
|
49
|
+
const next = `${JSON.stringify({ ...settings, enabledModels: kept }, null, 2)}\n`;
|
|
50
|
+
const temporary = `${settingsPath}.${process.pid}.tmp`;
|
|
51
|
+
fs.writeFileSync(temporary, next, { mode: fs.statSync(settingsPath).mode & 0o777 });
|
|
52
|
+
fs.renameSync(temporary, settingsPath);
|
|
53
|
+
return kept.length;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname)) {
|
|
57
|
+
const args = process.argv.slice(2), at = args.indexOf("--settings");
|
|
58
|
+
const agentDir = process.env.PI_CODING_AGENT_DIR || path.join(process.env.HOME ?? "", ".pi", "agent");
|
|
59
|
+
const settingsPath = at >= 0 && args[at + 1] ? path.resolve(args[at + 1]) : path.join(agentDir, "settings.json");
|
|
60
|
+
const json = args.includes("--json"), fix = args.includes("--fix");
|
|
61
|
+
const out = (value, text) => process.stdout.write(json ? `${JSON.stringify(value)}\n` : `${text}\n`);
|
|
62
|
+
const settings = fs.existsSync(settingsPath) ? JSON.parse(fs.readFileSync(settingsPath, "utf8")) : {};
|
|
63
|
+
const patterns = Array.isArray(settings.enabledModels) ? settings.enabledModels.filter((item) => typeof item === "string") : [];
|
|
64
|
+
const piRoot = piPackageRoot();
|
|
65
|
+
if (!piRoot) { out({ status: "unavailable", reason: "pi-not-found" }, "Pi is not on PATH; nothing checked."); process.exit(0); }
|
|
66
|
+
if (patterns.length === 0) { out({ status: "ok", stale: [] }, "enabledModels is empty; nothing to prune."); process.exit(0); }
|
|
67
|
+
const stale = await stalePatterns(patterns, await knownModels(piRoot, path.dirname(settingsPath)), piRoot);
|
|
68
|
+
if (stale.length === 0) { out({ status: "ok", stale: [] }, `All ${patterns.length} enabledModels patterns match a model Pi knows.`); process.exit(0); }
|
|
69
|
+
if (!fix) {
|
|
70
|
+
out({ status: "stale", stale }, `${stale.length} enabledModels pattern(s) match no model Pi knows:\n${stale.map((item) => ` - ${item}`).join("\n")}\nRun with --fix to remove only these.`);
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
const kept = pruneSettings(settingsPath, stale);
|
|
74
|
+
out({ status: "pruned", stale, kept }, `Removed ${stale.length} pattern(s) from ${settingsPath}; ${kept} kept.`);
|
|
75
|
+
}
|
|
@@ -106,13 +106,16 @@ for (const [name, tool] of tools) {
|
|
|
106
106
|
}
|
|
107
107
|
const duplicateDescriptions = [...descriptions.values()].filter((names) => names.length > 1);
|
|
108
108
|
const replay = lanes.map(replayLane);
|
|
109
|
+
// The task contract was retired on 2026-09-30: these evidence tools are no longer
|
|
110
|
+
// registered, so no phase may offer them and the runtime keeps only its hooks.
|
|
111
|
+
const p2BaselineRetired = lanes.every((lane) => lane.p2.every((name) => !tools.has(name)));
|
|
109
112
|
const runtimeOwnedEvidenceTools = ["piagent_context_record", "piagent_verify_record", "piagent_task_gate_check", "piagent_trace_record"];
|
|
110
113
|
const checks = [
|
|
111
114
|
{ id: "read-only-no-direct-mutator", passed: phaseRows.filter((row) => row.lane === "read-only").every((row) => row.piagentTools.every((tool) => !PIAGENT_MUTATION_CAPABLE_TOOLS.has(tool)) && row.requiredHostTools.every((tool) => !["edit", "write", "apply_patch"].includes(tool))) },
|
|
112
115
|
{ id: "review-carrier-checked-shell-retained", passed: phaseRows.filter((row) => row.phase === "review").every((row) => row.piagentTools.every((tool) => !PIAGENT_MUTATION_CAPABLE_TOOLS.has(tool)) && row.requiredHostTools.includes("bash") && row.requiredHostTools.every((tool) => !["edit", "write", "apply_patch"].includes(tool))) },
|
|
113
116
|
{ id: "verify-surface-complete", passed: phaseRows.filter((row) => row.phase === "verify").every((row) => row.requiredHostTools.includes("bash") && runtimeOwnedEvidenceTools.every((tool) => !row.piagentTools.includes(tool))) },
|
|
114
117
|
{ id: "handoff-surface-complete", passed: phaseRows.filter((row) => row.phase === "handoff").every((row) => row.requiredHostTools.includes("read") && runtimeOwnedEvidenceTools.every((tool) => !row.piagentTools.includes(tool))) },
|
|
115
|
-
{ id: "
|
|
118
|
+
{ id: "retired-evidence-tools-absent", passed: runtimeOwnedEvidenceTools.every((tool) => !tools.has(tool)) && ["tool_result", "message_end"].every((hook) => hooks.has(hook)) },
|
|
116
119
|
{ id: "high-risk-review-retained", passed: lanes.find((lane) => lane.id === "high-risk").phases.includes("review") },
|
|
117
120
|
{ id: "duplicate-descriptions-zero", passed: duplicateDescriptions.length === 0 },
|
|
118
121
|
{ id: "trajectory-replay-100-percent", passed: replay.every((item) => item.deterministic && item.finalPhase === "terminal") }
|
|
@@ -202,9 +205,11 @@ const report = {
|
|
|
202
205
|
evaluatedTurns: phaseRows.length,
|
|
203
206
|
p2WeightedSchemaBytes: p2WeightedBytes,
|
|
204
207
|
phaseWeightedSchemaBytes: phaseWeightedBytes,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
// Every P2 baseline tool was retired with the task contract, so there is no
|
|
209
|
+
// baseline left to compare against; null says so instead of a meaningless ratio.
|
|
210
|
+
schemaReduction: p2BaselineRetired ? null : Number(reduction.toFixed(4)),
|
|
211
|
+
schemaReductionPercent: p2BaselineRetired ? null : Number((reduction * 100).toFixed(2)),
|
|
212
|
+
schemaReductionSemantics: p2BaselineRetired ? "counterfactual-baseline-retired" : "counterfactual-intended-surface-only",
|
|
208
213
|
runtimeContract: { strict: strictContract, shadow: shadowContract },
|
|
209
214
|
duplicateDescriptions,
|
|
210
215
|
missingToolEvents: checks.filter((check) => !check.passed && check.id.includes("surface")).length,
|
package/scripts/piagent-cli.mjs
CHANGED
|
@@ -36,7 +36,10 @@ let script = scriptByCommand[invokedAs];
|
|
|
36
36
|
|
|
37
37
|
if (invokedAs === "piagent") {
|
|
38
38
|
const subcommand = forwardedArgs[0];
|
|
39
|
-
if (subcommand === "
|
|
39
|
+
if (subcommand === "studio") {
|
|
40
|
+
script = "scripts/piagent-studio.mjs";
|
|
41
|
+
forwardedArgs = forwardedArgs.slice(1);
|
|
42
|
+
} else if (subcommand === "dashboard") {
|
|
40
43
|
script = "scripts/piagent-dashboard.mjs";
|
|
41
44
|
forwardedArgs = forwardedArgs.slice(1);
|
|
42
45
|
} else if (subcommand === "explain") {
|
|
@@ -56,6 +59,7 @@ if (invokedAs === "piagent") {
|
|
|
56
59
|
console.log("");
|
|
57
60
|
console.log("Commands:");
|
|
58
61
|
console.log(" dashboard Open and manage the local session hub");
|
|
62
|
+
console.log(" studio Open the managed company agent imported by Agent Watch");
|
|
59
63
|
console.log(" explain Say why the guard would allow or block a shell command");
|
|
60
64
|
console.log(" approve-verification Preview or approve an independent verification plan");
|
|
61
65
|
console.log(" select-verification Preview reusable contracts for exact task criteria");
|
|
@@ -87,6 +91,51 @@ function benchmarkSourceRoot() {
|
|
|
87
91
|
process.exit(1);
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
// The Pi host this member runs: the `pi` found on PATH, as its package root.
|
|
95
|
+
function piHost() {
|
|
96
|
+
for (const dir of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
97
|
+
if (!path.isAbsolute(dir)) continue;
|
|
98
|
+
let root;
|
|
99
|
+
try { root = path.dirname(fs.realpathSync(path.join(dir, "pi"))); } catch { continue; }
|
|
100
|
+
for (let depth = 0; depth < 4; depth += 1, root = path.dirname(root)) {
|
|
101
|
+
try { if (JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).name === "@earendil-works/pi-coding-agent") return root; } catch { /* keep walking up */ }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Agent Watch binds the company runtime to the Piagent this member actually
|
|
108
|
+
// runs (nvm, Volta, Homebrew, a custom npm prefix…): record the installed
|
|
109
|
+
// entrypoint, the Node running it and the Pi host. Source checkouts never
|
|
110
|
+
// register, and a failure here never blocks the command. Asking for help or the
|
|
111
|
+
// version changes nothing in the operator's agent directory.
|
|
112
|
+
function recordRuntime() {
|
|
113
|
+
try {
|
|
114
|
+
if (forwardedArgs.some((value) => ["--help", "-h", "--version", "-v"].includes(value))) return;
|
|
115
|
+
if (!packageRoot.split(path.sep).includes("node_modules")) return;
|
|
116
|
+
// An install under the OS temporary directory (a packaging test, a scratch
|
|
117
|
+
// prefix) is about to disappear; recording it would point Agent Watch at
|
|
118
|
+
// an entrypoint that no longer exists.
|
|
119
|
+
const osTemp = fs.realpathSync.native(os.tmpdir()), installRoot = fs.realpathSync.native(packageRoot);
|
|
120
|
+
if (installRoot === osTemp || installRoot.startsWith(`${osTemp}${path.sep}`)) return;
|
|
121
|
+
const agentDir = path.resolve(process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent"));
|
|
122
|
+
if (!fs.statSync(agentDir).isDirectory()) return;
|
|
123
|
+
const body = `${JSON.stringify({
|
|
124
|
+
schema_version: 1,
|
|
125
|
+
version: JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")).version,
|
|
126
|
+
entrypoint: fs.realpathSync(path.join(packageRoot, "scripts", "piagent-studio.mjs")),
|
|
127
|
+
node: fs.realpathSync(process.execPath),
|
|
128
|
+
pi_sdk_root: piHost()
|
|
129
|
+
}, null, 2)}\n`;
|
|
130
|
+
const file = path.join(agentDir, "piagent-runtime.json");
|
|
131
|
+
try { if (fs.readFileSync(file, "utf8") === body) return; } catch { /* first run */ }
|
|
132
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
133
|
+
fs.writeFileSync(temporary, body, { mode: 0o600 });
|
|
134
|
+
fs.renameSync(temporary, file);
|
|
135
|
+
} catch { /* optional: Agent Watch falls back to known install locations */ }
|
|
136
|
+
}
|
|
137
|
+
recordRuntime();
|
|
138
|
+
|
|
90
139
|
const sourceRoot = invokedAs === "piagent-benchmark" ? benchmarkSourceRoot() : packageRoot;
|
|
91
140
|
const target = path.join(sourceRoot, script);
|
|
92
141
|
const runner = target.endsWith(".mjs") ? process.execPath : "bash";
|
|
@@ -95,7 +144,7 @@ const runnerArgs = target.endsWith(".mjs")
|
|
|
95
144
|
: [target];
|
|
96
145
|
const child = spawn(runner, [...runnerArgs, ...forwardedArgs], {
|
|
97
146
|
cwd: process.cwd(),
|
|
98
|
-
env: process.env,
|
|
147
|
+
env: script === "scripts/piagent-studio.mjs" ? { PATH: "/usr/bin:/bin", HOME: os.homedir(), TERM: process.env.TERM || "xterm-256color", LANG: "en_US.UTF-8" } : process.env,
|
|
99
148
|
stdio: "inherit"
|
|
100
149
|
});
|
|
101
150
|
|