@piagent/platform 1.1.7 → 1.1.9

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 CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  This file records release-facing changes for Pi Agent Platform. Copy the relevant version block into GitHub Releases when publishing a tag.
4
4
 
5
+ ## v1.1.9 - 2026-07-28
6
+
7
+ ### Fixed
8
+
9
+ - The update notice reaches the install path people actually use. v1.1.8 decided a platform tree was a maintainer's working copy when it found a `.git` directory in it — but `pi install git:...` clones the repository, so an ordinary install carries a `.git` and every other file a working copy carries. There is no mark inside the tree that separates them, which meant the notice never appeared for anyone who installed the documented way.
10
+
11
+ What separates them is where they sit: Pi puts packages it installed under its own agent directory, and npm puts the global helper under `node_modules`. A tree anywhere else is a working copy and is still told nothing. `PI_CODING_AGENT_DIR` is honoured, and both paths are resolved before comparison, because a home reached through a symlink is ordinary — macOS reaches `/var` that way — and comparing a real path against a symlinked one made an install look like it was somewhere else.
12
+
13
+ ## v1.1.8 - 2026-07-28
14
+
15
+ ### Added
16
+
17
+ - Pi says when a release is out, and names the command that takes it. The notice appears in the session-start line as `Piagent update available: 1.1.7 -> 1.1.8. Run \`piagent-update\` to move this machine to it.` Nobody has to remember to go and check, which until now was the only way to find out.
18
+
19
+ It costs the session nothing. The session reads a cache and, when that cache is older than a day, leaves a detached `npm view` behind to refresh it — so the first session after a release may still be quiet and the next one carries the notice. This is the only network traffic the guard originates, it sends nothing about the project, and the registry's answer reaches the terminal only if it is exactly `MAJOR.MINOR.PATCH`, so neither a hostile registry nor an edited cache can put chosen text in front of an operator. A checkout is never told to update. `PIAGENT_NO_UPDATE_CHECK=1` switches it off.
20
+
5
21
  ## v1.1.7 - 2026-07-28
6
22
 
7
23
  A platform update is one global install. It was not behaving like one: every project that selected capability packs stopped working after a release until its profile was reapplied by hand, and the policy a release corrected never reached a project that had already onboarded.
package/README.md CHANGED
@@ -283,7 +283,7 @@ This repository intentionally excludes:
283
283
 
284
284
  ## Maturity
285
285
 
286
- The current package version is read from package metadata and release tags. Personal machines may follow the unpinned package source when accepting ongoing updates; production/team quickstarts and committed project settings should pin an explicit tag such as `v1.1.7` or a reviewed commit.
286
+ The current package version is read from package metadata and release tags. Personal machines may follow the unpinned package source when accepting ongoing updates; production/team quickstarts and committed project settings should pin an explicit tag such as `v1.1.9` or a reviewed commit.
287
287
 
288
288
  Ready for:
289
289
 
@@ -240,7 +240,7 @@ Thư mục nguồn phải chứa `packs/`, nằm trong project, và không chứ
240
240
  piagent-capabilities vendor --profile .pi/piagent-profile.json
241
241
  ```
242
242
 
243
- Lệnh này ghi vào `.pi/capability-vendor/<name>/` và in digest để review trước khi commit. Đây là chỗ duy nhất chạm network. Resolve và guard chỉ đọc thứ đã có trên đĩa, nên pack không đổi được giữa lúc review và lúc chạy. Nguồn khai mà chưa vendor thì resolve fail — không im lặng chạy với ít pack hơn profile yêu cầu.
243
+ Lệnh này ghi vào `.pi/capability-vendor/<name>/` và in digest để review trước khi commit. Đây là chỗ duy nhất trong đường capability chạm network — update check là đường riêng, xem `docs/security-threat-model.md`. Resolve và guard chỉ đọc thứ đã có trên đĩa, nên pack không đổi được giữa lúc review và lúc chạy. Nguồn khai mà chưa vendor thì resolve fail — không im lặng chạy với ít pack hơn profile yêu cầu.
244
244
 
245
245
  ### Vẫn deny by default
246
246
 
@@ -419,12 +419,13 @@ Các lệnh này chạy ngoài Pi.
419
419
  | Command | Dùng khi nào |
420
420
  |---|---|
421
421
  | `npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0` | Cài Pi CLI tương thích với release hiện tại. |
422
- | `npm install -g --ignore-scripts @piagent/platform@1.1.7` | Cài terminal helper `piagent-*` từ release tag hiện tại. |
423
- | `pi install git:github.com/Vt-mmm/piagent@v1.1.7` | Cài pinned release khi cần reproducible team setup. |
422
+ | `npm install -g --ignore-scripts @piagent/platform@1.1.9` | Cài terminal helper `piagent-*` từ release tag hiện tại. |
423
+ | `pi install git:github.com/Vt-mmm/piagent@v1.1.9` | Cài pinned release khi cần reproducible team setup. |
424
424
  | `pi install git:github.com/Vt-mmm/piagent` | Cài latest package platform cho máy cá nhân/sandbox. |
425
425
  | `piagent-update --check` | Báo version hiện tại vs version sẽ lên cho cả ba thành phần; không cài gì. |
426
426
  | `piagent-update` | Full update một lệnh: Pi host → npm-global helper → Pi package, đúng thứ tự release yêu cầu. Thêm `--project <path>` để chạy doctor sau. |
427
427
  | `piagent-update --version X.Y.Z` | Update tới đúng một release thay vì latest; dùng cho rollout theo lô hoặc rollback. |
428
+ | `PIAGENT_NO_UPDATE_CHECK=1` | Tắt notice "có bản mới" ở đầu session Pi. Mặc định bật, hỏi registry nhiều nhất một lần mỗi 24 giờ ở tiến trình nền. |
428
429
  | Cài exact Pi host của release, rồi `npm install -g --ignore-scripts @piagent/platform@X.Y.Z` và `piagent-install --stable` | Sequence thủ công tương đương. Thứ tự bắt buộc: `piagent-install` fail nếu Pi host lệch bản pin và không tự cài host. Version pin của từng release nằm trong [release/install policy](release-install-policy.md). |
429
430
  | Cài exact Pi host ghi trong release cũ, rồi helper `vPREVIOUS` và `piagent-install --stable` | Full rollback; đánh giá dependency risk của host cũ trước khi hạ version. |
430
431
  | `piagent-install --stable --dry-run` | Preview Pi package matching với helper hiện tại; stable resolve tag → commit SHA. |
@@ -14,9 +14,9 @@ Repo này phải chạy được cho nhiều project/domain khác nhau. Vì vậ
14
14
 
15
15
  | Use case | Package source |
16
16
  |---|---|
17
- | Team pinned release | `git:github.com/Vt-mmm/piagent@v1.1.7` |
17
+ | Team pinned release | `git:github.com/Vt-mmm/piagent@v1.1.9` |
18
18
  | Personal/sandbox dev | `git:github.com/Vt-mmm/piagent` |
19
- | Registry distribution | `npm:@piagent/platform@1.1.7` |
19
+ | Registry distribution | `npm:@piagent/platform@1.1.9` |
20
20
  | Enterprise npm (fork under another scope) | `npm:@your-scope/platform@x.y.z` |
21
21
  | Local platform dev | `/path/to/piagent` |
22
22
 
@@ -26,7 +26,7 @@ Latest tiện cho máy cá nhân muốn nhận cập nhật nhanh. Pin tag/commi
26
26
 
27
27
  | Channel | Command | Target | Policy |
28
28
  |---|---|---|---|
29
- | `stable` | `bash scripts/install-global.sh --stable` | Helper release tag resolved to commit SHA, currently `v1.1.7` | Install the Pi package matching the helper. |
29
+ | `stable` | `bash scripts/install-global.sh --stable` | Helper release tag resolved to commit SHA, currently `v1.1.9` | Install the Pi package matching the helper. |
30
30
  | `exact` | `bash scripts/install-global.sh --version vX.Y.Z --resolve-tag` | Requested tag resolved to commit SHA | Pi-package-only rollout, rollback, and incident recovery. |
31
31
  | `dev` | `bash scripts/install-global.sh --dev` | Moving Git source | Personal/sandbox only; do not commit into project settings. |
32
32
  | `local` | `bash scripts/install-global.sh --local` | Current checkout path | Platform development only. |
@@ -53,7 +53,7 @@ Root `package.json` có `pi` manifest trỏ tới:
53
53
  Do đó team có thể:
54
54
 
55
55
  ```bash
56
- pi install git:github.com/Vt-mmm/piagent@v1.1.7
56
+ pi install git:github.com/Vt-mmm/piagent@v1.1.9
57
57
  ```
58
58
 
59
59
  Không cần biết internal folder `packages/piagent-core`. Source không pin chỉ dành cho personal/sandbox như bảng channel ở trên.
@@ -65,12 +65,12 @@ Team nên install global package một lần:
65
65
  ```bash
66
66
  node --version # >= 22.19.0
67
67
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0
68
- npm install -g --ignore-scripts @piagent/platform@1.1.7
68
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
69
69
  piagent-install --stable --dry-run
70
70
  piagent-install --stable
71
71
  ```
72
72
 
73
- `pi install git:github.com/Vt-mmm/piagent@v1.1.7` vẫn hợp lệ nếu chỉ cần cài Pi package. Lệnh đó không tự tạo các binary terminal `piagent-*`; muốn có helper global thì dùng `npm install -g --ignore-scripts @piagent/platform@1.1.7`.
73
+ `pi install git:github.com/Vt-mmm/piagent@v1.1.9` vẫn hợp lệ nếu chỉ cần cài Pi package. Lệnh đó không tự tạo các binary terminal `piagent-*`; muốn có helper global thì dùng `npm install -g --ignore-scripts @piagent/platform@1.1.9`.
74
74
 
75
75
  Support matrix hiện tại: macOS Apple Silicon + Bash và Linux x64 + Bash đã được verify; macOS Intel + Bash và Linux ARM64 + Bash là supported target cần chạy smoke trước rollout; native Windows chưa phải target rollout team; WSL2 experimental. Chi tiết và version runtime nằm trong [release/install policy](release-install-policy.md).
76
76
 
@@ -103,7 +103,7 @@ Nếu muốn commit sẵn `.pi/piagent-profile.json` vào repo hoặc bootstrap
103
103
  ```bash
104
104
  bash scripts/setup.sh /path/to/project \
105
105
  --profile be-readonly-fe \
106
- --package-source git:github.com/Vt-mmm/piagent@v1.1.7 \
106
+ --package-source git:github.com/Vt-mmm/piagent@v1.1.9 \
107
107
  --mcp-preset core \
108
108
  --subagents-preset safe
109
109
  ```
@@ -18,12 +18,12 @@ Support matrix release hiện tại: macOS Apple Silicon + Bash và Linux x64 +
18
18
  ```bash
19
19
  node --version # >= 22.19.0
20
20
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0
21
- npm install -g --ignore-scripts @piagent/platform@1.1.7
21
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
22
22
  piagent-install --stable --dry-run
23
23
  piagent-install --stable
24
24
  ```
25
25
 
26
- Khi seed `.pi/settings.json` cho team/repo cần audit lặp lại, giữ package source dạng pinned tag như `git:github.com/Vt-mmm/piagent@v1.1.7`. Máy cá nhân có thể dùng `git:github.com/Vt-mmm/piagent` để theo latest.
26
+ Khi seed `.pi/settings.json` cho team/repo cần audit lặp lại, giữ package source dạng pinned tag như `git:github.com/Vt-mmm/piagent@v1.1.9`. Máy cá nhân có thể dùng `git:github.com/Vt-mmm/piagent` để theo latest.
27
27
 
28
28
  Nếu đang ở source checkout của platform, dùng helper theo channel để preview trước khi đổi:
29
29
 
@@ -99,7 +99,7 @@ install package once
99
99
 
100
100
  ```bash
101
101
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0
102
- npm install -g --ignore-scripts @piagent/platform@1.1.7
102
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
103
103
  piagent-install --stable --dry-run
104
104
  piagent-install --stable
105
105
  ```
@@ -281,7 +281,7 @@ Không phải daily default. Dùng khi muốn tạo sẵn `.pi` files cho projec
281
281
  bash /path/to/piagent/scripts/setup.sh /path/to/project \
282
282
  --project-only \
283
283
  --profile auto \
284
- --package-source git:github.com/Vt-mmm/piagent@v1.1.7 \
284
+ --package-source git:github.com/Vt-mmm/piagent@v1.1.9 \
285
285
  --mcp-preset core \
286
286
  --subagents-preset safe
287
287
  ```
@@ -854,8 +854,8 @@ Watchdog là optional adversarial reviewer ở cuối turn, không bật mặc
854
854
 
855
855
  | Command | Dùng để |
856
856
  |---|---|
857
- | `npm install -g --ignore-scripts @piagent/platform@1.1.7` | Cài terminal helper `piagent-*` từ release tag hiện tại. |
858
- | `pi install git:github.com/Vt-mmm/piagent@v1.1.7` | Install pinned release cho reproducible team setup. |
857
+ | `npm install -g --ignore-scripts @piagent/platform@1.1.9` | Cài terminal helper `piagent-*` từ release tag hiện tại. |
858
+ | `pi install git:github.com/Vt-mmm/piagent@v1.1.9` | Install pinned release cho reproducible team setup. |
859
859
  | `pi install git:github.com/Vt-mmm/piagent` | Install latest platform package cho máy cá nhân/sandbox. |
860
860
  | Cài exact Pi host của release, rồi `npm install -g --ignore-scripts @piagent/platform@X.Y.Z` và `piagent-install --stable` | Full update: đồng bộ host, terminal helper và Pi package. Mỗi release pin một Pi host chính xác; lấy đúng version của release đang cài trong [release/install policy](release-install-policy.md). |
861
861
  | Cài exact Pi host ghi trong release cũ, rồi helper `vPREVIOUS` và `piagent-install --stable` | Full rollback; đánh giá lại dependency findings của host cũ trước khi hạ version. |
@@ -952,7 +952,7 @@ Mở lại Pi session sau khi install.
952
952
  Cài lại terminal helper đúng release rồi kiểm tra `PATH`:
953
953
 
954
954
  ```bash
955
- npm install -g --ignore-scripts @piagent/platform@1.1.7
955
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
956
956
  command -v piagent-install
957
957
  ```
958
958
 
@@ -50,6 +50,8 @@ pi install /path/to/piagent
50
50
  - `1.1.5`: thêm `piagent-update` — một lệnh update cả Pi host, terminal helper và Pi package đúng thứ tự.
51
51
  - `1.1.6`: `be-readonly-fe` giữ được backend read-only trong monorepo, detect FE/BE đọc cả workspace, và rule detect gộp về một chỗ.
52
52
  - `1.1.7`: update global không còn chặn project đang chạy — lock chỉ block khi grant thật sự đổi, và profile dạng `extends` nhận policy từ platform đang cài thay vì giữ bản copy lúc onboard.
53
+ - `1.1.8`: Pi báo ngay ở đầu session khi có release mới, kèm lệnh chạy update.
54
+ - `1.1.9`: notice update hiện đúng trên bản cài bằng `pi install git:` — bản cài nhận diện theo vị trí thư mục, không theo `.git`.
53
55
 
54
56
  ## Không publish
55
57
 
@@ -10,7 +10,7 @@ This file is the canonical install, update, rollback, and release checklist. Oth
10
10
 
11
11
  All supported environments require Node.js `>=22.19.0` and Pi Coding Agent `0.82.0`. The Pi host is installed as a Node CLI; Pi Agent Platform still defines its own release matrix because the terminal helpers and shell policy rely on Bash/POSIX behavior.
12
12
 
13
- | Surface | Status for v1.1.7 | Rollout guidance |
13
+ | Surface | Status for v1.1.9 | Rollout guidance |
14
14
  |---|---|---|
15
15
  | macOS Apple Silicon (`darwin/arm64`) + Bash | Verified for this release. | Safe default for team rollout after normal project smoke tests. |
16
16
  | Linux x64 + Bash | Verified in GitHub Actions. | Safe default for CI/server usage after normal project smoke tests. |
@@ -33,7 +33,7 @@ These three components are versioned independently. A full install, update, or r
33
33
 
34
34
  | Channel | Source shape | Mutability | Use when |
35
35
  |---|---|---:|---|
36
- | `stable` | `git:github.com/Vt-mmm/piagent@<resolved-commit-sha>` from `v1.1.7` | Fixed after resolution | Default for team rollout. |
36
+ | `stable` | `git:github.com/Vt-mmm/piagent@<resolved-commit-sha>` from `v1.1.9` | Fixed after resolution | Default for team rollout. |
37
37
  | `exact` | Tag, reviewed commit, or a tag resolved with `--resolve-tag` | Fixed when using a commit SHA | Pi-package-only roll forward, rollback, or reproduction. |
38
38
  | `dev` | `git:github.com/Vt-mmm/piagent` | Moving | Personal machine or sandbox only. |
39
39
  | `local` | `/path/to/piagent` | Local workspace | Platform development and dry-run validation. |
@@ -53,7 +53,7 @@ Use this flow when the team needs both terminal commands and the Pi package:
53
53
  ```bash
54
54
  node --version # >= 22.19.0
55
55
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0
56
- npm install -g --ignore-scripts @piagent/platform@1.1.7
56
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
57
57
  piagent-install --stable --dry-run
58
58
  piagent-install --stable
59
59
  ```
@@ -61,8 +61,8 @@ piagent-install --stable
61
61
  The stable preview and apply output includes:
62
62
 
63
63
  ```text
64
- currentRelease: v1.1.7 (helper package version)
65
- tag: v1.1.7
64
+ currentRelease: v1.1.9 (helper package version)
65
+ tag: v1.1.9
66
66
  resolvedCommit: <40-char-sha>
67
67
  source: git:github.com/Vt-mmm/piagent@<40-char-sha>
68
68
  ```
@@ -79,7 +79,7 @@ bash scripts/install-global.sh --stable
79
79
  Use this only when terminal commands are not needed:
80
80
 
81
81
  ```bash
82
- pi install git:github.com/Vt-mmm/piagent@v1.1.7
82
+ pi install git:github.com/Vt-mmm/piagent@v1.1.9
83
83
  ```
84
84
 
85
85
  Direct `pi install` does not create `piagent-*` commands on `PATH`.
@@ -21,7 +21,7 @@ This document covers the packaged guard, project profiles, capability locks, ins
21
21
  - The operator, operating-system account, Pi host, and explicitly trusted project repository are trusted to execute code.
22
22
  - Project-local Pi resources are loaded only after Pi project trust or an explicit operator override.
23
23
  - A human remains available to approve destructive and external-provider actions.
24
- - Supported release verification covers macOS Apple Silicon + Bash, Linux x64 + Bash, Node.js `>=22.19.0`, and the exact Pi host version declared by the release. macOS Intel + Bash and Linux ARM64 + Bash are supported targets that need local smoke verification before broad rollout. Native Windows is not a team-rollout target for v1.1.7, and WSL2 is experimental/unverified.
24
+ - Supported release verification covers macOS Apple Silicon + Bash, Linux x64 + Bash, Node.js `>=22.19.0`, and the exact Pi host version declared by the release. macOS Intel + Bash and Linux ARM64 + Bash are supported targets that need local smoke verification before broad rollout. Native Windows is not a team-rollout target for v1.1.9, and WSL2 is experimental/unverified.
25
25
  - Registry, GitHub, model-provider, MCP-provider, and Vercel controls are external dependencies. Their account security and platform guarantees are not replaced by this repository.
26
26
 
27
27
  ## Threat actors and failure modes
@@ -55,6 +55,16 @@ The final category is outside the containment capability of the Piagent guard. I
55
55
 
56
56
  `read-only`, `workspace-write`, and `trusted-full-access` change the allowed working scope; they do not disable protected paths, secret redaction, capability integrity, destructive confirmation, or external-provider confirmation. `trusted-full-access` is intended for an already trusted repository in an externally safe environment. It is not a bypass or sandbox mode.
57
57
 
58
+ ## Outbound network from a session
59
+
60
+ A Pi session makes one outbound request of its own: at most once every 24 hours it leaves a detached `npm view @piagent/platform version` behind to learn whether a newer release exists. This is the only network traffic the guard originates.
61
+
62
+ What it sends is an npm registry request for a public package and nothing else — no project path, no repository name, no profile, no telemetry. The registry reply reaches the terminal only if it matches `MAJOR.MINOR.PATCH` exactly; anything else is discarded rather than displayed, so a hostile or compromised registry response cannot become a line of attacker-chosen text in the operator's terminal. The answer is cached at `~/.pi/piagent-update-check.json`, an ordinary user-writable file whose worst case is suppressing the notice.
63
+
64
+ The session never waits on it: the request runs in a detached process with no inherited stdio, and the notice appears on the session after the one that refreshed the cache. A checkout is never told to update.
65
+
66
+ Set `PIAGENT_NO_UPDATE_CHECK=1` to switch it off. Nothing else changes.
67
+
58
68
  ## Security verification
59
69
 
60
70
  Release gates include policy regression tests, TypeScript checking, runtime smoke tests, redaction benchmarks, package-content inspection, release-identity checks, installer tests, exact Pi host/add-on dependency audit, and CI on Linux x64 plus the current macOS runner. CodeQL covers JavaScript/TypeScript and GitHub Actions.
@@ -9,7 +9,7 @@ Một thành viên mới không cần biết local path của maintainer. Luồn
9
9
  ```bash
10
10
  node --version # >= 22.19.0
11
11
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0
12
- npm install -g --ignore-scripts @piagent/platform@1.1.7
12
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
13
13
  piagent-install --stable
14
14
  cd /path/to/project
15
15
  pi
@@ -42,7 +42,7 @@ Mặc định team dùng stable helper:
42
42
  ```bash
43
43
  node --version # >= 22.19.0
44
44
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.0
45
- npm install -g --ignore-scripts @piagent/platform@1.1.7
45
+ npm install -g --ignore-scripts @piagent/platform@1.1.9
46
46
  piagent-install --stable --dry-run
47
47
  piagent-install --stable
48
48
  ```
@@ -70,7 +70,15 @@ piagent-update --check
70
70
  piagent-update --project /path/to/project
71
71
  ```
72
72
 
73
- `--check` chỉ báo version, không đụng gì. Lên đúng một bản cụ thể thì `piagent-update --version 1.1.7`.
73
+ `--check` chỉ báo version, không đụng gì. Lên đúng một bản cụ thể thì `piagent-update --version 1.1.9`.
74
+
75
+ Không cần ai nhớ đi kiểm tra: khi có release mới, Pi tự báo ngay ở dòng notice lúc mở session, kèm luôn lệnh chạy.
76
+
77
+ ```text
78
+ Piagent update available: 1.1.7 -> 1.1.8. Run `piagent-update` to move this machine to it.
79
+ ```
80
+
81
+ Version mới nhất được hỏi npm registry nhiều nhất một lần mỗi 24 giờ, ở tiến trình nền tách rời, nên session không chờ gì cả — lần mở đầu tiên sau khi có bản mới có thể chưa thấy notice, lần sau sẽ thấy. Checkout của maintainer không bao giờ được báo. Tắt bằng `PIAGENT_NO_UPDATE_CHECK=1`; chi tiết ở `docs/security-threat-model.md`.
74
82
 
75
83
  Sequence thủ công vẫn dùng được, và bắt buộc theo thứ tự này — `piagent-install` **fail** nếu Pi host lệch bản pin, nó không tự nâng host:
76
84
 
@@ -798,7 +798,7 @@
798
798
  <span>Pi Agent Platform</span>
799
799
  </a>
800
800
  <div class="top-actions">
801
- <span class="pill"><span class="pill-dot"></span>v1.1.7 docs</span>
801
+ <span class="pill"><span class="pill-dot"></span>v1.1.9 docs</span>
802
802
  <a class="button social" href="https://github.com/Vt-mmm/piagent" target="_blank" rel="noopener noreferrer" aria-label="Mở GitHub repository">
803
803
  <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
804
804
  <path d="M7 17 17 7M9 7h8v8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
@@ -934,7 +934,7 @@
934
934
  <article class="step">
935
935
  <div>
936
936
  <h3>Cài — hai lệnh</h3>
937
- <p>Cần Node.js 22.19.0 trở lên. Không cần cài Pi Coding Agent trước: <span class="inline-code">piagent-setup</span> tự cài đúng bản <span class="inline-code">0.82.0</span> mà release <span class="inline-code">v1.1.7</span> đang pin. Chạy nó ngay trong thư mục project — nó cài Pi package, khởi tạo <span class="inline-code">.pi/</span>, cài MCP baseline cùng subagents, rồi chạy doctor. Thêm <span class="inline-code">--no-mcp</span>, <span class="inline-code">--no-subagents</span>, <span class="inline-code">--no-herdr</span> hoặc <span class="inline-code">--global-only</span> để bỏ bớt.</p>
937
+ <p>Cần Node.js 22.19.0 trở lên. Không cần cài Pi Coding Agent trước: <span class="inline-code">piagent-setup</span> tự cài đúng bản <span class="inline-code">0.82.0</span> mà release <span class="inline-code">v1.1.9</span> đang pin. Chạy nó ngay trong thư mục project — nó cài Pi package, khởi tạo <span class="inline-code">.pi/</span>, cài MCP baseline cùng subagents, rồi chạy doctor. Thêm <span class="inline-code">--no-mcp</span>, <span class="inline-code">--no-subagents</span>, <span class="inline-code">--no-herdr</span> hoặc <span class="inline-code">--global-only</span> để bỏ bớt.</p>
938
938
  <div class="table-wrap compact-table">
939
939
  <table>
940
940
  <thead>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/platform",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -103,7 +103,7 @@
103
103
  ],
104
104
  "scripts": {
105
105
  "verify": "bash scripts/verify-local.sh",
106
- "test": "node --test tests/*.test.mjs",
106
+ "test": "PIAGENT_NO_UPDATE_CHECK=1 node --test tests/*.test.mjs",
107
107
  "test:policy": "node --test tests/policy-core.test.mjs",
108
108
  "test:golden": "node --test tests/golden-enforcement.test.mjs",
109
109
  "typecheck": "tsc --noEmit",
@@ -803,7 +803,12 @@ function buildCoreIntegrity(root) {
803
803
  // project-shape.js picks the profile a project is offered, and the profile is
804
804
  // the document that carries protectedPaths and readOnlyPaths. Steering the
805
805
  // recommendation is a way to choose what the guard will end up enforcing.
806
- "packages/piagent-core/extensions/project-shape.js"
806
+ "packages/piagent-core/extensions/project-shape.js",
807
+ // update-check.js is the one file here that enforces nothing. It is pinned
808
+ // for a different reason: it starts a process on session start and puts a
809
+ // line in the operator's terminal, so tampering with it buys code execution
810
+ // and a message the operator has been trained to trust.
811
+ "packages/piagent-core/extensions/update-check.js"
807
812
  ];
808
813
  return files.map((relativePath) => {
809
814
  const resolved = resolveRegularFile(root, relativePath, MAX_ARTIFACT_BYTES);
@@ -40,6 +40,7 @@ import {
40
40
  redactSensitiveText
41
41
  } from "./redaction-core.js";
42
42
  import { detectProfileName } from "./project-shape.js";
43
+ import { evaluateUpdateCheck, isInstalledPlatform, readUpdateCache, startUpdateProbe } from "./update-check.js";
43
44
  import { buildExtendingProfile, resolveProjectProfileDocument } from "../capabilities/project-profile.js";
44
45
  import {
45
46
  resolveCapabilityProfileDocument,
@@ -422,6 +423,23 @@ const DEFAULT_POLICY: BasePolicy = {
422
423
  // Which platform supplies the adapters is fixed by where this file is installed,
423
424
  // so it is resolved once here rather than threaded through every profile load.
424
425
  const PLATFORM_ROOT = findPlatformRoot(path.dirname(fileURLToPath(import.meta.url)));
426
+ const UPDATE_CHECK_MODULE = fileURLToPath(new URL("./update-check.js", import.meta.url));
427
+
428
+ // The installed version, read from the package this file ships in. A maintainer
429
+ // working in the repository is not running a release and has nothing to update
430
+ // to, so only a tree Pi or npm placed is given a version to compare.
431
+ function installedPlatformVersion(): string | undefined {
432
+ if (!isInstalledPlatform(PLATFORM_ROOT)) return undefined;
433
+ return readJsonFile<{ version?: string }>(path.join(PLATFORM_ROOT, "package.json"))?.version;
434
+ }
435
+
436
+ function updateAvailabilityNotice(): string | undefined {
437
+ const installed = installedPlatformVersion();
438
+ if (!installed) return undefined;
439
+ const decision = evaluateUpdateCheck({ installed, cache: readUpdateCache(), now: Date.now() });
440
+ if (decision.probe) startUpdateProbe(UPDATE_CHECK_MODULE);
441
+ return decision.notice;
442
+ }
425
443
 
426
444
  function loadPolicy(extensionDir: string): BasePolicy {
427
445
  const root = findPackageRoot(extensionDir);
@@ -3733,6 +3751,10 @@ export default function piagentGuard(pi: ExtensionAPI) {
3733
3751
  if (permissionProfile.mode === "trusted-full-access") {
3734
3752
  ctx.ui.notify("Piagent permission profile trusted-full-access is active; protected paths, secret redaction, and destructive/external confirmations remain enforced.", "warning");
3735
3753
  }
3754
+ // Last, so a release announcement never pushes a security warning out of
3755
+ // the first thing the operator reads.
3756
+ const updateNotice = updateAvailabilityNotice();
3757
+ if (updateNotice) ctx.ui.notify(updateNotice, "info");
3736
3758
  });
3737
3759
 
3738
3760
  pi.on("input", async (event, ctx) => {
@@ -0,0 +1,211 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { spawn, spawnSync } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ // Telling someone a new release exists is only useful if it costs them nothing.
8
+ // A session start that waits on the registry pays a network round trip every
9
+ // time Pi opens, for information that changes a few times a month, so the check
10
+ // never runs in the session's path: the session reads a cache file and, when
11
+ // that cache is old, leaves a detached process behind to refresh it. The notice
12
+ // lands on this session if the cache already knew, and on the next one if it did
13
+ // not.
14
+ //
15
+ // Everything read here comes from outside the process — a registry response and
16
+ // a file any local process can write — so the only value that ever reaches the
17
+ // terminal is a version string that matched RELEASE_VERSION. A registry that
18
+ // answers with a sentence, or a cache someone edited to carry one, produces no
19
+ // notice rather than a line of attacker-chosen text in the operator's terminal.
20
+
21
+ const HELPER_PACKAGE = "@piagent/platform";
22
+ const CACHE_FILE = "piagent-update-check.json";
23
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
24
+ const PROBE_TIMEOUT_MS = 10_000;
25
+ const MAX_CACHE_BYTES = 4096;
26
+ const RELEASE_VERSION = /^(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})$/;
27
+
28
+ /**
29
+ * Whether this platform tree is an install rather than somebody's working copy.
30
+ *
31
+ * Identified by where it sits, not by what it contains. `pi install git:...`
32
+ * clones the repository, so an install made the ordinary way carries a `.git`
33
+ * directory and every other file a maintainer's checkout carries — there is no
34
+ * mark inside the tree that separates them. What does separate them is that Pi
35
+ * puts packages it installed under its own agent directory, and npm puts the
36
+ * global helper under node_modules. A tree anywhere else is a working copy, and
37
+ * telling its owner to install over it is worse than saying nothing.
38
+ *
39
+ * @param {string} platformRoot
40
+ * @param {{home?: string, env?: Record<string, string|undefined>}} [options]
41
+ * @returns {boolean}
42
+ */
43
+ export function isInstalledPlatform(platformRoot, options = {}) {
44
+ if (typeof platformRoot !== "string" || platformRoot.length === 0) return false;
45
+ if (platformRoot.split(path.sep).includes("node_modules")) return true;
46
+ const env = options.env ?? process.env;
47
+ const configured = env.PI_CODING_AGENT_DIR?.trim();
48
+ const base = options.home ?? os.homedir();
49
+ const agentDir = configured || (typeof base === "string" && base.length > 0 ? path.join(base, ".pi", "agent") : undefined);
50
+ if (!agentDir) return false;
51
+ // Both sides are resolved before comparing. A home reached through a symlink
52
+ // is ordinary — macOS reaches /var that way — and comparing one real path
53
+ // against one symlinked path makes an install look like it is somewhere else.
54
+ const relative = path.relative(realPath(agentDir), realPath(platformRoot));
55
+ // Empty means the same directory, which is not a package inside it; a leading
56
+ // `..` or an absolute result means the tree is somewhere else entirely.
57
+ return relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative);
58
+ }
59
+
60
+ /** @param {string} value @returns {string} */
61
+ function realPath(value) {
62
+ try {
63
+ return fs.realpathSync(value);
64
+ } catch {
65
+ return value;
66
+ }
67
+ }
68
+
69
+ /** @param {string} [home] @returns {string|undefined} */
70
+ export function updateCacheFile(home) {
71
+ const base = home ?? os.homedir();
72
+ if (typeof base !== "string" || base.length === 0) return undefined;
73
+ return path.join(base, ".pi", CACHE_FILE);
74
+ }
75
+
76
+ /**
77
+ * A release version and nothing else. Prereleases and build metadata are
78
+ * rejected rather than parsed: this decides what to tell someone to install, and
79
+ * nudging an operator onto a prerelease is not what the notice is for.
80
+ * @param {unknown} value @returns {string|undefined}
81
+ */
82
+ export function releaseVersion(value) {
83
+ if (typeof value !== "string") return undefined;
84
+ const trimmed = value.trim();
85
+ return RELEASE_VERSION.test(trimmed) ? trimmed : undefined;
86
+ }
87
+
88
+ /**
89
+ * Compare two release versions numerically. `1.1.10` is after `1.1.9`, which
90
+ * string ordering gets backwards.
91
+ * @param {string} left @param {string} right @returns {number}
92
+ */
93
+ export function compareReleaseVersions(left, right) {
94
+ const leftParts = left.split(".").map(Number);
95
+ const rightParts = right.split(".").map(Number);
96
+ for (let index = 0; index < 3; index += 1) {
97
+ if (leftParts[index] !== rightParts[index]) return leftParts[index] < rightParts[index] ? -1 : 1;
98
+ }
99
+ return 0;
100
+ }
101
+
102
+ /** @param {string} [home] @returns {{latest?: string, checkedAt?: number}} */
103
+ export function readUpdateCache(home) {
104
+ const file = updateCacheFile(home);
105
+ if (!file) return {};
106
+ try {
107
+ // A cache larger than this is not something this code wrote.
108
+ if (fs.statSync(file).size > MAX_CACHE_BYTES) return {};
109
+ const document = JSON.parse(fs.readFileSync(file, "utf8"));
110
+ const checkedAt = Date.parse(document?.checkedAt);
111
+ return {
112
+ latest: releaseVersion(document?.latest),
113
+ checkedAt: Number.isFinite(checkedAt) ? checkedAt : undefined
114
+ };
115
+ } catch {
116
+ return {};
117
+ }
118
+ }
119
+
120
+ /** @param {string|undefined} home @param {string} latest @param {number} now @returns {boolean} */
121
+ export function writeUpdateCache(home, latest, now) {
122
+ const file = updateCacheFile(home);
123
+ const version = releaseVersion(latest);
124
+ if (!file || !version) return false;
125
+ const document = {
126
+ schemaVersion: 1,
127
+ package: HELPER_PACKAGE,
128
+ latest: version,
129
+ checkedAt: new Date(now).toISOString()
130
+ };
131
+ try {
132
+ fs.mkdirSync(path.dirname(file), { recursive: true });
133
+ const temporary = `${file}.${process.pid}.tmp`;
134
+ fs.writeFileSync(temporary, `${JSON.stringify(document, null, 2)}\n`);
135
+ fs.renameSync(temporary, file);
136
+ return true;
137
+ } catch {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * What the session should do about updates, decided from state alone so it can
144
+ * be tested without a registry or a clock.
145
+ * @param {{installed?: string, cache?: {latest?: string, checkedAt?: number}, now: number, env?: Record<string, string|undefined>}} options
146
+ * @returns {{notice?: string, probe: boolean}}
147
+ */
148
+ export function evaluateUpdateCheck(options) {
149
+ const env = options.env ?? process.env;
150
+ if (env.PIAGENT_NO_UPDATE_CHECK?.trim()) return { probe: false };
151
+
152
+ const installed = releaseVersion(options.installed);
153
+ const cache = options.cache ?? {};
154
+ const latest = releaseVersion(cache.latest);
155
+ const probe = !(typeof cache.checkedAt === "number" && options.now - cache.checkedAt < CACHE_TTL_MS);
156
+
157
+ if (!installed || !latest || compareReleaseVersions(latest, installed) <= 0) return { probe };
158
+ return {
159
+ probe,
160
+ notice: `Piagent update available: ${installed} -> ${latest}. Run \`piagent-update\` to move this machine to it.`
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Leave the refresh behind and return. The child is detached with no inherited
166
+ * stdio, so nothing it does can hold the session open or write into the
167
+ * terminal.
168
+ * @param {string} moduleFile @param {{spawn?: typeof spawn}} [options]
169
+ * @returns {boolean}
170
+ */
171
+ export function startUpdateProbe(moduleFile, options = {}) {
172
+ try {
173
+ const child = (options.spawn ?? spawn)(process.execPath, [moduleFile, "--probe"], {
174
+ detached: true,
175
+ stdio: "ignore",
176
+ windowsHide: true
177
+ });
178
+ child.unref();
179
+ return true;
180
+ } catch {
181
+ return false;
182
+ }
183
+ }
184
+
185
+ /** @returns {string|undefined} */
186
+ function fetchLatestVersion() {
187
+ const result = spawnSync("npm", ["view", HELPER_PACKAGE, "version", "--json"], {
188
+ encoding: "utf8",
189
+ timeout: PROBE_TIMEOUT_MS,
190
+ windowsHide: true
191
+ });
192
+ if (result.status !== 0 || typeof result.stdout !== "string") return undefined;
193
+ try {
194
+ return releaseVersion(JSON.parse(result.stdout));
195
+ } catch {
196
+ return undefined;
197
+ }
198
+ }
199
+
200
+ /** @param {{now?: number, home?: string, fetch?: () => string|undefined}} [options] @returns {boolean} */
201
+ export function runUpdateProbe(options = {}) {
202
+ const latest = (options.fetch ?? fetchLatestVersion)();
203
+ // A registry that could not be reached leaves the cache alone, so the next
204
+ // session retries instead of recording "no update" as if it had been checked.
205
+ if (!latest) return false;
206
+ return writeUpdateCache(options.home, latest, options.now ?? Date.now());
207
+ }
208
+
209
+ if (process.argv[2] === "--probe" && process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
210
+ runUpdateProbe();
211
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/core",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -15,7 +15,7 @@ Package source examples:
15
15
  # Installs the stable tag after resolving it to a commit SHA.
16
16
 
17
17
  # Pin a specific release:
18
- scripts/install-global.sh --version v1.1.7 --resolve-tag
18
+ scripts/install-global.sh --version v1.1.9 --resolve-tag
19
19
 
20
20
  # Preview the planned install/update without changing user config:
21
21
  scripts/install-global.sh --stable --dry-run
@@ -127,7 +127,7 @@ detect_runtime_surface() {
127
127
  printf 'native-windows/%s (not release-gated; terminal helpers require Bash semantics)' "$arch_name"
128
128
  ;;
129
129
  *)
130
- printf '%s/%s (outside v1.1.7 release matrix)' "$os_name" "$arch_name"
130
+ printf '%s/%s (outside v1.1.9 release matrix)' "$os_name" "$arch_name"
131
131
  ;;
132
132
  esac
133
133
  }
@@ -135,7 +135,7 @@ detect_runtime_surface() {
135
135
  warn_runtime_surface_if_needed() {
136
136
  local surface="$1"
137
137
  case "$surface" in
138
- *"supported target"*|*"experimental"*|*"not release-gated"*|*"outside v1.1.7 release matrix"*)
138
+ *"supported target"*|*"experimental"*|*"not release-gated"*|*"outside v1.1.9 release matrix"*)
139
139
  echo "WARN: runtime surface is $surface." >&2
140
140
  echo "WARN: For team rollout, run piagent-doctor plus the project smoke/verify suite on this machine before relying on it." >&2
141
141
  ;;
@@ -137,7 +137,7 @@ function runtimeMatrixStatus() {
137
137
  surface: `${platform}/${arch}`,
138
138
  status: "outside-release-matrix",
139
139
  teamRolloutReady: false,
140
- note: "This runtime is outside the documented v1.1.7 support matrix."
140
+ note: "This runtime is outside the documented v1.1.9 support matrix."
141
141
  };
142
142
  }
143
143