a2a-claude changelog
Release notes for a2a-claude.
This page is generated from
a2a-claude/CHANGELOG.mdand stays in sync with the repository.
0.4.1
Patch Changes
- 4e2bd82: No code changes. A prior release run published every package's exact
target version under the
canarynpm dist-tag instead oflatest(thecanaryjob's snapshot step no-ops when there are no pending changesets, which was the case immediately after that Version Packages PR merged — see the canary-job fix in a preceding commit). Since npm never allows republishing an already-used version string, the only way to get alatest-tagged release out is a fresh patch bump. - Updated dependencies [4e2bd82]
- @a2a-wrapper/[email protected]
0.4.0
Minor Changes
-
f3c7062: Handle Claude rate limit events instead of failing the turn generically.
Rate-limit signals were previously unrecognised:
rate_limit_event,system/api_retry, and assistantrate_limiterrors all fell through to a debug log, and the turn surfaced asfailedwith"Error during execution."A rejection now ends the turn immediately with an
input-requiredstatus naming the limit type and reset time, plus structured metadata (reason,rateLimitType,resetsAt,resetsAtIso,utilization, anderrorCode/canPurchaseCreditswhen the SDK reports them). The task stays non-terminal, so the client continues the same conversation on the same task once the limit resets. Configurable viarateLimit.taskStatefor clients that require a terminal state.This does not shorten the SDK's own retry behaviour: a
system/api_retryis deliberately treated as a warning that does not end the turn, so the SDK's internal retries still run to exhaustion inside the sametimeouts.promptwindow. The turn ends only once the SDK gives up and emits an assistantrate_limiterror. Those retries are at least visible now, asrate_limitsideband events withaction: "retrying"carrying the SDK'sattempt,maxRetries, anddelayMs.A rejection whose overage window is still open is treated as a warning rather than a rejection, since the request may proceed on overage credits; if it does not, the assistant
rate_limiterror still ends the turn. Limit details are never fabricated — a limit type or reset time is inherited by a later signal only from a snapshot that reported pressure, utilization is never inherited, and a reset time that is not in the future is dropped.Adds a
rate_limitsideband event type to@a2a-wrapper/core, gated byfeatures.emitRateLimitEvents. -
41e2d82: Hold the A2A Task open while Claude has background work in flight.
A Task used to reach a terminal state as soon as Claude's first turn ended — even when that turn had just started a background process and said it was waiting on the result. A2A gives an agent no way to open a new turn against a terminal Task, so the follow-up report had nowhere to land.
The Task now stays in
workingfor as long as Claude reports background work running, and completes only once a turn ends with nothing left. Each turn publishes its ownresponseartifact and a non-finalworkingstatus update whosemetadata.backgroundTaskslists what is still in flight. Chains of any length work this way, as rounds of one Task rather than several Tasks.Controlled by
features.holdTaskForBackgroundWork(defaulttrue; setfalsefor the old complete-at-first-result behavior) andfeatures.emitBackgroundTaskEvents(defaulttrue), which publishes a newbackground_taskssideband event — added to@a2a-wrapper/core— each time the live set changes.Bumps
@anthropic-ai/claude-agent-sdkfrom0.3.202to0.3.245. The feature needs at least0.3.235, the first version to emitbackground_tasks_changed.Three changes apply even with
holdTaskForBackgroundWorkoff:- Queries now use streaming input rather than a string prompt. A string prompt makes the SDK close the CLI subprocess's stdin on the first result, which ends the process before a second round is possible. This is not switchable.
agent_started/agent_finishedare emitted once per A2A Task rather than once per SDK turn.- A success result with empty text no longer publishes an empty
responseartifact.
See the a2a-claude README for caveats, including how
claude.maxTurnsandtimeouts.promptnow span a held-open Task's rounds. -
ddc0861: Allow
timeouts.promptto be disabled by setting it to0(or any value<= 0). Previously every turn was bounded at ten minutes by default with no way to opt out, which cut off legitimately long-running turns.Negative values were also a footgun:
setTimeoutcoerces a negative delay to the next tick, soprompt: -1— the intuitive spelling for "no timeout" — aborted the turn immediately instead of disabling the bound. The timer is now armed only for positive values, matching the "set to0to disable" convention already used forhealthCheck. -
a9101a1: Breaking: a rate limit now always fails the task. The
rateLimit.taskStateconfig option is removed.It previously defaulted to
input-required, on the reasoning that leaving the task open let the client continue the same conversation. That reasoning does not hold:- The interrupted turn cannot resume.
ctx.taskonly suppresses the initialTaskrecord; the prompt sent is always the new user message, and there is no replay or continue-previous-turn logic. A follow-up on the open task is just a new prompt appended to the conversation — exactly what a new task would send. - Continuity never depended on the task state. It comes from the
contextId→ Claude session mapping inSessionManager, which is indifferent to how a task ended. A new task on the samecontextIdresumes the identical Claude session. input-requiredmeans the agent lacks information. A rate limit lacks quota; nothing the client sends unblocks it, only elapsed time. Clients with genericinput-requiredhandling would prompt a human for input nobody wants.- Publishing a non-terminal state alongside
final: truewas also internally inconsistent.
The status message now always points at the
contextIdrather than the closed task, and still carries the structured metadata (reason,rateLimitType,resetsAt,resetsAtIso,utilization) that an orchestrator needs to schedule its own retry.features.emitRateLimitEventsis unchanged.Migration: remove any
rateLimitblock from your config. If your orchestrator alerts on failed tasks, key the suppression onmetadata.reason === "rate_limit". - The interrupted turn cannot resume.
-
3ec9e49: Breaking: session expiry is now disabled by default (
session.ttldefaults to0), and the background cleanup sweep with it (session.cleanupIntervalalso defaults to0). Set"session.ttl": 3600000and"session.cleanupInterval": 300000to restore the previous behaviour.Previously sessions expired one hour after their first message regardless of activity, which silently dropped the
contextId→ Claude session mapping and made a conversation lose all of its context with no error and only aninfo-level log line. Evicting the record reclaimed no disk either — the wrapper never deletes SDK session files, so eviction orphaned the transcript rather than removing it.session.ttl <= 0now disables expiry in both eviction paths. A positivettlbehaves as before.cleanupIntervalis only ever consulted whenttl > 0— with expiry off there is nothing to sweep — so leaving it at300000alongsidettl: 0was dead configuration that read as if it did something. It now defaults to0to match, and both example configs drop it entirely. If you setttlon its own you get expiry via the lazy check ingetOrCreate, but a context that is never used again holds its record until the process exits; setcleanupIntervaltoo if you want that reclaimed. Both disabled states are now logged at startup, so neither can be lost silently.Known consequence: if a Claude session's on-disk transcript is removed while the server is running, the stored
contextId→sessionIdmapping is now pinned for the life of the process instead of being evicted within the hour, so turns on that context keep failing to resume until the server restarts. Previously the one-hour expiry masked this by self-healing. -
b3673aa: Add marketplace plugin support:
claude.marketplacesandclaude.enabledPluginsmap to the SDK's flag-tiersettings(extraKnownMarketplaces/enabledPlugins), so the SDK fetches and installs plugins itself — no pre-baked plugin directories, and no dependence onsettingSources(marketplace plugins load even under full isolation).Because the SDK installs marketplace plugins asynchronously by default — installing nothing, reporting no error, and loading zero plugins on every subsequent run — the wrapper sets
CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1for those sessions and runs a startup preflight that verifies every enabled plugin actually loaded, failinginitialize()with the missing plugin names if not. The preflight diffs the session init message's plugin list rather than theplugin_installevents, which report per-marketplace status and so read as successful even when the named plugin does not exist. The probe costs no tokens (init precedes any model call) and warms the plugin cache. -
9e47b08: Add
claude.effortandclaude.thinking, mapping onto the Claude Agent SDK'sOptions.effortandOptions.thinking.effortacceptslow/medium/high/xhigh/maxand is also settable via theCLAUDE_EFFORTenvironment variable;thinkingaccepts{ type: "adaptive" },{ type: "enabled", budgetTokens?, display? }, or{ type: "disabled" }.Both are additive and optional —
claude.modelremains a plain string andclaude.fallbackModelis unchanged. Values are validated atinitialize(), so an unsupported effort level or a malformedthinkingobject fails at startup with a message naming the allowed values rather than surfacing as an SDK error mid-task.Also fixes
features.emitThinkingEventsproducing no events at all. The SDK leavesthinking.displayat"omitted", so thinking blocks arrive with an empty string and the mapper's non-empty guard drops every one of them. When thinking events are enabled the wrapper now requestsdisplay: "summarized"— supplying a full{ type: "adaptive", display: "summarized" }when no thinking config is set, and filling indisplayon an explicitadaptive/enabledconfig that omits it. An explicitdisplay(including"omitted") and{ type: "disabled" }are both left untouched. Note that with thinking events on and noclaude.thinkingof your own, this turns adaptive thinking on and it costs thinking tokens. -
d6c2701: Add native A2A protocol v1.0 support, built on
@a2a-js/[email protected], while preserving full backward compatibility with A2A v0.3.x clients.@a2a-wrapper/core(breaking):buildAgentCard()now produces the native v1.0AgentCardshape (supportedInterfaces[]instead ofurl/additionalInterfaces/protocolVersion;capabilities.extendedAgentCardinstead ofsupportsAuthenticatedExtendedCard;stateTransitionHistoryremoved). Callers reading the old v0.3 fields directly off the return value must update — the server itself still serves a fully v0.3-shaped card to legacy clients (see below).createA2AServer()'s JSON-RPC/REST transports and the/.well-known/agent-card.jsonroute now negotiate protocol version per-request via theA2A-Versionheader:1.0/absent-with-recent-header gets the native v1.0 card and wire shapes, while0.3/no-header-at-all (matching the SDK's own default) gets a fully v0.3-shaped card and JSON-RPC/REST translation via@a2a-js/sdk'scompat/v0_3layer.ServerOptions.protocolVersiondefault changed from"0.3"to"1.0".- New
ServerOptions.onListeninghook andServerOptions.agentCardSigningoption (Signed Agent Cards, RFC 7515 JWS, opt-in viaAgentCardConfig.signingor the programmatic option). - New
extractUserText(message)export centralizing inboundPartparsing for the v1.0 proto-oneofPartshape. TaskStateis now re-exported as a value (real numeric enum in v1.0, was a string-literal type in v0.3).@a2a-js/sdkpeer/dev dependency bumped to^1.0.0.
Wrapper packages (
a2a-claude,a2a-codex,a2a-copilot,a2a-opencode,a2a-antigravity):- Each wrapper's
createA2AServer()public API is unchanged, but is now a thin delegate to@a2a-wrapper/core'screateA2AServerinstead of duplicating Express/SDK server wiring — restoring the "A2A protocol code lives only in core" design. a2a-copilotanda2a-opencodeno longer ship their own localevent-publisher.ts; both now use@a2a-wrapper/core's (v1.0-aware) event publisher.@a2a-js/sdkdependency bumped to^1.0.0(previously an inconsistent mix of^0.3.9/^0.3.13).
Patch Changes
- Updated dependencies [f3c7062]
- Updated dependencies [41e2d82]
- Updated dependencies [d6c2701]
- @a2a-wrapper/[email protected]
0.2.0
Minor Changes
- 219f014: New wrapper: a2a-claude exposes Claude Code (via @anthropic-ai/claude-agent-sdk) as a spec-compliant A2A server — codex-parity feature set: streaming, per-context session resume, cancellation, permission-mode guardrails, MCP servers, A2A sub-agent delegation, memory materialization, and sideband observability events.