Summary
The WebSocket /pilo/run route always supplies an onUserDataRequired callback to WebAgent (packages/server/src/routes/piloWs.ts:135, :157). Per the comment at packages/core/src/webAgent.ts:93 ("Presence enables interactive mode"), this means interactive mode is structurally always-on for any consumer of the WS route.
The SSE /pilo/run route is non-interactive (packages/server/src/routes/pilo.ts:53, comment: "No onUserDataRequired: SSE is non-interactive"), but downstream consumers like tabs-api always use the WS route, so they have no way to expose a true non-interactive mode.
Why this matters
For non-interactive automation use cases (testing pipelines, batch workflows, anything without a human in the loop), the always-on callback wiring causes:
- The
request_user_data tool is registered on the agent (packages/core/src/webAgent.ts:408), so the model can choose to call it.
- The fill-gate is armed and rejects the first attempt on every unapproved ref (
packages/core/src/webAgent.ts:418-448). This is a generic two-strike mechanism on fill/select/check: first attempt rejects with FILL_GATE_ERROR, second attempt on the same ref is allowed through.
- The system prompt biases the model toward calling
request_user_data for "name, email, phone, address, date of birth..." (packages/core/src/prompts.ts:382).
Even when the upstream proxy auto-cancels every request_user_data call, the agent inside Pilo still pays the cost: a wasted iteration on every fresh page snapshot for the first-strike rejection, model confusion when its calls are cancelled, and frequent agent aborts when it interprets a cancelled call as "I cannot proceed without this data."
Suggested fix
Plumb a flag through PiloTaskRequest → taskRunner.runTask → piloWs.ts so the WS route can conditionally omit the callback. Default-on for backwards compatibility; opt-out via a new field like disableInteractive: true (or similar).
Sketch:
// piloWs.ts
const callback = req.disableInteractive
? undefined
: (req: UserDataRequest) => { /* existing callback */ };
await taskRunner.runTask({ ..., onUserDataRequired: callback });
When the callback is absent, WebAgent already correctly skips tool registration (webAgent.ts:408), the fill-gate (webAgent.ts:418), and adjusts the system prompt (webAgent.ts:1646).
Summary
The WebSocket
/pilo/runroute always supplies anonUserDataRequiredcallback toWebAgent(packages/server/src/routes/piloWs.ts:135,:157). Per the comment atpackages/core/src/webAgent.ts:93("Presence enables interactive mode"), this means interactive mode is structurally always-on for any consumer of the WS route.The SSE
/pilo/runroute is non-interactive (packages/server/src/routes/pilo.ts:53, comment: "No onUserDataRequired: SSE is non-interactive"), but downstream consumers like tabs-api always use the WS route, so they have no way to expose a true non-interactive mode.Why this matters
For non-interactive automation use cases (testing pipelines, batch workflows, anything without a human in the loop), the always-on callback wiring causes:
request_user_datatool is registered on the agent (packages/core/src/webAgent.ts:408), so the model can choose to call it.packages/core/src/webAgent.ts:418-448). This is a generic two-strike mechanism onfill/select/check: first attempt rejects withFILL_GATE_ERROR, second attempt on the samerefis allowed through.request_user_datafor "name, email, phone, address, date of birth..." (packages/core/src/prompts.ts:382).Even when the upstream proxy auto-cancels every
request_user_datacall, the agent inside Pilo still pays the cost: a wasted iteration on every fresh page snapshot for the first-strike rejection, model confusion when its calls are cancelled, and frequent agent aborts when it interprets a cancelled call as "I cannot proceed without this data."Suggested fix
Plumb a flag through
PiloTaskRequest→taskRunner.runTask→piloWs.tsso the WS route can conditionally omit the callback. Default-on for backwards compatibility; opt-out via a new field likedisableInteractive: true(or similar).Sketch:
When the callback is absent,
WebAgentalready correctly skips tool registration (webAgent.ts:408), the fill-gate (webAgent.ts:418), and adjusts the system prompt (webAgent.ts:1646).