@piagent/platform 1.41.0 → 1.42.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 (107) hide show
  1. package/CHANGELOG.md +1006 -2038
  2. package/CONTRIBUTING.md +2 -0
  3. package/README.md +128 -325
  4. package/README.vi.md +149 -143
  5. package/docs/command-reference-vietnamese.md +2 -2
  6. package/docs/distribution-standard.md +6 -6
  7. package/docs/languages.json +2 -2
  8. package/docs/operator-manual-vietnamese.md +7 -7
  9. package/docs/publishing-for-teams.md +1 -0
  10. package/docs/release-install-policy.md +3 -3
  11. package/docs/team-onboarding.md +2 -2
  12. package/docs-site/architecture.html +71 -63
  13. package/docs-site/assets/docs.css +2736 -1030
  14. package/docs-site/assets/docs.js +451 -43
  15. package/docs-site/assets/og.png +0 -0
  16. package/docs-site/assets/piagent-logo.svg +9 -31
  17. package/docs-site/assets/search-en.json +1 -0
  18. package/docs-site/assets/search-vi.json +1 -0
  19. package/docs-site/benchmark.html +71 -63
  20. package/docs-site/commands.html +72 -64
  21. package/docs-site/content/en/index.html +220 -131
  22. package/docs-site/content/en/quickstart.html +74 -90
  23. package/docs-site/content/en/update.html +80 -0
  24. package/docs-site/content/en/whats-new.html +1 -684
  25. package/docs-site/content/index.html +220 -131
  26. package/docs-site/content/quickstart.html +73 -108
  27. package/docs-site/content/update.html +80 -0
  28. package/docs-site/content/whats-new.html +1 -684
  29. package/docs-site/context-engine.html +73 -65
  30. package/docs-site/ecosystem.html +73 -65
  31. package/docs-site/en/architecture.html +67 -59
  32. package/docs-site/en/benchmark.html +67 -59
  33. package/docs-site/en/commands.html +68 -60
  34. package/docs-site/en/context-engine.html +68 -60
  35. package/docs-site/en/ecosystem.html +68 -60
  36. package/docs-site/en/index.html +281 -220
  37. package/docs-site/en/mcp-approval.html +67 -59
  38. package/docs-site/en/mcp-auth.html +67 -59
  39. package/docs-site/en/mcp-servers.html +68 -60
  40. package/docs-site/en/mcp.html +64 -56
  41. package/docs-site/en/permissions.html +68 -60
  42. package/docs-site/en/profiles.html +68 -60
  43. package/docs-site/en/quickstart.html +142 -150
  44. package/docs-site/en/runtime.html +67 -59
  45. package/docs-site/en/scope.html +68 -60
  46. package/docs-site/en/security.html +64 -56
  47. package/docs-site/en/team.html +67 -59
  48. package/docs-site/en/update.html +228 -0
  49. package/docs-site/en/whats-new.html +1601 -725
  50. package/docs-site/en/windows.html +71 -63
  51. package/docs-site/en/workflows.html +68 -60
  52. package/docs-site/favicon.svg +9 -14
  53. package/docs-site/index.html +281 -220
  54. package/docs-site/mcp-approval.html +70 -62
  55. package/docs-site/mcp-auth.html +70 -62
  56. package/docs-site/mcp-servers.html +71 -63
  57. package/docs-site/mcp.html +70 -62
  58. package/docs-site/permissions.html +72 -64
  59. package/docs-site/profiles.html +72 -64
  60. package/docs-site/quickstart.html +145 -172
  61. package/docs-site/releases.json +4449 -0
  62. package/docs-site/runtime.html +73 -65
  63. package/docs-site/scope.html +73 -65
  64. package/docs-site/security.html +73 -65
  65. package/docs-site/team.html +71 -63
  66. package/docs-site/update.html +228 -0
  67. package/docs-site/whats-new.html +1605 -729
  68. package/docs-site/windows.html +75 -67
  69. package/docs-site/workflows.html +73 -65
  70. package/evals/private-holdout-v1/public-exposure.v2.json +1 -1
  71. package/package.json +1 -1
  72. package/packages/piagent-core/package.json +1 -1
  73. package/packages/piagent-webui/dist/client/assets/App-DBx4BU3F.js +5 -0
  74. package/packages/piagent-webui/dist/client/assets/LineReview-DzonwD-g.js +3 -0
  75. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-DeLo-xiz.js → MarkdownMessage-DchODuO7.js} +2 -2
  76. package/packages/piagent-webui/dist/client/assets/SessionInspectorDrawer-BHHNlct3.js +1 -0
  77. package/packages/piagent-webui/dist/client/assets/SettingsPage-DX28MsZA.js +1 -0
  78. package/packages/piagent-webui/dist/client/assets/TerminalPanel-C--BiN0p.js +2 -0
  79. package/packages/piagent-webui/dist/client/assets/{TerminalView-r9rG24IO.js → TerminalView-BML7uS6R.js} +3 -3
  80. package/packages/piagent-webui/dist/client/assets/WorkspaceCodeEditor-B7ieqDnt.js +2 -0
  81. package/packages/piagent-webui/dist/client/assets/WorkspaceExplorer-BOafuPU-.js +5 -0
  82. package/packages/piagent-webui/dist/client/assets/WorkspaceQuickOpen-CGoGfUPk.js +1 -0
  83. package/packages/piagent-webui/dist/client/assets/api-BUjrtT1B.js +1 -0
  84. package/packages/piagent-webui/dist/client/assets/index-C7Azrwpq.js +132 -0
  85. package/packages/piagent-webui/dist/client/assets/index-Dqb2hNan.css +1 -0
  86. package/packages/piagent-webui/dist/client/assets/tab-close-Crj8vsUq.js +1 -0
  87. package/packages/piagent-webui/dist/client/assets/ui-preferences-B3UfDp-5.js +1 -0
  88. package/packages/piagent-webui/dist/client/assets/vendor-Ch5hIe0W.js +136 -0
  89. package/packages/piagent-webui/dist/client/index.html +5 -8
  90. package/scripts/build-docs-site.mjs +205 -348
  91. package/scripts/check-public-wording.mjs +2 -1
  92. package/scripts/docs-site-icons.mjs +84 -0
  93. package/scripts/docs-site-nav.mjs +302 -0
  94. package/scripts/docs-site-releases.mjs +227 -0
  95. package/packages/piagent-webui/dist/client/assets/Alert-DGWhP2MM.js +0 -1
  96. package/packages/piagent-webui/dist/client/assets/Button-QItfZUNj.js +0 -1
  97. package/packages/piagent-webui/dist/client/assets/DialogTitle-CyaFPaiW.js +0 -3
  98. package/packages/piagent-webui/dist/client/assets/TerminalPanel-BG-DP_qm.js +0 -2
  99. package/packages/piagent-webui/dist/client/assets/Typography-B9om7Evq.js +0 -1
  100. package/packages/piagent-webui/dist/client/assets/WorkspaceCodeEditor-BnqaR3pK.js +0 -2
  101. package/packages/piagent-webui/dist/client/assets/WorkspaceExplorer-BIxwiGtl.js +0 -5
  102. package/packages/piagent-webui/dist/client/assets/WorkspaceQuickOpen-CK3dILhQ.js +0 -1
  103. package/packages/piagent-webui/dist/client/assets/index-DBUTrEXi.css +0 -1
  104. package/packages/piagent-webui/dist/client/assets/index-rlyRccut.js +0 -102
  105. package/packages/piagent-webui/dist/client/assets/tab-close-BoJRyBO_.js +0 -1
  106. package/packages/piagent-webui/dist/client/assets/ui-preferences-BO3AV7QE.js +0 -10
  107. package/packages/piagent-webui/dist/client/assets/useId-DEefKUAT.js +0 -124
@@ -4,7 +4,8 @@ import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
6
  const root = path.resolve(import.meta.dirname, "..");
7
- const surfaces = ["README.md", "docs", "docs-site/content", "scripts/build-docs-site.mjs",
7
+ const surfaces = ["README.md", "docs", "docs-site/content", "docs-site/releases.json", "CHANGELOG.md",
8
+ "scripts/build-docs-site.mjs", "scripts/docs-site-nav.mjs", "scripts/docs-site-releases.mjs",
8
9
  "packages/piagent-core/README.md", "packages/piagent-core/prompts", "packages/piagent-core/mcp",
9
10
  "packages/piagent-core/extensions", "packages/piagent-core/runtime", "templates/project/AGENTS.md"];
10
11
  const sourceNames = ["Codex CLI", "Claude CLI", "Claude Code"];
@@ -0,0 +1,84 @@
1
+ // Line icons for the docs site, drawn on a 24px grid with a 1.75px stroke so
2
+ // they sit at the same weight as 14–15px text. Each page carries one inline
3
+ // sprite of the icons it uses and refers to them with <use>, so an icon costs
4
+ // one <symbol> per page rather than a request or a repeated path.
5
+
6
+ const ICONS = {
7
+ home: '<path d="M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6H9v6H4a1 1 0 0 1-1-1z"/>',
8
+ network: '<circle cx="12" cy="5" r="2.5"/><circle cx="5" cy="19" r="2.5"/><circle cx="19" cy="19" r="2.5"/><path d="M12 7.5V12m0 0-5.2 5.2M12 12l5.2 5.2"/>',
9
+ rocket: '<path d="M5.5 15.5C4 17 3.5 19.5 3.5 20.5c1 0 3.5-.5 5-2"/><path d="M9 15 6.5 12.5C8 8.5 11.5 4.5 20 4c-.5 8.5-4.5 12-8.5 13.5z"/><circle cx="14.5" cy="9.5" r="1.5"/>',
10
+ monitor: '<rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 20h8M12 16v4"/>',
11
+ refresh: '<path d="M20 11A8 8 0 0 0 5.3 6.7M4 3v4h4"/><path d="M4 13a8 8 0 0 0 14.7 4.3M20 21v-4h-4"/>',
12
+ sparkles: '<path d="M11 3c.6 4.6 2.4 6.4 7 7-4.6.6-6.4 2.4-7 7-.6-4.6-2.4-6.4-7-7 4.6-.6 6.4-2.4 7-7z"/><path d="M19 15v5M16.5 17.5h5"/>',
13
+ target: '<circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1"/>',
14
+ sliders: '<path d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2"/><circle cx="9" cy="17" r="2"/>',
15
+ key: '<circle cx="8" cy="15" r="4"/><path d="m10.9 12.1 8.6-8.6M16.5 6.5l2.5 2.5M14 9l2 2"/>',
16
+ terminal: '<rect x="3" y="4" width="18" height="16" rx="2.5"/><path d="m7.5 9.5 3 2.5-3 2.5M13 15h4"/>',
17
+ branch: '<circle cx="6" cy="5.5" r="2"/><circle cx="6" cy="18.5" r="2"/><circle cx="18" cy="7.5" r="2"/><path d="M6 7.5v9M18 9.5c0 4.5-5 4-11 7.5"/>',
18
+ plug: '<path d="M9 3v4M15 3v4M6 7h12v4a6 6 0 0 1-12 0zM12 17v4"/>',
19
+ server: '<rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><path d="M7 7.5h.01M7 16.5h.01"/>',
20
+ lock: '<rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>',
21
+ "shield-check": '<path d="M12 3 4.5 6v5.5c0 4.8 3.2 8.2 7.5 9.5 4.3-1.3 7.5-4.7 7.5-9.5V6z"/><path d="m9 12 2.2 2.2L15.5 10"/>',
22
+ shield: '<path d="M12 3 4.5 6v5.5c0 4.8 3.2 8.2 7.5 9.5 4.3-1.3 7.5-4.7 7.5-9.5V6z"/>',
23
+ layers: '<path d="m12 3 9 5-9 5-9-5z"/><path d="m3 13 9 5 9-5"/><path d="m3 17.5 9 5 9-5" opacity=".55"/>',
24
+ cpu: '<rect x="6" y="6" width="12" height="12" rx="2"/><rect x="9.5" y="9.5" width="5" height="5" rx=".8"/><path d="M9 2.5V6M15 2.5V6M9 18v3.5M15 18v3.5M2.5 9H6M2.5 15H6M18 9h3.5M18 15h3.5"/>',
25
+ chart: '<path d="M4 4v16h16"/><path d="M8.5 16v-5M12.5 16V8M16.5 16v-3"/>',
26
+ wrench: '<path d="M15 6.5a4.5 4.5 0 0 1-6 6l-5 5L6.5 20l5-5a4.5 4.5 0 0 0 6-6L15 11.5l-2.5-2.5z"/>',
27
+ users: '<circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0"/><path d="M16 4.5a3.5 3.5 0 0 1 0 7M18 14a6 6 0 0 1 3.5 6"/>',
28
+ search: '<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',
29
+ sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2.5v2M12 19.5v2M4.6 4.6 6 6M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4 6 18M18 6l1.4-1.4"/>',
30
+ moon: '<path d="M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5z"/>',
31
+ menu: '<path d="M4 7h16M4 12h16M4 17h16"/>',
32
+ "arrow-right": '<path d="M5 12h14M13 6l6 6-6 6"/>',
33
+ "arrow-up-right": '<path d="M7 17 17 7M8 7h9v9"/>',
34
+ copy: '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V6a2 2 0 0 1 2-2h9"/>',
35
+ check: '<path d="m5 12 5 5 9-10"/>',
36
+ hash: '<path d="M5 9h15M4 15h15M10 3 8 21M16 3l-2 18"/>',
37
+ book: '<path d="M4 5a2 2 0 0 1 2-2h13v16H6a2 2 0 0 0-2 2z"/><path d="M4 21V5M19 19v2H6"/>',
38
+ edit: '<path d="M4 20h4L19 9l-4-4L4 16z"/><path d="m13.5 6.5 4 4"/>',
39
+ eye: '<path d="M2.5 12S6 5 12 5s9.5 7 9.5 7-3.5 7-9.5 7-9.5-7-9.5-7z"/><circle cx="12" cy="12" r="3"/>',
40
+ message: '<path d="M4 5h16v11H9l-5 4z"/>',
41
+ play: '<path d="M7 4.5v15l12-7.5z"/>',
42
+ "check-circle": '<circle cx="12" cy="12" r="9"/><path d="m8.5 12 2.5 2.5 4.5-5"/>',
43
+ alert: '<path d="M12 3 2.5 20h19z"/><path d="M12 10v4M12 17h.01"/>',
44
+ diff: '<path d="M6 3v12M3 6h6M15 18h6"/><rect x="13" y="3" width="8" height="8" rx="2" opacity=".55"/><rect x="3" y="13" width="8" height="8" rx="2" opacity=".55"/>',
45
+ camera: '<path d="M4 8h3l2-3h6l2 3h3v11H4z"/><circle cx="12" cy="13" r="3.5"/>',
46
+ brain: '<path d="M9 4a3 3 0 0 0-3 3 3 3 0 0 0-2 5 3 3 0 0 0 2 5 3 3 0 0 0 3 3h0a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"/><path d="M15 4a3 3 0 0 1 3 3 3 3 0 0 1 2 5 3 3 0 0 1-2 5 3 3 0 0 1-3 3h0a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/>',
47
+ clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
48
+ code: '<path d="m8 7-5 5 5 5M16 7l5 5-5 5M14 4l-4 16"/>',
49
+ file: '<path d="M6 3h8l5 5v13H6z"/><path d="M14 3v5h5"/>',
50
+ folder: '<path d="M3 6a1 1 0 0 1 1-1h5l2 2h9a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z"/>',
51
+ zap: '<path d="M13 2.5 4.5 13.5H12l-1 8 8.5-11H12z"/>',
52
+ box: '<path d="m12 3 8 4.5v9L12 21l-8-4.5v-9z"/><path d="m4 7.5 8 4.5 8-4.5M12 12v9"/>',
53
+ apple: '<path d="M16 3.5c-1 .3-2.4 1.3-2.6 3 1.2.1 2.4-.8 2.6-3zM12 8c-1.5-1-5-1-6 2.5S6.5 19 9 20c1.2.5 2-.4 3-.4s1.8.9 3 .4c1.4-.6 2.5-2.7 3-4.5-2.5-1-2.8-4.6-.5-6-1.2-1.6-3.3-2-4.5-1.5z"/>',
54
+ windows: '<path d="M3.5 5.5 10.5 4.5v7h-7zM13 4.2 20.5 3v8.5H13zM3.5 13h7v7l-7-1zM13 13h7.5v8.5L13 20.3z"/>',
55
+ linux: '<rect x="3" y="4" width="18" height="16" rx="2.5"/><path d="M7 15.5h4M7 9l3 2-3 2"/>',
56
+ github: '<path d="M9 19c-4.5 1.5-4.5-2.5-6.5-3m13 6v-3.5a3 3 0 0 0-.9-2.4c3-.3 6.4-1.5 6.4-6.7a5.2 5.2 0 0 0-1.4-3.6 4.8 4.8 0 0 0-.1-3.6s-1.1-.3-3.7 1.4a12.6 12.6 0 0 0-6.6 0C6.6 1.9 5.5 2.2 5.5 2.2a4.8 4.8 0 0 0-.1 3.6A5.2 5.2 0 0 0 4 9.4c0 5.2 3.4 6.4 6.4 6.7a3 3 0 0 0-.9 2.4V22"/>',
57
+ x: '<path d="M6 6l12 12M18 6 6 18"/>',
58
+ globe: '<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.5 3.5 5.5 3.5 9s-1 6.5-3.5 9c-2.5-2.5-3.5-5.5-3.5-9s1-6.5 3.5-9z"/>',
59
+ undo: '<path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/>',
60
+ list: '<path d="M9 6h11M9 12h11M9 18h11M4.5 6h.01M4.5 12h.01M4.5 18h.01"/>',
61
+ building: '<rect x="4.5" y="3" width="15" height="18" rx="1.5"/><path d="M9 7h1.5M13.5 7H15M9 11h1.5M13.5 11H15M9 15h1.5M13.5 15H15M10 21v-3h4v3"/>',
62
+ gauge: '<path d="M4 18a8 8 0 1 1 16 0"/><path d="m12 18 4-5"/>'
63
+ };
64
+
65
+ /** @param {string} name @returns {string} */
66
+ function icon(name, className = "icon") {
67
+ if (!ICONS[name]) throw new Error(`unknown docs icon: ${name}`);
68
+ return `<svg class="${className}" viewBox="0 0 24 24" aria-hidden="true"><use href="#icon-${name}"/></svg>`;
69
+ }
70
+
71
+ /** The icon names a rendered page refers to through <use>. */
72
+ function iconsUsedIn(html) {
73
+ return [...new Set([...html.matchAll(/href="#icon-([a-z-]+)"/g)].map((match) => match[1]))].sort();
74
+ }
75
+
76
+ /** One hidden sprite with exactly the icons the page uses. */
77
+ function iconSprite(names) {
78
+ const unknown = names.filter((name) => !ICONS[name]);
79
+ if (unknown.length > 0) throw new Error(`unknown docs icon: ${unknown.join(", ")}`);
80
+ const symbols = names.map((name) => `<symbol id="icon-${name}" viewBox="0 0 24 24">${ICONS[name]}</symbol>`).join("");
81
+ return `<svg width="0" height="0" style="position:absolute" aria-hidden="true">${symbols}</svg>`;
82
+ }
83
+
84
+ export { ICONS, icon, iconSprite, iconsUsedIn };
@@ -0,0 +1,302 @@
1
+ // Page order, titles and leads for the docs site, in both languages.
2
+ //
3
+ // Order here is the order in the sidebar, and it is also the order of the
4
+ // previous/next links at the foot of each page. Leads are one or two short
5
+ // sentences: they become the page subtitle, the meta description and the text
6
+ // the search palette matches.
7
+
8
+ const NAV = [
9
+ {
10
+ group: "Bắt đầu",
11
+ pages: [
12
+ {
13
+ slug: "index",
14
+ href: "/",
15
+ icon: "home",
16
+ nav: "Tổng quan",
17
+ title: "Pi Agent Platform",
18
+ lead: "Coding agent cho cả team: guard kiểm tra mọi tool call, Harness của team tự chạy check và review, một dashboard cho Terminal và trình duyệt."
19
+ },
20
+ {
21
+ slug: "ecosystem",
22
+ icon: "network",
23
+ nav: "Hệ sinh thái",
24
+ title: "Piagent, Agent Watch và Agent Studio",
25
+ lead: "Phần nào chạy trên máy bạn, phần nào ở máy chủ công ty, và khi nào dùng model cá nhân hay model của công ty."
26
+ },
27
+ {
28
+ slug: "quickstart",
29
+ icon: "rocket",
30
+ nav: "Cài đặt",
31
+ title: "Cài đặt và chạy lượt đầu tiên",
32
+ lead: "Hai lệnh để cài. Mở repo, chọn profile và quyền, rồi nói việc bằng lời thường."
33
+ },
34
+ {
35
+ slug: "windows",
36
+ icon: "windows",
37
+ nav: "Cài trên Windows",
38
+ title: "Cài trên Windows bằng một lệnh",
39
+ lead: "Một lệnh PowerShell cài WSL2, Ubuntu, Piagent và Agent Watch, nối key công ty và thêm Piagent vào Start menu. Chạy lại để cập nhật."
40
+ },
41
+ {
42
+ slug: "update",
43
+ icon: "refresh",
44
+ nav: "Cập nhật và gỡ",
45
+ title: "Cập nhật, rollback và gỡ cài đặt",
46
+ lead: "Một lệnh để lên bản mới, dashboard và Terminal tự báo khi có bản mới. Rollback và gỡ cài đặt đều có bước xem trước."
47
+ },
48
+ {
49
+ slug: "whats-new",
50
+ icon: "sparkles",
51
+ nav: "Changelog",
52
+ title: "Changelog",
53
+ lead: "Mỗi bản phát hành: thay đổi gì, cần bản nào của Studio hay Agent Watch. Bản mới nhất ở trên cùng."
54
+ },
55
+ {
56
+ slug: "scope",
57
+ icon: "target",
58
+ nav: "Phạm vi platform",
59
+ title: "Platform nhận phần nào về mình",
60
+ lead: "Agent nào cũng có cơ chế governance riêng. Platform này đóng gói một cách vận hành cụ thể trên Pi để mọi repo dùng lại."
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ group: "Sử dụng",
66
+ pages: [
67
+ {
68
+ slug: "profiles",
69
+ icon: "sliders",
70
+ nav: "Project profile",
71
+ title: "Chọn đúng profile cho repo",
72
+ lead: "Profile quyết định protected path, verify command và capability mà repo được cấp; ghi vào .pi/piagent-profile.json và lock tương ứng."
73
+ },
74
+ {
75
+ slug: "permissions",
76
+ icon: "key",
77
+ nav: "Permission modes",
78
+ title: "Đổi quyền theo task bằng một lệnh",
79
+ lead: "Permission mode chỉ áp dụng cho session hiện tại. Đổi mode không sửa project profile."
80
+ },
81
+ {
82
+ slug: "commands",
83
+ icon: "terminal",
84
+ nav: "Command catalog",
85
+ title: "Command ngắn, hành vi rõ",
86
+ lead: "Slash command gói sẵn policy và flow; một inspector cho diff, command, safety và context."
87
+ },
88
+ {
89
+ slug: "workflows",
90
+ icon: "branch",
91
+ nav: "Workflows",
92
+ title: "Làm việc hằng ngày",
93
+ lead: "Không có launcher workflow: nói việc bằng lời thường. Command xem thêm chỉ quan sát, không mở lượt model."
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ group: "MCP",
99
+ pages: [
100
+ {
101
+ slug: "mcp",
102
+ icon: "plug",
103
+ nav: "Tổng quan MCP",
104
+ title: "MCP trong Pi Agent Platform",
105
+ lead: "Platform cài pi-mcp-adapter để dùng MCP tiết kiệm token, và quản lý server trên bốn lớp cấu hình."
106
+ },
107
+ {
108
+ slug: "mcp-servers",
109
+ icon: "server",
110
+ nav: "Quản lý server",
111
+ title: "Thêm, xem và gỡ MCP server",
112
+ lead: "piagent-mcp có đủ add, remove, get, list, enable, disable trên cả bốn scope."
113
+ },
114
+ {
115
+ slug: "mcp-auth",
116
+ icon: "lock",
117
+ nav: "Auth và readiness",
118
+ title: "Đăng nhập và trạng thái sẵn sàng",
119
+ lead: "Platform không giữ token và không tự chạy OAuth. Nó từ chối ghi secret vào config và cho biết server nào thật sự gọi được."
120
+ },
121
+ {
122
+ slug: "mcp-approval",
123
+ icon: "shield-check",
124
+ nav: "Approval gate",
125
+ title: "Server đến từ repo phải được duyệt",
126
+ lead: "Clone một repo lạ không cho tác giả repo đó chạy process trên máy bạn. Quyết định lưu ngoài repo và pin theo digest."
127
+ }
128
+ ]
129
+ },
130
+ {
131
+ group: "Vận hành",
132
+ pages: [
133
+ {
134
+ slug: "security",
135
+ icon: "shield",
136
+ nav: "Security model",
137
+ title: "Lớp policy, không phải OS sandbox",
138
+ lead: "Fail-closed trong bề mặt tool được kiểm soát: lock sai, profile sai hay thiếu quyền thì chặn, không đoán."
139
+ },
140
+ {
141
+ slug: "architecture",
142
+ icon: "layers",
143
+ nav: "Kiến trúc",
144
+ title: "Kiến trúc repo",
145
+ lead: "Core chỉ giữ policy, prompt, guard, skill và script dùng lại được; business logic ở lại project của nó."
146
+ },
147
+ {
148
+ slug: "context-engine",
149
+ icon: "cpu",
150
+ nav: "Context Engine",
151
+ title: "Ít context hơn, giữ nguyên chất lượng",
152
+ lead: "Index local, retrieval có xếp hạng, tool nạp theo task, compaction theo ngữ nghĩa và telemetry minh bạch."
153
+ },
154
+ {
155
+ slug: "benchmark",
156
+ icon: "chart",
157
+ nav: "Benchmark",
158
+ title: "Bằng chứng chất lượng và hiệu suất",
159
+ lead: "So Piagent với codex-cli trên cùng model, cùng thinking: 18 nhóm bài, 108 session chấm tự động."
160
+ },
161
+ {
162
+ slug: "runtime",
163
+ icon: "wrench",
164
+ nav: "Runtime tools",
165
+ title: "Terminal command cho người vận hành",
166
+ lead: "Command chạy ngoài slash command: setup, doctor, benchmark, capability và MCP."
167
+ },
168
+ {
169
+ slug: "team",
170
+ icon: "users",
171
+ nav: "Dùng cho team",
172
+ title: "Một chuẩn vận hành cho cả team",
173
+ lead: "Mỗi repo link package, chọn profile phù hợp và giữ tài liệu riêng ở repo của dự án đó."
174
+ }
175
+ ]
176
+ }
177
+ ];
178
+
179
+ const EN_GROUPS = {
180
+ "Bắt đầu": "Get started",
181
+ "Sử dụng": "Build",
182
+ MCP: "MCP",
183
+ "Vận hành": "Operate"
184
+ };
185
+
186
+ const EN_PAGE_COPY = {
187
+ index: {
188
+ nav: "Overview",
189
+ title: "Pi Agent Platform",
190
+ lead: "A coding agent for the whole team: a guard checks every tool call, the team's Harness runs checks and review, one dashboard for Terminal and the browser."
191
+ },
192
+ ecosystem: {
193
+ nav: "Ecosystem",
194
+ title: "Piagent, Agent Watch and Agent Studio",
195
+ lead: "What runs on your machine, what runs on the company server, and when to use your own model or the company's."
196
+ },
197
+ quickstart: {
198
+ nav: "Install",
199
+ title: "Install and run your first turn",
200
+ lead: "Two commands to install. Open a repository, pick its profile and permissions, then ask in plain language."
201
+ },
202
+ windows: {
203
+ nav: "Windows",
204
+ title: "Install on Windows with one command",
205
+ lead: "One PowerShell command installs WSL2, Ubuntu, Piagent and Agent Watch, binds the company key and adds Piagent to the Start menu. Run it again to update."
206
+ },
207
+ update: {
208
+ nav: "Update & uninstall",
209
+ title: "Update, roll back and uninstall",
210
+ lead: "One command moves to a new release, and the dashboard and Terminal tell you when one is out. Rollback and uninstall both preview first."
211
+ },
212
+ "whats-new": {
213
+ nav: "Changelog",
214
+ title: "Changelog",
215
+ lead: "Every release: what changed and which Studio or Agent Watch it needs. Newest first."
216
+ },
217
+ scope: {
218
+ nav: "Platform scope",
219
+ title: "What the platform owns",
220
+ lead: "Every agent has governance primitives. This platform packages one repeatable operating model on Pi for every repository."
221
+ },
222
+ profiles: {
223
+ nav: "Project profiles",
224
+ title: "Choose the right profile",
225
+ lead: "A profile sets protected paths, verification commands and the capabilities a repository is granted."
226
+ },
227
+ permissions: {
228
+ nav: "Permission modes",
229
+ title: "Change permissions per task",
230
+ lead: "Permission modes apply to the current session only. Changing one does not modify the project profile."
231
+ },
232
+ commands: {
233
+ nav: "Command catalog",
234
+ title: "Short commands, predictable behaviour",
235
+ lead: "Slash commands package policy and flow, with one inspector for diffs, commands, safety and context."
236
+ },
237
+ workflows: {
238
+ nav: "Workflows",
239
+ title: "Day-to-day work",
240
+ lead: "No workflow launchers: say what you need in plain language. Inspection commands only observe and never start a model turn."
241
+ },
242
+ mcp: {
243
+ nav: "MCP overview",
244
+ title: "MCP in Pi Agent Platform",
245
+ lead: "The platform installs pi-mcp-adapter for token-efficient MCP access and manages servers across four configuration layers."
246
+ },
247
+ "mcp-servers": {
248
+ nav: "Server management",
249
+ title: "Add, inspect and remove MCP servers",
250
+ lead: "piagent-mcp provides add, remove, get, list, enable and disable across all four scopes."
251
+ },
252
+ "mcp-auth": {
253
+ nav: "Authentication",
254
+ title: "Authentication and readiness",
255
+ lead: "The platform stores no tokens and runs no OAuth. It rejects secrets in configuration and reports which servers are actually ready."
256
+ },
257
+ "mcp-approval": {
258
+ nav: "Approval gate",
259
+ title: "Repository servers require approval",
260
+ lead: "Cloning an untrusted repository must not give its author a process on your machine. Decisions live outside the repository, pinned by digest."
261
+ },
262
+ security: {
263
+ nav: "Security model",
264
+ title: "Policy enforcement, not an OS sandbox",
265
+ lead: "Controlled tool surfaces fail closed when profiles, integrity locks or permissions are invalid."
266
+ },
267
+ architecture: {
268
+ nav: "Architecture",
269
+ title: "Repository architecture",
270
+ lead: "Core holds reusable policy and runtime infrastructure; business logic stays in the project that owns it."
271
+ },
272
+ "context-engine": {
273
+ nav: "Context Engine",
274
+ title: "Less context, same model quality",
275
+ lead: "Local indexing, ranked retrieval, task-driven tool loading, semantic compaction and transparent telemetry."
276
+ },
277
+ benchmark: {
278
+ nav: "Benchmark",
279
+ title: "Quality and efficiency evidence",
280
+ lead: "Piagent against codex-cli on the same model and thinking level: 18 task families, 108 automatically graded sessions."
281
+ },
282
+ runtime: {
283
+ nav: "Runtime tools",
284
+ title: "Terminal commands for operators",
285
+ lead: "Commands that run outside Pi: setup, updates, doctor, benchmarks, capabilities and MCP."
286
+ },
287
+ team: {
288
+ nav: "Team usage",
289
+ title: "One operating standard for the team",
290
+ lead: "Each repository links the package, selects a fitting profile and keeps project documentation in its own repository."
291
+ }
292
+ };
293
+
294
+ const NAV_BY_LOCALE = {
295
+ vi: NAV,
296
+ en: NAV.map((section) => ({
297
+ group: EN_GROUPS[section.group] ?? section.group,
298
+ pages: section.pages.map((page) => ({ ...page, ...EN_PAGE_COPY[page.slug] }))
299
+ }))
300
+ };
301
+
302
+ export { NAV, NAV_BY_LOCALE };
@@ -0,0 +1,227 @@
1
+ import fs from "node:fs";
2
+
3
+ // Release notes, from one source: docs-site/releases.json. Each entry carries
4
+ // both languages, so the Changelog page in VI and EN, the landing page's
5
+ // "latest" cards and CHANGELOG.md are all drawn from the same facts.
6
+
7
+ const KINDS = ["breaking", "new", "improved", "fixed", "security"];
8
+ const STATUSES = new Set(["stable", "prerelease", "unpublished"]);
9
+
10
+ const KIND_LABEL = {
11
+ vi: { breaking: "Thay đổi lớn", new: "Mới", improved: "Cải thiện", fixed: "Sửa lỗi", security: "Bảo mật" },
12
+ en: { breaking: "Breaking", new: "New", improved: "Improved", fixed: "Fixed", security: "Security" }
13
+ };
14
+
15
+ const MARKDOWN_HEADING = {
16
+ breaking: "Breaking changes",
17
+ new: "Added",
18
+ improved: "Improved",
19
+ fixed: "Fixed",
20
+ security: "Security"
21
+ };
22
+
23
+ const COPY = {
24
+ vi: {
25
+ requires: "Cần",
26
+ filterAria: "Lọc theo loại thay đổi",
27
+ all: "Tất cả",
28
+ archive: "Các bản trước",
29
+ archiveLead: "Bản cũ hơn, gom theo tháng. Mở một tháng để xem từng thay đổi.",
30
+ prerelease: "bản thử",
31
+ unpublished: "không phát hành",
32
+ latest: "Mới nhất",
33
+ month: (year, month) => `Tháng ${month}/${year}`,
34
+ date: (value) => {
35
+ const [year, month, day] = value.split("-");
36
+ return `${day}/${month}/${year}`;
37
+ },
38
+ readMore: "Xem chi tiết"
39
+ },
40
+ en: {
41
+ requires: "Requires",
42
+ filterAria: "Filter by kind of change",
43
+ all: "All",
44
+ archive: "Earlier releases",
45
+ archiveLead: "Older releases, grouped by month. Open a month to see every change.",
46
+ prerelease: "prerelease",
47
+ unpublished: "not published",
48
+ latest: "Latest",
49
+ month: (year, month) => new Date(Date.UTC(Number(year), Number(month) - 1, 1))
50
+ .toLocaleDateString("en-US", { month: "long", year: "numeric", timeZone: "UTC" }),
51
+ date: (value) => new Date(`${value}T00:00:00Z`)
52
+ .toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" }),
53
+ readMore: "Read more"
54
+ }
55
+ };
56
+
57
+ // The newest releases get a full card; everything older folds into months.
58
+ const DETAILED_COUNT = 24;
59
+
60
+ /** @param {string} file */
61
+ function readReleases(file) {
62
+ const releases = JSON.parse(fs.readFileSync(file, "utf8"));
63
+ if (!Array.isArray(releases) || releases.length === 0) throw new Error("docs-site/releases.json must be a non-empty array");
64
+ const seen = new Set();
65
+ for (const release of releases) {
66
+ const where = `releases.json ${release?.version ?? "?"}`;
67
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?$/.test(release.version ?? "")) throw new Error(`${where}: bad version`);
68
+ if (seen.has(release.version)) throw new Error(`${where}: duplicate version`);
69
+ seen.add(release.version);
70
+ if (release.date !== null && !/^\d{4}-\d{2}-\d{2}$/.test(release.date ?? "")) throw new Error(`${where}: date must be YYYY-MM-DD or null`);
71
+ if (!STATUSES.has(release.status)) throw new Error(`${where}: status must be stable, prerelease or unpublished`);
72
+ for (const field of ["title", "summary"]) {
73
+ if (!release[field]?.en || !release[field]?.vi) throw new Error(`${where}: ${field} needs en and vi`);
74
+ }
75
+ if (!Array.isArray(release.items) || release.items.length === 0) throw new Error(`${where}: items must be a non-empty array`);
76
+ for (const item of release.items) {
77
+ if (!KINDS.includes(item.kind) || !item.en || !item.vi) throw new Error(`${where}: every item needs a known kind, en and vi`);
78
+ }
79
+ if (release.requires !== null && (!release.requires?.en || !release.requires?.vi)) throw new Error(`${where}: requires needs en and vi, or null`);
80
+ }
81
+ return releases;
82
+ }
83
+
84
+ const anchorFor = (version) => `v${version.replace(/\./g, "-")}`;
85
+
86
+ /** Text from the JSON, escaped, with `code` spans. */
87
+ function inline(text, escapeHtml) {
88
+ return escapeHtml(text).replace(/`([^`]+)`/g, "<code>$1</code>");
89
+ }
90
+
91
+ function sortedItems(items) {
92
+ return [...items].sort((a, b) => KINDS.indexOf(a.kind) - KINDS.indexOf(b.kind));
93
+ }
94
+
95
+ function statusNote(release, copy) {
96
+ if (release.status === "prerelease") return ` <span class="tag">${copy.prerelease}</span>`;
97
+ if (release.status === "unpublished") return ` <span class="tag">${copy.unpublished}</span>`;
98
+ return "";
99
+ }
100
+
101
+ function renderItems(release, locale, escapeHtml) {
102
+ const labels = KIND_LABEL[locale.code];
103
+ return sortedItems(release.items)
104
+ .map((item) => ` <li data-kind="${item.kind}"><span class="kind kind-${item.kind}">${labels[item.kind]}</span><span>${inline(item[locale.code], escapeHtml)}</span></li>`)
105
+ .join("\n");
106
+ }
107
+
108
+ function renderRelease(release, index, locale, { escapeHtml }) {
109
+ const copy = COPY[locale.code];
110
+ const id = anchorFor(release.version);
111
+ const date = release.date ? `<time class="release-date" datetime="${release.date}">${copy.date(release.date)}</time>` : "";
112
+ const requires = release.requires
113
+ ? `\n <p class="release-requires"><strong>${copy.requires}:</strong> ${inline(release.requires[locale.code], escapeHtml)}</p>`
114
+ : "";
115
+ return `<article class="release${index === 0 ? " latest" : ""}" id="${id}" data-release>
116
+ <div class="release-meta"><a class="release-version" href="#${id}">v${release.version}</a>${date}</div>
117
+ <div class="release-body">
118
+ <h3 data-toc-skip id="${id}-title">${inline(release.title[locale.code], escapeHtml)}${statusNote(release, copy)}</h3>
119
+ <p>${inline(release.summary[locale.code], escapeHtml)}</p>
120
+ <ul class="release-items">
121
+ ${renderItems(release, locale, escapeHtml)}
122
+ </ul>${requires}
123
+ </div>
124
+ </article>`;
125
+ }
126
+
127
+ function monthKey(release) {
128
+ return release.date ? release.date.slice(0, 7) : "undated";
129
+ }
130
+
131
+ function renderArchive(releases, locale, { escapeHtml }) {
132
+ const copy = COPY[locale.code];
133
+ const months = new Map();
134
+ for (const release of releases) {
135
+ const key = monthKey(release);
136
+ if (!months.has(key)) months.set(key, []);
137
+ months.get(key).push(release);
138
+ }
139
+ const groups = [...months.entries()].map(([key, group]) => {
140
+ const [year, month] = key.split("-");
141
+ const label = key === "undated" ? copy.unpublished : copy.month(year, month);
142
+ const rows = group.map((release) => {
143
+ const id = anchorFor(release.version);
144
+ const date = release.date ? ` <span class="release-date">${copy.date(release.date)}</span>` : "";
145
+ return ` <div class="archive-row" id="${id}">
146
+ <div class="archive-head"><a class="release-version" href="#${id}">v${release.version}</a>${date}</div>
147
+ <div class="archive-body">
148
+ <p><strong>${inline(release.title[locale.code], escapeHtml)}</strong>${statusNote(release, copy)} — ${inline(release.summary[locale.code], escapeHtml)}</p>
149
+ <ul class="release-items">
150
+ ${renderItems(release, locale, escapeHtml).replace(/^/gm, " ")}
151
+ </ul>
152
+ </div>
153
+ </div>`;
154
+ }).join("\n");
155
+ return ` <details class="release-archive">
156
+ <summary><span>${label}</span><span class="archive-count">${group.length}</span></summary>
157
+ ${rows}
158
+ </details>`;
159
+ }).join("\n");
160
+ return `<section class="section archive">
161
+ <h2 id="archive">${copy.archive}</h2>
162
+ <p class="section-lead">${copy.archiveLead}</p>
163
+ ${groups}
164
+ </section>`;
165
+ }
166
+
167
+ /** The body of the Changelog page for one locale. */
168
+ function renderReleaseNotes(releases, locale, helpers) {
169
+ const copy = COPY[locale.code];
170
+ const labels = KIND_LABEL[locale.code];
171
+ const detailed = releases.filter((release) => release.status !== "prerelease").slice(0, DETAILED_COUNT);
172
+ const detailedSet = new Set(detailed);
173
+ const older = releases.filter((release) => !detailedSet.has(release));
174
+ const filters = ["all", "new", "improved", "fixed", "security"]
175
+ .map((kind, index) => `<button type="button" data-filter-kind="${kind}" aria-pressed="${index === 0}">${kind === "all" ? copy.all : labels[kind]}</button>`)
176
+ .join("");
177
+ return `<div class="release-filter" role="toolbar" aria-label="${copy.filterAria}">${filters}</div>
178
+ <section class="section releases" data-releases>
179
+ <h2 id="latest" class="sr-only">${copy.latest}</h2>
180
+ ${detailed.map((release, index) => renderRelease(release, index, locale, helpers)).join("\n")}
181
+ </section>
182
+ ${renderArchive(older, locale, helpers)}`;
183
+ }
184
+
185
+ /** Three cards for the landing page. */
186
+ function renderLatestReleases(releases, locale, { escapeHtml, icon, hrefFor }) {
187
+ const copy = COPY[locale.code];
188
+ const changelog = hrefFor({ slug: "whats-new" }, locale);
189
+ return releases.filter((release) => release.status === "stable").slice(0, 3).map((release) => ` <a class="update" href="${changelog}#${anchorFor(release.version)}" data-reveal>
190
+ <span class="update-meta"><span class="release-version">v${release.version}</span>${release.date ? copy.date(release.date) : ""}</span>
191
+ <h3>${inline(release.title[locale.code], escapeHtml)}</h3>
192
+ <p>${inline(release.summary[locale.code], escapeHtml)}</p>
193
+ <span class="update-more">${copy.readMore} ${icon("arrow-right")}</span>
194
+ </a>`).join("\n");
195
+ }
196
+
197
+ /** The pill above the landing headline: the newest stable release. */
198
+ function renderHeroPill(releases, locale, { escapeHtml, icon, hrefFor }) {
199
+ const latest = releases.find((release) => release.status === "stable");
200
+ return `<a class="hero-pill" href="${hrefFor({ slug: "whats-new" }, locale)}#${anchorFor(latest.version)}"><b>v${latest.version}</b><span>${inline(latest.title[locale.code], escapeHtml)}</span>${icon("arrow-right")}</a>`;
201
+ }
202
+
203
+ /** CHANGELOG.md, in English, newest first. */
204
+ function renderChangelogMarkdown(releases) {
205
+ const lines = [
206
+ "# Changelog",
207
+ "",
208
+ "Every release of Pi Agent Platform (`@piagent/platform`), newest first. Versions follow semantic versioning; prereleases and builds that were tagged but never published are marked.",
209
+ "",
210
+ "This file is generated from `docs-site/releases.json` by `node scripts/build-docs-site.mjs`, which also renders the [Changelog page](https://piagent.io.vn/en/whats-new). Edit the JSON, not this file. When publishing a tag, copy the version's block into the GitHub Release.",
211
+ ""
212
+ ];
213
+ for (const release of releases) {
214
+ const suffix = release.status === "unpublished" ? " (not published)" : "";
215
+ lines.push(release.date ? `## v${release.version} - ${release.date}${suffix}` : `## v${release.version} - not published`, "");
216
+ lines.push(`**${release.title.en}.** ${release.summary.en}`, "");
217
+ for (const kind of KINDS) {
218
+ const items = release.items.filter((item) => item.kind === kind);
219
+ if (items.length === 0) continue;
220
+ lines.push(`### ${MARKDOWN_HEADING[kind]}`, "", ...items.map((item) => `- ${item.en}`), "");
221
+ }
222
+ if (release.requires) lines.push(`Requires: ${release.requires.en}.`, "");
223
+ }
224
+ return `${lines.join("\n").replace(/\n+$/, "")}\n`;
225
+ }
226
+
227
+ export { readReleases, renderChangelogMarkdown, renderHeroPill, renderLatestReleases, renderReleaseNotes };