|
1 | | -# Agents.KT v0.4.3 — Complete BC Pin Across Both Modules |
| 1 | +# Agents.KT v0.4.4 — KSP, `wrap`, and reflect-free runtime |
2 | 2 |
|
3 | | -**Release date:** 2026-05-12 |
| 3 | +**Release date:** 2026-05-13 |
4 | 4 |
|
5 | | -Functionally identical to v0.4.2. Consumer-visible POMs and `runtimeClasspath` are byte-for-byte the same. The only change is finishing what v0.4.2 started — pinning BouncyCastle 1.84 in the `:agents-kt-ksp` subproject too, so all four dependabot advisories can finally clear. |
| 5 | +First Maven Central release after **v0.4.2**. The internal `v0.4.3` tag existed on GitHub but never reached Maven Central — its content (BC pin completeness across both Gradle modules) is folded into 0.4.4 alongside the KSP arc, the `wrap` operator, and a reflect-free runtime. |
6 | 6 |
|
7 | | -## What changed since v0.4.2 |
| 7 | +```kotlin |
| 8 | +implementation("ai.deep-code:agents-kt:0.4.4") |
| 9 | +``` |
| 10 | + |
| 11 | +Same artifact coordinates as before. Drop-in for 0.4.2 — every new public API has defaults; existing 0.4.x code compiles unchanged. |
| 12 | + |
| 13 | +--- |
8 | 14 |
|
9 | | -v0.4.2 added explicit `compileOnly("org.bouncycastle:*:1.84")` declarations + a `force(...)` block to the **root** `build.gradle.kts`, giving Dependabot visible 1.84 nodes in the root project's submitted graph. |
| 15 | +## What's new since v0.4.2 |
10 | 16 |
|
11 | | -But the **`:agents-kt-ksp` subproject** never got the same treatment. Its `kotlinBouncyCastleConfiguration` kept resolving to BC 1.80 transitively (Kotlin Gradle plugin pulls it for jar signing), and Dependabot's per-subproject graph submission kept showing 1.80, keeping the four advisories alive. |
| 17 | +### The KSP arc — `@Generable` is now compile-time |
12 | 18 |
|
13 | | -v0.4.3 mirrors the v0.4.2 fix into `agents-kt-ksp/build.gradle.kts`: |
| 19 | +A six-step progression replaces every reflection walk for `@Generable` types with KSP-generated companion objects. Apply `:agents-kt-ksp` in your build and: |
14 | 20 |
|
15 | 21 | ```kotlin |
16 | | -// agents-kt-ksp/build.gradle.kts |
17 | | -configurations.all { |
18 | | - resolutionStrategy { |
19 | | - force( |
20 | | - "org.bouncycastle:bcprov-jdk18on:1.84", |
21 | | - "org.bouncycastle:bcpg-jdk18on:1.84", |
22 | | - "org.bouncycastle:bcpkix-jdk18on:1.84", |
23 | | - "org.bouncycastle:bcutil-jdk18on:1.84", |
24 | | - ) |
25 | | - } |
| 22 | +// build.gradle.kts |
| 23 | +plugins { |
| 24 | + id("com.google.devtools.ksp") version "2.3.7-1.x" |
26 | 25 | } |
27 | | - |
28 | 26 | dependencies { |
29 | | - // ... existing deps ... |
30 | | - compileOnly("org.bouncycastle:bcprov-jdk18on:1.84") |
31 | | - compileOnly("org.bouncycastle:bcpg-jdk18on:1.84") |
32 | | - compileOnly("org.bouncycastle:bcpkix-jdk18on:1.84") |
33 | | - compileOnly("org.bouncycastle:bcutil-jdk18on:1.84") |
| 27 | + implementation("ai.deep-code:agents-kt:0.4.4") |
| 28 | + ksp("ai.deep-code:agents-kt-ksp:0.4.4") |
34 | 29 | } |
35 | 30 | ``` |
36 | 31 |
|
37 | | -Stale BC 1.80 entries in `gradle/verification-metadata.xml` (cumulative cruft from the original 1.80 era) were also pruned — only the 1.84 checksums remain. |
38 | | - |
39 | | -After v0.4.3 lands on `main`, the next `gradle/actions/dependency-submission` workflow run submits a clean 1.84-everywhere graph for both modules, and the four dependabot alerts should clear. |
| 32 | +What lights up for every `@Generable` class in your code: |
40 | 33 |
|
41 | | -## Inherited from v0.4.2 |
| 34 | +| What runs today | What ships in 0.4.4 with KSP applied | |
| 35 | +|---|---| |
| 36 | +| `KClass.jsonSchema()` walks the class reflectively on every call | Reads a `const val JSON_SCHEMA: String` baked at compile time. Byte-identical output. (#1701, #1702) | |
| 37 | +| `KClass.toLlmDescription()` walks + checks `@Guide` annotations | Reads a `const val LLM_DESCRIPTION: String`. `@LlmDescription(text)` override is baked into the constant. (#1703) | |
| 38 | +| `KClass.constructFromMap(map)` reflects to find the primary ctor and invokes via `callBy` | Calls a `@JvmStatic constructFromMap(Map<*,Any?>): T?` with named-arg construction. Strict overflow / type rejection preserved (#665, #855). (#1704) | |
| 39 | +| Sealed roots reflect through `sealedSubclasses` | Generated `oneOf` schema with `"type"` discriminators; generated dispatch by type-name to each variant's `constructFromMap`. (#1702, #1704) | |
42 | 40 |
|
43 | | -Same as v0.4.2 — see the v0.4.2 release notes for the full feature set since 0.3.0: |
| 41 | +**Compile-time error coverage.** The processor also catches `@Generable` misuses at build time — non-sealed interfaces, annotation classes, enums, abstract classes, classes without a parameterised primary constructor, and fields with types outside the supported set (`String`, `Int`, `Long`, `Double`, `Float`, `Boolean`, `List<T>`, nested `@Generable`). What used to be a silent `{"type":"string"}` fallback at runtime is now a red squiggle in the IDE (#1700, #1701). |
44 | 42 |
|
45 | | -- Three model providers — Ollama, Claude, OpenAI (#1644, #1656) |
46 | | -- LiveRunner precheck hook + `OllamaPreflight` (#1132) |
47 | | -- Live typed-args integration tests across all three providers (#1675) |
48 | | -- `ModelConfig.toString()` masks `apiKey` (#1665) |
49 | | -- Ollama wire-shape fix: `content: null` on assistant tool-call turns (#1694) |
50 | | -- Dependency refresh: `kotlinx-coroutines` 1.11.0, Gradle 9.5.0 |
51 | | -- BC 1.84 pinned visibly in root module (#1695, v0.4.2) |
| 43 | +**`kotlin-reflect` is no longer on the runtime classpath.** Moved from `implementation` to `compileOnly` in the published POM. Consumers see a ~3.5 MB lighter dependency tree. Reflection-using fallback paths still work for consumers who add `kotlin-reflect` themselves; for everyone else, the `ReflectionFallback.withReflection { ... }` wrap catches `LinkageError` and degrades gracefully — typed-tool deserialization returns null and routes through `onError.invalidArgs` instead of crashing (#1705). |
52 | 44 |
|
53 | | -## Migration from v0.4.2 |
| 45 | +**Defensive emission gate.** Sealed `@Generable` parents whose variants aren't visible to KSP (incremental-compile race, edge cases) skip schema emission; reflection takes over at runtime against the full JVM hierarchy. Single safety net for the case where the codegen view of the world is incomplete (#1705). |
54 | 46 |
|
55 | | -Drop-in. Nothing to do. |
| 47 | +### The `wrap` operator — teacher / student prompt override |
56 | 48 |
|
57 | | -## Migration from 0.3.x |
| 49 | +`teacher wrap student` is a new composition operator: the teacher agent's String output becomes the student's system prompt for one call only. The student's baked-in prompt is restored after. |
58 | 50 |
|
59 | 51 | ```kotlin |
60 | | -implementation("ai.deep-code:agents-kt:0.4.3") |
| 52 | +val teacher = agent<String, String>("teacher") { |
| 53 | + skills { skill<String, String>("brief", "Produce a prompt") { |
| 54 | + implementedBy { topic -> "You are a $topic specialist. Answer concisely." } |
| 55 | + } } |
| 56 | +} |
| 57 | +val worker = agent<String, String>("worker") { |
| 58 | + prompt("baked-in default") |
| 59 | + model { claude("claude-haiku-4-5-20251001"); apiKey = key } |
| 60 | + skills { skill<String, String>("answer", "Answer using the prompt") { tools() } } |
| 61 | +} |
| 62 | + |
| 63 | +val pipeline = teacher wrap worker |
| 64 | +val result = pipeline("Kotlin coroutines") |
| 65 | +// teacher emits "You are a Kotlin coroutines specialist. Answer concisely." |
| 66 | +// worker invokes Claude with that as its system prompt |
61 | 67 | ``` |
62 | 68 |
|
63 | | -(Or skip directly from 0.3.x to 0.4.3; v0.4.0, v0.4.1, and v0.4.2 details are in their own release notes if you want the breadcrumb trail.) |
| 69 | +Two framings the test suite proves out: |
| 70 | + |
| 71 | +- **Education** — one generalist student is reused across many narrow jobs because the teacher hands it task-specific context. Headline test: the teacher tells the student to compute Fibonacci via a `fib` tool; same student, different prompts, different jobs (#1698). |
| 72 | +- **Security** — the teacher locks down the student's task surface for the call. The student can't drift to its default prompt. Demonstrated by the guessing-game test (#1699): an oracle agent narrows the search window after each round; the guesser agent reads the teacher-supplied prompt and binary-searches to find the secret. |
| 73 | + |
| 74 | +Live cross-provider proof in the integration suite: Claude as the teacher emits the prompt, Ollama as the student computes `fib(10)` via its registered tool (#1698 live test). |
| 75 | + |
| 76 | +`wrap` returns a `Pipeline<IN, OUT>`, so it composes with `then` / `Pipeline.loop {}` / the rest. Closes the last open Phase 1 PRD item. |
| 77 | + |
| 78 | +### BouncyCastle pin completeness |
| 79 | + |
| 80 | +Internal tag `v0.4.3` existed on GitHub to complete the BC 1.84 pin in the `:agents-kt-ksp` subproject too, plus prune stale 1.80 entries from `gradle/verification-metadata.xml`. That work is folded into 0.4.4 (#1695, never reached Maven Central). The four dependabot advisories on `main` (BCprov / BCpg / BCpkix / BCutil 1.80) should clear after the next scanner pass. |
| 81 | + |
| 82 | +### Other |
| 83 | + |
| 84 | +- Gradle wrapper 9.4.1 → 9.5.0 (was prepped during the BC pass). |
| 85 | +- `kotlinx-coroutines-core` and `kotlinx-coroutines-test` 1.10.2 → 1.11.0. |
| 86 | +- Live integration tests for the wrap operator across Claude (teacher) + Ollama (student) (#1698 live test, #1699 live test). |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +## Migration |
| 91 | + |
| 92 | +### From v0.4.2 (the previous published release) |
| 93 | + |
| 94 | +Drop-in for the existing API surface. Two consumer-side changes to be aware of: |
| 95 | + |
| 96 | +1. **`kotlin-reflect` is no longer on the runtime classpath.** If you don't apply `:agents-kt-ksp` AND you use `@Generable` types, the reflection paths degrade to null returns + placeholder schemas. Recommended fix: apply `:agents-kt-ksp` (covers every `@Generable` hot path). Alternative: add `kotlin-reflect` to your own dependency declarations. |
| 97 | + |
| 98 | +2. **`wrap` is new infix syntax.** Code that uses `wrap` as a function name on its own types won't collide unless it's also `infix`-on-Agent-targeted; conflict is unlikely. Aliasing `import agents_engine.composition.wrap.wrap as wrapInto` if needed. |
| 99 | + |
| 100 | +### From v0.3.x or earlier |
| 101 | + |
| 102 | +Same as v0.4.2 migration (three model providers, fail-fast precheck, masked `apiKey` toString) — see the v0.4.2 release notes for the full intermediate-jump story — plus the v0.4.4 changes above. |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## What's next |
| 107 | + |
| 108 | +Open PRD items after this release: |
| 109 | + |
| 110 | +- **Constrained-decoding integration** — wire the already-generated `JSON_SCHEMA` constants into Ollama's structured-output mode, Anthropic's `tool` JSON-mode, and OpenAI's `response_format` JSON-mode. Provider-side enforcement instead of the prompt-fragment approach. |
| 111 | +- **`Tool<IN, OUT>` hierarchy + `McpTool<IN, OUT>`** — unlocks typed `grants { tools(writeFile, mcpServer.fooTool) }` permissions. |
| 112 | +- **Structure-level budgets** — `budget { }` on Pipeline / Forum / Parallel / Loop. |
| 113 | +- **Streaming (`Flow<LlmResponseChunk>`)** — the dead-air-spinner killer; touches `ModelClient` and all three adapters. |
| 114 | +- **Native CLI binary (GraalVM)** — KSP-generated code is now AOT-friendly, so this is unblocked. |
| 115 | +- **Gemini adapter** — finishes the multi-provider line. |
| 116 | + |
| 117 | +Full breakdown in [`docs/roadmap.md`](docs/roadmap.md). |
0 commit comments