@piagent/platform 1.44.3 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +1 -1
  3. package/README.vi.md +1 -1
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +6 -6
  6. package/docs/en/windows.md +2 -0
  7. package/docs/herdr-workflow.md +13 -1
  8. package/docs/languages.json +4 -4
  9. package/docs/operator-manual-vietnamese.md +7 -7
  10. package/docs/publishing-for-teams.md +2 -0
  11. package/docs/quickstart-vietnamese.md +1 -1
  12. package/docs/release-install-policy.md +2 -2
  13. package/docs/team-onboarding.md +2 -2
  14. package/docs/vi/windows.md +2 -0
  15. package/docs-site/architecture.html +1 -1
  16. package/docs-site/assets/search-en.json +1 -1
  17. package/docs-site/assets/search-vi.json +1 -1
  18. package/docs-site/benchmark.html +1 -1
  19. package/docs-site/commands.html +1 -1
  20. package/docs-site/content/en/quickstart.html +1 -1
  21. package/docs-site/content/quickstart.html +1 -1
  22. package/docs-site/context-engine.html +1 -1
  23. package/docs-site/ecosystem.html +1 -1
  24. package/docs-site/en/architecture.html +1 -1
  25. package/docs-site/en/benchmark.html +1 -1
  26. package/docs-site/en/commands.html +1 -1
  27. package/docs-site/en/context-engine.html +1 -1
  28. package/docs-site/en/ecosystem.html +1 -1
  29. package/docs-site/en/index.html +15 -15
  30. package/docs-site/en/mcp-approval.html +1 -1
  31. package/docs-site/en/mcp-auth.html +1 -1
  32. package/docs-site/en/mcp-servers.html +1 -1
  33. package/docs-site/en/mcp.html +1 -1
  34. package/docs-site/en/permissions.html +1 -1
  35. package/docs-site/en/profiles.html +1 -1
  36. package/docs-site/en/quickstart.html +2 -2
  37. package/docs-site/en/runtime.html +1 -1
  38. package/docs-site/en/scope.html +1 -1
  39. package/docs-site/en/security.html +1 -1
  40. package/docs-site/en/team.html +1 -1
  41. package/docs-site/en/update.html +3 -3
  42. package/docs-site/en/whats-new.html +54 -32
  43. package/docs-site/en/windows.html +1 -1
  44. package/docs-site/en/workflows.html +1 -1
  45. package/docs-site/index.html +15 -15
  46. package/docs-site/mcp-approval.html +1 -1
  47. package/docs-site/mcp-auth.html +1 -1
  48. package/docs-site/mcp-servers.html +1 -1
  49. package/docs-site/mcp.html +1 -1
  50. package/docs-site/permissions.html +1 -1
  51. package/docs-site/profiles.html +1 -1
  52. package/docs-site/quickstart.html +2 -2
  53. package/docs-site/releases.json +67 -0
  54. package/docs-site/runtime.html +1 -1
  55. package/docs-site/scope.html +1 -1
  56. package/docs-site/security.html +1 -1
  57. package/docs-site/team.html +1 -1
  58. package/docs-site/update.html +3 -3
  59. package/docs-site/whats-new.html +54 -32
  60. package/docs-site/windows.html +1 -1
  61. package/docs-site/workflows.html +1 -1
  62. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  63. package/package.json +1 -1
  64. package/packages/piagent-core/extensions/company-session-guard.ts +4 -2
  65. package/packages/piagent-core/extensions/working-tree-snapshot-worker.mjs +9 -0
  66. package/packages/piagent-core/extensions/workspace-evidence-roots.js +28 -2
  67. package/packages/piagent-core/managed/herdr-reporter.mjs +85 -0
  68. package/packages/piagent-core/managed/store.d.mts +1 -0
  69. package/packages/piagent-core/managed/store.mjs +13 -0
  70. package/packages/piagent-core/package.json +1 -1
  71. package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -3
  72. package/packages/piagent-core/runtime/input/chat-images.ts +66 -10
  73. package/packages/piagent-core/runtime/input/file-arguments.ts +102 -0
  74. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +2 -6
  75. package/packages/piagent-core/runtime/inspection/working-tree-off-thread.ts +52 -0
  76. package/packages/piagent-core/runtime/solver/runtime-features.ts +15 -3
  77. package/packages/piagent-webui/dist/client/assets/{App-DcaqK-_s.js → App-CiwY9VpU.js} +1 -1
  78. package/packages/piagent-webui/dist/client/assets/{GatewayApp-5DVdNIyr.js → GatewayApp-DrNJW3v_.js} +3 -3
  79. package/packages/piagent-webui/dist/client/assets/{InspectionApp-CBPHxuqj.js → InspectionApp-BBiwSaV0.js} +1 -1
  80. package/packages/piagent-webui/dist/client/assets/{SessionAgentWorkspace-DmD9Qt4f.js → SessionAgentWorkspace-DwyPebIX.js} +1 -1
  81. package/packages/piagent-webui/dist/client/assets/{SessionInspectorDrawer-CK5ujmH3.js → SessionInspectorDrawer-CpVzaf_G.js} +2 -2
  82. package/packages/piagent-webui/dist/client/assets/SettingsNavigation-C-tO5iXn.js +3 -0
  83. package/packages/piagent-webui/dist/client/assets/{SettingsPage-Dda8GW2a.js → SettingsPage-Ca-qRSZa.js} +1 -1
  84. package/packages/piagent-webui/dist/client/assets/{TerminalPanel-X2389XHl.js → TerminalPanel-B8j_r6-y.js} +1 -1
  85. package/packages/piagent-webui/dist/client/assets/{WorkspaceCodeEditor-Dt3dS_QG.js → WorkspaceCodeEditor-BDTPvq-0.js} +1 -1
  86. package/packages/piagent-webui/dist/client/assets/{WorkspaceExplorer-CBHwY4BN.js → WorkspaceExplorer-DVsNU0eu.js} +3 -3
  87. package/packages/piagent-webui/dist/client/assets/{index-BMQo9OEq.js → index-uhJMXxqD.js} +2 -2
  88. package/packages/piagent-webui/dist/client/index.html +1 -1
  89. package/packages/piagent-webui/shared/company-copy.ts +1 -0
  90. package/scripts/piagent-studio.mjs +16 -6
  91. package/packages/piagent-webui/dist/client/assets/SettingsNavigation-BnorH-l_.js +0 -3
@@ -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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</a>
41
41
  <nav class="top-nav" aria-label="Tài liệu">
42
42
  <a href="/quickstart" aria-current="page">Tài liệu</a>
43
43
  <a href="/whats-new">Changelog</a>
@@ -200,7 +200,7 @@ Review diff hiện tại. Chỉ đọc.</code></pre>
200
200
  </div>
201
201
  <div class="callout">
202
202
  <strong>Dùng Herdr?</strong>
203
- <p>Cài Herdr <em>trước</em> <code>piagent-setup</code> để được cài luôn tích hợp Pi; cài sau thì chạy lại <code>piagent-setup --global-only</code>. Herdr chỉ điều phối terminal, không phải lớp bảo vệ. Xem <a href="https://github.com/Vt-mmm/piagent/blob/main/docs/herdr-workflow.md">Herdr workflow</a>.</p>
203
+ <p>Cài Herdr <em>trước</em> <code>piagent-setup</code> để được cài luôn tích hợp Pi; cài sau thì chạy lại <code>piagent-setup --global-only</code>. Trên Windows, cài Herdr trong Ubuntu (WSL2), cùng chỗ với Piagent; trong pane Herdr, <code>piagent studio</code> được mở lại đúng cuộc trò chuyện công ty sau khi Herdr khởi động lại. Herdr chỉ điều phối terminal, không phải lớp bảo vệ. Xem <a href="https://github.com/Vt-mmm/piagent/blob/main/docs/herdr-workflow.md">Herdr workflow</a>.</p>
204
204
  </div>
205
205
  </section>
206
206
 
@@ -1,4 +1,71 @@
1
1
  [
2
+ {
3
+ "version": "1.45.0",
4
+ "date": "2026-10-28",
5
+ "status": "stable",
6
+ "title": {
7
+ "en": "Files and images in a prompt follow the read policy, and the terminal stays responsive",
8
+ "vi": "File và ảnh trong prompt theo đúng quyền đọc, terminal không còn đứng hình"
9
+ },
10
+ "summary": {
11
+ "en": "pi @file can no longer send a protected file to the model, images attach the way you add them, and a prompt in a folder of repositories no longer freezes the terminal.",
12
+ "vi": "pi @file không còn gửi file được bảo vệ cho model, ảnh được đính kèm theo mọi cách thêm vào, và prompt trong thư mục nhiều repo không còn làm terminal đứng hình."
13
+ },
14
+ "items": [
15
+ {
16
+ "kind": "security",
17
+ "en": "pi @file arguments follow the read policy: a protected file (.env) or one outside the project reaches the model as a withheld note, and its image is dropped.",
18
+ "vi": "Tham số pi @file theo quyền đọc: file được bảo vệ (.env) hoặc ngoài project chỉ tới model dưới dạng ghi chú bị giữ lại, ảnh của nó bị bỏ."
19
+ },
20
+ {
21
+ "kind": "new",
22
+ "en": "Images attach when named with @path or @\"path\", pasted with Ctrl+V, or dropped on the terminal; a skipped image says why and how to grant its folder.",
23
+ "vi": "Ảnh được đính kèm khi gọi bằng @path hoặc @\"path\", dán bằng Ctrl+V hay kéo thả vào terminal; ảnh bị bỏ qua nói rõ lý do và cách cấp quyền thư mục."
24
+ },
25
+ {
26
+ "kind": "fixed",
27
+ "en": "An image passed as pi @image.png is attached once, not twice.",
28
+ "vi": "Ảnh truyền bằng pi @image.png chỉ được đính kèm một lần, không còn hai lần."
29
+ },
30
+ {
31
+ "kind": "improved",
32
+ "en": "In a folder of repositories a prompt no longer freezes the terminal for seconds: the change check asks Git and the inspector scans in the background.",
33
+ "vi": "Trong thư mục nhiều repo, prompt không còn làm terminal đứng hình vài giây: kiểm tra thay đổi hỏi Git, inspector quét ở nền."
34
+ }
35
+ ],
36
+ "requires": null
37
+ },
38
+ {
39
+ "version": "1.44.4",
40
+ "date": "2026-10-28",
41
+ "status": "stable",
42
+ "title": {
43
+ "en": "Company conversations come back in Herdr panes",
44
+ "vi": "Conversation công ty trở lại trong pane Herdr"
45
+ },
46
+ "summary": {
47
+ "en": "In Herdr (on Windows: inside WSL), a company pane reports its state and reopens the same conversation after Herdr restarts, with piagent studio --session.",
48
+ "vi": "Trong Herdr (Windows: cài trong WSL), pane công ty báo trạng thái và mở lại đúng conversation sau khi Herdr khởi động lại, bằng piagent studio --session."
49
+ },
50
+ "items": [
51
+ {
52
+ "kind": "new",
53
+ "en": "piagent studio --session <file> reopens a company conversation; only a file of the member’s own store is accepted (managed-session-outside-store otherwise).",
54
+ "vi": "piagent studio --session <file> mở lại một conversation công ty; chỉ nhận file trong kho của chính member (ngoài kho báo managed-session-outside-store)."
55
+ },
56
+ {
57
+ "kind": "new",
58
+ "en": "A company conversation in a Herdr pane reports working, waiting or idle to Herdr, which resumes it with piagent studio, not personal pi. Needs Herdr 0.9.2+.",
59
+ "vi": "Conversation công ty trong pane Herdr báo đang chạy, chờ hay rảnh cho Herdr, và Herdr resume bằng piagent studio, không phải pi cá nhân. Cần Herdr 0.9.2+."
60
+ },
61
+ {
62
+ "kind": "fixed",
63
+ "en": "WSL/Linux: personal Pi no longer opens a company conversation that has no turn yet, and piagent studio --web opens the browser (wslview/explorer.exe).",
64
+ "vi": "WSL/Linux: Pi cá nhân không còn mở được conversation công ty chưa có lượt nào, và piagent studio --web mở được trình duyệt (wslview/explorer.exe)."
65
+ }
66
+ ],
67
+ "requires": null
68
+ },
2
69
  {
3
70
  "version": "1.44.3",
4
71
  "date": "2026-10-28",
@@ -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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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>
@@ -145,9 +145,9 @@
145
145
  <h2 id="pin-cho-team">Pin một bản cho cả team</h2>
146
146
  <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
147
  <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.44.3&#10;piagent-install --stable --dry-run&#10;piagent-install --stable">Lấy lệnh</button></header>
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.45.0&#10;piagent-install --stable --dry-run&#10;piagent-install --stable">Lấy lệnh</button></header>
149
149
  <pre><code>npm install -g --ignore-scripts @earendil-works/pi-coding-agent@1.3.0
150
- npm install -g --ignore-scripts @piagent/platform@1.44.3
150
+ npm install -g --ignore-scripts @piagent/platform@1.45.0
151
151
  piagent-install --stable --dry-run
152
152
  piagent-install --stable</code></pre>
153
153
  </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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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,32 @@
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-44-3" data-release>
111
+ <article class="release latest" id="v1-45-0" data-release>
112
+ <div class="release-meta"><a class="release-version" href="#v1-45-0">v1.45.0</a><time class="release-date" datetime="2026-10-28">28/10/2026</time></div>
113
+ <div class="release-body">
114
+ <h3 data-toc-skip id="v1-45-0-title">File và ảnh trong prompt theo đúng quyền đọc, terminal không còn đứng hình</h3>
115
+ <p>pi @file không còn gửi file được bảo vệ cho model, ảnh được đính kèm theo mọi cách thêm vào, và prompt trong thư mục nhiều repo không còn làm terminal đứng hình.</p>
116
+ <ul class="release-items">
117
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Ảnh được đính kèm khi gọi bằng @path hoặc @&quot;path&quot;, dán bằng Ctrl+V hay kéo thả vào terminal; ảnh bị bỏ qua nói rõ lý do và cách cấp quyền thư mục.</span></li>
118
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Trong thư mục nhiều repo, prompt không còn làm terminal đứng hình vài giây: kiểm tra thay đổi hỏi Git, inspector quét ở nền.</span></li>
119
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Ảnh truyền bằng pi @image.png chỉ được đính kèm một lần, không còn hai lần.</span></li>
120
+ <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Tham số pi @file theo quyền đọc: file được bảo vệ (.env) hoặc ngoài project chỉ tới model dưới dạng ghi chú bị giữ lại, ảnh của nó bị bỏ.</span></li>
121
+ </ul>
122
+ </div>
123
+ </article>
124
+ <article class="release" id="v1-44-4" data-release>
125
+ <div class="release-meta"><a class="release-version" href="#v1-44-4">v1.44.4</a><time class="release-date" datetime="2026-10-28">28/10/2026</time></div>
126
+ <div class="release-body">
127
+ <h3 data-toc-skip id="v1-44-4-title">Conversation công ty trở lại trong pane Herdr</h3>
128
+ <p>Trong Herdr (Windows: cài trong WSL), pane công ty báo trạng thái và mở lại đúng conversation sau khi Herdr khởi động lại, bằng piagent studio --session.</p>
129
+ <ul class="release-items">
130
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>piagent studio --session &lt;file&gt; mở lại một conversation công ty; chỉ nhận file trong kho của chính member (ngoài kho báo managed-session-outside-store).</span></li>
131
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Conversation công ty trong pane Herdr báo đang chạy, chờ hay rảnh cho Herdr, và Herdr resume bằng piagent studio, không phải pi cá nhân. Cần Herdr 0.9.2+.</span></li>
132
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>WSL/Linux: Pi cá nhân không còn mở được conversation công ty chưa có lượt nào, và piagent studio --web mở được trình duyệt (wslview/explorer.exe).</span></li>
133
+ </ul>
134
+ </div>
135
+ </article>
136
+ <article class="release" id="v1-44-3" data-release>
112
137
  <div class="release-meta"><a class="release-version" href="#v1-44-3">v1.44.3</a><time class="release-date" datetime="2026-10-28">28/10/2026</time></div>
113
138
  <div class="release-body">
114
139
  <h3 data-toc-skip id="v1-44-3-title">Prompt injection không lặng lẽ đọc được hệ thống</h3>
@@ -398,41 +423,38 @@
398
423
  <p class="release-requires"><strong>Cần:</strong> Agent Studio 0.38.0 và Agent Watch 0.15.14 (Windows 0.1.17) cho mức Bypass ở chế độ công ty</p>
399
424
  </div>
400
425
  </article>
401
- <article class="release" id="v1-37-1" data-release>
402
- <div class="release-meta"><a class="release-version" href="#v1-37-1">v1.37.1</a><time class="release-date" datetime="2026-10-22">22/10/2026</time></div>
403
- <div class="release-body">
404
- <h3 data-toc-skip id="v1-37-1-title">Đóng run và báo cáo helper mà Studio tin được</h3>
405
- <p>Lần đóng run hỏi lại chỉ được tính khi tới Studio, helper giữ câu trả lời, và yêu cầu ra ngoài chặn mọi dạng IPv6 của địa chỉ nội bộ.</p>
406
- <ul class="release-items">
407
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Lần đóng run hỏi lại sau lỗi thoáng qua có thể được ghi nhận dù không tới Studio; giờ chỉ được tính khi Studio nhận được.</span></li>
408
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Helper đóng lỗi trong chốc lát từng bị coi là thất bại; giờ nó giữ câu trả lời, và báo cáo được gửi lại sau lỗi thoáng qua.</span></li>
409
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Team memory từng gộp lỗi của hai lệnh khác nhau cùng một chương trình thành một bài học; giờ khóa là cả câu lệnh.</span></li>
410
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Phản hồi skill từng trộn các skill có tên giống nhau như <code>a/b</code> và <code>a_b</code>; giờ mỗi tên skill có file riêng.</span></li>
411
- <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Tool <code>web_fetch</code> và package proxy từng cho qua một số dạng IPv6 của địa chỉ nội bộ; giờ mọi dạng như vậy đều bị chặn.</span></li>
412
- <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Khi có upstream proxy của công ty (<code>HTTPS_PROXY</code>), package proxy giờ tự kiểm tra từng đích kết nối thay vì để upstream tự xử lý.</span></li>
413
- </ul>
414
- <p class="release-requires"><strong>Cần:</strong> Agent Watch 0.15.11 (Windows 0.1.16)</p>
415
- </div>
416
- </article>
417
- <article class="release" id="v1-37-0" data-release>
418
- <div class="release-meta"><a class="release-version" href="#v1-37-0">v1.37.0</a><time class="release-date" datetime="2026-10-22">22/10/2026</time></div>
419
- <div class="release-body">
420
- <h3 data-toc-skip id="v1-37-0-title">Câu trả lời bị cắt không còn làm dừng task dài</h3>
421
- <p>Câu trả lời bị cắt giữa chừng phía sau Studio được hỏi lại và task tự làm tiếp; <code>piagent studio --rpc</code> cho script điều khiển cuộc trò chuyện công ty.</p>
422
- <ul class="release-items">
423
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Lệnh mới <code>piagent studio --rpc</code> chạy một cuộc trò chuyện công ty qua giao thức RPC của Pi trên stdin/stdout, nhiều tin nhắn và có <code>ask_user</code>.</span></li>
424
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Repository có lane soak cho task dài: một member giả lập điều khiển agent công ty thật qua các task 45–150 phút.</span></li>
425
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Câu trả lời bị cắt giữa chừng từng hiện &quot;không gửi được yêu cầu&quot; và dừng task dài; giờ Piagent hỏi lại và task tự làm tiếp.</span></li>
426
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Terminal công ty không còn thoát với lỗi khi cuộc trò chuyện đóng ngay sau một lượt trong lúc Git vẫn đang trả trạng thái branch.</span></li>
427
- </ul>
428
- </div>
429
- </article>
430
426
  </section>
431
427
  <section class="section archive">
432
428
  <h2 id="archive">Các bản trước</h2>
433
429
  <p class="section-lead">Bản cũ hơn, gom theo tháng. Mở một tháng để xem từng thay đổi.</p>
434
430
  <details class="release-archive">
435
- <summary><span>Tháng 10/2026</span><span class="archive-count">54</span></summary>
431
+ <summary><span>Tháng 10/2026</span><span class="archive-count">56</span></summary>
432
+ <div class="archive-row" id="v1-37-1">
433
+ <div class="archive-head"><a class="release-version" href="#v1-37-1">v1.37.1</a> <span class="release-date">22/10/2026</span></div>
434
+ <div class="archive-body">
435
+ <p><strong>Đóng run và báo cáo helper mà Studio tin được</strong> — Lần đóng run hỏi lại chỉ được tính khi tới Studio, helper giữ câu trả lời, và yêu cầu ra ngoài chặn mọi dạng IPv6 của địa chỉ nội bộ.</p>
436
+ <ul class="release-items">
437
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Lần đóng run hỏi lại sau lỗi thoáng qua có thể được ghi nhận dù không tới Studio; giờ chỉ được tính khi Studio nhận được.</span></li>
438
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Helper đóng lỗi trong chốc lát từng bị coi là thất bại; giờ nó giữ câu trả lời, và báo cáo được gửi lại sau lỗi thoáng qua.</span></li>
439
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Team memory từng gộp lỗi của hai lệnh khác nhau cùng một chương trình thành một bài học; giờ khóa là cả câu lệnh.</span></li>
440
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Phản hồi skill từng trộn các skill có tên giống nhau như <code>a/b</code> và <code>a_b</code>; giờ mỗi tên skill có file riêng.</span></li>
441
+ <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Tool <code>web_fetch</code> và package proxy từng cho qua một số dạng IPv6 của địa chỉ nội bộ; giờ mọi dạng như vậy đều bị chặn.</span></li>
442
+ <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Khi có upstream proxy của công ty (<code>HTTPS_PROXY</code>), package proxy giờ tự kiểm tra từng đích kết nối thay vì để upstream tự xử lý.</span></li>
443
+ </ul>
444
+ </div>
445
+ </div>
446
+ <div class="archive-row" id="v1-37-0">
447
+ <div class="archive-head"><a class="release-version" href="#v1-37-0">v1.37.0</a> <span class="release-date">22/10/2026</span></div>
448
+ <div class="archive-body">
449
+ <p><strong>Câu trả lời bị cắt không còn làm dừng task dài</strong> — Câu trả lời bị cắt giữa chừng phía sau Studio được hỏi lại và task tự làm tiếp; <code>piagent studio --rpc</code> cho script điều khiển cuộc trò chuyện công ty.</p>
450
+ <ul class="release-items">
451
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Lệnh mới <code>piagent studio --rpc</code> chạy một cuộc trò chuyện công ty qua giao thức RPC của Pi trên stdin/stdout, nhiều tin nhắn và có <code>ask_user</code>.</span></li>
452
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Repository có lane soak cho task dài: một member giả lập điều khiển agent công ty thật qua các task 45–150 phút.</span></li>
453
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Câu trả lời bị cắt giữa chừng từng hiện &quot;không gửi được yêu cầu&quot; và dừng task dài; giờ Piagent hỏi lại và task tự làm tiếp.</span></li>
454
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Terminal công ty không còn thoát với lỗi khi cuộc trò chuyện đóng ngay sau một lượt trong lúc Git vẫn đang trả trạng thái branch.</span></li>
455
+ </ul>
456
+ </div>
457
+ </div>
436
458
  <div class="archive-row" id="v1-36-1">
437
459
  <div class="archive-head"><a class="release-version" href="#v1-36-1">v1.36.1</a> <span class="release-date">22/10/2026</span></div>
438
460
  <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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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.44.3" title="v1.44.3 docs · VI"><span class="dot"></span>v1.44.3</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.45.0" title="v1.45.0 docs · VI"><span class="dot"></span>v1.45.0</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": 631,
260
- "sha256": "37750935b5a593d52607baf8fa6b3faba03105501289fa83f4677504ed05e3d3"
259
+ "fileCount": 634,
260
+ "sha256": "a3731ca8ba76de242f6ce801a4bd1e9a1e61951fe6dcada375503ab9976197be"
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.44.3",
3
+ "version": "1.45.0",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -7,11 +7,13 @@ import path from "node:path";
7
7
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
8
8
 
9
9
  const COMPANY_PROVIDER = "agent_watch_managed";
10
- const STORE = "/AgentWatch/ManagedSessions/";
10
+ // Agent Watch's store: the macOS app's support folder, or the XDG data folder
11
+ // on Linux and WSL (managed/store.mjs).
12
+ const STORE = /\/(AgentWatch|agentwatch)\/ManagedSessions\//;
11
13
 
12
14
  export function isCompanySession(ctx: Pick<ExtensionContext, "sessionManager">): boolean {
13
15
  const file = String(ctx.sessionManager?.getSessionFile?.() ?? "").split(path.sep).join("/");
14
- if (file.includes(STORE)) return true;
16
+ if (STORE.test(file)) return true;
15
17
  try {
16
18
  return ctx.sessionManager.getEntries().some((entry: any) => typeof entry?.customType === "string" && entry.customType.startsWith("agent-watch-run"));
17
19
  } catch {
@@ -0,0 +1,9 @@
1
+ import { parentPort, workerData } from 'node:worker_threads';
2
+ import { matchesProtectedPath } from './policy-core.js';
3
+ import { workingTreeSnapshot } from './task-state.js';
4
+ // Hash the working tree away from the thread that draws the terminal.
5
+ const { cwd, gitTimeoutMs, protectedPaths } = workerData;
6
+ parentPort.postMessage(workingTreeSnapshot(cwd, {
7
+ gitTimeoutMs,
8
+ isProtectedProjectPath: (projectPath) => Boolean(matchesProtectedPath(projectPath, protectedPaths))
9
+ }));
@@ -104,11 +104,17 @@ function directChildGitEvidenceRootDetails(cwd) {
104
104
  complete = false;
105
105
  continue;
106
106
  }
107
- if (!isGitWorkingTree(child)) {
107
+ // One Git process per child: a parent of thirty repositories ran sixty.
108
+ let inside, topLevel;
109
+ try {
110
+ [inside, topLevel] = gitOutput(child, ["rev-parse", "--is-inside-work-tree", "--show-toplevel"]).trim().split("\n");
111
+ } catch {
112
+ inside = "false";
113
+ }
114
+ if (inside !== "true" || !topLevel) {
108
115
  complete = false;
109
116
  continue;
110
117
  }
111
- const topLevel = gitOutput(child, ["rev-parse", "--show-toplevel"]).trim();
112
118
  if (fs.realpathSync(topLevel) !== fs.realpathSync(child)) {
113
119
  complete = false;
114
120
  continue;
@@ -143,6 +149,26 @@ export function gitEvidenceRoots(cwd) {
143
149
  return gitEvidenceRootDetails(cwd).roots;
144
150
  }
145
151
 
152
+ // Whether the working tree differs from HEAD, without reading any file: the
153
+ // same answer as a non-empty workingTreeSnapshot, which hashes every changed
154
+ // and loose file and blocked the terminal for seconds on each prompt in a
155
+ // folder of repositories. Unknown counts as changed, as it does there.
156
+ export function workingTreeHasChanges(cwd, details = gitEvidenceRootDetails(cwd)) {
157
+ if (!details.complete) return true;
158
+ for (const root of details.roots) {
159
+ try {
160
+ const status = gitOutput(root.cwd, ["status", "--porcelain", "-z", "--untracked-files=normal",
161
+ "--", ".", ":(exclude).pi/piagent-state"]);
162
+ if (status) return true;
163
+ } catch {
164
+ return true;
165
+ }
166
+ }
167
+ if (details.roots.length === 0 || details.roots.some((root) => root.prefix === "")) return false;
168
+ const loose = nonGitWorkspaceFileDetails(cwd, details.roots.map((root) => root.prefix), 1);
169
+ return loose.files.length > 0 || !loose.complete;
170
+ }
171
+
146
172
  function workspaceEvidenceFileLimit(value) {
147
173
  return Number.isInteger(value)
148
174
  ? Math.max(1, Math.min(WORKSPACE_EVIDENCE_MAX_FILES, value))
@@ -0,0 +1,85 @@
1
+ import net from 'node:net';
2
+
3
+ // Herdr (herdr.dev) keeps panes of agents and resumes each one's session after
4
+ // its server restarts. Its own Pi extension lives in ~/.pi/agent/extensions,
5
+ // which a company conversation never loads, and `piagent studio` starts from a
6
+ // clean environment, so Herdr would see no state and resume nothing. This is
7
+ // the company Terminal's own reporter: the same socket requests (newline JSON,
8
+ // pane.report_agent), with the command that reopens this conversation in
9
+ // `piagent studio` instead of personal Pi.
10
+ const SOURCE = 'custom:piagent-studio', AGENT = 'piagent';
11
+
12
+ // What a Herdr pane gives its processes, read before the environment is
13
+ // cleared; null outside Herdr. Nothing else of it reaches the conversation.
14
+ export function herdrEnvironment(environment = process.env) {
15
+ const socket = environment.HERDR_SOCKET_PATH, pane = environment.HERDR_PANE_ID;
16
+ if (environment.HERDR_ENV !== '1' || !socket || !pane) return null;
17
+ return { socket: process.platform === 'win32' ? `\\\\.\\pipe\\${socket}` : socket, pane };
18
+ }
19
+
20
+ // Herdr types this into the restored pane's shell. It refuses apostrophes and
21
+ // control characters (its POSIX quoting), so such a path is not offered.
22
+ export function herdrResumeArgv(cwd, sessionFile) {
23
+ const argv = ['piagent', 'studio', '--project', cwd, ...(sessionFile ? ['--session', sessionFile] : [])];
24
+ return argv.every(part => typeof part === 'string' && part && !part.includes("'") && !/\p{Cc}/u.test(part)) ? argv : null;
25
+ }
26
+
27
+ // One request, delivered when Herdr answers; a second, longer try when the
28
+ // first is lost. A Herdr that is gone costs the conversation nothing.
29
+ function sendOnce(socketPath, request, timeoutMs) {
30
+ return new Promise(resolve => {
31
+ let done = false;
32
+ const socket = net.createConnection(socketPath);
33
+ const finish = delivered => { if (done) return; done = true; clearTimeout(timer); socket.destroy(); resolve(delivered); };
34
+ const timer = setTimeout(() => finish(false), timeoutMs); timer.unref?.();
35
+ socket.on('error', () => finish(false));
36
+ socket.on('connect', () => socket.write(`${JSON.stringify(request)}\n`));
37
+ socket.on('data', () => finish(true));
38
+ socket.on('end', () => finish(false));
39
+ });
40
+ }
41
+ export async function sendHerdrRequest(socketPath, request) {
42
+ if (!await sendOnce(socketPath, request, 500)) await sendOnce(socketPath, request, 1500);
43
+ }
44
+
45
+ // working while a turn runs, blocked while the conversation waits for the
46
+ // member (Pi's `herdr:blocked` events), idle otherwise. Reports go out one at a
47
+ // time, the newest state winning.
48
+ export function companyHerdrExtension(herdr, { send = sendHerdrRequest } = {}) {
49
+ return (pi) => {
50
+ let seq = Date.now() * 1000, active = false, blocked = 0, label, last, resumeArgv, queued, sending = false;
51
+ const desired = () => blocked > 0 ? { state: 'blocked', message: label } : { state: active ? 'working' : 'idle', message: undefined };
52
+ const drain = async () => {
53
+ if (sending) return;
54
+ sending = true;
55
+ try {
56
+ while (queued) {
57
+ const next = queued; queued = undefined;
58
+ await send(herdr.socket, { id: `${SOURCE}:${next.seq}`, method: 'pane.report_agent',
59
+ params: { pane_id: herdr.pane, source: SOURCE, agent: AGENT, state: next.state, message: next.message, seq: next.seq, ...(resumeArgv ? { resume_argv: resumeArgv } : {}) } });
60
+ }
61
+ } finally { sending = false; if (queued) void drain(); }
62
+ };
63
+ const publish = (force = false) => {
64
+ const next = desired();
65
+ if (!force && next.state === last?.state && next.message === last?.message) return;
66
+ last = next; queued = { ...next, seq: ++seq }; void drain();
67
+ };
68
+ const follow = ctx => {
69
+ let file;
70
+ try { file = ctx?.sessionManager?.getSessionFile?.(); } catch { file = undefined; }
71
+ const argv = herdrResumeArgv(ctx?.cwd, typeof file === 'string' && file ? file : undefined);
72
+ const changed = JSON.stringify(argv) !== JSON.stringify(resumeArgv);
73
+ resumeArgv = argv ?? undefined;
74
+ return changed;
75
+ };
76
+ pi.events?.on?.('herdr:blocked', data => {
77
+ if (data?.active) { blocked += 1; label = data.label; } else { blocked = Math.max(0, blocked - 1); if (!blocked) label = undefined; }
78
+ publish();
79
+ });
80
+ // /new, /resume and /fork start another session: Herdr follows it.
81
+ pi.on('session_start', async (_event, ctx) => { follow(ctx); active = ctx?.isIdle?.() === false; publish(true); });
82
+ pi.on('agent_start', (_event, ctx) => { const changed = follow(ctx); active = true; publish(changed); });
83
+ pi.on('agent_settled', (_event, ctx) => { if (ctx?.isIdle?.() !== true) return; active = false; publish(); });
84
+ };
85
+ }
@@ -3,3 +3,4 @@ export function storedSlot(root: string, slot: string): string | null;
3
3
  export function resolveStore(root: string, slot: string, identity: { origin: unknown; userId: unknown }): string;
4
4
  export function agentWatchDataDirectory(home: string, environment?: Readonly<Record<string, string | undefined>>): string;
5
5
  export function interopEnvironment(environment?: Readonly<Record<string, string | undefined>>): Record<string, string>;
6
+ export function companySessionFile(sessionsDir: string, file: string): string | null;
@@ -74,3 +74,16 @@ export function resolveStore(root, slot, { origin, userId }) {
74
74
  for (const name of new Set([slot, store])) { write(path.join(root, name, 'member'), member); write(path.join(root, name, 'store'), store); }
75
75
  return store;
76
76
  }
77
+
78
+ // A conversation `piagent studio --session` reopens (Herdr's restore names
79
+ // one): only a file of this store's sessions folder. Null when the file was
80
+ // never written (a conversation with no message yet): a new one starts.
81
+ export function companySessionFile(sessionsDir, file) {
82
+ const target = path.resolve(String(file));
83
+ if (!target.endsWith('.jsonl')) throw Error('managed-session-outside-store');
84
+ const real = candidate => { try { return fs.realpathSync(candidate); } catch { return null; } };
85
+ const found = real(target), root = real(sessionsDir) ?? path.resolve(sessionsDir);
86
+ const resolved = found ?? path.join(real(path.dirname(target)) ?? path.dirname(target), path.basename(target));
87
+ if (!resolved.startsWith(root + path.sep)) throw Error('managed-session-outside-store');
88
+ return found && fs.statSync(found).isFile() ? found : null;
89
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/core",
3
- "version": "1.44.3",
3
+ "version": "1.45.0",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -1,3 +1,5 @@
1
+ import path from "node:path";
2
+
1
3
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
4
 
3
5
  import { classifyContextTask } from "../../extensions/context-engine.js";
@@ -5,6 +7,7 @@ import { matchesProtectedPath } from "../../extensions/policy-core.js";
5
7
  import type { TaskContract } from "../../extensions/guard-types.js";
6
8
  import { attachLocalImagesFromText, extractLocalImagePathCandidates } from "../input/chat-images.ts";
7
9
  import type { ChatImageAccessPolicy } from "../input/chat-images.ts";
10
+ import { reviewFileArguments } from "../input/file-arguments.ts";
8
11
  import type { AuthorityResumeDecision } from "../policy/authority-resume-policy.ts";
9
12
  import { registerAdaptiveContextGovernor } from "../session/adaptive-context-governor.ts";
10
13
  import { toolGroupsForPrompt, PIAGENT_TOOL_NAMES, PIAGENT_TOOL_ORDER, PIAGENT_TOOL_GROUPS } from "../tools/tool-groups.ts";
@@ -74,10 +77,21 @@ export function registerInputHook(pi: ExtensionAPI, dependencies: InputHookDepen
74
77
  .filter((group) => !["intake", "task", "recovery"].includes(group)));
75
78
  dependencies.telemetry(ctx, { event: "user_input", turnId: turn.turnId, source: event.source,
76
79
  promptHash: signal.promptHash, promptChars: text.length, inputMode: "freeform" });
77
- const attachment = attachLocalImagesFromText(text, event.images, ctx.cwd, () => dependencies.imageAccess(ctx));
80
+ const reviewed = reviewFileArguments(text, event.images, ctx.cwd, () => dependencies.imageAccess(ctx));
81
+ if (reviewed && !reviewed.text) {
82
+ ctx.ui.notify("Not sent: a file changed while Pi was reading the @file arguments. Run the command again.", "error");
83
+ return { action: "handled" };
84
+ }
85
+ if (reviewed) ctx.ui.notify(`Withheld ${reviewed.withheld.map((item) => `${path.basename(item.path)} (${item.reason})`).join(", ")}`, "warning");
86
+ const nextText = reviewed?.text ?? text;
87
+ const nextImages = reviewed ? reviewed.images as typeof event.images : event.images;
88
+ const attachment = attachLocalImagesFromText(nextText, nextImages, ctx.cwd, () => dependencies.imageAccess(ctx));
89
+ if (attachment?.skipped.length) {
90
+ ctx.ui.notify(`Skipped ${attachment.skipped.map((item) => `${path.basename(item.path)} (${item.reason})`).join(", ")}`, "warning");
91
+ }
78
92
  if (attachment?.attached.length) return { action: "transform", text: attachment.text,
79
- images: [...(event.images ?? []), ...attachment.images] };
80
- if (attachment?.skipped.length) ctx.ui.notify(`Skipped ${attachment.skipped.length} inaccessible image(s)`, "warning");
93
+ images: [...(nextImages ?? []), ...attachment.images] };
94
+ if (reviewed) return { action: "transform", text: nextText, images: nextImages };
81
95
  return { action: "continue" };
82
96
  });
83
97
  }