@piagent/platform 1.13.1 → 1.15.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 (78) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +2 -2
  3. package/README.vi.md +2 -2
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +6 -6
  6. package/docs/en/windows.md +20 -1
  7. package/docs/languages.json +4 -4
  8. package/docs/model-options.md +16 -0
  9. package/docs/operator-manual-vietnamese.md +7 -7
  10. package/docs/publishing-for-teams.md +2 -0
  11. package/docs/release-install-policy.md +2 -2
  12. package/docs/team-onboarding.md +2 -2
  13. package/docs/vi/windows.md +20 -1
  14. package/docs-site/architecture.html +2 -1
  15. package/docs-site/benchmark.html +2 -1
  16. package/docs-site/commands.html +2 -1
  17. package/docs-site/content/en/index.html +4 -4
  18. package/docs-site/content/en/quickstart.html +1 -1
  19. package/docs-site/content/en/whats-new.html +23 -0
  20. package/docs-site/content/en/windows.html +76 -0
  21. package/docs-site/content/index.html +4 -4
  22. package/docs-site/content/quickstart.html +2 -2
  23. package/docs-site/content/whats-new.html +23 -0
  24. package/docs-site/content/windows.html +76 -0
  25. package/docs-site/context-engine.html +2 -1
  26. package/docs-site/ecosystem.html +2 -1
  27. package/docs-site/en/architecture.html +2 -1
  28. package/docs-site/en/benchmark.html +2 -1
  29. package/docs-site/en/commands.html +2 -1
  30. package/docs-site/en/context-engine.html +2 -1
  31. package/docs-site/en/ecosystem.html +2 -1
  32. package/docs-site/en/index.html +6 -5
  33. package/docs-site/en/mcp-approval.html +2 -1
  34. package/docs-site/en/mcp-auth.html +2 -1
  35. package/docs-site/en/mcp-servers.html +2 -1
  36. package/docs-site/en/mcp.html +2 -1
  37. package/docs-site/en/permissions.html +2 -1
  38. package/docs-site/en/profiles.html +2 -1
  39. package/docs-site/en/quickstart.html +5 -4
  40. package/docs-site/en/runtime.html +2 -1
  41. package/docs-site/en/scope.html +2 -1
  42. package/docs-site/en/security.html +2 -1
  43. package/docs-site/en/team.html +2 -1
  44. package/docs-site/en/whats-new.html +27 -3
  45. package/docs-site/en/windows.html +216 -0
  46. package/docs-site/en/workflows.html +2 -1
  47. package/docs-site/index.html +6 -5
  48. package/docs-site/mcp-approval.html +2 -1
  49. package/docs-site/mcp-auth.html +2 -1
  50. package/docs-site/mcp-servers.html +2 -1
  51. package/docs-site/mcp.html +2 -1
  52. package/docs-site/permissions.html +2 -1
  53. package/docs-site/profiles.html +2 -1
  54. package/docs-site/quickstart.html +6 -5
  55. package/docs-site/runtime.html +2 -1
  56. package/docs-site/scope.html +2 -1
  57. package/docs-site/security.html +2 -1
  58. package/docs-site/team.html +2 -1
  59. package/docs-site/whats-new.html +27 -3
  60. package/docs-site/windows.html +216 -0
  61. package/docs-site/workflows.html +2 -1
  62. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  63. package/package.json +1 -1
  64. package/packages/piagent-core/managed/broker-client.mjs +7 -1
  65. package/packages/piagent-core/managed/helper-run.mjs +2 -2
  66. package/packages/piagent-core/managed/sandbox-diagnostic.mjs +3 -0
  67. package/packages/piagent-core/managed/session.mjs +2 -2
  68. package/packages/piagent-core/managed/tool-boundary.mjs +5 -1
  69. package/packages/piagent-core/package.json +1 -1
  70. package/packages/piagent-core/runtime/managed-failure.mjs +9 -1
  71. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-CyT2G1H2.js → MarkdownMessage-PIk4Femi.js} +1 -1
  72. package/packages/piagent-webui/dist/client/assets/{index-BBEa7Ger.js → index-DeCt-FaN.js} +12 -12
  73. package/packages/piagent-webui/dist/client/index.html +1 -1
  74. package/packages/piagent-webui/gateway/pi-host.ts +16 -2
  75. package/scripts/build-docs-site.mjs +11 -0
  76. package/scripts/configure-model-scope.sh +37 -8
  77. package/scripts/install-global.sh +22 -4
  78. package/scripts/update-global.mjs +7 -1
@@ -7,7 +7,7 @@
7
7
  <meta name="csp-nonce" content="__PIAGENT_CSP_NONCE__" />
8
8
  <meta name="piagent-webui-mode" content="__PIAGENT_WEBUI_MODE__" />
9
9
  <title>Piagent WebUI</title>
10
- <script type="module" crossorigin src="/assets/index-BBEa7Ger.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-DeCt-FaN.js"></script>
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-Z91z6SlN.css">
12
12
  </head>
13
13
  <body>
@@ -28,9 +28,23 @@ export function installedPiHostRoot(): string {
28
28
  } catch {
29
29
  // The operator installation is normally global.
30
30
  }
31
+ const windows = process.platform === "win32";
32
+ if (windows) {
33
+ // npm's global prefix is configurable (CI commonly uses C:\npm\prefix).
34
+ // Read the package beside each PATH entry directly: where.exe may be
35
+ // unavailable in a restricted launcher, and an earlier stale shim must
36
+ // not hide a valid installation later on PATH. Windows env keys ignore case.
37
+ const environment = Object.fromEntries(Object.entries(process.env).map(([key, value]) => [key.toLowerCase(), value]));
38
+ const prefixes = [...(environment.path ?? "").split(";"), environment.npm_config_prefix];
39
+ for (const value of prefixes) {
40
+ const prefix = value?.trim().replace(/^"(.*)"$/, "$1");
41
+ if (!prefix || !path.isAbsolute(prefix)) continue;
42
+ const found = packageRootFrom(path.join(prefix, "node_modules", "@earendil-works", "pi-coding-agent", "package.json"));
43
+ if (found) return found;
44
+ }
45
+ }
31
46
  try {
32
- const windows = process.platform === "win32";
33
- const executable = execFileSync(windows ? "where" : "which", ["pi"], { encoding: "utf8", timeout: 2_000, stdio: ["ignore", "pipe", "ignore"] }).trim().split(/\r?\n/)[0];
47
+ const executable = execFileSync(windows ? "where.exe" : "which", ["pi"], { encoding: "utf8", timeout: 2_000, stdio: ["ignore", "pipe", "ignore"] }).trim().split(/\r?\n/)[0];
34
48
  // npm on Windows puts the pi.cmd shim in the global prefix and the package
35
49
  // in node_modules beside it.
36
50
  const found = packageRootFrom(fs.realpathSync(executable))
@@ -69,6 +69,12 @@ const NAV = [
69
69
  title: "Step by step cho thành viên mới",
70
70
  lead: "Hai lệnh để cài, mở repo, chọn profile và quyền, rồi giao việc bằng lời thường; guard kiểm tra từng tool call."
71
71
  },
72
+ {
73
+ slug: "windows",
74
+ nav: "Cài trên Windows",
75
+ title: "Piagent trên Windows bằng một lệnh",
76
+ 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."
77
+ },
72
78
  {
73
79
  slug: "whats-new",
74
80
  nav: "Có gì mới",
@@ -200,6 +206,11 @@ const EN_PAGE_COPY = {
200
206
  title: "Step by step for new team members",
201
207
  lead: "Install with two commands, open a repository, choose its profile and permissions, then ask in plain language; the guard checks every tool call."
202
208
  },
209
+ windows: {
210
+ nav: "Windows",
211
+ title: "Piagent on Windows with one command",
212
+ 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."
213
+ },
203
214
  "whats-new": {
204
215
  nav: "What’s new",
205
216
  title: "1.9.0: ask in plain language, the team's Harness checks",
@@ -14,6 +14,9 @@ Options:
14
14
  --dry-run Print resulting settings JSON without writing
15
15
  --prune Only remove enabledModels patterns that match no model
16
16
  Pi knows (Pi warns about each on every start); keep the rest
17
+ --keep-member-choices Install/update mode: set the default model and model list
18
+ only where Pi has none yet; otherwise keep the member's and
19
+ add only models this release offers for the first time
17
20
  -h, --help
18
21
 
19
22
  Purpose:
@@ -36,6 +39,7 @@ DEFAULT_MODEL="openai-codex/gpt-6-sol:high"
36
39
  SETTINGS_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/settings.json"
37
40
  DRY_RUN=false
38
41
  PRUNE=false
42
+ KEEP=false
39
43
 
40
44
  # A flag whose value is missing swallows the next flag instead. `--settings
41
45
  # --dry-run` used to set the settings path to the string "--dry-run", leave
@@ -75,6 +79,10 @@ while [[ $# -gt 0 ]]; do
75
79
  PRUNE=true
76
80
  shift
77
81
  ;;
82
+ --keep-member-choices)
83
+ KEEP=true
84
+ shift
85
+ ;;
78
86
  -h|--help)
79
87
  usage
80
88
  exit 0
@@ -102,14 +110,15 @@ case "$PRESET" in
102
110
  ;;
103
111
  esac
104
112
 
105
- PIAGENT_SCOPE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" node --input-type=module - "$SETTINGS_PATH" "$PRESET" "$DEFAULT_MODEL" "$DRY_RUN" <<'NODE'
113
+ PIAGENT_SCOPE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" node --input-type=module - "$SETTINGS_PATH" "$PRESET" "$DEFAULT_MODEL" "$DRY_RUN" "$KEEP" <<'NODE'
106
114
  import fs from "node:fs";
107
115
  import path from "node:path";
108
116
  import { pathToFileURL } from "node:url";
109
117
  const { prepareClaudeModelAdditions } = await import(pathToFileURL(process.env.PIAGENT_SCOPE_ROOT + "/scripts/claude-model-additions.mjs"));
110
118
 
111
- const [settingsPath, preset, defaultModelInput, dryRunRaw] = process.argv.slice(2);
119
+ const [settingsPath, preset, defaultModelInput, dryRunRaw, keepRaw] = process.argv.slice(2);
112
120
  const dryRun = dryRunRaw === "true";
121
+ const keep = keepRaw === "true";
113
122
 
114
123
  const codexModels = [
115
124
  "openai-codex/gpt-5.3-codex-spark:minimal",
@@ -160,10 +169,28 @@ const parsedDefault = parseDefaultModel(defaultModelInput);
160
169
  const settingsBefore = fs.existsSync(settingsPath) ? fs.readFileSync(settingsPath, "utf8") : null;
161
170
  const settings = settingsBefore === null ? {} : JSON.parse(settingsBefore);
162
171
 
163
- settings.defaultProvider = parsedDefault.provider;
164
- settings.defaultModel = parsedDefault.model;
165
- settings.defaultThinkingLevel = parsedDefault.thinking;
166
- settings.enabledModels = enabledModels;
172
+ // What Piagent last offered, so an update adds only models new to this
173
+ // release and never puts back one the member removed.
174
+ const recordPath = path.join(path.dirname(settingsPath), "piagent-model-scope.json");
175
+ let offered = null;
176
+ try { const record = JSON.parse(fs.readFileSync(recordPath, "utf8")); if (Array.isArray(record.offered)) offered = record.offered; } catch { /* none yet */ }
177
+ const notes = [];
178
+ if (!keep || !settings.defaultModel) {
179
+ settings.defaultProvider = parsedDefault.provider;
180
+ settings.defaultModel = parsedDefault.model;
181
+ settings.defaultThinkingLevel = parsedDefault.thinking;
182
+ } else {
183
+ notes.push(`kept the member's default: ${settings.defaultProvider ?? "?"}/${settings.defaultModel}${settings.defaultThinkingLevel ? `:${settings.defaultThinkingLevel}` : ""}`);
184
+ }
185
+ if (!keep || !Array.isArray(settings.enabledModels) || settings.enabledModels.length === 0) {
186
+ settings.enabledModels = enabledModels;
187
+ } else {
188
+ // Installed before this record existed: the member's list stays, and the
189
+ // record starts now.
190
+ const fresh = offered ? enabledModels.filter((model) => !offered.includes(model) && !settings.enabledModels.includes(model)) : [];
191
+ settings.enabledModels = [...settings.enabledModels, ...fresh];
192
+ notes.push(fresh.length ? `kept the member's model list, added ${fresh.join(", ")}` : "kept the member's model list");
193
+ }
167
194
 
168
195
  const catalogEdit = preset === "codex" ? null : prepareClaudeModelAdditions(path.join(path.dirname(settingsPath), "models.json"));
169
196
  const output = `${JSON.stringify(settings, null, 2)}\n`;
@@ -180,9 +207,11 @@ if (dryRun) {
180
207
  }
181
208
  throw error;
182
209
  }
210
+ fs.writeFileSync(recordPath, `${JSON.stringify({ version: 1, preset, offered: enabledModels }, null, 2)}\n`);
183
211
  console.log(`Configured Pi model scope: ${settingsPath}`);
184
212
  console.log(` preset: ${preset}`);
185
- console.log(` default: ${parsedDefault.provider}/${parsedDefault.model}:${parsedDefault.thinking}`);
186
- console.log(` enabledModels: ${enabledModels.length}`);
213
+ console.log(` default: ${settings.defaultProvider}/${settings.defaultModel}:${settings.defaultThinkingLevel}`);
214
+ console.log(` enabledModels: ${settings.enabledModels.length}`);
215
+ for (const note of notes) console.log(` ${note}`);
187
216
  }
188
217
  NODE
@@ -24,7 +24,11 @@ Options:
24
24
  Install pi-web-access for the researcher subagent (default: install).
25
25
  --with-herdr Also configure Herdr integration.
26
26
  --model-scope <full|codex|claude>
27
- Model families to enable in Pi (default: full).
27
+ Model families to enable in Pi (default: full). A member who
28
+ already has a default model and model list keeps them; an
29
+ update only adds models new to this release.
30
+ --reset-model-scope Replace the member's default model and model list with the
31
+ preset (what every update did before 1.14.0).
28
32
  --no-model-scope Leave the user's enabledModels untouched.
29
33
  --default-model <provider/model[:thinking]>
30
34
  Default model written to Pi settings
@@ -90,6 +94,7 @@ SUBAGENTS_MODEL_SCOPE="none"
90
94
  WITH_WEB_ACCESS=true
91
95
  WITH_HERDR=false
92
96
  CONFIGURE_MODEL_SCOPE=true
97
+ RESET_MODEL_SCOPE=false
93
98
  MODEL_SCOPE_PRESET="full"
94
99
  DEFAULT_MODEL="openai-codex/gpt-6-sol:high"
95
100
  DRY_RUN=false
@@ -135,7 +140,12 @@ detect_runtime_surface() {
135
140
  arch_name="$(uname -m 2>/dev/null || echo unknown)"
136
141
 
137
142
  if [[ "$os_name" == "Linux" && -r /proc/version ]] && grep -qiE "microsoft|wsl" /proc/version 2>/dev/null; then
138
- printf 'wsl2/%s (experimental; not release-gated)' "$arch_name"
143
+ # WSL2 is how Windows runs Piagent (since 1.13.0): the Windows setup and
144
+ # the bubblewrap sandbox run in CI on x86_64.
145
+ case "$arch_name" in
146
+ x86_64|amd64) printf 'wsl2/%s (verified in CI: Windows setup and the bubblewrap sandbox)' "$arch_name" ;;
147
+ *) printf 'wsl2/%s (supported target; run smoke before team rollout)' "$arch_name" ;;
148
+ esac
139
149
  return
140
150
  fi
141
151
 
@@ -153,7 +163,7 @@ detect_runtime_surface() {
153
163
  printf 'linux-arm64 (supported target; run smoke before team rollout)'
154
164
  ;;
155
165
  MINGW*|MSYS*|CYGWIN*)
156
- printf 'native-windows/%s (not release-gated; terminal helpers require Bash semantics)' "$arch_name"
166
+ printf 'native-windows/%s (personal-mode preview, not release-gated; teams use WSL2)' "$arch_name"
157
167
  ;;
158
168
  *)
159
169
  printf '%s/%s (outside v1.2.0 release matrix)' "$os_name" "$arch_name"
@@ -530,6 +540,10 @@ while [[ $# -gt 0 ]]; do
530
540
  DEFAULT_MODEL="$2"
531
541
  shift 2
532
542
  ;;
543
+ --reset-model-scope)
544
+ RESET_MODEL_SCOPE=true
545
+ shift
546
+ ;;
533
547
  --no-model-scope)
534
548
  CONFIGURE_MODEL_SCOPE=false
535
549
  shift
@@ -750,7 +764,11 @@ fi
750
764
 
751
765
  if [[ "$CONFIGURE_MODEL_SCOPE" == true ]]; then
752
766
  echo "Configuring Pi model selector scope:"
753
- run_cmd bash "$PLATFORM_ROOT/scripts/configure-model-scope.sh" --preset "$MODEL_SCOPE_PRESET" --default-model "$DEFAULT_MODEL"
767
+ if [[ "$RESET_MODEL_SCOPE" == true ]]; then
768
+ run_cmd bash "$PLATFORM_ROOT/scripts/configure-model-scope.sh" --preset "$MODEL_SCOPE_PRESET" --default-model "$DEFAULT_MODEL"
769
+ else
770
+ run_cmd bash "$PLATFORM_ROOT/scripts/configure-model-scope.sh" --preset "$MODEL_SCOPE_PRESET" --default-model "$DEFAULT_MODEL" --keep-member-choices
771
+ fi
754
772
  fi
755
773
 
756
774
  if [[ "$WITH_HERDR" == true ]]; then
@@ -423,7 +423,8 @@ function main() {
423
423
  fail(`${HELPER_PACKAGE}@${targetHelper} does not pin an exact ${HOST_PACKAGE} version, so the host cannot be updated safely.`);
424
424
  }
425
425
 
426
- const helperNeedsChange = options.force || currentHelper !== targetHelper;
426
+ // npm exec is temporary even when its version already matches the target.
427
+ const helperNeedsChange = options.force || currentHelper !== targetHelper || isNpxCacheRoot(platformRoot);
427
428
  const hostNeedsChange = options.host && (options.force || currentHost !== targetHost);
428
429
 
429
430
  console.log("Pi Agent Platform update");
@@ -475,6 +476,11 @@ function main() {
475
476
  }
476
477
  }
477
478
 
479
+ // Refuse an impossible package installation before replacing either global.
480
+ if (options.package && !options.host && effectiveHost !== targetHost) {
481
+ fail(`--no-host cannot install this Pi package: active Pi is ${effectiveHost ?? "missing"}, but v${targetHelper} requires ${targetHost}. Rerun without --no-host.`);
482
+ }
483
+
478
484
  // Host first: piagent-install refuses to run against a host that does not match
479
485
  // the version its package.json pins, so a helper installed ahead of the host
480
486
  // would fail on its very next step.