Repository navigation
Per-session availability for static tools, including workflow tools #5024
Description
What problem are you trying to solve?
Static tools, including workflow tools, cannot be scoped per session without becoming dynamic. Subagents already can: eve always compiles a subagent's resources (instructions, tools, skills, and sandbox), and a resolver decides whether a session sees it. "Return null to omit it from the parent's model-visible tools" (dynamic-capabilities.md L64-69, L99-103). Static tools have no equivalent: disableTool() is decided at build time, and a dynamic tool loses two static features:
- A result-based
endsTurn. Dynamic tools reject a function-valuedendsTurn(dynamic-tool-lifecycle.tsL366-374). The error suggests deciding in the resolver, but the resolver runs before the call and cannot see theexecuteoutput. - Workflow bodies. A resolver cannot return
defineWorkflowTool()(L102-111).
Example: a WhatsApp ordering agent on eve serves many merchants from one deployment, and each conversation is a session for one merchant. A pizzeria's sessions need a tool that turns "16 slices" into pizza sizes and ends the turn only on some results, and a workflow tool that waits for the shopper to tap a button. A store's sessions should not see either tool. Today both tools stay advertised to every merchant and return "not applicable" where they don't fit.
Proposed solution
Let static tools, including workflow tools, declare their availability per session. The definition stays static, so the workflow body, endsTurn function, approval policy, and schemas are unchanged. Only the tool's presence in the session's model-visible tools varies.
export default defineWorkflowTool({
// ...
available: (ctx) => merchantKind(ctx.session.auth.current) === "pizzeria",
});This is the authored-tool counterpart of #1543, which asks for per-session availability of built-in tools. An agent-level allowlist, like option (b) there, could serve both.
Availability would be evaluated at session.started, and optionally at turn.started, as for dynamic subagents. Session scope keeps the tool list stable for prompt caching. The result is a set of tool names stored in durable context, so no callback replay is needed. A call to an unavailable tool should fail before execute. Availability is not an authorization boundary.
Other frameworks filter statically defined tools per run or per step the same way: OpenAI Agents SDK isEnabled (source), Google ADK tool_filter (source), and AI SDK prepareStep with activeTools (source).
Prompt or implementation plan for our agents (optional)
Add per-session availability for static tools, including defineWorkflowTool tools and tools contributed by extensions, modeled on dynamic subagent availability. Start from packages/eve/src/harness/model-call/tools.ts, where eve assembles the model's tools and endsTurnTools. It is done when a workflow tool hidden in one session is visible in another, a function-valued endsTurn still ends the turn, and a call to a hidden tool fails before execute, including after resumption in a new process. Update the dynamic capabilities and tools docs.
Alternatives considered
- A durable
endsTurncallback for dynamic tools. This fixes only the first limit and leaves workflow tools unscoped. It is worth doing separately. - Static tools that return "not applicable". This is the current workaround. It costs tokens on every step and invites calls that can only fail.
Activity
roboeve commentedon Oct 19, 2026
Triaged as a feature request (
tools,p2). Not a duplicate.The cited constraints hold on current
main: dynamic resolvers rejectdefineWorkflowTool()and workflow-tool names, dynamic tool metadata only accepts a literalendsTurn, anddisableTool()applies to the whole agent.Related: #1543 asks for the same per-session gate over framework built-ins and is still open. One availability mechanism over all static tools (authored, workflow, extension, built-in) would likely cover both, so they should be designed together.
Next step: this adds public API, so it needs a short design doc under
research/that picks one shape (per-toolavailablepredicate or an agent-level allowlist resolver) and pins the semantics before implementation:disableTool()anddefaultTools: false