Repository navigation
Releases: monaccode/astromesh-a2a
Releases · monaccode/astromesh-a2a
Release list
astromesh-a2a v0.1.1
Added
- Task API on
RemoteAgent, over the A2A 1.0 methods:submit()(SendMessagewith
returnImmediately: returns as soon as the task exists),get_task()(GetTask),
subscribe()(SubscribeToTask, anEventStreamwith the same events, caps and policy
asstream()) andlist_tasks()(ListTasks, filtered by context and state,
paginated) returning the newTaskPage. Every call goes through the budget, timeout,
part policy, trace redaction and error translation ofsend()/stream(). These
report a task as it is, soResult.statusgainssubmitted,working,failed,
canceledandrejectedfor them;send()/reply()are unchanged. RemoteAgent.reply(task_id, text, *, context_id=None): the task's context can be
passed explicitly (a task from anotherRemoteAgent, a restart, a store); without it
the remembered one is used, as before.astromesh_a2a.adaptersexportstool_descriptionandPENDING_NOTE(also still in
adapters._common), to build your own tool with the same safety text.- Server:
create_appservesreturnImmediately(the handler keeps running in the
server, still bounded byhandler_timeout),GetTask,ListTasksand
SubscribeToTask, scoped per caller identity likeGetTask/CancelTask(tested:
another caller cannot get, list or subscribe to a task). - Ids the client sends (
task_id,context_id,page_token) are checked before any
request: a str of at most 256 characters, no control characters, elseValueError
(send()/stream()/submit()still acceptcontext_id=""as "none").
Changed
tool_description()(the default adapter tool description) also tells the model never
to answer a confirmation or approval on the user's behalf, like the Astromesh core's.
Compatibility
send()andreply()return the same statuses as in 0.1.0 (completed,
input_required,auth_required) and raise as before. The newResult.statusvalues
(submitted,working,failed,canceled,rejected) only come from the task API
(submit(),get_task(),list_tasks()).
astromesh-a2a v0.1.0
First release.
Added
net: pinned egress transport (PinnedTransport,safe_client). Only http/https and
public hosts; each host is resolved once and the connection pinned to the checked IP
(no DNS-rebinding window); no redirects; decoded response bodies capped; guard
failures raiseBlockedDestination.Policy: allow-list of hosts/card URLs (nothing allowed by default; URLs with user
info refused), accepted part kinds, byte/time/event caps, budgets (Budget, with
BudgetMeter.admit()/remaining_seconds()/record_seconds()), optional card
signatures with keys pinned bykid(signing_keys,signing_algorithms;signing
extra, PyJWT),max_stream_bytes(default 8 MiB) andcard_ttl(default 300 s: the
card is fetched and vetted again after it expires; a refresh that fails the checks
leaves no stale card behind).- Typed errors:
A2AError,PolicyRejected,BlockedDestination,TaskFailed
(remote=Trueonly when the remote itself reported the task ended),A2ATimeout,
UnsupportedVersion; localRuntimeErrorsLoopBindingErrorandAgentClosedError,
never translated. Trace: per-stage trace with secret redaction (keys, URL userinfo, query parameters
incl. presigned-URL signatures/credentials, Bearer/Basic values) and W3Ctraceparent.RemoteAgenton the officiala2a-sdk: the card is fetched and vetted by the library
(policy host, signature, A2A 1.x JSON-RPC interface only, interface host allowed)
before any SDK client exists;card()returns a frozenRemoteCard(name,
description,version,interface_url,protocol_version,streaming,
has_signatures,signature_verified,skills,.raw).send(),reply(task_id, text),cancel(task_id)andstream()(anEventStreamofEvent(kind, payload)
with its own trace) each run under the policy timeout; streams are also capped by
max_eventsand closed onasync withexit oraclose(). Origin andtraceparent
go in the message metadata. Parts the policy does not accept are dropped and traced;
PolicyRejectedonly when nothing acceptable came back. A confirmation is never
answered automatically:send()returnsinput_requiredwith the question in
Result.pending, and only an explicitreply()continues the task. Every call is
admitted against the budget before any request (card included) leaves. Unexpected
SDK errors become a genericTaskFailedthat never quotes the remote. An agent is
bound to the event loop of its first I/O and refuses others.Auth(bearer,api_key,from_callable): secrets read at call time from env vars
or callables, never stored, traced or shown in a repr. Sent on the card fetch and on
every RPC, only to policy-allowed hosts.astromesh_a2a.testing.allow_loopback(agent): the explicit, per-agent opt-in to reach
loopback, for local development and tests only (never production);Policyhas no
flag for it.- Server (
serverextra, imported lazily):create_app(handler, card, auth=...)
(Starlette, mountable) andserve(...)(uvicorn) expose any async agent as an A2A
agent: card at/.well-known/agent-card.jsonbuilt fromCardSpec/Skill, JSON-RPC
1.0 (blocking and SSE) at/a2a. Auth is mandatory (api_key_auth(env, header),
constant-time and fail closed, or anyasync (Request) -> bool | str;401JSON on
refusal); without itcreate_app/serveraiseValueErrorunlessinsecure=True.
Bodies overmax_body_bytes(default 1 MiB) answer413, a malformed
Content-Length400. OnlyA2A-Version: 1.xis served. Tasks are scoped by caller
identity;task_store=(default: the SDK's in-memory store) must keep that scoping.
handler_timeout(default 300 s) and handler exceptions fail the task with a generic
message. Incoming/Outgoing: every incoming message reaches the handler asOrigin.AGENT,
untrusted=True, whatever the remote claims (claimed_originkept for logging only);
file parts are dropped.Outgoing.needs_confirmationputs the task in
input_required; the remote's reply comes back as untrusted input withis_replyand
pending, and the library never marks anything as confirmed.Incoming.session_key
("<caller>:<context_id>") is what to key memory by.RemoteAgent.as_tool(framework)andastromesh_a2a.adapters: a remote agent as a
native tool of LangChain, CrewAI, Google ADK, OpenAI Agents SDK, PydanticAI,
LlamaIndex, smolagents, Microsoft Agent Framework and MCP (FastMCP;build_server
serves it as a whole MCP server). Onequeryinput; the tool keeps one conversation
and replies to a task waiting for input; output wrapped bywrap_untrusted()with a
note that only the user may answer a pending question; errors use each framework's
convention with a safe message; names (from the card orname=) sanitized for every
framework. Sync calls are dispatched to the agent's loop (run_sync) and queued per
tool before dispatch.MissingExtraErrornames the extra to install.adapters.server_langchain.handler_from_runnable(runnable): a LangChain runnable as a
serve()handler.- Attack eval (
astromesh_a2a.eval,python -m astromesh_a2a.eval): nine local
malicious remotes (card_spoof,redirect_internal,dns_private,huge_artifact,
file_part,injection_text,self_confirm,endless_stream,version_0_3), each
with a precise "stopped" rule; the default policy stops 9/9 and CI enforces it. - Documentation (README in English and Spanish, guides under
docs/), logo, runnable
examples (examples/hello_local.pyserves and calls an agent offline), CI (lint, core
matrix 3.10-3.13, eval, adapters with every extra) and a release workflow (TestPyPI,
smoke install from the wheel's metadata, PyPI and GitHub Release on av*tag).