Repository navigation
Dex agents 0.4.0
Connector tools
- Python and TypeScript cores: ConnectorToolSource offers reviewed Connector operations to a Stepwise agent as tools (
dex_agents.connectors, extradex-agents[connectors];@superdurable/dex-agents/connectors, optional peer@superdurable/connectors-sdk). The application names each operation withconnector_tool(connectorTool). Nothing is exposed by default, and an operation whosedex-connector.jsonentry lacksagent.exposed: trueis refused when the agent is defined. Tool name, description, and argument schema come from the manifest. - Effect classes: a Query is
READinCallTool; a Mutation withagent.retry: safeisIDEMPOTENT_WRITEinCallTool; any other Mutation isNON_IDEMPOTENT_WRITEinCallSingleAttemptTool. A call's tool Step gets the operation's Execute timeout and retry policy, at most three attempts. A sessiontool_policiesentry changes only approval. - Approval:
agent.approval: recommendedasks by default; other Mutations run without approval.requires_approval(requiresApproval) overrides it with a bool or a per-call predicate over the arguments. - Arguments are checked against
inputSchemabefore decoding. Branches map throughagent.branches: success issucceeded,rejectedanddefectarefailed,uncertain,unknown, and unlisted Mutation branches areoutcome_unknown, and unlisted Query branches arefailed. Results are canonical JSON, cut at 64 KiB by default with a note. - A single-attempt write that asks to retry records
outcome_unknown, unless its single-dispatch fence savednotSentorcanResendin that attempt; then it recordsfailedwith "not sent; the provider asked to retry later". - Connector writes of one model response run one at a time in the model's order. Once one is not
succeeded, the later ones recordfailedwith the newAgentToolCall.skippedmarker. - Needs Connector SDK 0.9.0 (
dex-connectors-sdk==0.9.0,@superdurable/connectors-sdk0.9.0), which exportsraise_dex_retryandthrowDexRetry. - Every Python and TypeScript framework adapter takes tool sources too; see Tool sources in framework adapters.
- Go core: no connector tools. Its AgentToolCall carries the four new fields, so a record written by another core round-trips.
- See Connector tools.
Tool call contract additions
DeferredToolExecution.context: the tool Step's live context, never stored.ToolOutcomeUnknownError: a tool recordsoutcome_unknownand does not retry.DeferredToolCall.ordered, andAgentToolCallfieldsordered_write,write_queued,skipped, andtruncated_output_bytes, all defaulting to off. Records written by 0.2.0 decode unchanged. Dex Web readsskipped, and the "the tool did not run" error prefix of older records.ToolExecutionResult.truncated_output_bytes,ToolCallStepOptions, and the optional runner methodtool_call_step_options(toolCallStepOptions), whichStepwiseAgentRunnerroutes to a tool source.RunModelmay move to itself once, to commit skipped writes before the model call, so the session graph gains that transition.
Tool sources in framework adapters
- Every Python and TypeScript framework adapter takes
tool_sources(toolSources), such asConnectorToolSource, andtool_source_listing_timeout(toolSourceListingTimeoutMs), default 10 seconds. EachRunModellists every source and offers its tools with the source's name, description, and JSON Schema. A source whose listing fails or times out offers no tools in that model call. - Every call of a source tool, reads included, runs once in its own tool Step through the source's
execute_deferred_tool_call(executeDeferredToolCall), with the Step's context, heartbeat checkpoint, and attempt, so a connector call keeps its Call ID and fence. The source builds the call from the final input, so it decides the effect class, the approval, and the write order.tool_call_step_options(toolCallStepOptions) gives the tool Step the source's timeout and retry policy. Atool_policiesentry for a connector tool changes only its approval. - Definition fails when two sources share a
source_name(sourceName), or a name a source declares repeats an agent tool, a built-in or control tool, or another source's tool. The same names listed at run time fail thatRunModelattempt. Mastra resolves agent tools per request, so it checks them atRunModel. - The Claude Agent SDK adapters, the TypeScript OpenAI Agents SDK adapter, and the Python Step-mode adapters keep a source tool that the turn already called but the current listing lacks as replay-only: its recorded call resumes, and a new call of it is refused inside the turn, so it never starts a tool Step under a new Call ID. A recorded result replays once, only under the tool name it was recorded for.
- The framework adapters poll no tool tasks: a source call that starts a task records
failedfor areadcall andoutcome_unknownotherwise. In the TypeScript adapters, a source call that asks a question recordsfailed. - Python and TypeScript Claude Agent SDK:
ClaudeAgentRunnerandclaude_agent_session_flow(createClaudeAgentSessionFlow) take the tool sources, and the in-process custom tool server offers their tools after the custom tools. Source tools are served without MCP annotations, so the CLI runs them in the model's order; a later write of one reply is refused inside the turn, not recorded asskipped. Anallowed_tools(allowedTools) entry for a source tool fails the attempt, because its calls must defer. - TypeScript Vercel AI SDK, OpenAI Agents SDK, and Mastra:
createVercelAiAgentSessionFlow,createOpenAIAgentsSessionFlow,createMastraAgentSessionFlow, andMastraAgentRunnertaketoolSources. The OpenAI Agents SDK adapter offers source tools to every agent of the graph as non-strict function tools, and maps names the SDK rewrites back to the source's name. Mastra offers them as client tools withoutexecute. - Python OpenAI Agents SDK, Pydantic AI, LangChain and LangGraph, and Google ADK:
OpenAIAgentsRunner,PydanticAIRunner,LangGraphAgentRunner, andAdkAgentRunnerand their session flow functions taketool_sources. Pydantic AI passes source tools as anExternalToolset. The OpenAI Agents SDK adapter adds a gated function tool for each tool of a source'scached_tools()when it is built, so a source withoutcached_toolsoffers nothing;ConnectorToolSourcecaches its whole listing. LangGraph'sDurableToolCallMiddlewareadds the tools to eachcreate_agentmodel request; a custom graph binds the newget_current_tool_source_tools()in its model node. Google ADK appends oneSourceToolsetto thetoolsof everyLlmAgentof the tree when the runner has sources; it lists nothing outside the runner'sRunModel. - TypeScript core:
ToolSourceRouter,ToolSourceRouterOptions,ListedSourceTool, anddefaultToolSourceListingTimeoutMsare exported from the package root, withToolSourceandSourceToolDefinition. The Stepwise runner and the Vercel AI, OpenAI Agents, and Mastra adapters use it. - Python core:
dex_agents.stepwiseaddsFrameworkToolSourceRouter,SourceToolListing,FRAMEWORK_TOOL_SOURCE_ENVELOPE_KEY, andSOURCE_TOOL_NOT_OFFERED_RESULTfor the Step-mode adapters. - Stored values: a source call stores its source's name in
tool_source_referenceunder an adapter key. The Python Step-mode adapters use the new keydex_tool_source. Keep eachsource_namestable across deploys, because stored calls are routed by it. - See Connector tools.
Claude Agent SDK: replay of recorded tool calls
- Python and TypeScript: an approved Claude Code tool, such as
Bash, resumes with its recorded arguments asupdatedInput: the input the application's hooks produced and the approval saw, not the model's original input.can_use_tool(canUseTool) answers a resumed call the same way. - A recorded call replays only as its own tool, with the same name and kind (a custom or source tool, or a Claude Code tool), at most once per segment, and only while the stored transcript has no result for it. Another tool, a second replay, or a call whose ID an earlier segment already resumed is denied and never runs.
- Each tool use ID gets one PreToolUse decision per segment: allow, deny, defer, or a replay. A later PreToolUse for that ID is denied. A
can_use_toolask after a hook allowed the call is denied. - No stored value changes.
Changed
- Python and TypeScript Claude Agent SDK: the application's PreToolUse hooks are not called for a resumed call that already has a final record, so a later deny cannot hide its recorded result. The call passed them when it was deferred.
- TypeScript Claude Agent SDK: a call that the CLI deferred but the adapter's hook did not defer fails the segment, which then retries, instead of being recorded. Python already did this.
- TypeScript Claude Agent SDK: the in-process custom tool server is built with
@modelcontextprotocol/sdk, a new direct dependency at^1.29.0, the range the Claude Agent SDK asks for. The custom tool listing is unchanged. Argument validation errors of inline custom tools now come from that SDK. - TypeScript Mastra: the new
mastraRunToolNamesnames the tools Mastra adds itself, such asagent-researcher, that may run insideRunModel. WithtoolSourcesormastraRunToolNames, atoolPolicieskey no longer allows such a tool by itself. Without either, nothing changes. - TypeScript Vercel AI SDK: the
toolPoliciestype accepts any string key, so a source tool can have a policy. WithouttoolSources, a key that names no tool still throwsTypeError. - Python LangGraph: a model tool call without a tool call ID fails the
RunModelattempt, because the session records each call by its ID.
Full changelog: agents/CHANGELOG.md. Changes since 0.3.0: agents/v0.3.0...agents/v0.4.0