Releases: vstorm-co/pydantic-deepagents
Release list
0.3.22
[0.3.22] - 2026-05-24
Fixed
AttributeError: 'LocalBackend' object has no attribute '_read_bytes'at toolsetget_instructions()time (#118, independently authored by @mcauthorn in #119).pydantic-ai-backend 0.2.8promoted the bytes-read entry point onBackendProtocolfrom private_read_bytesto publicread_bytesand (deliberately) kept no transitional alias. Withpydantic-ai-backend>=0.2.7unbounded, fresh resolutions pulled0.2.8transitively, so every toolset that reaches for bytes (context,memory,liteparse,skills/backend) blew up at instructions-load time. Word-boundary rename across all call sites and test mocks.
Changed
- Bumped
pydantic-ai-backend>=0.2.7 → >=0.2.8(#118, Renovate) — pulls in theexists()predicate, theread_bytesrename (see Fixed above),hashline_editper-(backend, path)serialization, and theasync_executewire-up in the console toolset'sexecutetool. - Bumped
summarization-pydantic-ai>=0.1.4 → >=0.1.5(#118, Renovate) — batched with the backend bump; pure CI-housekeeping release on the summarization side, no behaviour change.
0.3.21
[0.3.21] - 2026-05-24
Changed
- Bumped
pydantic-ai-todofloor from>=0.2.1to>=0.2.2(#114, auto-opened by the new Renovate config). Brings inpydantic-ai-todo 0.2.2: the newAsyncRedisStoragebackend (Redis Hash + companion List, session-scoped, multi-tenant, event-emitter integration) plus a follow-up batch of correctness fixes (remove_todoatomicity via single pipeline, Redis Cluster–safe hash-tagged keys, idempotentinitialize()).
0.3.20
[0.3.20] - 2026-05-18
Fixed
[WinError 2]crash on Windows when calling theexecutetool (#108) — bumped thepydantic-ai-backendfloor from>=0.2.4to>=0.2.7. Releases before 0.2.7 hardcoded["sh", "-c", command]inLocalBackend.execute(), so every shell invocation on Windows failed withFileNotFoundError: [WinError 2] The system cannot find the file specified, regardless of whether the target executable (e.g.powershell,pwsh.exe) was onPATH.pydantic-ai-backend 0.2.7routes through_shell_cmd()(cmd /conwin32,sh -celsewhere) and addsasync_execute()with cancellation support.wait_taskscancellation cascade in subagent orchestration — bumpedsubagents-pydantic-aifloor to>=0.2.4, which routes bothmode="all"andmode="any"throughasyncio.waitinstead ofasyncio.wait_for(asyncio.gather(...)). Previously, when pydantic-ai's_call_toolssibling-cancelled thewait_taskstool call (or any outer cancel reached the orchestrator), the cascade silently killed every in-flight subagent — surfacing asTaskStatus.CANCELLEDwith an emptyerroreven though the parent never requested it.
Changed
- Bumped minimum versions of all
pydantic-ai-*sister packages so a fresh install pulls the latest releases by default:pydantic-ai-slim>=1.97.0(was>=1.77.0),pydantic-ai-backend>=0.2.7(was>=0.2.4, for both[console]and[docker]extras),summarization-pydantic-ai>=0.1.4(was>=0.1.3),subagents-pydantic-ai>=0.2.4(was>=0.2.1),pydantic-ai-shields>=0.3.2(was>=0.3.1).
Internal
# type: ignore[attr-defined]on fiveBinaryContentattribute accesses inpydantic_deep/processors/eviction.py.pydantic-ai-slim>=1.97exposesBinaryContentas aPydanticDataclasswhose fields andidentifierproperty are invisible to pyright (the attributes exist at runtime — this is upstream type-info incompleteness). Keepsmake typecheckgreen until upstream stubs catch up.
Infrastructure
- Renovate config (
renovate.json) — opt-in dependency bot scoped to the five vstorm-co sibling packages (pydantic-ai-todo,pydantic-ai-backend,summarization-pydantic-ai,subagents-pydantic-ai,pydantic-ai-shields). All other dependencies are explicitly disabled. New releases of these packages will get grouped PRs that bump bothpyproject.tomlfloors anduv.lock, with auto-merge on green CI. Activates once the Renovate GitHub App is installed on thevstorm-coorganization.
0.3.19
[0.3.19] - 2026-05-14
Added
-
PeriodicReminderCapability— periodic task reminders for long agent runs (#94) — injects a "what are you supposed to be doing" reminder into the message history every N model-request turns to prevent agent drift on long, tool-heavy runs. Usesbefore_model_requestand per-run state isolation viafor_run().- Four CLI modes via a new
/remindcommand:off,first(zero-cost — re-states the first user message),context(zero-cost — compact transcript),llm(uses Claude Haiku / GPT mini / Gemini Flash to summarize progress). LLMReminderGeneratorwith exception fallback to the zero-cost default.- Three render styles:
system_reminder_tag(default),developer_note,user_prompt. create_deep_agent()gains aperiodic_reminder: bool | PeriodicReminderConfig | None = Noneparameter.- CLI: enabled by default in
llmmode; configurable viaperiodic_reminderandreminder_modekeys inconfig.toml. - New top-level exports:
PeriodicReminderCapability,PeriodicReminderConfig,ReminderGenerator,LLMReminderGenerator,make_config_for_mode.
- Four CLI modes via a new
-
MessageQueue— mid-run message delivery (steering & follow-up) (#100) — lets external code (CLI keystrokes, webhooks, subagents) push messages into a running agent loop without cancelling and restarting it, preserving in-flight tool results and the prompt cache.- Steering messages are injected before the next LLM call via
MessageQueueCapability.before_model_request. To avoid issues with downstream capabilities that strip lone trailingModelRequestnodes, the steeringUserPromptPartis merged into the last existingModelRequest. - Follow-up messages are queued for delivery when the agent would otherwise stop, triggering a re-entry via the new
run_with_queue()helper. - Two delivery modes:
one_at_a_time(default) andall(drain entire queue based on the head message's mode). DeepAgentDeps.message_queuefield, propagated by reference throughclone_for_subagent()so subagents can steer the parent.create_deep_agent()gains amessage_queue: MessageQueue | None = Noneparameter.- CLI:
>>textmid-run = steering, plain text mid-run = follow-up,!cmdstays as shell command in all states. Side-panelQueuedWidgetshows pending counts. Stale steering messages are surfaced as a warning when the run ends before reaching another LLM call, and follow-ups left over from a cancelled run are discarded with a count-only notification. - New top-level exports:
MessageQueue,MessageQueueCapability,QueuedMessage,run_with_queue,format_steering,format_follow_up.
- Steering messages are injected before the next LLM call via
-
Programmatic
skillsparameter oncreate_deep_agent(#97) — acceptslist[Skill]instances directly, complementing the existingskill_directories=discovery path. Emits aUserWarningwhenskills=orskill_directories=are provided alongsideinclude_skills=False. -
Docker sandbox environment variable support (#99 — fixes #98) — wires up the
RuntimeConfig.env_varsplumbing that the programmaticDockerSandboxAPI already supported but the CLI andfull_appexample never exposed.sandbox_env_vars: dict[str, str]andsandbox_env_file: str | Nonefields onCliConfig; matching parameters oncreate_cli_agent().- Three-level priority merge:
config.sandbox_env_vars(lowest) →.envfile → explicitsandbox_env_vars(highest). examples/full_app/app.pyauto-loadsexamples/full_app/.envinto theSessionManager's defaultRuntimeConfig.- Uses
RuntimeConfig(cache_image=False)to prevent secrets from being baked into cached Docker image layers. _write_toml()extended to emit[table]sections fordictvalues, enabling round-trip persistence viaset_config_value().
Fixed
-
Esc to interrupt, tool spinner lifecycle, and empty message cleanup (#96 — closes #93):
- Esc now interrupts a running agent (previously Ctrl+C only); focuses input when idle. Centralized on
DeepApp.action_escape_keyso any screen-level handler uses the same cancellation path. - Fixed a
ToolCallWidget.on_mountrace wherecomplete()arriving before mount left the spinner timer uninitialized — the widget now renders its final state immediately when not in thependingstate. AssistantMessage.complete_tool_callis now idempotent (only acts on widgets still in thependingstate), preventing the cancellation drain from overwriting correctly completed results./loadsession replay marks orphaned tool calls (from previously interrupted sessions) as "Interrupted" instead of leaving spinners hung; switched toisinstancechecks againstToolCallPart/ToolReturnPart/UserPromptPart/TextPartand usespart.args_as_dict()for correct label display.- Empty assistant message bubbles are removed when a run is cancelled before producing any output, thinking, or tool calls.
- The hints bar shows context-aware shortcuts (
Esc interrupt) while the agent is running.
- Esc now interrupts a running agent (previously Ctrl+C only); focuses input when idle. Centralized on
-
create_deep_agent()rejectedskills=kwarg (#97 — fixes #95) — callers usingskills=[Skill(...)]previously gotUserError: Unknown keyword arguments: 'skills'because the kwarg fell through to pydantic-ai'sAgent()constructor.
0.3.18
[0.3.18] - 2026-05-05
Fixed
EvictionCapabilitydroppedBinaryContent(e.g. screenshots) fromToolReturnresults — previously, anyToolReturn(return_value=..., content=[..., BinaryContent(...)])was collapsed into a plain string before the size check, so the multimodalcontent(images, audio, PDFs) was silently discarded along with a text eviction message. The capability now only measures and evictsreturn_value; thecontentlist andmetadataare always preserved by re-wrapping the result. (#90)
Fixed
DeepAgentDepswas missing thecheckpoint_storefield — the checkpointing middleware already resolved the store viagetattr(deps, "checkpoint_store", None)and the docs instructed users to pass it at construction time, but the field was never declared on the dataclass so assignments were silently ignored and type checkers raisedattr-definederrors. The field is now properly declared ascheckpoint_store: Any = Noneand propagated as a shared reference throughclone_for_subagent(). (#87)
Added
- Binary content pruning via
max_binary_content— a newbefore_model_requesthook inEvictionCapabilitybounds the number of multimodal binary parts kept in model-visible history. OlderBinaryContentvalues (from bothUserPromptPart.contentandToolReturnPart.content) are written to the backend at deterministic paths (/large_tool_results/binary_{id}.{ext}) and replaced with a compactread_file-able text reference so the agent can still retrieve them on demand. If a backend write fails, the binary is left in place to avoid data loss.EvictionCapabilitygains amax_binary_content: int | Nonefield (default3).create_deep_agent()gains a matchingmax_binary_content: int | None = 3parameter across all overloads; passNoneto keep every binary in history.- New constants exported from the top-level package:
DEFAULT_MAX_BINARY_CONTENT,BINARY_PRUNED_TEMPLATE. - Storage paths use
BinaryContent.identifier(a stable SHA1 of the bytes), making re-pruning the same binary idempotent.
0.3.17
[0.3.17] - 2026-04-22
Added
-
LiteparseToolset— document parsing via LiteParse- New toolset at
pydantic_deep.toolsets.liteparse - Tools:
parse_document(text extraction) andscreenshot_document(per-page images) - Reads files from any backend as bytes — works with
StateBackend,LocalBackend,DockerSandbox - Optional OCR via built-in Tesseract or pluggable HTTP server (PaddleOCR, EasyOCR)
- Lazy parser initialization — the Node.js CLI is found/installed on first tool call
- Configurable:
ocr_enabled,ocr_language,ocr_server_url,dpi,max_pages - Graceful error messages when the
liteparsepackage or Node.js CLI is not installed - Enabled via
include_liteparse=Trueincreate_deep_agent()
- New toolset at
-
liteparseoptional extra inpyproject.tomlpip install pydantic-deep[liteparse]installs the Python wrapper- Node.js >= 18 and
npm install -g @llamaindex/liteparseare required separately
0.3.16
[0.3.16] - 2026-04-22
Changed
instructionsnow replacesBASE_PROMPTinstead of appending to it — previously, passinginstructions="..."tocreate_deep_agent()produced a system prompt ofBASE_PROMPT + "\n\n" + instructions. Nowinstructionsis used verbatim as the full system prompt.instructions=None(the default) keeps the existing behaviour —BASE_PROMPTis used automatically. Users who want to extend the default rather than replace it can do so with an f-string:instructions=f"{BASE_PROMPT}\n\nYour extra text".BASE_PROMPTis exported from the top-level package. (#84, reported by @rremilian)- Subagent and team-member factories always prepend
BASE_PROMPT— agents spawned automatically by thetask()tool orspawn_team()continue to receiveBASE_PROMPTfollowed by their task-specificinstructions, so subagent behaviour is unchanged. apps/deepresearchdouble-prompt bug fixed —MAIN_INSTRUCTIONSalready containedBASE_PROMPT; it was previously duplicated in the final system prompt because the old append logic prepended it again. The new semantics resolve this without any change to the deepresearch app itself.
0.3.15
[0.3.15] - 2026-04-17
Fixed
PatchToolCallsCapabilitycausedValidationException: duplicate Idson Bedrock when tools raisedModelRetry— when a tool raisedModelRetry, pydantic-ai records the retry as aRetryPromptPart(carrying the originaltool_call_id) on the followingModelRequest, not as aToolReturnPart. The patch processor only scanned forToolReturnPartwhen deciding whether aToolCallPartwas orphaned, so it injected a syntheticToolReturnPartwith the same id — leaving the request with two parts sharing onetool_call_id. Strict providers (Bedrockminimax.minimax-m2.5and others) rejected the request withThe toolResult blocks at messages.N.content contain duplicate Ids. The processor now treatsRetryPromptPartas a valid answer to aToolCallPart, so no synthetic return is injected and the history remains valid. (#79, reported by @thatGreekGuy96)
0.3.14
[0.3.14] - 2026-04-16
Fixed
- Subagents ignored parent
web_search/web_fetchsettings — the default subagent factory increate_deep_agenthardcodedweb_search=Trueandweb_fetch=True, overriding the parent agent's configuration. On Bedrock and Vertex Anthropic models this produced a 400 error (web_fetch_20250910not accepted), because the beta web tools are not supported there. The factory now propagates the parent agent'sweb_searchandweb_fetchflags to spawned subagents. (#77, reported by @SvdR82)
0.3.13
[0.3.13] - 2026-04-13
Fixed
- User-provided tools lost metadata when passed via
tools=parameter — tools registered throughcreate_deep_agent(tools=[...])were previously added viaagent.tool(tool.function)after construction, which hardcodedtakes_ctx=Trueand discarded allTool-level metadata (name,description,prepare,max_retries,requires_approval,timeout). Tools are now passed directly to theAgentconstructor, preserving all metadata and correctly honouring the originaltakes_ctxvalue. (PR #75 by @ilayu-blip)