NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Go modules · #688 by repository stars
Last release 3 days ago
05 Oct 2026
Ships on a steady schedule
a new release about every 9 days
Nearly every release is documented
notes for 57 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
11 months old
927 releases · first in 2025
One column per month.
security(deps): bump pytest to >=9.0.3 (CVE-2025-71176) by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/488
⚠️ This is a staging/pre-release version for testing. Not recommended for production use.
# Staging binary (use --staging flag)
curl -fsSL https://agentfield.ai/install.sh | bash -s -- --staging
# Python SDK (prerelease - requires --pre flag)
pip install --pre agentfield
# TypeScript SDK
npm install @agentfield/sdk@next
VERSION=v0.1.138-rc.5 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64af call rejecting valid input for optional reasoner params by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/610_current_status issue where status stuck on `S… by @DebanKsahu in https://github.com/Agent-Field/agentfield/pull/673af install by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/738Note truncated.
POST /api/ui/v2/workflow-runs/:run_id/golden wrote the caller-supplied name and tag list into workflow_runs.metadata with no bounds at all. The name was only TrimSpace'd, so a 1 MiB name persisted verbatim; sanitizeStringList trimmed and de-duped but capped neither the entry count nor the entry length, and it preallocated its output slice (and an unbounded de-dupe map) straight from the attacker-controlled input length. That row is re-read and re-serialised on every runs-list page that contains the run, so both are stored amplification vectors (#944).
Cap tags at 20 entries of at most 64 runes each, and truncate the name at 200 runes. Over-long tags are dropped rather than truncated: byte-slicing can land mid-rune and json.Marshal silently rewrites the invalid UTF-8 to U+FFFD. Lengths are counted with utf8.RuneCountInString so a 64-rune CJK tag survives. The output slice and de-dupe map are now sized min(len(values), maxCount) and the loop stops once maxCount survivors are collected, so a multi-million-entry tag array cannot force a large allocation before the cap applies.
This route is UI-private and its only caller sends one hard-coded tag, so oversized input is bounded silently rather than rejected — a 400 would break the existing "Save as golden run" button. The name fallback is unchanged: an empty name still falls back to run_id, and run_id itself is not truncated.
Forward-only. Nothing re-validates on read, so rows that already hold oversized golden metadata keep reading back exactly as they do today.
The caps are named constants so the follow-up run-metadata endpoint can reuse the same bounds and the same helper.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Adds the behaviour tests for the golden-run caps:
The two pre-existing golden-route tests are left untouched as the behaviour-unchanged regression guard.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
A 'run' namespace in workflow_runs.metadata, written only through a namespace-merging transactional primitive (never the full-row upsert, which clobbers golden/lineage and resets state columns). POST /api/v1/runs/:run_id/metadata read-merge-writes it with strict caps (display_name<=200, labels<=20x64, links<=10, url<=2048, http/https only, no embedded credentials) and creates the carrier row on first write; an optional run_metadata execute field seeds it at dispatch, excluded from the replay dedupe key; restart lineage now writes through the same primitive. The run list, run detail, DAG and agentic overview surface it. external_status is deliberately out of scope.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Write transactions now take the write reservation at BEGIN instead of on first write, so the read-merge-write metadata primitive (and every other BeginTx writer) cannot hit the read->write upgrade deadlock; WAL and the 60s busy timeout were already in place. Global, deliberate change — the full suite gates it.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Display name takes precedence in the run list row, labels render as chips, links render only after scheme re-validation (http/https, host required) with rel="noopener noreferrer"; nothing is treated as trusted HTML.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Concurrent different-namespace writers both survive; lineage seed and metadata merge interleave without clobbering; two executes differing only in run_metadata replay-hit through the real findReplayHit path; byte-identity of untouched namespaces; endpoint-level negatives for every cap and for javascript:/data:/file:/credentialed/scheme-less URLs with storage untouched.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: Claude Fable 5 noreply@anthropic.com (c2df003)
On Kubernetes the control plane closes its listener the instant it is signalled, while kube-proxy is still routing traffic to the pod: in-flight and newly arriving requests get connection refusals for however long endpoint removal takes to propagate. There was also no way to tell a draining control plane from a healthy one -- /health and /api/v1/health answer 200 right up to the moment the listener goes away, so a readiness probe pointed at them can never fail early enough to help.
Add two pieces that fix that together:
/readyz is added to the API-key skip list (the middleware only exempts the /api/v1/health prefix, /health and /metrics), and both paths are listed in the DID auth skip paths.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Each test maps to one observable behaviour rather than to the code shape:
The manifest test reads the chart values and the kustomize base and asserts the shipped readinessProbe path is still /api/v1/health. The chart defaults to image tag latest with IfNotPresent and one replica, so flipping that path to one an older cached image does not serve would leave the Service with zero endpoints -- that regression should fail a test, not a cluster.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
The chart and the kustomize base now set AGENTFIELD_SHUTDOWN_MIN_DELAY to 5s and raise the control-plane pod grace from 45 to 60 seconds, which is what the shutdown actually needs: 5s minimum delay + the 30s AGENTFIELD_SHUTDOWN_TIMEOUT drain + roughly 20s of tail (a fresh >=5s async-pool budget plus 5s each for package maintenance, the observability forwarder and the tracer). The agent templates are left alone.
The readinessProbe path is deliberately NOT flipped to the new shutdown-aware route. controlPlane.image.tag defaults to latest with pullPolicy IfNotPresent and replicaCount 1, so a chart upgrade can land on a node holding an older cached image; pointing the probe at a path that image 404s would leave a single-replica Service with zero endpoints. The path moves behind controlPlane.readinessProbe.path, defaulting to today's /api/v1/health, with a comment saying when it is safe to switch.
The min-delay env entry is skipped when controlPlane.env already defines AGENTFIELD_SHUTDOWN_MIN_DELAY, so an explicit operator value never renders a duplicate env name.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
The Kubernetes guide told operators to raise terminationGracePeriodSeconds when they raised AGENTFIELD_SHUTDOWN_TIMEOUT, and stopped there. That is the exact configuration that breaks: the deferred reap fires AGENTFIELD_AGENT_DRAIN_GRACE after the REPLACEMENT registers, regardless of how much drain budget the departing pod still has, and under a default rolling update the replacement is Ready and registered before the old pod is even signalled. A reader following the old text ends up with a long reasoner reaped mid-flight and a 409 on its own success callback.
State the invariant instead, and the things a reader cannot guess:
Also document the new AGENTFIELD_SHUTDOWN_MIN_DELAY and readiness routes, the real control-plane pod-grace arithmetic (min delay + shutdown timeout + ~20s of tail, not the optimistic +5s), and reconcile the agent pod-grace floor to one number (+15s) across both files. Drops the stale claim that Agent.setup_signal_handlers() is retained for compatibility -- that delegate was removed and this line was its last mention in the repo.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Review caught 11m15s being written as 690s. Derive the number from the stated invariant (budget + settlement + headroom) instead.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
The values.yaml parse test would keep passing if the deployment template stopped referencing controlPlane.readinessProbe.path, shutdownMinDelay or terminationGracePeriodSeconds. Table-driven template-reference assertions close that hole without depending on a helm binary.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: Claude Fable 5 noreply@anthropic.com (e1c67ba)
LangFuse/Logfire/Langsmith users see app.ai completions as anonymous
LLM calls: nothing correlates a generation back to the AgentField run,
execution or reasoner that produced it, and wiring litellm callbacks by
hand means editing every acompletion site (#990, #997).
Add agentfield/litellm_observability.py:
AGENTFIELD_LITELLM_CALLBACKS — comma-separated litellm callback names,
trimmed, lowercased, deduped, registered once per process through
logging_callback_manager.add_litellm_callback, with a hand-rolled
dedupe fallback when that private attribute is absent. Unset means we
never import litellm and never touch its callback state, so today's
behaviour is unchanged by default. Registration failures are logged and
swallowed — a bad callback name must not stop an agent from booting.
Unknown names are passed through rather than allowlisted, because
litellm's own _known_custom_logger_compatible_callbacks omits real
callbacks (helicone, lunary, athina, plain s3).app.ai text completion:
agentfield_execution_id, agentfield_run_id, agentfield_agent_node_id,
agentfield_reasoner, plus session / parent execution ids when the
context has them. metadata is a litellm-only param, so none of this
reaches the provider request body. Opt out with
AGENTFIELD_LITELLM_METADATA=false, mirroring
AGENTFIELD_OPENROUTER_ATTRIBUTION.The LangFuse-native aliases (trace_id, session_id, trace_name, generation_name, tags) are stamped only when AgentField itself registered a callback. litellm's callback state is process-global, so stamping them unconditionally would silently re-key, rename and re-tag every generation belonging to a user who had already wired up LangFuse themselves.
user_id and requester_metadata are never emitted: anthropic copies
metadata["user_id"] into the request body and vertex turns
metadata["requester_metadata"] into request labels.
The stamp is applied in AgentAI.ai rather than in
AIConfig.get_litellm_params, because the two text-to-speech paths splat
that config into calls with no metadata kwarg. It is re-applied at the
top of the tool-loop completion so each turn gets its own metadata dict
instead of sharing one object through the loop's shallow param copies.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
32 tests over the new module, driven by injected litellm stubs so the
process-global callback lists are never mutated by accident; the two
tests that do touch the real module snapshot and restore
callbacks / success_callback / failure_callback.
The wire test is the important one. Asserting that metadata is in
litellm's all_litellm_params would only restate litellm's own table,
and that table moves — the SDK floats litellm on Python 3.11+. Instead a
stdlib HTTPServer on 127.0.0.1 captures the real request body for the
openai/, litellm_proxy/ and openrouter/ routes and asserts no metadata
key and no agentfield_* key ever reaches it.
The canary comment records why: the module reads litellm's private
logging_callback_manager and _known_custom_logger_compatible_callbacks,
and the weekly canary already runs this module via run_pytest.sh, so an
upstream rename surfaces there rather than in a user's process.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
New top-level page rather than docs/integrations/, which is reserved for control-plane integration packs.
Beyond the env vars and the metadata field list, the page states the three things that bite people: litellm callback state is process-global, so a process hosting several Agents applies the union of their configured callbacks; langfuse and logfire are not SDK dependencies and litellm degrades a missing one to a logged non-blocking error rather than failing the call (verified against litellm 1.98.0); and an otel-family callback produces a second trace tree disconnected from the control plane's own OTLP spans, because nothing in this repo propagates W3C traceparent to agent nodes.
It also names what the stamp does not cover — image generation and the
harness schema-repair call go to litellm directly, and the text-to-speech
paths take metadata= explicitly — so nobody reads the feature as wider
than it is.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Review follow-ups on the opt-in LiteLLM observability module.
Gate the LangFuse-native metadata aliases (trace_id, session_id, trace_name, generation_name, tags) on AgentField having registered a LangFuse-family callback rather than on it having registered any callback at all. The previous gate still re-keyed, renamed and re-tagged an application's own LangFuse generations whenever the operator set AGENTFIELD_LITELLM_CALLBACKS to a different vendor, which is the hazard the contract item was written to prevent.
Record a callback in _AGENTFIELD_REGISTERED only when a registration branch actually installed it. Given neither a logging_callback_manager nor a litellm.callbacks list, register_callbacks previously reported success and flipped the alias gate on for a callback that was never installed anywhere.
Copy a stamped list value when merging, so the tool-calling loop's
shallow {**litellm_params} copies do not share one metadata["tags"]
list across turns -- LangFuse-style integrations append to it.
Replace the two dead metadata.pop() calls with a comment recording why user_id and requester_metadata must never be added: LiteLLM's anthropic transform copies metadata["user_id"] into the provider request body, and its vertex transform turns metadata["requester_metadata"] into request labels.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Skip the openrouter parametrization of test_metadata_never_reaches_the_wire
on LiteLLM below 1.98. That version routes an openrouter/ model carrying
a custom api_base through the OpenAI SDK while still applying
OpenrouterConfig.transform_request, which unconditionally injects a
top-level usage key that AsyncCompletions.create() rejects. pyproject
caps LiteLLM at <1.98.0 on Python 3.10, so the CI matrix's 3.10 leg failed
on LiteLLM's own request shaping rather than on the metadata under test.
Real AgentField openrouter traffic sets no api_base and is unaffected, and
the route still runs on 3.11+.
Cover the two behaviour fixes: a non-LangFuse callback registered by AgentField must not stamp the LangFuse aliases even when the application registered langfuse itself, and register_callbacks must report nothing when neither registration branch can install anything.
Make three existing assertions load-bearing. Run the TTS one inside a live ExecutionContext so it fails if the stamp ever migrates into AIConfig.get_litellm_params; exercise the alias branch in the user_id/requester_metadata test so a future alias addition is caught; and assert the tool loop's turns do not share one tags list.
Clear _AGENTFIELD_REGISTERED on fixture entry so the alias-absence assertions cannot become order-dependent.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
The callback-native aliases are stamped only when AgentField itself
registered langfuse or langfuse_otel, not on any AgentField-registered
callback, so that registering some other vendor cannot silently re-key an
existing LangFuse setup's generations.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
The re-review caught the new module missing from the --cov list, so its patch coverage was never measured. Full sdk-python gate re-run with it included: all pass.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: Claude Fable 5 noreply@anthropic.com (41699ab)
docs/api/AGENT_NODE_LOGS.md has promised AGENTFIELD_LOG_STDOUT SDK-agnostically
since it was written, and the Python (_stdout_mirror_enabled) and Go
(executionLogStdoutEnabled) SDKs both honor it. The TypeScript
ExecutionLogger did not: mirrorToStdout defaulted to true and nothing ever
read the environment, so a TypeScript node had no way to turn the structured
stdout mirror off (#985).
mirrorToStdout becomes tri-state (boolean | undefined). An explicit option
still wins in both directions; when it is absent the flag is resolved from the
environment. The resolution happens per emit rather than in the constructor
because Agent builds one shared ExecutionLogger at construction time, so a
snapshot would freeze the flag for the whole process lifetime — Python and Go
both re-read it on every record.
The accepted falsy spellings (0/false/no/off, case-insensitive,
whitespace trimmed) move into a new internal utils/envFlags helper that
processLogs.ts now shares, so the list cannot drift between modules or
against the other SDKs. The helper guards process with
typeof process !== 'undefined', matching the guard ExecutionLogger already
uses for process.stdout, so the class stays usable outside Node.
The default is unchanged: unset, empty, or any unrecognised value keeps the mirror on, so a typo cannot silently drop log output, and nothing in the repo sets this variable. Serialization now happens inside the mirror branch — with the mirror off the JSON envelope is never built, which is the cost the flag exists to avoid (the transport is handed the object, not the string).
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
node_logs.max_line_bytes() had no direct test, yet its parsing differs from
the Go and TypeScript SDKs in ways the environment-variable reference is about
to describe: Python clamps every integer below 256 up to 256 (including zero
and negatives) where Go and TypeScript reject those values and fall back to
16384, and Python's int(raw, 10) rejects 512abc where TypeScript's
parseInt prefix-parses it to 512.
Table-driven so the documented matrix and the code cannot drift apart.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
AGENTFIELD_LOG_STDOUT was documented under "Python SDK agents" even though the Go SDK reads it too (and now the TypeScript SDK does). It moves to "Structured logging (SDKs)" with an explicit reader list; a pointer stays in the Python section so a reader scanning only their own section does not lose it. Two consequences that were previously undocumented are stated so they are not later filed as regressions: a record with no execution id is skipped by control-plane dispatch in all three SDKs and is therefore dropped entirely when the mirror is off, and because the node-log ring is fed by captured stdout, disabling the mirror also empties structured records out of GET /agentfield/v1/logs.
The AGENTFIELD_LOG_MAX_LINE_BYTES entry claimed "minimum: 256" and that "the Go
and TypeScript SDKs treat invalid values as unset". The first is misleading and
the second is false. Python clamps sub-256 integers up to 256; Go and
TypeScript reject them upward to the 16384 default, so =100 yields a cap 64x
larger than requested. Python and Go reject non-integers outright while
TypeScript's parseInt prefix-parses (512abc -> 512). The Python clamp also
governs both Python log paths — the stdout/stderr tee behind
/agentfield/v1/logs and the structured-mirror elision budget — not just the
mirror. Every number here is pinned by the new test_node_logs.py table.
Each SDK README gains a short Logging section carrying that SDK's own numbers. The Python one uses absolute github.com URLs because that file is the PyPI long_description, where relative links render dead.
No code behaviour changes in this commit.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: Claude Fable 5 noreply@anthropic.com Co-authored-by: Santosh kumar 29346072+santoshkumarradha@users.noreply.github.com (4b2b8bd)
Nothing published for this version
security(deps): bump pytest to >=9.0.3 (CVE-2025-71176) by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/488
⚠️ This is a staging/pre-release version for testing. Not recommended for production use.
# Staging binary (use --staging flag)
curl -fsSL https://agentfield.ai/install.sh | bash -s -- --staging
# Python SDK (prerelease - requires --pre flag)
pip install --pre agentfield
# TypeScript SDK
npm install @agentfield/sdk@next
VERSION=v0.1.138-rc.4 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64af call rejecting valid input for optional reasoner params by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/610_current_status issue where status stuck on `S… by @DebanKsahu in https://github.com/Agent-Field/agentfield/pull/673af install by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/738Note truncated.
#1019 replaced the /\/+$/ replace in the TypeScript SDK's LocalVerifier
after CodeQL flagged it as js/polynomial-redos. Four copies of the same
pattern remained in desktop/: catalog.sourceRepo, cloudUpdate.normalizedUrl
(twice) and cpClient.request.
CodeQL does not flag those — they take operator config, not attacker input, so there is no taint path — but there is no reason to keep four copies of a pattern the scanner objects to when one linear helper does the job.
Add shared/trimSlashes.ts with the same backward index scan LocalVerifier now uses, and route all four call sites through it. Behavior is unchanged for every input, interior '//' in catalog source strings included. (7f58f58)
The ruff hooks have been dead. sdk/python/pyproject.toml selects the
ASYNC ruleset, which the pinned v0.6.9 does not know, so both hooks
aborted before linting anything:
ruff failed
Cause: Failed to parse sdk/python/pyproject.toml
Cause: TOML parse error at line 136, column 1
Unknown rule selector: `ASYNC240`
Every Python commit hit this, which is how #1018 ended up needing --no-verify. Pin to v0.15.22, the version .github/workflows/sdk-python.yml already installs, so local and CI enforce one contract. 0.16.x stays out of scope: it flags ~2700 pre-existing violations repo-wide.
Scope both hooks to ^sdk/python/, matching that workflow's working-directory. Unscoped they run from the repo root and surface 53 pre-existing errors in examples/ and scripts/ that CI has never checked — a real backlog, but one that deserves its own PR rather than arriving as a side effect of a version bump.
Also switch ruff to ruff-check; the old id is now a legacy alias.
Verified: pre-commit run --all-files ruff-check passes, and the file
that forced --no-verify on #1018 passes every hook. (d48f40f)
POST /api/ui/v2/workflow-runs/:run_id/golden wrote the caller-supplied name and tag list into workflow_runs.metadata with no bounds at all. The name was only TrimSpace'd, so a 1 MiB name persisted verbatim; sanitizeStringList trimmed and de-duped but capped neither the entry count nor the entry length, and it preallocated its output slice (and an unbounded de-dupe map) straight from the attacker-controlled input length. That row is re-read and re-serialised on every runs-list page that contains the run, so both are stored amplification vectors (#944).
Cap tags at 20 entries of at most 64 runes each, and truncate the name at 200 runes. Over-long tags are dropped rather than truncated: byte-slicing can land mid-rune and json.Marshal silently rewrites the invalid UTF-8 to U+FFFD. Lengths are counted with utf8.RuneCountInString so a 64-rune CJK tag survives. The output slice and de-dupe map are now sized min(len(values), maxCount) and the loop stops once maxCount survivors are collected, so a multi-million-entry tag array cannot force a large allocation before the cap applies.
This route is UI-private and its only caller sends one hard-coded tag, so oversized input is bounded silently rather than rejected — a 400 would break the existing "Save as golden run" button. The name fallback is unchanged: an empty name still falls back to run_id, and run_id itself is not truncated.
Forward-only. Nothing re-validates on read, so rows that already hold oversized golden metadata keep reading back exactly as they do today.
The caps are named constants so the follow-up run-metadata endpoint can reuse the same bounds and the same helper.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Adds the behaviour tests for the golden-run caps:
The two pre-existing golden-route tests are left untouched as the behaviour-unchanged regression guard.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: Claude Fable 5 noreply@anthropic.com (e1f2831)
_bounded_mirror_line serialized the whole record with json.dumps before it knew whether the record fit the mirror budget, and serialized it a second time at the end to report original_size. A record carrying a multi-megabyte string attribute therefore passed that payload to json.dumps twice, and every size check did len(<str>.encode("utf-8")), materialising a throwaway multi-megabyte bytes copy purely to count.
Three changes, all inside that one private method and the helpers next to it:
_json_key fixes a latent off-by-two while it is in here: json coerces non-str dict keys (1 -> "1", True -> "true", None -> "null"), so measuring the raw key undercut the attributes size by two bytes per non-str key and could push an elision boundary the wrong way. bool is checked before int because isinstance(True, int).
Output is unchanged: a 220-case differential harness (hand-picked edges plus 200 randomised records across budgets 256-16384) produces byte-identical lines against the previous implementation, and the record handed to _dispatch_to_cp is still only ever shallow-copied. Measured, mean of 5: one 5 MB str attribute 20.4 ms -> 9.0 ms (json.dumps calls carrying a >=1 MB payload: 2 -> 1), 200 x 50 KB attributes 46.2 ms -> 18.8 ms, and a fitting 14.7 KB record stays at 0.18 ms.
Refs #985
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Regression tests for the mirror-line precheck, derived from the behavior contract rather than from the implementation:
No wall-clock assertion is added: the post-fix timing has only ~4.6x headroom and would be a CI flake source. Every existing structured-mirror test — including the exact <4002>/<4004> markers and the elapsed < 0.2 guard — passes unmodified.
Refs #985
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Both new guards passed against main's logger.py, so neither actually guarded the behaviour it was named for.
test_..._serializes_large_string_payload_at_most_once classified json.dumps calls by their argument (a >=1MB str). The redundant call this PR removes passes the record dict, not the 5 MB string, so both versions showed exactly one big-str argument. Count the output size instead: 1 on the branch, 2 on main, 2 with the precheck neutered.
test_..._fitting_nested_record_is_not_walked_recursively used a tripwire that raised AssertionError, but _certainly_exceeds_budget swallows every exception and falls back to the full-dumps path, so a recursive precheck silently emitted the byte-identical line the test asserted. Record the touches in a list and assert it stays empty; the byte-identical assertion is kept alongside it.
Verified by mutation: with return False inserted at the top of
_certainly_exceeds_budget the first test now fails (2 != 1), and with
_shallow_payload_bytes made recursive over dicts/lists the second fails
with 2800 recorded touches. Both were green before this change.
Also applies ruff format to the one hunk in
test_..._non_string_attribute_keys_have_exact_sizes that had drifted, so
the file stays format-clean.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: Claude Fable 5 noreply@anthropic.com (5a0d275)
Nothing published for this version
security(deps): bump pytest to >=9.0.3 (CVE-2025-71176) by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/488
⚠️ This is a staging/pre-release version for testing. Not recommended for production use.
# Staging binary (use --staging flag)
curl -fsSL https://agentfield.ai/install.sh | bash -s -- --staging
# Python SDK (prerelease - requires --pre flag)
pip install --pre agentfield
# TypeScript SDK
npm install @agentfield/sdk@next
VERSION=v0.1.138-rc.3 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64af call rejecting valid input for optional reasoner params by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/610_current_status issue where status stuck on `S… by @DebanKsahu in https://github.com/Agent-Field/agentfield/pull/673af install by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/738Note truncated.
fix(sdk/python): parse URL in logger test to resolve CodeQL substring-sanitization alert
review: assert the parsed hostname from the captured log line
The first pass added urlparse("https://api.openai.com").hostname == "api.openai.com", which parses a hardcoded literal and can never fail.
It asserts that urllib works, not that the logger emitted anything, and
left the original substring check as the only real assertion.
Pull the URL back out of caplog.text and compare parsed hostnames for
equality. Any in against a bare hostname literal, set membership
included, still trips py/incomplete-url-substring-sanitization, so the
check is written as any(... == ...).
Verified 17 passed, and that the assertion fails when the logged host changes. (35c1f18)
CodeQL alert #54 (js/polynomial-redos) flagged //+$/ in LocalVerifier's constructor as a polynomial-time regex on uncontrolled input. Trim trailing slashes with a plain loop instead; behavior is identical.
Fixes CodeQL alert #54.
The first pass traded the regex for while (url.endsWith('/')) url = url.slice(0, -1), which reallocates the string once per trailing slash
and is quadratic on the very input the CodeQL alert was about.
Walk an index backwards and slice once instead: linear, one allocation,
still no regex. Rewrite the comment, which described a .test call the
original code never made.
Add constructor tests covering single, repeated, absent, empty-string and all-slash inputs, plus a 200k-slash run to catch a regression back to quadratic behavior. (9af2d49)
security(deps): bump pytest to >=9.0.3 (CVE-2025-71176) by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/488
⚠️ This is a staging/pre-release version for testing. Not recommended for production use.
# Staging binary (use --staging flag)
curl -fsSL https://agentfield.ai/install.sh | bash -s -- --staging
# Python SDK (prerelease - requires --pre flag)
pip install --pre agentfield
# TypeScript SDK
npm install @agentfield/sdk@next
VERSION=v0.1.138-rc.2 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64af call rejecting valid input for optional reasoner params by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/610_current_status issue where status stuck on `S… by @DebanKsahu in https://github.com/Agent-Field/agentfield/pull/673af install by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/738Note truncated.
Adds production resilience to the Go SDK AI client, matching the Python and TypeScript SDKs.
Addresses review feedback on PR #1009. The previous half-open handling cleared circuitOpenTime on the first caller after the timeout, so every concurrent caller also passed through - reopening the floodgate instead of admitting one probe.
Replace the implicit time-based reset with an explicit state machine:
Adds tests for single-probe admission (including a 50-goroutine race test), failed-probe reopen, and probe release on non-rate-limit errors. (5097a58)
security(deps): bump pytest to >=9.0.3 (CVE-2025-71176) by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/488
⚠️ This is a staging/pre-release version for testing. Not recommended for production use.
# Staging binary (use --staging flag)
curl -fsSL https://agentfield.ai/install.sh | bash -s -- --staging
# Python SDK (prerelease - requires --pre flag)
pip install --pre agentfield
# TypeScript SDK
npm install @agentfield/sdk@next
VERSION=v0.1.138-rc.1 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64af call rejecting valid input for optional reasoner params by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/610_current_status issue where status stuck on `S… by @DebanKsahu in https://github.com/Agent-Field/agentfield/pull/673af install by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/738Note truncated.
Adds control-plane/internal/handlers/discovery_filters_test.go. Test-only; no source changes.
Covers the query-parsing and response-building helpers:
Coverage on the targeted functions: parseDiscoveryFilters 85.7 -> 100, collectAgentIDs 81.8 -> 100, decodeSchema/extractDescription/matchesTags/parseCSV/parseBool/parseInt 100, buildDiscoveryResponse 96.2, extractExamples 94.1. (a9ec91d)
Adds three test files under control-plane/internal/events. Test-only; no source changes.
Closes the coverage gaps left after prior events tests: cleanupEventCache 0 -> 100, StartHeartbeat 0 -> 100, StartNodeHeartbeat 0 -> 100, PublishNodeStatusUpdatedEnhanced 57 -> 100. Package total 87.0 -> 97.2 percent. (4dc4d17)
`bash curl -fsSL https://agentfield.ai/install.sh | bash `
curl -fsSL https://agentfield.ai/install.sh | bash
VERSION=v0.1.137 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64Full Changelog: https://github.com/Agent-Field/agentfield/compare/v0.1.136...v0.1.137
security(deps): bump pytest to >=9.0.3 (CVE-2025-71176) by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/488
⚠️ This is a staging/pre-release version for testing. Not recommended for production use.
# Staging binary (use --staging flag)
curl -fsSL https://agentfield.ai/install.sh | bash -s -- --staging
# Python SDK (prerelease - requires --pre flag)
pip install --pre agentfield
# TypeScript SDK
npm install @agentfield/sdk@next
VERSION=v0.1.137-rc.14 curl -fsSL https://agentfield.ai/install.sh | bash
Download the binary for your platform below, make it executable, and move it to your PATH.
agentfield-darwin-amd64agentfield-darwin-arm64agentfield-linux-amd64agentfield-linux-arm64af call rejecting valid input for optional reasoner params by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/610_current_status issue where status stuck on `S… by @DebanKsahu in https://github.com/Agent-Field/agentfield/pull/673af install by @AbirAbbas in https://github.com/Agent-Field/agentfield/pull/738Note truncated.
fix(server): cancel streams before graceful shutdown
fix(server): treat drain timeout as successful shutdown
fix(go-sdk): accept dispatch during shutdown notify
docs(shutdown): clarify limits and grace periods
fix(config): accept bare seconds for AGENTFIELD_SHUTDOWN_TIMEOUT like the SDKs do (b01e831)
Adds control-plane/internal/storage/migrations_test.go covering migrateAgentNodesCompositePK and autoMigrateSchema for local (SQLite) mode. Test-only; no source changes.
Six subtests seed the legacy agent_nodes schema via raw SQL and inspect pragma_table_info afterward:
Your coding agent can read these notes before it upgrades. Set up the MCP server →