Adopting preflight gates (#618) in cargo-hauler (ScriptedAlchemy/cargo-hauler#135) surfaced one ergonomic gap.
tool/after's preflight has to ask the daemon whether the session has finished background tickets (one bounded socket round trip), and answers execute only when it does. The rendered route then needs exactly that answer — the finished tickets — to write its additionalContext and advance the session's cursor, but EventPreflightResult is 'execute' | { outcome: 'continue' } | { outcome: 'deny', reason }: nothing crosses from the gate to the route, so the route repeats the query.
Concrete case: src/events/tool/after.preflight.ts pings, gets { kind: 'finished', tickets: [cc-7] }, returns 'execute'; src/events/tool/after.tsx → handleAfterShell pings again with the same cursor. Cost is one extra round trip only when tickets finished, and a second answer that fails just defers the announcement to the next tool call, so this is not blocking — but the pre-#618 config-declared handler passed the ping result straight through, and the route-based shape cannot.
Proposal: allow { outcome: 'execute', data?: JsonValue } (or a fourth execute shape) and expose data on AgentEventRouteProps (e.g. preflight.data), validated as JSON the same way deny.reason is. The framework already serializes the canonical payload to the .execute.mjs process, so one more JSON field rides the same boundary.
Adopting preflight gates (#618) in cargo-hauler (ScriptedAlchemy/cargo-hauler#135) surfaced one ergonomic gap.
tool/after's preflight has to ask the daemon whether the session has finished background tickets (one bounded socket round trip), and answersexecuteonly when it does. The rendered route then needs exactly that answer — the finished tickets — to write itsadditionalContextand advance the session's cursor, butEventPreflightResultis'execute' | { outcome: 'continue' } | { outcome: 'deny', reason }: nothing crosses from the gate to the route, so the route repeats the query.Concrete case:
src/events/tool/after.preflight.tspings, gets{ kind: 'finished', tickets: [cc-7] }, returns'execute';src/events/tool/after.tsx→handleAfterShellpings again with the same cursor. Cost is one extra round trip only when tickets finished, and a second answer that fails just defers the announcement to the next tool call, so this is not blocking — but the pre-#618 config-declared handler passed the ping result straight through, and the route-based shape cannot.Proposal: allow
{ outcome: 'execute', data?: JsonValue }(or a fourthexecuteshape) and exposedataonAgentEventRouteProps(e.g.preflight.data), validated as JSON the same waydeny.reasonis. The framework already serializes the canonical payload to the.execute.mjsprocess, so one more JSON field rides the same boundary.