Skip to content

feat: global extensions api - #146

Draft
maxy-shpfy wants to merge 1 commit into
maxy/refactor--memory-adaptersfrom
maxy/feat--global-skills-and-extensions
Draft

maxy-shpfy wants to merge 1 commit into
maxy/refactor--memory-adaptersfrom
maxy/feat--global-skills-and-extensions

Conversation

@maxy-shpfy

@maxy-shpfy maxy-shpfy commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Adds a server-side registry of global extensions: Pi packages (extensions, skills, prompt templates) installed at runtime, persisted on the data volume, and applied to sessions that opt in.

  • Marketplaces: a pluggable install-source abstraction keyed by a "<marketplace>:<ref>" source string. Built-ins are npm (searchable via the npm registry, keywords:pi-package), git, and file (.tgz / .zip / lone .ts upload). Adding a source takes one ExtensionMarketplace class plus one register() call.
  • Storage: FileExtensionStore under EXTENSIONS_ROOT (default SESSIONS_ROOT/.extensions, so it uses the sessions PVC). Writes are staged and then atomically renamed into place.
  • Reconcile on boot: GlobalExtensionService.reconcile() runs before the server starts listening. It reinstalls anything missing from the volume. Failures are marked error instead of being fatal.
  • Package resources and tools: resolvePackageResources follows Pi's discovery rules. If no tools are given, probeExtensionTools loads the extension in an isolated subprocess and records exactly the tools it registers. If the probe can't run, the allowlist falls back to ["*"] for code packages and [] for skills/prompts-only packages.
  • Per-session opt-in: the bundle globalExtensions policy ("all", an id list, or a locked { only: [...] }) is overlaid by the session selection (sessions.extensions: "all", "none", or ids) through resolveExtensionSelection. A locked policy rejects overrides with 409.
  • Spawn: PiAgentManager folds the resolved spawnPlan into --extension / --skill / --prompt-template, and unions the tools into --tools, the same way the memory plan is folded in.
  • HTTP: /api/extensions adds GET /, GET /marketplaces, GET /marketplaces/:id/search?q=, and the admin-only POST /, PATCH /:id, DELETE /:id. POST /api/sessions also accepts extensions, and PUT /api/sessions/:id/extensions is new.
  • DB: migration 0027 adds sessions.extensions. openDb now applies migrations with a resilient runner that tolerates benign already exists / duplicate column name errors from regenerated migration files.
  • Docs: docs/server/extensions-and-prompts.md (new "Global extensions" section) and docs/config-bundle-format.md (the globalExtensions field).

Screenshots

N/A (backend only; UI ships in #147)

How to test

  1. pnpm --filter @tangent/server test. New coverage includes globalExtensionService, the marketplaces (file, npm, registry), packageResources, probeTools, selection, db/client, and sqliteSessionStore.
  2. Start the server and, as an admin, install a package: curl -X POST /api/extensions -H 'content-type: application/json' -d '{"source":"npm:<pi-package>"}'. Then check that GET /api/extensions lists it as ready with detected tools.
  3. Upload a .zip or .ts with multipart POST /api/extensions (file field) and check that it installs.
  4. Create a session with extensions: "all" and check that Prime is spawned with the extension's --extension / --skill flags and that its tools are in --tools.
  5. Use a bundle with globalExtensions: { only: [...] } and check that PUT /api/sessions/:id/extensions returns 409.
  6. Restart the server after deleting an install's pkg/ and check that it is reinstalled on boot.

Notes

  • Reads under /api/extensions are open. Writes require admin.
  • Selection changes take effect on each agent's next spawn. Running agents are not restarted.
  • npm/git installs need network and npm available in the server image.
  • The tool probe resolves Pi's loader from PI_EXTENSION_LOADER, the pi binary's package, or the pnpm store.

Stack created with GitHub Stacks CLI • Give Feedback 💬

@maxy-shpfy
maxy-shpfy added this pull request to stack #139 November 2, 2026 21:08
@maxy-shpfy
maxy-shpfy force-pushed the maxy/feat--global-skills-and-extensions branch from 1b40e47 to 27533c7 Compare November 2, 2026 21:44
@maxy-shpfy
maxy-shpfy force-pushed the maxy/feat--global-skills-and-extensions branch from 27533c7 to 2a787dc Compare November 3, 2026 03:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant