@piagent/platform 1.46.2 → 1.46.4

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 (63) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/docs/command-reference-vietnamese.md +2 -2
  3. package/docs/distribution-standard.md +6 -6
  4. package/docs/operator-manual-vietnamese.md +7 -7
  5. package/docs/publishing-for-teams.md +2 -0
  6. package/docs/release-install-policy.md +2 -2
  7. package/docs/team-onboarding.md +2 -2
  8. package/docs-site/architecture.html +1 -1
  9. package/docs-site/assets/search-en.json +1 -1
  10. package/docs-site/assets/search-vi.json +1 -1
  11. package/docs-site/benchmark.html +1 -1
  12. package/docs-site/commands.html +1 -1
  13. package/docs-site/context-engine.html +1 -1
  14. package/docs-site/ecosystem.html +1 -1
  15. package/docs-site/en/architecture.html +1 -1
  16. package/docs-site/en/benchmark.html +1 -1
  17. package/docs-site/en/commands.html +1 -1
  18. package/docs-site/en/context-engine.html +1 -1
  19. package/docs-site/en/ecosystem.html +1 -1
  20. package/docs-site/en/index.html +9 -9
  21. package/docs-site/en/mcp-approval.html +1 -1
  22. package/docs-site/en/mcp-auth.html +1 -1
  23. package/docs-site/en/mcp-servers.html +1 -1
  24. package/docs-site/en/mcp.html +1 -1
  25. package/docs-site/en/permissions.html +1 -1
  26. package/docs-site/en/profiles.html +1 -1
  27. package/docs-site/en/quickstart.html +1 -1
  28. package/docs-site/en/runtime.html +1 -1
  29. package/docs-site/en/scope.html +1 -1
  30. package/docs-site/en/security.html +1 -1
  31. package/docs-site/en/team.html +1 -1
  32. package/docs-site/en/update.html +3 -3
  33. package/docs-site/en/whats-new.html +53 -33
  34. package/docs-site/en/windows.html +1 -1
  35. package/docs-site/en/workflows.html +1 -1
  36. package/docs-site/index.html +9 -9
  37. package/docs-site/mcp-approval.html +1 -1
  38. package/docs-site/mcp-auth.html +1 -1
  39. package/docs-site/mcp-servers.html +1 -1
  40. package/docs-site/mcp.html +1 -1
  41. package/docs-site/permissions.html +1 -1
  42. package/docs-site/profiles.html +1 -1
  43. package/docs-site/quickstart.html +1 -1
  44. package/docs-site/releases.json +62 -0
  45. package/docs-site/runtime.html +1 -1
  46. package/docs-site/scope.html +1 -1
  47. package/docs-site/security.html +1 -1
  48. package/docs-site/team.html +1 -1
  49. package/docs-site/update.html +3 -3
  50. package/docs-site/whats-new.html +53 -33
  51. package/docs-site/windows.html +1 -1
  52. package/docs-site/workflows.html +1 -1
  53. package/evals/private-holdout-v1/public-exposure.v2.json +1 -1
  54. package/package.json +1 -1
  55. package/packages/piagent-core/managed/copies.mjs +14 -4
  56. package/packages/piagent-core/managed/session.mjs +3 -3
  57. package/packages/piagent-core/package.json +1 -1
  58. package/packages/piagent-core/runtime/managed-failure.mjs +0 -1
  59. package/scripts/claude-model-additions.mjs +8 -1
  60. package/scripts/configure-model-scope.sh +6 -1
  61. package/scripts/install-global.sh +10 -4
  62. package/scripts/personal-pi-config.mjs +5 -2
  63. package/scripts/pi-models-json.mjs +13 -0
@@ -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.46.2" title="v1.46.2 docs · VI"><span class="dot"></span>v1.46.2</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.46.4" title="v1.46.4 docs · VI"><span class="dot"></span>v1.46.4</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,31 @@
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-46-2" data-release>
111
+ <article class="release latest" id="v1-46-4" data-release>
112
+ <div class="release-meta"><a class="release-version" href="#v1-46-4">v1.46.4</a><time class="release-date" datetime="2026-10-29">29/10/2026</time></div>
113
+ <div class="release-body">
114
+ <h3 data-toc-skip id="v1-46-4-title">Một subagent thay vì bị chặn; bản cập nhật đọc được models.json có comment</h3>
115
+ <p>Session công ty không còn lặp lại lệnh gọi subagent bị chặn, và bản cập nhật không còn dừng khi file danh sách model của Pi có comment.</p>
116
+ <ul class="release-items">
117
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Lỗi khi cấu hình danh sách model không còn làm dừng cả bản cài: bản cài cảnh báo, nêu tên file và lệnh để chạy lại.</span></li>
118
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Agent xin 2–3 subagent mà member không yêu cầu từng bị chặn, và một số model gọi lại liên tục đến khi member phải dừng. Giờ một subagent chạy, và agent được báo lý do.</span></li>
119
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>piagent-update lỗi khi models.json có comment //, dù Pi cho phép. Giờ file được đọc như Pi đọc, và file có comment được giữ nguyên.</span></li>
120
+ </ul>
121
+ </div>
122
+ </article>
123
+ <article class="release" id="v1-46-3" data-release>
124
+ <div class="release-meta"><a class="release-version" href="#v1-46-3">v1.46.3</a><time class="release-date" datetime="2026-10-29">29/10/2026</time></div>
125
+ <div class="release-body">
126
+ <h3 data-toc-skip id="v1-46-3-title">Một subagent thay vì bị chặn; bản cập nhật đọc được models.json có comment <span class="tag">không phát hành</span></h3>
127
+ <p>Đã tag nhưng chưa publish: tiêu đề bản phát hành dài quá giới hạn của trang nên kiểm tra release fail. Thay bằng 1.46.4.</p>
128
+ <ul class="release-items">
129
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Lỗi khi cấu hình danh sách model không còn làm dừng cả bản cài: bản cài cảnh báo, nêu tên file và lệnh để chạy lại.</span></li>
130
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Agent xin 2–3 subagent mà member không yêu cầu từng bị chặn, và một số model gọi lại liên tục đến khi member phải dừng. Giờ một subagent chạy, và agent được báo lý do.</span></li>
131
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>piagent-update lỗi khi models.json có comment //, dù Pi cho phép. Giờ file được đọc như Pi đọc, và file có comment được giữ nguyên.</span></li>
132
+ </ul>
133
+ </div>
134
+ </article>
135
+ <article class="release" id="v1-46-2" data-release>
112
136
  <div class="release-meta"><a class="release-version" href="#v1-46-2">v1.46.2</a><time class="release-date" datetime="2026-10-29">29/10/2026</time></div>
113
137
  <div class="release-body">
114
138
  <h3 data-toc-skip id="v1-46-2-title">Tin gửi sau câu trả lời hiện ngay; WebUI hiện chữ dần</h3>
@@ -395,42 +419,38 @@
395
419
  </ul>
396
420
  </div>
397
421
  </article>
398
- <article class="release" id="v1-40-0" data-release>
399
- <div class="release-meta"><a class="release-version" href="#v1-40-0">v1.40.0</a><time class="release-date" datetime="2026-10-24">24/10/2026</time></div>
400
- <div class="release-body">
401
- <h3 data-toc-skip id="v1-40-0-title">Project local cũng được báo cho Studio</h3>
402
- <p>Cuộc trò chuyện công ty trong thư mục không có remote báo project local cho Studio, mọi lượt chạy đều báo, và member thấy mọi loại cảnh báo project.</p>
403
- <ul class="release-items">
404
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Thư mục không có remote đọc được được báo như project local, theo commit gốc của Git hoặc theo mã băm; đường dẫn không bao giờ rời khỏi máy.</span></li>
405
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Key local của project Git giống nhau trên mọi máy có cùng lịch sử, kể cả bản clone và checkout không có remote <code>origin</code>.</span></li>
406
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Mọi lượt chạy đều báo project, không chỉ lượt đầu của cuộc trò chuyện, nên Studio đếm được từng lần dùng và biết máy nào chưa bao giờ báo.</span></li>
407
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Member thấy một dòng cho mọi cảnh báo: project của team khác, ngoài danh sách của team, chưa đăng ký cho team, hoặc thư mục không đọc được.</span></li>
408
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Thư mục chứa nhiều checkout báo từng checkout, tối đa sáu, và project local được gọi theo tên thư mục.</span></li>
409
- <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Ba cảnh báo bảo mật của dependency trong Pi host được chấp nhận tới 2026-11-07: từ chối dịch vụ và lỗi Pi không dùng tới, không lộ dữ liệu.</span></li>
410
- </ul>
411
- <p class="release-requires"><strong>Cần:</strong> Agent Studio 0.40.0</p>
412
- </div>
413
- </article>
414
- <article class="release" id="v1-39-0" data-release>
415
- <div class="release-meta"><a class="release-version" href="#v1-39-0">v1.39.0</a><time class="release-date" datetime="2026-10-23">23/10/2026</time></div>
416
- <div class="release-body">
417
- <h3 data-toc-skip id="v1-39-0-title">Cảnh báo khi key của team được dùng ngoài team</h3>
418
- <p>Studio biết cuộc trò chuyện công ty làm trong repository nào và cảnh báo khi key của team bị dùng ngoài team; dashboard giữ ngôn ngữ và theme màu.</p>
419
- <ul class="release-items">
420
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Cuộc trò chuyện công ty báo cho Studio các repository đang làm, dùng cùng key <code>host/path</code> mà team memory đã gửi.</span></li>
421
- <li data-kind="new"><span class="kind kind-new">Mới</span><span>Admin thấy cảnh báo trong Logs → &quot;Sai project&quot;; member thấy một dòng nhắc phía trên ô soạn tin và trong <code>piagent studio</code>.</span></li>
422
- <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Không có gì bị chặn, và team memory chạy như cũ.</span></li>
423
- <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Dashboard trước đây quay về ngôn ngữ và theme màu mặc định sau mỗi lần khởi động lại; giờ nó giữ cả hai.</span></li>
424
- </ul>
425
- <p class="release-requires"><strong>Cần:</strong> Agent Studio 0.39.0</p>
426
- </div>
427
- </article>
428
422
  </section>
429
423
  <section class="section archive">
430
424
  <h2 id="archive">Các bản trước</h2>
431
425
  <p class="section-lead">Bản cũ hơn, gom theo tháng. Mở một tháng để xem từng thay đổi.</p>
432
426
  <details class="release-archive">
433
- <summary><span>Tháng 10/2026</span><span class="archive-count">59</span></summary>
427
+ <summary><span>Tháng 10/2026</span><span class="archive-count">61</span></summary>
428
+ <div class="archive-row" id="v1-40-0">
429
+ <div class="archive-head"><a class="release-version" href="#v1-40-0">v1.40.0</a> <span class="release-date">24/10/2026</span></div>
430
+ <div class="archive-body">
431
+ <p><strong>Project local cũng được báo cho Studio</strong> — Cuộc trò chuyện công ty trong thư mục không có remote báo project local cho Studio, mọi lượt chạy đều báo, và member thấy mọi loại cảnh báo project.</p>
432
+ <ul class="release-items">
433
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Thư mục không có remote đọc được được báo như project local, theo commit gốc của Git hoặc theo mã băm; đường dẫn không bao giờ rời khỏi máy.</span></li>
434
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Key local của project Git giống nhau trên mọi máy có cùng lịch sử, kể cả bản clone và checkout không có remote <code>origin</code>.</span></li>
435
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Mọi lượt chạy đều báo project, không chỉ lượt đầu của cuộc trò chuyện, nên Studio đếm được từng lần dùng và biết máy nào chưa bao giờ báo.</span></li>
436
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Member thấy một dòng cho mọi cảnh báo: project của team khác, ngoài danh sách của team, chưa đăng ký cho team, hoặc thư mục không đọc được.</span></li>
437
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Thư mục chứa nhiều checkout báo từng checkout, tối đa sáu, và project local được gọi theo tên thư mục.</span></li>
438
+ <li data-kind="security"><span class="kind kind-security">Bảo mật</span><span>Ba cảnh báo bảo mật của dependency trong Pi host được chấp nhận tới 2026-11-07: từ chối dịch vụ và lỗi Pi không dùng tới, không lộ dữ liệu.</span></li>
439
+ </ul>
440
+ </div>
441
+ </div>
442
+ <div class="archive-row" id="v1-39-0">
443
+ <div class="archive-head"><a class="release-version" href="#v1-39-0">v1.39.0</a> <span class="release-date">23/10/2026</span></div>
444
+ <div class="archive-body">
445
+ <p><strong>Cảnh báo khi key của team được dùng ngoài team</strong> — Studio biết cuộc trò chuyện công ty làm trong repository nào và cảnh báo khi key của team bị dùng ngoài team; dashboard giữ ngôn ngữ và theme màu.</p>
446
+ <ul class="release-items">
447
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Cuộc trò chuyện công ty báo cho Studio các repository đang làm, dùng cùng key <code>host/path</code> mà team memory đã gửi.</span></li>
448
+ <li data-kind="new"><span class="kind kind-new">Mới</span><span>Admin thấy cảnh báo trong Logs → &quot;Sai project&quot;; member thấy một dòng nhắc phía trên ô soạn tin và trong <code>piagent studio</code>.</span></li>
449
+ <li data-kind="improved"><span class="kind kind-improved">Cải thiện</span><span>Không có gì bị chặn, và team memory chạy như cũ.</span></li>
450
+ <li data-kind="fixed"><span class="kind kind-fixed">Sửa lỗi</span><span>Dashboard trước đây quay về ngôn ngữ và theme màu mặc định sau mỗi lần khởi động lại; giờ nó giữ cả hai.</span></li>
451
+ </ul>
452
+ </div>
453
+ </div>
434
454
  <div class="archive-row" id="v1-38-2">
435
455
  <div class="archive-head"><a class="release-version" href="#v1-38-2">v1.38.2</a> <span class="release-date">23/10/2026</span></div>
436
456
  <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.46.2" title="v1.46.2 docs · VI"><span class="dot"></span>v1.46.2</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.46.4" title="v1.46.4 docs · VI"><span class="dot"></span>v1.46.4</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.46.2" title="v1.46.2 docs · VI"><span class="dot"></span>v1.46.2</a>
40
+ <a class="version-badge" href="/whats-new" data-docs-version="v1.46.4" title="v1.46.4 docs · VI"><span class="dot"></span>v1.46.4</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>
@@ -257,7 +257,7 @@
257
257
  {
258
258
  "path": "tests",
259
259
  "fileCount": 636,
260
- "sha256": "2684ca3bdb704a5c2ea6764048dbed440ec0a8000fdbe7b1309cd42c099a8af6"
260
+ "sha256": "7d8a3af48dd9179f3bf3921cae65d3e6d18d17ff87d3454c29247f8cf1b34b63"
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.46.2",
3
+ "version": "1.46.4",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -20,17 +20,27 @@ export function namedCount(words) {
20
20
  }
21
21
  // The copies a delegate call asks for, checked against the member's words;
22
22
  // null for a plain call. `split`: one line per copy naming its share of the work.
23
+ // Copies the member did not ask for come back as `{ dropped }`: one helper runs
24
+ // and the main agent is told why. A refusal here sent agents round in retries,
25
+ // each quoting other words, until the member stopped the turn.
23
26
  export function fanOut(manager, role, args, harness) {
24
27
  const split = Array.isArray(args?.split) ? args.split.map(s => typeof s === 'string' ? s.trim().slice(0, 500) : '').filter(Boolean) : [];
25
28
  const n = split.length || (Number.isInteger(args?.copies) ? args.copies : 1);
26
- if (n <= 1 && !args?.member_words) return null;
29
+ if (n <= 1) return null;
27
30
  if (harness) throw Error('managed-copies-invalid: the harness runs one helper per call.');
28
- if (n < 2 || n > MAX_COPIES) throw Error(`managed-copies-invalid: ask for 2 to ${MAX_COPIES} ${role} helpers (copies, or one split line per helper).`);
29
31
  const words = normal(args?.member_words);
30
- if (words.length < 3 || !memberRequest(memberMessages(manager)).some(text => normal(text).includes(words)) || namedCount(words) < n || !HELPER_WORDS.test(words))
31
- throw Error(`managed-copies-not-asked: more than one ${role} helper runs only when the member asks for it. Quote in member_words the member's own words that ask for ${n} (verbatim, with the number); otherwise use one helper.`);
32
+ if (words.length < 3 || !memberRequest(memberMessages(manager)).some(text => normal(text).includes(words)) || namedCount(words) < n || !HELPER_WORDS.test(words)) return { dropped: n };
33
+ if (n > MAX_COPIES) throw Error(`managed-copies-invalid: ask for 2 to ${MAX_COPIES} ${role} helpers (copies, or one split line per helper).`);
32
34
  return { n, split: split.length ? split : null };
33
35
  }
36
+ // The helper's answer, with what the main agent reads above it when it asked
37
+ // for helpers the member did not (`asked`: what fanOut returned).
38
+ export function withDroppedNote(result, role, asked) {
39
+ const n = asked?.dropped, first = result?.content?.[0];
40
+ if (!n || first?.type !== 'text') return result;
41
+ const note = `One ${role} helper ran, not ${n}: the member's own words did not ask for ${n} ${role} helpers. Go on with this answer; run several only when the member asks for that many.\n\n`;
42
+ return { ...result, content: [{ ...first, text: note + first.text }, ...result.content.slice(1)] };
43
+ }
34
44
  // What one copy reads under its brief.
35
45
  export function copyNote(role, i, fan) {
36
46
  return `\n\nThe member asked for ${fan.n} ${role} helpers; you are ${role} helper ${i + 1} of ${fan.n}. ${fan.split ? `Your share of the work: ${fan.split[i]}. The others cover the rest.` : 'The others have the same brief: answer on your own judgement.'}`;
@@ -24,7 +24,7 @@ import { readPatchDigest, readPatchSnapshot, readChangedFiles, readHead } from '
24
24
  import { memberTurn } from './continuation.mjs';
25
25
  import { workflowPolicy, repositoryChecks, sessionPlanTool, currentPlan, workflowPrompt, RunProcess, completionGate, pendingBaseline, pendingPaths, pendingHead, processEditTools } from './workflow.mjs';
26
26
  import { workflowContextTool, workflowContextExtension } from './workflow-context.mjs';
27
- import { beforeDelegate, resolveTool } from './objections.mjs'; import { fanOut } from './copies.mjs';
27
+ import { beforeDelegate, resolveTool } from './objections.mjs'; import { fanOut, withDroppedNote } from './copies.mjs';
28
28
  import { HELPER_CALLS, HELPER_ROLES, helperRoles, helperPrompt, webPrompt, delegateDescription, mainWebLookups, limitedWebTools } from './helper-roles.mjs'; import { delegateParameters, delegateArgs, SPEC_PROMPT } from './helper-settlement.mjs';
29
29
  import { loadAgentResources } from './agent-skills.mjs';
30
30
  import { loadPiagentThemes } from '../runtime/terminal/terminal-style.mjs';
@@ -469,7 +469,7 @@ export class ManagedSession {
469
469
  if (!enabled.includes(role)) throw Error(`managed-helper-not-configured: the company Harness has no ${role} subagent${enabled.length ? `; use ${enabled.join(', ')}` : ''}.`);
470
470
  // Studio lets each helper role run HELPER_CALLS times per run (one user
471
471
  // message), each helper the member asked for counted (copies.mjs). Say so at once.
472
- const fan = fanOut(this.session?.sessionManager, role, more, harness);
472
+ const asked = fanOut(this.session?.sessionManager, role, more, harness), fan = asked?.dropped ? null : asked;
473
473
  if ((this.helperCalls.get(role) ?? 0) + (fan?.n ?? 1) > HELPER_CALLS) throw Error(`managed-helper-limit: the ${role} subagent already ran ${HELPER_CALLS} times for this user message. Go on with what it returned, or use it again after the next user message.`);
474
474
  beforeDelegate(this.run, role, harness, { since, review: this.review, changed: (this.ownPaths?.size ?? 0) > 0, asked: !!fan });
475
475
  const job = this.runHelper(role, task, signal, harness, title, role === 'review' && typeof since === 'string' && since.trim() ? since.trim() : null, fan); this.helpers.set(role, job); this.publishHelpers();
@@ -477,7 +477,7 @@ export class ManagedSession {
477
477
  // quiet: a beat each minute tells the dashboard's watchdog, which stops a
478
478
  // turn after 15 minutes without an event, that the turn is working.
479
479
  const beat = setInterval(() => this.notify({ type: 'managed_helper_progress', role }), this.helperBeatMs ?? HELPER_BEAT_MS); beat.unref?.();
480
- try { return await job; } finally { clearInterval(beat); this.helpers.delete(role); this.publishHelpers(); }
480
+ try { return withDroppedNote(await job, role, asked); } finally { clearInterval(beat); this.helpers.delete(role); this.publishHelpers(); }
481
481
  }
482
482
  publishHelpers() {
483
483
  // One of each enabled helper role can run at a time.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/core",
3
- "version": "1.46.2",
3
+ "version": "1.46.4",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -96,7 +96,6 @@ const CODES = {
96
96
  'managed-review-by-harness': ['tool', 'the harness reviews the final patch; the agent does not ask for one'],
97
97
  'managed-review-once': ['tool', 'this change was reviewed already; ask again only after fixing blocking findings'],
98
98
  'managed-copies-invalid': ['tool', 'several helpers of a role run 2 or 3 at a time, at the member\'s request'],
99
- 'managed-copies-not-asked': ['tool', 'more than one helper of a role runs only when the member asks for it'],
100
99
  helper_run_limit_reached: ['tool', 'this subagent already ran 8 times for this message'],
101
100
  'managed-helper-cancelled': ['tool', 'the subagent was stopped'],
102
101
  'managed-helper-failed': ['failed', 'the subagent ended without an answer'],
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { parsePiModelsJson } from "./pi-models-json.mjs";
3
4
 
4
5
  const additions = JSON.parse(fs.readFileSync(new URL("../catalog/claude-model-additions.json", import.meta.url), "utf8"));
5
6
  const object = value => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -9,7 +10,11 @@ const object = value => value !== null && typeof value === "object" && !Array.is
9
10
  export function prepareClaudeModelAdditions(file) {
10
11
  if (fs.existsSync(file) && (!fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink())) throw new Error("Unsafe model configuration path");
11
12
  const before = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : null;
12
- const data = before === null ? {} : JSON.parse(before);
13
+ let data = {}, strict = true;
14
+ if (before !== null) {
15
+ try { ({ data, strict } = parsePiModelsJson(before)); }
16
+ catch (error) { throw new Error(`Cannot read model configuration ${file}: ${error.message}`); }
17
+ }
13
18
  if (!object(data) || (data.providers !== undefined && !object(data.providers))) throw new Error("Unsupported model configuration");
14
19
  data.providers ??= {};
15
20
  const provider = data.providers.anthropic ?? {};
@@ -19,6 +24,8 @@ export function prepareClaudeModelAdditions(file) {
19
24
  const models = provider.models ?? [];
20
25
  const missing = additions.models.filter(model => !models.some(existing => existing.id === model.id));
21
26
  if (missing.length === 0) return null;
27
+ // Rewriting would drop the member's comments; leave the file to them.
28
+ if (!strict) return { skipped: `${file} has comments; not adding ${missing.map(model => model.id).join(", ")} (add them by hand if wanted)`, apply() {} };
22
29
  data.providers.anthropic = { ...provider, models: [...models, ...missing] };
23
30
  return { apply() {
24
31
  const current = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : null;
@@ -168,7 +168,11 @@ function parseDefaultModel(input) {
168
168
 
169
169
  const parsedDefault = parseDefaultModel(defaultModelInput);
170
170
  const settingsBefore = fs.existsSync(settingsPath) ? fs.readFileSync(settingsPath, "utf8") : null;
171
- const settings = settingsBefore === null ? {} : JSON.parse(settingsBefore);
171
+ let settings = {};
172
+ if (settingsBefore !== null) {
173
+ try { settings = JSON.parse(settingsBefore); }
174
+ catch (error) { console.error(`FAIL: cannot read Pi settings ${settingsPath}: ${error.message}`); process.exit(1); }
175
+ }
172
176
 
173
177
  // What Piagent last offered, so an update adds only models new to this
174
178
  // release and never puts back one the member removed.
@@ -213,6 +217,7 @@ if (dryRun) {
213
217
  console.log(` preset: ${preset}`);
214
218
  console.log(` default: ${settings.defaultProvider}/${settings.defaultModel}:${settings.defaultThinkingLevel}`);
215
219
  console.log(` enabledModels: ${settings.enabledModels.length}`);
220
+ if (catalogEdit?.skipped) console.log(` note: ${catalogEdit.skipped}`);
216
221
  for (const note of notes) console.log(` ${note}`);
217
222
  }
218
223
  NODE
@@ -881,10 +881,16 @@ fi
881
881
 
882
882
  if [[ "$CONFIGURE_MODEL_SCOPE" == true ]]; then
883
883
  echo "Configuring Pi model selector scope:"
884
- if [[ "$RESET_MODEL_SCOPE" == true ]]; then
885
- run_cmd bash "$PLATFORM_ROOT/scripts/configure-model-scope.sh" --preset "$MODEL_SCOPE_PRESET" --default-model "$DEFAULT_MODEL"
886
- else
887
- run_cmd bash "$PLATFORM_ROOT/scripts/configure-model-scope.sh" --preset "$MODEL_SCOPE_PRESET" --default-model "$DEFAULT_MODEL" --keep-member-choices
884
+ MODEL_SCOPE_ARGS=(--preset "$MODEL_SCOPE_PRESET" --default-model "$DEFAULT_MODEL")
885
+ if [[ "$RESET_MODEL_SCOPE" != true ]]; then
886
+ MODEL_SCOPE_ARGS+=(--keep-member-choices)
887
+ fi
888
+ # The model list is a convenience: a member's settings or models.json that the
889
+ # script cannot read must not stop the install (Pi and the package are already
890
+ # in place). The script restores settings.json when it fails part way.
891
+ if ! run_cmd bash "$PLATFORM_ROOT/scripts/configure-model-scope.sh" "${MODEL_SCOPE_ARGS[@]}"; then
892
+ echo "WARN: the Pi model selector scope was not changed; the install continues." >&2
893
+ echo " Fix the file named above, then run: piagent-model-scope ${MODEL_SCOPE_ARGS[*]}" >&2
888
894
  fi
889
895
  fi
890
896
 
@@ -15,11 +15,14 @@ import fs from "node:fs";
15
15
  import os from "node:os";
16
16
  import path from "node:path";
17
17
  import { fileURLToPath } from "node:url";
18
+ import { parsePiModelsJson } from "./pi-models-json.mjs";
18
19
 
19
20
  const COMPANY_PROVIDER = /^agent_watch_/;
20
21
  const SESSION_HEADER_EXTENSION = "agentwatch-studio-session.mjs";
21
22
 
22
23
  const readJson = (file) => { try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return null; } };
24
+ // models.json may carry comments, as Pi allows; a strict read would miss company providers in it.
25
+ const readModels = (file) => { try { return parsePiModelsJson(fs.readFileSync(file, "utf8")).data; } catch { return null; } };
23
26
  const real = (file) => { try { return fs.realpathSync(file); } catch { return null; } };
24
27
 
25
28
  // The Piagent package a path belongs to (its root), or null.
@@ -47,7 +50,7 @@ export function packageVersion(agentDir, source) {
47
50
 
48
51
  export function inspect(agentDir) {
49
52
  const settingsFile = path.join(agentDir, "settings.json"), modelsFile = path.join(agentDir, "models.json");
50
- const settings = readJson(settingsFile) ?? {}, models = readJson(modelsFile) ?? {};
53
+ const settings = readJson(settingsFile) ?? {}, models = readModels(modelsFile) ?? {};
51
54
  const managed = fs.existsSync(path.join(agentDir, "agent-watch-managed.json"));
52
55
  const packages = Array.isArray(settings.packages) ? settings.packages : [];
53
56
  const piagentPackages = packages.filter(isPiagentSource);
@@ -109,7 +112,7 @@ export function repair(agentDir, now = new Date()) {
109
112
  backups.push(backup(settingsFile, stamp)); writeJson(settingsFile, settings); changed.push(settingsFile);
110
113
  }
111
114
  if (kinds.has("company-providers")) {
112
- const models = readJson(modelsFile);
115
+ const models = readModels(modelsFile);
113
116
  for (const name of Object.keys(models.providers ?? {})) if (COMPANY_PROVIDER.test(name)) delete models.providers[name];
114
117
  backups.push(backup(modelsFile, stamp)); writeJson(modelsFile, models); changed.push(modelsFile);
115
118
  }
@@ -0,0 +1,13 @@
1
+ // Pi reads models.json leniently: a BOM, `//` line comments and trailing
2
+ // commas are allowed (Pi's stripJsonComments). Piagent reads it the same way,
3
+ // and says whether the text was plain JSON, so a writer knows it would drop
4
+ // the member's comments.
5
+ const stripComments = text => text
6
+ .replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, m => (m[0] === '"' ? m : ""))
7
+ .replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (m, tail) => tail ?? (m[0] === '"' ? m : ""));
8
+
9
+ export function parsePiModelsJson(text) {
10
+ const body = text.replace(/^\uFEFF/, "");
11
+ try { return { data: JSON.parse(body), strict: true }; }
12
+ catch { return { data: JSON.parse(stripComments(body)), strict: false }; }
13
+ }