Repository navigation
[Q&A] How can an out-of-tree plugin answer approval/request in web-profile sessions? (listeners never fire despite scopeTarget semantics) #9530
Replies: 3 comments
|
Thanks for the 20-variant matrix — it made this checkable. I rebuilt the harness against First, your tag is not the variable: I checked 1. There is one registry —
2. An untagged listener does fire — for every agent. Dispatch is this.ctx.waterfall(scopeTarget(req.agent, req.agent), 'approval/request', req, () => 'unavailable')and the gate (
3. That is exactly why "all four variants failed" does not fit the filter. Variants A, B and D all register untagged contexts (the approval service is a root-level bundle row), and C registers on
4. The shipped pattern to copy — both in-tree answerers register on the root-level plugin's own
And if you do want to rule the filter in or out in one line, log your own tag inside import { scopeOf } from '@deepseek-ai/dsh-scope'
ctx.logger.info('[approval-probe] scope tag =', scopeOf(ctx))A defined tag means your row landed under a scope (a preset owner, or anything built on Two contract details that also bear on the outcome you saw: the waterfall's fallback is Happy to post the ~40-line harness (stubs the agent, drives the waterfall directly, no harness boot) if it helps you re-run your table. |
|
I encountered the same problem when I tried to connect Paseo with DSH |
|
Resolved — it was our plugin claiming the waterfall, not a dsh issue. Thanks for the discriminator. @argszero — your Root cause: our out-of-tree bundle plugin registered a Fix: removed our plugin's The footgun for other out-of-tree plugin authors: don't register a claiming |
Uh oh!
There was an error while loading. Please reload this page.
Summary
We're building an out-of-tree plugin (profile bundle via
node_modules+dsh.profile.bundles+ bundle patch with- insertentry) that needs to answerapproval/requestwaterfall events — e.g. to forward sandbox-escalation prompts to an external UI. Despite thescopeTargetfilter semantics appearing to allow untagged listeners, our plugin's listeners never fire, in any attachment variant we could construct.Reproduction
dsh.bundle.patchpointing at a patch file containing an- insert: - id/nameentry (the entry is what actually registers the plugin — a[]patch silently registers nothing, we learned this the hard way).apply(ctx)registers listeners, tried all of:webprofile (bundlesarray, before and afterdsh-web-app), startdsh --profile web.writeunderread-onlysandbox policy → agent escalates toworkspace-write).Current behavior
apply()runs (confirmed via startup logs), listeners are registered (variant D even shows our hook counted in the registry's hook list).approval/requestevent never reaches any of them: the escalation fails immediately withsandbox escalation to "workspace-write" requires approval, but no approval channel is available(the waterfall's fail-closed fallback), and our handlers' log lines never appear.session/eventin the same plugin does fire — so the plugin's event surface is alive; only the agent-scoped waterfall is unreachable.Our best diagnosis so far:
ctx.eventsis not a root singleton (ctx.events !== ctx.root.events— observed at runtime), andApprovalService.decidedispatches on the service's own fiber registry. A plugin-fiber listener (global or not) appears to live on a different registry than the one the dispatch walks — soscopeTarget's filter (which would pass untagged listeners) is never even consulted for them.Expected behavior
Reading
packages/core/scope/src/index.ts,scopeTarget's carrier filter returnstruefor listeners with no scope tag:From this we'd expect a root/plugin-fiber
ctx.on('approval/request', ...)listener to receive the dispatch for any agent. Is that the intended contract? And if the correct pattern for out-of-tree plugins is something else entirely (a different mounting point, a service-injection contract, or "not supported yet"), we'd appreciate a pointer.Environment
0.1.2-rc.1(pinned; all runtime experiments above)master/0.1.5-rc.1:core/scopescopeTarget,interaction/user-approvaldispatch, andboot/app-bootbundle loading are all unchanged in the relevant partsnode_modules(no pnpm store access at runtime)Context: we're using dsh as the runtime base for a multi-user agent workspace (SuperWhale — an agent-OS experiment for a 50-person pilot), and approval forwarding to a human UI is the one seam we couldn't bridge from the plugin side. Happy to share the full 20-variant experiment matrix and source anchors if useful. Thanks!
All reactions