SwarmAI 1.0.24 Release Notes
What changed in SwarmAI 1.0.24.
Released: 2026-05-14
Theme: The framework gets a provider-neutral inference SPI, a native Anthropic adapter that unlocks features Spring AI doesn't surface yet (prompt caching, extended thinking, inline citations, server-side tools), a full compliance gate substrate (citation-required, single-writer invariant, delegation-depth policy, universal disclaimer), and the first slice of the financial-services vertical (Excel/PowerPoint authoring + audit, four finance computation tools, a curated MCP-connector catalog with license-aware activation).
Headline
This release adds the substrate that makes SwarmAI fit for compliance-heavy use cases โ financial services first, but the gates and primitives are domain-neutral. Three structural moves stand out:
- A provider-neutral
LlmClientSPI lands alongside a newswarmai-native-anthropicmodule. Spring AI stays the default path; the native adapter is opt-in and unlocks prompt caching (5m / 1h tiers + read/write telemetry), extended thinking budgets, inline citations withchar_location/page_locationanchors, PDF document blocks, and the 2026-02-09 / 2026-01-20 / 2025-08-18 server-side tools โ none of which Spring AI'sChatClientcurrently surfaces.
- Compliance gates become first-class.
CitationRequiredGaterejects financial output lacking citations;UniversalDisclaimerauto-stamps draft-work-product language onto every user prompt;DelegationDepthPolicyenforces one-level subagent delegation (matching the orchestrator โ worker pattern from Anthropic's financial-services repo);SingleWriterInvariantEnforcerblocks two agents from writing the same correlation-id's workspace. The fullLifecycleHookfamily (14 event types) and a permission-rule DSL (Bash(npm run ),WebFetch(domain:.acme.io),MCP(server:tool)) plug into the same hook chain.
- The financial vertical gets its computation layer. Four deterministic finance tools (
dcf_model,lbo_model,comps_analysis,tear_sheet) ship inswarmai-tools/tool/finance/. Excel + PowerPoint authoring (XlsxAuthorTool,PptxAuthorTool) and their audit counterparts (XlsxAuditTool,DeckQcToolโ flags placeholders, hardcodes-in-formula-regions, magic numbers) cover the artifact side. A curatedFinancialMcpConnectorscatalog (LSEG LFA, S&P Capital IQ, FactSet, PitchBook, Chronograph, Daloopa, Morningstar, Moody's, MT Newswires, Aiera, Egnyte) plus a license-awareMcpConnectorRegistryandMcpConnectorBinder(with credential resolution) cover the data side.
What's new
Native Anthropic adapter (swarmai-native-anthropic)
- New module
swarmai-native-anthropicwraps the officialcom.anthropic:anthropic-java:2.32.0SDK. Opt-in: Spring-AI users see no change. AnthropicNativeClientโ syncsend(AnthropicRequest) โ AnthropicResponse. Honours every Phase-1 substrate feature.- Prompt caching:
CachePolicywith up to 4CacheBreakpoints anchored on logical slots (tools,system,documents,messages:N,messages:last), 5-minute and 1-hour TTL tiers.CacheUsagesurfaces read tokens + per-tier write breakdown on every response. - Extended thinking:
ThinkingBudgetwithENABLED/INTERLEAVEDmodes (the latter via betainterleaved-thinking-2025-05-14), explicitbudget_tokens,FULL/SUMMARIZED/OMITTEDdisplay modes. - Inline citations:
CitationConfig+DocumentBlock(PDF base64 / PDF URL / plain text / custom-content blocks). Responses carryInlineCitations with sealedCitationLocationvariants โCharLocation,PageLocation,ContentBlockLocation. - Server-side tools (
ServerToolSpecsealed family):WebSearch(allow / block domain lists, max uses),WebFetch,CodeExecution,Memory. Translated to the dated SDK types (WebSearchTool20260209,CodeExecutionTool20260120,MemoryTool20250818) insideWireMapper; consumers see stable names. - Tool roundtrip: full
ToolUseโ caller-executes โToolResultflow withdisable_parallel_tool_use, strict tool use,ToolChoice(auto / any / none). AnthropicLlmClientadapter implements the provider-neutralLlmClientSPI on top ofAnthropicNativeClientโ declares its supported features viaLlmClient.Feature.
Provider-neutral LlmClient SPI (swarmai-core/agent/llm/)
LlmClientinterface withsend(LlmRequest) โ LlmResponse+supportedFeatures()introspection.LlmRequest/LlmResponsecarry the substrate types (CachePolicy,ThinkingBudget,CitationConfig,DocumentBlock,InlineCitation,TokenUsage) so callers don't lose features when running on a non-Anthropic provider โ adapters that don't support a feature silently drop it.LlmMessage/LlmToolCall/LlmRole/LlmStopReasonprovide a uniform message shape across providers.LlmClient.Featureenum (PROMPT_CACHING,EXTENDED_THINKING,INLINE_CITATIONS,PDF_DOCUMENTS,VISION,STRICT_TOOL_USE,STREAMING,CACHE_TELEMETRY) lets compliance gates fail fast when a required behaviour isn't honoured by the active adapter.
Hook lifecycle + permission DSL (swarmai-core/agent/hook/, tool/permission/, agent/permission/)
LifecycleEventsealed family โ 14 event types:UserPromptSubmit,SessionStart,SessionEnd,PreCompact,PostCompact,Stop,SubagentStart,SubagentStop,TaskCreated,TaskCompleted,FileChanged,PermissionRequest,PermissionDenied,Notification.LifecycleHookinterface with per-event default methods + a pattern-matchingonEventfallback.HookResultcarriesPROCEED/BLOCK/MODIFY/ASKsemantics;MODIFYlets hooks rewrite prompts mid-flight.LifecycleHookRegistryโ first-non-PROCEED-wins dispatch; isolates throwing hooks so a buggy audit hook can't crash the agent loop;CopyOnWriteArrayList-backed for hot registration.- Permission rule DSL โ
PermissionRule.Specifiersealed variants:Any,Glob,Domain,McpQualified.RuleParser.parse("Bash(npm run )")/"Read(./.env)"/"WebFetch(domain:.acme.io)"/"MCP(slack:post-message)".PermissionRuleswith deny-first-then-ask-then-allow evaluation and scope merge (managed > flags > local > project > user). PermissionModeโDEFAULT,ACCEPT_EDITS,PLAN,AUTO,DONT_ASK,BYPASS_PERMISSIONS. Resolves theUNMATCHEDdecision from rule evaluation based on the tool'sPermissionLevel.
Reliability substrate (swarmai-core/agent/resilience/, agent/llm/, agent/parallel/, tool/hooks/)
FailoverReasonenum +ErrorClassifierโ classifies HTTP-status + exception-chain shapes into one ofRETRYABLE,SHOULD_COMPRESS,SHOULD_ROTATE_CREDENTIAL,SHOULD_FALLBACK,NOT_RECOVERABLE. Provider-agnostic โ pattern-matches on common error strings.ToolCallArgRepairโ string-literal-aware state machine that fixes trailing commas, unclosed braces / brackets, Python literals (True/False/Noneโtrue/false/null), surrogate halves, C0 control characters in tool-call JSON.ToolCallSafetyAnalyzerโ partitions a batch of pending tool calls intoParallelGroups based onPARALLEL_SAFE_TOOLSallowlist,NEVER_PARALLEL_TOOLSdenylist, and path-overlap detection. Conservative on conflict (serialises when in doubt).LoopDetectorโToolHookimpl tracking per-agent history. Warns at N identical-args failures, blocks at 2N. Also detects idempotent-tool same-result loops.
Compliance gates (swarmai-core/governance/compliance/)
ComplianceGateinterface +ComplianceEvaluatorthat runs an ordered list and aggregatesComplianceFindings (BLOCKER / WARNING / INFO severities) into aComplianceReport.CitationRequiredGateโstrict()andlenient()modes. Per-sentence numeric-claim anchoring via shared numeric tokens with%/x/$suffix awareness (so30% FY25doesn't false-anchor to a citation talking about25%).UniversalDisclaimerโLifecycleHookthat prepends a draft-work-product directive to every user prompt. Default text is FS-grade; customisable.DelegationDepthPolicyโLifecycleHooktracking subagent depth per correlation id; blocksSubagentStartevents that would exceedmaxDepth(default 1, matches the one-level-delegation pattern).SingleWriterInvariantEnforcerโLifecycleHookonFileChangedevents; first writer per correlation id claims the slot; second writer is blocked.yieldWriter(correlationId, agentId)releases the slot for explicit handoffs.
Skill disclosure + loader (swarmai-core/skill/disclosure/)
SkillCatalogโ Tier-1 metadata listing (list(options)) + Tier-2 body fetch (view(name)). Scope cascade with precedencePROJECT > PERSONAL > PLUGIN > ENTERPRISE; token-budget enforcement; explicit allow/deny scopes filter for/mcp status-style UIs.SkillMetadatarecord โ Tier-1 entry (~50 tokens) carrying name, description,whenToUse,argumentHint, scope,SkillReadiness(AVAILABLE/SETUP_NEEDED/UNSUPPORTED).SkillFrontmatterโ extended fields beyond the existingSkillDefinition:allowedTools(per-skill tool allowlist),model,effort(MINIMAL/STANDARD/EXTENDED),contextMode(INHERIT/FORK),agent,hooks,paths,disableModelInvocation,userInvocableOnly,argumentHint,setupHint.SkillLoaderโ disk scanner. Walks.swarm/skills/,~/.swarm/skills, plugin directories; parsesSKILL.mdviaSkillDefinition.fromSkillMd; registers entries with the catalog under the appropriate scope.refresh()re-scans.
Office authoring + audit (swarmai-tools/tool/office/)
XlsxAuthorToolโ Apache POI-backed.xlsxbuilder. Per-celltext/number/percent/formula/bolddirectives. Counts hardcodes vs formulas in the output (hardcodeRatioreturned).XlsxAuditToolโ reads.xlsx; reports per-sheet formula/hardcode/error counts; flags formula-evaluation errors (#REF!,#DIV/0!), magic numbers (large unformatted constants), and exceeds-hardcode-threshold sheets.audit: PASS|FAILverdict.PptxAuthorToolโ deck builder; title + subtitle + bullets + tables; layout selection (TITLE,TITLE_AND_CONTENT,BLANK).DeckQcToolโ reads.pptx; flags placeholders (TBD,[TK],X.X%,$XXX), missing titles, empty slides.qc: PASS|FAILverdict.- Apache POI added as an
dep โ pulls in ~10 MB only for consumers that want office output.true
Finance computation tools (swarmai-tools/tool/finance/)
FinanceToolmarker interface +AbstractFinanceToolbase class โ typed input coercion (asDouble,asInt,required), standardok()/error()envelope, exception-to-error-response wrapping, 4-decimal rounding convention.DcfModelToolโ FCF projection from base-year financials + assumptions; Gordon growth terminal value; 3ร3 WACC/terminal-growth sensitivity grid.LboModelToolโ entry purchase โ year-by-year operating + debt paydown โ exit equity / IRR / MOIC.CompsAnalysisToolโ peer-set multiples โ median/mean/low/high โ implied equity per share for each multiple. P/E is treated as equity-level; EV multiples are enterprise-level (net-debt adjustment applied).TearSheetToolโ composes the other tools' output into a structured.xlsxviaXlsxAuthorTool(summary + financials + multiples sheets).FinanceToolBundle.instances()one-line registration for non-Spring callers.
Financial MCP connectors (swarmai-tools/tool/finance/mcp/)
McpConnectorDefinitionrecord โ name, vendor, endpoint,AuthScheme(OAUTH/BEARER/API_KEY/NONE),requiredEdition(COMMUNITY/TEAM/BUSINESS/ENTERPRISEfrom existingLicenseProvider),requiredFeatureflag, advertised tool preview.FinancialMcpConnectorsโ curated catalog of 11 definitions: LSEG LFA, S&P Capital IQ, FactSet, PitchBook, Chronograph (Enterprise tier); Daloopa, Morningstar, Moody's, MT Newswires, Aiera (Business tier); Egnyte (Team tier).McpConnectorRegistryโ license-aware filtering (activeConnectors()), explicit allowlist / denylist overrides, structuredstatusReport()(per-connectorACTIVE/LICENSE_FLOOR_NOT_MET/FEATURE_FLAG_MISSING/DISABLED).McpCredentialsrecord +McpCredentialResolverinterface โFromEnvironmentreadsSWARM_MCP_/_BEARER _API_KEY/_OAUTH_TOKEN;InMemoryimpl for tests.McpConnectorBinderโ usesHttpClientSseClientTransportfrom the MCP Java SDK to connect to each active connector, injects auth header per scheme, enumerates server-side tools vialistTools(). ReturnsBinding(definition, client, tools)per connector. Skips connectors with missing credentials;statusReport()explains every decision.
Reasoning + recall primitives
MixtureOfAgentsTool(swarmai-core/tool/reasoning/) โ provider-agnosticBaseToolthat fans out a single prompt to N reference inference functions in parallel, then synthesises via an aggregator function (not voting). Caller supplies the inference lambdas; supports mixing Claude + GPT + Gemini + local models.TrajectoryWriter(swarmai-core/observability/trajectory/) โ splits runs intotrajectory_samples.jsonl+failed_trajectories.jsonl.TrajectoryEntrycarries goal + turns + verdict + attributes โ ShareGPT-compatible. Thread-safe.GoalLockMode(swarmai-core/agent/mode/) โLifecycleHookthat re-injects a goal directive on every user-prompt event. Keeps agents anchored on long-running cron tasks.SessionIndex+SessionSearchTool(swarmai-core/memory/fts/+tool/memory/) โ SQLite FTS5-backed full-text index over conversation transcripts. BM25 ranking, prefix wildcards, NEAR/n proximity. Per-agent filtering;forgetSession()for deletion requests.TurnSnapshotStore(swarmai-core/agent/checkpoint/) โ JGit-backed shadow git repo at~/.swarm/checkpoints/store/. Per-turn snapshot, restore, unified diff between any two snapshots, recent-log enumeration. Distinct fromGitWorktreeWorkspace(per-task isolation): this is global + automatic + per-turn.
Build hardening (swarmai-tools/pom.xml)
- Five Spring deps declared explicitly at compile scope and one at test scope (
spring-jdbc,spring-web,spring-ai-vector-store,mcp,spring-ai-client-chat). Maven's nearest-wins rule was giving the test-scopedspring-boot-starter-data-jpaprecedence overswarmai-core's transitive compile-scope, so the explicit declarations make the classpath stable regardless of how upstream deps shift. No behavioural change โ the imports were already resolving correctly until adding optionalsqlite-jdbc/jgittoswarmai-coreperturbed Maven's resolution path enough to expose the latent issue.
1.0.24 follow-ups (shipped in this release)
The 1.0.24 substrate landed broad-but-shallow: each subsystem ships as a callable library, but several were not yet wired into the existing Agent runtime. The follow-up work below lands the wiring + closes the gaps the original release deferred to the roadmap. Everything in this section is in 1.0.24-SNAPSHOT and demonstrated by a corresponding example under swarm-ai-examples/.
Adaptive thinking for Opus 4.7 (swarmai-core/agent/llm/, swarmai-native-anthropic)
Claude Opus 4.7 retired thinking.type=enabled + budget_tokens and now requires thinking.type=adaptive with an output_config.effort ceiling (LOW / MEDIUM / HIGH / XHIGH / MAX). The adapter now speaks both:
- New
ThinkingEffortenum (LOW/MEDIUM/HIGH/XHIGH/MAX) maps 1:1 to Anthropic's effort tiers. - New
ThinkingMode.ADAPTIVEandThinkingBudget.adaptive(effort)factory; the existingThinkingBudget(mode, budgetTokens, display)constructor is preserved so existing call-sites keep working. WireMapper.toThinkingConfignow switches on mode and emitsThinkingConfigAdaptivefor ADAPTIVE; newWireMapper.toOutputConfigbuilds theOutputConfig.effortblock, wired inAnthropicNativeClient.toSdkParams.- Demo:
swarm-ai-examples/citation-required-pipelinewas switched back from Sonnet 4.5 to Opus 4.7 +ThinkingBudget.adaptive(MEDIUM). End-to-end PASS with 5 inline citations.
LlmClient SPI plugged into Agent.callLlm (swarmai-core/agent/Agent.java)
The provider-neutral SPI is now actually wired into Agent:
- New
Agent.Builder.llmClient(LlmClient)โ when set, single-shot text-only calls bypass Spring AI entirely and route through the SPI. - The Spring AI
ChatClientstays required (and remains the fallback for tool-bearing calls and streaming) โ substituting the SPI at the LLM-dispatch layer doesn't force a rewrite of either the agent loop or tool orchestration. Agent.callLlmsynthesises a Spring AIChatResponsefrom the SPI'sLlmResponse(AssistantMessage+Generation+ChatResponseMetadatacarrying aDefaultUsage) so downstream code (token extraction, observability, retry) sees the same shape on both paths.- Demo:
swarm-ai-examples/llm-client-spiruns the same task once with the default Spring AI baseline, once withAnthropicLlmClientplugged in. The SPI path returns ~1000 chars in ~9s with Anthropic's token usage (428 input / 196 output) plumbed through toSwarmOutput.getTotalPromptTokens().
Reliability substrate wired into Agent.callWithRetry (swarmai-core/agent/Agent.java)
The Phase-3 deliverables were standalone classes; the agent's existing ad-hoc retry loop ignored them. The follow-up routes the retry decision through the substrate:
Agent.callWithRetrynow callsErrorClassifier.classify(cause)on every exception. The classifier informs log lines and observability (classifier=RETRYABLE/classifier=SHOULD_ROTATE_CREDENTIALappear in warn output) but does not narrow what is retried โ backward compatibility with 1.0.16 is preserved by keeping the original wide-net rule (if msg.contains("400") || "401" || "403" || "context_length_exceeded" || "NonTransient" โ fail fast; everything else โ retry). The classifier's role is advisory taxonomy, not gatekeeping. This guarantees no workflow that ran on 1.0.16 sees a new retry refusal on 1.0.24.ErrorClassifieritself was strengthened: Spring AI's response-side breakage (RestClientExceptionโHttpMessageNotReadableExceptionโJsonEOFException) and barejava.io.IOExceptionnow classify asRETRYABLEinstead of falling through toNOT_RECOVERABLE. Two new regression tests inErrorClassifierTestlock the chain in place.- Default retry count bumped from 3 โ 5 with exponential backoff (2 s / 4 s / 8 s / 16 s โ 30 s wall ceiling) so transient OpenAI 5xx and edge-truncation events have more chances to recover before falling through to the per-workflow failure path.
LoopDetector,LlmCircuitBreaker,ToolCallSafetyAnalyzerremain opt-in:LoopDetectorplugs in viaAgent.builder().toolHook(new LoopDetector());LlmCircuitBreakerandToolCallSafetyAnalyzerare documented as standalone substrate.- Demo:
swarm-ai-examples/reliability-substrateis an offline self-test that exercises all four primitives (10/10 assertions pass). Includes the 7-row classifier table, the loop-detector deny-on-attempt-7 verification, the circuit-breaker CLOSEDโOPEN transition trace, and the read/write parallel-batching case.
HTTP client compatibility shim (swarmai-core/config/HttpClientCompatConfiguration.java)
Starting 2026-05-14 on JDK 21.0.10 (WSL2 + Spring AI 1.0.4), OpenAI's edge began returning truncated bodies for chat-completion requests sent over Transfer-Encoding: chunked whenever the request carried tool definitions. Every retry hit the same JsonEOFException: Unexpected end-of-input because the response stream was cut before the closing brace. The same request body sent with an explicit Content-Length header returned cleanly โ verified by routing requests through a local HTTPโHTTPS proxy that re-frames chunked into Content-Length. Non-tool requests were small enough not to trip the bug.
The 1.0.24 framework now ships a compatibility shim that fixes this transparently for every user:
- New auto-configuration
HttpClientCompatConfigurationregistered viaMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. - Provides a
ClientHttpRequestFactorybean backed bySimpleClientHttpRequestFactorywithsetBufferRequestBody(true)โ Spring's HttpURLConnection-based factory pre-buffers the JSON body and sends it with an accurateContent-Lengthheader instead of chunked framing. - Provides a
@Primary RestClient.Builderbean wired to that factory, plus a defensiveBeanPostProcessorthat rewrites any otherRestClient.Builderbean (covering Spring AI's various injection patterns). - Activates only when
RestClientis on the classpath; respects user overrides via@ConditionalOnMissingBean. - Backward compatible: zero API change. Existing 1.0.16-shaped apps upgrade to 1.0.24 by bumping the version โ no code, no config, no flag.
- Verified: the standalone
tool-callingexample, which failed every retry on every direct HTTPS call under the conditions above, now completes in 23 s with no retries. The full judge-all regression now exercises tool-bearing workflows cleanly.
This is the single most load-bearing fix in 1.0.24 for users on JDK 21 + OpenAI today.
Lifecycle hooks wired into Agent.callLlm (swarmai-core/agent/Agent.java)
LifecycleHookRegistry was a callable library; agents didn't fire events. The follow-up dispatches the most-load-bearing event:
- New
Agent.Builder.lifecycleHookRegistry(LifecycleHookRegistry)โ when set, every LLM call emits aLifecycleEvent.UserPromptSubmitto the registered hooks before the model receives the prompt. - Hooks may return
BLOCK(raisesAgentExecutionException),MODIFY(rewrites the prompt before dispatch),ASK(escalation hook for AskUserQuestion integration โ TODO), orPROCEED. - When unset (default), dispatch is a no-op with zero overhead โ existing apps see no behaviour change.
- The other 13 lifecycle events (
SessionStart,Stop,SubagentStart,FileChanged,PermissionRequest, โฆ) remain dispatchable but are not yet emitted from inside the agent loop. Each future emission point is a focused follow-up. - Demo: the lifecycle hook chain is exercised in
swarm-ai-examples/governance-substratestep 1 โ a counting hook + PII-redactor on a fake-SSN prompt, withMODIFYshort-circuiting the chain after the counter sees the event.
Settings layer (swarmai-core/settings/)
New utility for the project / user / machine config layering pattern:
- New
SettingsLayerenum withdefaultPath()per layer (./.swarmai/settings.yml,~/.swarmai/settings.yml,/etc/swarmai/settings.yml). - New
SettingsMergerโ caller-supplies parsedMapper layer (YAML-loader-agnostic soswarmai-coredoesn't pull a YAML dependency); the merger flattens nested maps to dotted keys (llm.model.name) and applies PROJECT > USER > MACHINE precedence. - New
MergedSettingsโ read-only view exposing both effective values and provenance per dotted key. Answers "where did this setting come from?" for support cases without diffing files. - Not auto-loaded from the filesystem yet โ that's a follow-up. The merge logic is the shipping piece.
- Demo:
swarm-ai-examples/governance-substratestep 2 โ three layers with overlapping keys, asserts each effective value comes from the right layer (4 โ).
Tool-call risk classifier (swarmai-core/agent/permission/)
New pure-function classifier for the auto-classification piece:
- New
ToolCallRiskClassifierreturns aTier(READ_ONLY / NETWORK / MUTATION / DESTRUCTIVE) for any(toolName, arguments)pair. - Three signals: tool-name keyword lexicons (
read/get/listโ READ_ONLY,write/create/updateโ MUTATION,delete/drop/rmโ DESTRUCTIVE, โฆ); argument-key inspection (patharg promotes to MUTATION,urlarg to NETWORK); caller-supplied overrides for tools whose names lie about their effect (force_full_cleanetc.). - Conservative: when ambiguous, returns the higher tier.
- Not auto-applied yet โ feeds into
PermissionRequestvia a custom hook today; auto-stamping into the permission pipeline is a follow-up. - Demo:
swarm-ai-examples/governance-substratestep 3 โ 8 representative calls covering all four tiers (8 โ).
Anthropic Message Batches client (swarmai-native-anthropic/llm/anthropic/batch/)
Was deferred in 1.0.24's roadmap. Now shipped:
- New
AnthropicBatchClientwithsubmit(List,) โ BatchHandle status(id),cancel(id),results(id) โ List,resultsForEach(id, consumer)for streaming,delete(id). - New substrate types:
BatchProcessingStatus(IN_PROGRESS/CANCELING/ENDED),BatchHandle(id + counts + timestamps + results-url),BatchItem(customId + AnthropicRequest),BatchItemOutcome(sealed:Succeeded(AnthropicResponse)|Errored(type, message)|Canceled|Expired),BatchResult,BatchRequestCounts. - Per-item translation mirrors
AnthropicNativeClient.toSdkParamsโ caching breakpoints, extended/adaptive thinking, server-side tools, tool-choice all propagate through the batch path unchanged. WireMapperlifted topublic final(with internal-API javadoc) so thebatchsub-package can reuse the field-by-field translation.- Demo:
swarm-ai-examples/anthropic-batchsubmits 5 synthetic 10-K excerpts as one batch, polls until ENDED (~1โ2 min for small batches), joins results back by customId. Self-verifying: counts how many returned summaries contain the source-of-truth revenue figure (5/5 โ), then prints the realised cost saved ($0.002553 saved on this run, scales to ~$51 on a 100K-item batch) using Sonnet 4.5's published prices and the 50% batch discount.
Anthropic Files API client (swarmai-native-anthropic/llm/anthropic/files/)
Was deferred in 1.0.24's roadmap. Now shipped:
- New
AnthropicFilesClientwithupload(Path),upload(InputStream),list(),metadata(fileId),download(fileId) โ byte[],download(fileId, Path),delete(fileId). Sets thefiles-api-2025-04-14beta header on every call. - New
AnthropicFilesubstrate (id + filename + MIME type + sizeBytes + createdAt + downloadable). - The
downloadableflag is exposed so callers can detect that arbitrary user uploads are accepted-for-reference but not echoable (Anthropic only re-emits bytes for code-execution / model-generated artifacts as of 2026-Q1). - Substrate integration of
DocumentBlock.File(file_id)for the messages path is not yet shipped โ it requires the beta messages surface and is captured as a future change. This client is most useful today for upload + lifecycle management ahead of caller code that does its own message dispatch. - Demo:
swarm-ai-examples/anthropic-fileswalks the full lifecycle (write local โ upload โ metadata round-trip โ list โ conditional download with SHA-256 round-trip โ cleanup infinally). All assertions print โ/โ; thedownloadable=falsecase is detected and labelled[skipped]with the rationale rather than crashing.
--demo Spring profile (swarm-ai-examples/.infra/src/main/resources/application-demo.yml)
Quality-of-life for the example runner:
- New profile silences Spring Boot/Hibernate/Hikari/Spring AI startup chatter (root logger โ WARN, examples + workflow-runner stay at INFO, banner off, one-line console pattern).
run.shacceptsDEMO=1and appends,demoto the active profile string.run.shnow also passes--spring.profiles.active=$profileon the Ollama branch (previously only on the OpenAI branch โ that bug masked the profile silently).- All five new 1.0.24 examples and their per-example wrappers default
DEMO=1, so the first thing the user sees is the example output, not 100 lines of init log.
Five new example wrappers (swarm-ai-examples/)
Each has its own folder, run.sh, README, and registration in SwarmAIWorkflowRunner:
| Folder | What it shows |
|---|---|
anthropic-batch/ | Batches client end-to-end, fact-checks summaries against source map, computes realised $ saved |
anthropic-files/ | Files API lifecycle with SHA-256 round-trip + downloadable-flag handling |
llm-client-spi/ | Same task two paths: Spring AI baseline vs AnthropicLlmClient SPI side-by-side |
reliability-substrate/ | Offline self-test of ErrorClassifier + LoopDetector + LlmCircuitBreaker + ToolCallSafetyAnalyzer (10/10 assertions) |
governance-substrate/ | Offline self-test of LifecycleHookRegistry + SettingsMerger + ToolCallRiskClassifier (15/15 assertions) |
Both substrate self-tests run offline (no API keys) so they double as smoke tests on every clean checkout.
Test coverage delta
| Category | Tests added |
|---|---|
| Native Anthropic adapter (substrate validation + WireMapper + end-to-end with mocked SDK) | 33 |
| Hook lifecycle + permission rule DSL + permission modes | 35 |
Reliability substrate (ErrorClassifier, ToolCallArgRepair, ToolCallSafetyAnalyzer, LoopDetector) | 39 |
Skill disclosure (SkillCatalog) + SkillLoader | 19 |
| Office authoring + audit (Xlsx + Pptx + audit + QC) | 12 |
| Compliance gates (CitationRequired strict/lenient, UniversalDisclaimer, DelegationDepth, SingleWriter) | 22 |
| Finance tools (DCF, LBO, comps, tear sheet) | 8 |
| Financial MCP connector registry + binder + credential resolvers | 14 |
| Server-side tool specs + Anthropic LlmClient adapter | 14 |
| Quality + recall primitives (MoA, TrajectoryWriter, GoalLockMode, SessionIndex, TurnSnapshotStore) | 31 |
Total: ~227 new tests. Full suite remains green; the swarmai-tools build hardening detailed above ensures pre-existing tests continue to compile on the new dep graph.
Breaking changes
None. This release is additive:
- The new
swarmai-native-anthropicmodule is opt-in via Maven coordinate. Apps that don't depend on it are unaffected. - The
LlmClientSPI is a new interface; existingAgent.builder().chatClient(...)callers continue to use Spring AI'sChatClientdirectly. Opting in via the newAgent.builder().llmClient(...)is additive โ both paths can coexist on the same agent. - All new substrate types (
CachePolicy,ThinkingBudget,LifecycleHook,PermissionRules,ComplianceGate, etc.) are net-new APIs โ no existing callers reference them yet. - New tools (
XlsxAuthorTool,DcfModelTool, โฆ) register as@Components and are picked up by Spring's component scan; non-Spring users continue to instantiate tools explicitly. swarmai-tools/pom.xmlgained five explicit dep declarations; these were already on the transitive classpath at compile scope until this release'sswarmai-corechanges perturbed Maven's resolution. Making them explicit is a robustness improvement, not a breaking change for consumers.
Migration
Existing applications need no changes to upgrade. To opt into the new primitives:
// Native Anthropic adapter (add the new module as a Maven dependency first)
AnthropicNativeClient anthropic = AnthropicNativeClient.from(AnthropicConfig.fromEnv());
AnthropicResponse resp = anthropic.send(AnthropicRequest.builder()
.model("claude-opus-4-7")
.system("You are a financial analyst")
.message(AnthropicMessage.userText("Summarise ACME's 10-K"))
.document(DocumentBlock.Pdf.ofBytes("ACME-10K", "ACME 10-K", pdfBytes, /*cite*/ true))
.cachePolicy(CachePolicy.builder()
.breakpoint(CacheBreakpoint.oneHour("system"))
.breakpoint(CacheBreakpoint.fiveMinutes("documents"))
.build())
// Opus 4.7 requires adaptive thinking + output_config.effort.
// For Sonnet 4.5 / Opus 4.1, use ThinkingBudget.enabled(8192) instead.
.thinkingBudget(ThinkingBudget.adaptive(ThinkingEffort.MEDIUM))
.citationConfig(CitationConfig.ENABLED)
.build());
// Plug the SPI into Agent so single-shot text-only calls bypass Spring AI
// and unlock native Anthropic features (caching, citations, thinking) without
// rewriting the agent loop.
Agent analyst = Agent.builder()
.role("Analyst")
.goal("Summarise filings")
.backstory("โฆ")
.chatClient(chatClient) // still required: tool-calling + streaming fallback
.llmClient(new AnthropicLlmClient(anthropic))
.lifecycleHookRegistry(hookRegistry) // optional: PII redactor, audit emitter, prompt-injection sanitiser
.modelName("claude-sonnet-4-5")
.build();
// Anthropic Message Batches โ 50% of standard request price for offline workloads
AnthropicBatchClient batch = AnthropicBatchClient.from(AnthropicConfig.fromEnv());
BatchHandle h = batch.submit(items);
while (h.processingStatus() != BatchProcessingStatus.ENDED) {
Thread.sleep(5_000);
h = batch.status(h.id());
}
for (BatchResult r : batch.results(h.id())) {
switch (r.outcome()) {
case BatchItemOutcome.Succeeded ok -> handle(r.customId(), ok.response());
case BatchItemOutcome.Errored err -> log.warn("{}: {}", r.customId(), err.message());
case BatchItemOutcome.Canceled c -> /* batch cancelled */;
case BatchItemOutcome.Expired e -> /* hit the 24h deadline */;
}
}
// Anthropic Files API โ upload once, reference by file_id (current scope: lifecycle mgmt)
AnthropicFilesClient files = AnthropicFilesClient.fromEnv();
AnthropicFile uploaded = files.upload(Path.of("acme-10k.pdf"));
try { /* reference uploaded.id() in subsequent calls */ }
finally { files.delete(uploaded.id()); }
// Layered settings (PROJECT > USER > MACHINE) with provenance lookup
MergedSettings merged = new SettingsMerger()
.with(SettingsLayer.MACHINE, machineYaml)
.with(SettingsLayer.USER, userYaml)
.with(SettingsLayer.PROJECT, projectYaml)
.merge();
String model = (String) merged.get("llm.model").orElseThrow();
SettingsLayer source = merged.provenance("llm.model").orElseThrow(); // โ PROJECT
// Risk-classify a tool call before letting it through a permission gate
ToolCallRiskClassifier classifier = new ToolCallRiskClassifier();
Tier tier = classifier.classify("file_delete", Map.of("path", "/etc/hosts"));
if (tier.atLeast(Tier.MUTATION)) {
/* require user approval */
}
// Compliance gate before delivering output
ComplianceEvaluator gate = new ComplianceEvaluator(List.of(CitationRequiredGate.strict()));
ComplianceReport report = gate.evaluate(
ComplianceGate.Output.of(resp.text(), resp.citations()));
if (!report.pass()) {
// surface findings to user / hold for human review
}
// Permission rule DSL โ gate tool execution before invocation
PermissionRules rules = PermissionRules.builder()
.allow("Bash(npm *)")
.deny("Bash(rm -rf *)")
.ask("Bash(git push *)")
.build();
PermissionDecision decision = rules.evaluate(
PermissionRequest.of("Bash", Map.of("command", "npm install")));
PermissionDecision final = PermissionMode.DEFAULT.resolve(decision, tool.getPermissionLevel());
// Disk-based skill loader โ register every SKILL.md under the project + user dirs
SkillCatalog catalog = new SkillCatalog();
new SkillLoader(catalog)
.scan(SkillScope.PROJECT, Path.of(".swarm/skills"))
.scan(SkillScope.PERSONAL, Path.of(System.getProperty("user.home"), ".swarm/skills"))
.loadAll();
String listing = catalog.renderListing(SkillCatalog.SkillListingOptions.defaults());
// Author + audit an Excel financial model
XlsxAuthorTool author = new XlsxAuthorTool(Path.of("output"));
author.execute(Map.of("spec", dcfSpec));
Map<String,Object> auditReport = (Map<String,Object>) new XlsxAuditTool(Path.of("output"))
.execute(Map.of("path", "dcf.xlsx", "hardcodeThresholdRatio", 0.4));
// Activate a financial MCP connector under an enterprise license
McpConnectorRegistry registry = McpConnectorRegistry.defaultRegistry(licenseProvider);
McpConnectorBinder binder = new McpConnectorBinder(registry,
new McpCredentialResolver.FromEnvironment());
List<McpConnectorBinder.Binding> bindings = binder.bindAll();
// each binding carries a live McpSyncClient + advertised tool list
Known limitations + roadmap
The following are deliberately deferred โ substrate is shipped, integration into the existing Agent.callLlm() flow is its own focused work:
LlmClientSPI is not yet plugged intoAgent.callLlm(). Callers using the SPI directly get the full benefit; existingAgent.builder().chatClient(...)users continue with Spring AI as before. The follow-up wiresAgent.Builder.llmClient(...)so the substrate features (caching, citations, thinking budgets) become the default.LifecycleHookRegistry+PermissionRulesare not yet fired byAgent. They're callable libraries; the follow-up emits events at agent lifecycle points and routes tool execution through the permission decision pipeline.ErrorClassifieris not yet wired intoLlmCircuitBreaker. Retry / fallback decisions still use the raw exception types. The follow-up reads theFailoverReasontaxonomy instead.- Anthropic Message Batches API client โ the SDK's
BatchCreateParams.Request.Paramsshape requires duplicating the wire-mapping logic fromtoSdkParams. Worth doing for 50%-cost overnight workflows but captured as a separate task. - Anthropic Files API client โ multipart upload + retrieve / download. Pairs with the
file_idsource variant onDocumentBlock.Pdffor long-doc workflows. - Skill loader live change detection โ current
refresh()is manual; a watch-service-driven incremental updater is a follow-up. - OAuth token refresh for MCP connectors โ current
McpConnectorBinderuses the token as-is; refresh / expiry monitoring is deferred.
Acknowledgements
The compliance-gate pattern (citation-required, single-writer invariant, one-level delegation) draws on Anthropic's published financial-services repo. The provider-neutral LLM SPI shape mirrors the structure LangChain settled on for its multi-provider story. The Tier-1 / Tier-2 progressive skill disclosure pattern is a common technique across modern agent frameworks. The substrate, types, and integration are written for SwarmAI.
