Skip to content

Release: Phase 2 — Differentiate, go beyond (0.2.0) - #1522

Merged
tommasobolis merged 177 commits into
mainfrom
develop
Oct 30, 2026
Merged

tommasobolis merged 177 commits into
mainfrom
develop

Conversation

@tommasobolis

Copy link
Copy Markdown
Collaborator

Promotion of develop → main for 0.2.0. develop reads __version__ = "0.2.0" (#1521), and main is on 0.1.3.

Included

Everything on develop since the 0.1.3 promotion (#1138): Phase 2 (epic #260) and the 0.2.0 pre-release code review (epic #1414). The release readiness work is tracked in #1435. Per-PR list: main...develop

Still open in the milestone, and closed after the tag: #1435, #1414, #260, and #219 (its demos PR pins the published package).

Breaking changes

From 0.1.3. No name was removed from donkey_kit, and no extra was dropped. These changes can still affect existing code. Each has its own section, with the fix, in MIGRATION.md.

From a 0.2.0 pre-release (0.2.0.devN, 0.2.0rc1). These surfaces first shipped in the 0.2.0 pre-releases, and they changed before the final with no deprecation cycle. The full list is in MIGRATION.md. The changes most likely to need a code edit:

  • Identity: donkey.as_user(id_token=...) is donkey.as_user(token=...), and token_exchange_path is gone. as_user() refuses an llm_proxy_url from the working-directory config files. Set it in the environment or in code, or set DONKEY_TRUST_PROJECT_CONFIG=1.
  • Tools: ToolSet.openai() is ToolSet.openai_agents(). A per-tool binding raises AuthError under a different as_user() user, so bind inside the block that runs the agent. A gateway with a plain http:// public URL is refused unless DONKEY_ALLOW_HTTP=1 is set.
  • Approvals: donkey.approvals is per client, and the module-level instance is gone (ADR 0018). An unknown approval id raises ApprovalNotFound, which is still a KeyError. ApprovalRejected and GatewayApprovalDenied share the new ApprovalError base. A resumed approval runs as its requester. The adapter helpers return Approval objects, with the framework object on .native. A Strands approval covers exactly one call. require_approval() on a whole-server binding raises ConfigError.
  • CLI and Action: donkey dev --version is --agent-version. The --json documents carry their own format key (publish_format, verify_format, expose_format). A failed publish --json prints its outcome and exits 1. donkey verify exits 2 with no gateway credential. Pass the Action's client-secret from secrets.*; its skipped output is gone.
  • Errors: the ADK, LangGraph and Agent Framework helpers raise the install-hint ImportError instead of a bare ModuleNotFoundError.

Notes

donkey-kit 0.2.0: Phase 2, Differentiate, go beyond. This release adds the capabilities a stock OpenAI client with a base_url can't offer, because they depend on the platform behind the gateway. You get governed MCP tool access, publication to Exchange from your code and CI, on-behalf-of user identity, and human-in-the-loop approvals, including the gateway's own CIBA step-up. You also get an A2A server built on the official a2a-sdk, declarative refusal handlers, typed custom policies, and the OpenAI Agents SDK as the second deep adapter.

pip install --upgrade "donkey-kit==0.2.0"
pip install --upgrade "donkey-kit[all]==0.2.0"   # now includes mcp and a2a

Highlights

Extras changes (§8.4)

Verification-status changes (§0.3)

In docs/verified-apis.md:

Acceptance

donkey-kit==0.2.0 from TestPyPI: passed. This was a live run of all seven groups (./run.sh --version 0.2.0 --index testpypi) on Python 3.12, against the artifact this Release publishes.

  • Proxies: all 24 declared proxies were configured, and none was skipped for missing credentials.
  • Results: 559 passed, 33 skipped, 0 failed.
Session Passed Skipped
[all] + langchain-mcp-adapters (data plane, governance, public surface, blocked, telemetry, frameworks) 415 27
[all,adk] 21 1
[all,strands] 22 1
[all,agent_framework] 12 1
[all,openai-agents] 27 1
[all,anthropic] 11 0
[all,crewai] + crewai-tools[mcp] 12 1
[all,llamaindex] + llama-index-tools-mcp 9 0
packaging 30 1
  • Coverage map: consistent. 108 features are mapped, and 321 of 330 collected tests are claimed by a feature.
  • Offline published gate (groups 3, 4 and 5): passed in the TestPyPI publish run for 0.2.0.
  • Release candidate: 0.2.0rc1 also passed the live run (546 passed, 33 skipped, 0 failed).

tommasobolis and others added 30 commits October 14, 2026 20:37
…1143)

* chore(deps): refresh the PR CI lock

Recompiled by lock-refresh.yml after a green nightly: https://github.com/Donkey-Development-Kit/donkey-development-kit/actions/runs/38558337462

* chore(deps): ship the framework releases in the lock refresh (#1126)

The nightly lock refresh moved every pending framework release into the
PR CI lock (agent-framework 1.21.0, anthropic 1.13.0, langgraph 1.2.15
with langchain-core 1.6.10 and langchain-openai 1.7.1, strands-agents
1.60.0, google-adk 2.12.0 with litellm 1.105.1, crewai 1.15.28, openai
3.31.0, llama-index-llms-openai 0.8.3). It also moved ruff from 0.16.10
to 0.17.0, but left the ruff-pre-commit rev in .pre-commit-config.yaml at
v0.16.10. test_pre_commit_runs_the_lint_jobs_tool_versions pins the two
together, so every session that runs the whole suite failed on it.

Bump both ruff hooks to v0.17.0. Under ruff 0.17.0 the tree passes ruff
check and ruff format --check, and the python/**/*.md code blocks the
hook formats are already formatted, so no code changes.

Record each framework's offline re-check in docs/verified-apis.md
section 8. The live rows keep their 2026-10-13 live dates, because no
live round-trip was run. Also:
- the LangGraph row now gives use_responses_api=False as the adapter's
  default (the adapter has defaulted to it since #1043);
- the agent-framework middleware row moves to 2026-10-14;
- the section 8.1 co-install row notes that agent-framework-anthropic
  now accepts anthropic 1.x.

Closes #1126, #1127, #1128, #1129, #1130, #1132, #1139, #1140, #1141,
#1142.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Recompiled python/constraints/ after a green nightly (lock-refresh.yml, ADR 0011), with the docs and llms outputs that restate the lock. Refs #1111
Recompiled python/constraints/ after green nightly run 38628667542 (#763, ADR 0011); regenerated framework-support docs and llms outputs (#1068).
…refuse (BG §2.1) (#1146)

Closes #208
Closes #209

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ptions (BG §2.2) (#1152)

Closes #210

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1165)

Ships the nightly lock refresh (#1159) with the ruff pre-commit rev bumped to v0.17.1, and records the offline re-checks in docs/verified-apis.md section 8 (the openai-agents row comes from #1164).

Closes #1156
Closes #1157
Closes #1161
Closes #1162
Closes #1163

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves openai 3.35.1, litellm 1.106.0, langgraph 1.2.16, langchain-core 1.6.12 (Refs #1171, #1170, #1167, #1166). §8 re-checks recorded in #1179.
§8 re-checks for langgraph (Refs #1166, #1167), Google ADK (Refs #1170), OpenAI Agents SDK (Refs #1171) on the #1174 lock.
…1187)

ContentSafetyBlocked gains .provider, a stable slug (azure-content-safety,
bedrock-guardrails, google-model-armor, crowdstrike-aidr) carried in
_CONTENT_SAFETY_VENDORS beside the display name. The refusal span gains
donkey.policy.provider and donkey.policy.categories, emitted only for a
violation that carries a provider, so a PII or budget refusal omits both.
The names are generic so the federated-guardrail types (#313, #1177)
reuse them.

errors.py and telemetry.py stay at their ratchet ceilings: the growth is
offset by reflowing docstrings and comments, with no wording dropped.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Recompiled by lock-refresh.yml after a green nightly: https://github.com/Donkey-Development-Kit/donkey-development-kit/actions/runs/39414748693

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@043fb46...cf430e0)

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@3e5f45b...9000827)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the website-npm group in /website with 1 update: [next](https://github.com/vercel/next.js).


Updates `next` from 16.3.8 to 16.4.0
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](vercel/next.js@v16.3.8...v16.4.0)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: website-npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(attribution): set the business group per request (#310)

One process serving several tenants or teams could only state one business
group, fixed on the Donkey's config, so a shared or multi-tenant agent had to
build one Donkey per group: a client, pool, auth cache and Budget each (BG §1.7).

- donkey.attribution(business_group=, team=, project=, env=, enduser_id=):
  a dual sync/async scope that attributes every governed call in its block
  without starting a run. The correlation ID is left alone, so one run can
  span two cost centres. Its cost tags merge per field over the enclosing
  binding.
- donkey.run(business_group=...) and @donkey.governed(business_group=...)
  take the same argument when the group belongs to the whole run.
- The group rides a contextvar beside the correlation one
  (core/correlation.py). The transport reads it on every data-plane send and
  sends it in place of the configured business_group. Precedence: scope,
  then Donkey config, then env, then file. Control-plane requests never
  carry it.
- The value is validated like a cost tag (cost.validate_tag_value): empty,
  over-long or control-character values raise ConfigError at the call site.
- default_headers snapshot adapters keep the configured group, a documented
  degradation (BG §1.8). The header name stays the Unverified placeholder
  (docs/verified-apis.md §3, #21).
- Docs: "Per-request attribution" on the telemetry page, and the
  configuration reference's attribution and header tables. API reference
  regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs(website): regenerate llms artifacts for per-request attribution (#310)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(lint): whitelist the public Donkey.attribution for vulture (#310)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
tommasobolis and others added 18 commits October 30, 2026 09:18
…1506) (#1507)

The Secret scan reads every ref, so the fake URL password in the open
fix/1478 branch (test_endpoints.py) fails every PR. Allowlist it by exact
text, per the file's no-path-allowlist policy.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
redirect.seen.append({k: self.headers.get(k) for k in ("client_id", "client_secret")})
self.rfile.read(int(self.headers.get("content-length") or 0))
self.send_response(307)
self.send_header("location", f"{target_origin}{self.path}")
def do_POST(self) -> None:
self.rfile.read(int(self.headers.get("content-length") or 0))
self.send_response(307)
self.send_header("location", f"{target}{self.path}")
@tommasobolis
tommasobolis marked this pull request as draft October 30, 2026 15:09
@tommasobolis

Copy link
Copy Markdown
Collaborator Author

Held as draft: this is the develop → main promotion. It merges only with the maintainer's explicit approval, as a no-ff merge commit (gh pr merge --merge), never squash. Auto-mergers: please skip.

This branch had an error being deployed

1 failed and 1 active deployments
github-pages — 897b5a39 Deployed Oct 30, 2026 by amirkhan-ak-sf via deploy #163
testpypi — 897b5a39 Deployed Oct 30, 2026 by tommasobolis via Publish to TestPyPI (dev dry-run) #26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants