@piagent/platform 1.48.0 → 1.48.2

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.
Files changed (87) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +2 -0
  3. package/README.vi.md +2 -0
  4. package/docs/command-reference-vietnamese.md +4 -3
  5. package/docs/distribution-standard.md +6 -6
  6. package/docs/harness-conformance.md +4 -0
  7. package/docs/languages.json +2 -2
  8. package/docs/operator-manual-vietnamese.md +7 -7
  9. package/docs/publishing-for-teams.md +2 -0
  10. package/docs/release-install-policy.md +2 -2
  11. package/docs/team-onboarding.md +2 -2
  12. package/docs-site/architecture.html +1 -1
  13. package/docs-site/assets/search-en.json +1 -1
  14. package/docs-site/assets/search-vi.json +1 -1
  15. package/docs-site/benchmark.html +1 -1
  16. package/docs-site/commands.html +1 -1
  17. package/docs-site/content/en/update.html +8 -0
  18. package/docs-site/content/update.html +8 -0
  19. package/docs-site/context-engine.html +1 -1
  20. package/docs-site/ecosystem.html +1 -1
  21. package/docs-site/en/architecture.html +1 -1
  22. package/docs-site/en/benchmark.html +1 -1
  23. package/docs-site/en/commands.html +1 -1
  24. package/docs-site/en/context-engine.html +1 -1
  25. package/docs-site/en/ecosystem.html +1 -1
  26. package/docs-site/en/index.html +15 -15
  27. package/docs-site/en/mcp-approval.html +1 -1
  28. package/docs-site/en/mcp-auth.html +1 -1
  29. package/docs-site/en/mcp-servers.html +1 -1
  30. package/docs-site/en/mcp.html +1 -1
  31. package/docs-site/en/permissions.html +1 -1
  32. package/docs-site/en/profiles.html +1 -1
  33. package/docs-site/en/quickstart.html +1 -1
  34. package/docs-site/en/runtime.html +1 -1
  35. package/docs-site/en/scope.html +1 -1
  36. package/docs-site/en/security.html +1 -1
  37. package/docs-site/en/team.html +1 -1
  38. package/docs-site/en/update.html +11 -3
  39. package/docs-site/en/whats-new.html +52 -28
  40. package/docs-site/en/windows.html +1 -1
  41. package/docs-site/en/workflows.html +1 -1
  42. package/docs-site/index.html +15 -15
  43. package/docs-site/mcp-approval.html +1 -1
  44. package/docs-site/mcp-auth.html +1 -1
  45. package/docs-site/mcp-servers.html +1 -1
  46. package/docs-site/mcp.html +1 -1
  47. package/docs-site/permissions.html +1 -1
  48. package/docs-site/profiles.html +1 -1
  49. package/docs-site/quickstart.html +1 -1
  50. package/docs-site/releases.json +72 -0
  51. package/docs-site/runtime.html +1 -1
  52. package/docs-site/scope.html +1 -1
  53. package/docs-site/security.html +1 -1
  54. package/docs-site/team.html +1 -1
  55. package/docs-site/update.html +11 -3
  56. package/docs-site/whats-new.html +52 -28
  57. package/docs-site/windows.html +1 -1
  58. package/docs-site/workflows.html +1 -1
  59. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  60. package/package.json +1 -1
  61. package/packages/piagent-core/extensions/piagent-guard.ts +10 -2
  62. package/packages/piagent-core/managed/check-waiver.mjs +35 -0
  63. package/packages/piagent-core/managed/command-tools.mjs +2 -2
  64. package/packages/piagent-core/managed/session.mjs +2 -2
  65. package/packages/piagent-core/managed/workflow.mjs +4 -4
  66. package/packages/piagent-core/package.json +1 -1
  67. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +5 -1
  68. package/packages/piagent-core/runtime/solver/runtime-features.ts +52 -10
  69. package/packages/piagent-core/runtime/update/terminal-reload.mjs +122 -0
  70. package/packages/piagent-core/runtime/update/terminal-update-offer.mjs +1 -1
  71. package/packages/piagent-webui/dist/client/assets/{App-fZeIRoaD.js → App-Dg6Firq1.js} +1 -1
  72. package/packages/piagent-webui/dist/client/assets/{GatewayApp-CpC4lgYK.js → GatewayApp-CCczgphI.js} +3 -3
  73. package/packages/piagent-webui/dist/client/assets/{InspectionApp-pD-BqBXm.js → InspectionApp-CEilR0FG.js} +1 -1
  74. package/packages/piagent-webui/dist/client/assets/{SessionAgentWorkspace-3tM7Q6HH.js → SessionAgentWorkspace-yh0py16t.js} +1 -1
  75. package/packages/piagent-webui/dist/client/assets/{SessionInspectorDrawer-BJ94aQ6y.js → SessionInspectorDrawer-C-18TcGH.js} +2 -2
  76. package/packages/piagent-webui/dist/client/assets/{TerminalPanel-CtZyb0tj.js → TerminalPanel-B2-YAz2x.js} +1 -1
  77. package/packages/piagent-webui/dist/client/assets/{WorkspaceCodeEditor-BS_ZrjPN.js → WorkspaceCodeEditor-DAdMuw6x.js} +1 -1
  78. package/packages/piagent-webui/dist/client/assets/{WorkspaceExplorer-CkFBpcM7.js → WorkspaceExplorer-97MLNtEa.js} +3 -3
  79. package/packages/piagent-webui/dist/client/assets/{index-CNdhIouM.js → index-B8tL7HFz.js} +2 -2
  80. package/packages/piagent-webui/dist/client/assets/{transcript-view-model-CpEMY1xb.js → transcript-view-model-DqO4xO12.js} +1 -1
  81. package/packages/piagent-webui/dist/client/index.html +1 -1
  82. package/packages/piagent-webui/gateway/session-inspection-registry.ts +9 -4
  83. package/scripts/company-terminal.mjs +21 -1
  84. package/scripts/piagent-cli.mjs +44 -21
  85. package/scripts/piagent-studio.mjs +22 -2
  86. package/scripts/terminal-reload.mjs +15 -0
  87. package/scripts/update-global.mjs +10 -0
@@ -1,4 +1,76 @@
1
1
  [
2
+ {
3
+ "version": "1.48.2",
4
+ "date": "2026-10-30",
5
+ "status": "stable",
6
+ "title": {
7
+ "en": "Terminals load a new release without closing; only the member waives checks",
8
+ "vi": "Terminal nạp bản mới không cần đóng; chỉ member được bỏ qua kiểm tra"
9
+ },
10
+ "summary": {
11
+ "en": "After an update every open Terminal moves to the new release when idle and keeps its conversation, and a team's required checks can be skipped only by the member, on a card.",
12
+ "vi": "Sau khi cập nhật, mọi Terminal đang mở tự nạp bản mới khi rảnh và giữ nguyên cuộc trò chuyện; kiểm tra bắt buộc của team chỉ được bỏ qua khi member xác nhận trên thẻ."
13
+ },
14
+ "requires": null,
15
+ "items": [
16
+ {
17
+ "kind": "new",
18
+ "en": "piagent reload: every open Terminal (piagent and piagent studio) loads the installed release when idle and keeps its conversation; piagent-update asks for it after installing.",
19
+ "vi": "piagent reload: mọi Terminal đang mở (piagent và piagent studio) nạp bản đã cài khi rảnh, giữ nguyên cuộc trò chuyện; piagent-update tự yêu cầu sau khi cài."
20
+ },
21
+ {
22
+ "kind": "improved",
23
+ "en": "A Terminal running a task is never cut off: it loads the new release when the task finishes, and its footer says it is waiting.",
24
+ "vi": "Terminal đang chạy task không bị cắt ngang: nạp bản mới khi task xong, footer ghi rõ đang chờ."
25
+ },
26
+ {
27
+ "kind": "new",
28
+ "en": "Only the member can skip the checks a team requires: the agent asks on an approval card, once, for the code as it is then; later changes are checked again.",
29
+ "vi": "Chỉ member được bỏ qua kiểm tra mà team bắt buộc: agent hỏi trên thẻ duyệt, một lần, cho đúng code lúc đó; code đổi sau đó phải kiểm tra lại."
30
+ },
31
+ {
32
+ "kind": "improved",
33
+ "en": "A turn whose checks the member skipped says so in its status, apart from a turn where no check ran; a push still asks as in every mode.",
34
+ "vi": "Lượt được member bỏ qua kiểm tra ghi rõ trong trạng thái, tách khỏi lượt không có kiểm tra nào chạy; push vẫn hỏi như mọi chế độ."
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "version": "1.48.1",
40
+ "date": "2026-10-30",
41
+ "status": "stable",
42
+ "title": {
43
+ "en": "Faster first replies on the dashboard and an instant Enter in the Terminal",
44
+ "vi": "Dashboard trả lời nhanh hơn, Enter trong Terminal hiện tin nhắn ngay"
45
+ },
46
+ "summary": {
47
+ "en": "New dashboard conversations start on the model and thinking level you use in the Terminal, and sending a message no longer waits on repository checks or disk flushes.",
48
+ "vi": "Cuộc trò chuyện mới trên dashboard dùng đúng model và mức suy nghĩ bạn dùng ở Terminal, và gửi tin nhắn không còn phải chờ kiểm tra repository hay ghi đĩa."
49
+ },
50
+ "requires": null,
51
+ "items": [
52
+ {
53
+ "kind": "fixed",
54
+ "en": "A new dashboard conversation starts on the model and thinking level set in the Terminal, instead of always the highest reasoning model, so a short question gets a short wait.",
55
+ "vi": "Cuộc trò chuyện mới trên dashboard bắt đầu bằng model và mức suy nghĩ đã chọn ở Terminal, thay vì luôn dùng model suy nghĩ cao nhất, nên câu hỏi ngắn được trả lời nhanh."
56
+ },
57
+ {
58
+ "kind": "fixed",
59
+ "en": "The first message of a new dashboard conversation is always sent, including on models without a thinking mode; it no longer stays behind a \"session was created\" notice.",
60
+ "vi": "Tin nhắn đầu tiên của cuộc trò chuyện mới trên dashboard luôn được gửi, kể cả với model không có chế độ suy nghĩ; không còn kẹt ở thông báo \"Session đã được tạo\"."
61
+ },
62
+ {
63
+ "kind": "improved",
64
+ "en": "Pressing Enter shows your message right away: repository status is read in the background between turns, not while the message waits to appear.",
65
+ "vi": "Nhấn Enter là tin nhắn hiện ngay: trạng thái repository được đọc ở nền giữa các lượt, không còn chặn lúc tin nhắn chờ hiển thị."
66
+ },
67
+ {
68
+ "kind": "improved",
69
+ "en": "Live session events are written without a disk flush per event, which removes a pause before each message and each streamed update.",
70
+ "vi": "Sự kiện của phiên được ghi mà không ép ghi đĩa cho từng sự kiện, bỏ một nhịp chờ trước mỗi tin nhắn và mỗi đoạn stream."
71
+ }
72
+ ]
73
+ },
2
74
  {
3
75
  "version": "1.48.0",
4
76
  "date": "2026-10-30",
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -114,6 +114,14 @@
114
114
  </div>
115
115
  <p>Khởi động lại dashboard sau khi cập nhật để giao diện mới có hiệu lực.</p>
116
116
 
117
+ <h3 id="terminal-dang-mo">Terminal đang mở</h3>
118
+ <p>Không cần đóng Terminal. Sau khi cập nhật, mọi Terminal đang mở (<code>piagent</code> và <code>piagent studio</code>) tự nạp bản mới khi rảnh và giữ nguyên cuộc trò chuyện. Terminal đang chạy task thì chờ task xong rồi mới nạp; footer ghi rõ đang chờ. Terminal cá nhân nạp lại extension của Piagent; Terminal công ty mở lại trong chính cửa sổ đó, trên đúng cuộc trò chuyện. Muốn yêu cầu lại bất cứ lúc nào:</p>
119
+ <div class="command-card">
120
+ <header><span>Nạp bản đã cài vào mọi Terminal đang mở</span><button class="clip-btn" data-clip="piagent reload">Lấy lệnh</button></header>
121
+ <pre><code>piagent reload</code></pre>
122
+ </div>
123
+ <p>Bản mới chạy trên một bản Pi khác thì Terminal cá nhân báo cần mở lại <code>piagent</code>.</p>
124
+
117
125
  <h3 id="cap-nhat-lam-gi">Lệnh này làm gì, theo thứ tự</h3>
118
126
  <ol>
119
127
  <li>Xác định bản đích và bản Pi mà bản đó pin.</li>
@@ -145,9 +153,9 @@
145
153
  <h2 id="pin-cho-team">Pin một bản cho cả team</h2>
146
154
  <p>Khi rollout cho team, cài đúng bản Pi host và đúng bản platform, rồi chạy installer ở chế độ stable. Bước <code>--dry-run</code> in ra những gì sẽ đổi.</p>
147
155
  <div class="command-card">
148
- <header><span>Cài pin theo bản</span><button class="clip-btn" data-clip="npm install -g --ignore-scripts @earendil-works/pi-coding-agent@1.3.0&#10;npm install -g --ignore-scripts @piagent/platform@1.48.0&#10;piagent-install --stable --dry-run&#10;piagent-install --stable">Lấy lệnh</button></header>
156
+ <header><span>Cài pin theo bản</span><button class="clip-btn" data-clip="npm install -g --ignore-scripts @earendil-works/pi-coding-agent@1.3.0&#10;npm install -g --ignore-scripts @piagent/platform@1.48.2&#10;piagent-install --stable --dry-run&#10;piagent-install --stable">Lấy lệnh</button></header>
149
157
  <pre><code>npm install -g --ignore-scripts @earendil-works/pi-coding-agent@1.3.0
150
- npm install -g --ignore-scripts @piagent/platform@1.48.0
158
+ npm install -g --ignore-scripts @piagent/platform@1.48.2
151
159
  piagent-install --stable --dry-run
152
160
  piagent-install --stable</code></pre>
153
161
  </div>
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new" aria-current="page">Changelog</a>
@@ -108,7 +108,33 @@
108
108
  <div class="release-filter" role="toolbar" aria-label="Lọc theo loại thay đổi"><button type="button" data-filter-kind="all" aria-pressed="true">Tất cả</button><button type="button" data-filter-kind="new" aria-pressed="false">Mới</button><button type="button" data-filter-kind="improved" aria-pressed="false">Cải thiện</button><button type="button" data-filter-kind="fixed" aria-pressed="false">Sửa lỗi</button><button type="button" data-filter-kind="security" aria-pressed="false">Bảo mật</button></div>
109
109
  <section class="section releases" data-releases>
110
110
  <h2 id="latest" class="sr-only">Mới nhất</h2>
111
- <article class="release latest" id="v1-48-0" data-release>
111
+ <article class="release latest" id="v1-48-2" data-release>
112
+ <div class="release-meta"><a class="release-version" href="#v1-48-2">v1.48.2</a><time class="release-date" datetime="2026-10-30">30/10/2026</time></div>
113
+ <div class="release-body">
114
+ <h3 data-toc-skip id="v1-48-2-title">Terminal nạp bản mới không cần đóng; chỉ member được bỏ qua kiểm tra</h3>
115
+ <p>Sau khi cập nhật, mọi Terminal đang mở tự nạp bản mới khi rảnh và giữ nguyên cuộc trò chuyện; kiểm tra bắt buộc của team chỉ được bỏ qua khi member xác nhận trên thẻ.</p>
116
+ <ul class="release-items">
117
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>piagent reload: mọi Terminal đang mở (piagent và piagent studio) nạp bản đã cài khi rảnh, giữ nguyên cuộc trò chuyện; piagent-update tự yêu cầu sau khi cài.</span></li>
118
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Chỉ member được bỏ qua kiểm tra mà team bắt buộc: agent hỏi trên thẻ duyệt, một lần, cho đúng code lúc đó; code đổi sau đó phải kiểm tra lại.</span></li>
119
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Terminal đang chạy task không bị cắt ngang: nạp bản mới khi task xong, footer ghi rõ đang chờ.</span></li>
120
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Lượt được member bỏ qua kiểm tra ghi rõ trong trạng thái, tách khỏi lượt không có kiểm tra nào chạy; push vẫn hỏi như mọi chế độ.</span></li>
121
+ </ul>
122
+ </div>
123
+ </article>
124
+ <article class="release" id="v1-48-1" data-release>
125
+ <div class="release-meta"><a class="release-version" href="#v1-48-1">v1.48.1</a><time class="release-date" datetime="2026-10-30">30/10/2026</time></div>
126
+ <div class="release-body">
127
+ <h3 data-toc-skip id="v1-48-1-title">Dashboard trả lời nhanh hơn, Enter trong Terminal hiện tin nhắn ngay</h3>
128
+ <p>Cuộc trò chuyện mới trên dashboard dùng đúng model và mức suy nghĩ bạn dùng ở Terminal, và gửi tin nhắn không còn phải chờ kiểm tra repository hay ghi đĩa.</p>
129
+ <ul class="release-items">
130
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Nhấn Enter là tin nhắn hiện ngay: trạng thái repository được đọc ở nền giữa các lượt, không còn chặn lúc tin nhắn chờ hiển thị.</span></li>
131
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Sự kiện của phiên được ghi mà không ép ghi đĩa cho từng sự kiện, bỏ một nhịp chờ trước mỗi tin nhắn và mỗi đoạn stream.</span></li>
132
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Cuộc trò chuyện mới trên dashboard bắt đầu bằng model và mức suy nghĩ đã chọn ở Terminal, thay vì luôn dùng model suy nghĩ cao nhất, nên câu hỏi ngắn được trả lời nhanh.</span></li>
133
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Tin nhắn đầu tiên của cuộc trò chuyện mới trên dashboard luôn được gửi, kể cả với model không có chế độ suy nghĩ; không còn kẹt ở thông báo &quot;Session đã được tạo&quot;.</span></li>
134
+ </ul>
135
+ </div>
136
+ </article>
137
+ <article class="release" id="v1-48-0" data-release>
112
138
  <div class="release-meta"><a class="release-version" href="#v1-48-0">v1.48.0</a><time class="release-date" datetime="2026-10-30">30/10/2026</time></div>
113
139
  <div class="release-body">
114
140
  <h3 data-toc-skip id="v1-48-0-title">Bypass dừng ở bước bàn giao: mọi chế độ hỏi trước khi việc rời khỏi máy</h3>
@@ -394,37 +420,35 @@
394
420
  </ul>
395
421
  </div>
396
422
  </article>
397
- <article class="release" id="v1-43-0" data-release>
398
- <div class="release-meta"><a class="release-version" href="#v1-43-0">v1.43.0</a><time class="release-date" datetime="2026-10-26">26/10/2026</time></div>
399
- <div class="release-body">
400
- <h3 data-toc-skip id="v1-43-0-title">Dev chọn workflow, harness hỗ trợ BE/FE theo project <span class="tag">không phát hành</span></h3>
401
- <p>Đã tag nhưng chưa publish: gate wording tài liệu chặn phát hành. Thay bằng 1.43.4.</p>
402
- <ul class="release-items">
403
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Tự nhận diện repo/package FE, BE trong thư mục hội thoại và gợi ý checks phù hợp, làm mới theo task.</span></li>
404
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Context workflow tùy chọn nhớ nguồn requirement, plan và tasks của công việc khi mở lại hội thoại, truyền chỉ đọc cho helper và hỗ trợ đổi hoặc xóa.</span></li>
405
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Lifecycle là hướng dẫn theo workflow và điểm dừng Dev chọn, không ép file spec hoặc tự tạo gate release. Skill được chọn không cấp quyền hay bỏ qua kiểm chứng, review và duyệt deploy.</span></li>
406
- </ul>
407
- </div>
408
- </article>
409
- <article class="release" id="v1-42-1" data-release>
410
- <div class="release-meta"><a class="release-version" href="#v1-42-1">v1.42.1</a><time class="release-date" datetime="2026-10-25">25/10/2026</time></div>
411
- <div class="release-body">
412
- <h3 data-toc-skip id="v1-42-1-title">Dashboard tải nhanh hơn, hash workspace dùng ít bộ nhớ hơn</h3>
413
- <p>Mã và CSS tĩnh được nén khi truyền; giao diện phụ tải khi cần, có cách phục hồi khi tải lỗi.</p>
414
- <ul class="release-items">
415
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Dashboard local gửi JS/CSS gzip khi trình duyệt hỗ trợ, dùng lại dữ liệu đã nén.</span></li>
416
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Gateway và giao diện inspection tải riêng; lỗi tải Settings nằm trong dialog có thể đóng. Theme và tùy chọn trên server được giữ nguyên.</span></li>
417
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Hash file workspace nhỏ dùng buffer theo kích thước file, vẫn kiểm tra toàn bộ nội dung và định danh file.</span></li>
418
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Gateway khởi động trên Windows native và lưu receipt workspace có chữ ký mà không áp kiểm tra quyền POSIX hoặc fsync thư mục lên file Windows.</span></li>
419
- </ul>
420
- </div>
421
- </article>
422
423
  </section>
423
424
  <section class="section archive">
424
425
  <h2 id="archive">Các bản trước</h2>
425
426
  <p class="section-lead">Bản cũ hơn, gom theo tháng. Mở một tháng để xem từng thay đổi.</p>
426
427
  <details class="release-archive">
427
- <summary><span>Tháng 10/2026</span><span class="archive-count">63</span></summary>
428
+ <summary><span>Tháng 10/2026</span><span class="archive-count">65</span></summary>
429
+ <div class="archive-row" id="v1-43-0">
430
+ <div class="archive-head"><a class="release-version" href="#v1-43-0">v1.43.0</a> <span class="release-date">26/10/2026</span></div>
431
+ <div class="archive-body">
432
+ <p><strong>Dev chọn workflow, harness hỗ trợ BE/FE theo project</strong> <span class="tag">không phát hành</span> — Đã tag nhưng chưa publish: gate wording tài liệu chặn phát hành. Thay bằng 1.43.4.</p>
433
+ <ul class="release-items">
434
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Tự nhận diện repo/package FE, BE trong thư mục hội thoại và gợi ý checks phù hợp, làm mới theo task.</span></li>
435
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Context workflow tùy chọn nhớ nguồn requirement, plan và tasks của công việc khi mở lại hội thoại, truyền chỉ đọc cho helper và hỗ trợ đổi hoặc xóa.</span></li>
436
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Lifecycle là hướng dẫn theo workflow và điểm dừng Dev chọn, không ép file spec hoặc tự tạo gate release. Skill được chọn không cấp quyền hay bỏ qua kiểm chứng, review và duyệt deploy.</span></li>
437
+ </ul>
438
+ </div>
439
+ </div>
440
+ <div class="archive-row" id="v1-42-1">
441
+ <div class="archive-head"><a class="release-version" href="#v1-42-1">v1.42.1</a> <span class="release-date">25/10/2026</span></div>
442
+ <div class="archive-body">
443
+ <p><strong>Dashboard tải nhanh hơn, hash workspace dùng ít bộ nhớ hơn</strong> — Mã và CSS tĩnh được nén khi truyền; giao diện phụ tải khi cần, có cách phục hồi khi tải lỗi.</p>
444
+ <ul class="release-items">
445
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Dashboard local gửi JS/CSS gzip khi trình duyệt hỗ trợ, dùng lại dữ liệu đã nén.</span></li>
446
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Gateway và giao diện inspection tải riêng; lỗi tải Settings nằm trong dialog có thể đóng. Theme và tùy chọn trên server được giữ nguyên.</span></li>
447
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Hash file workspace nhỏ dùng buffer theo kích thước file, vẫn kiểm tra toàn bộ nội dung và định danh file.</span></li>
448
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Gateway khởi động trên Windows native và lưu receipt workspace có chữ ký mà không áp kiểm tra quyền POSIX hoặc fsync thư mục lên file Windows.</span></li>
449
+ </ul>
450
+ </div>
451
+ </div>
428
452
  <div class="archive-row" id="v1-42-0">
429
453
  <div class="archive-head"><a class="release-version" href="#v1-42-0">v1.42.0</a> <span class="release-date">25/10/2026</span></div>
430
454
  <div class="archive-body">
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -37,7 +37,7 @@
37
37
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
38
38
  <span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
39
39
  </a>
40
- <a class="version-badge" href="/whats-new" data-docs-version="v1.48.0" title="v1.48.0 docs · VI"><span class="dot"></span>v1.48.0</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.48.2" title="v1.48.2 docs · VI"><span class="dot"></span>v1.48.2</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -256,8 +256,8 @@
256
256
  },
257
257
  {
258
258
  "path": "tests",
259
- "fileCount": 641,
260
- "sha256": "35835601b983facf21131404df71c12f622503a45ddfcdc1869610022e7bb101"
259
+ "fileCount": 643,
260
+ "sha256": "da53dba99cba2d0d9f5adc6101fb1a49d058038cb5eca770de3bf8785de3290c"
261
261
  }
262
262
  ],
263
263
  "custodianComparisonRules": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/platform",
3
- "version": "1.48.0",
3
+ "version": "1.48.2",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -148,7 +148,7 @@ import { parentRoutingModeFromEnvironment, routingObjectiveFromEnvironment } fro
148
148
  import { ModelSelectionProvenanceTracker } from "../runtime/model/model-selection-provenance.ts";
149
149
  import type { TaskFeatures } from "../runtime/solver/solver-types.ts";
150
150
  import { planRetrievalRoute } from "../runtime/context/retrieval-route-policy.ts";
151
- import { evaluateRuntimeSolver } from "../runtime/solver/runtime-features.ts";
151
+ import { evaluateRuntimeSolver, refreshGitShape } from "../runtime/solver/runtime-features.ts";
152
152
  import { SolverShadowRuntime, solverModeFromEnvironment } from "../runtime/solver/solver-shadow.ts";
153
153
  import { TrajectoryRuntime } from "../runtime/trajectory/trajectory-runtime.ts";
154
154
  import { parseApplyPatchTargets, registerApplyPatchTool } from "../runtime/tools/apply-patch-tool.ts";
@@ -166,6 +166,7 @@ import { prefixCompletions, registerPiagentTool as registerToolDefinition, regis
166
166
  import { registerTaskEvidenceTools } from "../runtime/registration/task-evidence-tools.ts";
167
167
  import { registerAgentResources } from "../runtime/registration/agent-resources.ts";
168
168
  import { registerTerminalUpdateOffer } from "../runtime/update/terminal-update-offer.mjs";
169
+ import { registerPersonalTerminalReload } from "../runtime/update/terminal-reload.mjs";
169
170
  import { registerStyleCommand } from "../runtime/terminal/terminal-style.mjs";
170
171
  import { userSkillFolders } from "../runtime/resources/agent-resources.mjs";
171
172
  import { FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP, ONBOARDING_COMMAND_ACTIONS, WORKFLOW_COMMAND_EXCLUSIONS } from "../runtime/registration/operator-catalogs.ts";
@@ -3386,6 +3387,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
3386
3387
  });
3387
3388
  const runtimeSnapshotCapture = new RuntimeSnapshotCapture(), runtimeVersions = readRuntimeVersionMetadata(PLATFORM_ROOT);
3388
3389
  const solverShadow = solverMode === "off" ? undefined : new SolverShadowRuntime(solverMode);
3390
+ if (solverShadow) {
3391
+ // The solver's Git shape is read ahead of the prompt (runtime-features.ts).
3392
+ pi.on("session_start", (_event, ctx) => { void refreshGitShape(ctx.cwd); });
3393
+ pi.on("agent_settled", (_event, ctx) => { void refreshGitShape(ctx.cwd); });
3394
+ }
3389
3395
  const modelRouteRuntime = new ModelRouteRuntime(parentRoutingMode, routingObjective);
3390
3396
  const modelSelectionProvenance = new ModelSelectionProvenanceTracker();
3391
3397
  const serviceTierRuntime = new ServiceTierRuntime({ environmentValue: process.env.PIAGENT_FAST_MODE });
@@ -4047,7 +4053,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4047
4053
  }
4048
4054
 
4049
4055
  const grants = sessionGrants.get(ctx.sessionManager.getSessionId()) ?? new Set<string>();
4050
- const grantable = Boolean(pendingHumanApproval) && !/handoff/.test(String(pendingHumanApproval!.action.requestedScope));
4056
+ const grantable = Boolean(pendingHumanApproval) && !/handoff|waiver/.test(String(pendingHumanApproval!.action.requestedScope));
4051
4057
  const grant = grantKey(event.toolName, typeof pendingHumanApproval?.action.commandPreview === "string" ? pendingHumanApproval.action.commandPreview : null, preparedInput.input);
4052
4058
  if (pendingHumanApproval && grantable && grants.has(grant)) pendingHumanApproval = null;
4053
4059
  if (pendingHumanApproval) {
@@ -4187,6 +4193,8 @@ export default function piagentGuard(pi: ExtensionAPI) {
4187
4193
  registerAgentResources(pi);
4188
4194
  // A new release is asked about in the Terminal, as the dashboard does.
4189
4195
  registerTerminalUpdateOffer(pi, { installed: installedPlatformVersion(), packageRoot: PLATFORM_ROOT, restart: "pi" });
4196
+ // `piagent reload` and a finished update reach this Terminal: it reloads when idle.
4197
+ registerPersonalTerminalReload(pi, { installed: readJsonFile<{ version?: string }>(path.join(PLATFORM_ROOT, "package.json"))?.version });
4190
4198
  // The Terminal's look (/style): Pi keeps the pick in its own settings.
4191
4199
  registerStyleCommand(pi);
4192
4200
  registerSessionCommands(pi, { ...registrationDeps, ...contextCommandApi });
@@ -0,0 +1,35 @@
1
+ // The member, never the agent, may waive the repository checks the Harness
2
+ // asks for ("bỏ qua test, đẩy luôn"). Before, the member's words changed
3
+ // nothing: under verify "require" the harness sent the agent back
4
+ // max_fix_loops times anyway, while the delivery prompt already said checks
5
+ // may be "explicitly human-waived". The agent now asks with waive_checks; the
6
+ // member answers on an approval card, once, for the code as it is then. A
7
+ // later change needs its checks again. The turn still reports that no check
8
+ // passed, and says the member waived them.
9
+ import { approve } from './command-tools.mjs';
10
+
11
+ const PARAMETERS = { type: 'object', properties: {
12
+ member_words: { type: 'string', minLength: 1, maxLength: 300, description: 'What the member said, quoted: the request to skip the checks.' },
13
+ reason: { type: 'string', minLength: 1, maxLength: 300, description: 'Which checks are skipped and what is left unverified.' } },
14
+ required: ['member_words', 'reason'], additionalProperties: false };
15
+
16
+ const text = value => ({ content: [{ type: 'text', text: value }], details: {} });
17
+
18
+ export function waiveChecksTool(self) {
19
+ return { name: 'waive_checks', label: 'Waive checks', parameters: PARAMETERS,
20
+ description: 'Ask the member to confirm that the repository checks are skipped for the code as it is now. Call it only when the member asked in this conversation to skip the tests or checks (quote them in member_words); never to end a turn sooner, never on your own judgment. The member answers on an approval card. Approved: the harness stops asking for checks on this exact code, and the turn reports that no check passed and that the member waived them. Change the code after it and the checks are required again. Pushing still asks the member as always.',
21
+ execute: async (id, args, signal, _onUpdate, ctx) => {
22
+ const run = self.run;
23
+ if (!run) throw Error('managed-checks-waiver-no-run: no member request is running.');
24
+ if (run.policy?.verify === 'off') return text('The Harness asks for no checks here: nothing to waive.');
25
+ const digest = await self.digest();
26
+ if (digest === null) throw Error('managed-checks-waiver-unreadable: the current code cannot be read, so a waiver could not be bound to it.');
27
+ await approve(self, id, signal, ctx, { toolName: 'waive_checks', rawAction: { digest }, commandPreview: `waive checks: ${args.member_words}`,
28
+ provider: 'harness', requestedScope: 'checks-waiver-once', reason: String(args.reason).slice(0, 300), riskClass: 'medium',
29
+ allowConsequence: 'The harness stops asking for checks on this exact code; the turn reports that no check passed and that you waived them.',
30
+ denyConsequence: 'The checks stay required; the agent runs them before it finishes.' },
31
+ { title: 'Bỏ qua kiểm tra?', body: `Bạn đã nói: "${args.member_words}"\n\nBỏ qua: ${args.reason}\n\nĐồng ý: Harness không yêu cầu chạy kiểm tra cho đúng code hiện tại; lượt này ghi là chưa có kiểm tra nào qua và bạn đã miễn. Sửa code sau đó thì phải kiểm tra lại. Push vẫn hỏi bạn như thường.\nTừ chối: vẫn phải chạy kiểm tra trước khi xong.` });
32
+ run.waivedDigest = digest;
33
+ return text('The member waived the checks for the code as it is now. Do not run them again unless the member asks; say in your answer that the change is unverified.');
34
+ } };
35
+ }
@@ -75,11 +75,11 @@ export function stillAsks(self, reason) {
75
75
 
76
76
  // One approval of the member (the dashboard card, else the Terminal). The
77
77
  // member may allow it once, allow the same exact action for the rest of the
78
- // conversation (never a handoff: its scope names it), deny it, or deny it and
78
+ // conversation (never a handoff or a waiver of checks: its scope names it), deny it, or deny it and
79
79
  // stop the turn so the agent does not try again.
80
80
  const CHOICES = { once: 'Đồng ý một lần', session: 'Đồng ý cho cả cuộc trò chuyện (đúng lệnh này)', deny: 'Từ chối', stop: 'Từ chối và dừng lượt này' };
81
81
  export async function approve(self, id, signal, ctx, action, { title, body }) {
82
- const grantable = !/handoff/.test(String(action.requestedScope ?? '')), key = `${action.toolName}\u0000${action.commandPreview ?? JSON.stringify(action.rawAction ?? {})}`;
82
+ const grantable = !/handoff|waiver/.test(String(action.requestedScope ?? '')), key = `${action.toolName}\u0000${action.commandPreview ?? JSON.stringify(action.rawAction ?? {})}`;
83
83
  if (grantable && self.sessionApprovals?.has(key)) return;
84
84
  const { piApprovalBroker } = await import('../runtime/inspection/approval-broker.ts');
85
85
  const choices = ['once', ...(grantable ? ['session'] : []), 'deny', 'stop'];
@@ -29,7 +29,7 @@ import { HELPER_CALLS, HELPER_ROLES, helperRoles, helperPrompt, webPrompt, deleg
29
29
  import { loadAgentResources } from './agent-skills.mjs';
30
30
  import { loadPiagentThemes } from '../runtime/terminal/terminal-style.mjs';
31
31
  import { restorePermission, recordBypass, permissionSetter } from './permission.mjs';
32
- import { commandTools, servicesPrompt } from './command-tools.mjs'; import { attachMemory, memoryToolsFor, openRound, closeRound, prepareMemoryContext, trackMemoryLessons } from './memory.mjs';
32
+ import { commandTools, servicesPrompt } from './command-tools.mjs'; import { waiveChecksTool } from './check-waiver.mjs'; import { attachMemory, memoryToolsFor, openRound, closeRound, prepareMemoryContext, trackMemoryLessons } from './memory.mjs';
33
33
 
34
34
  const PROVIDER = 'agent_watch_managed';
35
35
  export const HELPER_BEAT_MS = 60_000;
@@ -129,7 +129,7 @@ export class ManagedSession {
129
129
  if (roles.length) customTools.push({ name: 'delegate', label: 'Subagent', description: delegateDescription(roles) + SPEC_PROMPT,
130
130
  parameters: delegateParameters(), // the brief and its structured fields (helper-settlement.mjs)
131
131
  execute: (_id, args, signal) => self.delegate(delegateArgs(self, args), signal) });
132
- customTools.push(sessionPlanTool(self), ...(roles.length ? [resolveTool(self)] : [])); // the objection ledger's decisions (ledger.mjs)
132
+ customTools.push(sessionPlanTool(self), waiveChecksTool(self), ...(roles.length ? [resolveTool(self)] : [])); // the member's waiver of checks (check-waiver.mjs), the objection ledger's decisions (ledger.mjs)
133
133
  // The member decides what the request leaves open: numbered options, or their own words.
134
134
  customTools.push(workflowContextTool(() => self.session.sessionManager));
135
135
  customTools.push(askTool(self), ...memoryToolsFor(self, agentDir)); // team memory's tools, once Studio answered memory here (memory.mjs)
@@ -148,7 +148,7 @@ export function workflowPrompt(policy, checks) {
148
148
  if (policy.plan !== 'off') lines.push(`- Plan: for work with several steps keep a checklist with update_plan and update it as you go. When the member asks for several items (plans, files, tickets), put every item in the checklist and work through all of them; do not end your turn to report between items.${policy.plan === 'require' ? ' For a complex task, file edits are refused until a plan exists. A checklist left with pending steps sends you back to the next one until every step is completed or deferred: if you cannot go on without the member, ask with ask_user. Mark a step deferred only when the member asked to leave it for later or it is no longer needed; never mark completed a step you did not do.' : ''}`);
149
149
  if (policy.verify !== 'off') {
150
150
  const list = checks.commands.length ? `${checks.commands.map(c => '`' + c + '`').join(', ')}${checks.source === 'detected' ? ' (detected; the repository declares none in AGENTS.md "## Checks")' : ''}` : 'none declared: choose the tests, type check or build that cover the change';
151
- lines.push(`- Verify: after changing code, run the repository checks that cover the change and fix failures before you finish. Repository checks: ${list}.${policy.verify === 'require' ? ' The turn does not end until a check has passed on the final code.' : ''}`);
151
+ lines.push(`- Verify: after changing code, run the repository checks that cover the change and fix failures before you finish. Repository checks: ${list}.${policy.verify === 'require' ? ' The turn does not end until a check has passed on the final code.' : ''} Only the member may skip the checks: when they ask to, call waive_checks with their words; never skip them on your own.`);
152
152
  }
153
153
  // What each severity asks: blocking is fixed or argued, major decided, minor only listed.
154
154
  const severities = ' Blocking findings: fix them, or reject one with resolve_objection and the reviewer judges your reason; a disagreement that remains goes to the member. Major findings: fix them or reject them with resolve_objection; no new review follows. Minor findings are listed for the member and need nothing.';
@@ -350,7 +350,7 @@ export async function completionGate(managed, run, signal) {
350
350
  // The member declined the approval a check needed (a JDK, Docker, the
351
351
  // network): asking again would only repeat the same answer.
352
352
  if (policy.verify === 'require' && !run.verified && run.approvalDeclined && run.fixLoops > 0) verifyGaveUp = true;
353
- if (policy.verify === 'require' && !run.verified && !run.docsOnly && !verifyGaveUp) {
353
+ if (policy.verify === 'require' && !run.verified && !run.docsOnly && !verifyGaveUp && run.waivedDigest !== digest) {
354
354
  run.fixLoops += 1;
355
355
  const list = run.checks.commands.length ? run.checks.commands.map(c => '`' + c + '`').join(', ') : 'the tests, type check or build that cover the change';
356
356
  await send(`Harness process check: code changed in this turn, but no check has passed on the current code. Run the checks that cover your change (${list}) and fix what fails. If a check cannot run because the machine lacks something (a runtime such as a JDK, Docker, a database or another service, the network), request the approval that makes it run (run_with_network, run_with_docker, start_docker, run_on_host) or ask the member with ask_user, then run it; install or start nothing without that approval. If it fails for a reason unrelated to your change, say so plainly.`,
@@ -441,8 +441,8 @@ export async function completionGate(managed, run, signal) {
441
441
  if (!run.changed && !run.disputes.length && !ledgerOf(run).length) return;
442
442
  const finalPlan = currentPlan(session.sessionManager), planOpen = run.planUpdated ? finalPlan?.plan?.filter(p => p.status === 'pending' || p.status === 'in_progress').length ?? 0 : 0;
443
443
  const status = { verified: run.verified, reviewed: run.reviewed, blockingOpen: run.blockingOpen, checks: run.checksRun, fixLoops: run.fixLoops,
444
- ...(planOpen ? { planOpen } : {}), ...(run.planSkipped ? { planSkipped: true } : {}), ...(run.disputes.length ? { disputes: run.disputes.length } : {}), ...(run.changed ? {} : { unchanged: true }) };
445
- const words = [...(run.changed ? [run.verified ? 'a check passed on the final code' : policy.verify === 'off' ? null : run.docsOnly ? 'only documentation changed: no check needed' : 'no check passed on the final code',
444
+ ...(planOpen ? { planOpen } : {}), ...(run.planSkipped ? { planSkipped: true } : {}), ...(!run.verified && run.waivedDigest === digest ? { checksWaived: true } : {}), ...(run.disputes.length ? { disputes: run.disputes.length } : {}), ...(run.changed ? {} : { unchanged: true }) };
445
+ const words = [...(run.changed ? [run.verified ? 'a check passed on the final code' : policy.verify === 'off' ? null : run.docsOnly ? 'only documentation changed: no check needed' : run.waivedDigest === digest ? 'no check passed on the final code: the member waived the checks' : 'no check passed on the final code',
446
446
  run.reviewed ? (run.blockingOpen ? `review: ${run.blockingOpen} blocking issue(s) still open` : 'review: no blocking issue') : policy.review === 'off' ? null : run.reviewUnavailable ? `review could not run (${run.reviewUnavailableReason})` : 'not reviewed',
447
447
  run.planSkipped ? 'code was changed before the required plan existed' : null, planOpen ? `plan: ${planOpen} step(s) not completed` : null] : []),
448
448
  run.disputes.length ? `${run.disputes.length} disagreement(s) between the main agent and a subagent go to the member` : null, ledgerWords(run)].filter(Boolean);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/core",
3
- "version": "1.48.0",
3
+ "version": "1.48.2",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -252,7 +252,11 @@ export class RuntimeEventStore {
252
252
  const flags = fs.constants.O_WRONLY | fs.constants.O_APPEND | (fs.constants.O_NOFOLLOW ?? 0)
253
253
  | (creating ? fs.constants.O_CREAT | fs.constants.O_EXCL : 0);
254
254
  const descriptor = fs.openSync(target, flags, 0o600);
255
- try { fs.writeFileSync(descriptor, `${JSON.stringify(event)}\n`); fs.fsyncSync(descriptor); fs.fchmodSync(descriptor, 0o600); } finally { fs.closeSync(descriptor); }
255
+ // No fsync: it ran for every event, Terminal sessions included, and three of
256
+ // them sat between Enter and the message showing. A store belongs to one
257
+ // runtime instance, and one left damaged by a power cut only sends the
258
+ // dashboard back to a snapshot (resyncRequired).
259
+ try { fs.writeFileSync(descriptor, `${JSON.stringify(event)}\n`); fs.fchmodSync(descriptor, 0o600); } finally { fs.closeSync(descriptor); }
256
260
  segment.count += 1; this.events.push(event); this.eventById.set(event.eventId, event);
257
261
  if (candidate) this.emissionGuard.recordAccepted(candidate);
258
262
  try { this.enforceRetention(); } catch { this.corruptions.push("event-retention-failed"); }
@@ -1,3 +1,5 @@
1
+ import { execFile } from "node:child_process";
2
+
1
3
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
4
 
3
5
  import { gitEvidenceRootDetails, workingTreeHasChanges } from "../../extensions/workspace-evidence-roots.js";
@@ -8,17 +10,57 @@ import type { TaskFeatureInput } from "./task-features.ts";
8
10
  import type { SolverShadowEvaluation } from "./solver-shadow.ts";
9
11
  import { SolverShadowRuntime } from "./solver-shadow.ts";
10
12
 
11
- // Finding the repositories under a parent folder runs Git once per child and
12
- // blocks the terminal; they rarely change between two prompts.
13
- const ROOTS_TTL_MS = 30_000;
14
- const rootDetails = new Map<string, { at: number; details: ReturnType<typeof gitEvidenceRootDetails> }>();
15
- function gitShape(cwd: string): { gitReady: boolean; dirtyTree: boolean } {
16
- let cached = rootDetails.get(cwd);
17
- if (!cached || Date.now() - cached.at > ROOTS_TTL_MS) {
18
- try { cached = { at: Date.now(), details: gitEvidenceRootDetails(cwd) }; } catch { return { gitReady: false, dirtyTree: true }; }
19
- rootDetails.set(cwd, cached);
13
+ type GitShape = { gitReady: boolean; dirtyTree: boolean };
14
+
15
+ function readGitShape(cwd: string): GitShape {
16
+ try {
17
+ const details = gitEvidenceRootDetails(cwd);
18
+ return { gitReady: details.roots.length > 0, dirtyTree: workingTreeHasChanges(cwd, details) };
19
+ } catch { return { gitReady: false, dirtyTree: true }; }
20
+ }
21
+
22
+ // The Git shape runs `git rev-parse` (once per child repository in a parent
23
+ // folder) and `git status`. Read synchronously on every prompt it held the
24
+ // Terminal between Enter and the message showing, longer the larger the
25
+ // repository. It is now read in a child process when the session starts and
26
+ // after each run, and a prompt takes the last answer. Only a prompt with no
27
+ // answer yet reads it inline, as before.
28
+ const SHAPE_FRESH_MS = 5_000;
29
+ const SHAPE_PROBE = `import(${JSON.stringify(new URL("../../extensions/workspace-evidence-roots.js", import.meta.url).href)}).then((git) => {
30
+ let shape; try { const details = git.gitEvidenceRootDetails(process.argv[1]);
31
+ shape = { gitReady: details.roots.length > 0, dirtyTree: git.workingTreeHasChanges(process.argv[1], details) };
32
+ } catch { shape = { gitReady: false, dirtyTree: true }; }
33
+ process.stdout.write(JSON.stringify(shape));
34
+ });`;
35
+ const shapes = new Map<string, { at: number; shape: GitShape }>();
36
+ const probing = new Map<string, Promise<void>>();
37
+
38
+ export function refreshGitShape(cwd: string): Promise<void> {
39
+ const running = probing.get(cwd);
40
+ if (running) return running;
41
+ const startedAt = Date.now();
42
+ const probe = new Promise<void>((resolve) => {
43
+ execFile(process.execPath, ["--input-type=module", "-e", SHAPE_PROBE, cwd], { timeout: 60_000, maxBuffer: 4096, windowsHide: true }, (error, stdout) => {
44
+ try {
45
+ const shape = JSON.parse(String(stdout)) as GitShape;
46
+ if (!error && typeof shape.gitReady === "boolean" && typeof shape.dirtyTree === "boolean") shapes.set(cwd, { at: startedAt, shape });
47
+ } catch { /* the next prompt reads it inline */ }
48
+ resolve();
49
+ });
50
+ }).finally(() => probing.delete(cwd));
51
+ probing.set(cwd, probe);
52
+ return probe;
53
+ }
54
+
55
+ function gitShape(cwd: string): GitShape {
56
+ const cached = shapes.get(cwd);
57
+ if (!cached) {
58
+ const shape = readGitShape(cwd);
59
+ shapes.set(cwd, { at: Date.now(), shape });
60
+ return shape;
20
61
  }
21
- return { gitReady: cached.details.roots.length > 0, dirtyTree: workingTreeHasChanges(cwd, cached.details) };
62
+ if (Date.now() - cached.at > SHAPE_FRESH_MS) void refreshGitShape(cwd);
63
+ return cached.shape;
22
64
  }
23
65
 
24
66
  export function runtimeTaskFeatureInput(options: {