Skip to content

Allow non-interactive consumers to disable onUserDataRequired in /pilo/run WS route #415

@lmorchard

Description

@lmorchard

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 PiloTaskRequesttaskRunner.runTaskpiloWs.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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions