Skip to content

harness-pi: opt-in setting to load Pi extensions from the filesystem (extensions: boolean, default false) #20408

Description

@hteek

Description

@ai-sdk/harness-pi 1.0.101. createPiSession hardcodes filesystem extension discovery off:

const resourceLoader = new DefaultResourceLoader({
  cwd: sessionWorkDir,
  agentDir: hostAgentDir,          // per-session tmp dir, not `input.agentDir`
  ...
  // Pi runs in the host process, so its default resource discovery reaches
  // the host developer's personal config ...
  noExtensions: true,

The reasoning is right for the default case: pi runs in the host process, so loading a developer's ~/.pi/agent/extensions or a checked-out repo's .pi/extensions would execute untrusted code in the server. But it leaves no opt-in for deployments where the harness process is the sandbox.

Use case

We run harness-pi inside a Firecracker microVM (one VM per session, disposable HOME, the VM is the trust boundary). We want the guest image to ship pi extensions as files, the way pi itself is configured:

/opt/pi/agent/extensions/rtk.ts            # from `rtk init --agent pi`
/opt/pi/agent/extensions/context-mode -> node_modules/context-mode   # pi manifest package

Today the only route is extensionFactories, which means hand-porting every extension into TypeScript inside our package: an inline copy of rtk's rtk.ts, a loader that resolves context-mode's adapter path and imports it, plus tests for both. Each new extension is another port. Tools that already publish a pi extension (rtk, context-mode, anything with a pi manifest in package.json) cannot be used as published.

Also, agentDir: hostAgentDir means that even if noExtensions were lifted, <agentDir>/extensions would resolve to an empty tmp dir; the caller's agentDir setting is only used for auth.json/models.json/settings, not discovery.

Proposal

A PiHarnessSettings.extensions?: boolean, default false, that:

  1. sets noExtensions: !extensions on the DefaultResourceLoader;
  2. when enabled, passes the caller's agentDir (falling back to hostAgentDir) to the loader so <agentDir>/extensions is discovered;
  3. takes the noTools: "builtin" path so extension-registered tools reach the model (same gap as harness-pi: tools registered by extensionFactories extensions never reach the model (dropped by tools allowlist) #20402).

Default behavior is unchanged. The docstring should state the trust implication plainly: enabling this runs whatever is under <agentDir>/extensions and the working directory's .pi/extensions, so only enable it where the harness process is already isolated.

Patch we run

--- a/dist/index.d.ts
+++ b/dist/index.d.ts
     readonly extensionFactories?: ReadonlyArray<ExtensionFactory>;
+    /**
+     * Load Pi extensions from the filesystem: `<agentDir>/extensions` and the
+     * session working directory's `.pi/extensions`. Defaults to false; only
+     * enable where the harness process itself is the sandbox.
+     */
+    readonly extensions?: boolean;
--- a/dist/index.js
+++ b/dist/index.js
-  const hasExtensionFactories = extensionFactories.length > 0;
+  const fsExtensions = input.settings.extensions === true;
+  const hasExtensionFactories = extensionFactories.length > 0 || fsExtensions;
   ...
-    agentDir: hostAgentDir,
+    agentDir: fsExtensions ? agentDir : hostAgentDir,
   ...
-    noExtensions: true,
+    noExtensions: !fsExtensions,
   ...
-      ...hasMcpServers ? { noTools: "builtin" } : { tools: toolNames },
+      ...hasMcpServers || hasExtensionFactories ? { noTools: "builtin" } : { tools: toolNames },
   ...
-          ...settings.extensionFactories ? { extensionFactories: settings.extensionFactories } : {}
+          ...settings.extensionFactories ? { extensionFactories: settings.extensionFactories } : {},
+          ...settings.extensions ? { extensions: settings.extensions } : {}

Verified: with extensions: true and agentDir set, a plain .ts under <agentDir>/extensions and a symlinked package directory with a pi manifest both load at session start, and their registered tools are listed by the model. With the setting unset nothing changes.

Happy to open a PR against packages/harness-pi if the shape is acceptable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions