@earendil-works/pi-coding-agent 0.79.2 → 0.79.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 (64) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/cli/startup-ui.d.ts.map +1 -1
  3. package/dist/cli/startup-ui.js +19 -14
  4. package/dist/cli/startup-ui.js.map +1 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +14 -3
  7. package/dist/config.js.map +1 -1
  8. package/dist/core/model-registry.d.ts.map +1 -1
  9. package/dist/core/model-registry.js +4 -57
  10. package/dist/core/model-registry.js.map +1 -1
  11. package/dist/core/package-manager.d.ts +1 -1
  12. package/dist/core/package-manager.d.ts.map +1 -1
  13. package/dist/core/package-manager.js +31 -17
  14. package/dist/core/package-manager.js.map +1 -1
  15. package/dist/core/resolve-config-value.d.ts +0 -1
  16. package/dist/core/resolve-config-value.d.ts.map +1 -1
  17. package/dist/core/resolve-config-value.js +0 -4
  18. package/dist/core/resolve-config-value.js.map +1 -1
  19. package/dist/core/tools/bash.d.ts.map +1 -1
  20. package/dist/core/tools/bash.js +4 -0
  21. package/dist/core/tools/bash.js.map +1 -1
  22. package/dist/main.d.ts.map +1 -1
  23. package/dist/main.js +1 -0
  24. package/dist/main.js.map +1 -1
  25. package/dist/migrations.d.ts.map +1 -1
  26. package/dist/migrations.js +1 -123
  27. package/dist/migrations.js.map +1 -1
  28. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  29. package/dist/modes/interactive/components/tree-selector.js +87 -12
  30. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  31. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  32. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  33. package/dist/modes/interactive/interactive-mode.js +38 -18
  34. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  35. package/dist/modes/interactive/theme/theme.d.ts +12 -8
  36. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  37. package/dist/modes/interactive/theme/theme.js +19 -41
  38. package/dist/modes/interactive/theme/theme.js.map +1 -1
  39. package/dist/utils/child-process.d.ts +7 -4
  40. package/dist/utils/child-process.d.ts.map +1 -1
  41. package/dist/utils/child-process.js +23 -5
  42. package/dist/utils/child-process.js.map +1 -1
  43. package/dist/utils/version-check.d.ts.map +1 -1
  44. package/dist/utils/version-check.js +4 -27
  45. package/dist/utils/version-check.js.map +1 -1
  46. package/docs/containerization.md +35 -35
  47. package/docs/extensions.md +11 -5
  48. package/docs/index.md +1 -1
  49. package/docs/models.md +1 -3
  50. package/docs/providers.md +2 -2
  51. package/docs/security.md +1 -1
  52. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  53. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  54. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  55. package/examples/extensions/gondolin/package-lock.json +2 -2
  56. package/examples/extensions/gondolin/package.json +1 -1
  57. package/examples/extensions/question.ts +39 -18
  58. package/examples/extensions/questionnaire.ts +49 -28
  59. package/examples/extensions/sandbox/package-lock.json +2 -2
  60. package/examples/extensions/sandbox/package.json +1 -1
  61. package/examples/extensions/with-deps/package-lock.json +2 -2
  62. package/examples/extensions/with-deps/package.json +1 -1
  63. package/npm-shrinkwrap.json +25 -12
  64. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { getPiUserAgent } from \"./pi-user-agent.ts\";\n\nconst LATEST_VERSION_URL = \"https://pi.dev/api/latest-version\";\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestPiRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\ninterface ParsedVersion {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\tprerelease?: string;\n}\n\nfunction parsePackageVersion(version: string): ParsedVersion | undefined {\n\tconst match = version.trim().match(/^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?(?:\\+.*)?$/);\n\tif (!match) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tmajor: Number.parseInt(match[1], 10),\n\t\tminor: Number.parseInt(match[2], 10),\n\t\tpatch: Number.parseInt(match[3], 10),\n\t\tprerelease: match[4],\n\t};\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = parsePackageVersion(leftVersion);\n\tconst right = parsePackageVersion(rightVersion);\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\n\tif (left.major !== right.major) return left.major - right.major;\n\tif (left.minor !== right.minor) return left.minor - right.minor;\n\tif (left.patch !== right.patch) return left.patch - right.patch;\n\tif (left.prerelease === right.prerelease) return 0;\n\tif (!left.prerelease) return 1;\n\tif (!right.prerelease) return -1;\n\treturn left.prerelease.localeCompare(right.prerelease);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestPiRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<LatestPiRelease | undefined> {\n\tif (process.env.PI_SKIP_VERSION_CHECK || process.env.PI_OFFLINE) return undefined;\n\n\tconst response = await fetch(LATEST_VERSION_URL, {\n\t\theaders: {\n\t\t\t\"User-Agent\": getPiUserAgent(currentVersion),\n\t\t\taccept: \"application/json\",\n\t\t},\n\t\tsignal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS),\n\t});\n\tif (!response.ok) return undefined;\n\n\tconst data = (await response.json()) as {\n\t\tpackageName?: unknown;\n\t\tversion?: unknown;\n\t\tnote?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName =\n\t\ttypeof data.packageName === \"string\" && data.packageName.trim() ? data.packageName.trim() : undefined;\n\tconst note = typeof data.note === \"string\" && data.note.trim() ? data.note.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\tpackageName,\n\t\t...(note ? { note } : {}),\n\t};\n}\n\nexport async function getLatestPiVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestPiRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewPiVersion(currentVersion: string): Promise<LatestPiRelease | undefined> {\n\ttry {\n\t\tconst latestRelease = await getLatestPiRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAC/D,MAAM,gCAAgC,GAAG,KAAK,CAAC;AAQ/C,MAAM,UAAU,sBAAsB,CAAC,WAAmB,EAAE,YAAoB,EAAsB;IACrG,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,qBAAqB,CAAC,gBAAwB,EAAE,cAAsB,EAAW;IAChG,MAAM,UAAU,GAAG,sBAAsB,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC;IAC5E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,UAAU,GAAG,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,EAAE,KAAK,cAAc,CAAC,IAAI,EAAE,CAAC;AAAA,CACzD;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,cAAsB,EACtB,OAAO,GAA2B,EAAE,EACG;IACvC,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAElF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,kBAAkB,EAAE;QAChD,OAAO,EAAE;YACR,YAAY,EAAE,cAAc,CAAC,cAAc,CAAC;YAC5C,MAAM,EAAE,kBAAkB;SAC1B;QACD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,IAAI,gCAAgC,CAAC;KAClF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAEnC,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAIlC,CAAC;IACF,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9D,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,WAAW,GAChB,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACvG,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,OAAO;QACN,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;QAC5B,WAAW;QACX,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzB,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,cAAsB,EACtB,OAAO,GAA2B,EAAE,EACN;IAC9B,OAAO,CAAC,MAAM,kBAAkB,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC;AAAA,CACpE;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,cAAsB,EAAwC;IACxG,IAAI,CAAC;QACJ,MAAM,aAAa,GAAG,MAAM,kBAAkB,CAAC,cAAc,CAAC,CAAC;QAC/D,IAAI,aAAa,IAAI,qBAAqB,CAAC,aAAa,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC;YACnF,OAAO,aAAa,CAAC;QACtB,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD","sourcesContent":["import { compare, valid } from \"semver\";\nimport { getPiUserAgent } from \"./pi-user-agent.ts\";\n\nconst LATEST_VERSION_URL = \"https://pi.dev/api/latest-version\";\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestPiRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestPiRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<LatestPiRelease | undefined> {\n\tif (process.env.PI_SKIP_VERSION_CHECK || process.env.PI_OFFLINE) return undefined;\n\n\tconst response = await fetch(LATEST_VERSION_URL, {\n\t\theaders: {\n\t\t\t\"User-Agent\": getPiUserAgent(currentVersion),\n\t\t\taccept: \"application/json\",\n\t\t},\n\t\tsignal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS),\n\t});\n\tif (!response.ok) return undefined;\n\n\tconst data = (await response.json()) as {\n\t\tpackageName?: unknown;\n\t\tversion?: unknown;\n\t\tnote?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName =\n\t\ttypeof data.packageName === \"string\" && data.packageName.trim() ? data.packageName.trim() : undefined;\n\tconst note = typeof data.note === \"string\" && data.note.trim() ? data.note.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\tpackageName,\n\t\t...(note ? { note } : {}),\n\t};\n}\n\nexport async function getLatestPiVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestPiRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewPiVersion(currentVersion: string): Promise<LatestPiRelease | undefined> {\n\ttry {\n\t\tconst latestRelease = await getLatestPiRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -10,46 +10,12 @@ There are two general options. You can either
10
10
 
11
11
  | Pattern | What is isolated | Best for | Notes |
12
12
  | --- | --- | --- | --- |
13
- | OpenShell | Whole `pi` process in a policy-controlled sandbox | Local or remote managed sandbox | Requires an OpenShell gateway |
14
13
  | Gondolin extension | Built-in tools and `!` commands | Local micro-VM isolation while keeping auth on host | See [`examples/extensions/gondolin/`](../examples/extensions/gondolin/). |
15
14
  | Plain Docker | Whole `pi` process in a local container | Simple local isolation | Provider API keys enter the container. |
15
+ | OpenShell | Whole `pi` process in a policy-controlled sandbox | Local or remote managed sandbox | Requires an OpenShell gateway |
16
16
 
17
17
  Extensions run wherever the `pi` process runs. If you run host `pi` with a tool-routing extension, other custom extension tools still run on the host unless they also delegate their operations.
18
18
 
19
- ## OpenShell
20
-
21
- Use [NVIDIA OpenShell](https://docs.nvidia.com/openshell/about/overview) when you want a policy-controlled sandbox with filesystem, process, network, credential, and inference controls.
22
- OpenShell can run sandboxes through a local gateway backed by Docker, Podman, or a VM runtime, or through a remote Kubernetes gateway.
23
-
24
- Every sandbox requires an active gateway.
25
- Register and select one before creating a sandbox:
26
-
27
- ```bash
28
- openshell gateway add <gateway-url> --name <name>
29
- openshell gateway select <name>
30
- ```
31
-
32
- Launch `pi` inside an OpenShell sandbox:
33
-
34
- ```bash
35
- openshell sandbox create --name pi-sandbox --from pi -- pi
36
- ```
37
-
38
- In this pattern, the whole `pi` process runs inside the sandbox.
39
- Built-in tools, `!` commands, and extension tools execute inside the OpenShell boundary.
40
-
41
- If the gateway is remote, project files are not bind-mounted from the host, meaning writes in the sandbox are not reflected on your machine.
42
- Clone the repository inside the sandbox or use OpenShell file transfer commands:
43
-
44
- ```bash
45
- openshell sandbox upload pi-sandbox ./repo /workspace
46
- openshell sandbox download pi-sandbox /workspace/repo ./repo-out
47
- ```
48
-
49
- OpenShell providers can keep raw model API keys outside the sandbox.
50
- When inference routing is configured, code inside the sandbox can call `https://inference.local`, and the gateway injects the configured provider credentials upstream.
51
- Configure Pi to use the corresponding OpenAI-compatible or Anthropic-compatible endpoint if you want model traffic to use this route.
52
-
53
19
  ## Gondolin
54
20
 
55
21
  [Gondolin](https://github.com/earendil-works/gondolin) is a local Linux micro-VM.
@@ -109,3 +75,37 @@ docker run --rm -it \
109
75
  The `-v "$PWD:/workspace"` mounts your current directory into the container at /workspace such that reads and writes in `/workspace` inside Docker directly affect your host files, like in the Gondolin example.
110
76
 
111
77
  Use a named volume for `/root/.pi/agent` if you want container-local settings and sessions. Mounting your host `~/.pi/agent` exposes host auth and session files to the container.
78
+
79
+ ## OpenShell
80
+
81
+ Use [NVIDIA OpenShell](https://docs.nvidia.com/openshell/about/overview) when you want a policy-controlled sandbox with filesystem, process, network, credential, and inference controls.
82
+ OpenShell can run sandboxes through a local gateway backed by Docker, Podman, or a VM runtime, or through a remote Kubernetes gateway.
83
+
84
+ Every sandbox requires an active gateway.
85
+ Register and select one before creating a sandbox:
86
+
87
+ ```bash
88
+ openshell gateway add <gateway-url> --name <name>
89
+ openshell gateway select <name>
90
+ ```
91
+
92
+ Launch `pi` inside an OpenShell sandbox:
93
+
94
+ ```bash
95
+ openshell sandbox create --name pi-sandbox --from pi -- pi
96
+ ```
97
+
98
+ In this pattern, the whole `pi` process runs inside the sandbox.
99
+ Built-in tools, `!` commands, and extension tools execute inside the OpenShell boundary.
100
+
101
+ If the gateway is remote, project files are not bind-mounted from the host, meaning writes in the sandbox are not reflected on your machine.
102
+ Clone the repository inside the sandbox or use OpenShell file transfer commands:
103
+
104
+ ```bash
105
+ openshell sandbox upload pi-sandbox ./repo /workspace
106
+ openshell sandbox download pi-sandbox /workspace/repo ./repo-out
107
+ ```
108
+
109
+ OpenShell providers can keep raw model API keys outside the sandbox.
110
+ When inference routing is configured, code inside the sandbox can call `https://inference.local`, and the gateway injects the configured provider credentials upstream.
111
+ Configure Pi to use the corresponding OpenAI-compatible or Anthropic-compatible endpoint if you want model traffic to use this route.
@@ -216,6 +216,12 @@ export default async function (pi: ExtensionAPI) {
216
216
 
217
217
  This pattern makes the fetched models available during normal startup and to `pi --list-models`.
218
218
 
219
+ ### Long-lived resources and shutdown
220
+
221
+ Extension factories may run in invocations that never start a session. Do not start background resources such as processes, sockets, file watchers, or timers from the factory.
222
+
223
+ Defer background resource startup until `session_start` or the command/tool/event that needs the resource. Register an idempotent `session_shutdown` handler to close any session-scoped resources you start.
224
+
219
225
  ### Extension Styles
220
226
 
221
227
  **Single file** - simplest, for small extensions:
@@ -471,7 +477,7 @@ pi.on("session_tree", async (event, ctx) => {
471
477
 
472
478
  #### session_shutdown
473
479
 
474
- Fired before an extension runtime is torn down.
480
+ Fired before a started session runtime is torn down. Use this to clean up resources opened from `session_start` or other session-scoped hooks.
475
481
 
476
482
  ```typescript
477
483
  pi.on("session_shutdown", async (event, ctx) => {
@@ -1528,21 +1534,21 @@ const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
1528
1534
 
1529
1535
  ### pi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(names)
1530
1536
 
1531
- Manage active tools. This works for both built-in tools and dynamically registered tools.
1537
+ Manage active tools. This works for both built-in tools and dynamically registered tools. `pi.getActiveTools()` returns the active tool names as `string[]`; `pi.getAllTools()` returns metadata for all configured tools.
1532
1538
 
1533
1539
  ```typescript
1534
- const active = pi.getActiveTools();
1540
+ const active = pi.getActiveTools(); // ["read", "bash", ...]
1535
1541
  const all = pi.getAllTools();
1536
- // [{
1542
+ // all = [{
1537
1543
  // name: "read",
1538
1544
  // description: "Read file contents...",
1539
1545
  // parameters: ...,
1540
1546
  // promptGuidelines: ["Use read to examine files instead of cat or sed."],
1541
1547
  // sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
1542
1548
  // }, ...]
1543
- const names = all.map(t => t.name);
1544
1549
  const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
1545
1550
  const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
1551
+ pi.setActiveTools([...new Set([...active, "my_custom_tool"])]); // Keep current tools and enable my_custom_tool
1546
1552
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
1547
1553
  ```
1548
1554
 
package/docs/index.md CHANGED
@@ -42,7 +42,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
42
42
  - [Using Pi](usage.md) - interactive mode, slash commands, context files, and CLI reference.
43
43
  - [Providers](providers.md) - subscription and API-key setup for built-in providers.
44
44
  - [Security](security.md) - project trust, sandbox boundaries, and vulnerability reporting.
45
- - [Containerization](containerization.md) - sandbox pi with OpenShell, Gondolin, or Docker.
45
+ - [Containerization](containerization.md) - sandbox pi with Gondolin, Docker, or OpenShell.
46
46
  - [Settings](settings.md) - global and project settings.
47
47
  - [Keybindings](keybindings.md) - default shortcuts and custom keybindings.
48
48
  - [Sessions](sessions.md) - session management, branching, and tree navigation.
package/docs/models.md CHANGED
@@ -161,13 +161,11 @@ The `apiKey` and `headers` fields support command execution, environment interpo
161
161
  "apiKey": "$$literal-dollar-prefix"
162
162
  "apiKey": "$!literal-bang-prefix"
163
163
  ```
164
- - **Literal value:** Used directly
164
+ - **Literal value:** Used directly. Plain uppercase strings such as `MY_API_KEY` are literals; use `$MY_API_KEY` for environment variables.
165
165
  ```json
166
166
  "apiKey": "sk-..."
167
167
  ```
168
168
 
169
- Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup.
170
-
171
169
  For `models.json`, shell commands are resolved at request time. pi intentionally does not apply built-in TTL, stale reuse, or recovery logic for arbitrary commands. Different commands need different caching and failure strategies, and pi cannot infer the right one.
172
170
 
173
171
  If your command is slow, expensive, rate-limited, or should keep using a previous value on transient failures, wrap it in your own script or command that implements the caching or TTL behavior you want.
package/docs/providers.md CHANGED
@@ -124,13 +124,13 @@ The `key` field supports command execution, environment interpolation, and liter
124
124
  { "type": "api_key", "key": "$$literal-dollar-prefix" }
125
125
  { "type": "api_key", "key": "$!literal-bang-prefix" }
126
126
  ```
127
- - **Literal value:** Used directly
127
+ - **Literal value:** Used directly. Plain uppercase strings such as `MY_API_KEY` are literals; use `$MY_API_KEY` for environment variables.
128
128
  ```json
129
129
  { "type": "api_key", "key": "sk-ant-..." }
130
130
  { "type": "api_key", "key": "public" }
131
131
  ```
132
132
 
133
- Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup. OAuth credentials are also stored here after `/login` and managed automatically.
133
+ OAuth credentials are also stored here after `/login` and managed automatically.
134
134
 
135
135
  ## Cloud Providers
136
136
 
package/docs/security.md CHANGED
@@ -42,7 +42,7 @@ For untrusted repositories, generated code you do not intend to monitor closely,
42
42
 
43
43
  Common patterns are documented in [Containerization](containerization.md):
44
44
 
45
- - run the whole `pi` process inside OpenShell or Docker
45
+ - run the whole `pi` process inside a container/sandbox
46
46
  - run host pi while routing built-in tool execution into a Gondolin micro-VM
47
47
  - mount only the workspace paths the agent should access
48
48
  - avoid mounting host `~/.pi/agent` unless the container should access host sessions, settings, and credentials
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.79.2",
3
+ "version": "0.79.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.79.2",
9
+ "version": "0.79.4",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.79.2",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.79.2",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
- "version": "0.79.2",
3
+ "version": "0.79.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "0.79.2",
9
+ "version": "0.79.4",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
3
  "private": true,
4
- "version": "0.79.2",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -5,7 +5,15 @@
5
5
  */
6
6
 
7
7
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
- import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
8
+ import {
9
+ Editor,
10
+ type EditorTheme,
11
+ Key,
12
+ matchesKey,
13
+ Text,
14
+ visibleWidth,
15
+ wrapTextWithAnsi,
16
+ } from "@earendil-works/pi-tui";
9
17
  import { Type } from "typebox";
10
18
 
11
19
  interface OptionWithDesc {
@@ -139,10 +147,27 @@ export default function question(pi: ExtensionAPI) {
139
147
  if (cachedLines) return cachedLines;
140
148
 
141
149
  const lines: string[] = [];
142
- const add = (s: string) => lines.push(truncateToWidth(s, width));
150
+ const renderWidth = Math.max(1, width);
143
151
 
144
- add(theme.fg("accent", "─".repeat(width)));
145
- add(theme.fg("text", ` ${params.question}`));
152
+ function addWrapped(text: string) {
153
+ lines.push(...wrapTextWithAnsi(text, renderWidth));
154
+ }
155
+
156
+ function addWrappedWithPrefix(prefix: string, text: string) {
157
+ const prefixWidth = visibleWidth(prefix);
158
+ if (prefixWidth >= renderWidth) {
159
+ addWrapped(prefix + text);
160
+ return;
161
+ }
162
+ const wrapped = wrapTextWithAnsi(text, renderWidth - prefixWidth);
163
+ const continuationPrefix = " ".repeat(prefixWidth);
164
+ for (let i = 0; i < wrapped.length; i++) {
165
+ lines.push(`${i === 0 ? prefix : continuationPrefix}${wrapped[i]}`);
166
+ }
167
+ }
168
+
169
+ lines.push(theme.fg("accent", "─".repeat(renderWidth)));
170
+ addWrappedWithPrefix(" ", theme.fg("text", params.question));
146
171
  lines.push("");
147
172
 
148
173
  for (let i = 0; i < allOptions.length; i++) {
@@ -150,36 +175,32 @@ export default function question(pi: ExtensionAPI) {
150
175
  const selected = i === optionIndex;
151
176
  const isOther = opt.isOther === true;
152
177
  const prefix = selected ? theme.fg("accent", "> ") : " ";
178
+ const label = `${i + 1}. ${opt.label}${isOther && editMode ? " ✎" : ""}`;
179
+ const color = selected || (isOther && editMode) ? "accent" : "text";
153
180
 
154
- if (isOther && editMode) {
155
- add(prefix + theme.fg("accent", `${i + 1}. ${opt.label} ✎`));
156
- } else if (selected) {
157
- add(prefix + theme.fg("accent", `${i + 1}. ${opt.label}`));
158
- } else {
159
- add(` ${theme.fg("text", `${i + 1}. ${opt.label}`)}`);
160
- }
181
+ addWrappedWithPrefix(prefix, theme.fg(color, label));
161
182
 
162
183
  // Show description if present
163
184
  if (opt.description) {
164
- add(` ${theme.fg("muted", opt.description)}`);
185
+ addWrappedWithPrefix(" ", theme.fg("muted", opt.description));
165
186
  }
166
187
  }
167
188
 
168
189
  if (editMode) {
169
190
  lines.push("");
170
- add(theme.fg("muted", " Your answer:"));
171
- for (const line of editor.render(width - 2)) {
172
- add(` ${line}`);
191
+ addWrappedWithPrefix(" ", theme.fg("muted", "Your answer:"));
192
+ for (const line of editor.render(Math.max(1, renderWidth - 2))) {
193
+ lines.push(` ${line}`);
173
194
  }
174
195
  }
175
196
 
176
197
  lines.push("");
177
198
  if (editMode) {
178
- add(theme.fg("dim", " Enter to submit • Esc to go back"));
199
+ addWrappedWithPrefix(" ", theme.fg("dim", "Enter to submit • Esc to go back"));
179
200
  } else {
180
- add(theme.fg("dim", " ↑↓ navigate • Enter to select • Esc to cancel"));
201
+ addWrappedWithPrefix(" ", theme.fg("dim", "↑↓ navigate • Enter to select • Esc to cancel"));
181
202
  }
182
- add(theme.fg("accent", "─".repeat(width)));
203
+ lines.push(theme.fg("accent", "─".repeat(renderWidth)));
183
204
 
184
205
  cachedLines = lines;
185
206
  return lines;
@@ -6,7 +6,15 @@
6
6
  */
7
7
 
8
8
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
- import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
9
+ import {
10
+ Editor,
11
+ type EditorTheme,
12
+ Key,
13
+ matchesKey,
14
+ Text,
15
+ visibleWidth,
16
+ wrapTextWithAnsi,
17
+ } from "@earendil-works/pi-tui";
10
18
  import { Type } from "typebox";
11
19
 
12
20
  // Types
@@ -259,13 +267,28 @@ export default function questionnaire(pi: ExtensionAPI) {
259
267
  if (cachedLines) return cachedLines;
260
268
 
261
269
  const lines: string[] = [];
270
+ const renderWidth = Math.max(1, width);
262
271
  const q = currentQuestion();
263
272
  const opts = currentOptions();
264
273
 
265
- // Helper to add truncated line
266
- const add = (s: string) => lines.push(truncateToWidth(s, width));
274
+ function addWrapped(text: string) {
275
+ lines.push(...wrapTextWithAnsi(text, renderWidth));
276
+ }
277
+
278
+ function addWrappedWithPrefix(prefix: string, text: string) {
279
+ const prefixWidth = visibleWidth(prefix);
280
+ if (prefixWidth >= renderWidth) {
281
+ addWrapped(prefix + text);
282
+ return;
283
+ }
284
+ const wrapped = wrapTextWithAnsi(text, renderWidth - prefixWidth);
285
+ const continuationPrefix = " ".repeat(prefixWidth);
286
+ for (let i = 0; i < wrapped.length; i++) {
287
+ lines.push(`${i === 0 ? prefix : continuationPrefix}${wrapped[i]}`);
288
+ }
289
+ }
267
290
 
268
- add(theme.fg("accent", "─".repeat(width)));
291
+ lines.push(theme.fg("accent", "─".repeat(renderWidth)));
269
292
 
270
293
  // Tab bar (multi-question only)
271
294
  if (isMulti) {
@@ -287,7 +310,7 @@ export default function questionnaire(pi: ExtensionAPI) {
287
310
  ? theme.bg("selectedBg", theme.fg("text", submitText))
288
311
  : theme.fg(canSubmit ? "success" : "dim", submitText);
289
312
  tabs.push(`${submitStyled} →`);
290
- add(` ${tabs.join("")}`);
313
+ addWrappedWithPrefix(" ", tabs.join(""));
291
314
  lines.push("");
292
315
  }
293
316
 
@@ -298,54 +321,52 @@ export default function questionnaire(pi: ExtensionAPI) {
298
321
  const selected = i === optionIndex;
299
322
  const isOther = opt.isOther === true;
300
323
  const prefix = selected ? theme.fg("accent", "> ") : " ";
301
- const color = selected ? "accent" : "text";
302
- // Mark "Type something" differently when in input mode
303
- if (isOther && inputMode) {
304
- add(prefix + theme.fg("accent", `${i + 1}. ${opt.label} ✎`));
305
- } else {
306
- add(prefix + theme.fg(color, `${i + 1}. ${opt.label}`));
307
- }
324
+ const label = `${i + 1}. ${opt.label}${isOther && inputMode ? "" : ""}`;
325
+ const color = selected || (isOther && inputMode) ? "accent" : "text";
326
+
327
+ addWrappedWithPrefix(prefix, theme.fg(color, label));
308
328
  if (opt.description) {
309
- add(` ${theme.fg("muted", opt.description)}`);
329
+ addWrappedWithPrefix(" ", theme.fg("muted", opt.description));
310
330
  }
311
331
  }
312
332
  }
313
333
 
314
334
  // Content
315
335
  if (inputMode && q) {
316
- add(theme.fg("text", ` ${q.prompt}`));
336
+ addWrappedWithPrefix(" ", theme.fg("text", q.prompt));
317
337
  lines.push("");
318
338
  // Show options for reference
319
339
  renderOptions();
320
340
  lines.push("");
321
- add(theme.fg("muted", " Your answer:"));
322
- for (const line of editor.render(width - 2)) {
323
- add(` ${line}`);
341
+ addWrappedWithPrefix(" ", theme.fg("muted", "Your answer:"));
342
+ for (const line of editor.render(Math.max(1, renderWidth - 2))) {
343
+ lines.push(` ${line}`);
324
344
  }
325
345
  lines.push("");
326
- add(theme.fg("dim", " Enter to submit • Esc to cancel"));
346
+ addWrappedWithPrefix(" ", theme.fg("dim", "Enter to submit • Esc to cancel"));
327
347
  } else if (currentTab === questions.length) {
328
- add(theme.fg("accent", theme.bold(" Ready to submit")));
348
+ addWrappedWithPrefix(" ", theme.fg("accent", theme.bold("Ready to submit")));
329
349
  lines.push("");
330
350
  for (const question of questions) {
331
351
  const answer = answers.get(question.id);
332
352
  if (answer) {
333
353
  const prefix = answer.wasCustom ? "(wrote) " : "";
334
- add(`${theme.fg("muted", ` ${question.label}: `)}${theme.fg("text", prefix + answer.label)}`);
354
+ const summary = `${theme.fg("muted", `${question.label}: `)}${theme.fg("text", prefix + answer.label)}`;
355
+ addWrappedWithPrefix(" ", summary);
335
356
  }
336
357
  }
337
358
  lines.push("");
338
359
  if (allAnswered()) {
339
- add(theme.fg("success", " Press Enter to submit"));
360
+ addWrappedWithPrefix(" ", theme.fg("success", "Press Enter to submit"));
340
361
  } else {
341
362
  const missing = questions
342
363
  .filter((q) => !answers.has(q.id))
343
364
  .map((q) => q.label)
344
365
  .join(", ");
345
- add(theme.fg("warning", ` Unanswered: ${missing}`));
366
+ addWrappedWithPrefix(" ", theme.fg("warning", `Unanswered: ${missing}`));
346
367
  }
347
368
  } else if (q) {
348
- add(theme.fg("text", ` ${q.prompt}`));
369
+ addWrappedWithPrefix(" ", theme.fg("text", q.prompt));
349
370
  lines.push("");
350
371
  renderOptions();
351
372
  }
@@ -353,11 +374,11 @@ export default function questionnaire(pi: ExtensionAPI) {
353
374
  lines.push("");
354
375
  if (!inputMode) {
355
376
  const help = isMulti
356
- ? " Tab/←→ navigate • ↑↓ select • Enter confirm • Esc cancel"
357
- : " ↑↓ navigate • Enter select • Esc cancel";
358
- add(theme.fg("dim", help));
377
+ ? "Tab/←→ navigate • ↑↓ select • Enter confirm • Esc cancel"
378
+ : "↑↓ navigate • Enter select • Esc cancel";
379
+ addWrappedWithPrefix(" ", theme.fg("dim", help));
359
380
  }
360
- add(theme.fg("accent", "─".repeat(width)));
381
+ lines.push(theme.fg("accent", "─".repeat(renderWidth)));
361
382
 
362
383
  cachedLines = lines;
363
384
  return lines;
@@ -400,7 +421,7 @@ export default function questionnaire(pi: ExtensionAPI) {
400
421
  let text = theme.fg("toolTitle", theme.bold("questionnaire "));
401
422
  text += theme.fg("muted", `${count} question${count !== 1 ? "s" : ""}`);
402
423
  if (labels) {
403
- text += theme.fg("dim", ` (${truncateToWidth(labels, 40)})`);
424
+ text += theme.fg("dim", ` (${labels})`);
404
425
  }
405
426
  return new Text(text, 0, 0);
406
427
  },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.9.2",
9
+ "version": "1.9.4",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "1.9.2",
4
+ "version": "1.9.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.79.2",
3
+ "version": "0.79.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.79.2",
9
+ "version": "0.79.4",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.79.2",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",