Context
@hachej/boring-ask-user currently has two package surfaces:
boring.front → workspace UI: Questions pane/provider/surface resolver.
boring.server → workspace backend: routes, file store, bridge publisher, runtime, and the ask_user agent tool via WorkspaceServerPlugin.agentTools.
That works today because boring-workspace collects server plugin agentTools and passes them to boring-agent as host-injected extraTools. The boring-agent Pi harness adapts those tools with the active boring workspace sessionId, so ask_user can block the correct chat session and open/update the correct Questions UI.
But conceptually, chat-callable tools belong on the Pi/agent surface, especially for package plugins and hot reload:
{
"boring": {
"front": "dist/front/index.js",
"server": "dist/server/index.js"
},
"pi": {
"extensions": ["dist/agent/index.js"]
}
}
Target package shape:
plugins/ask-user/
src/front/ # workspace UI plugin
src/server/ # workspace HTTP routes, store, bridge publisher/runtime
src/agent/ # Pi extension registering ask_user
Problem
Moving ask_user directly from WorkspaceServerPlugin.agentTools to package.json#pi.extensions would lose important boring-workspace context.
Native Pi extension tools receive Pi's ExtensionContext, but not the boring-agent/web session id currently injected by the host tool adapter:
adaptToolsForPi(opts.tools, input.sessionId)
ask_user needs that session id to call the ask-user runtime correctly:
runtime.ask({ ...input, sessionId })
It also needs a clean way to reach the workspace ask-user backend/runtime/bridge from the Pi extension. Without an explicit seam, a Pi-native ask_user tool would need hidden globals, brittle HTTP assumptions, or would route questions to the wrong/default session.
End goal
Add a workspace-aware Pi extension bridge for package agent surfaces, then migrate ask_user to the Pi extension path.
Desired end state:
ask_user is registered by plugins/ask-user/src/agent/index.ts via pi.registerTool().
plugins/ask-user/package.json#pi.extensions points to that agent entry.
plugins/ask-user/src/server no longer contributes agentTools; it owns only routes/store/runtime/bridge publishing.
/reload hot reloads the ask-user tool through Pi's native extension loader.
- The tool still receives/derives the correct boring workspace session id and can safely talk to the ask-user server/runtime.
Possible design directions
One of these seams is likely needed:
-
Workspace session metadata in Pi tool context
- Extend boring-agent's Pi harness integration so package Pi tools can read the active boring workspace
sessionId from tool execution context or a documented helper.
-
Workspace agent bridge/client for Pi extensions
- Provide a small helper usable from
src/agent/index.ts, e.g. createWorkspacePluginAgentClient(ctx), that can call plugin server routes with the correct session context.
-
Ask-user-specific agent client
- Provide
createAskUserAgentExtension(...) that registers the Pi tool and delegates to the ask-user server route/runtime using a supported context channel.
Avoid hidden globals if possible. The bridge should be explicit, typed, testable, and compatible with hot reload.
Acceptance criteria
- Define/document the three package surfaces:
front, server, agent.
- Add the workspace-aware Pi extension context/client seam.
- Migrate
@hachej/boring-ask-user:
- add
src/agent/index.ts that registers ask_user using Pi's extension API;
- add
pi.extensions in package.json;
- remove
agentTools from createAskUserServerPlugin();
- keep server routes/store/bridge behavior intact.
- Add tests proving:
- package scan exposes the ask-user Pi extension path;
ask_user is available through Pi extension loading, not server agentTools;
- tool execution uses the correct boring workspace session id;
/reload picks up changes to the ask-user agent extension.
- Update docs/templates to describe the 3-surface plugin layout:
front: workspace UI contributions;
server: workspace backend/routes/stores/bridge;
agent: Pi extensions/tools/skills loaded via package.json#pi.
Non-goals
- Do not force every plugin to have all three surfaces.
- Do not remove
WorkspaceServerPlugin.agentTools immediately; keep it for static/host-composed tools and backwards compatibility.
- Do not try to make workspace plugins use Pi's imperative API wholesale. Workspace front/server contribution introspection should remain declarative where possible.
Context
@hachej/boring-ask-usercurrently has two package surfaces:boring.front→ workspace UI: Questions pane/provider/surface resolver.boring.server→ workspace backend: routes, file store, bridge publisher, runtime, and theask_useragent tool viaWorkspaceServerPlugin.agentTools.That works today because boring-workspace collects server plugin
agentToolsand passes them to boring-agent as host-injectedextraTools. The boring-agent Pi harness adapts those tools with the active boring workspacesessionId, soask_usercan block the correct chat session and open/update the correct Questions UI.But conceptually, chat-callable tools belong on the Pi/agent surface, especially for package plugins and hot reload:
{ "boring": { "front": "dist/front/index.js", "server": "dist/server/index.js" }, "pi": { "extensions": ["dist/agent/index.js"] } }Target package shape:
Problem
Moving
ask_userdirectly fromWorkspaceServerPlugin.agentToolstopackage.json#pi.extensionswould lose important boring-workspace context.Native Pi extension tools receive Pi's
ExtensionContext, but not the boring-agent/web session id currently injected by the host tool adapter:ask_userneeds that session id to call the ask-user runtime correctly:It also needs a clean way to reach the workspace ask-user backend/runtime/bridge from the Pi extension. Without an explicit seam, a Pi-native ask_user tool would need hidden globals, brittle HTTP assumptions, or would route questions to the wrong/default session.
End goal
Add a workspace-aware Pi extension bridge for package agent surfaces, then migrate
ask_userto the Pi extension path.Desired end state:
ask_useris registered byplugins/ask-user/src/agent/index.tsviapi.registerTool().plugins/ask-user/package.json#pi.extensionspoints to that agent entry.plugins/ask-user/src/serverno longer contributesagentTools; it owns only routes/store/runtime/bridge publishing./reloadhot reloads the ask-user tool through Pi's native extension loader.Possible design directions
One of these seams is likely needed:
Workspace session metadata in Pi tool context
sessionIdfrom tool execution context or a documented helper.Workspace agent bridge/client for Pi extensions
src/agent/index.ts, e.g.createWorkspacePluginAgentClient(ctx), that can call plugin server routes with the correct session context.Ask-user-specific agent client
createAskUserAgentExtension(...)that registers the Pi tool and delegates to the ask-user server route/runtime using a supported context channel.Avoid hidden globals if possible. The bridge should be explicit, typed, testable, and compatible with hot reload.
Acceptance criteria
front,server,agent.@hachej/boring-ask-user:src/agent/index.tsthat registersask_userusing Pi's extension API;pi.extensionsinpackage.json;agentToolsfromcreateAskUserServerPlugin();ask_useris available through Pi extension loading, not serveragentTools;/reloadpicks up changes to the ask-user agent extension.front: workspace UI contributions;server: workspace backend/routes/stores/bridge;agent: Pi extensions/tools/skills loaded viapackage.json#pi.Non-goals
WorkspaceServerPlugin.agentToolsimmediately; keep it for static/host-composed tools and backwards compatibility.