All notable changes to the Atmosphere Framework are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- screen long-term-memory writes for injection by default harden coverage evidence gate; relabel opt-in OWASP/compliance rows honestly
- bump Spring Boot, LangChain4j, Embabel, ADK, Spring AI Alibaba Embabel 0.5.0 adds EmbeddingService.pricingModel; test stub reports ALL_YOU_CAN_EAT.
- record coverage-overstatement drift + self-referential-gate rule
- add obsidian skills + atmosphere-vault docs routing
- align capability comment, sample runtime defaults, and XSS skip note with reality
- add workflow_dispatch to Core and Doc Version Guard
- bump version to 4.0.58
- prepare for next development iteration 4.0.59-SNAPSHOT
- default-on RAG injection-safety wiring across runtimes Screens retrieved RAG documents for indirect prompt injection before the LLM (fail-closed RULE_BASED/DROP); wired for @AiEndpoint, Spring Boot 3/4, and Quarkus with console runtime-truth and a poisoned-doc sample.
- pin rag-chat getting-started to released 4.0.57
- stage doc sweep and fail-fast on doc-version drift before publish
- bump version to 4.0.57
- prepare next development version 5.0.35
- prepare for next development iteration 4.0.58-SNAPSHOT
- provider-native structured output across 9 runtimes
- guard expo stats render against undefined token metrics
- unblock MCP Apps sandbox CSP and stop optional-tab 404 probes
- expose analyzer as @AgentSkill so approve happy path works
- expect analyzer's headless A2A registration
- note path-scoped native structured output for Spring AI, Koog, ADK
- enforce Atmosphere doc matches the released version
- retry fetch+rebase+push so a lost race can't red a green release
- apt-get update before installing libxml2-utils so the delisted stale version isn't fetched
- rebase before pushing JS dev-bump so it can't lose the race to the Maven job
- prepare next development version 5.0.34
- bump version to 4.0.56
- prepare for next development iteration 4.0.57-SNAPSHOT
- guardrails + OBO + cost + run-registry AI bean parity with SB4
- config-driven AI routing parity with the SB4 starter
- install governance decision log out-of-box for the queryable audit trail
- warn on unauthenticated A2A endpoint + advertise declared security scheme
- make spring-boot-agui-chat a real AG-UI agent via the native bridge @Agent + @Prompt + real @AiTool through session.stream() (demo fallback when no key); AiEvent->AgUiEvent over /atmosphere/agent/{name}/agui; replaces the scripted controller
- config-driven cost/latency/model routing rules in atmosphere.ai.routing extends F3a (content-only); compose order content->model->cost->latency; off-by-default + content behavior byte-identical
- emit experimental OTel GenAI semconv span attributes via GenAiTracer gen_ai.usage./request+response.model/operation/provider on the live span; fixes provider Runtime-Truth bug (real runtime name, not hardcoded atmosphere); legacy ai.tokens. byte-identical
- add --routing flag to 'atmosphere new' for routing config scaffolding injects a commented atmosphere.ai.routing.* block into AI-template application.yml; off by default, rejected for non-AI templates
- wire real LongTermMemory cross-session recall into personal-assistant InMemoryLongTermMemory + LongTermMemoryInterceptor on the UpstreamMcpAgent @AiEndpoint (no-arg interceptor + static holder pattern); makes the memory-bearing claim true
- opt-in RoutingLlmClient via atmosphere.ai.routing.enabled autoconfig wraps the resolved client with content-based routing rules; adds AiConfig.installClient seam; default-off byte-identical
- wire ApiKeyResolver into anthropic/cohere with provider env-var support renamed from CredentialResolver (clashed with AiGateway.CredentialResolver); per-provider precedence reads ANTHROPIC_API_KEY/COHERE_API_KEY, no cross-provider key leak
- add generation params (temperature/maxTokens/topP/stop) to LlmSettings wired to the wire for built-in/anthropic/spring-ai/langchain4j; honest per-runtime matrix in README; empty=byte-identical
- explicit prompt-cache-key tri-state replaces base-URL sniffing PromptCacheKeyMode AUTO/ENABLED/DISABLED on LlmSettings; AUTO preserves current per-path heuristics byte-for-byte
- store resolved apiKey on LlmSettings so apiKey() works for any client removes the OpenAiCompatibleClient-only instanceof; 4-arg constructor preserves old behavior; adds CredentialResolver precedence primitive
- provider-neutral model tier aliases (fast/frontier/reasoning) ModelTier resolves tier tokens to a concrete model by active provider; raw model strings pass through unchanged
- add provider-neutral configureNativeClient(Object) to AbstractAgentRuntime type-checked against nativeClientClassName(); provider-typed static setters remain the primary wiring path
- reachable agent bridge + keep response open during AG-UI streaming AgUiAgentBridge made public (cross-module reflective invoke from agui handler); handlePost joins the run thread so virtual-thread SSE writes don't hit a recycled response — fixes the agui-chat demo+UI e2e (only RUN_STARTED was reaching the wire)
- converge AUTO prompt-cache-key to one default-deny allow-list built-in and framework runtimes share CacheHint.endpointAcceptsPromptCacheKey; framework no longer emits on unknown hosts under AUTO (force via PromptCacheKeyMode.ENABLED)
- honor per-request ToolLoopPolicy in anthropic/cohere/langchain4j tool loops they hardcoded a 5-round cap and ignored maxIterations/onMaxIterations; now route through the shared ToolLoopGuard like the built-in (default behavior unchanged)
- make default model configurable, default claude-sonnet-4-6 adds anthropic.model system property; per-request and AiConfig models still win over the fallback
- version-bump touches only Atmosphere deps; regen SKILLCARDs
- repair rag-chat/mcp-server/browser-agent + SB3 Tomcat + stream errors
- restore real third-party dep versions clobbered by 4.0.x bump
- pin deny-policy refusal on A2A and AG-UI bridges
- fail-closed Maven Central pre-check guards against version burn
- e2e proving config-driven routing is consumed on the wire
- attribute GenAI "experimental" to the OpenTelemetry spec, not the emitter GenAiTracer/MetricsCapturingSession Javadoc + README make clear the implementation is production code; only the upstream OTel GenAI convention is experimental
- document atmosphere new --routing flag and correct agui-chat sample row agui-chat is now a real @Agent (LLM + @AiTool over the AG-UI native bridge), not scripted
- consolidate per-model-call observability into ModelCallScope replaces duplicated fireModelStart/End/Error + timing across 9 adapters and 2 Kotlin runtimes; event count/ordering unchanged (ADK start-time aligned to dispatch)
- unify tool-call accumulator across built-in, anthropic, cohere shared ToolCallAccumulator gains argumentsAsMap(); deletes the two private copies; built-in parse path unchanged
- extract AbstractSseLlmClient shared by Anthropic and Cohere clients collapses ~268 lines of duplicated HTTP/SSE plumbing (header filter, snippet read, data: loop, tool-schema) into one base; wire behavior byte-identical (black-box suites unchanged)
- add TokenUsage.fromCounts and migrate adapter usage translation collapses 11 hand-rolled null-guard/total-fallback sites; 2 sites now compute total=input+output instead of 0 when the provider omits total (regression-tested)
- hoist models() default into AbstractAgentRuntime removes 9 byte-identical adapter overrides; koog (distinct logic) and the interface default unchanged
- fix cancel-test race by awaiting worker interrupt observation worker records the interrupt asynchronously; wait on a latch instead of reading the flag right after whenDone()
- correct AI doc/sample drift vs verified runtime capabilities embeddings 5->7 runtimes, TOOL_CALL_DELTA Built-in+Cohere, ai.md classpath table, agui relabel, samples.json reattach
- bound playwright install with timeout to prevent multi-hour hangs
- both-layer regressions for the sweep failures (JUnit + Playwright)
- sync version stamp to 4.0.56-SNAPSHOT
- deep-link concept tables to docs site; fix verifier count
- bump version to 4.0.55
- prepare next development version 5.0.33
- prepare for next development iteration 4.0.56-SNAPSHOT
- Static verifier over MCP. The plan-and-verify ("Guardians") stack is
reachable as read-only MCP tools when
atmosphere-verifieris on the classpath:atmosphere_verifier_summary,atmosphere_verifier_examples, andatmosphere_verifier_check. The check tool plans a goal and runs every verifier over the resulting plan without executing it (statusverified/refusedwith the per-verifier violations); the mutating verify-then-execute path stays behind the admin write gate.
- wasync: a user
close()is no longer resurrected by a late OPEN event — a close requested before the transport finished opening is now honored when the OPEN arrives, instead of reviving the connection. - Documentation accuracy sweep — corrected the Z3 binding version (4.14.0)
and
SmtCheckerpriority (200); aligned ADK / Semantic Kernel / Alibaba versions, crewai test counts, and the ms-governance policy name; fixed the embedded-jetty client type, the admin-bundle default authorizer, and runtime-truth claims in the embabel / agentscope / coding-agent docs; corrected third-party dependency versions and citations.
- CI doc-drift gates — fact/enumeration checks, link-rot detection, and
sibling-site (atmosphere.github.io) verification, plus an
atmosphere-skillslink-checker allowlist.
- Rich human-in-the-loop approval payloads. A reviewer can now resolve a tool
approval with more than approve/deny: approve-with-edited-arguments (the
tool runs with the reviewer's arguments) or respond (the reviewer answers on
the tool's behalf — structured JSON or free-form text — and the tool does not
run). Wire protocol:
/__approval/<id>/approve {"arguments":{…}}and/__approval/<id>/respond {…}. Fail-safe: a malformed edited-args payload denies. Session-scoped in-memory (not crash-durable). The legacy boolean resolution path is unchanged. - Eval flywheel.
JournalDatasetPromoterturns a recordedCoordinationJournalinteraction into anEvalCasedataset row (trace→dataset), andSampledLiveScorergrades a configurable fraction of live turns intoEvalRunverdicts (online scoring). Both are wired intoEvalControllerwith admin REST routes (/api/admin/evals/dataset,/dataset/promote,/score). - OAuth on-behalf-of credential vault.
OAuthOnBehalfOfCredentialStoreis a concreteCredentialStorethat performs an RFC 8693 token exchange — swapping a user's stored subject token for a short-lived access token scoped to a downstream tool, so an agent calls external APIs as the user. Fail-closed (no token → no fallback credential), token-cached until expiry. Opt in withatmosphere.ai.identity.oauth-obo.enabled=true. - Realtime voice bridge.
VoiceBridge+ theRealtimeVoiceProviderSPI bridge client audio frames over the existing WebSocket broadcaster to a speech-to-speech provider, fanning synthesized audio (Content.Audio) and transcripts back to the client. A dependency-freeLoopbackVoiceProviderships as the runnable reference (echoes audio); OpenAI Realtime / Gemini Live providers implement the same SPI. - Content-safety moderation guardrail.
ModerationGuardrailblocks turns whose request and/or response is flagged for hate / harassment / self-harm / sexual / violence / illicit content, on the existing fail-closed guardrail pipeline. Pluggable detector: zero-depRuleBasedModerationDetector(default) or cross-runtimeLlmModerationDetector. Fail-closed by default (a detector outage blocks the turn;.failOpen()is the explicit opt-out). Opt in withatmosphere.ai.guardrails.moderation.enabled=true(...detector=llmfor the model tier). - Self-healing structured output.
@AiEndpoint(structuredOutputRetries = N)(or theai.structured.retryrequest-metadata key on theAiPipelinepath) re-prompts the model with the schema-validation error as feedback when a typed response fails to parse, up toNextra attempts, then fails closed. Works identically on the websocket and channel-bridge paths. - OpenAPI → governed tools.
OpenApiToolImporterturns an OpenAPI 3.x spec (JSON or YAML, with local$refresolution) intoToolDefinitions whose executor performs the HTTP call. The imported operations ride the same policy-admission and plan-and-verify path as hand-written@AiToolmethods;approvalForWritesroutes mutating verbs through the HITL gate. - MCP client depth.
McpClientOptionsadds per-server tool filtering and display-only renaming (the executor still calls the server's original tool name), plus elicitation/sampling callback handlers advertised duringinitialize.McpServerRegistryaggregates several servers into one collision-free tool list (first-wins) and owns their lifecycle.
- MCP authorization now validates bearer tokens end-to-end. A request is authenticated
when either a servlet resource-server filter set the request principal (e.g. Spring
Security
oauth2ResourceServer) or a configuredTokenValidatoraccepts theAuthorization: Bearertoken (loaded fromorg.atmosphere.auth.tokenValidator, validated byatmosphere-mcpitself — no framework-specific wiring). The RFC 9728 metadata is now served on the agent registration path too. Proven end-to-end on the embedded server, Spring Boot, and Quarkus (JVM). Thespring-boot-mcp-serversample gains an opt-inauthprofile (default off) demonstrating it. - MCP runs on Quarkus.
@Agent-based MCP endpoints now register under the Quarkus extension (the build scan recognizes@Agentand indexes the optionalatmosphere-agent/atmosphere-mcpjars when an@Agentclass is present). JVM mode; native image is not yet supported for@Agent-based MCP.
- Added a stateless
2026-07-28round-robin end-to-end test (twotools/callwith no session header both succeed, plusserver/discoverandMcp-Methodmismatch) inmodules/integration-tests, proving the no-session-affinity claim over live HTTP.
- MCP
2026-07-28release candidate — the largest MCP revision since launch, implemented as a stateless dialect that coexists with the session-based protocol (2024-11-05through2025-11-25). The dialect is selected per request (the client carries the protocol version inparams._metaor callsserver/discover), so existing clients are unaffected. Stateless core has noMcp-Session-Idand noinitializehandshake, so the server runs behind a plain round-robin load balancer with no session affinity. - MCP operability —
Mcp-Method/Mcp-Namerouting headers (validated against the body),ttlMs+cacheScopecache metadata ontools/list/resources/list/resources/read, and W3C Trace Context (traceparent/tracestate/baggage) read from_metaand bridged into the OpenTelemetry span. - MCP Tasks extension (
io.modelcontextprotocol/tasks) and multi-round-trip input —@McpTool(longRunning = true)returns a task handle polled viatasks/get, and the stateless dialect can returnInputRequiredResultwith a base64requestStateto request more input mid-call and resume on any instance. - JSON Schema 2020-12 dialect (
$schema) on generated tool input schemas, and a standardized resource-not-found error (-32602) on the stateless dialect. - MCP Apps (SEP-1865) —
@McpTool(uiResource = "ui://…")plus atext/html;profile=mcp-appresource makes a tool an MCP App. The Atmosphere console is a working host: it renders the app in a sandboxed iframe, runs a bidirectional App Bridge (apps call server tools through the host under the policy gateway; the host lists and calls the app's ownappCapabilities.tools), and uses a separate-origin sandbox proxy for isolation (atmosphere.mcp-sandbox-origin, with alocalhost↔127.0.0.1dev fallback, otherwise an opaque-origin direct sandbox). - MCP authorization (protocol glue) — the server acts as an OAuth 2.0 Resource Server:
RFC 9728 protected-resource metadata at
/.well-known/oauth-protected-resourceand a401+WWW-Authenticatechallenge for unauthenticated requests; opt in via theorg.atmosphere.mcp.auth.*init parameters. This release shipped the protocol glue only; bearer-token validation was wired end-to-end in 4.0.52 (see Unreleased).
- Pruned dead/unwired internal classes found during a release-readiness audit —
none was documented, advertised, or reachable from a user code path:
McpWebSocketHandler(superseded byMcpHandler's direct WebSocket-frame handling),AgUiSession(superseded byResourceAgUiStreamingSession),AiCoalescingBroadcasterCache(a delegate-onlyBroadcasterCachethat the no-arg reflective cache-wiring path cannot instantiate),AdkArtifactBridge,AdkCompactionBridge,AtmosphereRequestBridge,AtmosphereResponseBridge, the channelsAuditLoggingFilter(never registered as a bean, so it never reached the filter chain), the unwiredGrpcProtocolBridge, and the A2AListTaskPushNotificationConfigsResponseDTO (theListTaskPushNotificationConfigsmethod returnsERROR_PUSH_NOT_SUPPORTED, so the response type was never constructed).
ToolBridgeUtils.findUnescapedQuoteno longer advances the scan index past the end of the string when malformed tool-call JSON ends in a lone backslash — the escaped-character skip is now bounds-checked (boundary safety, Correctness Invariant #4). Regression test added.
-
Interactions API (
org.atmosphere.interactions, artifactatmosphere-interactions) — a stateful agent-turn resource layered above theAgentRuntimeSPI, so it works for every adapter with no per-runtime code. AnInteractioncarries a stable id, a durablesteps[]event log, and chains turns viapreviousInteractionId(the server holds history; the client does not resend it). Turns run synchronously or in the background (background=truereturns aRUNNINGrecord immediately and is retrievable after a disconnect), andstore=falsestreams without persisting. The starter exposes the resource overPOST /api/interactions,POST /api/interactions/{id}/continue,GET /api/interactions/{id},GET /api/interactions,POST /api/interactions/{id}/cancel, andDELETE /api/interactions/{id}— every mutating route is default-deny behindatmosphere.interactions.http-write-enabledplus an authenticated principal (Correctness Invariant #6). TwoInteractionStorebackends ship:InMemoryInteractionStore(default) andSqliteInteractionStore; the SPI is pluggable for others.atmosphere.jsgains a typedInteractionsClient(atmosphere.js/interactions) covering the REST surface pluspollUntilTerminal/watchhelpers. -
Interactions live streaming — a background interaction now streams its durable
steps[]to a subscribed browser as they are produced, over the Atmosphere transport (/atmosphere/interactions-stream?id=<id>, WebSocket/SSE). On connect the handler replays the steps captured so far (late-joiner catch-up, deduped client-side by sequence), then pushes each new step live and a terminal frame on completion; ownership is enforced per-interaction, same scope as the REST read.InteractionsClient.subscribe(id, handlers)bridges it on the client and the Atmosphere Console's Interactions tab renders the live step timeline. AnAtmosphereInterceptorresolves the principal for the stream socket so ownership holds across all transports (a servlet filter's request attribute does not survive the WebSocket upgrade). Demonstrated inspring-boot-coding-agentandspring-boot-multi-agent-startup-team. -
ToolKind+@AiTool(kind = …)— tools declare a behavioural category (EDIT,READ,EXECUTE,NETWORK,DELETE,OTHER; defaultOTHER). This makesPermissionMode.ACCEPT_EDITSa real behaviour instead of aDEFAULTalias: it now auto-approves a tool's own@RequiresApprovalprompt whenkind == EDIT, while every other tool still routes through the per-tool approval gate. The classification is compile-time author metadata (not runtime caller-asserted intent), the defaultOTHERkeeps the approval posture exactly as restrictive as before, and the relaxation never overrides an operator-configuredToolPermissionPolicyDENY/CONFIRMor aDenyAllpolicy.ToolExecutionHelperAcceptEditsTestpins all four cases. -
Code-as-action sandbox (
org.atmosphere.ai.code) — acode_exectool that lets a model accomplish tasks by writing a block of code (bash / JavaScript / Python) instead of negotiating many fine-grained tool calls. Each session gets an isolated, ephemeral container (CodeSandboxSPI,ContainerCodeSandboxover Docker/Podman) with hardening applied —--network noneby default, non-root,--cap-drop ALL,--security-opt no-new-privileges, read-only rootfs + a bounded writable workspace, and memory/cpu/pid caps — provisioned lazily on first use and torn down on every terminal path via the newStreamingSession.onTerminate(AutoCloseable)primitive. Default-deny: code execution is off unlessorg.atmosphere.ai.code.enabled=trueand a container engine is confirmed present at runtime (Correctness Invariant #5); the tool is registered into@AiEndpointdispatch only then, with the tool-loop ceiling lifted to 25 write→run→observe rounds. Each round streams anAiEvent.AgentStepplus any screenshots the code produced, rendered inline in the Console as markdown data-URI images. Newsamples/spring-boot-browser-agentdemonstrates it (Cohere-backed, requires Docker): the agent drives a headless browser with Playwright and you watch the screenshots arrive live.
ai-policy-regoandai-policy-cedarnow ship aMETA-INF/services/org.atmosphere.ai.governance.PolicyParserregistration, so Rego and Cedar policy artifacts are auto-discovered byServiceLoaderthe same way YAML always has been — no programmatic parser wiring required. Safe because both parsers have lazy no-arg constructors (theopa/cedarbinary is only touched at evaluation, and parse failure is already fail-closed). The Kafka/Postgres audit sinks are deliberately left on programmaticGovernanceDecisionLog.addSink()wiring: they need a live broker / JDBC connection, so auto-activating them on classpath presence would advertise capability that cannot run (Runtime-Truth, Correctness Invariant #5).RegoPolicyParserTest/CedarPolicyParserTestpin the discovery.- Four new pre-push drift-prevention gates, each closing a class the
.harness/drift-log.mdhad recorded but left un-automated, wired intoscripts/pre-push-validate.shTier-1:validate-runtime-overlay-coverage.sh(every snapshot runtime must have a CLI overlay and abom/pom.xmlartifact — drift #59);validate-dangling-doc-comments.sh(parse-onlyjavac -Xlint:dangling-doc-commentsunder a JDK ≥ 23 to catch detached Javadoc locally, not only on the Native Image lane — drift #80);validate-doc-version-alignment.sh(third-party dependency versions in Markdown must match the pinnedpom.xml/package.json— drift #12/#18/#75); andvalidate-doc-symbols.sh(annotation references in Markdown must resolve to an in-tree declaration or a curated external allowlist — drift #72). Two of the gates caught a pre-existing drift on first run:atmosphere-semantic-kernelwas missing frombom/pom.xml(now added), andmodules/langchain4j/README.mdnamed LangChain4j 1.12.2 while the pom pins 1.15.0 (now corrected). CLI overlay coverage was also extended to the three native runtimes (anthropic,cohere,crewai) incli-e2e.ymlpath filters and thetest-cli.shscaffold+compile matrix.
-
atmosphere-crewai—AgentRuntimefor the CrewAI multi-agent framework via an out-of-process Python sidecar. First non-Java runtime adapter in the project; the boundary isHTTP + SSEfor the request stream plus a loopbackToolCallbackServerfor Java→Python tool RPC. Pins 9 capabilities (TEXT_STREAMING,TOKEN_USAGE,AGENT_ORCHESTRATION,CANCELLATION,TOOL_CALLING,SYSTEM_PROMPT,STRUCTURED_OUTPUT,TOOL_APPROVAL,PER_REQUEST_RETRY) viaCrewAiRuntimeContractTest+ the capability snapshot (which now enumerates 12 runtimes). Like every other runtime,isAvailable()is config-gated — requiresATMOSPHERE_CREWAI_SIDECAR_URLpointing at a running sidecar that responds OK toGET /health. -
modules/crewai/sidecar/— companion Python packageatmosphere-crewai-bridge(FastAPI + uvicorn + crewai 1.14) speaking the documented wire protocol. Materialises JavaToolDefinitions ascrewai.tools.BaseToolsubclasses viapydantic.create_model, injects them into agents, and threadscontext.systemPrompt()into each agent'sbackstoryinside a delimited block. Ships with a workingexamples/ollama_crew.pyfactory that targetsqwen2.5:0.5b(no API key required). -
CLI runtime overlay (
cli/runtime-overlays.json) forcrewai, soatmosphere new my-app --template ai-chat --runtime crewaiscaffolds with the dependency wired and the sidecar setup documented inline. -
End-to-end validation captured at
.harness/crewai-e2e-success.png: chrome-devtools drove/atmosphere/console/against a real Ollama-backed crew; the browser rendered 25 tokens at 46.8 tok/s through the full chainWebSocket → @AiEndpoint(runtime=crewai) → HttpSseSidecarClient → atmosphere-crewai-bridge → crewai 1.14 → litellm → Ollama. Console zero errors, sidecar log confirmsPOST /v1/chat/completions HTTP/1.1 200 OKagainst the local Ollama instance. -
modules/coordinator/journal— event-sourced execution log for the coordinator. Layers four additive pieces onto the existingCoordinationJournalSPI without breaking any of the 119 existingnew CoordinationEvent.*call sites across coordinator / admin / checkpoint / integration-tests:EventEnvelope(eventId, parentEventId, event)+ default-methodrecordEnveloped/retrieveEnvelopedonCoordinationJournal.JournalingAgentFleetthreads parent IDs through every dispatch path (parallel/pipeline/route/proxy.call/callAsync/stream):CoordinationStarted→AgentDispatched→AgentCompleted/AgentFailed→AgentEvaluated. Legacyrecord(event)callers continue working — events are wrapped as root envelopes with no parent.CoordinationProjection.from(journal, coordinationId)— pure read-only causal DAG built fromretrieveEnveloped. Exposesroots(),children(eventId),walk(visitor),agents(),failedDispatches(),evaluations(). No execution, no LLM, no side effects.FileCoordinationJournal(Path)— append-only NDJSON file backend, one JSON object per line. Replays onstart()into an in-memory index for queries; tolerates a truncated final line from a JVM kill mid-append (logs and skips). Single-writer locked appends; polymorphic ser/deser of the sealedCoordinationEventhierarchy via a Jackson 3 mix-in so the event records stay annotation-free.CoordinationFork+ newForkCreatedevent variant — what-if branching primitive.fork.from(coordId, eventId).reason(...).with(altCall).execute(fleet)creates a new coordination id (or accepts an explicit one), records aForkCreatedenvelope linking back to the parent event, and runs the alternate dispatch viaJournalingAgentFleet.withCoordinationId(...). The parent coordination is immutable; the fork is a peer with its own future. Pre-flight check rejects unknownparentEventIdwith a fastIllegalArgumentException.
Backed by 78 tests in
modules/coordinator/src/test/java/.../journal/including a three-process integration test that runs a parallel coordination, restart-replays from disk, projects the DAG, forks an alternate, restart-replays again, and verifies both the original and the forked branch survive across two simulated JVM kills.modules/coordinator/README.mddocuments the new surface. -
Cohere
TOOL_CALL_DELTAstreaming capability (3327425d50).CohereChatClient.handleToolCallDeltasurfaces incremental tool-call argument fragments as they arrive, andCohereAgentRuntime(line 269) now declaresTOOL_CALL_DELTA. The same honesty pass removedPROMPT_CACHINGfrom Cohere — the v2 API exposes no prompt-cache control, so advertising it was Runtime-Truth drift; the capability snapshot was re-pinned accordingly. -
Quarkus extension integration parity: five optional surfaces, each gated on classpath presence and covered by a dedicated build-step test (
3327425d50).AtmosphereProcessorregisters Cache, Health (HealthBuildItem), Micrometer metrics (AtmosphereMetricsProducer), OpenTelemetry tracing (AtmosphereTracingProducer), and governance metrics (AtmosphereGovernanceMetricsProducer) producers — seeAtmosphereProcessor.java:432-510and theAtmosphere{Cache,Health,Metrics,Tracing,GovernanceMetrics}BuildStepTestsuite. -
modules/quarkus-grpc— Quarkus gRPC bridge extension (runtime+deploymentsubmodules) (3327425d50). -
scripts/validate-no-beta-on-main.sh— push-time gate enforcing the release-frequency rule: pre-GA escape-hatch framing (beta annotations, hourglass deferral markers, phased planning labels, or roadmap-deferral prose) introduced relative toorigin/mainfails the build, somainstays release-ready (3327425d50).
- Bumped JetBrains Koog
0.8.0 → 1.0.0(4685a844bb, root pomkoog.version) — Koog's first GA. The adapter configures via Koog 1.0's stableOpenAILLMClient/MultiLLMPromptExecutor(AtmosphereKoogAutoConfiguration.kt); the full Koog capability set (VISION,AUDIO,MULTI_MODAL,PROMPT_CACHING,TOOL_CALLING,TOOL_APPROVAL, …) is unchanged and re-pinned byKoogRuntimeContractTest+ the capability snapshot. - Bumped
langchain4j.version1.14.0 → 1.15.0(abd774f68d),logback-version1.5.25 → 1.5.32(58f2e6d373), andcommons-lang33.18.0 → 3.20.0(8dea5788ac).
HttpSseSidecarClientnow pinsHttpClient.Version.HTTP_1_1. The JDK'sjava.net.http.HttpClientdefaults to HTTP/2 for plain HTTP and attempts anUpgrade: h2cnegotiation; uvicorn (the FastAPI host for the CrewAI sidecar) does not implement the h2c upgrade and the resulting request lands with an empty body, which FastAPI rejects as422 Field required, loc=["body"], input=null. The bridge-testFakeSidecar(acom.sun.net.httpserver.HttpServer) tolerated the upgrade preamble and parsed the body anyway, so the bug only surfaced under real uvicorn — exactly the gapfeedback_chrome_devtools_only.mdwarns about. Added a regression test (CrewAiAgentRuntimeBridgeTest.httpClient_pinnedToHttp11) that reflects into the client and asserts the version, so a future "just use the defaultHttpClient" refactor breaks the build before it breaks production. Drift recorded as.harness/drift-log.md#64.- Koog runtime reaches Gemini via Google's OpenAI-compatible base
URL (
87aa2cc824). Koog 1.0's native Google client ships only on a JVM-incompatible path, soAtmosphereKoogAutoConfigurationpoints the stableOpenAILLMClientat any OpenAI-compatible endpoint whenatmosphere.koog.base-url/LLM_BASE_URLis set (e.g.https://generativelanguage.googleapis.com/v1beta/openaiforgemini-2.5-flash). Regression-gated byAtmosphereKoogAutoConfigurationTest. Drift recorded as.harness/drift-log.md#77 — the0.8.0 → 1.0.0bump had been reported done on CI alone, which hid the dropped-Gemini regression. - Spring Boot JDK 26 long-term-memory disconnect hang resolved via
an idle-reaper fallback (
b2e9e09e71).LongTermMemoryHttpE2eTest's disconnect path intermittently hung on the JDK 26 lane because the WebSocket-close →onDisconnectlifecycle could be dropped under fork contention; anIdleResourceInterceptor-based reaper (platform-thread scheduler,maxInactiveActivity=5000) now fires the disconnect lifecycle independently, so suspended resources are reaped and facts persisted even when the close frame is lost. Drift recorded as.harness/drift-log.md#78–#79 — an earlier 60s → 120s await bump was ineffective (a timeout cannot fix a hang).
- Bumped
tomcat-embed-core11.0.21 → 11.0.22(root pomtomcat-versionproperty) to close 7 Dependabot advisories — 3 critical (security-constraint bypassGHSA-5m62-pw8w-7w9f, digest-auth bypassGHSA-h6fc-48rj-7qqh, HTTP/2 header validationGHSA-r29c-68gh-xp6x), 3 high (LockOutRealm case-sensitivityGHSA-5mp6-jrq3-r938, WebSocket auth-header exposureGHSA-fv25-8xcx-gqjc, WebDAVLOCK/PROPFINDunbounded readGHSA-gx5v-xp9w-j4cg), and 1 low (AJP secret non-constant-time compareGHSA-9m89-8frq-c98c). The pin stays scoped totomcat-embed-core;tomcat-embed-elandtomcat-embed-websocketcontinue to follow each Spring Boot BOM (3.5.x keeps the 10.1.x line, 4.0.x stays on 11.0.x). - Bumped
protobufjs7.5.6 → 7.5.8inmodules/integration-tests/package.json+ lockfile to closeGHSA-jggg-4jg4-v7c6. - Dismissed 3 remaining open Dependabot alerts that have no in-tree
fix path. Two
org.json:jsonalerts (GHSA-3vqj-43w4-2q58,GHSA-4jq9-2xhw-jpx7) citedmodules/runtime/pom.xml— a manifest that no longer exists;org.json:jsonwas removed reactor-wide in commit4f40968d4d(4.0.42-SNAPSHOT, "drop org.json:json — Jackson 3 only"). Dismissed asnot_used. Oneopentelemetry-apialert (GHSA-rcgg-9c38-7xpx/CVE-2026-45292, medium DoS via unbounded W3C Baggage Propagation) is blocked upstream: Quarkus 3.35.x and 3.36.0 both ship OTel1.60.1, and thesamples/quarkus-ai-chatpin must follow the BOM to keepquarkus-micrometer-registry-prometheusworking. Dismissed astolerable_risk(sample, baggage propagation not enabled, Vert.x enforces the 8 KiB header limit recommended in the advisory). Re-evaluate when Quarkus's BOM picks up OTel 1.62+.
- CLI runtime overlays for
anthropicandcohere(cli/runtime-overlays.json). Both runtimes had been shipped inmodules/and documented in the top-level README —atmosphere-anthropicsince 2026-05-19 (1195845304),atmosphere-coheresince 2026-05-23 (1dfebcb5ff) — but neither had a CLI scaffolding overlay. The commandatmosphere new my-app --template ai-chat --runtime cohere(or--runtime anthropic) now works. Same change adds both artifacts tobom/pom.xmland the parentpom.xml's<dependencyManagement>so a Maven build resolves their version without an explicit<version>in the consuming pom. Verified end-to-end via chrome-devtools against the Atmosphere Console: real Coherecommand-a-plus-05-2026LLM response over WebSocket with 18.3 tok/s streaming through theCohereChatClient→ realhttps://api.cohere.com/v2/chatHTTP call. - Durable hibernating
Workflow<S>primitive inatmosphere-checkpoint(a0ac15f1e3). OrdersWorkflowStep<S>instances over an application-owned state type and composes the existingCheckpointStoreSPI for persistence. SealedStepOutcome<S>(Advance/Hibernate/Done/Fail) andWorkflowResult<S>(Completed/Hibernated/Failed) drive the runner; per-stepmaxRetries()+retryDelay()cover transient failures. Hibernation is return-not-park: a step that returnsStepOutcome.hibernate(state)writes a snapshot and the call returns to the caller with no platform thread held; a laterrun()against the same coordination resumes at the next un-completed step, including across JVM restarts when the store is persistent.WorkflowSqliteResumeTestpins the cold-restart contract — closesSqliteCheckpointStore, opens a fresh handle on the same file, builds a freshWorkflowinstance, and asserts only the un-completed step executes. Ten unit tests cover linear execution, hibernate-and-resume, retry-success, retry-exhaustion, explicit fail, duplicate-step-name rejection, snapshot-precedence overinitialState, anddeleteAllSnapshots. SqliteLongTermMemoryandRedisLongTermMemory(835a88d252, rebased tofbbfa457a2) — persistent backends for theLongTermMemorySPI inatmosphere-durable-sessions-sqliteandatmosphere-durable-sessions-redis. Both can share a connection with their siblingSessionStore/ConversationPersistenceimplementations.LongTermMemoryBackendIntegrationTestparameterizes the fullLongTermMemoryInterceptorround-trip over all three backends;LongTermMemoryMultiInstanceTest(d4609cf0fc) proves the pod-A-writes / pod-B-reads scenario the persistent backends exist for, using two independentLongTermMemoryhandles against the same shared store.scripts/validate-backend-class-refs.sh(835a88d252) — structural gate scanning*.javaJavadoc and*.mddocumentation for(Sqlite|Redis|Postgres|Mongo|Cassandra|Hazelcast|JGroups|Kafka|Nats)<Word>tokens that don't resolve to a declared class inmodules/**/src/{main,test}/java/..harness/external-class-allowlist.txtwhitelists genuine third-party types (Lettuce, Kafka, Testcontainers, brand names). Wired intopre-push-validate.shTier-1. Catches future SPI-backend overclaim drift before it merges (the class of bug that drift #53 logged on the originalLongTermMemoryJavadoc).- Per-runtime
SKILLCARD.yamlmanifests with OpenSSF Model Signing (32a8e8b935+ this commit) — capability + provenance documents that are signable via the same Sigstore-keyless toolchain NVIDIA's verified-agent-skills programme uses. Cards ship unsigned (signing.status: unsigned); signatures are produced at release-tag time by.github/workflows/sign-skillcards.yml.scripts/regen-skillcards.shemits one card per snapshot-pinned runtime atmodules/<X>/SKILLCARD.yaml, derived from.harness/capabilities.snapshot.jsonand each module'spom.xml. The repo-rootSKILLCARDS.mdcatalog (regenerated by the same script) lists every shipped runtime, its capability count, and its signature state — that file is the source of truth for "which runtimes have a card", not this paragraph (a named list and count would stale every time a new adapter lands). Each card declaresspec: atmosphere/skillcard/v1with artifact coordinates, the registeredAgentRuntimeSPI implementation FQN, the alphabetical+count-pinnedAiCapabilityset, contract-test path, license (Apache-2.0), and asigningblock referencingsignature_file: SKILLCARD.yaml.sigwithenvelope: openssf-model-signing/v1andbundle_format: sigstore..github/workflows/sign-skillcards.ymlsigns every card on tag push viamodel_signing sign sigstore— short-lived Fulcio cert- Rekor transparency-log entry, OIDC identity bound to the
workflow path — and uploads the
.sigfiles as a GitHub release asset and as a workflow artifact for downstream bundling. Cards AND signatures are bundled into each runtime jar atMETA-INF/atmosphere/via a root-pom<resource>declaration so consumers can verify integrity without unpacking the source tree; modules without a SKILLCARD (cpr, mcp, channels…) see the include filter match nothing and are unaffected.SkillCardSnapshotTestinatmosphere-ai-testenforces three contracts: (a) capability set + count drift against the snapshot, (b) every snapshot runtime has a card on disk, (c) shape conformance with required top-level keys (schema_version,spec,status,name,language,description,license,artifact,spi,capabilities,contract_test,provenance,signing) and required signing fields (envelope,signature_file). When a.sigis present beside a card, the test shells out tomodel_signing verify sigstorewith the Atmosphere workflow's identity pin and fails on any signature mismatch; sigs are skipped silently when absent (the normal state onmainbetween tagged releases) or when themodel_signingCLI is not installed locally.scripts/scan-skillcards.shis the SkillSpector-equivalent pre-publish gate — scans every card for prompt-injection markers (regex set: "ignore previous instructions",[INST], ChatML role tags), hidden Unicode (zero-width chars, Bidi overrides), capability-safety violations (TOOL_CALLING ⇒ TOOL_APPROVAL per OWASP excessive-agency), missing SPI classes (FQN doesn't resolve to a source file on disk), and path-shaped-field safety. HIGH-severity findings fail pre-push and the signing workflow, so a compromised manifest can never be published as "signed".scripts/validate-capability-claims.shruns all three ofregen-skillcards.sh --check,scan-skillcards.sh --check, and the snapshot freshness check;scripts/sign-skillcards.sh+verify-skillcards.share the local CLI wrappers (key / certificate / sigstore modes).regen-skillcards.shadditionally emitsSKILLCARDS.mdat the repo root — the catalog index that lists every runtime, its signature state, and links to the card + contract test. Distribution model: git itself is the daily sync, consistent with the rest of this repository; release-time signatures are also attached to the GitHub release as workflow artifacts. Pre-push validator regex coversSKILLCARD.yaml(.sig)?,(regen|sign|verify|scan)-skillcards.sh, the signing workflow, andSKILLCARDS.md. Curated runtime-specific risk and mitigation prose remains in each runtime's module README.
- Rekor transparency-log entry, OIDC identity bound to the
workflow path — and uploads the
- Native Anthropic Messages API runtime in a new
atmosphere-anthropicmodule (1195845304).AnthropicMessagesClientposts directly tohttps://api.anthropic.com/v1/messages, parses the SSE stream (message_start,content_block_start,content_block_deltawithtext_deltaandinput_json_delta,message_deltacarryingusage.input_tokens/output_tokens,message_stop), and drives thetool_use→tool_resultloop through the sharedToolExecutionHelper.executeWithApproval(max five rounds, cancellation-aware).AnthropicAgentRuntimeis registered viaServiceLoaderat priority 100 — same posture as every other framework runtime — and inheritsexecuteWithOuterRetryso it claimsPER_REQUEST_RETRYhonestly alongsideTEXT_STREAMING,SYSTEM_PROMPT,STRUCTURED_OUTPUT,TOOL_CALLING,TOOL_APPROVAL,TOKEN_USAGE,CONVERSATION_MEMORY,BUDGET_ENFORCEMENT,CONFIDENCE_SCORES, andPASSIVATION.isAvailable()returns true only whenanthropic.api.key(system property orAiConfig.LlmSettings) is present, satisfying Correctness Invariant #5 (Runtime Truth). The capability snapshot, capability matrix, andmodules/ai/README.mdprose were regenerated in the same commit; runtime count is now ten. - LangChain4j 1.15.0 parity (
9e72c6c6f7): tool-error fallback inToolExecutionHelper.executeWithApprovalwraps null/blank exception messages with the throwable's simple class name so NPEs surface as{"error":"NullPointerException"}to the model instead of opaque{"error":"null"}. Custom HTTP headers onOpenAiCompatibleClientcarry proxy / per-tenant / tracing metadata (Helicone, OpenRouter, …); reserved names (Authorization,Content-Type,Accept) are filtered at request-build time.AgentFleet.vote(...)adds consensus dispatch — fans every call out in parallel and returns the result whose normalized text (strip().toLowerCase(Locale.ROOT)) is shared by the most peers, with deterministic insertion-order tie-breaking and a synthetic"vote"failure when every peer fails. - JFR observability, declarative tool permissions, first-run sub-agent
guard, and episodic memory (
63e34f11a4):- JFR events under
org.atmosphere.ai.jfr—AiCallEvent,AgentTurnEvent,ToolInvocationEvent,SubAgentDispatchEvent,EpisodicMemoryAccessEvent,SessionLifecycleEvent,AiErrorEvent— emitted byJfrAiMetrics.CompositeAiMetricslets JFR coexist with Micrometer (and any other backend) instead of one replacing the other. ToolPermissionPolicySPI with thePropertiesToolPermissionPolicyreference impl: declarative ALLOW/DENY rules layered on top of the existing@RequiresApprovalannotation gate. Reachable fromToolExecutionHelperso every runtime inherits it.- First-run sub-agent dispatch guard in
DefaultAgentFleet— the bootstrap pass sequences the first call through every peer individually before the parallel fan-out path is unlocked, so a cold-cache misconfiguration is caught against one peer instead of fanned out N× simultaneously. EpisodicMemoryStoreSPI withInMemoryEpisodicMemoryStoreandJsonFileEpisodicMemoryStorebackends, theMemoryEntry/EpisodicMemoryQuery/EpisodicMemoryTyperecords, and theEpisodicMemoryAccessEventBridgeJFR fan-out.
- JFR events under
GrpcWasyncTransportTeststatus-poll widened from 5 s to 20 s and the@Timeoutfrom 10 s to 30 s for JDK 26 scheduler variance (df19027ab8). Closes a recurring "Core (JDK 21/26)" flake.
samples/spring-boot-coding-agent: case-insensitive README probe (38565e43ef). Repos likesindresorhus/awesomeshipreadme.mdlowercase, which the oldREADME.md-only lookup missed silently. The redundantapplication.propertiesand the unusedatmosphere.ai.runtimesetting were also dropped — the sample is deterministic and never invokes an LLM.
- Spring AI Alibaba: unconditional
TOOL_CALLING/TOOL_APPROVAL/TOKEN_USAGE(534317f03d) —UsageCapturingChatModelwraps the configured Spring AIChatModelbean at auto-configuration time; per-thread accumulator capturesChatResponseMetadata.getUsage()across every step of the ReAct graph and emits a singlesession.usage(TokenUsage)after each dispatch. Tool calling is no longer gated onstaticChatModel != null—SpringAiAlibabaToolBridgeis wired on every dispatch with tools, and the runtime fails fast withconfigurationHint()ifChatModelis missing. Closes the last conditional capability gap from the runtime parity push (62a9b7e6af). - RAG vector-store matrix expanded with three direct connectors
(
31d6455a75):PgVectorContextProvider(Postgres + pgvector via JDBC),QdrantContextProvider(Qdrant REST overjava.net.http.HttpClient), andPineconeContextProvider(Pinecone REST). Each connector embeds the user query throughEmbeddingRuntime, validates caller-controlled identifiers at construction time per Boundary-Safety invariant, and ships with a Mockito-backed unit-test suite.modules/rag/README.mdadds a reachability matrix showing the six direct providers plus the Spring AI / LangChain4j bridges covering the long tail (Weaviate, Milvus, Chroma, Elasticsearch, Redis Stack, MongoDB Atlas, OpenSearch, Cassandra). - Workflow authoring inside the admin control plane (
81ff454177) —WorkflowManifestJSON record,WorkflowStoreSPI withInMemoryWorkflowStoredefault and optimistic-concurrency version conflict detection,WorkflowControllerwithControlAuthorizergating plus audit-log entries on every save / delete, Spring Boot endpoint exposesGET/POST/DELETE /api/admin/workflow, and/atmosphere/admin/workflow.htmlships a vanilla-JS authoring UI that lists / creates / edits manifests against the REST surface. - Eval dashboard inside the admin control plane (
38e2a45920) —EvalRunJSON record,EvalRunStoreSPI with bounded-ring-bufferInMemoryEvalRunStoredefault (500 runs per baseline, oldest-evicted),EvalControlleraggregates pass-rate per baseline, Spring Boot endpoint exposesGET/POST/DELETE /api/admin/evals/{runs,baselines}, and/atmosphere/admin/evals.htmlsurfaces pass-rate meters + recent-run table with auto-refresh. CI submits a JSON body per LLM-as-judge run and the dashboard surfaces the trend without leaving the control plane. atmosphere-admin-bundleenterprise console aggregator (eaad0df089) — single Mavenpom-packaging artifact that transitively pulls inatmosphere-spring-boot-starter,atmosphere-admin,atmosphere-ai,atmosphere-coordinator,atmosphere-agent,atmosphere-rag,atmosphere-checkpoint,atmosphere-durable-sessions, andatmosphere-durable-sessions-sqlite. Adding one dep gives operators the dashboard, journal flow viewer, workflow authoring, eval dashboard, and governance decision viewer; deliberately does not pin anAgentRuntimeadapter or a vector-store driver so operators choose those independently.docs/runtime-selection.md(97130eeeeb) — nine-runtime decision tree walking the questions an architect should answer before picking anAgentRuntime, cross-referenced against the pinned capability snapshot. Companion to the cli / samples "flagship enterprise templates" promotion that calls outrag,ai-tools,guarded-agent,coding-agent, andms-governanceas the canonical agent shapes.
- AI gap Playwright coverage (
e91b8084fd) —ai-gap-coverage.spec.tsexercises the deterministic RAG, input-assembly telemetry, and evaluator artifact paths throughAiFeatureTestServer; Vue, Svelte, and React Native hook tests pin the new chat-hook parity surface. UsageCapturingChatModelTest,SpringAiAlibabaRuntimeContractTest(TC/TA/TU pinned),WorkflowManifestTest,WorkflowControllerTest,EvalControllerTest,PgVectorContextProviderTest,QdrantContextProviderTest,PineconeContextProviderTest(534317f03d,31d6455a75,81ff454177,38e2a45920).
- capability-matrix snapshot + drift gate (
d22d18a7cd) — new.harness/capabilities.snapshot.jsonis the canonical aggregate of theAiCapabilityenum (20 capabilities) and each runtime's pinnedexpectedCapabilities()(9 runtimes). Regenerated byscripts/regen-capability-snapshot.shand validated by bothscripts/validate-capability-claims.sh(wired intoscripts/pre-push-validate.shTier 1) and the newCapabilitySnapshotTestinmodules/ai-test. The per-runtime contract tests already pin per-runtime drift; this layer pins aggregate count claims inmodules/ai/README.mdagainst the snapshot so prose like "All 9 runtimes…" cannot drift from the running code without breaking the build. Adds a "What capability flags do not claim" disclosure block tomodules/ai/README.md§ Adapter Runtimes covering implementation parity, limit numbers, provider-side guarantees, and production fitness — the four edges callers commonly assume from a capability flag and that the flag does not promise. - drift-log + Stop-hook enforcement (
c685f9588f+4f6a51d3a8) — new.harness/drift-log.mdis the append-only record of every agent claim that diverged from ground truth (code, git history, runtime state). Two enforcement points:scripts/validate-drift-log.shchecks structural hygiene (chronological sections, no future dates, prior-section append-only invariant againstorigin/main) and is wired into pre-push Tier 1;.claude/hooks/check-drift-log.shis a Claude Code Stop hook (registered in.claude/settings.json) that greps the session transcript for high-precision drift-correction patterns and blocks session end if the log was not modified. Together with the capability snapshot, this is the verification rail of the harness pattern documented by Anthropic (Effective harnesses for long-running agents) and OpenAI (Harness engineering), applied to AI prose claims about this repo — measuring change failure rate by agent claim, not utilization. Operator notes live in.harness/README.md.
- AiPipeline input-assembly snapshot stale across guardrails+policies (
f16e9da396) — the per-stage telemetry snapshot ofrequest.systemPrompt()was captured before guardrails (AiGuardrail.GuardrailResult.Modify) and governance policies (PolicyDecision.Transform) had a chance to mutate the request. The runtime later executed the post-mutation request, so the system stage was under- or over-reported. Snapshot moved to after both loops have run, before pipeline-driven augmentations (structured-output schema, confidence cue) which are tracked separately. Regression test inAiPipelineInputAssemblyTest. - BudgetCapturingSession wall-clock callback-lazy enforcement (
f16e9da396) — wall-clock cap was sampled only at session-method boundaries, so a provider that hung silently after dispatch never tripped. Now the deadline is scheduled up-front via a daemonScheduledExecutorService; on trip the decorator both routes the typedAiBudgetExceededExceptionthroughsession.errorand fires a pipeline-suppliedonTriphook bound toruntime.executeWithHandle'shandle::cancel, so the in-flight runtime call is cancelled instead of left dangling. The task is cancelled oncomplete()/complete(summary)/error()so the scheduler thread is freed promptly. Existing wall-clock assertion loosened from>to>=(09b2d2b610) because the scheduled task can fire exactly at the deadline. Regression test (wallClockBudgetTripsWhenRuntimeMakesNoSessionCallbacks) covers the no-callback path. - McpToolSource transport leak on initialize/listTools failure (
f16e9da396) —connect(transport, label)constructed anMcpClientand calledinitialize()+listTools()without a failure cleanup path, leaking the underlying transport (subprocess pipes, sockets, HTTP connection pool) for the JVM lifetime if either threw. Wrapped in try/catch withclient.closeGracefully()on failure (Ownership Invariant #1). Regression test stubs anMcpClientTransportwhoseconnect()returnsMono.errorand assertscloseGracefullywas called. - AgentPassivation responseType ignored on resume (
f16e9da396) —AgentSnapshotpersistedresponseTypeNamebutresume()rebuilt the context frombase.responseType(), so a structured-output agent resumed with a base context that lacked the type lost typed parsing despite the snapshot carrying the field. NewresolveResponseTypehelper resolves the snapshot's class name via the thread-context classloader (with fallback tobase.responseType()plus a one-line WARN if the class is not on the resumer's classpath). Regression test inAgentPassivationTest. - off-by-one in
modules/ai/README.md§ ToolLoopPolicy "the other six runtimes" → "the other seven runtimes". With Built-in and Koog handlingCOMPLETE_WITHOUT_TOOLSnatively and 9 runtimes total, the count of "other" runtimes is 7, not 6. Surfaced by the new capability-matrix verification work.
atmosphere.github.iocapability claims aligned with the snapshot. Cross-validated against.harness/capabilities.snapshot.json; fixeddocs/.../reference/ai.mdTOOL_APPROVAL list (Embabel and SK were wrongly excluded),docs/.../tutorial/26-foundation-primitives.mdgateway-consumer count ("seven of nine" → all nine — every runtime callsadmitThroughGateway), added 5 missing capability rows (BUDGET_ENFORCEMENT,CONFIDENCE_SCORES,PASSIVATION,MODEL_ENUMERATION,MULTI_AGENT_HANDOFF) totutorial/11-ai-adapters.md, and refreshed four pre-Koog/SK/AgentScope/Alibaba narrative enumerations to the current 9-runtime set. Website main:13fe8c4..harness/README.md— operator manual for the harness directory (canonical files, validators, hooks, regen + append protocols).
- predictable-AI primitives — three framework-level capabilities that close gaps Bonér's "Herding LLMs" deck flagged for distributed-system reliability, all declared on every framework runtime so the matrix closes without
@Betashims:BUDGET_ENFORCEMENT(a4fae39464) — newAiBudgetvalue record (max input / output / total tokens, max steps, max wall clock) installed viapipeline.setDefaultBudget(...)or per-requestai.budgetmetadata.BudgetCapturingSessiondecorator slots into the AiPipeline session-decorator stack between metrics and guardrail layers; on breach it routes a typedAiBudgetExceededException extends AiExceptionthroughsession.error(...)and short-circuits subsequentsend/usage/progress/emit/completecalls so the wire protocol's "one terminal frame" invariant holds. Distinct fromorg.atmosphere.ai.budget.StreamingTextBudgetManager(long-running per-tenant cumulative spend); this capability is the per-call death-spiral guard. 13 new unit tests cover every breach reason, default vs. per-request override, and the post-trip swallow. Wall-clock limits trip universally; token / step limits depend onTOKEN_USAGE(every runtime except Spring AI Alibaba honors both).CONFIDENCE_SCORES(a4fae39464) — newAiConfidencerecord withOptionalDouble aggregate,List<TokenLogprob>tokens, andSourceenum (LOGPROBS_NATIVE/MODEL_REPORTED_FIELD/HEURISTIC).StreamingSession.confidence(AiConfidence)default method auto-emitsai.confidence.aggregate/.source/.tokensmetadata mirroring theusage(TokenUsage)convention;DelegatingStreamingSessiongains the matching forwarding override. Universal model-reported path via the newAiConfidenceElicitationplus theConfidenceCapturingSessiondecorator: pipeline appends an opt-in cue to the system prompt, decorator parses the model-emitted{"confidence": 0.x}field on stream completion (same regex shape as the existingConfidenceThresholdGuardrail) and firessession.confidenceahead of the terminal frame. Decorator self-suppresses when a runtime already invokedconfidence(...)directly withLOGPROBS_NATIVE. Skipped when structured-output mode is in play because the schema parser owns the response shape — callers add aconfidencefield to their record schema in that mode. 14 new unit tests cover record validation, the elicitation cue, parser fallbacks, runtime-explicit override, per-request override of the pipeline default, and the structured-output skip.PASSIVATION(a4fae39464) — newAgentSnapshotrecord inmodules/ai(persistable subset ofAgentExecutionContext— message, system prompt, identity columns, history, JSON-clean metadata, response type name, reason, paused-at). NewAgentPassivationstatic helper inmodules/checkpointwithpassivate(runtime, ctx, store, reason): String,resume(runtime, store, id, externalSignal, base, session), andloadSnapshot(store, id).resumemerges the snapshot onto a caller-supplied base context (which carries the runtime references — tools, memory, listeners, retry policy — that don't survive a JVM restart) and re-runsruntime.execute(...); base wins on metadata-key collision so caller-injected refs (e.g. trace context) are not clobbered by stale snapshot values. Helper lives inmodules/checkpointrather than onAgentRuntimeitself becausemodules/ai → modules/checkpointintroduces aai → checkpoint → coordinator → aicycle; the reverse direction is acyclic. Capability flag declared on every runtime — flag advertises "this runtime cooperates withAgentPassivation," honest because every runtime threadscontext.history()through its dispatch path so a resumed call observes the same conversation the paused call saw. 10 new unit tests cover snapshot round-trip, resume flow with external signal, signal-less replay of pending message, missing-checkpoint errors, metadata filtering (non-String values dropped pre-snapshot), metadata merge precedence, unique IDs across passivations, and null-arg rejection.
AiCapabilityenum gains 3 entries — total 20 capabilities (was 17).AbstractAgentRuntimeContractTest.expectedCapabilities()pin updated for all 9 framework runtimes: BuiltIn, Spring AI, LangChain4j, ADK, Embabel, Koog, AgentScope, Spring AI Alibaba, Semantic Kernel.BuiltInAgentRuntimecapability count test bumped from 13 to 16. Capability matrix inmodules/ai/README.mdextended withBE/CS/PSVcolumns plus a "Predictable-AI primitives" section documenting each capability's decorator placement, source enum, and runtime-by-runtime caveats (Spring AI Alibaba'sBUDGET_ENFORCEMENTis wall-clock-only because the runtime does not surfaceTOKEN_USAGE).
- Playwright e2e coverage for the predictable-AI primitives (
4be20c240c) —ai-budget-circuit-breaker.spec.ts,ai-confidence-elicitation.spec.ts,ai-passivation.spec.tsexercise the fullAiPipelinesession-decorator stack through Atmosphere's wire transport. Each spec drives a dedicated test handler (BudgetCircuitBreakerTestHandler/ConfidenceElicitationTestHandler/PassivationTestHandler) registered inAiFeatureTestServerso the harness seesAiBudgetExceededExceptionon the wire's error frame, theai.confidence.aggregate/.source/.tokensmetadata frames theconfidence(...)default sink emits, and the snapshot/resume round-trip across two sequential WebSocket connections.modules/integration-tests/pom.xmlbumpsatmosphere-checkpointfrom test to compile scope soPassivationTestHandlercan callAgentPassivationdirectly.modules/integration-tests/playwright.config.tsregisters the three new spec project entries.
- CLI standalone-scaffold compile against the released parent POM (
7383eb0ee2) — twentysamples/spring-boot-*/pom.xmlfiles now declare<netty.version>4.2.13.Final</netty.version>in their own<properties>block so the sample is self-contained: scaffold-then-compile against the releasedorg.atmosphere:atmosphere-project:4.0.43parent (which predates the netty bump and therefore does not declarenetty.version) no longer fails withNon-resolvable import POM: io.netty:netty-bom:pom:${netty.version}.cli/e2e-test-cli-runtime.shalso fixes a long-standing comment-vs-code drift: the script was exportingATMOSPHERE_CLI_VERSIONwhilecli/atmosphereonly ever readATMOSPHERE_VERSION_OVERRIDE, so the SNAPSHOT-against-SNAPSHOT lane silently degraded to release-pin coverage. Renaming the export aligns the script with the CLI's actual contract.
- per-request runtime extension helpers — small
attach(context, ...)/from(context)helpers (modeled on the existingCacheHint) that let callers stash framework-native composition objects inAgentExecutionContext.metadata(), so a runtime can apply them per-request without growing the unifiedAgentRuntimeSPI with framework-specific knobs. The matrix closes on 4.0.43: all eight framework runtimes have a sidecar —SpringAiAdvisors(Spring AIAdvisorchain — RAG / memory / guardrails / observability),LangChain4jAiServices(LangChain4jAiServices/TokenStream),KoogStrategy(KoogAIAgentGraphStrategyDSL),AdkRootAgent(ADKBaseAgent/SequentialAgent/ParallelAgent/LoopAgenttopology),EmbabelPromptRunner(UnaryOperator<PromptRunner>customizer applied AFTER default wiring),AgentScopeAgent(per-requestReActAgent),SemanticKernelInvocation(per-requestInvocationContext— unlocksKernelHooks,withMaxAutoInvokeAttempts, customPromptExecutionSettings),SpringAiAlibabaRunnableConfig(per-request AlibabaRunnableConfigforthreadId/checkPointId/streamMode/metadata/store), plus the cross-runtimeToolLoopPolicieshonored byBuiltInAgentRuntime's OpenAI-compatible tool loop. Initial five sidecars landed viaf1493c3f9c; the remaining four runtimes plus the lifecycle hook fan-out below landed viaeec98890fe. Also added:AgentLifecycleListener.onModelStart/onModelEnd/onModelErrorhooks withfireXxxfan-out helpers wired in all 8 framework runtimes (was Built-in only), andAiEventForwardingListeneradapter that translates lifecycle hooks to wire-formatAiEvent.Progressframes (opt-in viacontext.withListeners(...)). Each bridge ships with a unit-level*BridgeTestproving the runtime honors the sidecar.
- Quarkus extension closes the
/api/console/infoparity gap — the bundled Atmosphere Console UI gets the samesubtitle / endpoint / runtime / modepayload it gets from the Spring Boot starter, instead of falling through to the Vue defaults on a 404. NewAtmosphereConsoleInfoServlet(HttpServlet, registered at build time via a secondServletBuildItemmapped to/api/console/info) reuses the same package-prefix mode-detection heuristic asAtmosphereConsoleInfoEndpoint(org.atmosphere.{ai,agent,coordinator}.*→"ai", anything else includingManagedAtmosphereHandler→"broadcast"). Endpoint auto-detection prefers the canonical/atmosphere/ai-chatwhen registered (samples likequarkus-ai-chatship multiple@AiEndpoints), then/atmosphere/agent/*, then any other/atmosphere/*. New config keysquarkus.atmosphere.console-subtitleandquarkus.atmosphere.console-endpointmirror the Springatmosphere.console-subtitle/atmosphere.console-endpointproperties. JSON is hand-rolled so the runtime POM stays Jackson-free;AgentRuntimeResolveris reached via reflection so the servlet keeps no compile-time link tomodules/ai. Empirically verified in chrome-devtools againstquarkus-ai-chat—/api/console/infonow returns{"subtitle":"Runtime: langchain4j","endpoint":"/atmosphere/ai-chat","runtime":"langchain4j","mode":"ai"}, the Vue Console shows the runtime label in the header subtitle, and the cross-tab isolation matrix continues to pass on the Quarkus leg. - bundled Atmosphere Console now auto-detects AI vs. broadcast endpoints (
c1e8e36c7b) —/api/console/infoadds amodefield ("ai"for@AiEndpoint/@Agent/@Coordinator,"broadcast"for@ManagedServicechats);AtmosphereConsoleInfoEndpoint#detectModeclassifies via the registered handler's package prefix (org.atmosphere.{ai,agent,coordinator}.*→ ai, everything else includingManagedAtmosphereHandler→ broadcast), so the check stays compile-time independent ofmodules/aiandmodules/agent. The Vue frontend swaps empty-state copy ("Start a conversation" + "AI assistant" → "Start a broadcast" + "every connected client on this endpoint will receive it") and the default subtitle ("Runtime: " → "Multi-client broadcast chat") based on the detected mode. Closes the misleading-UI half of the cross-tab leak follow-up: pre-fix,spring-boot-mcp-serverandspring-boot-otel-chatrendered the AI-assistant copy despite being broadcast-shared by design. Empirically verified in chrome-devtools against both broadcast samples andspring-boot-ai-chat. Per-sampleatmosphere.console-subtitleoverrides still win over the mode-aware default. 5 new contract tests inAtmosphereConsoleInfoEndpointModeTestpin the four classification paths plus the override interaction. - cross-tab isolation matrix extended from 11 → 15 samples (
c1e8e36c7b) — addsquarkus-ai-chat(cross-runtime parity, proves the targeted-dispatch fix from1fbb0958f0survives Quarkus's distinctQuarkusJSR356AsyncSupportpath),spring-boot-checkpoint-agent(@Coordinatorwith analyzer/approver fleet + checkpoint store),spring-boot-ms-governance-chat(@AiEndpointwith@AgentScopeclassification interceptors stacked in front), andspring-boot-channels-chat(omnichannel@AiEndpointwith Telegram/Slack/WhatsApp/Messenger channel-bridge adapters). New fixture entries ine2e/fixtures/sample-server.tsfor the checkpoint and ms-governance samples; quarkus-ai-chat reuses the existing fixture. Out-of-scope docstring corrected to removespring-boot-channels-chat(it's actually an isolated@AiEndpoint, not a broadcast chat) and to explicitly tagspring-boot-a2a-agentas out-of-scope (A2A JSON-RPC has no two-tab Console scenario). All 15 cases pass locally in 3.5m. OpenAiCompatibleClientJavadoc placement broke JDK 26 strict mode (28703ea064) — two stacked Javadoc blocks onforwardResponsesApiUsageraiseddocumentation comment is not attached to any declarationunder-Xlint:all-Werroron JDK 26 (silent on JDK 21). Merged into a single coherent block; CI: Core (JDK 21/26) green.cli/samples.jsonandcli/atmospheretemplate map referenced deletedspring-boot-embabel-chatsample (3a9373e875) —cli/test-cli.shfailed with "samples missing README.md: spring-boot-embabel-chat". Entry removed from samples.json + template-map case statement.
- atmosphere-verifier — plan-and-verify (Meijer "Guardians of the Agents") New module modules/verifier/ + sample samples/spring-boot-guarded-email-agent/ — sealed Workflow AST, ServiceLoader-discovered PlanVerifier chain (Allowlist/WellFormed/Capability/Taint/Automaton/SmtChecker SPI), @Sink + @RequiresCapability scanners, PlanAndVerify orchestrator, WorkflowExecutor with partial-env on failure, verify CLI; sample REST + UI exercises the inbox-exfiltration scenario end-to-end (refused before any tool fires) — 74 unit + 4 boot + 6 Playwright tests, all CI green on the feature branch.
- fail-closed verifier empty-chain, JSON-escape govern. deny, deflake wasync PlanAndVerify.withDefaults + VerifyCli runChain throw / emit chain-empty violations when ServiceLoader yields no providers (P1: silent fail-open under shading / native-image / fat-jar relocation); governance-deny tool result routes every interpolated field through ToolBridgeUtils.escapeJson via a new buildGovernanceDenyJson helper (P2: backslash/newline/control char break); ChatIntegrationTest.socketStatusTransitions polls for status transition rather than asserting in the same instant the OPEN handler fires (release-pipeline timing flake). 5 new verifier tests + 6 governance-JSON tests.
- drop org.json:json — Jackson 3 only (CVE hygiene) RoomProtocolCodec + SimpleRestInterceptor migrated to tools.jackson; brace-balanced reader preserves SwaggerSocket header/body chunk semantics; ALLOW_SINGLE_QUOTES kept for wire compatibility; org.json removed from parent + 3 spring-boot samples.
- bump version to 4.0.41
- prepare for next development iteration 4.0.42-SNAPSHOT
atmosphere-a2aretracked to A2A v1.0.0 (a2aproject/A2A@v1.0.0, released 2026-03-12). The pre-1.0 wire surface was the slash-style method names (message/send,tasks/get, …) and a polymorphicPartenvelope; both are gone in v1.0.0.- JSON-RPC method names switched to PascalCase per spec §9.4 —
SendMessage,SendStreamingMessage,GetTask,ListTasks,CancelTask,SubscribeToTask, the four{Create,Get,List,Delete}TaskPushNotificationConfigoperations, andGetExtendedAgentCard. The pre-1.0 slash names and the oldtasks/pushNotification/*path are aliased to their v1.0.0 equivalents at handler entry, with a one-time WARN per legacy method seen — existing Atmosphere clients keep working through the transition. - HTTP+JSON / REST binding added — colon-verb endpoints
(
POST /tasks/{id}:cancel,POST /tasks/{id}:subscribe,POST /message:send/:stream),pushNotificationConfigsCRUD URLs, andGET /extendedAgentCardare recognized byA2aHandler. REST requests are translated to JSON-RPC envelopes and dispatched through the same handler so the two bindings agree by construction (Mode Parity invariant #7). - Type schema rewrite under
org.atmosphere.a2a.types:Partcollapses three legacy subtypes (TextPart/FilePart/DataPart) into a single record carrying atext | raw | url | dataoneof plus sharedmetadata,filename,mediaType. The deserializer continues to accept the pre-1.0{"type":"text",…}/{"kind":"text",…}envelopes for migration.Message.roleis now theRoleenum (ROLE_USER/ROLE_AGENTper ADR-001 ProtoJSON). Lower-case legacy forms parse for back-compat.TaskStateaddsSUBMITTED(the v1.0.0 ack-before-work state) and emits its proto-JSON name on the wire (TASK_STATE_WORKING, …).Task.messagesis renamed toTask.history;TaskStatusis promoted to a top-level type and carries atimestamp; both update events (TaskStatusUpdateEvent,TaskArtifactUpdateEvent) gaincontextIdandmetadataand drop the redundantfinalflag.AgentCardgainssupportedInterfaces(so an agent can advertise JSON-RPC + HTTP+JSON at distinct URLs), structuredAgentProvider,AgentCardSignature,iconUrl, structuredSecurityScheme/SecurityRequirement, andextendedAgentCardmoves intoAgentCapabilities(wassupportsAuthenticatedExtendedCard). The pre-1.0 top-levelguardrailsfield is no longer modeled — guardrails surface as anAgentExtensiononAgentCapabilities.extensionsunderhttps://atmosphere.async-io.org/extensions/guardrails/v1.- New types added for the missing v1.0.0 surface:
AgentInterface,AgentExtension,AgentSkill(replacesSkill),SecurityRequirement,SecurityScheme+APIKey/HTTPAuth/OAuth2/OpenIdConnect/MutualTls,OAuthFlows+ the three non-deprecated flow shapes,AuthenticationInfo,TaskPushNotificationConfig, and the response wrappersSendMessageResponse,StreamResponse,ListTasksResponse,ListTaskPushNotificationConfigsResponse.
- Pagination + history honored:
GetTaskreadshistoryLength;ListTasksreadspageSize(clamped 1..100, default 50),pageToken, optionalstatusfilter; the response is the v1.0.0ListTasksResponsewithnextPageToken,pageSize,totalSize.SubscribeToTaskreturns-32004UnsupportedOperationErroron a terminal task. - Push-notification methods: handlers route the four CRUD names
but return
-32003PushNotificationNotSupportedError—AgentCapabilities.pushNotificationsis advertised asfalse(Runtime Truth invariant #5; deliveries are not yet wired so the capability flag stays honest). - SSE chunks emitted by
A2aHandler.handleSseStreamingare now spec-compliantStreamResponseenvelopes carrying anartifactUpdateoneof variant (was a custom{"artifact":{…}}shape pre-1.0). - Coordinator
A2aAgentTransportupdated to send/receiveSendMessage/SendMessageResponseand to accept both the v1.0.0 and pre-1.0 task-status shapes when classifying failure replies. modules/agentAgentProcessorupdated to construct the v1.0.0AgentCardand surface guardrails via the extension URI above. Spring BootWellKnownAgentFilterTestfixture rewritten for the new card constructor.
- Tool-call admission seam (
1def61ddf0) —PolicyAdmissionGate.admitToolCallbuilds a syntheticAiRequestwhose metadata carriestool_name,action, and an argument preview so MS-schema rules overtool_namefire before the tool's executor runs.ToolExecutionHelperconsults the gate on every@AiTooldispatch; the canonical MS example{field: tool_name, operator: eq, value: delete_database, action: deny}fires without operator plumbing. OWASP A02 upgraded from PARTIAL to COVERED. @AgentScope.postResponseCheck(2913da1b81) — when enabled on a high-stakes scope,ScopePolicyre-classifies the streamed response text against the declared purpose. OUT_OF_SCOPE responses become Deny with apost-response:prefix; errors fail-open on the response path (bytes already on the wire).POLITE_REDIRECTbreaches downgrade to Deny because Transform can't rewind a stream.- Cross-provider governance contract (
613d216019) —AbstractAgentRuntimeContractTest.policyDenyBlocksRuntimeExecuteis inherited by all seven runtime adapters (Built-in, Spring AI, LangChain4j, ADK, Embabel, Koog, Semantic Kernel); the "deny before runtime" guarantee is now a build-time invariant for each provider. - Per-request
ScopePolicyinstall (334bde4969) — an interceptor can write aScopeConfigunderScopePolicy.REQUEST_SCOPE_METADATA_KEYand the pipeline / streaming session / admission gate install a transientScopePolicyahead of endpoint-level policies for that one turn. Classroom sample uses this for per-room scope (math / code / science / general) — one@AiEndpointhosts four personas, each with its own purpose and forbidden-topic set.perRequestScopeBlocksRuntimeExecuteextends the cross-provider contract to the per-request path. - Admin console governance views — three Vue views under the existing
Atmosphere Console (
/atmosphere/console/) poll/api/admin/governance/{policies,decisions,owasp}on live intervals. Tabs auto-hide when governance is not installed. Verified end-to-end against the classroom sample via chrome-devtools (tabs render, OWASP matrix shows 7 Covered / 1 Partial / 1 Design / 1 Not-addressed, zero console errors). - Persistent
AuditSinkSPI —GovernanceDecisionLog.addSink(AuditSink)fans every admission decision out to registered sinks while keeping the ring buffer authoritative for the admin console. Sink failures are isolated: one unreachable Kafka broker does not take down the pipeline.AsyncAuditSinkwraps a blocking delegate with a bounded drop-on-full queue so the admission thread never blocks on IO (Backpressure invariant #3). Two reference modules ship:atmosphere-ai-audit-kafka(KafkaAuditSink→ JSON to any topic) andatmosphere-ai-audit-postgres(JdbcAuditSink→ JDBC upsert with schema auto-create, works against any JSR-221DataSource; tests exercise H2 in-memory). The JSON shape matches MS Agent Governance Toolkit'saudit_entryso downstream SIEM consumers of either system can read both.
@AgentScopeannotation +ScopeGuardrailSPI (ba7ddf3688) — architectural goal-hijacking prevention. Annotation declarespurpose,forbiddenTopics,onBreach(POLITE_REDIRECT / DENY / CUSTOM_MESSAGE), andtier(RULE_BASED / EMBEDDING_SIMILARITY default / LLM_CLASSIFIER opt-in).ScopePolicymapsScopeGuardrailoutcomes to admit/transform/deny semantics per the breach policy.RuleBasedScopeGuardrailships with built-in hijacking probes for code, medical, legal, and financial patterns so the McDonald's failure mode is caught without operator-declared topic enumeration.- System-prompt hardening + endpoint auto-wiring (
a11239cac3) —AiEndpointProcessorreads@AgentScopeon the endpoint class and auto-installs aScopePolicyahead of user-declared policies;AiPipelineprepends an unbypassable scope-confinement preamble to the system prompt on everyexecute()call, surviving sample-level substitutions. - Sample-hygiene CI lint + 12-sample retrofit (
287a5f9b71) —SampleAgentScopeLintTestwalkssamples/and fails the build on any@AiEndpointmissing@AgentScope(or lacking a non-blankjustificationwhenunrestricted = true). All 12 existing sample endpoints retrofitted — 11 declareunrestricted = truewith specific justifications (production deployments replace with a scoped@AgentScope);ReviewExtractordeclares a real scoped purpose. - Embedding-similarity scope tier (
2c856bd00d) — default tier. Resolves anEmbeddingRuntimevia ServiceLoader; caches the purpose vector (and every forbidden-topic vector) on first use; rejects when cosine similarity falls belowsimilarityThreshold. Absent runtime admits-with-warning so the rule-based tier remains a safe fallback. - LLM-classifier scope tier (
5b8b6f51da) — opt-in tier for high-stakes scopes. Zero-shot YES/NO classifier over the resolvedAgentRuntime; tolerant parser handles**YES**/YES./*no*/ "Not sure" edge cases; ambiguous verdicts fall through to admit so LLM quirks don't over-reject; timeouts and runtime errors fail-closed at theScopePolicylayer. - Governance audit trail (
a534f5e462) —AuditEntryrecords everyGovernancePolicy.evaluatedecision with identity, reason, context snapshot (redaction-safe: truncated message, primitive-only metadata), andevaluation_ms.GovernanceDecisionLogis a thread-safe ring buffer (default 500 entries) installed viaGovernanceDecisionLog.install(capacity). Surfaced viaGET /api/admin/governance/decisions?limit=N.GovernanceTraceremits an OpenTelemetry span per evaluation through reflective classpath detection — OTel stays an optional dependency. - ms-governance-chat feature-parity retrofit (
3fc8ead4cb) — the sample now declares@AgentScope(purpose = "Customer support for Example Corp — orders, billing, ...")and loads 9 MS-schema rules mirroring MS'scustomer-service/main.pyexample (destructive SQL, legal/media/exec escalation, human-request auto-escalate, PII shapes, password disclosure, discount-limit enforcement, plus anaudit-action rule for code-request probing). - OWASP Agentic Top-10 self-assessment matrix (
712c57e4e8) —OwaspAgenticMatrix.MATRIXpins 10 rows (6 COVERED, 2 PARTIAL, 1 DESIGN, 1 NOT_ADDRESSED) with evidence classes, test references, and consumer grep patterns per row.OwaspMatrixPinTestfails the build when any evidence class is renamed or removed — structural answer to the v4 §4 discipline risk. Served over HTTP atGET /api/admin/governance/owaspforagt verify-style compliance consumers.
GovernancePolicySPI (0ace2b6947) — declarative policy identity (name/source/version) plusPolicyContext→PolicyDecisionevaluation. Vocabulary aligned with OPA/Rego and Microsoft Agent Governance Toolkit at the evaluate-decision level (admit/deny/ transform). The SPI is strictly additive — existingAiGuardrailwiring keeps working unchanged.GuardrailAsPolicy+PolicyAsGuardrailadapters (efefaea40a) — every existingAiGuardrailis reachable as aGovernancePolicyviaGuardrailAsPolicy; policies land on the currentAiPipelineadmission seam viaPolicyAsGuardrail.Transformdecisions on the post-response path are downgraded topasswith a warning (streamed text is not retroactively rewritable).- YAML
PolicyParser+PolicyRegistry(83e5c2dafd) — default parser reads YAML via SnakeYAML'sSafeConstructor(no arbitrary class instantiation). Built-in types:pii-redaction,cost-ceiling,output-length-zscore— factories wrap the shipped guardrails with the identity from the YAML entry.modules/aideclares SnakeYAML as an explicit runtime dep so bare-JVM / Jetty-embedded deployments get the parser out-of-the-box. Parser discovered via ServiceLoader; additional formats (Rego, Cedar) plug in by shipping anotherPolicyParserservice entry. - Native
AiPipelinewiring (9ac9ed1d6c) —AiPipelineaccepts aList<GovernancePolicy>on a new constructor, evaluates them in a dedicated pre-admission loop (fail-closed on exception per Correctness Invariant #2), and merges them onto the existingGuardrailCapturingSessionfor post-response viaPolicyAsGuardrail.pipeline.policies()accessor exposes the installed list so admin surfaces can enumerate them without re-parsing YAML. Response cache skips when policies are present so each turn re-evaluates. - Spring + endpoint-processor bridge (
9d7b75be78) —AiEndpointProcessor.instantiatePolicies()merges ServiceLoader andPOLICIES_PROPERTYsources with dedup byname(); policies are wrapped throughPolicyAsGuardrailand threaded onto every@AiEndpointin the app.AtmosphereAiAutoConfigurationnow bridges Spring-managedGovernancePolicybeans onto the same property so the default path "drop a YAML file on the classpath" just works. Parity test (PolicyPlaneSourceParityTest) pins YAML / programmatic / ServiceLoader sources to identical admission decisions. - Classroom sample retrofit (
aaac15f725) —samples/spring-boot-ai-classroom/src/main/resources/atmosphere-policies.yamldeclares a PII redaction and a z-score drift detector;PoliciesConfigreads it at startup and publishes the list toPOLICIES_PROPERTY. Demonstrates the key promise: change YAML, restart, governance changes — zero code edits. - Admin introspection (
b973aa2828) —GovernanceControllerreadsPOLICIES_PROPERTYfor per-policy identity and distinct-source counts;/api/admin/governance/policiesand/api/admin/governance/summaryHTTP endpoints expose the live list;AtmosphereAdmin.overview()reports the policy count alongside the AI runtime name. - Microsoft Agent Governance Toolkit YAML parity —
YamlPolicyParserauto-detects the MS schema (documents with top-levelrules:) and produces aMsAgentOsPolicythat preserves MS's first-match-by-priority rule-evaluation semantic. All nine comparison operators (eq,ne,gt,lt,gte,lte,in,contains,matches) and all four actions (allow,deny,audit,block) are honored. Context map bridgesAiRequestfields (message,model,user_id, …) and every metadata entry to rule field names.MsAgentOsYamlConformanceTestloads MS's own example YAMLs (copied unmodified frommicrosoft/ agent-governance-toolkit@April-2026) and asserts byte-for-byte interop.
AgentStateSPI (a0fd3fc48c) — unifies conversation history, durable facts, daily notes, working memory, and hierarchical rules under one runtime-agnostic interface. File-backed default (FileSystemAgentState) reads and writes an OpenClaw-compatible Markdown workspace.AutoMemoryStrategypluggable with four built-ins (EveryNTurns,LlmDecided,SessionEnd,Hybrid).AgentStateConversationMemoryis a thin shim over the legacyAiConversationMemory.AdkAgentRuntimeseeds its ADKSessionfromcontext.history()(closes Correctness Invariant #5 gap whereCONVERSATION_MEMORYwas advertised but silently dropped).AgentWorkspaceSPI (d4b3e341c7) — agent-as-artifact. ServiceLoader discovery withOpenClawWorkspaceAdapter+AtmosphereNativeWorkspaceAdapter. OpenClaw canonical layout runs on Atmosphere without conversion.ProtocolBridgeSPI (853cccc4aa) —InMemoryProtocolBridgeelevated to first-class bridge, same footing as wire bridges.ProtocolBridgeRegistryenumerates active bridges.AiGatewayfacade (4d48c3eb4a,43870cb537) — single admission point for outbound LLM calls.PerUserRateLimiter, pluggableCredentialResolverandGatewayTraceExporter.BuiltInAgentRuntimenow routes every dispatch throughAiGatewayHolder.get().admit(...)— Correctness Invariant #3 enforced at the runtime boundary.AgentIdentitySPI (f4df5603a7) — per-user identity, permissions, credentials, audit, session sharing.PermissionModelayers over per-tool@RequiresApproval.AtmosphereEncryptedCredentialStoreuses AES-GCM with a 256-bit key and per-entry random IV; decryption failure is fail-closed.ToolExtensibilityPointSPI (59f7ecd197) — bounded tool discovery (ToolIndex+DynamicToolSelector) and pluggable per-user MCP trust (McpTrustProviderwithCredentialStoreBackeddefault).SandboxSPI (818f531216,1e2daa1143) — pluggable isolated execution.DockerSandboxProviderdefault + dev-onlyInProcessSandboxProvider.@SandboxToolannotation. Default limits 1 CPU · 512 MB · 5 min · no network.NetworkPolicyenum (NONE/GIT_ONLY/ALLOWLIST/FULL) replaces the boolean network flag; Docker provider labels containers with the resolved policy.AgentResumeHandle+RunRegistry(2ae4e8835b,27425b15f6) — mid-stream reconnect primitive with boundedRunEventReplayBuffer.StreamingSession.runId()default method returns the id registered withRunRegistry;DurableSessionInterceptorstashes theX-Atmosphere-Run-Idheader in a request attribute so the ai module can reattach without the durable-sessions module depending on atmosphere-ai.- Wire
ProtocolBridgeimplementations (74d3ecbd6e) for MCP, A2A, AG-UI, and gRPC so the admin control plane can answer "which agents are reachable via which protocol?" across every transport.
spring-boot-personal-assistant(2a7ae59a41) — primary coordinator delegates to scheduler / research / drafter crew viaInMemoryProtocolBridge. Ships an OpenClaw-compatible workspace (AGENTS.md/SOUL.md/USER.md/IDENTITY.md/MEMORY.md) plus Atmosphere extension files (CHANNELS.md/MCP.md/PERMISSIONS.md).spring-boot-coding-agent(ae9c2e174c) — clones a repo into a Docker sandbox and reads files.SandboxProviderdiscovered viaServiceLoader; defaults to Docker, falls back to in-process for dev.
ControlAuthorizer.DENY_ALLandREQUIRE_PRINCIPALas explicit admin-plane baselines alongside the existingALLOW_ALL.ALLOW_ALLis documented as non-production; operators wireREQUIRE_PRINCIPALon top of their transport auth (Spring Security, Quarkus security) for production deployments.
BusinessMetadata— standard keys (business.tenant.id,business.customer.id,business.session.revenue,business.event.kind, ...) with anEventKindenum. Published to SLF4J MDC on the dispatching virtual thread and cleared infinallyso Dynatrace / Datadog / OTel log exporters propagate tenant + customer + revenue tags onto the active span for every agent turn.FactResolverSPI +DefaultFactResolver— injects deterministic facts (time, user identity, plan tier, customapp.*keys) into the system prompt before every turn. Resolution order matchesCoordinationJournal/AsyncSupport: framework-property bridge (Spring beans) →ServiceLoader→ process-wide holder → default. Newline / tab / control characters in values are escaped so fact values cannot reshape the instruction context.PiiRedactionGuardrail— regex-based detection of email, phone, credit card, US SSN, IPv4. Redacts on the request path, Blocks on the response path (the SPI cannot rewrite an already-emitted stream, so default-mode log-only signalling was security theatre).OutputLengthZScoreGuardrail— rolling-window drift detector; Blocks responses more than N standard deviations above the window mean. Opt-in viaatmosphere.ai.guardrails.drift.enabled=true.- Agent-to-Agent Flow Viewer —
GET /api/admin/flowandGET /api/admin/flow/{coordinationId}render theCoordinationJournalas a graph (nodes = agents, edges = dispatch count + success / failure / avg-duration). Edge attribution is keyed per-coordinationIdso concurrent tenant runs stay scoped. - Run reattach consumer —
AiEndpointHandlernow reads theX-Atmosphere-Run-Idheader on reconnection, looks up the liveAgentResumeHandleviaRunRegistry, and replays the buffered events onto the new resource. Closes the "producer present, consumer absent" gap in the original primitive wire-in.
PiiRedactionFilter(c2076a41a1) —BroadcasterFilterthat rewrites email / phone / credit-card / US SSN / IPv4 tokens in-flight before bytes reach the client. Atmosphere owns the broadcaster, so rewriting happens on the same thread that would have flushed the unfiltered bytes — a pure orchestration layer can only block a streaming response, never redact it mid-flight. Auto-installs on every present and future broadcaster whenatmosphere.ai.guardrails.pii.enabled=true. Replacement token configurable viaatmosphere.ai.guardrails.pii.replacement(default[REDACTED]). Listener ownership is symmetric — aDisposableBeanremoves the installed listener on shutdown.
CostAccountantSPI +CostCeilingAccountantimpl (1e06de99bb) — bridgesTokenUsageevents from any runtime intoCostCeilingGuardrail.addCost(tenantId, dollars). Installed automatically when both aCostCeilingGuardrailbean and aTokenPricingbean are present on the Spring Boot classpath. Closes the "observability as dashboard" gap: cumulative tenant cost now gates outbound dispatch instead of only surfacing on a Grafana panel.TokenPricingSPI — per-model dollar-per-token schedule. Applications supply their own pricing (provider quotes change); no baked-in table.CostAccountingSessiondecorator — wraps every@Promptsession inAiStreamingSession.dispatchwhenever theCostAccountantHolderis non-NOOP. CapturesTokenUsagefrom every runtime via the existingStreamingSession.usage(...)hook, routes the cost through the accountant, then forwards to the delegate so the runtime call path is unchanged. Tenant MDC is snapshotted at construction so Reactor-thread usage events don't collapse into the__default__bucket.CostCeilingGuardrail(c2076a41a1) — inspects requests before dispatch; blocks outbound@Promptwhen the tenant's cumulative cost is at or above the per-tenant budget. Per-tenant buckets keyed bybusiness.tenant.idMDC; turns without a tenant tag share a__default__bucket.resetTenant(...)/resetAll()on an operator-driven schedule for monthly billing boundaries.
OutputLengthZScoreGuardrailtenant partition (c2076a41a1) — the rolling-window drift detector now partitions its window bybusiness.tenant.id, so a noisy tenant can no longer poison other tenants' baselines. Single-tenant deployments fall back to a shared__default__bucket and behave unchanged.
RunReattachSupport(c2076a41a1) — stateless helper extracted fromAiEndpointHandler.reattachPendingRun. Replay writes the joined buffer directly toresponse.getWriter()(U+001E between events) on reconnection. Unit-tested standalone.RunEventCapturingSessionproducer wire (8156842fd4) — closes the "consumer wired, producer missing" half of the first reattach landing. Mirrors everysession.send/complete/errorinto the run'sRunEventReplayBufferso reconnecting clients have something to replay.- Reattach wire fidelity (
69d5dad403) — replay emitsAiStreamMessageJSON frames matching the live-stream schema (one parser, two paths);AiEndpointHandlerroutes timeout / exception terminals through the capturing session so replayed streams end with a proper error envelope. - P0 reattach ownership + filter-chain replay (
ffedda4b7e) — replay refuses when the reconnecting caller's resolveduserIddoes not match the run's registereduserId(closes a bearer-token cross-user leak). Anonymous runs keep the open-mode carve-out so demo deployments still work. Every replay frame is routed through the broadcaster'sBroadcastFilterchain soPiiRedactionFilterand downstream content filters apply identically to replay and live frames — a direct-writer path previously bypassed them. RunEventCapturingSession.handoff()forwarding (ff8c2d5542) — the defaultStreamingSession.handoffthrowsUnsupportedOperationException; the capturing wrapper inherited it and broke orchestration-primitives handoffs. Now delegates.
atmosphere.admin.http-read-auth-requiredopt-in flag (2d3ee5afc3) — when true,GET/HEAD/OPTIONSon/api/admin/*require the same principal chain as the write-side gate (minusControlAuthorizer). Default off so local demo consoles keep working; multi-tenant operators exposing/api/admin/*on a routable network flip one flag.AdminApiAuthFilter(Spring) +AdminReadAuthFilter(Quarkus JAX-RS@Provider) — symmetric enforcement across starters.
- Fourth principal source on Quarkus admin writes
(
b3d032d00c) —X-Atmosphere-Authheader validated via constant-time compare againstatmosphere.admin.auth.token. A synthetic principal is admitted on match. Intended for sample fixtures and operator tooling that have not yet integrated Jakarta Security; production stacks still resolve viaSecurityContext.getUserPrincipal()first. - Malformed journal timestamp returns 400 (
9aa1651f3f) — previously 200 with an error-item array, which masked client errors and broke Spring / Quarkus API parity. Now matches the SpringAtmosphereAdminEndpointbehavior (Correctness Invariant #4).
AtmosphereFaviconAutoConfiguration(98c6ae408b,2d3ee5afc3) — serves/favicon.icoand/favicon.pngwith the Atmosphere logo PNG on every app using either spring-boot starter, killing the default 404 on the admin UI, console UI, and every sample. Opt out withatmosphere.favicon.enabled=false. Ships as a nested@RestControllerinside the@AutoConfigurationclass; a@Beanfactory introduced in the initial commit produced a duplicateatmosphereFaviconControllerbean and triggeredAmbiguous mappingat startup — removed in the fix.
- SVG coordination-graph visualizer (
d1245d7780) — new admin console tab renders/api/admin/flowas a circle-layout SVG. Nodes are agents, edges carry dispatch count / success / failure / average duration (red on failure, arrowheads for direction). Optionalcoordination-iddrilldown andlookback-minutesfilter. Zero external graph library. Mirrored acrossspring-boot-starterandspring-boot3-starteradmin assets.
RuntimeGatewayAdmissionParityTest(d5f8a03174,81c135cf5c,1632360f7f) — source-level parity scan with brace-balanced method-body extraction; every*AgentRuntime.{java,kt}must calladmitThroughGatewayfrom its designated dispatch methods or the build fails. Catches a regression where a runtime's dispatch path silently bypasses rate limiting and credential policy.- Seven exec-level
*GatewayAdmissionTestfiles —SpringAiGatewayAdmissionTest(48a38d58c6); LangChain4j, ADK, Semantic Kernel (Java) and Koog, Embabel (Kotlin) together (1006a4301d); plus the existingBuiltInExecuteWithHandleGatewayTest. Each installs a countingAiGateway.GatewayTraceExporterand drivesruntime.execute(...)so an admission entry with the correct provider label is captured at the exec level, not just source-level grep. ChangelogClaimsTest(94404ce023) — pins theAgentStateOpenClaw workspace layout andRunEventReplayBufferbound so CHANGELOG-to-code drift breaks the build.
samples/spring-boot-reattach-harness(65f2f6ce5f) —SlowEmitterChatplus aSyntheticRunControllerthat pre-populates theRunRegistryso Playwright can drive theHTTP → reattachPendingRun → replayPendingRunwire with deterministic timing.e2e/tests/reattach.spec.tsruns on every push via a dedicatedfoundation-e2e.ymljob on port 8096 — the reattach contract is proven end-to-end, not just at unit level.
BusinessMdcBenchmark(82899e5145) — JMH harness pinning the cost of the per-turnbusiness.*MDC snapshot → apply → clear cycle thatAiEndpointHandler.invokePromptruns on every dispatch. Baseline, six-key production, and empty-snapshot scenarios, so a regression on the hot path shows as numbers, not intuition.
- Admin HTTP writes now enforce authentication in addition to
the feature flag.
guardWrite(HttpServletRequest, action, target)resolves a Principal from the servletUserPrincipal, the AtmosphereAuthInterceptor-set attribute, or theai.userIdattribute, then consultsControlAuthorizer. The earlier feature-flag-only gate let any anonymous caller mutate state once the flag was flipped. Correctness Invariant #6 (Security). - MCP write tools forward the authenticated principal to
ControlAuthorizer.authorize(...). Previously every tool passednull, soREQUIRE_PRINCIPALpermanently denied andALLOW_ALLpermanently admitted regardless of identity. NewIdentityAwareToolHandlerfunctional interface threads the servlet-resolved principal throughMcpProtocolHandler.executeToolCall. AiGatewayadmission on cancel-capable dispatch paths.BuiltInAgentRuntime.doExecuteWithHandleandKoogAgentRuntime.executeWithHandlenow calladmitThroughGateway— parity with the plainexecutepath so rate limits and credential policies fire on every mode (Correctness Invariant #7 — mode parity).- Business MDC lifecycle. The MDC population was previously done on the servlet thread (wrong thread — VT logs never saw it) and never cleared. Snapshot on the servlet thread, apply on the VT dispatcher with try/finally clear, so every log record during the turn carries the tags and the VT pool starts clean on the next turn.
- Flow graph attribution under interleaved coordinations.
FlowController.buildGraphpreviously carried a flatcurrentCoordinatorcursor — concurrent runs misattributed every second edge. Now maintains acoordinationId → coordinatorNamemap. - User
@AiEndpointpaths get Spring + ServiceLoader guardrails.AiEndpointProcessormerges annotation-declared guardrails withServiceLoader.load(AiGuardrail.class)and the framework-property bridge so annotation-declared endpoints are no longer starved of the auto-wired guardrail set. NewAiGuardrail.GUARDRAILS_PROPERTYmirrors theCoordinationJournalbridge key. - Foundation E2E stops skipping the Docker sandbox regression.
SKIP_SANDBOX_E2E=truepreviously hid the command-injection hardening; removed fromfoundation-e2e.ymlso the clone+read spec runs on every PR. ubuntu-latest ships with Docker — the new workflow also verifies its presence early. - Sample boot modernization.
spring-boot-coding-agentreverted fromapplication.propertiestoapplication.yml; both samples addspring-boot-starter-actuator;foundation-e2e.ymlboots via./mvnw spring-boot:runand waits on/actuator/healthviawait-oninstead of shelling out tocurland pre-building a fat jar.
- Jetty 12.0.33, Tomcat 11.0.21, Kafka 3.9.2 (
8e4b63e18a) — closes 13 Dependabot advisories (1 critical, 5 high, 3 medium). - Bouncy Castle 1.84 pinned in
dependencyManagement(246c29bc75) — closes LDAP-injection and risky-crypto advisories against the 1.82 tree thatdocker-java-core 3.7.0pulls in transitively. Provided-scope only (Docker sandbox path); no runtime fat-jar drift. - protobuf 4.34.1 pinned to match
protoc(4876779978) —grpc-protobuf 1.80.0still pulledprotobuf-java 3.25.8transitively, soprotoc 4.x-generated sources failed to compile. - MCP SDK 1.0.0 → 1.1.1 (
da57094ce6). - React / React DOM 19.2.5 in lockstep (
8226e28fcb) — React requires an exact version match betweenreactandreact-dom; a partial Dependabot bump broke every jsdom-backed test withensureCorrectIsomorphicReactVersion. HtmlEncoderregistered as a CodeQL XSS sanitizer (b5f184e417) — resolves four false-positivejava/xsscode-scanning alerts.
AdminResourcesurvivesIllegalStateException: UT000048(e2d254ad68) — resteasy-reactive dispatches on Vert.x, so@Context HttpServletRequestattribute access throws on the admin write path. Attribute access is swallowed (attributes cannot fire on Vert.x anyway) andX-Atmosphere-Authis read via@Context HttpHeaders, which works on both transports.
GrpcWasyncTransportTeststatus-poll 2s → 5s (1586d91246) — wAsync updatesSocket.status()on its dispatch thread after theCLOSEcallback returns; the 2s polling cap was too tight on JDK 26 where scheduler latency between callback and CAS is observably longer.
FileSystemAgentStatecross-scope bleed (ad850f9f35).MEMORY.mdandmemory/YYYY-MM-DD.mdnow live underusers/<userId>/agents/<agentId>/so facts never bleed across users or agents (Correctness Invariant #6, default deny on cross-scope access). Three new isolation tests cover cross-user, cross-agent, and cross-scope delete boundaries.
atmosphere newis now sample-clone based (b7f98d42f0,0b9a8f194d). The CLI no longer ships a mustache-based scaffold.atmosphere new <name> --template <t>now sparse-clones the matching sample fromcli/samples.jsonand rewrites the clonedpom.xmlso itsorg.atmosphere:atmosphere-projectparent resolves from Maven Central (pins the version from SNAPSHOT to the release incli/samples.json, drops the reactor-relative<relativePath>, disables repo-local checkstyle/pmd bindings). The resulting project compiles standalone with plainmvn compile.- Nine templates in
cli/atmospherecmd_new:chat,ai-chat,ai-tools,mcp-server,rag,agent,koog,multi-agent,classroom. Each maps 1:1 to a sample incli/samples.json;multi-agentandclassroomare new starters exposing the 5-agent A2A fleet and the AI-classroom Spring Boot + Expo RN sample respectively. create-atmosphere-app(npx) rewritten as a thin delegating shim (944b190f43). Drops the old JBang branch and the 240-line inline Java/HTML fallback, resolves the installedatmosphereCLI on PATH, and execsatmosphere new <name> --template <t> [--skill-file <f>]. Prints an actionable install hint if the CLI is missing.TEMPLATESlist synchronized with the shell CLI's nine entries.
generator/AtmosphereInit.java+AtmosphereInitTest.java+generator/templates/handler/**generator/templates/frontend/**+generator/templates/{Application.java,application.yml,pom.xml}.mustachegenerator/test-generator.sh+.github/workflows/generator-ci.yml(b7f98d42f0). The JBang mustache scaffold is fully gone.generator/ComposeGenerator.javaand itsgenerator/templates/compose/**tree remain — they back the parametric skill-file driven multi-module scaffold invoked byatmosphere compose, which has no single-sample equivalent.
cli/atmospherebash fallback tree —create_minimal_project,create_chat_handler,create_ai_chat_handler,create_agent_handler,create_index_html(~430 lines).cmd_newnow always clones; there is no fallback path.--groupflag onatmosphere newandcreate-atmosphere-app. Samples ship with their own groupId; passing--groupprints a deprecation warning and is ignored. Rename the groupId inpom.xmlandsrc/main/javaby hand after scaffolding if needed.
4.0.36 - 2026-04-13
Every bullet in this section is grounded in a real commit on main at the
time of release; commit hashes are listed where the attribution matters.
- Microsoft Semantic Kernel adapter (
atmosphere-semantic-kernel). SeventhAgentRuntimeimplementation backed by Semantic Kernel'sChatCompletionService. Streams via the SK streaming chat API, honors system prompts, threadsAgentExecutionContextinto the SK invocation context, and reports token usage. Tool calling is deferred in 4.0.36 (SK's JavaKernelFunctiontool binding is not yet bridged throughToolExecutionHelper.executeWithApproval).SemanticKernelEmbeddingRuntimeships alongside for embedding support via SK'sTextEmbeddingGenerationService; blocks the reactive response at a 60s ceiling to avoid pinning a virtual thread on a hung service.
ToolApprovalPolicysealed interface (c83469a478). Four permitted implementations:annotated()(default, honors@RequiresApproval),allowAll()(trusted test fixtures),denyAll()(preview / shadow mode, no invocation ever runs), andcustom(Predicate<ToolDefinition>)for runtime-dependent decisions. Attach viaAgentExecutionContext.withApprovalPolicy(...).ExecutionHandlecooperative cancel for in-flight executions viaAgentRuntime.executeWithHandle(context, session). Idempotentcancel(), terminalwhenDone()future,isDone(). Runtime cancel primitives (verified from source):- Built-in:
HttpClientrequest + SSEInputStream.close() - Spring AI:
reactor.core.Disposable.dispose()on the streamingFlux - LangChain4j:
CompletableFuture.completeExceptionally+AtomicBooleansoft-cancel flag consulted in the streaming response handler - Google ADK:
AdkEventAdapter.cancel()→io.reactivex.rxjava3.disposables.Disposable.dispose()on the Runner subscription - JetBrains Koog:
AtomicReference<Job>captured byexecuteInternal→Job.cancel()+ virtual-threadThread.interrupt()fallback + immediatedone.complete(null)backstop - Semantic Kernel, Embabel: no-op sentinel (
ExecutionHandle.completed()) — neither runtime overridesexecuteWithHandle, documented as a known gap.
- Built-in:
AgentLifecycleListener— observability SPI withonStart,onToolCall,onToolResult,onCompletion,onError. Attach viaAgentExecutionContext.withListeners(List<AgentLifecycleListener>).AbstractAgentRuntimefires start / completion / error via protectedfireStart/fireCompletion/fireErrorhelpers, so the five runtimes that extend it (Built-in, Spring AI, LC4j, ADK, SK) get lifecycle events automatically. Tool events fire through the staticfireToolCall/fireToolResultdispatchers used by every tool bridge. Koog and Embabel implementAgentRuntimedirectly and do not yet fire start / completion / error (documented exclusion indocs/reference/lifecycle-listener.md).EmbeddingRuntimeSPI withfloat[] embed(String),List<float[]> embedAll(List<String>),int dimensions(),isAvailable(),name(),priority(). Five implementations ship; service-loader resolution picks the highest-priority available one at runtime:SpringAiEmbeddingRuntime— priority 200LangChain4jEmbeddingRuntime— priority 190SemanticKernelEmbeddingRuntime— priority 180EmbabelEmbeddingRuntime— priority 170BuiltInEmbeddingRuntime— priority 50 (zero-dep OpenAI-compatible fallback)
- Per-request
RetryPolicyonAgentExecutionContext. Record shape:(int maxRetries, Duration initialDelay, Duration maxDelay, double backoffMultiplier, Set<String> retryableErrors). Only the Built-in runtime currently honors the per-request override; framework runtimes inherit their own native retry layers and the capability is Built-in-only in 4.0.36 (per the pinned capability matrix inAbstractAgentRuntimeContractTest.expectedCapabilities()). - Pipeline-level
ResponseCache(3e1fc6e4a7). SHA-256CacheKeyover model, system prompt, message, response type, conversation history, tool names, and content parts (text / image mime+length / audio mime+length / file mime+length+bytes). Session-ID-independent so identical prompts hit the same cache line.CacheHintmetadata on the context selects policy (CONSERVATIVE,AGGRESSIVE,NONE). - Multi-modal
Content— sealedContenttype withText,Image,Audio,Filesubtypes. Wire frames carry base64-encoded payloads with explicitmimeTypeandcontentType. Runtimes that do not support multi-modal input declare the exclusion in theircapabilities()set (Correctness Invariant #5 — Runtime Truth). session.toolCallDelta()+AiCapability.TOOL_CALL_DELTA— incremental tool-argument streaming so clients can render partial JSON as the model generates it. Declared as anAiCapabilityenum value so the distinction is machine-readable on the SPI, not just prose in the matrix. OnlyBuiltInAgentRuntimeadvertises it — itsOpenAiCompatibleClientforwards everydelta.tool_calls[].function.argumentsfragment throughsession.toolCallDelta(id, chunk)on both the chat-completions and responses-API streaming paths. The six framework bridges (Spring AI, LC4j, ADK, Embabel, Koog, Semantic Kernel) cannot emit deltas without bypassing their high-level streaming APIs (895a7e0a2e); they honor the default no-op contract instead. Pinned in the Built-in contract test and inmodules/integration-tests/e2e/ai-tool-call-delta.spec.ts's negative capability assertion.AgentRuntime.models()default method returning the list of models the resolved runtime can actually serve. Replaces the configuration-intent model flag with a runtime-resolved list (Correctness Invariant #5).TokenUsagerecord(long input, long output, long cachedInput, long total, String model). Reported on completion metadata asai.tokens.input/ai.tokens.output/ai.tokens.total/ai.tokens.cachedwhen the provider surfaces it.@AiEndpoint.promptCache()and@AiEndpoint.retry()— declarative annotations on@AiEndpoint.promptCache()returnsCacheHint.CachePolicy(defaultNONE).retry()returns a nested@Retryannotation withmaxRetries,initialDelayMs,maxDelayMs,backoffMultiplier. Resolved at bean post-processing on Spring Boot and via the annotation processor at build time on Quarkus.AbstractAgentRuntimeContractTest— TCK inmodules/ai-testthat everyAgentRuntimesubclass must pass. Exercises text streaming, tool calling, tool approval, system prompt, multi-modal input, cache hint threading, execution cancel, and capability-set pinning viaexpectedCapabilities()(addedc13e309d) so adding or removing a capability from a runtime without updating its pinned set breaks the build. Drift between the code and the docs matrix intutorial/11-ai-adapters.mdcannot ship silently.
@Agent+@Command— one annotation defines the agent; slash commands routed on every wired channel (Web WebSocket plus the five external channels whenatmosphere-channelsis present). Auto-generates@AiEndpoint, A2A Agent Card, MCP tool manifest, and AG-UI event bindings based on classpath detection.atmosphere-agentmodule — annotation processor,CommandRouter,SkillFileParser,AgentHandler.skill.md— markdown files that serve as both the LLM system prompt and agent metadata (## Skills/## Tools/## Channels/## Guardrailssections parsed into Agent Card, MCP manifest, channel validation).- JetBrains Koog adapter (
atmosphere-koog). SixthAgentRuntimebacked by Koog'sAIAgent/chatAgentStrategy()with tool calling viaAtmosphereToolBridgeand cooperative cancel viaAtomicReference<Job>. - Orchestration primitives — agent handoffs
(
session.handoff(target, message)), approval gates (@RequiresApproval), conditional routing in@Fleet, and LLM-as-judge eval assertions (LlmJudge). - Samples:
spring-boot-dentist-agent,spring-boot-orchestration-demo,spring-boot-checkpoint-agent(durable HITL workflow surviving JVM restart viaSqliteCheckpointStore).
Commit hashes listed for every Fixed bullet. If there is no commit, the bullet does not belong here.
ToolApprovalPolicy.DenyAllbypass — P0 security (40d616b6ee).DenyAll.requiresApproval()previously returnedtrueand fell through to the session-scopedApprovalStrategy, so an auto-approve strategy could silently run a tool the caller intended to deny.ToolExecutionHelper.executeWithApprovalnow detectsDenyAllbefore consulting the strategy and returns{"status":"cancelled","message":"Tool execution denied by policy"}immediately. Closes Correctness Invariant #6 (fail-closed default).- Null-strategy approval bypass (
56b1046f6f). Before the DenyAll evaluation fix, a tool annotated@RequiresApprovalrunning under a context with noApprovalStrategywired would execute unguarded. NowToolExecutionHelperfails closed on null strategy; DenyAll is evaluated before the null-strategy branch. The same commit also stoppedCachingStreamingSessionfrom auto-persisting oncomplete()so a cancel-induced clean termination can no longer cache a partial response — the pipeline decides whether to commit afterruntime.executereturns. ToolApprovalPolicynot threaded through the tool-loop (b9b1af4aff). Every runtime bridge previously called the 5-argexecuteWithApprovaloverload which defaulted toToolApprovalPolicy.annotated()—context.approvalPolicy()was never consumed. All five tool-calling bridges (Built-in, Spring AI, LC4j, ADK, Koog) now call the 6-arg form and pass the policy through.ChatCompletionRequestgainedapprovalPolicyas its 13th canonical field, preserved across tool-loop rounds.tryResolvetri-state approval ID resolution (0db97e3276,c3cc904644).ApprovalRegistry.tryResolve(id)returnedtrueonUNKNOWN_ID, which causedAiPipeline/AiStreamingSession/ChannelAiBridgeto swallow stale or cross-session approval messages as if consumed. Callers now use the tri-stateresolve()method and only short-circuit onRESOLVED, lettingUNKNOWN_IDfall through to the normal pipeline.- Koog cancel race (
ae732f8301).KoogAgentRuntime.executeWithHandlepreviously relied on a soft-cancel flag polled at suspension points, so a cancel racing with a slow KoogPromptExecutorstall could leave the virtual thread hanging on a native I/O read. The runtime now captures the active coroutineJobin anAtomicReference, cancels the job, and interrupts the virtual thread as a belt-and-suspenders fallback. The same commit enables ADKContextCacheConfigbootstrap and fixes anEmbeddingRuntimeResolverstartup-order race. - LC4j premature completion + ADK model override + cross-session approval
fallback removal (
d4c11ca76a). LC4j'sdoExecutenow blocks onhandle.whenDone()before returning so the lifecycle completion fires after the tool stream actually finishes. ADK'sbuildRequestRunnernow honorscontext.model()when set instead of falling through to the module-level default.AiEndpointHandlerno longer performs a cross-session approval fallback, preserving session-ownership guarantees. - LC4j post-cancel error suppression + terminal-reason first-writer wins
(
4ca8e983d8). LC4j now dropsonErrorcallbacks that arrive after the caller cancelled (the underlying HTTP may drain an IOException out of band).ExecutionHandle.Settablenow records the first-writer terminal reason so observers can distinguish cancel from post-cancel error.RetryPolicy.isInheritSentinelformalisesDEFAULT-as-inheritance contract. ResponseCacheobservability gap + structured-output / RAG / guardrail cache-skip (28d381d4ff).CacheKeynow hashesresponseTypeso a structured-JSON request cannot replay a plain-text cached answer.AiPipelineskips the cache when context providers, guardrails, or a latently non-empty tool registry are present. The cache-hit path now firesAgentLifecycleListener.onStart+onCompletionso observability and audit traffic see a clean pair on both hit and miss paths.CacheKeyContent.File collision + tool-loop cache skip (13cf557532).CacheKeynow hashesContent.Fileparts (mime + length, later upgraded to full byte hash inc29542f1e6) so two distinct PDFs of identical length cannot collide.AiPipeline.streamTextskips the cache whencontext.tools()is non-empty so text-only replays never silently drop tool round-trips.CachingStreamingSessionbinary-sendContent poisoning (0670e2f8b3). BinaryContent(Image / Audio / File) cannot ride through a text-onlyStringBuilder, so the defaultsendContentthrow would have bypassed the text capture. Override now marks the session as errored so the pipeline's post-execute commit short-circuits — never caching a partial text-only response for a flow that emitted binary output.- Embedding runtime timeout + resolver DCL + cancel-swallow logging
(
c29542f1e6).SemanticKernelEmbeddingRuntime.block()calls inherit a 60s ceiling so a hung service cannot pin a virtual thread forever.EmbeddingRuntimeResolverwraps the slow path in a synchronized block so two early callers do not race duplicate ServiceLoader scans.ExecutionHandle.Settable.cancellogs native-cancel exceptions at TRACE instead of silently swallowing them, and documents the first-writer terminal-reason race explicitly. - Release automation stale-version patterns (
a8516e9d7c). Two gaps inscripts/update-doc-versions.shleftREADME.md"Current release" andcli/sdkman/*.mdpublish.shexamples pointing at the previous release afterrelease-4x.ymlran. Both patterns are now swept on every release. - Cross-repo docs sync on release (
dce1fba280,aadec4e1d8).release-4x.ymlnow fires arepository_dispatchevent atAtmosphere/atmosphere.github.iovia the existingSITE_DISPATCH_TOKENon successful release. A companionsync-version.ymlworkflow in the docs repo runsscripts/update-doc-versions.shand commits the result. Closes the long-standing gap where the docs site lagged the Maven Central release by days.
4.0.11 - 2026-03-11
- WebSocket XSS sanitization bypass. Disabled HTML sanitization for WebSocket transport — HTML-encoding JSON in WebSocket frames broke the AI streaming wire protocol.
- XSS and insecure cookie hardening. Sanitize HTML output in write
methods and set the
Secureflag on cookies over HTTPS.
- Token → Streaming Text rename. All AI module APIs, javadoc,
and the atmosphere.js client now use "streaming text" instead of "token"
to describe LLM output chunks. This affects method names
(
onToken→onStreamingText,totalTokens→totalStreamingTexts), field names, and the wire protocol message type ("token"→"streaming-text"). This is a breaking change for atmosphere.js consumers and customAiStreamBroadcastFilterimplementations. - Javadoc published to GitHub Pages. API docs for
atmosphere-runtimeare now deployed automatically toasync-io.org/apidocs. - Starlight tutorial site. A 20-chapter tutorial book is now available at the project documentation site.
4.0.3 - 2026-02-22
- Room Protocol broadcast bug.
DefaultRoom.broadcast()now wraps messages inRawMessageto bypass@Messagedecoder mangling. Room JSON envelopes (join/leave/message events) are delivered intact to clients. enableHistory()NPE.UUIDBroadcasterCacheis now properly configured before use, preventingNullPointerExceptionwhen room history is enabled.- Native Image build. Spring Boot samples use
process-aotandexecclassifier in thenativeprofile so GraalVM can find the main class.
RawMessageAPI (org.atmosphere.cpr.RawMessage) — first-class public wrapper for pre-encoded messages that bypass@Messagedecoder/encoder pipelines.ManagedAtmosphereHandler.Managedis deprecated in favor ofRawMessage.- Playwright E2E tests for all sample applications (chat, spring-boot-chat, embedded-jetty, quarkus-chat, AI samples, durable-sessions, MCP server).
- Unified parent POM. All samples now inherit from
atmosphere-project, makingmvn versions:setupdate every module in a single command. - Normalized artifact names. All modules use lowercase kebab-case
atmosphere-*naming consistently. - Release workflow hardened. Stale tags are cleaned before tagging, and
git rebasehandles diverged branches during release builds.
4.0.0 - 2026-02-18
Atmosphere 4.0 is a rewrite of the framework for JDK 21+ and Jakarta EE 10. It keeps the annotation-driven programming model and transport abstraction from prior versions, and adds support for virtual threads, AI/LLM streaming, rooms and presence, native image compilation, and frontend framework bindings.
This release succeeds the 2.x/3.x line (last release: 3.1.0 / 2.7.16). The
javax.servlet namespace, Java 8 runtime, and legacy application server
integrations have been removed. Applications migrating from 2.x or 3.x should
consult the Migration Guide.
- JDK 21 minimum requirement. The framework compiles with
--release 21and is tested on JDK 21, 23, and 25 in CI. - Jakarta EE 10 baseline. All Servlet, WebSocket, and CDI APIs use the
jakarta.*namespace. Servlet 6.0, WebSocket 2.1, and CDI 4.0 are the minimum supported versions. - Virtual Thread support.
ExecutorsFactorycreates virtual-thread-per-task executors by default viaExecutors.newVirtualThreadPerTaskExecutor().DefaultBroadcasterand 16 other core classes have been migrated fromsynchronizedblocks toReentrantLockto avoid virtual thread pinning. Virtual threads can be disabled withApplicationConfig.USE_VIRTUAL_THREADS=false. - GraalVM native image support. Both the Spring Boot starter and Quarkus extension include reflection and resource hints for ahead-of-time compilation. Spring Boot requires GraalVM 25+; Quarkus works with GraalVM 21+ or Mandrel.
atmosphere-spring-boot-starter-- Spring Boot 4.0 auto-configuration with annotation scanning, Spring DI bridge (SpringAtmosphereObjectFactory), Actuator health indicator (AtmosphereHealthIndicator), and GraalVM AOT runtime hints (AtmosphereRuntimeHints). Configuration viaatmosphere.*properties inapplication.yml.atmosphere-quarkus-extension(runtime + deployment) -- Quarkus 3.21+ extension with build-time Jandex annotation scanning, Arc CDI integration, customQuarkusJSR356AsyncSupport, and@BuildStep-driven native image registration. Configuration viaquarkus.atmosphere.*properties.atmosphere-ai-- AI/LLM streaming SPI. DefinesStreamingSession,StreamingSessions,AiStreamingAdapter, andAiConfigfor streaming streaming texts from any LLM provider to connected clients. Includes the@AiEndpointannotation for zero-boilerplate AI handlers and the@Promptannotation for marking prompt-handling methods that run on virtual threads automatically.atmosphere-spring-ai-- Spring AI adapter (SpringAiStreamingAdapter) that bridgesChatClientstreaming responses toStreamingSession.atmosphere-langchain4j-- LangChain4j adapter (LangChain4jStreamingAdapter,AtmosphereStreamingResponseHandler) for callback-based LLM streaming.atmosphere-embabel-- Embabel Agent Framework adapter for agentic AI with progress events.atmosphere-mcp-- Model Context Protocol (MCP) server module. Annotation-driven tools (@McpTool), resources (@McpResource), prompts (@McpPrompt), and server declaration (@McpServer). Supports WebSocket transport, Streamable HTTP transport (MCP 2025-03-26 spec), stdio bridge for Claude Desktop, and a programmaticMcpRegistryAPI.atmosphere-kotlin-- Kotlin DSL (atmosphere { ... }builder) and coroutine extensions (broadcastSuspend,writeSuspend) for idiomatic Kotlin integration. Requires Kotlin 2.1+.atmosphere-redis-- Redis clustering broadcaster using Lettuce 6.x for non-blocking pub/sub. Messages broadcast on any node are delivered to clients connected to all other nodes.atmosphere-kafka-- Kafka clustering broadcaster using the Apache Kafka client 3.x. Configurable topic prefix, consumer group, and bootstrap servers.atmosphere-durable-sessions-- Durable session SPI withDurableSessionInterceptor,SessionStoreinterface, and in-memory implementation. Sessions survive server restarts; room memberships, broadcaster subscriptions, and metadata are restored on reconnection.atmosphere-durable-sessions-sqlite-- SQLite-backedSessionStorefor single-node deployments.atmosphere-durable-sessions-redis-- Redis-backedSessionStorefor clustered deployments.atmosphere-integration-tests-- Integration test suite with embedded Jetty and Testcontainers covering WebSocket, SSE, long-polling transports, Redis and Kafka clustering, and MCP protocol compliance.
- Room API (
org.atmosphere.room).RoomManagercreates and manages named rooms backed by dedicatedBroadcasterinstances.Roomsupportsjoin,leave,broadcast, presence tracking viaonPresencecallbacks, and configurable message history replay for late joiners. - Room protocol (
org.atmosphere.room.protocol).RoomProtocolMessageis a sealed interface withJoin,Leave,Broadcast, andDirectrecord subtypes, enabling exhaustive pattern matching in Java 21 switch expressions. @RoomServiceannotation for declarative room handler registration with automaticRoomcreation viaRoomManager.VirtualRoomMemberfor adding LLM agents as room participants.- Room authorization (
RoomAuth,RoomAuthorizer) for controlling room access. RoomProtocolInterceptorfor automatic protocol message parsing and dispatching.
- Micrometer metrics (
AtmosphereMetrics). Registers gauges, counters, and timers on anAtmosphereFrameworkinstance: active connections, active broadcasters, total connections, messages broadcast, broadcast latency, room-level gauges, cache hit/miss/eviction counters, and backpressure drop/disconnect metrics. Requiresmicrometer-coreon the classpath (optional dependency). - OpenTelemetry tracing (
AtmosphereTracing). Interceptor that creates spans for every request lifecycle with attributes:atmosphere.resource.uuid,atmosphere.transport,atmosphere.action,atmosphere.broadcaster,atmosphere.room. Requiresopentelemetry-apion the classpath (optional dependency). - Health check (
AtmosphereHealth). Framework-level health snapshot reporting status, version, active connections, and broadcaster count. Integrated into the Spring Boot Actuator health endpoint viaAtmosphereHealthIndicator. - MDC interceptor (
MDCInterceptor). Setsatmosphere.uuid,atmosphere.transport, andatmosphere.broadcasterin the SLF4J MDC for structured logging.
BackpressureInterceptor-- protects against slow clients with configurable high-water mark (default 1000 pending messages) and overflow policies:drop-oldest,drop-newest, ordisconnect.
- atmosphere.js 5.0 -- TypeScript rewrite with no runtime dependencies. Ships as ESM, CJS, and IIFE bundles.
- Transport fallback -- WebSocket with configurable fallback to SSE, HTTP streaming, or long-polling. Full protocol handler with heartbeat, reconnection, and message tracking.
- React hooks --
useAtmosphere,useRoom,usePresence,useStreamingviaatmosphere.js/react. IncludesAtmosphereProviderfor connection lifecycle management. - Vue composables --
useAtmosphere,useRoom,usePresence,useStreamingviaatmosphere.js/vue. - Svelte stores --
createAtmosphereStore,createRoomStore,createPresenceStore,createStreamingStoreviaatmosphere.js/svelte. - AI streaming client --
subscribeStreamingwithonStreamingText,onProgress,onComplete, andonErrorcallbacks for real-time LLM streaming text display. - Room and presence client API -- join/leave rooms, broadcast within rooms, track online members, and display presence counts.
- Chat UI components -- shared React chat components for sample
applications via
atmosphere.js/chat.
spring-boot-chat-- Spring Boot 4 chat application with React frontend.quarkus-chat-- Quarkus 3.21+ chat application.chat-- Standalone Jetty embedded chat.embedded-jetty-websocket-chat-- Embedded Jetty with WebSocket.grpc-chat-- Standalone gRPC transport chat.spring-boot-ai-chat-- Streaming AI chat via theAgentRuntimeSPI.spring-boot-ai-tools-- Portable@AiTooltool calling across runtimes.spring-boot-ai-classroom-- Multi-room AI with a React Native / Expo client.spring-boot-rag-chat-- RAG chat withContextProvider.spring-boot-mcp-server-- MCP server with annotation-driven tools.spring-boot-durable-sessions-- Durable sessions with SQLite backend.
- Multi-JDK CI -- GitHub Actions matrix testing on JDK 21, 23, and 25.
- Native image CI -- GraalVM native builds for both Spring Boot and Quarkus with smoke tests.
- atmosphere.js CI -- TypeScript build, test, lint, and bundle size verification.
- Samples CI -- Compilation verification for all sample applications including frontend npm builds.
- Unified release workflow (
release-4x.yml) for coordinated Maven Central and npm publishing. - CodeQL analysis for automated security scanning.
- Pre-commit hooks enforcing Apache 2.0 copyright headers and conventional commit message format.
- Checkstyle and PMD enforced in the
validatephase withfailsOnError=true.
- Java 8 minimum raised to Java 21. All source compiled with
--release 21. javax.servletnamespace replaced withjakarta.servletthroughout the codebase.- Jetty 9 support replaced with Jetty 12 (
12.0.16). - Tomcat 8 support replaced with Tomcat 11 (
11.0.18). - SLF4J upgraded from 1.x to 2.0.16; Logback from 1.2.x to 1.5.18.
synchronizedblocks inDefaultBroadcaster,AtmosphereResourceImpl,AsynchronousProcessor, and 13 other core classes replaced withReentrantLockfor virtual thread compatibility.HashMapandArrayListin concurrent contexts replaced withConcurrentHashMapandCopyOnWriteArrayList.ScheduledExecutorServiceremains on platform threads for timed tasks (expected -- virtual threads do not benefit from scheduling).
instanceofchecks replaced with pattern matching throughout the codebase.if/elsechains on enums replaced with switch expressions (JDK 21).- Immutable collection factories (
List.of(),Map.of(),Set.of()) used in place ofCollections.unmodifiable*wrappers. - Lambda expressions replace anonymous inner classes where appropriate.
String.repeat()replaces manual loop concatenation.- Diamond operator applied consistently.
try-with-resourcesapplied to allAutoCloseableusage.varused for local variables where the type is obvious from context.- Records used for room protocol messages (
Join,Leave,Broadcast,Direct), cache entries, and event types. - Sealed interfaces used for
RoomProtocolMessageand related type hierarchies.
- atmosphere.js rewritten from jQuery-based JavaScript to TypeScript with zero runtime dependencies.
- Package renamed to
atmosphere.json npm, version 5.0.0. - Build tooling changed from Grunt/Bower to tsup (esbuild-based bundler) with Vitest for testing.
- Module format changed from AMD/global to ESM + CJS + IIFE triple output.
- Peer dependencies on React 18+, Vue 3.3+, and Svelte 4+ are all optional.
- TestNG retained for core
atmosphere-runtimetests. - JUnit 5 adopted for Spring Boot starter tests (via
spring-boot-starter-test). - JUnit 5 adopted for Quarkus extension tests (via
quarkus-junit5). - Mockito upgraded to 5.21.0 for JDK 25 compatibility (ByteBuddy 1.17.7).
- Integration tests use Testcontainers for Redis and Kafka.
JSR356WebSocketTestexcluded (Mockito cannot mock sealed interfaces on JDK 21+).
AtmosphereFrameworkdecomposed into focused component classes. The former 3,400-line god object is now an orchestrator (~2,260 lines) that delegates to single-responsibility components. The public API is fully preserved -- all existingframework.addAtmosphereHandler(),framework.interceptor(), etc. calls continue to work unchanged. New internal components:BroadcasterSetup-- broadcaster configuration, factory, and lifecycleClasspathScanner-- annotation scanning, handler/WebSocket auto-detectionInterceptorRegistry-- interceptor lifecycle and orderingHandlerRegistry-- handler registration and endpoint mappingWebSocketConfig-- WebSocket protocol and processor configurationFrameworkEventDispatcher-- listener management and lifecycle eventsFrameworkDiagnostics-- startup diagnostics and analytics reporting
AtmosphereHandlerWrapperfields encapsulated. Previously public mutable fields (broadcaster,interceptors,mapping) are now private with accessor methods.- Inner classes promoted to top-level.
AtmosphereHandlerWrapper,MetaServiceAction, andDefaultAtmosphereObjectFactoryare now standalone classes inorg.atmosphere.cpr.
- Legacy Maven repositories (Codehaus, maven.java.net, JBoss Nexus, Sonatype) removed. All dependencies sourced from Maven Central.
- Publishing migrated from legacy OSSRH to the Central Publishing
Portal (
central-publishing-maven-plugin). - CDDL-licensed Jersey utility classes (
UriTemplate,PathTemplate) replaced with Apache 2.0 implementations. - OSGi bundle configuration updated for
jakarta.*imports.
- Java 8, 11, and 17 support. JDK 21 is the minimum.
javax.servletnamespace. All APIs usejakarta.*.- Legacy application server support. GlassFish 3/4, Jetty 6-9, Tomcat 6-8, WebLogic, JBoss AS 7, and Netty-based transports are no longer supported. The framework targets Servlet 6.0+ containers (Jetty 12, Tomcat 11, Undertow via Quarkus).
- Deprecated APIs. Two passes of deprecated code removal
(
cf24377f0,a8e6f2be3) cleaned out dead code paths, unused configuration options, and obsolete utility classes accumulated over the 2.x/3.x lifecycle. - CDDL-licensed code. Jersey-derived
UriTemplateand related classes removed and replaced with Apache 2.0 implementations. - jQuery dependency in atmosphere.js. The client library has zero runtime dependencies.
- Netty, Play Framework, and Vert.x integrations. These have been moved to a legacy section and are no longer maintained.
- Update your JDK. Atmosphere 4.0 requires JDK 21 or later.
- Replace
javax.servletimports withjakarta.servlet. This includesHttpServletRequest,HttpServletResponse,ServletContext, and all related types. - Update your container. Use Jetty 12+, Tomcat 11+, or deploy via Spring Boot 4.0+ / Quarkus 3.21+.
- Review synchronized code. If you extended core Atmosphere classes
that used
synchronized, your subclasses may need correspondingReentrantLockupdates. - Check deprecated API usage. Methods and classes deprecated in 2.x and 3.x have been removed. Consult the Javadoc for replacements.
- Remove jQuery. atmosphere.js 5.0 has no jQuery dependency.
- Update imports. The package is now
atmosphere.json npm. Useimport { atmosphere } from 'atmosphere.js'. - Review transport configuration. The new client supports the same transports (WebSocket, SSE, long-polling, streaming) but the configuration API has been streamlined.
| Module | GroupId | ArtifactId | Version |
|---|---|---|---|
| Core runtime | org.atmosphere |
atmosphere-runtime |
4.0.0 |
| Spring Boot starter | org.atmosphere |
atmosphere-spring-boot-starter |
4.0.0 |
| Quarkus extension | org.atmosphere |
atmosphere-quarkus-extension |
4.0.0 |
| AI streaming SPI | org.atmosphere |
atmosphere-ai |
4.0.0 |
| Spring AI adapter | org.atmosphere |
atmosphere-spring-ai |
4.0.0 |
| LangChain4j adapter | org.atmosphere |
atmosphere-langchain4j |
4.0.0 |
| Embabel adapter | org.atmosphere |
atmosphere-embabel |
not yet published (pending Embabel Maven Central release) |
| MCP server | org.atmosphere |
atmosphere-mcp |
4.0.0 |
| Kotlin DSL | org.atmosphere |
atmosphere-kotlin |
4.0.0 |
| Redis clustering | org.atmosphere |
atmosphere-redis |
4.0.0 |
| Kafka clustering | org.atmosphere |
atmosphere-kafka |
4.0.0 |
| Durable sessions | org.atmosphere |
atmosphere-durable-sessions |
4.0.0 |
| Durable sessions (SQLite) | org.atmosphere |
atmosphere-durable-sessions-sqlite |
4.0.0 |
| Durable sessions (Redis) | org.atmosphere |
atmosphere-durable-sessions-redis |
4.0.0 |
| TypeScript client | atmosphere.js (npm) |
atmosphere.js |
5.0.0 |
| Dependency | Minimum Version | Tested Up To |
|---|---|---|
| JDK | 21 | 25 |
| Servlet API | 6.0 (Jakarta EE 10) | 6.1 |
| Spring Boot | 4.0.5 | 4.0.5 |
| Spring Framework | 6.2.8 | 6.2.8 |
| Quarkus | 3.21 | 3.31.3 |
| Jetty | 12.0 | 12.0.16 |
| Tomcat | 11.0 | 11.0.18 |
| Kotlin | 2.1 | 2.1+ |
| GraalVM (Spring Boot) | 25 | 25 |
| GraalVM / Mandrel (Quarkus) | 21 | 25 |
For changes in the 2.x and 3.x release lines, see the
GitHub Releases page
and the atmosphere-2.6.x branch.