@piagent/platform 1.2.15 → 1.2.17

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 (75) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +3 -3
  3. package/docs/README.md +1 -1
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +7 -7
  6. package/docs/model-options.md +1 -1
  7. package/docs/operator-manual-vietnamese.md +7 -7
  8. package/docs/release-install-policy.md +6 -6
  9. package/docs/security-threat-model.md +1 -1
  10. package/docs/team-onboarding.md +2 -2
  11. package/docs/vercel-docs-site.md +7 -5
  12. package/docs-site/architecture.html +12 -6
  13. package/docs-site/assets/docs.css +52 -1
  14. package/docs-site/assets/docs.js +4 -2
  15. package/docs-site/benchmark.html +12 -6
  16. package/docs-site/commands.html +12 -6
  17. package/docs-site/content/en/architecture.html +33 -0
  18. package/docs-site/content/en/benchmark.html +164 -0
  19. package/docs-site/content/en/commands.html +39 -0
  20. package/docs-site/content/en/context-engine.html +30 -0
  21. package/docs-site/content/en/index.html +85 -0
  22. package/docs-site/content/en/mcp-approval.html +74 -0
  23. package/docs-site/content/en/mcp-auth.html +57 -0
  24. package/docs-site/content/en/mcp-servers.html +91 -0
  25. package/docs-site/content/en/mcp.html +90 -0
  26. package/docs-site/content/en/permissions.html +19 -0
  27. package/docs-site/content/en/profiles.html +27 -0
  28. package/docs-site/content/en/quickstart.html +126 -0
  29. package/docs-site/content/en/runtime.html +27 -0
  30. package/docs-site/content/en/scope.html +45 -0
  31. package/docs-site/content/en/security.html +33 -0
  32. package/docs-site/content/en/team.html +14 -0
  33. package/docs-site/content/en/workflows.html +21 -0
  34. package/docs-site/content/quickstart.html +1 -1
  35. package/docs-site/context-engine.html +12 -6
  36. package/docs-site/en/architecture.html +166 -0
  37. package/docs-site/en/benchmark.html +297 -0
  38. package/docs-site/en/commands.html +172 -0
  39. package/docs-site/en/context-engine.html +163 -0
  40. package/docs-site/en/index.html +203 -0
  41. package/docs-site/en/mcp-approval.html +207 -0
  42. package/docs-site/en/mcp-auth.html +190 -0
  43. package/docs-site/en/mcp-servers.html +224 -0
  44. package/docs-site/en/mcp.html +223 -0
  45. package/docs-site/en/permissions.html +152 -0
  46. package/docs-site/en/profiles.html +160 -0
  47. package/docs-site/en/quickstart.html +259 -0
  48. package/docs-site/en/runtime.html +160 -0
  49. package/docs-site/en/scope.html +178 -0
  50. package/docs-site/en/security.html +166 -0
  51. package/docs-site/en/team.html +143 -0
  52. package/docs-site/en/workflows.html +154 -0
  53. package/docs-site/index.html +12 -6
  54. package/docs-site/mcp-approval.html +12 -6
  55. package/docs-site/mcp-auth.html +12 -6
  56. package/docs-site/mcp-servers.html +12 -6
  57. package/docs-site/mcp.html +12 -6
  58. package/docs-site/permissions.html +12 -6
  59. package/docs-site/profiles.html +12 -6
  60. package/docs-site/quickstart.html +13 -7
  61. package/docs-site/runtime.html +12 -6
  62. package/docs-site/scope.html +12 -6
  63. package/docs-site/security.html +12 -6
  64. package/docs-site/team.html +12 -6
  65. package/docs-site/workflows.html +12 -6
  66. package/package.json +1 -1
  67. package/packages/piagent-core/package.json +1 -1
  68. package/scripts/build-docs-site.mjs +224 -50
  69. package/scripts/configure-model-scope.sh +4 -4
  70. package/scripts/install-global.sh +1 -1
  71. package/scripts/setup.sh +2 -2
  72. package/scripts/verify-local.sh +7 -1
  73. package/scripts/verify-release-identity.mjs +15 -6
  74. package/templates/global/settings.json +2 -2
  75. package/templates/project/.pi/settings.json +1 -1
@@ -21,7 +21,6 @@ const SITE = {
21
21
  title: "Pi Agent Platform Docs",
22
22
  origin: "https://piagent.io.vn",
23
23
  repo: "https://github.com/Vt-mmm/piagent",
24
- englishDocs: "https://github.com/Vt-mmm/piagent/blob/main/docs/en/README.md",
25
24
  facebook: "https://www.facebook.com/vinhtam0544/"
26
25
  };
27
26
 
@@ -153,11 +152,164 @@ const NAV = [
153
152
  }
154
153
  ];
155
154
 
156
- const pages = NAV.flatMap((section) => section.pages.map((page) => ({ ...page, group: section.group })));
155
+ const EN_PAGE_COPY = {
156
+ index: {
157
+ nav: "Overview",
158
+ title: "Pi Agent Platform",
159
+ lead: "Reusable infrastructure for multiple projects: installation, profiles, permissions, MCP, security, and workflows in one place."
160
+ },
161
+ quickstart: {
162
+ nav: "Quickstart",
163
+ title: "Step by step for new team members",
164
+ lead: "Install with two commands, open a repository, select its profile and permission mode, run a task, and verify real evidence before handoff."
165
+ },
166
+ scope: {
167
+ nav: "Platform scope",
168
+ title: "What the platform owns",
169
+ lead: "General coding agents already provide governance primitives. This platform packages one repeatable operating model for Pi across every repository."
170
+ },
171
+ profiles: {
172
+ nav: "Project profiles",
173
+ title: "Choose the right operating profile",
174
+ lead: "A project profile defines protected paths, verification commands, and the capabilities granted to a repository."
175
+ },
176
+ permissions: {
177
+ nav: "Permission modes",
178
+ title: "Change task permissions with one command",
179
+ lead: "Permission modes are session-local overrides. Changing a mode does not modify the project profile."
180
+ },
181
+ commands: {
182
+ nav: "Command catalog",
183
+ title: "Short commands with predictable behavior",
184
+ lead: "Slash commands package policy and workflow directly, avoiding model-driven command discovery and unnecessary token use."
185
+ },
186
+ workflows: {
187
+ nav: "Workflows",
188
+ title: "Core workflows",
189
+ lead: "Select a workflow by intent instead of repeating policy in every prompt. This keeps behavior consistent and context compact."
190
+ },
191
+ mcp: {
192
+ nav: "MCP overview",
193
+ title: "MCP in Pi Agent Platform",
194
+ lead: "The platform installs pi-mcp-adapter for token-efficient MCP access and manages servers across four configuration layers."
195
+ },
196
+ "mcp-servers": {
197
+ nav: "Server management",
198
+ title: "Add, inspect, and remove MCP servers",
199
+ lead: "piagent-mcp provides deterministic add, remove, get, list, enable, and disable operations across all four scopes."
200
+ },
201
+ "mcp-auth": {
202
+ nav: "Authentication",
203
+ title: "Authentication and readiness",
204
+ lead: "The platform does not store tokens or run OAuth. It rejects secrets in configuration and reports which servers are actually ready."
205
+ },
206
+ "mcp-approval": {
207
+ nav: "Approval gate",
208
+ title: "Repository servers require approval",
209
+ lead: "Cloning an untrusted repository must not grant its author a process on the operator's machine. Decisions live outside the repository and are pinned by digest."
210
+ },
211
+ security: {
212
+ nav: "Security model",
213
+ title: "Policy enforcement, not an OS sandbox",
214
+ lead: "Controlled tool surfaces fail closed when profiles, integrity locks, permissions, or verification evidence are invalid."
215
+ },
216
+ architecture: {
217
+ nav: "Architecture",
218
+ title: "Repository architecture",
219
+ lead: "Core contains reusable policy and runtime infrastructure, while project-specific business logic stays in the project that owns it."
220
+ },
221
+ "context-engine": {
222
+ nav: "Context Engine",
223
+ title: "Less context without reducing model quality",
224
+ lead: "Local indexing, ranked retrieval, task-driven tool loading, semantic compaction, and transparent telemetry for Agent Watch."
225
+ },
226
+ benchmark: {
227
+ nav: "Benchmark",
228
+ title: "Quality and efficiency evidence",
229
+ lead: "The production benchmark compares Piagent with codex-cli using the same model, thinking level, 18 task families, and 108 automatically graded sessions."
230
+ },
231
+ runtime: {
232
+ nav: "Runtime tools",
233
+ title: "Terminal commands for operators",
234
+ lead: "Use these commands outside Pi for setup, updates, doctor checks, benchmarks, capabilities, and MCP management."
235
+ },
236
+ team: {
237
+ nav: "Team usage",
238
+ title: "A repeatable team operating standard",
239
+ lead: "Each repository links the package, selects a fitting profile, and keeps project-specific documentation in the repository that owns it."
240
+ }
241
+ };
242
+
243
+ const NAV_BY_LOCALE = {
244
+ vi: NAV,
245
+ en: NAV.map((section) => ({
246
+ group: section.group,
247
+ pages: section.pages.map((page) => ({ ...page, ...EN_PAGE_COPY[page.slug] }))
248
+ }))
249
+ };
250
+
251
+ const LOCALES = {
252
+ vi: {
253
+ code: "vi",
254
+ label: "VI",
255
+ prefix: "",
256
+ contentRoot,
257
+ nav: NAV_BY_LOCALE.vi,
258
+ ui: {
259
+ breadcrumb: "Docs",
260
+ language: "Ngôn ngữ",
261
+ githubAria: "Mở GitHub repository",
262
+ menu: "Lộ trình",
263
+ commands: "Commands",
264
+ searchAria: "Tìm trong tài liệu",
265
+ searchPlaceholder: "Tìm trang",
266
+ navAria: "Tài liệu",
267
+ pagerAria: "Trang kế tiếp",
268
+ previous: "Trước",
269
+ next: "Tiếp",
270
+ tocAria: "Mục lục trang",
271
+ tocTitle: "Trên trang này",
272
+ externalLinks: "Liên kết ngoài",
273
+ copySuccess: "Đã lấy",
274
+ copyFallback: "Chọn lệnh"
275
+ }
276
+ },
277
+ en: {
278
+ code: "en",
279
+ label: "EN",
280
+ prefix: "/en",
281
+ contentRoot: path.join(contentRoot, "en"),
282
+ nav: NAV_BY_LOCALE.en,
283
+ ui: {
284
+ breadcrumb: "Docs",
285
+ language: "Language",
286
+ githubAria: "Open GitHub repository",
287
+ menu: "Menu",
288
+ commands: "Commands",
289
+ searchAria: "Search documentation",
290
+ searchPlaceholder: "Search pages",
291
+ navAria: "Documentation",
292
+ pagerAria: "Adjacent pages",
293
+ previous: "Previous",
294
+ next: "Next",
295
+ tocAria: "Page contents",
296
+ tocTitle: "On this page",
297
+ externalLinks: "External links",
298
+ copySuccess: "Copied",
299
+ copyFallback: "Select command"
300
+ }
301
+ }
302
+ };
303
+
304
+ const pagesFor = (locale) => locale.nav.flatMap((section) =>
305
+ section.pages.map((page) => ({ ...page, group: section.group }))
306
+ );
157
307
 
158
308
  /** @param {{slug: string, href?: string}} page @returns {string} */
159
- function hrefFor(page) {
160
- return page.href ?? `/${page.slug}`;
309
+ function hrefFor(page, locale) {
310
+ const relative = page.href ?? `/${page.slug}`;
311
+ if (!locale.prefix) return relative;
312
+ return relative === "/" ? `${locale.prefix}/` : `${locale.prefix}${relative}`;
161
313
  }
162
314
 
163
315
  /** @param {string} value @returns {string} */
@@ -191,27 +343,27 @@ function stripTags(value) {
191
343
  return text;
192
344
  }
193
345
 
194
- /** @param {string} currentSlug @returns {string} */
195
- function renderNav(currentSlug) {
196
- return NAV.map((section) => {
346
+ /** @param {string} currentSlug @param {typeof LOCALES.vi} locale @returns {string} */
347
+ function renderNav(currentSlug, locale) {
348
+ return locale.nav.map((section) => {
197
349
  const links = section.pages
198
350
  .map((page) => {
199
351
  const current = page.slug === currentSlug;
200
352
  const classes = current ? "nav-link current" : "nav-link";
201
353
  const aria = current ? ' aria-current="page"' : "";
202
- return ` <a class="${classes}" href="${hrefFor(page)}"${aria}>${escapeHtml(page.nav)}</a>`;
354
+ return ` <a class="${classes}" href="${hrefFor(page, locale)}"${aria}>${escapeHtml(page.nav)}</a>`;
203
355
  })
204
356
  .join("\n");
205
357
  return ` <div class="nav-group">\n <p class="nav-title">${escapeHtml(section.group)}</p>\n${links}\n </div>`;
206
358
  }).join("\n");
207
359
  }
208
360
 
209
- /** @param {typeof pages[number]} page @returns {string} */
210
- function renderBreadcrumb(page) {
361
+ /** @param {ReturnType<typeof pagesFor>[number]} page @param {typeof LOCALES.vi} locale @returns {string} */
362
+ function renderBreadcrumb(page, locale) {
211
363
  if (page.slug === "index") return "";
212
364
  return [
213
365
  ' <nav class="breadcrumb" aria-label="Breadcrumb">',
214
- ' <a href="/">Docs</a>',
366
+ ` <a href="${hrefFor({ slug: "index", href: "/" }, locale)}">${locale.ui.breadcrumb}</a>`,
215
367
  ' <span aria-hidden="true">/</span>',
216
368
  ` <span>${escapeHtml(page.group)}</span>`,
217
369
  ' <span aria-hidden="true">/</span>',
@@ -221,7 +373,7 @@ function renderBreadcrumb(page) {
221
373
  ].join("\n");
222
374
  }
223
375
 
224
- /** @param {typeof pages[number]} page @returns {string} */
376
+ /** @param {ReturnType<typeof pagesFor>[number]} page @returns {string} */
225
377
  function renderPageHead(page) {
226
378
  // The landing page carries its own hero, so it is not given a generated head.
227
379
  if (page.slug === "index") return "";
@@ -234,24 +386,24 @@ function renderPageHead(page) {
234
386
  ].join("\n");
235
387
  }
236
388
 
237
- /** @param {number} index @returns {string} */
238
- function renderPager(index) {
389
+ /** @param {number} index @param {ReturnType<typeof pagesFor>} pages @param {typeof LOCALES.vi} locale @returns {string} */
390
+ function renderPager(index, pages, locale) {
239
391
  const previous = pages[index - 1];
240
392
  const next = pages[index + 1];
241
393
  if (!previous && !next) return "";
242
- const parts = [' <nav class="pager" aria-label="Trang kế tiếp">'];
394
+ const parts = [` <nav class="pager" aria-label="${locale.ui.pagerAria}">`];
243
395
  if (previous) {
244
396
  parts.push(
245
- ` <a class="pager-prev" href="${hrefFor(previous)}">`,
246
- ' <span class="pager-label">Trước</span>',
397
+ ` <a class="pager-prev" href="${hrefFor(previous, locale)}">`,
398
+ ` <span class="pager-label">${locale.ui.previous}</span>`,
247
399
  ` <span class="pager-title">${escapeHtml(previous.nav)}</span>`,
248
400
  " </a>"
249
401
  );
250
402
  }
251
403
  if (next) {
252
404
  parts.push(
253
- ` <a class="pager-next" href="${hrefFor(next)}">`,
254
- ' <span class="pager-label">Tiếp</span>',
405
+ ` <a class="pager-next" href="${hrefFor(next, locale)}">`,
406
+ ` <span class="pager-label">${locale.ui.next}</span>`,
255
407
  ` <span class="pager-title">${escapeHtml(next.nav)}</span>`,
256
408
  " </a>"
257
409
  );
@@ -261,19 +413,29 @@ function renderPager(index) {
261
413
  }
262
414
 
263
415
  /**
264
- * @param {typeof pages[number]} page
416
+ * @param {ReturnType<typeof pagesFor>[number]} page
265
417
  * @param {number} index
266
418
  * @param {string} body
267
419
  * @param {string} version
420
+ * @param {typeof LOCALES.vi} locale
421
+ * @param {ReturnType<typeof pagesFor>} pages
268
422
  * @returns {string}
269
423
  */
270
- function renderPage(page, index, body, version) {
271
- const canonical = page.slug === "index" ? `${SITE.origin}/` : `${SITE.origin}${hrefFor(page)}`;
424
+ function renderPage(page, index, body, version, locale, pages) {
425
+ const canonical = `${SITE.origin}${hrefFor(page, locale)}`;
426
+ const alternateVi = `${SITE.origin}${hrefFor(page, LOCALES.vi)}`;
427
+ const alternateEn = `${SITE.origin}${hrefFor(page, LOCALES.en)}`;
272
428
  const documentTitle = page.slug === "index" ? SITE.title : `${page.title} · ${SITE.title}`;
273
429
  const description = stripTags(page.lead);
430
+ const languageLinks = Object.values(LOCALES).map((candidate) => {
431
+ const current = candidate.code === locale.code;
432
+ const classes = current ? "language-option current" : "language-option";
433
+ const aria = current ? ' aria-current="true"' : "";
434
+ return `<a class="${classes}" href="${hrefFor(page, candidate)}" hreflang="${candidate.code}" lang="${candidate.code}"${aria}>${candidate.label}</a>`;
435
+ }).join("\n ");
274
436
 
275
437
  return `<!doctype html>
276
- <html lang="vi">
438
+ <html lang="${locale.code}" data-copy-success="${locale.ui.copySuccess}" data-copy-fallback="${locale.ui.copyFallback}">
277
439
  <head>
278
440
  <meta charset="utf-8" />
279
441
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -281,6 +443,9 @@ function renderPage(page, index, body, version) {
281
443
  <meta name="description" content="${escapeHtml(description)}" />
282
444
  <meta name="theme-color" content="#050505" />
283
445
  <link rel="canonical" href="${canonical}" />
446
+ <link rel="alternate" hreflang="vi" href="${alternateVi}" />
447
+ <link rel="alternate" hreflang="en" href="${alternateEn}" />
448
+ <link rel="alternate" hreflang="x-default" href="${alternateVi}" />
284
449
  <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
285
450
  <meta property="og:title" content="${escapeHtml(documentTitle)}" />
286
451
  <meta property="og:description" content="${escapeHtml(description)}" />
@@ -296,56 +461,58 @@ function renderPage(page, index, body, version) {
296
461
  </head>
297
462
  <body>
298
463
  <header class="topbar">
299
- <a class="brand" href="/" aria-label="Pi Agent Platform">
464
+ <a class="brand" href="${hrefFor({ slug: "index", href: "/" }, locale)}" aria-label="Pi Agent Platform">
300
465
  <span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" /></span>
301
466
  <span>Pi Agent Platform</span>
302
467
  </a>
303
468
  <div class="top-actions">
304
- <span class="pill"><span class="pill-dot"></span>v${version} docs · VI</span>
305
- <a class="button" href="${SITE.englishDocs}" lang="en" target="_blank" rel="noopener noreferrer">English docs</a>
306
- <a class="button social" href="${SITE.repo}" target="_blank" rel="noopener noreferrer" aria-label="Mở GitHub repository">
469
+ <span class="pill"><span class="pill-dot"></span>v${version} docs · ${locale.label}</span>
470
+ <nav class="language-switch" aria-label="${locale.ui.language}">
471
+ ${languageLinks}
472
+ </nav>
473
+ <a class="button social" href="${SITE.repo}" target="_blank" rel="noopener noreferrer" aria-label="${locale.ui.githubAria}">
307
474
  <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
308
475
  <path d="M7 17 17 7M9 7h8v8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
309
476
  </svg>
310
477
  GitHub
311
478
  </a>
312
- <button class="button mobile-menu" type="button" data-menu>Lộ trình</button>
313
- <a class="button" href="/commands">Commands</a>
479
+ <button class="button mobile-menu" type="button" data-menu>${locale.ui.menu}</button>
480
+ <a class="button top-command-link" href="${hrefFor({ slug: "commands" }, locale)}">${locale.ui.commands}</a>
314
481
  </div>
315
482
  </header>
316
483
 
317
484
  <div class="layout with-toc">
318
485
  <aside class="sidebar" data-sidebar>
319
- <div class="sidebar-search" aria-label="Tìm trong tài liệu">
486
+ <div class="sidebar-search" aria-label="${locale.ui.searchAria}">
320
487
  <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
321
488
  <path d="M10.8 18.1a7.3 7.3 0 1 1 0-14.6 7.3 7.3 0 0 1 0 14.6Zm5.3-1.7 4.4 4.4" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
322
489
  </svg>
323
- <input type="search" placeholder="Tìm trang" data-filter />
490
+ <input type="search" placeholder="${locale.ui.searchPlaceholder}" data-filter />
324
491
  </div>
325
492
 
326
- <nav aria-label="Tài liệu">
327
- ${renderNav(page.slug)}
493
+ <nav aria-label="${locale.ui.navAria}">
494
+ ${renderNav(page.slug, locale)}
328
495
  </nav>
329
496
  </aside>
330
497
 
331
498
  <main>
332
499
  <div class="content-shell" data-article>
333
- ${renderBreadcrumb(page)}${renderPageHead(page)}${body}
334
- ${renderPager(index)}
500
+ ${renderBreadcrumb(page, locale)}${renderPageHead(page)}${body}
501
+ ${renderPager(index, pages, locale)}
335
502
  <footer class="footer">
336
503
  <p>Pi Agent Platform docs · static HTML · governed agent workflow</p>
337
- <div class="footer-links" aria-label="External links">
504
+ <div class="footer-links" aria-label="${locale.ui.externalLinks}">
338
505
  <a href="${SITE.origin}" target="_blank" rel="noopener noreferrer">piagent.io.vn</a>
339
506
  <a href="${SITE.repo}" target="_blank" rel="noopener noreferrer">GitHub repository</a>
340
- <a href="${SITE.englishDocs}" lang="en" target="_blank" rel="noopener noreferrer">English docs</a>
507
+ <a href="${hrefFor(page, locale.code === "vi" ? LOCALES.en : LOCALES.vi)}" hreflang="${locale.code === "vi" ? "en" : "vi"}">${locale.code === "vi" ? "English" : "Tiếng Việt"}</a>
341
508
  <a href="${SITE.facebook}" target="_blank" rel="noopener noreferrer">Facebook profile</a>
342
509
  </div>
343
510
  </footer>
344
511
  </div>
345
512
  </main>
346
513
 
347
- <aside class="toc" aria-label="Mục lục trang">
348
- <p class="toc-title">Trên trang này</p>
514
+ <aside class="toc" aria-label="${locale.ui.tocAria}">
515
+ <p class="toc-title">${locale.ui.tocTitle}</p>
349
516
  <nav data-toc></nav>
350
517
  </aside>
351
518
  </div>
@@ -386,14 +553,18 @@ function build() {
386
553
  const version = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")).version;
387
554
  /** @type {{file: string, html: string}[]} */
388
555
  const outputs = [];
389
- for (const [index, page] of pages.entries()) {
390
- const fragment = path.join(contentRoot, `${page.slug}.html`);
391
- if (!fs.existsSync(fragment)) throw new Error(`missing content fragment: ${path.relative(repoRoot, fragment)}`);
392
- const body = fs.readFileSync(fragment, "utf8").replace(/\s+$/, "");
393
- if (body.includes("\t")) throw new Error(`${page.slug}: content fragment contains a tab character`);
394
- const html = renderPage(page, index, body, version);
395
- assertPreBlocksIntact(page.slug, body, html);
396
- outputs.push({ file: path.join(siteRoot, `${page.slug}.html`), html });
556
+ for (const locale of Object.values(LOCALES)) {
557
+ const pages = pagesFor(locale);
558
+ for (const [index, page] of pages.entries()) {
559
+ const fragment = path.join(locale.contentRoot, `${page.slug}.html`);
560
+ if (!fs.existsSync(fragment)) throw new Error(`missing content fragment: ${path.relative(repoRoot, fragment)}`);
561
+ const body = fs.readFileSync(fragment, "utf8").replace(/\s+$/, "");
562
+ if (body.includes("\t")) throw new Error(`${locale.code}/${page.slug}: content fragment contains a tab character`);
563
+ const html = renderPage(page, index, body, version, locale, pages);
564
+ assertPreBlocksIntact(`${locale.code}/${page.slug}`, body, html);
565
+ const outputDir = locale.prefix ? path.join(siteRoot, locale.code) : siteRoot;
566
+ outputs.push({ file: path.join(outputDir, `${page.slug}.html`), html });
567
+ }
397
568
  }
398
569
  return outputs;
399
570
  }
@@ -415,7 +586,7 @@ function main(argv) {
415
586
  });
416
587
  if (stale.length > 0) {
417
588
  process.stderr.write(
418
- `FAIL: docs-site output is stale for ${stale.map((item) => path.basename(item.file)).join(", ")}. ` +
589
+ `FAIL: docs-site output is stale for ${stale.map((item) => path.relative(siteRoot, item.file)).join(", ")}. ` +
419
590
  "Run: node scripts/build-docs-site.mjs\n"
420
591
  );
421
592
  return 1;
@@ -424,7 +595,10 @@ function main(argv) {
424
595
  return 0;
425
596
  }
426
597
 
427
- for (const output of outputs) fs.writeFileSync(output.file, output.html);
598
+ for (const output of outputs) {
599
+ fs.mkdirSync(path.dirname(output.file), { recursive: true });
600
+ fs.writeFileSync(output.file, output.html);
601
+ }
428
602
  process.stdout.write(`${JSON.stringify({ ok: true, pages: outputs.length, out: path.relative(repoRoot, siteRoot) })}\n`);
429
603
  return 0;
430
604
  }
@@ -433,4 +607,4 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
433
607
  process.exit(main(process.argv.slice(2)));
434
608
  }
435
609
 
436
- export { NAV, build, main };
610
+ export { LOCALES, NAV, NAV_BY_LOCALE, build, hrefFor, main, pagesFor };
@@ -9,7 +9,7 @@ Usage:
9
9
  Options:
10
10
  --preset <full|codex|claude> Model families to enable (default: full = both)
11
11
  --default-model <provider/model[:thinking]>
12
- Default model (default: openai-codex/gpt-5.5:xhigh)
12
+ Default model (default: openai-codex/gpt-5.5:high)
13
13
  --settings <path> Pi settings file (default: ~/.pi/agent/settings.json)
14
14
  --dry-run Print resulting settings JSON without writing
15
15
  -h, --help
@@ -30,7 +30,7 @@ USAGE
30
30
  }
31
31
 
32
32
  PRESET="full"
33
- DEFAULT_MODEL="openai-codex/gpt-5.5:xhigh"
33
+ DEFAULT_MODEL="openai-codex/gpt-5.5:high"
34
34
  SETTINGS_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/settings.json"
35
35
  DRY_RUN=false
36
36
 
@@ -99,7 +99,7 @@ const codexModels = [
99
99
  "openai-codex/gpt-5.3-codex-spark:minimal",
100
100
  "openai-codex/gpt-5.4-mini:minimal",
101
101
  "openai-codex/gpt-5.4:xhigh",
102
- "openai-codex/gpt-5.5:xhigh",
102
+ "openai-codex/gpt-5.5:high",
103
103
  "openai-codex/gpt-5.6-luna:xhigh",
104
104
  "openai-codex/gpt-5.6-sol:xhigh",
105
105
  "openai-codex/gpt-5.6-terra:xhigh"
@@ -131,7 +131,7 @@ function parseDefaultModel(input) {
131
131
  return {
132
132
  provider: match[1],
133
133
  model: match[2],
134
- thinking: match[3] || "xhigh"
134
+ thinking: match[3] || "high"
135
135
  };
136
136
  }
137
137
 
@@ -66,7 +66,7 @@ WITH_WEB_ACCESS=true
66
66
  WITH_HERDR=false
67
67
  CONFIGURE_MODEL_SCOPE=true
68
68
  MODEL_SCOPE_PRESET="full"
69
- DEFAULT_MODEL="openai-codex/gpt-5.5:xhigh"
69
+ DEFAULT_MODEL="openai-codex/gpt-5.5:high"
70
70
  DRY_RUN=false
71
71
  FLOATING_PACKAGE_SOURCE=false
72
72
  RESOLVED_CHANNEL_LABEL="custom"
package/scripts/setup.sh CHANGED
@@ -36,7 +36,7 @@ Options:
36
36
  --model-scope <full|codex|claude> model families to enable
37
37
  Configure Pi enabledModels for selector/cycling (default: full)
38
38
  --default-model <provider/model[:thinking]>
39
- Configure default provider/model/thinking (default: openai-codex/gpt-5.5:xhigh)
39
+ Configure default provider/model/thinking (default: openai-codex/gpt-5.5:high)
40
40
  --no-model-scope Skip global model-scope configuration
41
41
  --install-pi / --no-install-pi Install/skip Pi CLI with npm if `pi` is missing (default: install)
42
42
  --force-profile Replace existing project .pi/piagent-profile.json
@@ -78,7 +78,7 @@ WITH_WEB_ACCESS=true
78
78
  WITH_HERDR=true
79
79
  CONFIGURE_MODEL_SCOPE=true
80
80
  MODEL_SCOPE_PRESET="full"
81
- DEFAULT_MODEL="openai-codex/gpt-5.5:xhigh"
81
+ DEFAULT_MODEL="openai-codex/gpt-5.5:high"
82
82
  AUTO_INSTALL_PI=true
83
83
  FORCE_PROFILE=false
84
84
  FORCE_SETTINGS=false
@@ -154,6 +154,9 @@ required_files=(
154
154
  "$ROOT/docs/runtime-policy-design.md"
155
155
  "$ROOT/docs-site/index.html"
156
156
  "$ROOT/docs-site/mcp.html"
157
+ "$ROOT/docs-site/en/index.html"
158
+ "$ROOT/docs-site/en/context-engine.html"
159
+ "$ROOT/docs-site/en/mcp.html"
157
160
  "$ROOT/docs-site/favicon.svg"
158
161
  "$ROOT/docs-site/assets/piagent-logo.svg"
159
162
  "$ROOT/docs-site/assets/docs.css"
@@ -161,6 +164,9 @@ required_files=(
161
164
  "$ROOT/docs-site/content/index.html"
162
165
  "$ROOT/docs-site/content/context-engine.html"
163
166
  "$ROOT/docs-site/content/mcp.html"
167
+ "$ROOT/docs-site/content/en/index.html"
168
+ "$ROOT/docs-site/content/en/context-engine.html"
169
+ "$ROOT/docs-site/content/en/mcp.html"
164
170
  "$ROOT/docs-site/vercel.json"
165
171
  "$ROOT/scripts/build-docs-site.mjs"
166
172
  "$ROOT/scripts/check-architecture.mjs"
@@ -636,7 +642,7 @@ if [[ "$OFFLINE" == true || "${PIAGENT_VERIFY_OFFLINE:-}" == "1" || "${CI:-}" ==
636
642
  else
637
643
  bash "$ROOT/scripts/pi-model-catalog.sh" --json >/dev/null
638
644
  fi
639
- bash "$ROOT/scripts/configure-model-scope.sh" --dry-run --preset full --default-model openai-codex/gpt-5.5:xhigh >/dev/null
645
+ bash "$ROOT/scripts/configure-model-scope.sh" --dry-run --preset full --default-model openai-codex/gpt-5.5:high >/dev/null
640
646
  node "$ROOT/scripts/mcp-manage.mjs" --list >/dev/null
641
647
  node "$ROOT/scripts/mcp-manage.mjs" --dry-run --preset popular --scope project --project "$ROOT" >/dev/null
642
648
  node --input-type=module - "$ROOT" <<'NODE'
@@ -20,6 +20,17 @@ function readJson(relative) {
20
20
  }
21
21
  }
22
22
 
23
+ function filesUnder(relativeDirectory, extension) {
24
+ const directory = path.join(root, relativeDirectory);
25
+ const files = [];
26
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
27
+ const relative = path.join(relativeDirectory, entry.name);
28
+ if (entry.isDirectory()) files.push(...filesUnder(relative, extension));
29
+ else if (entry.isFile() && entry.name.endsWith(extension)) files.push(relative);
30
+ }
31
+ return files;
32
+ }
33
+
23
34
  function parseArguments(values) {
24
35
  const parsed = { tag: "", commit: "" };
25
36
  for (let index = 0; index < values.length; index += 1) {
@@ -73,11 +84,11 @@ if (!changelog.includes(`## ${expectedTag} -`)) fail(`CHANGELOG.md has no ${expe
73
84
 
74
85
  // The badge is generated onto every page, so a stale build shows up as one page
75
86
  // still carrying the previous version rather than as a single missed edit.
76
- const docsPages = fs.readdirSync(path.join(root, "docs-site")).filter((name) => name.endsWith(".html"));
87
+ const docsPages = filesUnder("docs-site", ".html").filter((name) => !name.startsWith("docs-site/content/"));
77
88
  if (docsPages.length === 0) fail("docs-site has no generated pages");
78
89
  for (const page of docsPages) {
79
- const html = fs.readFileSync(path.join(root, "docs-site", page), "utf8");
80
- if (!html.includes(`${expectedTag} docs`)) fail(`docs-site/${page} version badge does not identify ${expectedTag}`);
90
+ const html = fs.readFileSync(path.join(root, page), "utf8");
91
+ if (!html.includes(`${expectedTag} docs`)) fail(`${page} version badge does not identify ${expectedTag}`);
81
92
  }
82
93
 
83
94
  // A version written into an install command does not come from the build, so it
@@ -96,9 +107,7 @@ const RELEASE_TAG_MENTION = /(?:@piagent\/platform@|Vt-mmm\/piagent@v?|--version
96
107
  function proseFiles() {
97
108
  const files = fs.readdirSync(path.join(root, "docs")).filter((name) => name.endsWith(".md")).map((name) => `docs/${name}`);
98
109
  files.push("README.md");
99
- for (const name of fs.readdirSync(path.join(root, "docs-site", "content"))) {
100
- if (name.endsWith(".html")) files.push(`docs-site/content/${name}`);
101
- }
110
+ files.push(...filesUnder("docs-site/content", ".html"));
102
111
  return files.filter((file) => !VERSION_HISTORY_FILES.has(file));
103
112
  }
104
113
 
@@ -2,13 +2,13 @@
2
2
  "defaultProjectTrust": "ask",
3
3
  "defaultProvider": "openai-codex",
4
4
  "defaultModel": "gpt-5.5",
5
- "defaultThinkingLevel": "xhigh",
5
+ "defaultThinkingLevel": "high",
6
6
  "showCacheMissNotices": true,
7
7
  "enabledModels": [
8
8
  "openai-codex/gpt-5.3-codex-spark:minimal",
9
9
  "openai-codex/gpt-5.4-mini:minimal",
10
10
  "openai-codex/gpt-5.4:xhigh",
11
- "openai-codex/gpt-5.5:xhigh",
11
+ "openai-codex/gpt-5.5:high",
12
12
  "openai-codex/gpt-5.6-luna:xhigh",
13
13
  "openai-codex/gpt-5.6-sol:xhigh",
14
14
  "openai-codex/gpt-5.6-terra:xhigh",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "defaultProjectTrust": "ask",
3
- "defaultThinkingLevel": "xhigh",
3
+ "defaultThinkingLevel": "high",
4
4
  "showCacheMissNotices": true,
5
5
  "compaction": {
6
6
  "enabled": true,