Skip to content

Commit a3ad93e

Browse files
Skobeltsynclaude
andcommitted
release(v0.5.0): streaming runtime + MCP-as-skills
v0.5.0 is the platform release. v0.4.x established what an agent IS (typed boundaries, KSP, reflect-optional); v0.5.0 establishes what an agent does to the outside world: it streams, and it speaks MCP fluently in both directions. Streaming runtime: - agent.session(input) → cold Flow<AgentEvent<OUT>> + await() - Eight typed event subtypes (Token, ToolCall*, Skill*, Completed, Failed) - Every event carries agentId for composed-stream demultiplex - Native wire-level streaming on all three adapters (Ollama NDJSON, Anthropic SSE, OpenAI SSE) - Cumulative TokenUsage on SkillCompleted/Completed Composition session surface: - Pipeline (2 and 3+ stages), wrap, Branch, Loop, Parallel, Forum, Swarm - Each operator preserves agentId provenance through inner events - Sequential operators run nodes to completion between transitions; Parallel/Forum interleave events by arrival order MCP-as-skills unification (the conceptual point of v0.5.0): - mcp.toolSkills() — every MCP tool as a Skill - mcp.promptSkills() — every prompt template as a Skill (new RPC) - mcp.resourceSkills() — every resource as a Skill (new RPC) - McpServer.from() gains prompt() and resource() DSLs - McpClient.snapshot: McpServerInfo with full capability matrix Self-contained test infrastructure: - Loopback MCP fixture: agent → McpServer → McpClient in-JVM - Three pre-existing MCP_REDMINE_URL-gated tests converted - ./gradlew testAll: unit + KSP + no-reflect + live-llm + live-mcp - 1074+ unit tests, 0 failures; 7 MCP tests, 0 skipped Drop-in for v0.4.6 consumers — every existing API unchanged. Streaming and MCP-as-skills are additive opt-in surfaces. Premortem at docs/premortem-0.5.0-streaming.md was written before any code shipped; every success criterion in it is ticked at release time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d74bf9e commit a3ad93e

5 files changed

Lines changed: 318 additions & 68 deletions

File tree

CHANGELOG.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,104 @@
22

33
All notable changes to Agents.KT are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-1.0, minor bumps may add new public API; existing API surface is preserved.
44

5+
## [0.5.0] — 2026-05-16
6+
7+
The platform release. Streaming runtime end-to-end, MCP-as-skills unification, every composition operator surfacing typed event flows. v0.4.x was about correctness (typed boundaries, KSP, reflect-optional); v0.5.0 is about visibility — what's happening inside an agent's loop and across the wire is now first-class.
8+
9+
### Added
10+
11+
#### Streaming runtime
12+
- **`agent.session(input): AgentSession<OUT>`** — primary entry point for observing agent execution. Returns a cold `Flow<AgentEvent<OUT>>` of typed events plus a `suspend fun await(): OUT` terminal. Each call starts a fresh invocation; sharing across collectors is via `events.shareIn(...)`. Defined in `agents_engine.runtime.events`. Backward compat preserved — existing `agent.invoke(input)` and `agent.invokeSuspend(input)` go through the same internal path with a no-op emitter, byte-for-byte unchanged behavior.
13+
- **`AgentEvent<OUT>` sealed hierarchy** — eight subtypes covering the full lifecycle: `Token(skillName, text)`, `ToolCallStarted(callId, toolName)`, `ToolCallArgumentsDelta(callId, deltaJson)`, `ToolCallFinished(callId, toolName, arguments, result, isError)`, `SkillStarted(skillName)`, `SkillCompleted(skillName, tokensUsed)`, `Completed<OUT>(output, tokensUsed)`, `Failed(cause)`. Every event carries `agentId` so consumers can demultiplex composed streams. Only `Completed<OUT>` is parameterized on the typed output; the rest are `AgentEvent<Nothing>` and flow through any `AgentSession<OUT>`.
14+
- **`ModelClient.chatStream(messages): Flow<LlmChunk>`** as a default-implementing sibling of `chat`. Non-streaming providers keep working unchanged; the default wraps `chat()` and emits a chunk-equivalent sequence.
15+
- **`LlmChunk` sealed type** — provider-level chunks: `TextDelta`, `ToolCallStarted`, `ToolCallArgumentsDelta`, `ToolCallFinished`, `End(tokenUsage)`. Sits between adapters and `chatOrStream`, keeping provider quirks from leaking into `AgentEvent`.
16+
- **Cumulative `TokenUsage` on `SkillCompleted` and `Completed`** — summed across every LLM turn of one skill invocation (prompt and completion tokens summed independently). Null for `implementedBy` skills (no LLM round-trip).
17+
18+
#### Native streaming adapters
19+
Three adapters override the default `chatStream` with real wire-level streaming:
20+
- **Ollama (NDJSON)**`POST /api/chat` with `stream: true`. Line-by-line parser; tool calls land in the final chunk (Ollama limitation), emitted as the canonical `ToolCallStarted` / `ArgumentsDelta` / `ToolCallFinished` triple. Live integration: ~19 chunks per response, measurable timing gap between first and last.
21+
- **Anthropic SSE**`POST /v1/messages` with `stream: true`. Indexed content-block aware: tracks `Map<Int, BlockState>` so interleaved `content_block_delta` events for text + tool_use can be routed to the right block. `tool_use` blocks carry the canonical Anthropic `toolu_*` id; we use it verbatim as `LlmChunk.ToolCallStarted.callId` (the case `ToolCall.callId` was designed for). Live integration verified against `claude-haiku-4-5-20251001`.
22+
- **OpenAI SSE**`POST /v1/chat/completions` with `stream: true` + `stream_options.include_usage: true`. Per-index tool-call state (id from first delta, args accumulated across deltas). Terminator: `data: [DONE]`. Live integration verified against `gpt-4o-mini`.
23+
24+
Cancellation contract verified by regression-guard tests on all three adapters: Kotlin Flow's channel-backed `emit` propagates collector cancellation back through `useLines` + `.use { stream }`, closing the underlying InputStream before the next blocking read.
25+
26+
#### Composition session support
27+
Every composition operator now exposes a `.session(input)` entry point. Inner events from each contained agent flow with their own `agentId`s; the operator emits a single terminal `Completed`/`Failed`:
28+
29+
- **`Pipeline.session(input)`** (#1745, #1746) — sequential composition. Each stage runs to completion (streaming its tokens), then the next starts with the typed `MID` value. Three-stage chains (`a then b then c`) emit events from all three.
30+
- **`wrap` (`teacher wrap student`)** (#1747) — teacher streams; its output becomes the student's prompt override; student streams. Consolidated `invokeSuspendForSession` to take an optional `promptOverride`, collapsing two near-identical entry points.
31+
- **`Branch.session(input)`** (#1748) — source agent streams, matched route streams. `BranchRoute` gains `sessionExecutor` and `routedAgentName` so terminal `Completed.agentId` points at the agent that actually produced the output.
32+
- **`Loop.session(input)`** (#1749) — bracket events emitted per iteration; same `agentId` repeated each iteration.
33+
- **`Parallel.session(input)`** (#1750) — branches run concurrently on `Dispatchers.Default`; their events interleave by arrival order in the shared Flow, demultiplexable by `agentId`. Terminal `Completed.agentId = "parallel"`.
34+
- **`Forum.session(input)`** (#1751) — participants stream concurrently, captain streams sequentially after. Preserves the `ForumReturnException` short-circuit.
35+
- **`Swarm.absorb(sibling)`** (#1752) — absorbed siblings stream their inner events into the captain's session, between the captain's own `ToolCallStarted` and `ToolCallFinished` brackets. `ToolDef` gains an optional `sessionExecutor` channel that any future sub-agent-wrapping tool can use.
36+
37+
#### MCP-as-skills unification
38+
The conceptual point of v0.5.0: an MCP capability and an agent `Skill` share the same shape (named, described, typed unit of work). All three MCP capability surfaces now expose as `Skill<Map<String, Any?>, String>`:
39+
40+
- **`mcp.toolSkills()`** (#1795) — every MCP-exposed tool wrapped as a Skill whose `implementedBy` invokes `mcp.call(toolName, args)`. Sits alongside the existing `mcp.toolDefs()` (tools as auxiliary functions a skill calls); consumers pick the shape that matches their agent design.
41+
- **`mcp.promptSkills()`** (#1796) — every server-side prompt template wrapped as a Skill whose `implementedBy` invokes `mcp.getPrompt(name, args)`. New `McpClient.listPrompts()` and `McpClient.getPrompt(name, args)` methods.
42+
- **`mcp.resourceSkills()`** (#1810) — every URI-addressable resource wrapped as a Skill whose `implementedBy` invokes `mcp.readResource(uri)`. Skill args are ignored — the URI is captured in the skill's closure. New `McpClient.listResources()` and `McpClient.readResource(uri)` methods.
43+
44+
`McpServer` gains DSLs for the server side:
45+
```kotlin
46+
McpServer.from(agent) {
47+
port = 0
48+
expose("skill-name") // tool (existing)
49+
prompt("greet", "Greeting template") { args -> "Hello ${args["name"]}" } // new
50+
resource("policy:///precision.md", "precision-policy",
51+
description = "...", mimeType = "text/markdown") { // new
52+
"Be precise. Cite sources."
53+
}
54+
}
55+
```
56+
Handlers added for `prompts/list`, `prompts/get`, `resources/list`, `resources/read`. Initialize capabilities now declare prompts and resources when registered.
57+
58+
- **`McpClient.snapshot: McpServerInfo`** (#1734) — immutable view of the connected server's full surface (identity, capabilities matrix, tools, prompts, resources, resource templates). Populated after `handshake()` + `loadTools()`.
59+
60+
#### Test infrastructure
61+
- **Loopback MCP fixture (`LoopbackMcpAlgebraTest`, #1754)** — agent → `McpServer.from(...)``McpClient.connect(server.url)` → tool invocation, all in-JVM. Round-trip verified by computing `sqrt(π/e)` (digits-as-arrays + BigInteger) and checking the result with both a Math.sqrt sanity floor and a BigDecimal square-back proving `result² ≈ π/e` to 20 decimal places.
62+
- **Three pre-existing MCP tests converted to loopback** (#1794) — no more `MCP_REDMINE_URL` requirement. `./gradlew mcpIntegrationTest` runs fully out of the box.
63+
- **`./gradlew testAll`** task (#1720) aggregates unit + KSP + no-reflect smoke + live-llm integration + live-mcp integration into one command for pre-push verification.
64+
- **`docs/streaming.md`** (#1744) — consumer guide for the session API, native streaming status, cancellation contract, test coverage map, composition note.
65+
- **`docs/premortem-0.5.0-streaming.md`** (#1721) — design-before-code premortem listing the typed event hierarchy, cancellation contract, composition fidelity matrix, success criteria. Every claim in this release notes points at a criterion this premortem listed.
66+
67+
### Roadmap updates
68+
- **Sandboxed tool execution** refined in `docs/roadmap.md` Phase 3 with concrete backends: `ProcessSandbox` (Seatbelt on macOS, bwrap on Linux), `WasmSandbox` (Chicory pure-Java), `DockerSandbox` (docker-java extras module). Scoped to subprocess-shaped tools only — `grants { }` covers in-process lambdas.
69+
- **Multimodal I/O** added — image/audio input (Phase 2) via `LlmContent` sealed-block evolution of `LlmMessage`; image generation (`ImageModelClient`) and TTS (`TTSModelClient`) in Phase 3.
70+
- **HTTP `sendAsync` migration** documented as the cancellation latency optimization deferred past v0.5.0 — correctness already holds via Flow semantics (verified by adapter regression-guard tests); `sendAsync` would tighten mid-line cancellation but is not blocking.
71+
72+
### Migration notes
73+
v0.5.0 is **drop-in for v0.4.6** consumers. Every existing API still works:
74+
- `agent.invoke(input)` and `agent.invokeSuspend(input)` unchanged.
75+
- `agent.observe { PipelineEvent -> ... }` unchanged (the v0.4.x event surface for post-hoc skill/tool/error observability).
76+
- `model { ollama / claude / openai }` adapters unchanged; `chatStream` is a default-impl addition.
77+
78+
To opt into streaming:
79+
```kotlin
80+
val session = myAgent.session(input)
81+
session.events.collect { event -> /* render Token, log ToolCall*, ... */ }
82+
val output: OUT = session.await() // typed terminal
83+
```
84+
85+
To consume an MCP server via the unified surface:
86+
```kotlin
87+
val mcp = McpClient.connect(url)
88+
val agent = agent<Map<String, Any?>, String>("wrapper") {
89+
skills {
90+
mcp.toolSkills().forEach { +it }
91+
mcp.promptSkills().forEach { +it }
92+
mcp.resourceSkills().forEach { +it }
93+
}
94+
}
95+
```
96+
97+
### Stats
98+
- **1,074+ unit tests** across root + KSP + no-reflect smoke subprojects — 0 failures
99+
- **54 live-LLM integration tests** — green on clean runs against `gpt-oss:120b-cloud`, `claude-haiku-4-5-20251001`, `gpt-4o-mini`
100+
- **7 live-MCP integration tests** — fully self-contained loopback coverage, no external infrastructure
101+
- v0.4.6 → v0.5.0: ~30 commits, ~25 new test files
102+
5103
## [0.4.6] — 2026-05-15
6104

7105
Follow-up to v0.4.5's open thread: actually make `kotlin-reflect` optional at runtime, and ship the smoke test that proves it. The premortem (`docs/premortem-0.4.6.md`) defined the success criteria; this release meets them.

PUBLISHING.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ Escape newlines as `\n` for the property file.
5757
./gradlew publishMavenCentralPublicationToMavenLocal
5858
```
5959

60-
Artifacts land in `~/.m2/repository/ai/deep-code/agents-kt/0.4.6/`.
60+
Artifacts land in `~/.m2/repository/ai/deep-code/agents-kt/0.5.0/`.
6161

6262
### 2. Generate checksums and create bundle
6363

6464
```bash
65-
SRC=~/.m2/repository/ai/deep-code/agents-kt/0.4.6
66-
DEST=build/bundle/ai/deep-code/agents-kt/0.4.6
65+
SRC=~/.m2/repository/ai/deep-code/agents-kt/0.5.0
66+
DEST=build/bundle/ai/deep-code/agents-kt/0.5.0
6767
mkdir -p "$DEST"
6868

6969
for f in "$SRC"/agents-kt-*; do
@@ -78,17 +78,17 @@ done
7878

7979
```bash
8080
cd build/bundle
81-
zip -r ../agents-kt-0.4.6-bundle.zip ai/
81+
zip -r ../agents-kt-0.5.0-bundle.zip ai/
8282
```
8383

84-
The ZIP must contain the full path: `ai/deep-code/agents-kt/0.4.6/...`
84+
The ZIP must contain the full path: `ai/deep-code/agents-kt/0.5.0/...`
8585

8686
## Upload to Central Portal
8787

8888
1. Go to [central.sonatype.com](https://central.sonatype.com)**Deployments****Publish Component**
89-
2. **Deployment Name:** `ai.deep-code:agents-kt:0.4.6`
89+
2. **Deployment Name:** `ai.deep-code:agents-kt:0.5.0`
9090
3. **Description:** `Typed Kotlin DSL framework for AI agent systems`
91-
4. Upload `build/agents-kt-0.4.6-bundle.zip`
91+
4. Upload `build/agents-kt-0.5.0-bundle.zip`
9292
5. Wait for validation to pass
9393
6. Click **Publish**
9494

@@ -99,27 +99,27 @@ Propagation to Maven Central search takes 10-30 minutes after publishing.
9999
Each artifact needs: the file itself, `.asc` (GPG signature), `.md5`, and `.sha1`.
100100

101101
```
102-
ai/deep-code/agents-kt/0.4.6/
103-
agents-kt-0.4.6.jar
104-
agents-kt-0.4.6.jar.asc
105-
agents-kt-0.4.6.jar.md5
106-
agents-kt-0.4.6.jar.sha1
107-
agents-kt-0.4.6-sources.jar
108-
agents-kt-0.4.6-sources.jar.asc
109-
agents-kt-0.4.6-sources.jar.md5
110-
agents-kt-0.4.6-sources.jar.sha1
111-
agents-kt-0.4.6-javadoc.jar
112-
agents-kt-0.4.6-javadoc.jar.asc
113-
agents-kt-0.4.6-javadoc.jar.md5
114-
agents-kt-0.4.6-javadoc.jar.sha1
115-
agents-kt-0.4.6.pom
116-
agents-kt-0.4.6.pom.asc
117-
agents-kt-0.4.6.pom.md5
118-
agents-kt-0.4.6.pom.sha1
119-
agents-kt-0.4.6.module
120-
agents-kt-0.4.6.module.asc
121-
agents-kt-0.4.6.module.md5
122-
agents-kt-0.4.6.module.sha1
102+
ai/deep-code/agents-kt/0.5.0/
103+
agents-kt-0.5.0.jar
104+
agents-kt-0.5.0.jar.asc
105+
agents-kt-0.5.0.jar.md5
106+
agents-kt-0.5.0.jar.sha1
107+
agents-kt-0.5.0-sources.jar
108+
agents-kt-0.5.0-sources.jar.asc
109+
agents-kt-0.5.0-sources.jar.md5
110+
agents-kt-0.5.0-sources.jar.sha1
111+
agents-kt-0.5.0-javadoc.jar
112+
agents-kt-0.5.0-javadoc.jar.asc
113+
agents-kt-0.5.0-javadoc.jar.md5
114+
agents-kt-0.5.0-javadoc.jar.sha1
115+
agents-kt-0.5.0.pom
116+
agents-kt-0.5.0.pom.asc
117+
agents-kt-0.5.0.pom.md5
118+
agents-kt-0.5.0.pom.sha1
119+
agents-kt-0.5.0.module
120+
agents-kt-0.5.0.module.asc
121+
agents-kt-0.5.0.module.md5
122+
agents-kt-0.5.0.module.sha1
123123
```
124124

125125
## Version Bump

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ Topical guides:
190190

191191
## Current Release
192192

193-
`main` is currently `0.4.6`. The 0.4 line covers Anthropic and OpenAI adapters alongside Ollama, LiveRunner prechecks, `OllamaPreflight`, typed tool references, `Swarm` / `absorb`, supply-chain hardening for the BouncyCastle advisory cleanup, the `wrap` operator (`teacher wrap student`), and the KSP processor that generates compile-time schema, LLM-description and `constructFromMap` constants for `@Generable` types. As of v0.4.6, `kotlin-reflect` is genuinely `compileOnly` — every `kotlin.reflect.full.*` callsite is wrapped or routed through the KSP cache, and an `agents-kt-no-reflect-test` smoke subproject pins the contract by excluding `kotlin-reflect` from its own classpath.
193+
`main` is currently `0.5.0` — the platform release. **Streaming runtime**: `agent.session(input).events: Flow<AgentEvent<OUT>>` surfaces typed `Token` / `ToolCall*` / bracket events as the agentic loop runs. All three adapters (Ollama NDJSON, Anthropic SSE, OpenAI SSE) stream natively at the wire. Every composition operator (`then` / `wrap` / `Branch` / `Loop` / `Parallel` / `Forum` / `Swarm`) surfaces sessions with `agentId`-tagged inner events. **MCP-as-skills unification**: `mcp.toolSkills()` + `mcp.promptSkills()` + `mcp.resourceSkills()` — every MCP capability shape exposes as a `Skill` consumable in `skills { +... }`. `McpServer` gains DSLs to register prompts and resources alongside agents-as-tools, plus `McpServerInfo` snapshot for the full capability matrix. The 0.4 line (kotlin-reflect compileOnly, KSP @Generable, BouncyCastle hardening, wrap operator, three providers) is included.
194194

195195
Use Maven Central for published artifacts and tags for immutable release points.
196196

@@ -203,7 +203,7 @@ Use Maven Central for published artifacts and tags for immutable release points.
203203
```kotlin
204204
// build.gradle.kts
205205
dependencies {
206-
implementation("ai.deep-code:agents-kt:0.4.6")
206+
implementation("ai.deep-code:agents-kt:0.5.0")
207207
}
208208
```
209209

0 commit comments

Comments
 (0)