paperclip/server
Ramon-nassa 62863126a3
Some checks failed
Docker / build-and-push (push) Failing after 3m41s
Refresh Lockfile / refresh (push) Failing after 5m12s
Release / verify_canary (push) Failing after 10m53s
Release / verify_stable (push) Has been skipped
Release / publish_canary (push) Has been skipped
Release / preview_stable (push) Has been skipped
Release / publish_stable (push) Has been skipped
fix(plugin-tool-dispatcher): propagate pluginDbId so worker.isRunning resolves (#5671)
Fixes #2391
Fixes #3394
Fixes #4094
Fixes #5501
Fixes #5916
Fixes #6215
Fixes #6514

## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Plugins extend the platform by registering agent-callable tools
backed by long-running worker processes
> - `PluginToolDispatcher` is the boundary between the HTTP
`/api/plugins/tools/execute` route and `PluginWorkerManager`, which owns
those worker processes
> - `PluginWorkerManager` keys live workers by the plugin's **database
UUID**, but `plugin-loader` was registering tools using only `pluginKey`
— so every tool call did `workerManager.isRunning(pluginKey)` and always
got `false`
> - As a result, every `POST /api/plugins/tools/execute` against a
tool-exposing plugin returned 502 `worker for plugin X is not running`,
even though the worker process was alive (hit in production by
`vexion.council-chat`; `mem0-sync` would be next)
> - This pull request threads the DB UUID through the dispatcher →
registry hop and hardens the contract so omitting the UUID is a
compile-time error, not a silent fallback
> - The benefit is plugin tool execution actually works for any plugin
declaring `manifest.tools[]`, and the type system prevents the same bug
from recurring

## What Changed

- `server/src/services/plugin-loader.ts` — pass in-scope `pluginId` (DB
UUID) as the third argument to `toolDispatcher.registerPluginTools`.
Single-line root fix.
- `server/src/services/plugin-tool-dispatcher.ts` —
`registerPluginTools` now takes `pluginDbId: string` (required, was
optional). JSDoc updated to document the worker-routing contract and why
the optional signature masked the bug.
- `server/src/services/plugin-tool-registry.ts` — `registerPlugin`
throws on missing/empty `pluginDbId` so any new call site that forgets
the UUID fails immediately rather than silently falling back to
`pluginKey`.
- `server/src/__tests__/plugin-tool-dispatcher-pluginDbId.test.ts` — new
focused regression suite covering the activation path, disable→enable
lifecycle, worker re-spawn, and the empty-UUID guard.

## Verification

- `pnpm vitest run
server/src/__tests__/plugin-tool-dispatcher-pluginDbId.test.ts` — 6/6
passing.
- `pnpm vitest run server/src/__tests__/plugin-database.test.ts
server/src/__tests__/plugin-routes-authz.test.ts
server/src/__tests__/plugin-lifecycle-restart.test.ts` — 48/48 passing
on the merge commit.
- `pnpm --filter @paperclipai/server typecheck` — no new errors
introduced by these files.
- Manual repro path:
1. Install a plugin that declares `manifest.tools[]` and uses
`runWorker`.
2. Confirm status `ready` and a live worker (`paperclipai plugin
diagnostics <key>`).
3. `POST /api/plugins/tools/execute` with `{ tool:
"<pluginKey>:<toolName>", parameters, runContext }`.
4. Pre-fix: HTTP 502, `worker for plugin <key> is not running`.
Post-fix: tool dispatches normally.

## Risks

- Low risk. The signature tightening (`pluginDbId?` → `pluginDbId`) is a
back-compatible behavioral fix at the only production call site
(`plugin-loader`), which already had the UUID in scope.
- Test/recovery paths that previously omitted the UUID must now supply
it; the new error message identifies the missing arg explicitly.
- No database migration, no API/schema change, no plugin-author-facing
change.
- The merge commit pulls master into the PR branch additively (no
rebase); reviewers can read the fix commits independently of the merge.

## Model Used

- Provider/model: Anthropic Claude (Opus 4.7, `claude-opus-4-7`) for the
additive merge-conflict resolution, PR description rewrite, and Greptile
follow-up; original fix authored by
[@Ramon-nassa](https://github.com/Ramon-nassa).
- Capabilities used: tool use (file edit, shell, GitHub CLI), extended
thinking off, no code execution by the model.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (N/A — server-only change)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

---

## Original Summary (preserved from contributor)

`plugin-loader` activates plugins and calls

```ts
toolDispatcher.registerPluginTools(pluginKey, manifest)
```

with only two args. `PluginToolDispatcher.registerPluginTools` forwards
them to `registry.registerPlugin(pluginKey, manifest)`. The registry
falls back `pluginDbId ?? pluginKey`, but `PluginWorkerManager` keys
live workers by the DB UUID — so the downstream

```ts
workerManager.isRunning(pluginKey)   // always false
```

causes every `POST /api/plugins/tools/execute` to fail with `worker for
plugin X is not running`, even when the worker process is alive and
healthy. **This hits every plugin that exposes tools** (we hit it in
`vexion.council-chat`; `mem0-sync` would too).

Reported-by: Vexion / Ramon Nassar (vexion.council-chat plugin, MO-068).

---------

Co-authored-by: ramon nassar <ramon@tabs.co>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Devin Foley <devin@devinfoley.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-06-03 19:13:21 -07:00
..
scripts fix(server): use stable tsx/cli entry point in dev-watch 2026-03-28 06:42:03 +07:00
src fix(plugin-tool-dispatcher): propagate pluginDbId so worker.isRunning resolves (#5671) 2026-06-03 19:13:21 -07:00
CHANGELOG.md chore: release v0.3.1 2026-03-12 13:09:22 -05:00
package.json Add built-in grok_local adapter (#6087) 2026-05-16 09:51:09 -07:00
tsconfig.json Fix root TypeScript solution config 2026-03-09 14:09:30 -05:00
vitest.config.ts Stabilize serialized server route tests (#4448) 2026-04-24 19:27:00 -05:00