{"owner":"JetBrains","repo":"koog","hasSkills":true,"totalSkillsCount":27,"totalTokensCount":55738,"categories":["anthropic-skill","root-instruction","claude-rule","subagent-persona"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/add-java-code-snippets-in-docs/SKILL.md",".claude/skills/add-java-code-snippets-in-docs/reference/factory-classes.md",".claude/skills/add-java-code-snippets-in-docs/reference/interop-tables.md",".claude/skills/add-java-code-snippets-in-docs/reference/pitfalls.md",".claude/skills/split-jvm-nonjvm/SKILL.md","AGENTS.md","CLAUDE.md","a2a/CLAUDE.md","agents/agents-features/agents-features-opentelemetry/README.md","agents/agents-features/agents-features-snapshot/README.md","docs/docs/agents/basic-agents.md","docs/docs/agents/functional-agents.md","docs/docs/agents/graph-based-agents.md","docs/docs/agents/index.md","docs/docs/agents/planner-agents/goap-agents.md","docs/docs/agents/planner-agents/index.md","docs/docs/agents/planner-agents/llm-based-planners.md","docs/docs/tools/annotation-based-tools.md","docs/docs/tools/built-in-tools.md","docs/docs/tools/class-based-tools.md","docs/docs/tools/index.md","docs/docs/tools/tool-descriptor-schemer.md","examples/simple-examples/src/main/kotlin/ai/koog/agents/example/a2a/README.md","examples/simple-examples/src/main/kotlin/ai/koog/agents/example/snapshot/sql/README.md","integration-tests/src/jvmTest/resources/skills/arithmetic-evaluator/SKILL.md","integration-tests/src/jvmTest/resources/skills/weather-retrieval/SKILL.md","skills/Module.md"],"skills":{".claude/skills/add-java-code-snippets-in-docs/SKILL.md":"---\nname: add-java-code-snippets-in-docs\ndescription: Use when the user asks to add Java tabs/examples to mkdocs docs that already have Kotlin examples (files under docs/docs/**.md using === \"Kotlin\" / Knit INCLUDE+SUFFIX blocks). Handles mkdocs-material tabs, Knit directives, @JavaAPI bridge methods, and JVM factory classes.\nallowed-tools: Read, Edit, Write, Bash, Grep, Glob\n---\n\n## When to use\n\nAdding Java code blocks alongside existing Kotlin blocks in mkdocs documentation (`docs/docs/**.md`). Typical triggers: \"add Java examples to <file>\", \"add Java tabs\", \"complete the Java side of <doc>\", \"fix the Java block in <doc>\".\n\n**Do NOT use this skill for:**\n\n- Writing brand-new Kotlin docs (no precursor Kotlin block to mirror).\n- Splitting source files between JVM and non-JVM source sets — use the **`split-jvm-nonjvm`** skill instead.\n- Java examples outside the mkdocs/Knit pipeline.\n\n## Prerequisites\n\nJDK 17. Gradle 8.x fails cryptically on newer JDKs. If the default JDK is newer, prefix every `./gradlew` command in this workflow with `JAVA_HOME=/path/to/jdk-17`.\n\n## Before starting\n\nRead **one** reference doc to anchor on the established pattern:\n\n- `docs/docs/features/tracing.md` — full Tracing feature (blocks 1–6).\n- `docs/docs/agent-events.md` — custom `FeatureMessageProcessor` subclass (suspend bridge methods `handleMessage` / `handleClose`).\n- `docs/docs/testing.md` — `MockPromptExecutor` builder, tool mocking, comment-only blocks for Kotlin-only DSL.\n- `docs/docs/features/open-telemetry/index.md` — OpenTelemetry examples.\n- `docs/docs/features/open-telemetry/opentelemetry-langfuse-exporter.md` — Langfuse exporter.\n- `docs/docs/features/open-telemetry/opentelemetry-weave-exporter.md` — Weave exporter.\n\n## Workflow\n\nFor each Kotlin code block in the target `.md`:\n\n1. **Read** the Kotlin block: INCLUDE, visible code, SUFFIX, KNIT directive.\n2. **Check existing Java tab.** If absent → add one. If it has empty `/** */` placeholder content → replace entirely. If it has real content → update.\n3. **Consider Kotlin-side alignment.** If the Kotlin block uses Kotlin-only APIs (`kotlinx.io`, `MutableStateFlow`, `KLogger`, inline `Duration`), consider whether the Kotlin example should switch to a JVM `create()` factory (e.g., `TraceFeatureMessageFileWriter.create(path)`). This keeps the two examples structurally aligned. See `reference/factory-classes.md`.\n4. **Add `@JavaAPI` bridge methods if missing.** If the Java code needs a bridge that doesn't exist yet (e.g., a builder method on `MockExecutorBuilder`), add it before writing the Java tab. This may pause the doc work for a source-code change — flag it to the user before proceeding.\n5. **Write the Java tab** immediately after the Kotlin tab, using the template below.\n6. **If renaming Knit files** (e.g., hyphenated `example-feature-java-01.java` → camelCase `exampleFeatureJava01.java`), delete the old generated files first.\n7. **Generate and compile** (see Verification at the bottom).\n8. **Read the generated `.java`** to confirm correctness. Fix and repeat 7–8 on failure.\n\n## Mkdocs / Knit tab structure\n\nJava tabs go immediately after the Kotlin tab. All content inside a tab is indented 4 spaces. `<!--- INCLUDE -->`, `<!--- SUFFIX -->`, `<!--- KNIT -->` are Knit directives (HTML comments — invisible when rendered), not mkdocs syntax.\n\n```markdown\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ...\n    -->\n    ```kotlin\n    // visible code\n    ```\n    <!--- KNIT example-feature-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ...;\n    public class exampleFeatureJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // visible code\n    ```\n    <!--- KNIT exampleFeatureJava01.java -->\n```\n\n### KNIT naming — capital \"Java\" matters\n\n`docs/knit.code.include` has the check `<#if !knit.name?contains(\"Java\")>package ...`. Files with capital \"Java\" in the name skip the `package` declaration; lowercase \"java\" or hyphenated names emit a `package` line that breaks compilation.\n\n- ✅ `exampleFeatureJava01.java`, `exampleTestingJava13.java`\n- ❌ `example-feature-java-01.java`, `examplefeaturejava01.java`\n\n### Knit rules\n\n- INCLUDE + visible code + SUFFIX must concatenate into a valid Java file.\n- Hidden variables in Kotlin INCLUDE (`outputPath`, `input`, etc.) must also appear in Java INCLUDE.\n- Visible-code indentation in `.md` is 4 spaces (tab content). Knit strips that when generating.\n- Always import in INCLUDE; never use FQNs in visible code.\n- Never use file-level annotations (`@file:Suppress`, `@file:OptIn`) — scope to specific declarations.\n\n## Structural matching (the most important rule)\n\n**The Java visible code must match the Kotlin visible code in scope and structure.**\n\n- If Kotlin shows no `main()`, Java must hide `main()` in INCLUDE/SUFFIX.\n- If Kotlin shows `install(Feature) { ... }`, Java must show `.install(Feature.Feature, config -> { })`.\n- If Kotlin creates a writer before the agent, Java must too.\n- If Kotlin shows `agent.run(input)`, Java must too.\n\nRead both visible blocks side-by-side after writing. They should read as equivalent code in their respective languages.\n\n### When Kotlin uses APIs with no Java bridge\n\nSome Kotlin DSLs (graph testing: `testGraph`, `assertSubgraphByName`, `assertNodes`, `assertEdges`, `verifySubgraph`, `assertReachable`) have no `@JavaAPI` bridges. To check: grep for `@JavaAPI` in the source file — if absent, treat as Kotlin-only.\n\nWhen the Kotlin block uses an entirely Kotlin-only API:\n\n1. **Do NOT leave the Java tab empty.** Write a compilable Java class with explanatory comments.\n2. The class must have a valid `main` so it compiles.\n3. Comments explain what the Kotlin DSL does and the recommended Java alternative.\n\n```java\n// Graph structure testing (testGraph, assertSubgraphByName, assertEdges,\n// verifySubgraph, assertReachable) is available through the Kotlin testing DSL.\n//\n// In Java, test agent behavior by running and asserting on the result:\n//   String result = agent.run(\"test input\");\n//   assert \"expected\".equals(result);\n```\n\n## Deep references\n\nLoad these only when you need them:\n\n- **`reference/interop-tables.md`** — Kotlin↔Java mapping table, suspend bridges (`@JvmName(\"run\")`, `handleMessage`, `handleClose`), abstract-property rules, key source files to grep for `@JavaAPI`.\n- **`reference/factory-classes.md`** — JVM `create()` factory pattern, two-overload skeleton, type-conversion cheat sheet, when needed vs not, anti-patterns. Cross-references the `split-jvm-nonjvm` skill.\n- **`reference/pitfalls.md`** — `var`-vs-field, brace-counting in SUFFIX, `is`-prefixed property getters, `@JvmOverloads` requirement, docs classpath limits (no JUnit), legacy `/** */` placeholder, `MockExecutorBuilder` API.\n\n## Verification\n\nAfter each Java block:\n\n```bash\n./gradlew :docs:knit\n./gradlew :docs:compileJava\n```\n\nOn failure, read the generated file to diagnose:\n\n```bash\ncat docs/src/main/kotlin/exampleFeatureJavaNN.java\n```\n",".claude/skills/add-java-code-snippets-in-docs/reference/factory-classes.md":"# JVM `create()` factory classes\n\nLoad this file when the Kotlin block uses types that aren't callable from Java and you need to expose (or audit) a Java-friendly factory.\n\n## When a factory is needed\n\nKotlin constructors / APIs Java can't call cleanly:\n\n- Uses `kotlinx.io` types (`Sink`, `Path`, `SystemFileSystem`).\n- Uses inline value classes (`Duration`) — the constructor becomes private from Java.\n- Uses `KLogger` (kotlin-logging) instead of SLF4J `Logger`.\n- Has default parameters without `@JvmOverloads` — generates synthetic constructors with `DefaultConstructorMarker`.\n\nIf the Kotlin constructor uses only Java-compatible types and has `@JvmOverloads` (or no default params), Java can call it directly with `new`. Example:\n\n```java\n// No factory needed — constructor is Java-accessible\nnew TraceFeatureMessageRemoteWriter()\nnew TraceFeatureMessageRemoteWriter(connectionConfig)\n```\n\n## Pattern\n\nAdd a `companion object` with `create(...)` methods **on the class itself**, in the `jvmCommonMain` source set. The class must already live in `jvmCommonMain` (or have a JVM actual) for this to apply.\n\n> **Prerequisite:** if the class is still in `commonMain`, run the **`split-jvm-nonjvm`** skill first to produce the `expect class` + `jvmCommonMain` actual scaffolding. `split-jvm-nonjvm` requires the class to have no companion object at split time — so the order is: (1) split, then (2) add the companion `create()` on the JVM actual. If the class already has a companion in `commonMain`, raise it with the user before splitting.\n\nProvide two overloads of `create()`:\n\n1. **Kotlin-facing** — accepts the original Kotlin-only types (`kotlinx.io.Path`, `Sink`, `KLogger`, `Duration`). No JVM annotations needed; convenient sugar for Kotlin callers and keeps API symmetry.\n2. **Java-facing** — accepts only Java-compatible types (`java.nio.file.Path`, `java.io.OutputStream`, `org.slf4j.Logger`, primitives, etc.). Annotate with `@JavaAPI` (intent), `@JvmStatic` (static call site), and `@JvmOverloads` (one method per default-parameter combination). Convert to Kotlin-only types inside the body, then delegate to the primary constructor.\n\n## Skeleton\n\nSubstitute `Xxx` with the class name, `KotlinT` / `JavaT` with the actual type pair:\n\n```kotlin\n// In: jvmCommonMain/.../Xxx.kt\npublic class Xxx(/* primary constructor uses Kotlin-only types */) {\n    public companion object {\n        // (1) Kotlin-facing\n        public fun create(\n            param: KotlinT,\n            // ...defaulted params with Kotlin types\n        ): Xxx = Xxx(/* pass through */)\n\n        // (2) Java-facing\n        @JavaAPI\n        @JvmStatic\n        @JvmOverloads\n        public fun create(\n            param: JavaT,\n            // ...defaulted params with Java-friendly types\n        ): Xxx {\n            val kotlinParam = /* convert JavaT -> KotlinT */\n            return Xxx(/* pass converted values */)\n        }\n    }\n}\n```\n\nJava callers then write `Xxx.create(javaParam)` — no separate class, no `Continuation`, no `DefaultConstructorMarker`.\n\n## Conversion cheat sheet\n\n| Kotlin-only type | Java-facing replacement | Conversion inside `create()` |\n|------------------|-------------------------|------------------------------|\n| `kotlinx.io.files.Path` | `java.nio.file.Path` | `Path.of(javaPath.toString())` or use `SystemFileSystem` |\n| `kotlinx.io.Sink` | `java.io.OutputStream` (via opener lambda) | `outputStream.asSink().buffered()` |\n| `KLogger` (kotlin-logging) | `org.slf4j.Logger` | `KotlinLogging.logger(slf4jLogger)` |\n| `kotlin.time.Duration` | `long millis` / `java.time.Duration` | `millis.milliseconds` / `Duration.ofMillis(...).toKotlinDuration()` |\n| `(T) -> R` with Kotlin types | `Function<T, R>` or SAM with Java types | `{ t -> javaFn.apply(t) }` |\n\n## Reference implementations\n\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageFileWriter.kt` — `Path` / `Sink` ↔ `java.nio.file.Path` / `OutputStream`.\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageLogWriter.kt` — `KLogger` ↔ SLF4J `Logger`.\n\n## Anti-pattern: separate `*Jvm` object\n\nDo **not** introduce a separate `public object XxxJvm { ... }` alongside the original class. The convention is to put the Java-facing `create()` on the original class's companion. A `*Jvm` companion class would be redundant and inconsistent with the existing `TraceFeatureMessage*Writer` classes.\n",".claude/skills/add-java-code-snippets-in-docs/reference/interop-tables.md":"# Kotlin ↔ Java Interop Reference\n\nLoad this file when writing the Java side of a non-trivial Kotlin block: figuring out how a Kotlin construct maps to Java, or whether a `suspend` function has a Java-friendly bridge.\n\n## Quick mapping table\n\n| Kotlin | Java |\n|--------|------|\n| `AIAgent(...) { }` | `AIAgent.builder()...build()` |\n| `install(Feature) { }` | `.install(Feature.Feature, config -> { })` |\n| `install(Testing) { config() }` | `.install(Testing.Feature, config -> { })` (`ConfigureAction<T>` is a `fun interface`) |\n| `simpleOllamaAIExecutor()` | `PromptExecutor.builder().ollama().build()` |\n| `KotlinLogging.logger { }` | `LoggerFactory.getLogger(\"name\")` |\n| `val x = ...` (local) | `var x = ...` |\n| `val x = ...` (field) | `static Type x = ...` (never `static var`) |\n| `runBlocking { }` | not needed (`agent.run` is blocking in Java — see suspend bridges below) |\n| `config.addMessageProcessor(...)` | `config.addMessageProcessor(...)` |\n| `class Foo : Bar()` | `class Foo extends Bar` |\n| `when (x) { is Type -> ... }` | `if (x instanceof Type) { ... } else if ...` |\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` — see suspend bridges |\n| `override suspend fun close()` | `void handleClose()` — see suspend bridges |\n| `TraceFeatureMessageFileWriter(path, sinkOpener)` | `TraceFeatureMessageFileWriter.create(path)` — JVM `create()` overload taking `java.nio.file.Path` |\n| `TraceFeatureMessageLogWriter(klogger)` | `TraceFeatureMessageLogWriter.create(slf4jLogger)` — JVM `create()` overload taking SLF4J `Logger` |\n| `TraceFeatureMessageRemoteWriter(config)` | `new TraceFeatureMessageRemoteWriter()` (direct, no factory needed) |\n| `getMockExecutor(...) { mockLLMAnswer(...) }` | `MockPromptExecutor.builder(serializer).mockLLMAnswer(...).build()` |\n\n## Suspend functions and Java\n\nKotlin `suspend` functions compile to JVM methods with an extra `Continuation` parameter and `Object` return type. **Java code snippets must NEVER show `Continuation` parameters.** The codebase provides Java-friendly bridges.\n\n### Calling suspend methods from Java\n\nMethods annotated with `@JavaAPI` and `@JvmName` provide blocking wrappers. Use the Java-facing name directly:\n\n| Kotlin (suspend) | Java (blocking) |\n|------------------|-----------------|\n| `agent.run(input)` | `agent.run(input)` (calls `javaNonSuspendRun` via `@JvmName(\"run\")`) |\n| `processor.initialize()` | `processor.initialize()` (calls `javaNonSuspendInitialize` via `@JvmName`) |\n| `processor.onMessage(msg)` | `processor.onMessage(msg)` (calls `javaNonSuspendOnMessage` via `@JvmName`) |\n\nThese wrappers use `runBlockingIfRequired()` internally. Java code calls them like normal blocking methods.\n\n### Overriding suspend methods from Java\n\nWhen a Java class extends a Kotlin class with `suspend` methods to override, look for non-suspend bridge methods annotated with `@JavaAPI`:\n\n| Kotlin override | Java override |\n|-----------------|---------------|\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` |\n| `override suspend fun close()` | `void handleClose()` |\n\n**Why the names differ:** `@JvmName` cannot be used on `open` or `override` methods (Kotlin restriction — it would break polymorphism). So Java-facing override names can diverge from the Kotlin suspend names (`handleMessage` not `processMessage`).\n\n### Abstract properties with Kotlin types\n\nAbstract properties like `val isOpen: StateFlow<Boolean>` leak Kotlin coroutine types to Java. If the base class provides a default `open val` implementation (e.g., via `MutableStateFlow`), Java subclasses don't need to implement it. Check whether the property is `abstract` or `open` before writing the Java example.\n\n### Before writing a Java subclass example\n\n1. Check what `@JavaAPI` bridge methods exist on the class — grep for `@JavaAPI` in the `jvmCommonMain` actual.\n2. Check which members are `abstract` vs `open` — abstract ones MUST be implemented in Java.\n3. Never show `Continuation`, `Object` return types, or `Unit.INSTANCE` in Java — use the bridge methods.\n\n## `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention for boolean-like property names.\n\n## Key source files to grep\n\nWhen writing Java examples that use these classes, check the JVM actuals for available `@JavaAPI` bridge methods:\n\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/agent/AIAgent.kt` — `javaNonSuspendRun` with `@JvmName(\"run\")`.\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/feature/message/FeatureMessageProcessor.kt` — calling bridges (`javaNonSuspendInitialize`, `javaNonSuspendOnMessage`) and overriding bridges (`handleMessage`, `handleClose`).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorBuilder.kt` — Java builder for mock executor: `mockLLMAnswer()`, `mockLLMToolCall()`, `mockTool()` + inner builder classes.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockPromptExecutor.kt` — entry point: `MockPromptExecutor.builder(serializer)` with `@JvmStatic @JavaAPI`.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorDSLBuilder.kt` — Kotlin DSL builder (reference for understanding what the Java builder delegates to).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/feature/TestingFeature.kt` — `Testing.Feature` companion object (used in Java as `Testing.Feature`).\n- `agents/agents-core/src/jvmTest/java/ai/koog/agents/core/agent/JavaAPIAgentBuilderJavaTest.java` — reference Java test using `MockPromptExecutor`, `AIAgent.builder()`, and functional strategies.\n",".claude/skills/add-java-code-snippets-in-docs/reference/pitfalls.md":"# Pitfalls, testing API, and docs classpath\n\nLoad this file when something fails to compile, or when you're writing a `MockPromptExecutor`-based testing block.\n\n## Common pitfalls\n\n### `var` is only for local variables\n\nJava `var` (type inference) works only for local variables inside methods. For fields, use explicit types:\n\n```java\n// WRONG: static var logger = LoggerFactory.getLogger(\"x\");\n// RIGHT: static Logger logger = LoggerFactory.getLogger(\"x\");\n```\n\n### Inline value classes make constructors private from Java\n\nKotlin classes with `Duration`, `Duration?`, or other inline value class parameters in their constructors generate synthetic constructors with `DefaultConstructorMarker`. These are not callable from Java. Solution: add a JVM `create()` factory — see `factory-classes.md`.\n\n### Import management\n\nAlways add imports to the INCLUDE block. Never use fully-qualified names in visible code:\n\n```java\n// WRONG (in visible code): private static final org.slf4j.Logger logger = ...\n// RIGHT (in INCLUDE):      import org.slf4j.Logger;\n// RIGHT (in visible code): static Logger logger = ...\n```\n\n### `@JvmOverloads` check\n\nBefore calling a Kotlin constructor/method with default parameters from Java, verify it has `@JvmOverloads`. Without it, Java only sees the full-parameter version (or synthetic constructors with `DefaultConstructorMarker`).\n\n### SUFFIX brace counting\n\nCount opening braces in INCLUDE carefully. The SUFFIX must have exactly the matching number of closing braces. Off-by-one errors cause `Syntax error: Expecting a top level declaration` at the end of the generated file. After writing SUFFIX, mentally concatenate INCLUDE + visible code + SUFFIX and verify all braces match.\n\n### `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention.\n\n### Kotlin `expect`/`actual` classes\n\nWhen an `expect abstract class` in `commonMain` has `abstract` members, Java subclasses must implement them. Check the **JVM actual** (in `jvmCommonMain`), not the `expect` declaration — the actual may provide default implementations or bridge methods not visible in the `expect`.\n\n### Legacy `/** **/` placeholder pattern\n\nSome docs have existing Java tabs that wrap code in `/** */` block comments (making the code a Javadoc comment that trivially compiles). This is a legacy pattern — **always replace with real compilable Java code** or explanatory comments for Kotlin-only APIs.\n\n## Testing API: `MockPromptExecutor.builder`\n\nThe testing module (`agents-test`) provides Java-friendly builder APIs via `MockPromptExecutor.builder(serializer)`.\n\n### `MockExecutorBuilder` methods\n\n| Method | Description |\n|--------|-------------|\n| `mockLLMAnswer(text)` | Returns `MockLLMAnswerBuilder` — configure with `.asDefaultResponse()`, `.onRequestContains(pattern)`, `.onRequestEquals(pattern)`, `.onCondition(pred)`. |\n| `mockLLMToolCall(tool, args)` | Returns `MockLLMToolCallBuilder` — configure with `.onRequestEquals(pattern)`, `.onRequestContains(pattern)`, `.onCondition(pred)`. |\n| `mockTool(tool)` | Returns `MockToolBuilder` — configure with `.alwaysReturns(result)`, `.alwaysDoes(action)`, `.returns(result).onArguments(args)`, `.returns(result).onArgumentsMatching(pred)`. |\n| `clock(clock)` | Set custom clock. |\n| `tokenizer(tokenizer)` | Set custom tokenizer. |\n| `handleLastAssistantMessage(bool)` | Configure last-assistant-message handling. |\n| `build()` | Returns `PromptExecutor`. |\n\nAll sub-builders return `MockExecutorBuilder` for method chaining.\n\n### Full Java test pattern\n\n```java\nPromptExecutor mockLLMApi = MockPromptExecutor.builder(new JacksonSerializer())\n    .mockLLMToolCall(SayToUser.INSTANCE, new SayToUser.Args(positiveText))\n        .onRequestEquals(positiveText)\n    .mockLLMAnswer(positiveResponse).onRequestContains(positiveResponse)\n    .mockLLMAnswer(defaultText).asDefaultResponse()\n    .mockTool(SayToUser.INSTANCE).alwaysReturns(positiveResponse)\n    .build();\n\nvar agent = AIAgent.builder()\n    .promptExecutor(mockLLMApi)\n    .llmModel(OpenAIModels.Chat.GPT4o)\n    .install(Testing.Feature, config -> { })\n    .build();\n\nString result = agent.run(positiveText);\n```\n\n### Importing Kotlin objects in Java INCLUDE\n\nWhen Java code needs Kotlin objects defined in a Kotlin example's generated package, import from the generated package:\n\n```java\nimport ai.koog.agents.example.exampleTesting03.*;\n```\n\nThis gives access to tool definitions and other objects declared at the top level of the Kotlin example file.\n\n## Docs compilation environment\n\n### JDK version\n\nThe project requires JDK 17. If the default JDK is newer (e.g., 25), compilation fails with cryptic errors. Use:\n\n```bash\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:knit\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:compileJava\n```\n\n### Docs classpath constraints\n\nThe docs module has a limited classpath:\n\n- **JUnit is NOT available.** Do not use `import org.junit.jupiter.api.Assertions.*`. Use Java `assert` statements instead: `assert expected.equals(result) : \"message\";`\n- **JacksonSerializer IS available:** `import ai.koog.serialization.jackson.JacksonSerializer;`\n- **agents-core, agents-test, agents-ext ARE available:** agent builders, mock executors, `SayToUser` tool, etc.\n\nIf compilation fails with \"package X does not exist\", check `docs/build.gradle.kts` for available dependencies.\n",".claude/skills/split-jvm-nonjvm/SKILL.md":"---\nname: split-jvm-nonjvm\ndescription: |\n    Splits a class or several classes in the common source set commonMain into JVM and non-JVM parts into jvmCommonMain and nonJvmCommonMain. \n    This is used to provide some JVM-specific functionality for a class on JVM targets (e.g. Kotlin/JVM or Java backend or Android).\n---\n\nThe following example shows how to split a class into JVM and non-JVM parts.\nWhen using this skill, avoid unnecessarily looking at existing implementations of the split in the project, just follow this instruction.\nYou can, however, check existing implementations for reference if you have a complex case or otherwise stuck.\n\nHere's a sample class in a `commonMain` source set\n```kt\npackage com.example\n\n/**\n* A sample class with an external dependency, some logic and public methods\n * @param name Some public property\n * @param myRepo Some external dependency, private property\n*/\npublic class MyClass(\n    public val name: String,\n    private val myRepo: MyRepo,\n) {\n    /**\n     * Public API method\n     */\n    public fun doSomething(): String {\n        return fetchData()\n    }\n    \n    /**\n     * Private implementation logic\n     */\n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    /*\n     * Internal method\n     */\n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nIn the same package as `MyClass` in `commonMain`, create the following files. The original `MyClass.kt` file will be overwritten with the expect class (see step three).\n\nFirst, the API interface describing the contract. It should contain only public symbols from the `MyClass`.\nTake KDocs from the respective symbols in `MyClass` and add them to the interface.\n\n```kt\npackage com.example\n\n/**\n * API for [MyClass]\n */\npublic interface MyClassAPI {\n    /**\n     * Some public property\n     */\n    public val name: String\n    \n    /**\n     * Public API method\n     */\n    public fun doSomething(): String\n}\n```\n\nSecond, `MyClassImpl.kt` — implementation of the API interface providing the implementation logic. The filename matches the class name.\n\n```kt\ninternal class MyClassImpl(\n    override val name: String,\n    private val myRepo: MyRepo,\n) : MyClassAPI {\n    override fun doSomething(): String {\n        return fetchData()\n    }\n    \n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nThird, the original class becomes `expect` class\n`MyClass` should extend `MyClassAPI` and override all public symbols from `MyClassAPI`, without method bodies and property values, since `expect` classes can't have any.\nIf `MyClass` had any internal symbols, these should also be listed in the `expect` class along with their KDocs.\nIf `MyClass` had a KDoc, add it on top of the expect class without parameters/properties description.\nIf `MyClass` original KDoc described certain parameters/properties that it accepted in its constructor, add these to KDoc of the expect class secondary constructor.\nThis constructor KDoc can't have `@property` tag for parameters, use only `@param` for all parameters, even if the original was `@property`.\n\n```kt\npackage com.example\n\n/**\n * A sample class with an external dependency, some logic and public methods\n */\npublic expect class MyClass internal constructor(\n    delegate: MyClassImpl,\n) : MyClassAPI {\n    /**\n     * Constructs a new instance of [MyClass]\n     * \n     * @param name Some public property\n     * @param myRepo Some external dependency,\n     */\n    public constructor(\n        name: String,\n        myRepo: MyRepo,\n    )\n    \n    internal val delegate: MyClassImpl\n    \n    override val name: String\n    override fun doSomething(): String\n    \n    /**\n     * Internal method\n     */\n    internal fun doInternal(): String\n}\n```\n\nFourth, ensure that `jvmCommonMain` and `nonJvmCommonMain` source sets have a package directory for `MyClass`, i.e., `com.example`. If not, create missing package directories.\nThe source sets themselves are already declared in the Gradle convention plugin — you do NOT need to add them to the module's `build.gradle.kts` unless you need to add dependencies specifically for these source sets.\nThen, create `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` source sets.\n\nAfter these steps, the structure should look as follows, omitting irrelevant packages and files:\n```\ncommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n            MyClassAPI.kt\n            MyClassImpl.kt\njvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\nnonJvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n```\n\nFifth, in `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` create `actual` declarations for `MyClass`.\nInitially, they should be identical for both source sets.\nIt should use `by delegate` to delegate all public symbols from `MyClassAPI` to the implementation, and direct delegation to delegate internal symbols.\n\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    internal actual fun doInternal(): String = delegate.doInternal()\n}\n```\n\nSixth, optional, step.\nIf the users asked for certain JVM-specific/non-JVM-specific functionality, these actual classes might contain additional \npublic symbols not present in the API interface. Such additional public symbols should be documented properly and concisely with KDoc\n\nFor example, `jvmCommonMain` implementation that uses JVM-specific API\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    /**\n     * Gets environment variable value by [key]\n     */\n    public fun getEnv(key: String): String = System.getenv(key)\n}\n```\n\nSeventh, if `MyClass` had any tests in the original `commonTest` source set, use `MyClassImpl` in the test to verify common implementation.\nDo not rename the test, it should still be called e.g. `MyClassTest`.\nIf you need to test some platform-specific functionality, create tests in `jvmCommonTest` and/or `nonJvmCommonTest` source sets.\nKotlin tests for JVM-specific functionality should go to `jvmCommonTest`.\nIMPORTANT: if the test suite is written in Java, it MUST go to `jvmTest`, not `jvmCommonTest`. KMP only compiles Java code in `jvmMain`/`jvmTest` source sets.\n\nFinally, ensure that the implementation compiles by running:\n```sh\n./gradlew :mymodule:compileKotlinJvm\n./gradlew :mymodule:compileKotlinJs\n```\nReplace `:mymodule` with the actual Gradle module path derived from the file location (e.g., `agents/agents-core` → `:agents:agents-core`).\nIf both pass, the split is working correctly.\n\n### IntelliJ false positives\nDue to the experimental state of expect/actual classifiers, IntelliJ may report false positive errors such as \"no actual for expect\" or \"no expect for actual\". \nThese diagnostics should NOT be trusted. The only reliable verification is running the Gradle compile tasks listed above.\n\n## Important notes\n\n### Imports\nWhen creating the new files (API interface, Impl, expect class, actual classes), carry over all relevant imports from the original class.\n\n### Annotations\nClasses being split should not have annotations. If the original class has annotations (e.g., `@Serializable`, `@Deprecated`), ask the user explicitly how to handle them before proceeding.\n\n### Companion objects\nClasses being split should not have companion objects. If the original class has a companion object, ask the user explicitly how to handle it before proceeding.\n","AGENTS.md":"# Koog AI Agent Framework\n\nKoog is a Kotlin multiplatform framework for building AI agents with graph-based workflows.\nIt supports JVM and JS targets and integrates with multiple LLM providers\n(OpenAI, Anthropic, Google, OpenRouter, Ollama) and Model Context Protocol (MCP).\n\n## Project Structure\n\nThe project follows a modular architecture with a clear separation of concerns:\n\n```\nkoog/\n├── agents/\n│   ├── agents-core/           # Core abstractions (AIAgent, AIAgentStrategy, AIAgentEnvironment)\n│   ├── agents-tools/          # Tool infrastructure (Tool<TArgs, TResult>, ToolRegistry, AIAgentTool)\n│   ├── agents-features-*/     # Feature implementations (memory, tracing, event handling)\n│   ├── agents-mcp/           # Model Context Protocol integration\n│   └── agents-test/          # Testing utilities and framework\n├── prompt-*/                 # LLM interaction layer (executors, models, structured data)\n├── embeddings-*/             # Vector embedding support\n├── examples/                 # Reference implementations and usage patterns\n└── build.gradle.kts          # Root build configuration\n```\n\n## Build & Commands\n\n### Development Commands\n\n```bash\n# Full build including tests\n./gradlew build\n\n# Build without tests\n./gradlew assemble\n\n# Run all JVM tests\n./gradlew jvmTest\n\n# Run all JS tests  \n./gradlew jsTest\n\n# Test specific module\n./gradlew :agents:agents-core:jvmTest\n\n# Run specific test class\n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest\"\n\n# Run specific test method  \n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest.test AIAgent doesn't call tools by default\"\n\n# Compile test classes only (for faster iteration)\n./gradlew jvmTestClasses jsTestClasses\n```\n\n### Development Environment\n\n- **JDK**: 17+ required for JVM target\n- **Build System**: Gradle with version catalogs for dependency management\n- **Targets**: JVM, JavaScript (Kotlin Multiplatform), WASM\n- **IDE**: IntelliJ IDEA recommended with Kotlin plugin\n\n## Code Style\n\n- Follow [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html)\n- Use four spaces for indentation (consistent across all files)\n- Name test functions as `testXxx` (no backticks for readability)\n- Use descriptive variable and function names\n- Prefer functional programming patterns where appropriate\n- Use type-safe builders and DSLs for configuration\n- Document public APIs with KDoc comments\n- NEVER suppress compiler warnings without a good reason\n\n## Quality Gates\nRead and follow the Quality Gates section in /TESTING.md before considering any code change complete.\n\n## Architecture\n\n### Core Framework Components\n\n**AIAgent** — Main orchestrator that executes strategies in coroutine scopes, manages tools via ToolRegistry,\nruns features through AIAgentPipeline, and handles LLM communication via PromptExecutor.\n\n**AIAgentStrategy** — Graph-based execution logic that defines workflows as subgraphs with start/finish nodes,\nmanages tool selection strategy, and handles termination/error reporting.\n\n**ToolRegistry** — Centralized, type-safe tool management using a builder pattern: `ToolRegistry { tool(MyTool()) }`.\nSupports registry merging with `+` operator.\n\n**AIAgentFeature** — Extensible capabilities installed into AIAgentPipeline with configuration.\nFeatures have unique storage keys and can intercept agent lifecycle events.\n\n### Module Organization\n\n1. **agents-core**: Core abstractions (`AIAgent`, `AIAgentStrategy`, `AIAgentEnvironment`)\n2. **agents-tools**: Tool infrastructure (`Tool<TArgs, TResult>`, `ToolRegistry`, `AIAgentTool`)\n3. **agents-features-***: Feature implementations (memory, tracing, event handling)\n4. **agents-mcp**: Model Context Protocol integration\n5. **prompt-***: LLM interaction layer (executors, models, structured data)\n6. **embeddings-***: Vector embedding support\n7. **examples**: Reference implementations and usage patterns\n\n### Key Architectural Patterns\n\n- **State Machine Graphs**: Agents execute as node graphs with typed edges\n- **Feature Pipeline**: Extensible behavior via installable features with lifecycle hooks\n- **Environment Abstraction**: Safe tool execution context preventing direct tool calls\n- **Type Safety**: Generics ensure compile-time correctness for tool arguments/results\n- **Builder Patterns**: Fluent APIs for configuration throughout the framework\n\n## Testing\n\nThe framework provides comprehensive testing utilities in `agents-test` module:\n\n### LLM Response Mocking\n\n```kotlin\nval mockLLMApi = getMockExecutor(toolRegistry, eventHandler) {\n    mockLLMAnswer(\"Hello!\") onRequestContains \"Hello\"\n    mockLLMToolCall(CreateTool, CreateTool.Args(\"solve\")) onRequestEquals \"Solve task\"\n    mockLLMAnswer(\"Default response\").asDefaultResponse\n}\n```\n\n### Tool Behavior Mocking\n\n```kotlin\n// Simple return value\nmockTool(PositiveToneTool) alwaysReturns \"The text has a positive tone.\"\n\n// With additional actions\nmockTool(NegativeToneTool) alwaysTells {\n    println(\"Tool called\")\n    \"The text has a negative tone.\"\n}\n\n// Conditional responses\nmockTool(SearchTool) returns SearchTool.Result(\"Found\") onArgumentsMatching {\n    args.query.contains(\"important\")\n}\n```\n\n### Graph Structure Testing\n\n```kotlin\nAIAgent(...) {\n    withTesting()\n\n    testGraph(\"test\") {\n        val firstSubgraph = assertSubgraphByName<String, String>(\"first\")\n        val secondSubgraph = assertSubgraphByName<String, String>(\"second\")\n\n        assertEdges {\n            startNode() alwaysGoesTo firstSubgraph\n            firstSubgraph alwaysGoesTo secondSubgraph\n        }\n\n        verifySubgraph(firstSubgraph) {\n            val askLLM = assertNodeByName<String, Message.Response>(\"callLLM\")\n            assertNodes {\n                askLLM withInput \"Hello\" outputs Message.Assistant(\"Hello!\")\n            }\n        }\n    }\n}\n```\n\nFor comprehensive testing examples, see `agents/agents-test/TESTING.md`.\n\n## Security\n\n### API Key Management\n\n- **NEVER** commit API keys or secrets to the repository\n- Use environment variables for all sensitive configuration\n- Store test API keys in a local environment only\n- Required environment variables for integration tests:\n    - `ANTHROPIC_API_TEST_KEY`\n    - `GEMINI_API_TEST_KEY`\n    - `MISTRAL_AI_API_TEST_KEY`\n    - `OLLAMA_IMAGE_URL`\n    - `OPEN_AI_API_TEST_KEY`\n    - `OPEN_ROUTER_API_TEST_KEY`\n\n### Tool Execution Safety\n\n- Tools execute within controlled `AIAgentEnvironment` contexts\n- Direct tool calls are prevented outside agent execution\n- Use type-safe tool arguments to prevent injection attacks\n- Validate all external inputs in tool implementations\n\n### Dependency Security\n\n- Regularly update dependencies using Gradle version catalogs\n- Use specific version ranges to avoid supply chain attacks\n- Review dependencies for known vulnerabilities\n- Follow the principle of the least privilege in tool implementations\n\n## Configuration\n\n### Environment Setup\n\nSet environment variables for integration testing (never commit API keys):\n\n```bash\n# Export in your shell or IDE run configuration\nexport ANTHROPIC_API_TEST_KEY=your_key_here\nexport DEEPSEEK_API_TEST_KEY=your_key_here\nexport GEMINI_API_TEST_KEY=your_key_here\nexport MISTRAL_AI_API_TEST_KEY=your_key_here\nexport OLLAMA_IMAGE_URL=http://localhost:11434\nexport OPEN_AI_API_TEST_KEY=your_key_here\nexport OPEN_ROUTER_API_TEST_KEY=your_key_here\n\n# Or add to ~/.bashrc, ~/.zshrc, or IDE environment variables\n```\n\n### Gradle Configuration\n\n- Uses version catalogs (`gradle/libs.versions.toml`) for dependency management\n- Multiplatform configuration in `build.gradle.kts`\n- Test configuration supports both JVM and JS targets\n\n### Development Environment Requirements\n\n- **JDK**: 17+ (OpenJDK recommended)\n- **IDE**: IntelliJ IDEA with Kotlin Multiplatform plugin\n- **Optional**: Docker for Ollama local testing\n\n## Development Workflow\n\n### Branch Strategy\n\n- **develop**: All development (features and bug fixes)\n- **main**: Released versions only\n- Base all PRs against `develop` branch\n- Use descriptive branch names: `feature/agent-memory`, `fix/tool-registry-bug`\n\n### Code Quality\n\n- **ALWAYS** run `./gradlew build` before submitting PRs\n- Ensure all tests pass on JVM, JS, WASM targets\n- Follow established patterns in existing code\n- Add tests for new functionality\n- Update documentation for API changes\n\n### Commit Guidelines\n\n- Use conventional commit format: `feat:`, `fix:`, `docs:`, `test:`\n- Include issue references where applicable\n- Keep commits focused and atomic\n","CLAUDE.md":"AGENTS.md","a2a/CLAUDE.md":"# A2A Module Development Guidelines\n\n## Module Overview\n\nThe A2A (Agent-to-Agent) module is a **meta-module** within the larger Koog project that implements a comprehensive client and server library for the A2A protocol, a standardized communication protocol for AI agents based on the specification at https://a2a-protocol.org/latest/specification/.\n\n**Important**: Since this is a meta-module inside a bigger root project, the Gradle wrapper executable is located one directory up. All Gradle commands must use `../gradlew` when running from the a2a directory.\n\n### Purpose\n- **Agent Discovery**: Through AgentCard manifests describing agent capabilities and interfaces\n- **Message Exchange**: Standardized message format with support for different content types\n- **Task Management**: Long-running operations with state tracking and lifecycle management\n- **Push Notifications**: Asynchronous notifications for task updates\n- **Multiple Transport Protocols**: JSON-RPC, HTTP+JSON/REST with extensibility for gRPC\n- **Authentication & Security**: OpenAPI 3.0 compatible security schemes\n\n### Submodules Architecture\n\n1. **a2a-core**: Core abstractions, data models, and transport interfaces\n   - AgentCard, Message, Task, Event hierarchy\n   - Transport interfaces: ClientTransport, ServerTransport, RequestHandler\n   - No external dependencies beyond Kotlin stdlib and serialization\n\n2. **a2a-client**: High-level client library for communicating with A2A servers\n   - A2AClient wrapper, AgentCardResolver\n   - Capability validation, request/response handling\n   - Depends on: a2a-core, Ktor HTTP client\n\n3. **a2a-server**: Server-side implementation for hosting A2A agents\n   - A2AServer, AgentExecutor interface, session management\n   - Storage abstractions with in-memory implementations\n   - Depends on: a2a-core, coroutines, logging\n\n4. **a2a-transport**: Multiple transport protocol implementations\n   - a2a-transport-core-jsonrpc: JSON-RPC protocol base classes\n   - a2a-transport-client-jsonrpc-http: HTTP JSON-RPC client transport\n   - a2a-transport-server-jsonrpc-http: HTTP JSON-RPC server transport\n   - a2a-transport-*-rest: HTTP+JSON/REST protocol implementations\n\n## Technologies & Libraries\n\n### Core Dependencies (from gradle/libs.versions.toml)\n- **Kotlin Multiplatform**\n- **kotlinx-serialization**: JSON serialization for protocol messages\n- **kotlinx-coroutines**: Async/concurrent programming, Flow APIs\n- **kotlinx-datetime**: Timestamp handling in protocol messages\n- **ktor3**: HTTP client/server for transport implementations\n- **oshai-kotlin-logging**: Structured logging\n\n### Testing Libraries\n- **kotlin-test**: Multiplatform test framework\n- **kotest-assertions**: Rich assertion library for complex objects\n- **kotlinx-coroutines-test**: Testing coroutines with runTest\n- **testcontainers**: Docker-based integration testing\n- **logback-classic**: Runtime logging for tests\n\n### Platform Support\n- **JVM**: Full server and client support\n- **JS (Browser)**: Client-only support\n- **Future platforms**: Architecture ready for native support\n\n## Development Guidelines\n\n### Architecture Decisions\n⚠️ **CRITICAL**: Never make design and architecture decisions independently. Always ask the user for confirmation before:\n- Adding new transport protocols\n- Changing storage interfaces\n- Modifying core protocol message formats\n- Adding new authentication schemes\n- Changing session management behavior\n- Doing other sorts of major architectural changes\n\n### Code Style Conventions\n\n#### API Visibility\n- Use `explicitApi()` - all public APIs must have explicit visibility modifiers\n- Prefer `public` declarations for APIs, `internal` for implementation details\n- Use `@InternalA2AApi` annotation for APIs that are public but internal to the module\n\n#### Naming Conventions\n- Classes: PascalCase (`A2AServer`, `SessionManager`, `AgentExecutor`)\n- Interfaces: Same as classes, often ending in -or/-er for behaviors (`AgentExecutor`)\n- Functions: camelCase with descriptive names (`onSendMessage`, `getByContext`)\n- Properties: camelCase (`agentCard`, `sessionMutex`)\n\n#### Async Patterns\n- Use `suspend` functions for all async operations\n- Prefer `Flow` for streaming APIs (task events, message streams)\n- Use `RWLock` pattern: `rwLock.withReadLock/withWriteLock` for concurrent access\n- Use `Mutex` for single-threaded critical sections: `mutex.withLock`\n\n#### Error Handling\n- Use domain-specific exceptions (`A2AInternalErrorException`, `TaskOperationException`)\n- Propagate errors properly in async contexts\n- Include contextual information in exception messages\n\n#### KDoc Documentation\n- **Placement**: KDoc directly above declarations (classes, functions, properties)\n- **Constructor properties**: Document using `@param` tags in class KDoc\n- **Public class properties**: Document using `@property` tags in class KDoc\n- **Cross-references**: Use `[ClassName]`, `[ClassName.propertyName]` syntax for linking components\n- **Examples**: Include practical code examples for complex APIs\n- **Validation rules**: Document constraints with bullet points and clear explanations\n- **Exception documentation**: Use `@throws` with specific conditions\n- **Required**: All public APIs must have KDoc (enforced by `explicitApi()`)\n\n## Testing Requirements\n\n### Mandatory Test Execution\n⚠️ **ALWAYS** run the specific test suite using Gradle's jvmTest task to ensure changes work correctly:\n\n```bash\n# Run all tests in a specific module\n../gradlew :a2a:a2a-server:jvmTest\n\n# Run a specific test class\n../gradlew :a2a:a2a-server:jvmTest --tests \"ai.koog.a2a.server.tasks.InMemoryTaskStorageTest\"\n\n# Run a specific test method\n../gradlew :a2a:a2a-client:jvmTest --tests \"ai.koog.a2a.client.A2AClientIntegrationTest.test get agent card\"\n```\n\n### Testing Approach: Crucial Minimum\n- **Focus on core functionality** - test essential behavior, not implementation details\n- **Separate dedicated tests** - one test method per core scenario/edge case\n- **Avoid verbose, unnecessary tests** - don't test trivial getters/setters\n- **Test error conditions** - verify proper exception handling and error states\n\n#### Preferred Assertion Pattern\n**✅ DO**: Assert on whole objects when possible (especially data classes):\n```kotlin\nassertEquals(expectedTask, actualTask)  // Compares all properties\nassertEquals(listOf(expectedArtifact), retrieved?.artifacts)\n```\n\n**❌ AVOID**: Bunch of individual field assertions unless necessary:\n```kotlin\n// Avoid this pattern:\nassertEquals(expected.id, actual.id)\nassertEquals(expected.contextId, actual.contextId)\nassertEquals(expected.status, actual.status)\n// ... many more individual assertions\n```\n\n### Integration Tests\n- **Docker-based testing**: a2a-client uses testcontainers with Python A2A server\n- **Docker build dependency**: Integration tests automatically build required containers\n- **Test isolation**: Each test should be independent and clean up after itself\n\n### Test Structure Examples\n\n**Unit Tests** (InMemoryTaskStorageTest pattern):\n```kotlin\nclass InMemoryTaskStorageTest {\n    private lateinit var storage: InMemoryTaskStorage\n\n    @BeforeTest\n    fun setUp() {\n        storage = InMemoryTaskStorage()\n    }\n\n    @Test\n    fun testStoreAndRetrieveTask() = runTest {\n        val task = createTask(id = \"task-1\", contextId = \"context-1\")\n        storage.update(task)\n        val retrieved = storage.get(\"task-1\")\n\n        assertNotNull(retrieved)\n        assertEquals(task, retrieved)  // Whole object assertion\n    }\n}\n```\n\n**Integration Tests** (A2AClientIntegrationTest pattern):\n```kotlin\n@Testcontainers\nclass A2AClientIntegrationTest {\n    @Container\n    val testA2AServer = GenericContainer(\"test-python-a2a-server\")\n        .withExposedPorts(9999)\n        .waitingFor(Wait.forListeningPort())\n\n    @Test\n    fun `test get agent card`() = runTest {\n        val agentCard = client.getAgentCard()\n        val expectedAgentCard = AgentCard(...)\n        assertEquals(expectedAgentCard, agentCard)  // Full object comparison\n    }\n}\n```\n\n## Implementation Patterns\n\n### Transport Layer Abstraction\n- Implement `ClientTransport` interface for new client protocols\n- Implement `ServerTransport` and `RequestHandler` for new server protocols\n- Keep protocol logic separate from transport mechanism\n- Use suspend functions for all transport operations\n\n### Storage Interface Pattern\n```kotlin\npublic interface SomeStorage {\n    public suspend fun save(item: Item)\n    public suspend fun get(id: String): Item?\n    public suspend fun delete(id: String)\n}\n\n// In-memory implementation for testing/development\n@OptIn(InternalA2AApi::class)\npublic class InMemorySomeStorage : SomeStorage {\n    private val rwLock = RWLock()\n    private val items = mutableMapOf<String, Item>()\n\n    override suspend fun save(item: Item) = rwLock.withWriteLock {\n        items[item.id] = item\n    }\n}\n```\n\n### Session Management Pattern\n- Use `SessionEventProcessor` for event-driven session handling\n- Implement proper cleanup with `Closeable` interface\n- Use structured concurrency with `CoroutineScope`\n- Handle session lifecycle properly (start → active → closed)\n\n### Agent Executor Implementation\n```kotlin\npublic class MyAgentExecutor : AgentExecutor {\n    override suspend fun execute(\n        context: RequestContext<MessageSendParams>,\n        eventProcessor: SessionEventProcessor\n    ) {\n        // Process request and send events through eventProcessor\n        eventProcessor.sendTaskEvent(/* task event */)\n    }\n}\n```\n\n## Some Common Commands\n\n```bash\n# Build specific modules (a2a is a meta-module, build individual modules)\n../gradlew :a2a:a2a-core:assemble\n../gradlew :a2a:a2a-client:assemble\n../gradlew :a2a:a2a-server:assemble\n\n# Run JVM tests for specific modules\n../gradlew :a2a:a2a-core:jvmTest\n../gradlew :a2a:a2a-client:jvmTest\n../gradlew :a2a:a2a-server:jvmTest\n../gradlew :a2a:a2a-transport:a2a-transport-core-jsonrpc:jvmTest\n\n# Run JS tests for specific modules\n../gradlew :a2a:a2a-core:jsTest\n../gradlew :a2a:a2a-client:jsTest\n../gradlew :a2a:a2a-server:jsTest\n\n# Build all non-transport a2a modules at once\n../gradlew :a2a:a2a-core:assemble :a2a:a2a-client:assemble :a2a:a2a-server:assemble\n\n# Run all JVM tests across a2a modules\n../gradlew :a2a:a2a-core:jvmTest :a2a:a2a-client:jvmTest :a2a:a2a-server:jvmTest\n```\n","agents/agents-features/agents-features-opentelemetry/README.md":"# OpenTelemetry support\n\nThis guide provides comprehensive instructions on how to use OpenTelemetry with the Koog agentic framework for tracing and monitoring your AI agents.\n\n## Overview\n\nOpenTelemetry is an observability framework that provides tools for generating, collecting, and exporting telemetry data (metrics, logs, and traces) from your applications. The Koog OpenTelemetry feature allows you to instrument your AI agents to collect telemetry data, which can help you:\n\n- Monitor agent performance and behavior\n- Debug issues in complex agent workflows\n- Visualize the execution flow of your agents\n- Track LLM calls and tool usage\n- Count tokens (input, output, and total) for LLM calls to monitor usage and costs\n- Analyze agent behavior patterns\n\nKey OpenTelemetry concepts\n\n- **Spans**: Spans represent individual units of work or operations within a distributed trace. They indicate the beginning and end of a specific activity in an application, such as an agent execution, a function call, an LLM call, or a tool call.\n- **Exporters**: Exporters are components responsible for sending the collected telemetry data (spans, metrics, logs) to various backends or destinations.\n- **Collectors**: An OpenTelemetry Collector receives, processes, and exports telemetry data. It acts as an intermediary between your applications and your observability backend.\n\nThe OpenTelemetry feature in Koog automatically creates spans for various agent events, including:\n\n- Agent creation and invocation\n- Strategy execution\n- Node execution\n- LLM calls (inference)\n- Tool calls\n\n## Installation\n\nTo use OpenTelemetry with Koog, add the OpenTelemetry feature to your agent:\n\n```kotlin\nval agent = AIAgent(\n    executor = simpleOpenAIExecutor(apiKey),\n    llmModel = OpenAIModels.Chat.GPT4o,\n    systemPrompt = \"You are a helpful assistant.\",\n    installFeatures = {\n        install(OpenTelemetry) {\n            // Configuration options here\n        }\n    }\n)\n```\n\n## Configuration\n\n### Basic configuration\n\nHere is an example of installing the OpenTelemetry feature with a basic set of configuration items:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.feature.OpenTelemetry\nimport io.opentelemetry.exporter.logging.LoggingSpanExporter\n\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Java SDK exporter is bridged automatically on JVM\n}\n```\n\nThe example includes the following configuration methods:\n\n| Name               | Data type | Required | Description                                                                                     |\n|--------------------|-----------|----------|-------------------------------------------------------------------------------------------------|\n| `setServiceInfo`   | Unit      | No       | Sets the information about the service being instrumented, including service name and version.  |\n| `addSpanExporter`  | Unit      | No       | Registers an exporter wrapped in a [`batchSpanProcessor`][batchSpanProcessor] (OTel-recommended). |\n\nPlease see below the full list of available configuration properties:\n\n| Name                    | Data type       | Default value | Description                                                                                                              |\n|-------------------------|-----------------|---------------|--------------------------------------------------------------------------------------------------------------------------|\n| `serviceName`           | `String`        | `ai.koog`     | The name of the service being instrumented.                                                                              |\n| `serviceVersion`        | `String`        | `0.0.0`       | The version of the service being instrumented.                                                                           |\n| `isVerbose`             | `Boolean`       | `false`       | Whether to enable verbose logging for debugging OpenTelemetry configuration.                                             |\n| `isShutdownOnAgentClose`| `Boolean`       | `false`       | Whether the SDK is shut down when the owning agent closes.                                                               |\n| `tracer`                | `Tracer`        |               | Kotlin SDK [`Tracer`][Tracer] used for creating spans.                                                                  |\n\nConfiguration API:\n\n| Name                    | Arguments                                                | Description                                                                                                              |\n|-------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| `setServiceInfo`        | `serviceName: String, serviceVersion: String`            | Sets the service information including name and version.                                                                 |\n| `addSpanExporter`       | `exporter: SpanExporter` (Kotlin SDK or Java SDK on JVM) | Registers an exporter behind a `batchSpanProcessor`. On JVM the method is overloaded to accept Java-SDK exporters directly (bridged via the compat layer). |\n| `addSpanProcessor`      | `factory: TraceExportConfigDsl.() -> SpanProcessor`      | Registers a custom span processor (e.g., simple/composite/custom-batched) via the SDK's DSL.                             |\n| `addResourceAttributes` | `attributes: Map<String, Any>`                           | Adds resource attributes to provide additional context about the service. Supported value types: `String`, `Long`, `Double`, `Boolean`. |\n| `setVerbose`            | `verbose: Boolean`                                       | Enables or disables verbose logging for debugging OpenTelemetry configuration.                                           |\n| `setSdk`                | `openTelemetry: OpenTelemetry`                           | Injects a pre-built Kotlin OpenTelemetry SDK instance, bypassing internal configuration.                                 |\n| `setShutdownOnAgentClose`| `shutdownOnAgentClose: Boolean`                         | Toggles whether the SDK is shut down when the owning agent closes.                                                       |\n\nJVM-only members on [`OpenTelemetryConfig`][OpenTelemetryConfig] cover metrics (`addMetricExporter`, `addMetricFilter`, `meter`) — the Kotlin SDK 0.3.0 ships no metrics module, so metrics are sent through the Java SDK.\n\n### Advanced configuration\n\nFor more advanced configuration, you can customize resource attributes:\n\n```kotlin\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Add Logging exporter\n\n    // Add resource attributes (Map<String, Any>; supports String, Long, Double, Boolean)\n    addResourceAttributes(mapOf(\n        \"deployment.environment\" to \"production\",\n        \"custom.attribute\" to \"custom-value\"\n    ))\n}\n```\n\n### Custom span processors\n\n`addSpanExporter` always wraps in `batchSpanProcessor`. For full control over the processor — custom batching parameters, simple per-span export for tests, or a composite processor over several inner processors — use `addSpanProcessor` and the SDK's DSL-scoped factory functions:\n\n```kotlin\nimport io.opentelemetry.kotlin.tracing.export.batchSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.simpleSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.stdoutSpanExporter\n\ninstall(OpenTelemetry) {\n    // Stdout exporter wrapped in a simple (synchronous) processor — useful for local debugging.\n    addSpanProcessor { simpleSpanProcessor(stdoutSpanExporter()) }\n\n    // Custom batch parameters (default: 5s schedule delay, 512 max batch size).\n    addSpanProcessor { batchSpanProcessor(myExporter, scheduleDelayMs = 1000) }\n}\n```\n\n### Sdk configuration\n\nIf you already have an initialized [`OpenTelemetry`][OpenTelemetry] SDK instance — for example, configured elsewhere in your application or provided by a framework — you can pass it directly using `setSdk`. When using `setSdk` any other configuration methods like `addSpanExporter`, `addSpanProcessor`, `addResourceAttributes` are ignored, since the SDK is assumed to be fully configured.\n\n```kotlin\nimport io.opentelemetry.kotlin.OpenTelemetry\nimport io.opentelemetry.kotlin.createOpenTelemetry\n\nval sdk: OpenTelemetry = createOpenTelemetry { /* … */ } // Your preconfigured SDK\n\ninstall(OpenTelemetry) {\n    setSdk(sdk) // Use your existing OpenTelemetry instance\n}\n```\n\n#### Resource attributes\n\nResource attributes represent additional information about a process producing telemetry. This information can be\nincluded in an OpenTelemetry configuration in Koog using the `addResourceAttributes()` method that takes a\n`Map<String, Any>`. Supported value types are `String`, `Long`, `Double`, and `Boolean`.\n\nThe following default resource attributes are automatically added:\n\n- `service.name`: The name of the service being instrumented. Set to the value of `serviceName`.\n- `service.version`: The version of the service being instrumented. Set to the value of `serviceVersion`.\n- `service.instance.time`: The timestamp when the service instance was created.\n- `os.type`: The operating system type.\n- `os.version`: The operating system version.\n- `os.arch`: The operating system architecture.\n\nThe OpenTelemetry feature automatically adds various attributes to spans following the [OpenTelemetry Semantic Convention for GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/):\n\n### Common Attributes\n\n- `gen_ai.operation.name`: the type of operation being performed (e.g., \"create_agent\", \"invoke_agent\", \"chat\", \"execute_tool\")\n- `gen_ai.system`: the LLM provider being used\n- `gen_ai.agent.id`: the ID of the agent\n- `gen_ai.agent.name`: the name of the agent (when available)\n- `gen_ai.conversation.id`: the ID of the conversation/run\n- `gen_ai.request.model`: the LLM model being used\n- `gen_ai.request.temperature`: the temperature parameter for the LLM request\n- `error.type`: the type of error that occurred (when applicable)\n\n### Custom Attributes\n\nSome custom attributes specific to Koog are also added:\n\n- `koog.agent.strategy.name`: the name of the agent strategy\n- `koog.node.id`: the name of the node being executed\n\n### Span Types\n\nThe OpenTelemetry feature creates different types of spans for various operations. Each span type has a distinct purpose, parent–child relationship, and set of attributes and events.\n\n1. **Create Agent Span**\n    - Purpose: Long‑lived span representing the lifecycle of an agent instance (created once per agent ID).\n    - Emitted: When an agent is first started. Closed when the agent is closed.\n    - Parent: none. Children: **Invoke Agent Span** (one per run).\n    - Useful for: Grouping all agent runs and capturing static agent configuration.\n    - Key attributes:\n        - `gen_ai.operation.name` = `create_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.request.model`\n\n2. **Invoke Agent Span**\n    - Purpose: One concrete execution (run) of an agent.\n    - Emitted: At the start of a run. Closed on successful finish or error.\n    - Parent: **Create Agent Span**. Children: **Strategy Span** instances, and, indirectly, node, tool, and LLM spans within strategies.\n    - Useful for: Measuring run‑level timing, status, and grouping of all strategy, node, tool, and LLM activity for a run.\n    - Key attributes:\n        - `gen_ai.operation.name` = `invoke_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.conversation.id`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.response.finish_reasons` (derived from the final response in the session prompt: `Stop` for Assistant/Reasoning, `ToolCalls` for Tool.Call)\n\n3. **Strategy Span**\n    - Purpose: Execution of a strategy within an agent run.\n    - Emitted: At the start of strategy execution. Closed when the strategy completes or errors.\n    - Parent: **Invoke Agent Span**. Children: **Node Execute Span** instances.\n    - Useful for: Tracking strategy-level execution, grouping all node executions within a strategy, and measuring strategy performance.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.strategy.name`\n        - `koog.event.id`\n    - Note: This is a custom span type specific to Koog, not defined in the OpenTelemetry Semantic Conventions.\n\n4. **Node Execute Span**\n    - Purpose: Execution of a single node in the agent strategy.\n    - Emitted: Immediately before a node runs. Closed after the node completes or errors.\n    - Parent: **Strategy Span**. Children: **Inference Spans** and **Execute Tool Span** instances created by the node.\n    - Useful for: Understanding strategy flow and attributing LLM or tool work to a specific node.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.node.id`\n\n5. **Inference Span**\n    - Purpose: A single LLM call (prompt execution).\n    - Emitted: Before the LLM is invoked. Closed after responses are received.\n    - Parent: **Node Execute Span**.\n    - Events added:\n        - system, user, and assistant messages from the prompt\n        - tool choice and tool result messages\n        - moderation response (when present)\n    - Useful for: Tracking model usage, prompt parameters, messages, token usage, and finish reasons.\n    - Key attributes:\n        - `gen_ai.operation.name` = `chat`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.conversation.id`\n        - `gen_ai.request.model`\n        - `gen_ai.request.temperature`\n        - `gen_ai.request.max_tokens` (when available)\n        - `gen_ai.usage.input_tokens` (when available)\n        - `gen_ai.usage.output_tokens` (when available)\n        - `gen_ai.usage.total_tokens` (when available)\n        - `gen_ai.response.finish_reasons` (Stop, ToolCalls, etc.)\n        - `error.type` (on LLM call failure)\n        - `gen_ai.response.metadata` (when `ResponseMetaInfo.metadata` is present - serialized JSON containing free-form provider or application metadata)\n\n6. **Execute Tool Span**\n    - Purpose: Execution of a tool or function call triggered by the agent or LLM.\n    - Emitted: When a tool is called. Closed after the tool returns a result or fails with an error during validation or execution.\n    - Parent: **Node Execute Span**.\n    - Useful for: Auditing tool usage, inputs and outputs, and failure causes.\n    - Key attributes:\n        - `gen_ai.tool.name`\n        - `gen_ai.tool.description`\n        - `gen_ai.tool.arguments` (when available)\n        - `gen_ai.tool.call_id` (when available)\n        - `gen_ai.tool.output` (when available)\n        - `error.type` (on failure or validation error)\n\n## Exporters\n\nExporters send collected telemetry data to an OpenTelemetry Collector or other types of destinations or backend implementations.\n\n### OTLP Exporter example\n\nThe OTLP (OpenTelemetry Protocol) exporter sends telemetry data to an OpenTelemetry Collector. This is useful for integrating with systems like Jaeger, Zipkin, or Prometheus.\n\nTo add an OpenTelemetry Exporter, use the `addSpanExporter` function to a custom exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    // The default endpoint is http://localhost:4317\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n### Logging Exporter example\n\nA logging exporter that outputs trace information to the console is included by default. This type of export is useful\nfor development and debugging purposes.\n\n```kotlin\ninstall(OpenTelemetry) {\n    // The logging exporter is added by default\n    addSpanExporter(LoggingSpanExporter.create())\n}\n```\n\n## Integration with Jaeger\n\nJaeger is a popular distributed tracing system that works with OpenTelemetry. To use Jaeger with Koog:\n\n1. Start a Jaeger container:\n```yaml\n# docker-compose.yaml\nservices:\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:1.39\n    container_name: jaeger-all-in-one\n    environment:\n      - COLLECTOR_OTLP_ENABLED=true\n    ports:\n      - \"4317:4317\"  # OTLP gRPC port\n      - \"16686:16686\"  # Jaeger UI port\n```\n\n2. Configure your agent to use the OTLP exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n3. Access the Jaeger UI at `http://localhost:16686` to view your traces.\n\n## Examples\n\nThis section includes two examples of implementing OpenTelemetry support in your agent workflow. The basic example\nincludes quick OpenTelemetry setup with minimal configuration, while the advanced example shows how you can create a\nmore elaborate and customized OpenTelemetry configuration.\n\n### Basic example\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleGoogleAIExecutor(apiKey),\n        llmModel = GoogleModels.Gemini2_0Flash,\n        systemPrompt = \"You are a code assistant. Provide concise code examples.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                setServiceInfo(\"my-otlp-agent\", \"1.0.0\")\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Create python function that prints hello world\")\n    println(result)\n    \n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\n### Advanced example with custom exporters and attributes\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                // Configure service info\n                setServiceInfo(\"advanced-agent\", \"2.0.0\")\n\n                // Add resource attributes (Map<String, Any>)\n                addResourceAttributes(mapOf(\n                    \"deployment.environment\" to \"production\",\n                    \"custom.attribute\" to \"custom-value\"\n                ))\n\n                // Add exporters (Java SDK exporter bridged via the JVM addSpanExporter)\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Tell me about OpenTelemetry\")\n    println(result)\n\n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\nFor full sampling control or other SDK-level options, build your own [`OpenTelemetry`][OpenTelemetry] instance with [`createOpenTelemetry { … }`][createOpenTelemetry] and inject it via `setSdk`.\n\n### Datadog\n\n[Datadog](https://www.datadoghq.com/) supports direct OTLP intake for LLM Observability. To send traces to Datadog:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.integration.datadog.addDatadogExporter\n\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n}\n```\n\nThe exporter reads the `DD_API_KEY` environment variable by default. You can also pass it explicitly:\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter(\n        datadogApiKey = \"your-api-key\",\n        url = \"datadoghq.eu\",                    // defaults to datadoghq.com\n        resourceAttributes = mapOf(\n            \"env\" to \"production\",\n            \"service.name\" to \"my-agent\",\n        ),\n    )\n}\n```\n\nTo send traces to Datadog and another backend at the same time, register both via `addSpanExporter` (Datadog's helper handles its own registration internally; the second exporter goes through the public API):\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n    addSpanExporter(\n        OtlpHttpSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4318/v1/traces\")\n            .build()\n    )\n}\n```\n\n## Troubleshooting\n\n### Common Issues\n\n1. **No traces appearing in Jaeger UI**\n    - Ensure the Jaeger container is running and the OTLP port (4317) is accessible\n    - Check that the OTLP exporter is configured with the correct endpoint\n    - Make sure to wait a few seconds after agent execution for traces to be exported\n\n2. **Missing spans or incomplete traces**\n    - Verify that the agent execution completes successfully\n    - Ensure that you're not closing the application too quickly after agent execution\n    - Add a delay (e.g., `TimeUnit.SECONDS.sleep(10)`) after agent execution to allow time for spans to be exported\n\n3. **Excessive number of spans**\n    - Build a custom Kotlin OTel SDK with a sampler (e.g., `parentBased(root = traceIdRatioBased(0.1))`) and inject it via `setSdk(...)`.\n","agents/agents-features/agents-features-snapshot/README.md":"# AgentCheckpoint\n\nAgentCheckpoint is a feature that provides checkpoint functionality for AI agents in the Koog Agents framework. It allows saving and restoring the state of an agent at specific points during execution, enabling powerful capabilities such as:\n\n- Resuming agent execution from a specific point\n- Rolling back to previous states\n- Persisting agent state across sessions\n\n## Core Concepts\n\n### Checkpoints\n\nA checkpoint captures the complete state of an agent at a specific point in its execution, including:\n\n- Message history (all interactions between user, system, assistant, and tools)\n- Current node being executed\n- Input data for the current node\n- Timestamp of creation\n\nCheckpoints are identified by unique IDs and are associated with a specific agent.\n\n### Storage Providers\n\nAgentCheckpoint uses storage providers to save and retrieve checkpoints. The framework includes several built-in providers:\n\n- `InMemoryAgentCheckpointStorageProvider`: Stores checkpoints in memory (lost when the application restarts)\n- `FileAgentCheckpointStorageProvider`: Persists checkpoints to the file system\n- `NoAgentCheckpointStorageProvider`: A no-op implementation (default) that doesn't store checkpoints\n\nYou can also implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface.\n\n### Continuous Persistence\n\nThe feature can be configured to automatically create checkpoints after each node execution, ensuring that the agent's state is continuously persisted and can be recovered at any point.\n\n## Installation\n\nTo use the AgentCheckpoint feature, you need to add it to your agent's configuration:\n\n```kotlin\nval agent = aiAgent {\n    // ... other configuration\n    \n    features {\n        install(AgentCheckpoint) {\n            // Configure the feature\n            snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n            continuouslyPersistent() // Optional: create checkpoints after each node\n        }\n    }\n}\n```\n\n## Configuration Options\n\nThe AgentCheckpoint feature has two main configuration options:\n\n### Storage Provider\n\nSet the storage provider that will be used to save and retrieve checkpoints:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n}\n```\n\nAvailable built-in providers:\n- `InMemoryAgentCheckpointStorageProvider`: In-memory storage (non-persistent)\n- `FileAgentCheckpointStorageProvider`: File-based storage\n- `NoAgentCheckpointStorageProvider`: No-op provider (default)\n\n### Continuous Persistence\n\nEnable automatic checkpoint creation after each node execution:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    continuouslyPersistent()\n}\n```\n\nWhen enabled, the agent will automatically create a checkpoint after each node is executed, allowing for fine-grained recovery.\n\n## Basic Usage\n\n### Creating a Checkpoint\n\nCreate a checkpoint at a specific point in your agent's execution:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Create a checkpoint with the current state\n    val checkpoint = context.checkpoint().createCheckpoint(\n        agentId = context.id,\n        agentContext = context,\n        nodeId = \"current-node-id\",\n        lastInput = inputData\n    )\n    \n    // The checkpoint ID can be stored for later use\n    val checkpointId = checkpoint.checkpointId\n}\n```\n\n### Restoring from a Checkpoint\n\nRestore an agent's state from a specific checkpoint:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase, checkpointId: String) {\n    // Roll back to a specific checkpoint\n    context.checkpoint().rollbackToCheckpoint(checkpointId, context)\n    \n    // Or roll back to the latest checkpoint\n    context.checkpoint().rollbackToLatestCheckpoint(context)\n}\n```\n\n### Using Extension Functions\n\nThe feature provides convenient extension functions for working with checkpoints:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Access the checkpoint feature\n    val checkpointFeature = context.checkpoint()\n    \n    // Or perform an action with the checkpoint feature\n    context.withCheckpoints(context) { ctx ->\n        // 'this' is the checkpoint feature\n        createCheckpoint(ctx.id, ctx, \"node-id\", inputData)\n    }\n}\n```\n\n## Advanced Usage\n\n### Custom Storage Providers\n\nYou can implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface:\n\n```kotlin\nclass MyCustomStorageProvider : AgentCheckpointStorageProvider {\n    override suspend fun getCheckpoints(agentId: String): List<AgentCheckpointData> {\n        // Implementation\n    }\n    \n    override suspend fun saveCheckpoint(agentId: String, agentCheckpointData: AgentCheckpointData) {\n        // Implementation\n    }\n    \n    override suspend fun getLatestCheckpoint(agentId: String): AgentCheckpointData? {\n        // Implementation\n    }\n}\n```\n\nThen use your custom provider in the feature configuration:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(MyCustomStorageProvider())\n}\n```\n\n### Setting Execution Points\n\nFor advanced control, you can directly set the execution point of an agent:\n\n```kotlin\nfun example(context: AIAgentContextBase) {\n    context.checkpoint().setExecutionPoint(\n        agentContext = context,\n        nodeId = \"target-node-id\",\n        messageHistory = customMessageHistory,\n        input = customInput\n    )\n}\n```\n\nThis allows for more fine-grained control over the agent's state beyond just restoring from checkpoints.\n\n## Requirements\n\nThe AgentCheckpoint feature requires that all nodes in your agent's strategy have unique names. This is enforced when the feature is installed:\n\n```kotlin\nrequire(ctx.strategy.metadata.uniqueNames) { \n    \"Checkpoint feature requires unique node names in the strategy metadata\" \n}\n```\n\nMake sure to set `uniqueNames = true` in your strategy metadata when using this feature.\n","docs/docs/agents/basic-agents.md":"# Basic agents\n\nA basic agent uses a predefined strategy with a simple execution flow that works for most common use cases.\nIt accepts a string input (a question, request, or task description) and sends this input to the configured LLM.\nThe LLM may decide to call provided tools.\nThe agent will execute the tools and send the results back to the LLM.\nThis repeats until the LLM does not request any more tool calls and returns a string response.\nThe agent then outputs this response.\n\nIn [Graph-based agents](graph-based-agents.md),\nyou can see how to re-create the predefined strategy graph used by basic agents.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\n## Create a minimal agent\n\nTo create the most basic agent, instantiate [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html)\nand provide a [prompt executor](../prompts/prompt-executors.md) with a [language model](../model-capabilities.md#creating-a-model-llmodel-configuration):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n\n    This agent will expect a string as input and return a string as output.\n    To run the agent, use the `run()` function with some user input:\n\n    ```kotlin\n    fun main() = runBlocking {\n        val result = agent.run(\"Hello! How can you help me?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-basic-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n\n    This agent expects a string as input and returns a string as output.\n    To run the agent, use the `run()` method with some user input:\n\n    ```java\n    String result = agent.run(\"Hello! How can you help me?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleBasicJava01.java -->\n\nThe agent will return a generic answer, such as:\n\n```text\nI can assist with a wide range of topics and tasks. Here are some examples:\n\n1. **Answering questions**: I can provide information on various subjects, from science and history to entertainment and culture.\n2. **Generating text**: I can help with writing tasks, such as suggesting alternative phrases, providing definitions, or even creating entire articles or stories.\n3. **Translation**: I can translate text from one language to another, including popular languages such as Spanish, French, German, Chinese, and many more.\n4. **Conversation**: I can engage in natural-sounding conversations, using context and understanding to respond to questions and statements.\n5. **Brainstorming**: I can help generate ideas for creative projects, such as writing stories, composing music, or coming up with business ideas.\n6. **Learning**: I can help with language learning, explaining grammar rules, vocabulary, and pronunciation.\n7. **Calculations**: I can perform mathematical calculations, including basic arithmetic, algebra, and more advanced math concepts.\n\nWhat's on your mind? Do you have a specific question, topic, or task you'd like to tackle?\n```\n<!--- KNIT example-basic-01.txt -->\n\n## Add a system prompt\n\nProvide a [system message](../prompts/prompt-creation/index.md#system-message) to define the agent's role\nas well as the purpose, context, and instructions related to the task.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n    <!--- KNIT example-basic-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava02.java -->\n\nThe instructions in the system prompt will guide the agent's response:\n\n```text\nI'm here to help you navigate the wild world of internet memes!\n\nWhat's on your mind? Are you trying to understand a specific meme, need help finding a popular joke, or perhaps want some recommendations for trending memes? Let me know, and I'll do my best to provide you with some LOLs!\n```\n<!--- KNIT example-basic-02.txt -->\n\n## Configure LLM output\n\nYou can provide some [LLM parameters](../llm-parameters.md#llm-parameter-reference) directly to the agent constructor \n(Kotlin) or via the builder methods (Java) to customize the behavior of the LLM.\nFor example, use the `temperature` parameter to adjust the randomness of the generated responses:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7\n    )\n    ```\n    <!--- KNIT example-basic-java-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava03 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava03.java -->\n\nHere are some response examples with different temperature values:\n\n=== \"0.4\"\n    \n    ```text\n    I'm here to help you navigate the wild world of internet memes! Whether you're looking for explanations, examples, or just want to share a meme with someone, I'm your go-to expert. What's on your mind? Got a specific meme in mind that's got you curious? Or maybe you need some meme-related advice? Fire away!\n    ```\n    <!--- KNIT example-basic-03.txt -->\n\n=== \"0.7\"\n\n    ```text\n    I'm here to help you navigate the wild world of internet memes!\n    \n    What's on your mind? Need help understanding a specific meme, finding a popular joke or trend, or maybe even creating your own meme? Let's get this meme party started!\n    ```\n    <!--- KNIT example-basic-04.txt -->\n\n=== \"1.0\"\n\n    ```text\n    I'd be happy to help you navigate the wild world of internet memes!\n    \n    Whether you're looking for explanations of classic memes, suggestions for new ones to try out, or just want to discuss your favorite meme culture trends, I'm here to assist. What's on your mind?\n    \n    Do you have a specific question about memes (e.g., \"What does this meme mean?\"), or are you looking for some meme-related recommendations (e.g., \"Can you recommend a funny meme to share with friends?\"). Let me know how I can help!\n    ```\n    <!--- KNIT example-basic-05.txt -->\n\n## Add tools\n\nAgents can use [tools](../tools/index.md) to perform specific tasks.\n\nFirst, create a tool by annotating a function (Kotlin) or method (Java) with the [`@Tool`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools.annotations/-tool/index.html) annotation:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    ```\n\n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        }\n    )\n    ```\n    <!--- KNIT example-basic-03.kt -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n    If the agent decides to ask the user a question,\n    it can call this tool that writes to `stdout` via `println()` and reads from `stdin` via `readln()`.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava04 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n    ```\n    \n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```java\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava04.java -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n\nHere is an example interaction with the agent:\n\n```text\nAgent: Which meme would you like me to explain? Please choose from: Grumpy Cat, Success Kid, or Doge.\n\nUser: Explain Doge\n\nAgent:\n\n**Doge**\n\nDoge is a popular internet meme that originated on the imageboard website 4chan in 2013. The meme features a picture of a Shiba Inu dog with a caption written in a playful, exaggerated style.\n\nThe typical format of a Doge meme includes:\n\n* A photo of a Shiba Inu dog\n* A caption written in all capital letters, using a intentionally simplistic and childlike tone\n* Exaggerated or made-up words or phrases, often used to convey a humorous or nonsensical idea\n\nExamples of Doge memes might include:\n\n* \"Such wow. Such happy.\"\n* \"I had fun today!\"\n* \"Wow, I am good at napping.\"\n\nThe meme is known for its lighthearted and playful tone, and is often used to express excitement, happiness, or silliness. The meme has since become a cultural phenomenon, with countless variations and parodies emerging online.\n```\n<!--- KNIT example-basic-06.txt -->\n\n## Adjust agent iterations\n\nTo avoid infinite loops, Koog allows any agent to take a limited number of steps (50 by default).\nUse the `maxIterations` parameter to either increase this limit if you expect the agent to require more steps\n(such as tool calls and LLM requests) or decrease it for agents that require only a few steps.\nFor example, a simple agent described here is not likely to require more than 10 steps:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-basic-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava05 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava05.java -->\n\n!!! tip\n\n    Instead of passing the model, temperature, max iterations, and other parameters directly to the Kotlin constructor \n    or Java builder, you can also define and pass them as a separate configuration object.\n    For more information, see [Agent configuration](index.md#agent-configuration).\n\n## Handle events during agent runtime\n\nTo assist with testing and debugging, as well as making hooks for chained agent interactions,\nKoog provides the [EventHandler](https://api.koog.ai/agents/agents-features/agents-features-event-handler/ai.koog.agents.features.eventHandler.feature/-event-handler/index.html) feature.\n\n=== \"Kotlin\"\n\n    Call the `handleEvents()` function inside the agent constructor lambda to install the feature and register event handlers:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.features.eventHandler.feature.handleEvents\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    ){\n        handleEvents {\n            // Handle tool calls\n            onToolCallStarting { eventContext ->\n                println(\"Tool called: ${eventContext.toolName} with args ${eventContext.toolArgs}\")\n            }\n        }\n    }\n    ```\n    <!--- KNIT example-basic-05.kt -->\n\n=== \"Java\"\n    Use the `.install()` method on the agent builder to register event handlers with `EventHandler.Feature`:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.agents.features.eventHandler.feature.EventHandler;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava06 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .install(EventHandler.Feature, config -> {\n            config.onToolCallStarting(eventContext -> {\n                System.out.println(\"Tool called: \" + eventContext.getToolName() +\n                    \" with args \" + eventContext.getToolArgs());\n            });\n        })\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava06.java -->\n\nThe agent will now output something similar to the following when it calls the `askUser` tool:\n\n```text\nTool called: askUser with args {\"question\":\"Which meme would you like me to explain?\"}\n```\n<!--- KNIT example-basic-07.txt -->\n\nFor more information about Koog agent features, see [Features](../features/index.md).\n\n## Next steps\n\n- Learn more about building [graph-based agents](graph-based-agents.md) and [functional agents](functional-agents.md)\n","docs/docs/agents/functional-agents.md":"# Functional agents\n\nWith functional agents, you implement the logic as a function that handles user input,\ninteracts with LLMs, calls tools if necessary, and produces the final output.\nCompared to [graph-based agents](graph-based-agents.md),\nthis usually means faster prototyping with the following downsides:\n\n- Not easy to visualize\n- No state persistence\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to implement a functional strategy to quickly prototype some custom logic for your agent.\n\n## Create a minimal functional agent\n\nTo create a minimal functional agent, use the same [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html) interface as for a [basic agent](basic-agents.md) and pass an \ninstance of [`AIAgentFunctionalStrategy`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent-functional-strategy/index.html) to it.\nYou can define a functional strategy that expects an input and returns an output, makes one LLM call, then returns the \ncontent of the assistant message from the response.\n\nIn Kotlin, the most convenient way is to use the `functionalStrategy {...}` DSL method. In Java, you can use the `functionalStrategy` method on the `AIAgent` builder.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val strategy = functionalStrategy<String, String> { input ->\n        val response = requestLLM(input)\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgent.run(\"What is 12 × 9?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy(\"mathStrategy\", (AIAgentFunctionalContext context, String input) -> {\n            Message.Response response = context.requestLLM(input);\n            if (response instanceof Message.Assistant) {\n                return ((Message.Assistant) response).getContent();\n            }\n            return \"\";\n        })\n        .build();\n\n    String result = mathAgent.run(\"What is 12 × 9?\");\n    System.out.println(result);\n    ```\n   <!--- KNIT example-functional-agent-java-01.java -->\n\nThe agent can produce the following output:\n\n```text\nThe answer to 12 × 9 is 108.\n```\n<!--- KNIT example-functional-agent-01.txt -->\n\n## Make sequential LLM calls\n\nYou can extend the previous strategy to make multiple sequential LLM calls:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    -->\n    ```kotlin\n    fun Message.Assistant.text(): String =\n        parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // The first LLM call produces an initial draft based on the user input\n        val draft = requestLLM(\"Draft: $input\").text()\n        // The second LLM call improves the initial draft\n        val improved = requestLLM(\"Improve and clarify.\").text()\n        // The final LLM call formats the improved text and returns the result\n        requestLLM(\"Format the result as bold.\").text()\n    }\n    ```\n    <!--- KNIT example-functional-agent-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .systemPrompt(\"You are a precise math assistant.\")\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy((AIAgentFunctionalContext context, String input) -> {\n            // The first LLM call produces an initial draft based on the user input\n            Message.Response draftResponse = context.requestLLM(\"Draft: \" + input);\n            String draft = \"\";\n            if (draftResponse instanceof Message.Assistant) {\n                draft = ((Message.Assistant) draftResponse).getContent();\n            }\n\n            // The second LLM call improves the initial draft\n            Message.Response improvedResponse = context.requestLLM(\"Improve and clarify.\");\n            String improved = \"\";\n            if (improvedResponse instanceof Message.Assistant) {\n                improved = ((Message.Assistant) improvedResponse).getContent();\n            }\n\n            // The final LLM call formats the improved text and returns the result\n            Message.Response finalResponse = context.requestLLM(\"Format the result as bold.\");\n            if (finalResponse instanceof Message.Assistant) {\n                return ((Message.Assistant) finalResponse).getContent();\n            }\n            return \"\";\n        })\n        .build();\n    ```\n    <!--- KNIT example-functional-agent-java-02.java -->\n\nThe agent can produce the following output:\n\n```text\nTo calculate the product of 12 and 9, we multiply these two numbers together.\n\n12 × 9 = **108**\n```\n<!--- KNIT example-functional-agent-02.txt -->\n\n## Add tools\n\nIn many cases, a functional agent needs to complete specific tasks,\nsuch as reading and writing data, calling APIs, or performing other deterministic operations.\nIn Koog, you expose such capabilities as [tools](../tools/index.md) and let the LLM decide when to call them.\n\nHere is what you need to do:\n\n1. Create an [annotation-based tool](../tools/annotation-based-tools.md).\n2. Add it to a tool registry and pass the registry to the agent.\n3. Make sure the agent strategy can identify tool calls in LLM responses, execute the requested tools,\n   send their results back to the LLM, and repeat the process until there are no tool calls remaining.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n\n    val toolRegistry = ToolRegistry {\n        tool(MathTools()::multiply)\n    }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // Send the user input to the LLM\n        var response = requestLLM(input)\n\n        // Only loop while the LLM requests tools\n        var toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        while (toolCalls.isNotEmpty()) {\n            // Execute the tools and return the results\n            val results = executeTools(toolCalls)\n            // Send the tool results back to the LLM. The LLM may call more tools or return a final output\n            response = sendToolResults(results)\n            toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        }\n\n        // When no tool calls remain, extract and return the assistant message content from the response\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgentWithTools = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        toolRegistry = toolRegistry,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n\n    public static void main(String[] args) {\n        MathTools mathTools = new MathTools();\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(mathTools)\n            .build();\n\n        AIAgent<String, String> mathAgentWithTools = AIAgent.builder()\n            .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n            .llmModel(OllamaModels.Meta.LLAMA_3_2)\n            .toolRegistry(toolRegistry)\n            .functionalStrategy(\"mathWithTools\", (AIAgentFunctionalContext context, String input) -> {\n                // Send the user input to the LLM\n                List<Message.Response> responses = context.requestLLMMultiple(input);\n\n                // Only loop while the LLM requests tools\n                while (context.containsToolCalls(responses)) {\n                    // Extract tool calls from the response\n                    List<MessagePart.Tool.Call> pendingCalls = context.extractToolCalls(responses);\n                    // Execute the tools and return the results\n                    List<ReceivedToolResult> results = context.executeMultipleTools(pendingCalls, false);\n                    // Send the tool results back to the LLM\n                    responses = context.sendMultipleToolResults(results);\n                }\n\n                // Extract and return the assistant message content from the response\n                Message.Response finalResponse = responses.get(0);\n                if (finalResponse instanceof Message.Assistant) {\n                    return ((Message.Assistant) finalResponse).getContent();\n                }\n                return \"\";\n            })\n            .build();\n\n        String result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\");\n        System.out.println(result);\n    }\n    ```\n   <!--- KNIT example-functional-agent-java-03.java -->\n\nThe agent can produce the following output:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nThe result of multiplying 3 by 4 is 12. Multiplying 12 by 5 gives us a final answer of 60.\n```\n<!--- KNIT example-functional-agent-03.txt -->\n\n## Next steps\n\n- Learn how to create [graph-based agents](graph-based-agents.md)\n","docs/docs/agents/graph-based-agents.md":"# Graph-based agents\n\nWith graph-based agents, you model the behavior as an explicit state machine:\nnodes of a graph strategy represent actions (LLM calls, tool execution)\nand edges represent data flow between nodes.\n\nThe main advantages of graph-based agents are:\n\n- Easy to visualize\n- State persistence\n- Composable architecture\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to re-create the strategy graph used by [basic agents](basic-agents.md).\nIt sends a request to an LLM and then either outputs the response (if the LLM responded with an assistant message)\nor executes a tool (if the LLM requested a tool call).\nIn case of a tool call, the agent sends the tool result to the LLM\nand then either outputs the response or executes a tool.\n\nHere is an illustration of the strategy graph:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph TB\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    subgraph nodeExecuteTool\n        executeTool(Execute tool call)\n    end\n    \n    subgraph nodeSendToolResult\n        sendToolResult(Request LLM)\n    end\n    \n    Input --String--> llmRequest\n    llmRequest --Message.Assistant--> onToolCalls{{onToolCalls}}\n    llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}}\n    onTextMessage --String--> Output\n    onToolCalls --ToolCalls--> executeTool --ReceivedToolResults--> sendToolResult\n    sendToolResult --Message.Assistant--> onToolCalls\n    sendToolResult --Message.Assistant--> onTextMessage\n```\n<!--- KNIT example-graph-agents-01.txt -->\n\n\n## Build a strategy graph\n\nIn Koog, you implement a strategy using [`AIAgentGraphStrategyBuilder`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/-a-i-agent-graph-strategy-builder/index.html).\nJust like every node has an input and output type,\nthe strategy as a whole also defines some input and output type.\nThis example assumes that the input and output types are strings,\nwhich means the agent implementing this strategy will expect a string and return a string.\n\nTo create a strategy, use the [`strategy()`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/strategy.html) function with two generics as the input and output types,\nprovide a unique identifier for the strategy, and define the nodes and edges.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n        \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    ```\n    <!--- KNIT example-graph-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    ```\n    <!--- KNIT exampleGraphAgentsJava01.java -->\n\nThis example uses only [predefined nodes](../nodes-and-components.md),\nbut you can also create [custom nodes](../custom-nodes.md).\n\nEvery strategy graph must have a path from `nodeStart` to `nodeFinish` connected by [edges](../custom-strategy-graphs.md#edges).\nEdges can have conditions to determine when to follow a particular edge.\nEdges can also transform the output of the previous node before passing it to the next one.\nThis is necessary to connect nodes that have non-matching output and input types.\n\nIn the previous example, `onToolCalls { true }` means that the edge will follow\nonly if the previous node returned an assistant message containing at least one tool call (`MessagePart.Tool.Call`).\n\nWhen using `onTextMessage { true }`, the edge will follow\nonly if the previous node returned an assistant message containing text parts (`MessagePart.Text`).\nThis function also extracts and joins the text content of those parts,\neffectively transforming `Message.Assistant` to `String`, because `nodeFinish` expects a string.\n\n!!! tip\n\n    Instead of `onTextMessage { true }`, you can do the following:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onMessageParts(MessagePart.Text::class) transformed { it.joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-02.kt -->\n\n    Or:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onCondition { it is Message.Assistant } transformed { (it as Message.Assistant).parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-03.kt -->\n\n## Create and run the agent\n\nLet's create an agent instance with this strategy and run it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    \n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n\n    var promptExecutor = PromptExecutor.builder()\n        .ollama(\"http://localhost:11434\")\n        .build();\n\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .build();\n\n        String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n        System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava02.java -->\n\nWhen you run this agent, it will respond with something like this:\n\n```text\nTo calculate this, I'll follow the order of operations:\n\n1. Multiply 3 by 4: 3 * 4 = 12\n2. Multiply the result by 5: 12 * 5 = 60\n3. Add 10: 60 + 10 = 70\n4. Add 123: 70 + 123 = 193\n\nThe final answer is 193.\n```\n<!--- KNIT example-graph-agents-02.txt -->\n\nHowever, since this agent doesn't have any tools, the LLM never returns a tool call \nand simply generates the whole answer.\nThis is what effectively happens:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph LR\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    Input --String--> llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}} --String--> Output\n```\n<!--- KNIT example-graph-agents-03.txt -->\n\nEven though it is correct in this case, the answer will depend on the arithmetic abilities of the underlying LLM.\nTo make sure the calculations are correct, we should provide the agent with math tools.\nThen the LLM will be able to decide to call tools that perform the calculations deterministically.\n\n## Add tools\n\nDefine [tools](../tools/index.md) for performing math operations and add them to a [ToolRegistry](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    ```\n    <!--- KNIT example-graph-agents-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOllamaAIExecutor;\n    class exampleGraphAgentsJava03 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    @LLMDescription(\"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        public int add(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n            return a + b;\n        }\n\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n    public static void main(String[] args) {\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(new MathTools())\n            .build();\n    }\n    ```\n    <!--- KNIT exampleGraphAgentsJava03.java -->\n\nAdd the tool registry to the agent configuration:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy,\n        toolRegistry = toolRegistry\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava04 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class);\n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava04.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nThe output from the first operation was multiplied by 5:\n5 * 12 = 60\n\nThen, 10 was added to the result:\n60 + 10 = 70\n\nFinally, 123 was added to the result:\n70 + 123 = 193\n```\n<!--- KNIT example-graph-agents-04.txt -->\n\nAccording to this output, the agent correctly performed the calculations, but it only called the `multiply` tool once\ninstead of calling the corresponding tool for every operation.\nWe can help the agent by describing its role and providing instructions for using appropriate tools in the system prompt.\n\n## Provide a system prompt\n\nA [system prompt](../prompts/prompt-creation/index.md#system-message) defines the agent's role and instructions for performing tasks.\nIn our example, it is important to describe how the agent should process complex multistep calculations:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        systemPrompt = \"\"\"\n                    You are a simple calculator assistant.\n                    You can add and multiply two numbers using the 'add' and 'multiply' tools.\n                    When the user provides input, extract the numbers and operations they requested.\n                    Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result.\n                    Always respond with a clear, friendly message showing the calculation and result.\n                    \"\"\".trimIndent(),\n        toolRegistry = toolRegistry,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava05 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class); \n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\"); \n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .systemPrompt(\"You are a simple calculator assistant. You can add and multiply two numbers using the 'add' and 'multiply' tools. When the user provides input, extract the numbers and operations they requested. Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result. Always respond with a clear, friendly message showing the calculation and result.\")\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava05.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nAdding 60 and 10...\nAdding 70 and 123...\nThe final result is: 193\n```\n<!--- KNIT example-graph-agents-05.txt -->\n\nAs you can see, the agent now correctly calls the appropriate tool for each operation,\nensuring that it performs the calculations deterministically instead of risking a hallucinated result.\n\n## Next steps\n\n- Compare to [functional agents](functional-agents.md) and [planner agents](planner-agents/index.md)\n- Enhance your agent by [installing features](../features/index.md)\n- Improve the predictability and reliability with [structured output](../structured-output.md)\n","docs/docs/agents/index.md":"# Agents\n\nAI agents are autonomous systems that can reason, make decisions,\ninteract with the environment, and take actions to achieve a specific goal.\nIn Koog, an AI agent is more than just a wrapper around an LLM;\nit is a structured, type-safe state machine designed for the JVM ecosystem.\n\nKoog agents are built around the following core concepts:\n\n- A [prompt executor](../prompts/prompt-executors.md) manages and executes prompts,\n  enabling the agent to interact with LLMs for reasoning and decision-making.\n- A [strategy](../nodes-and-components.md) defines the agent's workflow.\n  It can be in the form of a directed graph, a function, or a planner.\n  See [Agent types](#agent-types).\n- An agent can use [tools](../tools/index.md) to interact with external data sources and services.\n- You can extend and enhance the functionality of AI agents using [features](../features/index.md).\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\n## Agent types\n\nDepending on the task you need to perform, Koog provides several agent types:\n\n- [Basic agents](basic-agents.md) are ideal for simple tasks that don't require any custom logic.\n  These agents implement a predefined strategy that works for most common use cases.\n- [Graph-based agents](graph-based-agents.md) provide full control and flexibility of the agent's workflow, state management, and visualization.\n- [Functional agents](functional-agents.md) enable you to quickly prototype custom logic as a function with access to the agent's context.\n- [Planner agents](planner-agents/index.md) can autonomously plan and execute multistep tasks through iterative cycles until they reach a desired final state.\n\n## Agent configuration\n\nAgent configuration defines the agent's execution parameters,\nincluding the initial prompt, language model, and iteration limits.\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\nFor simple agents, besides the mandatory prompt executor and language model,\nyou can specify the initial system prompt and some other parameters directly in the agent constructor:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        temperature = 0.7,\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-agent-config-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful assistant.\")\n        .temperature(0.7)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-01.java -->\n\nAlternatively, you can create an instance of [`AIAgentConfig`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent.config/-a-i-agent-config/index.html)\nto define the agent's behavior and parameters more granularly, then pass it to the agent constructor.\nThis enables you to define complex prompts with multiple messages,\nconversation history, LLM parameters, and additional execution parameters.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.prompt.params.LLMParams\n    -->\n    ```kotlin\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\n            id = \"assistant\",\n            params = LLMParams(\n                temperature = 0.7\n            )\n        ) {\n            system(\"You are a helpful assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 10\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        agentConfig = agentConfig\n    )\n    ```\n    <!--- KNIT example-agent-config-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    Prompt prompt = Prompt.builder(\"assistant\")\n        .system(\"You are a helpful assistant.\")\n        .build()\n        .withParams(new LLMParams(\n            0.7,         // temperature\n            null,        // maxTokens\n            1,           // numberOfChoices\n            null,        // speculation\n            null,        // schema\n            LLMParams.ToolChoice.Auto.INSTANCE, // toolChoice\n            null,        // user\n            null         // additionalProperties\n        ));\n\n    AIAgentConfig agentConfig = AIAgentConfig.builder(OpenAIModels.Chat.GPT4o)\n        .prompt(prompt)\n        .maxAgentIterations(10)\n        .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .agentConfig(agentConfig)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-02.java -->\n\nHere are the parameters of `AIAgentConfig`:\n\n- `prompt` defines the initial [prompt](../prompts/prompt-creation/index.md) and [LLM parameters](../llm-parameters.md).\n\n- `model` specifies the language model with which the agent interacts.\n  You can use one of the predefined models or [create a custom model configuration](../model-capabilities.md#creating-a-model-llmodel-configuration).\n\n- `maxAgentIterations` limits the maximum number of steps the agent can take before it terminates.\n  Each step is a [node](../nodes-and-components.md) in the agent's workflow.\n\n- `missingToolsConversionStrategy` defines a strategy for handling missing tools during agent execution.\n\n[//]: # (TODO write about missing tools in the TOols section and link from here)\n\n- `responseProcessor` can be used to define a custom response processor.\n  For example, it can moderate and validate the response content, change the response format, or log the response.\n\n[//]: # (TODO write about response processing somewhere?)\n","docs/docs/agents/planner-agents/goap-agents.md":"---\nstatus: beta\n---\n\n# GOAP agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nGOAP is an algorithmic planning approach that uses [A* search] find optimal action sequences\nthat satisfy the goal conditions while minimizing the total cost.\nUnlike [LLM-based planners](llm-based-planners.md) that use an LLM to generate plans,\na GOAP agent algorithmically discovers action sequences based on predefined goals and actions.\n\nGOAP planners work with three main concepts:\n\n- **State**: Represents the current state of the world.\n- **Actions**: Define what can be done, including preconditions, effects (beliefs), costs, and execution logic.\n- **Goals**: Define target conditions, heuristic costs, and value functions.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nIn Koog, you define a GOAP agent using a DSL by declaratively specifying the goals and actions.\n\nTo create a GOAP agent, you need to:\n\n1. Define the state as a data class with properties representing various aspects specific to your goal.\n2. Create a [GOAPPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner/index.html) instance using the [goap()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/goap.html) function.\n    1. Define actions with preconditions and beliefs using the [action()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/action.html) function.\n    2. Define goals with completion conditions using the [goal()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/goal.html) function.\n3. Wrap the planner with [AIAgentPlannerStrategy](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-a-i-agent-planner-strategy/index.html) and pass it to the [PlannerAIAgent](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-planner-a-i-agent/index.html) constructor.\n\n!!! note\n\n    The planner selects individual actions and their sequence.\n    Each action includes a precondition that must hold true for the action to be executed\n    and a belief that defines the predicted outcome.\n    For more information about beliefs, see [State beliefs compared to actual execution](#state-beliefs-compared-to-actual-execution).\n\nIn the following example, GOAP handles high-level planning for creating an article\n(outline → draft → review → publish),\nwhile the LLM performs the actual content generation within each action.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.planner.goap\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Define a state for content creation\n    data class ContentState(\n        val topic: String,\n        val hasOutline: Boolean = false,\n        val outline: String = \"\",\n        val hasDraft: Boolean = false,\n        val draft: String = \"\",\n        val hasReview: Boolean = false,\n        val isPublished: Boolean = false\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = draft\n    }\n\n    // Create GOAP planner with LLM-powered actions\n    val planner = goap(\"content-planner\", ::ContentState) {\n        // Define actions with preconditions and beliefs\n        action(\n            name = \"Create outline\",\n            precondition = { state -> !state.hasOutline },\n            belief = { state -> state.copy(hasOutline = true, outline = \"Outline\") },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to create the outline\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Create a detailed outline for an article about: ${state.topic}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasOutline = true, outline = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Write draft\",\n            precondition = { state -> state.hasOutline && !state.hasDraft },\n            belief = { state -> state.copy(hasDraft = true, draft = \"Draft\") },\n            cost = { 2.0 }\n        ) { ctx, state ->\n            // Use LLM to write the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Write an article based on this outline:\\n${state.outline}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasDraft = true, draft = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Review content\",\n            precondition = { state -> state.hasDraft && !state.hasReview },\n            belief = { state -> state.copy(hasReview = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to review the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Review this article and suggest improvements:\\n${state.draft}\")\n                }\n                requestLLM()\n            }\n            println(\"Review feedback: ${response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }}\")\n            state.copy(hasReview = true)\n        }\n\n        action(\n            name = \"Publish\",\n            precondition = { state -> state.hasReview && !state.isPublished },\n            belief = { state -> state.copy(isPublished = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            println(\"Publishing article...\")\n            state.copy(isPublished = true)\n        }\n\n        // Define the goal with a completion condition\n        goal(\n            name = \"Published article\",\n            description = \"Complete and publish the article\",\n            condition = { state -> state.isPublished }\n        )\n    }\n\n    // Create and run the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"writer\") {\n            system(\"You are a professional content writer.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 20\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = planner,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        val result = agent.run(\"The Future of AI in Software Development\")\n        println(\"Final state: $result\")\n    }\n    ```\n    <!--- KNIT example-goap-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGoapAgents01 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    // Define a state for content creation\n    static class ContentState extends GoapAgentState<String, String> {\n        public String topic;\n        public boolean hasOutline = false;\n        public String outline = \"\";\n        public boolean hasDraft = false;\n        public String draft = \"\";\n        public boolean hasReview = false;\n        public boolean isPublished = false;\n    \n        public ContentState(String topic) {\n            this.topic = topic;\n        }\n\n        @Override\n        public String getAgentInput() {\n            return topic;\n        }\n\n        public ContentState copy(boolean hasOutline, String outline, boolean hasDraft,\n                                 String draft, boolean hasReview, boolean isPublished) {\n            ContentState state = new ContentState(topic);\n            state.hasOutline = hasOutline;\n            state.outline = outline;\n            state.hasDraft = hasDraft;\n            state.draft = draft;\n            state.hasReview = hasReview;\n            state.isPublished = isPublished;\n            return state;\n        }\n\n        @Override\n        public String provideOutput() {\n            return draft;\n        }\n    }\n\n    public static void main(String[] args) {\n        var promptExecutor = PromptExecutor.builder()\n            .openAI(\"OPENAI_API_KEY\")\n            .build();\n\n        var strategy = Planners.goap(\"content-planner\", ContentState::new)\n            .action(\"Create outline\", builder -> builder\n                .precondition(state -> !state.hasOutline)\n                .belief(state -> state.copy(true, \"Outline\", false, \"\", false, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Create a detailed outline for an article about: \" + state.topic);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(true, response, state.hasDraft, state.draft,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Write draft\", builder -> builder\n                .precondition(state -> state.hasOutline && !state.hasDraft)\n                .belief(state -> state.copy(state.hasOutline, state.outline, true, \"Draft\", false, false))\n                .cost(state -> 2.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Write an article based on this outline:\\n\" + state.outline);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(state.hasOutline, state.outline, true, response,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Review content\", builder -> builder\n                .precondition(state -> state.hasDraft && !state.hasReview)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, true, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Review this article and suggest improvements:\\n\" + state.draft);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    System.out.println(\"Review feedback: \" + response);\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, true, state.isPublished);\n                })\n            )\n            .action(\"Publish\", builder -> builder\n                .precondition(state -> state.hasReview && !state.isPublished)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, state.hasReview, true))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    System.out.println(\"Publishing article...\");\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, state.hasReview, true);\n                })\n            )\n            .goal(\"Published article\", builder -> builder\n                .description(\"Complete and publish the article\")\n                .condition(state -> state.isPublished)\n            )\n            .build();\n\n        var agent = AIAgent.builder()\n            .plannerStrategy(strategy)\n            .promptExecutor(promptExecutor)\n            .llmModel(OpenAIModels.Chat.GPT4o)\n            .systemPrompt(\"You are a professional content writer.\")\n            .maxIterations(20)\n            .build();\n\n        String result = agent.run(\"The Future of AI in Software Development\");\n        System.out.println(\"Final state: \" + result);\n    }\n    ```\n    <!--- KNIT exampleGoapAgentsJava01.java -->\n    \n\n## Custom cost functions\n\nAs [A* search] uses cost as a factor in finding the optimal sequence of actions,\nyou can define custom cost functions for actions and goals to guide the planner:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val operationDone: Boolean = true,\n        val hasOptimization: Boolean = true\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Expensive operation\",\n        precondition = { true },\n        belief = { state -> state.copy(operationDone = true) },\n        cost = { state ->\n            // Dynamic cost based on state\n            if (state.hasOptimization) 1.0 else 10.0\n        }\n    ) { ctx, state ->\n        // Execute action\n        state.copy(operationDone = true)\n    }\n    ```\n    <!--- KNIT example-goap-agents-02.kt -->\n\n=== \"Java\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents02 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean operationDone = false;\n            public boolean hasOptimization = true;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean operationDone) {\n                MyState state = new MyState(topic);\n                state.operationDone = operationDone;\n                state.hasOptimization = this.hasOptimization;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Expensive operation\", builder -> builder\n        .precondition(state -> true)\n        .belief(state -> state.copy(true))\n        .cost(state -> {\n            // Dynamic cost based on state\n            return state.hasOptimization ? 1.0 : 10.0;\n        })\n        .execute((context, state) -> {\n            // Execute action\n            return state.copy(true);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava02.java -->\n\n## State beliefs compared to actual execution\n\nGOAP distinguishes between the concepts of beliefs (optimistic predictions) and actual execution:\n\n- **Belief**: What the planner thinks will happen, used for planning.\n- **Execution**: What actually happens, used for real state updates.\n\nThis allows the planner to make plans based on expected outcomes while handling actual results properly:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val taskComplete: Boolean = true,\n        val attempts: Int = 0\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    fun performComplexTask(): Boolean = true\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Attempt complex task\",\n        precondition = { state -> !state.taskComplete },\n        belief = { state ->\n            // Optimistic belief: task will succeed\n            state.copy(taskComplete = true)\n        },\n        cost = { 5.0 }\n    ) { ctx, state ->\n        // Actual execution might fail or have different results\n        val success = performComplexTask()\n        state.copy(\n            taskComplete = success,\n            attempts = state.attempts + 1\n        )\n    }\n    ```\n    <!--- KNIT example-goap-agents-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents03 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean taskComplete = false;\n            public int attempts = 0;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean taskComplete, int attempts) {\n                MyState state = new MyState(topic);\n                state.taskComplete = taskComplete;\n                state.attempts = attempts;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        static boolean performComplexTask() {\n            return true;\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Attempt complex task\", builder -> builder\n        .precondition(state -> !state.taskComplete)\n        .belief(state -> {\n            // Optimistic belief: task will succeed\n            return state.copy(true, state.attempts);\n        })\n        .cost(state -> 5.0)\n        .execute((context, state) -> {\n            // Actual execution might fail or have different results\n            boolean success = performComplexTask();\n            return state.copy(success, state.attempts + 1);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava03.java -->\n\n[A* search]: https://en.wikipedia.org/wiki/A*_search_algorithm\n","docs/docs/agents/planner-agents/index.md":"---\nstatus: beta\n---\n\n# Planner agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nPlanner agents are AI agents that plan and execute multistep tasks through iterative planning cycles.\nThey continuously build or update plans, execute steps, and check completion criteria against the current state.\n\nPlanner agents are suitable for complex tasks\nthat require breaking down a high-level goal into smaller, actionable steps\nand adapting the plan based on the results of each step.\n\nWhile with [graph-based agents](../graph-based-agents.md) you define all the nodes and edges,\nfor planner agents, you only define the actions (nodes) with typed inputs and outputs.\nThe planner creates reasonable edges that would be suitable to achieve the desired state,\nand it can also update the optimal path between steps.\nThis allows for a more dynamic approach which can be more powerful \nbut less controllable compared to graph-based agents.\n\nPlanner agents operate through an iterative planning cycle:\n\n1. The planner creates or updates a plan based on the current state.\n2. The planner executes a single step from the plan, updating the state.\n3. The planner determines whether the plan is completed according to the current state.\n    - If the plan is completed, the cycle ends.\n    - If the plan is not completed, the cycle repeats from the first step.\n\n```mermaid\ngraph LR\n  A[Create or update plan] --> B[\"Execute step and update state\"]\n  B --> C[\"Check completion\"]\n  C -->|Completed| D[[Done]]\n  C -->|\"Not completed\"| A\n```\n\nKoog provides two types of planner agents:\n\n- [LLM-based planners](llm-based-planners.md) use an LLM to create and update plans\n- [GOAP agents](goap-agents.md) use a special algorithm to determine the optimal action sequences\n","docs/docs/agents/planner-agents/llm-based-planners.md":"---\nstatus: beta\n---\n\n# LLM-based planners\n\n--8<-- \"versioning-snippets.md:beta\"\n\nLLM-based planners use LLMs to generate and evaluate plans.\nThey operate on a string-based state and execute steps through LLM requests.\nString-based state means that the agent state is a single string.\nAt every step, the agent accepts an initial state string and returns the final state string as the result.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nKoog provides two simple planners:\n\n- [SimpleLLMPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-planner/index.html)\n  generates a plan only once at the very beginning and then follows the plan until it is completed.\n  To include replanning, extend `SimpleLLMPlanner` and override the `assessPlan` method,\n  indicating when the agent should replan.\n- [SimpleLLMWithCriticPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-with-critic-planner/index.html)\n  implements the `assessPlan` method that uses an LLM to check the validity of the plan via an LLM request\n  and assess whether the agent should replan.\n\nThe following example shows how to create a simple planner agent using `SimpleLLMPlanner`:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy\n    import ai.koog.agents.core.planner.PlannerAIAgent\n    import ai.koog.agents.planner.llm.SimpleLLMPlanner\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    // Create the planner\n    val planner = SimpleLLMPlanner()\n\n    // Wrap it in a planner strategy\n    val strategy = AIAgentPlannerStrategy(\n        name = \"simple-planner\",\n        planner = planner\n    )\n\n    // Configure the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"planner\") {\n            system(\"You are a helpful planning assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 50\n    )\n\n    // Create the planner agent\n    val agent = PlannerAIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = strategy,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        // Run the agent with a task\n        val result = agent.run(\"Create a plan to organize a team meeting\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-llm-based-planners-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleLLMBasedPlanner01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create the planner strategy with LLM-based planner\n    AIAgentPlannerStrategy<String, String> strategy =\n        Planners.llmBased(\"simple-planner\")\n            .build();\n\n    // Create the OpenAI executor\n    var promptExecutor = PromptExecutor.builder()\n        .openAI(\"OPENAI_API_KEY\")\n        .build();\n\n    // Create the planner agent using AIAgent builder\n    AIAgent<String, String> agent = AIAgent.builder()\n        .plannerStrategy(strategy)\n        .promptExecutor(promptExecutor)\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful planning assistant.\")\n        .maxIterations(50)\n        .build();\n\n    // Run the agent with a task\n    String result = agent.run(\"Create a plan to organize a team meeting\");\n    System.out.println(result);\n    ```\n     <!--- KNIT exampleLLMBasedPlannerJava01.java -->\n\n## Next steps\n\n- Learn about [GOAP agents](goap-agents.md)\n","docs/docs/tools/annotation-based-tools.md":"# Annotation-based tools\n\nAnnotation-based tools provide a declarative way to expose functions and methods as tools for large language models (LLMs) in both Kotlin and Java.\nBy using annotations, you can transform any function or method into a tool that LLMs can understand and use.\n\nThis approach is useful when you need to expose existing functionality to LLMs in Kotlin or Java without implementing tool descriptions manually.\n\n!!! note\n    Annotation-based tools are JVM-only and not available for other platforms. For multiplatform support, use the [class-based tool API](class-based-tools.md).\n\n## Key annotations\n\nTo start using annotation-based tools in your project, you need to understand the following key annotations:\n\n| Annotation        | Description                                                             |\n|-------------------|-------------------------------------------------------------------------|\n| `@Tool`           | Marks functions that should be exposed as tools to LLMs.                |\n| `@LLMDescription` | Provides descriptive information about your tools and their components. |\n\n\n## @Tool annotation\n\nThe `@Tool` annotation is used to mark functions (Kotlin) or methods (Java) that should be exposed as tools to LLMs.\nThe functions and methods annotated with `@Tool` are collected by reflection from objects that implement the `ToolSet` interface. For details, see [Implement the ToolSet interface](#1-implement-the-toolset-interface).\n\n### Definition\n\n```kotlin\n@Target(AnnotationTarget.FUNCTION)\npublic annotation class Tool(val customName: String = \"\")\n```\n<!--- KNIT example-annotation-based-tools-01.txt -->\n\n### Parameters\n\n| <div style=\"width:100px\">Name</div> | Required | Description                                                                              |\n|-------------------------------------|----------|------------------------------------------------------------------------------------------|\n| `customName`                        | No       | Specifies a custom name for the tool. If not provided, the name of the function is used. |\n\n### Usage\n\nTo mark a function or method as a tool, apply the `@Tool` annotation to this function or method in a class that implements the `ToolSet` interface:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    \n        @Tool(customName = \"customToolName\")\n        fun anotherTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyToolSet implements ToolSet {\n        @Tool\n        public String myTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    \n        @Tool(customName = \"customToolName\")\n        public String anotherTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-01.java -->\n\n## @LLMDescription annotation\n\nThe `@LLMDescription` annotation provides descriptive information about code elements (classes, functions, methods, parameters, and so on) to LLMs.\nThis helps LLMs understand the purpose and usage of these elements.\n\n### Definition\n\n```kotlin\n@Target(\n    AnnotationTarget.PROPERTY,\n    AnnotationTarget.CLASS,\n    AnnotationTarget.TYPE,\n    AnnotationTarget.VALUE_PARAMETER,\n    AnnotationTarget.FUNCTION\n)\npublic annotation class LLMDescription(val description: String)\n```\n<!--- KNIT example-annotation-based-tools-02.txt -->\n\n### Parameters\n\n| Name          | Required | Description                                    |\n|---------------|----------|------------------------------------------------|\n| `description` | Yes      | A string that describes the annotated element. |\n\n### Usage\n\nThe `@LLMDescription` annotation can be applied at various levels. For example:\n\n* Function level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Performs a specific operation and returns the result\")\n    fun myTool(): String {\n        // Function implementation\n        return \"Result\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-02.kt -->\n\n=== \"Java\"\n\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Performs a specific operation and returns the result\")\n    public String myTool() {\n        // Function implementation\n        return \"Result\";\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-02.java -->\n\n    \n* Parameter level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Processes input data\")\n    fun processTool(\n        @LLMDescription(\"The input data to process\")\n        input: String,\n    \n        @LLMDescription(\"Optional configuration parameters\")\n        config: String = \"\"\n    ): String {\n        // Function implementation\n        return \"Processed: $input with config: $config\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Processes input data\")\n    public String processTool(\n            @LLMDescription(description = \"The input data to process\") String input,\n            @LLMDescription(description = \"Optional configuration parameters\") String config\n    ) {\n        // Function implementation\n        return \"Processed: \" + input + \" with config: \" + config;\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-03.java -->\n\n\n## Creating a tool\n\n### 1. Implement the ToolSet interface\n\nCreate a class that implements the [`ToolSet`](api:agents-tools::ai.koog.agents.core.tools.reflect.ToolSet) interface.\nThis interface marks your class as a container for tools.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-04.java -->\n\n### 2. Add tool functions\n\nAdd functions or methods to your class and annotate them with `@Tool` to expose them as tools:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        fun getWeather(location: String): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        public String getWeather(String location) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-05.java -->\n\n### 3. Add descriptions\n\nAdd `@LLMDescription` annotations to provide context for the LLM:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for getting weather information\")\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Get the current weather for a location\")\n        fun getWeather(\n            @LLMDescription(\"The city and state/country\")\n            location: String\n        ): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for getting weather information\")\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Get the current weather for a location\")\n        public String getWeather(\n                @LLMDescription(description = \"The city and state/country\") String location\n        ) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-06.java -->\n\n### 4. Use your tools with an agent\n\nNow you can use your tools with an agent:\n\n=== \"Kotlin\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleAnnotationBasedTools06.MyFirstToolSet\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    const val apiToken = \"\"\n    -->\n    ```kotlin\n    fun main() {\n        runBlocking {\n            // Create your tool set\n            val weatherTools = MyFirstToolSet()\n    \n            // Create an agent with your tools\n    \n            val agent = AIAgent(\n                promptExecutor = simpleOpenAIExecutor(apiToken),\n                systemPrompt = \"Provide weather information for a given location.\",\n                llmModel = OpenAIModels.Chat.GPT4o,\n                toolRegistry = ToolRegistry {\n                    tools(weatherTools)\n                }\n            )\n    \n            // The agent can now use your weather tools\n            agent.run(\"What's the weather like in New York?\")\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    String apiToken = System.getenv(\"OPENAI_API_KEY\");\n\n    // Create your tool set\n     MyFirstToolSet weatherTools = new MyFirstToolSet();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(weatherTools)\n        .build();\n\n    // Create an agent with your tools\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"Provide weather information for a given location.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(toolRegistry)\n        .build();\n\n    // The agent can now use your weather tools\n    String result = agent.run(\"What's the weather like in New York?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT example-annotation-based-tools-java-07.java -->\n\n## Usage examples\n\nHere are some real-world examples of tool annotations.\n\n### Basic example: Switch controller\n\nThis example shows a simple tool set for controlling a switch:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    class Switch(private var state: Boolean) {\n        fun switch(state: Boolean) {\n            this.state = state\n        }\n        fun isOn(): Boolean {\n            return state\n        }\n    }\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for controlling a switch\")\n    class SwitchTools(val switch: Switch) : ToolSet {\n        @Tool\n        @LLMDescription(\"Switches the state of the switch\")\n        fun switch(\n            @LLMDescription(\"The state to set (true for on, false for off)\")\n            state: Boolean\n        ): String {\n            switch.switch(state)\n            return \"Switched to ${if (state) \"on\" else \"off\"}\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Returns the current state of the switch\")\n        fun switchState(): String {\n            return \"Switch is ${if (switch.isOn()) \"on\" else \"off\"}\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-08.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class Switch {\n        private boolean state;\n\n        public Switch(boolean state) {\n            this.state = state;\n        }\n\n        // \"switch\" is a reserved keyword in Java, so we use a different method name\n        public void setState(boolean state) {\n            this.state = state;\n        }\n\n        public boolean isOn() {\n            return state;\n        }\n    }\n    \n    @LLMDescription(description = \"Tools for controlling a switch\")\n    public class SwitchTools implements ToolSet {\n        private final Switch sw;\n\n        public SwitchTools(Switch sw) {\n            this.sw = sw;\n        }\n\n        @Tool\n        @LLMDescription(description = \"Switches the state of the switch\")\n        public String switchStateTo(\n                @LLMDescription(description = \"The state to set (true for on, false for off)\") boolean state\n        ) {\n            sw.setState(state);\n            return \"Switched to \" + (state ? \"on\" : \"off\");\n        }\n\n        @Tool\n        @LLMDescription(description = \"Returns the current state of the switch\")\n        public String switchState() {\n            return \"Switch is \" + (sw.isOn() ? \"on\" : \"off\");\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-08.java -->\n\nWhen an LLM needs to control a switch, it can understand the following information from the provided description:\n\n- The purpose and functionality of the tools.\n- The required parameters for using the tools.\n- The acceptable values for each parameter.\n- The expected return values upon execution.\n\n### Advanced example: Diagnostic tools\n\nThis example shows a more complex tool set for device diagnostics:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing diagnostics and troubleshooting on devices\")\n    class DiagnosticToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Run diagnostic on a device to check its status and identify any issues\")\n        fun runDiagnostic(\n            @LLMDescription(\"The ID of the device to diagnose\")\n            deviceId: String,\n    \n            @LLMDescription(\"Additional information for the diagnostic (optional)\")\n            additionalInfo: String = \"\"\n        ): String {\n            // Implementation\n            return \"Diagnostic results for device $deviceId\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Analyze an error code to determine its meaning and possible solutions\")\n        fun analyzeError(\n            @LLMDescription(\"The error code to analyze (e.g., 'E1001')\")\n            errorCode: String\n        ): String {\n            // Implementation\n            return \"Analysis of error code $errorCode\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-09.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing diagnostics and troubleshooting on devices\")\n    public class DiagnosticToolSet implements ToolSet {\n        // Convenience overload (not exposed as a tool)\n        public String runDiagnostic(String deviceId) {\n            return runDiagnostic(deviceId, \"\");\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Run diagnostic on a device to check its status and identify any issues\")\n        public String runDiagnostic(\n                @LLMDescription(description = \"The ID of the device to diagnose\") String deviceId,\n                @LLMDescription(description = \"Additional information for the diagnostic (optional)\") String additionalInfo\n        ) {\n            // Implementation\n            return \"Diagnostic results for device \" + deviceId;\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Analyze an error code to determine its meaning and possible solutions\")\n        public String analyzeError(\n                @LLMDescription(description = \"The error code to analyze (e.g., 'E1001')\") String errorCode\n        ) {\n            // Implementation\n            return \"Analysis of error code \" + errorCode;\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-09.java -->\n\n\n## Best practices\n\n* **Provide clear descriptions**: write clear, concise descriptions that explain the purpose and behavior of tools, parameters, and return values.\n* **Describe all parameters**: add `@LLMDescription` to all parameters to help LLMs understand what each parameter is for.\n* **Use consistent naming**: use consistent naming conventions for tools and parameters to make them more intuitive.\n* **Group related tools**: group related tools in the same `ToolSet` implementation and provide a class-level description.\n* **Return informative results**: make sure tool return values provide clear information about the result of the operation.\n* **Handle errors gracefully**: include error handling in your tools and return informative error messages.\n* **Document default values**: when parameters have default values (Kotlin) or overloads (Java), document this in the description.\n* **Keep tools focused**: Each tool should perform a specific, well-defined task rather than trying to do too many things.\n\n## Troubleshooting common issues\n\nWhen working with tool annotations, you might encounter some common issues.\n\n### Tools not being recognized\n\nIf the agent does not recognize your tools, check the following:\n\n- Your class implements the `ToolSet` interface.\n- All tool functions or methods are annotated with `@Tool`.\n- Tool functions or methods have appropriate return types (`String` is recommended for simplicity).\n- Your tools are properly registered with the agent.\n\n### Unclear tool descriptions\n\nIf the LLM does not use your tools correctly or misunderstands their purpose, try the following:\n\n- Use primitive parameter types when possible (`String`, `Boolean`, `Int` in Kotlin, or `String`, `boolean`, `int` in Java).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n- Note that Java doesn't support default parameters. Use method overloading instead.\n\n### Parameter type issues\n\nIf the LLM provides incorrect parameter types, try the following:\n\n- Use simple parameter types when possible (`String`, `Boolean`, `Int`).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n\n### Performance issues\n\nIf your tools cause performance problems, try the following:\n\n- Keep tool implementations lightweight.\n- For resource-intensive operations, consider implementing asynchronous processing.\n- Cache results when appropriate.\n- Log tool usage to identify bottlenecks.\n","docs/docs/tools/built-in-tools.md":"# Built-in tools\n\nKoog provides built-in tools for Kotlin and Java to help you quickly prototype and experiment with agent-user interaction.\nThese tools are not intended for production use. To use them, add `ai.koog:agents-ext` to your dependencies.\nThe following built-in tools are available:\n\n| Tool              | <div style=\"width:115px\">Name</div> | Description                                                                                                              |\n|-------------------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| SayToUser         | `__say_to_user__`                   | Lets the agent send a message to the user. It prints the agent message to the console with the `Agent says: ` prefix.    |\n| AskUser           | `__ask_user__`                      | Lets the agent ask the user for input. It prints the agent message to the console and waits for user response.           |\n| ExitTool          | `__exit__`                          | Lets the agent finish the conversation and terminate the session.                                                        |\n| ReadFileTool      | `__read_file__`                     | Reads text file with optional line range selection. Returns formatted content with metadata using 0-based line indexing. |\n| EditFileTool      | `__edit_file__`                     | Makes a single, targeted text replacement in a file; can also create new files or fully replace contents.                |\n| ListDirectoryTool | `__list_directory__`                | Lists directory contents as a hierarchical tree with optional depth control and glob filtering.                          |\n| WriteFileTool     | `__write_file__`                    | Writes text content to a file (creating parent directories if needed).                                                   |\n\n\n## Registering built-in tools\n\nLike any other tool, a built-in tool must be added to the tool registry to become available for an agent. Here is an example:\n\n<!--- INCLUDE\nimport ai.koog.agents.core.agent.AIAgent\nimport ai.koog.agents.core.tools.ToolRegistry\nimport ai.koog.agents.ext.tool.SayToUser\nimport ai.koog.agents.ext.tool.AskUser\nimport ai.koog.agents.ext.tool.ExitTool\nimport ai.koog.agents.ext.tool.file.ListDirectoryTool\nimport ai.koog.agents.ext.tool.file.ReadFileTool\nimport ai.koog.agents.ext.tool.file.WriteFileTool\nimport ai.koog.prompt.executor.clients.openai.OpenAIModels\nimport ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\nimport ai.koog.rag.base.files.JVMFileSystemProvider\n\nconst val apiToken = \"\"\n\n-->\n```kotlin\n// Create a tool registry with all built-in tools\nval toolRegistry = ToolRegistry {\n    tool(SayToUser)\n    tool(AskUser)\n    tool(ExitTool)\n    tool(ReadFileTool(JVMFileSystemProvider.ReadOnly))\n    tool(ListDirectoryTool(JVMFileSystemProvider.ReadOnly))\n    tool(WriteFileTool(JVMFileSystemProvider.ReadWrite))\n}\n\n// Pass the registry when creating an agent\nval agent = AIAgent(\n    promptExecutor = simpleOpenAIExecutor(apiToken),\n    systemPrompt = \"You are a helpful assistant.\",\n    llmModel = OpenAIModels.Chat.GPT4o,\n    toolRegistry = toolRegistry\n)\n\n```\n<!--- KNIT example-built-in-tools-01.kt -->\n\nYou can create a comprehensive set of capabilities for your agent by combining built-in tools and custom tools within the same registry in both Kotlin and Java.\nTo learn more about custom tools, see [Annotation-based tools](annotation-based-tools.md) and [Class-based tools](class-based-tools.md).\n","docs/docs/tools/class-based-tools.md":"# Class-based tools\n\nThis section explains the API designed for scenarios that require enhanced flexibility and customized behavior.\nWith this approach in Kotlin, you have full control over a tool, including its parameters, metadata, execution logic, and how it is registered and invoked. In Java, tools are created using annotation-based methods with reflection-based registration.\n\nThis level of control is ideal for creating sophisticated tools that extend basic use cases, enabling seamless integration into agent sessions and workflows.\n\nThis page describes how to implement a tool in both Kotlin and Java, manage tools through registries, call them, and use within node-based agent architectures.\n\n!!! note\n    The API is multiplatform for Kotlin. Java tools are implemented using annotation-based methods and registered via reflection. This lets you use the same tools across different platforms in Kotlin, while Java provides full JVM interoperability.\n\n## Tool implementation\n\nThe Koog framework provides the following approaches for implementing tools:\n\nFor Kotlin:\n\n* Using the base class `Tool` for all tools. You should use this class when you need to return non-text results or require complete control over the tool behavior.\n* Using the `SimpleTool` class that extends the base `Tool` class and simplifies the creation of tools that return text results. You should use this approach for scenarios where the \n  tool only needs to return a text.\n\nBoth approaches use the same core components but differ in implementation and the results they return.\n\nFor Java:\n\n* Using annotation-based methods (`@Tool` and `@LLMDescription`) with reflection-based registration. This is the recommended approach for Java interoperability, as subclassing Kotlin's `Tool` or `SimpleTool` from Java is not supported due to suspend function limitations.\n\n### Tool class (Kotlin)\n\nThe [`Tool<Args, Result>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html) abstract class is the base class for creating tools in Kotlin.\nIt lets you create tools that accept specific argument types (`Args`) and return results of various types (`Result`).\n\nEach tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the tool.                                                                                                                                                                                                                                                                                                                                                             |\n| `Result`                                 | The serializable type of result that the tool returns. If you want to present tool results in a custom format, please inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) class and implement `textForLLM(): String` method                                                                                                           |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are deserialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                                                                                                                                                        |\n| `resultSerializer`                       | The overridden variable that defines how the result of the tool is deserialized. See also [resultSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/result-serializer.html). If you chose to inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) consider using `ToolResultUtils.toTextSerializer()` |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/>- `optionalParameters` (empty by default)<br/>See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html).                                                                                                                                |\n| `execute()`                              | The function that implements the logic of the tool. It takes arguments of type `Args` and returns a result of type `Result`. See also [execute()]().                                                                                                                                                                                                                                                                                  |\n\n!!! note \"Java Implementation\"\n    In Java, instead of subclassing `Tool<Args, Result>`, use annotation-based methods with `@Tool` and `@LLMDescription`. The framework handles serialization and registration automatically through reflection. For more\n    details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` property; in Java, use `@LLMDescription` annotations.\n\n#### Usage example\n\nHere is an example of a custom tool implementation using the `Tool` class that returns a numeric result:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    -->\n    ```kotlin\n    // Implement a simple calculator tool that adds two digits\n    object CalculatorTool : Tool<CalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"calculator\",\n        description = \"A simple calculator that can add two digits (0-9).\"\n    ) {\n\n        // Arguments for the calculator tool\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        ) {\n            init {\n                require(digit1 in 0..9) { \"digit1 must be a single digit (0-9)\" }\n                require(digit2 in 0..9) { \"digit2 must be a single digit (0-9)\" }\n            }\n        }\n\n        // Function to add two digits\n        override suspend fun execute(args: Args): Int = args.digit1 + args.digit2\n    }\n    ```\n    <!--- KNIT example-class-based-tools-01.kt -->\n\nAfter implementing your tool, you need to add it to a tool registry and then use it with an agent. For details, see [Tool registry](../tools/index.md#tool-registry).\n\nFor more details, see [API reference](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html).\n\n#### Reading the agent context from a tool\n\nTools that need the agent's full state (LLM context, run id, configuration, storage, ...) extend `AgentContextAwareTool<Args, Result>` instead of `Tool<Args, Result>`. The framework injects the live `AIAgentContext` driving the call, and the tool receives it as a typed parameter rather than reading it out of the argument schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.context.AIAgentContext\n    import ai.koog.agents.core.agent.tools.AgentContextAwareTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // A tool that reads the live AIAgentContext driving the call.\n    object TracingCalculatorTool : AgentContextAwareTool<TracingCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"tracing_calculator\",\n        description = \"Adds two digits and emits a log line tagged with the agent run id.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, context: AIAgentContext): Int {\n            val runId = context.runId\n            // ... use runId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-01.kt -->\n\n`AgentContextAwareTool` is dispatched by the framework via a per-call `ToolCallMetadata` side channel that the framework manages on the tool's behalf. Invoking such a tool outside an agent run throws `IllegalStateException` because no `AIAgentContext` was injected; production code should always go through `ContextualAgentEnvironment`, and unit tests can supply the context explicitly via `ToolCallMetadata.of(AgentContextAwareTool.AgentContextKey to context)`.\n\n#### Reading raw per-call metadata\n\nA small number of tools want to read caller- or feature-contributed entries that are *not* the agent context (for example a distributed-tracing span id contributed by an observability feature). These tools extend `ToolBase<Args, Result>` directly, which exposes the full `ToolCallMetadata` bag:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolBase\n    import ai.koog.agents.core.tools.ToolCallMetadata\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    object SpanAwareCalculatorTool : ToolBase<SpanAwareCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"span_aware_calculator\",\n        description = \"Adds two digits, propagating a tracing span id from caller or feature metadata.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, metadata: ToolCallMetadata): Int {\n            val traceSpanId = metadata[\"trace.span.id\"] as? String\n            // ... use traceSpanId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-02.kt -->\n\nCallers can pass metadata through `SafeTool.execute(args, serializer, metadata)` or directly through `AIAgentEnvironment.executeTool(toolCall, metadata)`. Features can contribute metadata for every tool call during installation by calling `pipeline.provideToolCallMetadata(this) { eventContext -> mapOf(...) }`. Caller-supplied metadata always wins over feature contributions on key collision.\n\nExisting tools that extend `Tool<Args, Result>` and override `execute(args)` continue to work unchanged: the framework dispatches them through the same path and discards any `ToolCallMetadata`. To opt in to metadata, switch to `AgentContextAwareTool` (typed context access) or `ToolBase` (raw bag access).\n\n### SimpleTool class (Kotlin)\n\nThe [`SimpleTool<Args>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/index.html) abstract class extends `Tool<Args, ToolResult.Text>` and simplifies the creation of tools that return text results.\n\nEach simple tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                              |\n|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the custom tool.                                                                                                                                                                                                                         |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are serialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                             |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/> - `optionalParameters` (empty by default)<br/> See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html). |\n| `doExecute()`                            | The overridden function that describes the main action performed by the tool. It takes arguments of type `Args` and returns a `String`. See also [doExecute()](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/do-execute.html).                                          |\n\n!!! note \"Java Implementation\"\n    In Java, the equivalent approach is to use annotation-based methods that return `String`. The framework automatically handles the text result wrapping. For more details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` and constructor parameters; in Java, use `@Tool` and `@LLMDescription` annotations.\n\n#### Usage example \n\nHere is an example of a custom tool implementation using `SimpleTool` in Kotlin:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.SimpleTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // Create a tool that casts a string expression to a double value\n    object CastToDoubleTool : SimpleTool<CastToDoubleTool.Args>(\n        argsType = typeToken<Args>(),\n        name = \"cast_to_double\",\n        description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"An expression to case to double\")\n            val expression: String,\n            @property:LLMDescription(\"A comment on how to process the expression\")\n            val comment: String\n        )\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): String {\n            return \"Result: ${castToDouble(args.expression)}, \" + \"the comment was: ${args.comment}\"\n        }\n\n        // Function to cast a string expression to a double value\n        private fun castToDouble(expression: String): Double {\n            return expression.toDoubleOrNull() ?: 0.0\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-02.kt -->\n\n### Annotation-based methods (Java)\n\nTo implement tools in Java, instead of subclassing `Tool` or `SimpleTool`, use annotation-based methods with `@Tool` and\n`@LLMDescription`. Koog handles serialization and registration automatically through reflection. To learn more about the\nimplementation, see Java examples below.\n\n#### Usage examples\n\nThis is an example of a tool implementation in Java, equivalent to using the `Tool` class in Kotlin.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent: implement the tool as a Java method and register it via ToolRegistry.builder().\n    // This is the recommended Java interop path instead of subclassing the Kotlin Tool base class.\n    public final class CalculatorTool {\n        private CalculatorTool() {}\n    \n        @Tool(customName = \"calculator\")\n        @LLMDescription(description = \"A simple calculator that can add two digits (0-9).\")\n        public static int calculator(\n                @LLMDescription(description = \"The first digit to add (0-9)\") int digit1,\n                @LLMDescription(description = \"The second digit to add (0-9)\") int digit2\n        ) {\n            if (digit1 < 0 || digit1 > 9) throw new IllegalArgumentException(\"digit1 must be a single digit (0-9)\");\n            if (digit2 < 0 || digit2 > 9) throw new IllegalArgumentException(\"digit2 must be a single digit (0-9)\");\n            return digit1 + digit2;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CalculatorTool.class.getMethod(\"calculator\", int.class, int.class))\n                .build();\n        }\n    }\n    // Note: Subclassing the Kotlin Tool<TArgs, TResult> and overriding a suspend execute(...) from Java is not supported.\n    // The Java interop uses reflection-based registration of Java methods as tools.\n    ```\n    <!--- KNIT example-class-based-tools-java-01.java -->\n\nHere is an example of a tool implementation in Java, equivalent to using the `SimpleTool` class in Kotlin. This example\nimplements a simple tool that returns a text result.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent of SimpleTool: provide a Java method and register it as a tool.\n    public final class CastToDoubleTool {\n        private CastToDoubleTool() {}\n    \n        @Tool(customName = \"cast_to_double\")\n        @LLMDescription(description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\")\n        public static String castToDouble(\n                @LLMDescription(description = \"An expression to case to double\") String expression,\n                @LLMDescription(description = \"A comment on how to process the expression\") String comment\n        ) {\n            double value;\n            try {\n                value = Double.parseDouble(expression);\n            } catch (Exception e) {\n                value = 0.0;\n            }\n            return \"Result: \" + value + \", the comment was: \" + comment;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CastToDoubleTool.class.getMethod(\"castToDouble\", String.class, String.class))\n                .build();\n        }\n    }\n    // Note: Extending Kotlin SimpleTool<TArgs> from Java is not required; registering a Java method is the idiomatic approach.\n    ```\n    <!--- KNIT example-class-based-tools-java-02.java -->\n\n### Sending tool result to LLM in custom format\n\nFor Kotlin:\n\nIf you are not happy with JSON results sent to LLM (in some cases, LLMs can work better if tool output is structured as Markdown, for instance), you have to follow the following steps:\n\n1. Implement `ToolResult.TextSerializable` interface, and override `textForLLM()` method\n2. Override `resultSerializer` using `ToolResultUtils.toTextSerializer<T>()`\n\nFor Java:\n\nReturn formatted text (such as Markdown) directly as a `String` from your annotated method. The framework handles this automatically.\n\n#### Example\n\nHere is an example showing custom formatted output in both Kotlin and Java:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.prompt.markdown.markdown\n    -->\n    ```kotlin\n    // A tool that edits file\n    object EditFile : Tool<EditFile.Args, EditFile.Result>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Result>(),\n        name = \"edit_file\",\n        description = \"Edits the given file\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        public data class Args(\n            val path: String,\n            val original: String,\n            val replacement: String\n        )\n\n        @Serializable\n        public data class Result(\n            private val patchApplyResult: PatchApplyResult\n        ) {\n\n            @Serializable\n            public sealed interface PatchApplyResult {\n                @Serializable\n                public data class Success(val updatedContent: String) : PatchApplyResult\n\n                @Serializable\n                public sealed class Failure(public val reason: String) : PatchApplyResult\n            }\n\n            // Textual output (in Markdown format) that will be visible to the LLM after the tool finishes.\n            fun textForLLM(): String = markdown {\n                if (patchApplyResult is PatchApplyResult.Success) {\n                    line {\n                        bold(\"Successfully\").text(\" edited file (patch applied)\")\n                    }\n                } else {\n                    line {\n                        text(\"File was \")\n                            .bold(\"not\")\n                            .text(\" modified (patch application failed: ${(patchApplyResult as PatchApplyResult.Failure).reason})\")\n                    }\n                }\n            }\n\n            override fun toString(): String = textForLLM()\n        }\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): Result {\n            return TODO(\"Implement file edit\")\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n\n    // Java equivalent: return Markdown text directly to the LLM from a Java method and register it as a tool.\n    // This avoids needing a custom serializable Result type (which would require Kotlin serialization support).\n    public final class EditFile {\n        private EditFile() {}\n\n        @Tool(customName = \"edit_file\")\n        @LLMDescription(description = \"Edits the given file\")\n        public static String editFile(\n                String path,\n                String original,\n                String replacement\n        ) {\n            // TODO: Implement file edit logic; below is a placeholder illustrating Markdown output\n            boolean success = false;\n            if (success) {\n                return \"**Successfully** edited file (patch applied)\";\n            } else {\n                return \"File was **not** modified (patch application failed: reason)\";\n            }\n        }\n\n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(EditFile.class.getMethod(\"editFile\", String.class, String.class, String.class))\n                .build();\n        }\n    }\n    // Note: If you need a structured custom Result object from Java, you must expose a Kotlin @Serializable type\n    // or another serializer-aware type. Returning String works out-of-the-box with Koog's Java interop.\n    ```\n    <!--- KNIT example-class-based-tools-java-03.java -->\n\nAfter implementing your tool in Kotlin or Java, you need to add it to a tool registry and then use it with an agent.\nFor details, see [Tool registry](../tools/index#tool-registry).\n","docs/docs/tools/index.md":"# Overview\n\n Agents use tools to perform specific tasks or access external systems.\n\n## Tool workflow\n\nThe Koog framework offers the following workflow for working with tools in Kotlin and Java:\n\n1. Create a custom tool or use one of the built-in tools.\n2. Add the tool to a tool registry.\n3. Pass the tool registry to an agent.\n4. Use the tool with the agent.\n\n### Available tool types\n\nThere are three types of tools in the Koog framework:\n\n- Built-in tools that provide functionality for agent-user interaction and conversation management. For details, see [Built-in tools](built-in-tools.md).\n- Annotation-based custom tools that let you expose functions as tools to LLMs. For details, see [Annotation-based tools](annotation-based-tools.md).\n- Custom tools that let you control tool parameters, metadata, execution logic, and how it is registered and invoked. For details, see [Class-based\n  tools](class-based-tools.md).\n\n### Tool registry\n\nBefore you can use a tool in an agent, you must add it to a tool registry.\nThe tool registry manages all tools available to the agent.\n\nThe key features of the tool registry:\n\n- Organizes tools.\n- Supports merging of multiple tool registries.\n- Provides methods to retrieve tools by name or type.\n\nTo learn more, see [ToolRegistry](api:agents-tools::ai.koog.agents.core.tools.ToolRegistry).\n\nHere is an example of how to create the tool registry and add the tool to it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    val myTool = MyToolSet()\n    -->\n    ```kotlin\n    val toolRegistry = ToolRegistry {\n        tools(myTool)\n    }\n    ```\n    <!--- KNIT example-tools-overview-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create an instance of your ToolSet\n    MyToolSet myTool = new MyToolSet();\n\n    // Build the ToolRegistry and register tools from the ToolSet\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(myTool)\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-01.java -->\n\nTo merge multiple tool registries, do the following:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class FirstToolSet : ToolSet {\n        @Tool\n        fun firstSampleTool(): String {\n            // Tool implementation\n            return \"First result\"\n        }\n    }\n    class SecondToolSet : ToolSet {\n        @Tool\n        fun secondSampleTool(): String {\n            // Tool implementation\n            return \"Second result\"\n        }\n    }\n    val firstSampleTool = FirstToolSet()\n    val secondSampleTool = SecondToolSet()\n    -->\n    ```kotlin\n    val firstToolRegistry = ToolRegistry {\n        tools(firstSampleTool)\n    }\n    \n    val secondToolRegistry = ToolRegistry {\n        tools(secondSampleTool)\n    }\n    \n    val newRegistry = firstToolRegistry + secondToolRegistry\n    ```\n    <!--- KNIT example-tools-overview-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create instances of your ToolSets\n    FirstToolSet firstSampleTool = new FirstToolSet();\n    SecondToolSet secondSampleTool = new SecondToolSet();\n\n    // Build separate tool registries\n    ToolRegistry firstToolRegistry = ToolRegistry.builder()\n        .tools(firstSampleTool)\n        .build();\n\n    ToolRegistry secondToolRegistry = ToolRegistry.builder()\n        .tools(secondSampleTool)\n        .build();\n\n    ToolRegistry newRegistry = firstToolRegistry.plus(secondToolRegistry);\n    ```\n    <!--- KNIT example-tools-overview-java-02.java -->\n\n### Passing tools to an agent\n\nTo enable an agent to use a tool, you need to provide a tool registry that contains this tool as an argument when creating the agent:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.example.exampleToolsOverview01.toolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Agent initialization\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        systemPrompt = \"You are a helpful assistant with strong mathematical skills.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        // Pass your tool registry to the agent\n        toolRegistry = toolRegistry\n    )\n    ```\n    <!--- KNIT example-tools-overview-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are a helpful assistant with strong mathematical skills.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(ToolRegistry.builder()\n            .tools(secondSampleTool)\n            .build()\n        )\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-03.java -->\n\n### Calling tools\n\nThere are several ways to call tools within your agent code. The recommended approach is to use the provided methods\nin the agent context rather than calling tools directly, as this ensures proper handling of tool operation within the\nagent environment.\n\n!!! tip\n    Ensure you have implemented proper [error handling](../features/agent-event-handlers.md) in your tools to prevent agent failure.\n\nThe tools are called within a specific session context represented by `AIAgentLLMWriteSession`.\nIt provides several methods for calling tools so that you can:\n\n- Call a tool with the given arguments.\n- Call a tool by its name and the given arguments.\n- Call a tool by the provided tool class and arguments.\n- Call a tool of the specified type with the given arguments.\n- Call a tool that returns a raw string result.\n\nFor more details, the API reference for [AIAgentLLMWriteSession](api:agents-core::ai.koog.agents.core.agent.session.AIAgentLLMWriteSession).\n\n#### Parallel tool calls\n\nYou can also call tools in parallel using the `toParallelToolCallsRaw` extension. For example:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.builder.node\n    import ai.koog.agents.core.tools.SimpleTool\n    import kotlinx.coroutines.flow.collect\n    import kotlinx.coroutines.flow.flow\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    @Serializable\n    data class Book(\n        val title: String,\n        val author: String,\n        val description: String\n    )\n    \n    class BookTool() : SimpleTool<Book>(\n        argsType = typeToken<Book>(),\n        name = NAME,\n        description = \"A tool to parse book information from Markdown\"\n    ) {\n        companion object {\n            const val NAME = \"book\"\n        }\n    \n        override suspend fun execute(args: Book): String {\n            println(\"${args.title} by ${args.author}:\\n ${args.description}\")\n            return \"Done\"\n        }\n    }\n    \n    val strategy = strategy<Unit, Unit>(\"strategy-name\") {\n    \n        /*...*/\n    \n        val myNode by node<Unit, Unit> { _ ->\n            llm.writeSession {\n                flow {\n                    emit(Book(\"Book 1\", \"Author 1\", \"Description 1\"))\n                }.toParallelToolCallsRaw(BookTool::class).collect()\n            }\n        }\n    }\n    \n    ```\n    <!--- KNIT example-tools-overview-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-04.java -->\n\n#### Calling tools from nodes\n\nWhen building agent workflows with nodes, you can use special nodes to call tools:\n\n* **nodeExecuteTool**: calls a single tool call and returns its result. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteTool).\n\n* **nodeExecuteSingleTool** that calls a specific tool with the provided arguments. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteSingleTool).\n\n* **nodeExecuteMultipleTools** that performs multiple tool calls and returns their results. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteMultipleTools).\n\n* **nodeLLMSendToolResult** that sends a tool result to the LLM and gets a response. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendToolResult).\n\n* **nodeLLMSendMultipleToolResults** that sends multiple tool results to the LLM. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendMultipleToolResults).\n\n## Using agents as tools\n\nThe framework provides the capability to convert any AI agent into a tool that can be used by other agents. \nThis powerful feature enables you to create hierarchical agent architectures where specialized agents can be called as tools by higher-level orchestrating agents.\n\n### Converting agents to tools\n\nTo convert an agent into a tool, use the `AIAgentService` and the `createAgentTool()` extension function:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.AIAgentService\n    import ai.koog.agents.core.agent.createAgentTool\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.serialization.typeToken\n    const val apiKey = \"\"\n    val analysisToolRegistry = ToolRegistry {}\n    -->\n    ```kotlin\n    // Create a specialized agent service, responsible for creating financial analysis agents.\n    val analysisAgentService = AIAgentService(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a financial analysis specialist.\",\n        toolRegistry = analysisToolRegistry\n    )\n    \n    // Create a tool that would run financial analysis agent once called.\n    val analysisAgentTool = analysisAgentService.createAgentTool(\n        agentName = \"analyzeTransactions\",\n        agentDescription = \"Performs financial transaction analysis\",\n        inputDescription = \"Transaction analysis request\",\n        inputType = typeToken<String>(),\n    )\n    ```\n    <!--- KNIT example-tools-overview-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-05.java -->\n\n\n### Using agent tools in other agents\n\nOnce converted to a tool, you can add the agent tool to another agent's tool registry:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleToolsOverview05.analysisAgentTool\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    const val apiKey = \"\"\n    -->\n    ```kotlin\n    // Create a coordinator agent that can use specialized agents as tools\n    val coordinatorAgent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You coordinate different specialized services.\",\n        toolRegistry = ToolRegistry {\n            tool(analysisAgentTool)\n            // Add other tools as needed\n        }\n    )\n    ```\n    <!--- KNIT example-tools-overview-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-06.java -->\n\n\n### Agent tool execution\n\nWhen an agent tool is called:\n\n1. The arguments are deserialized according to the input descriptor.\n2. The wrapped agent is executed with the deserialized input.\n3. The agent's output is serialized and returned as the tool result.\n\n### Benefits of agents as tools\n\n- **Modularity**: Break complex workflows into specialized agents.\n- **Reusability**: Use the same specialized agent across multiple coordinator agents.\n- **Separation of concerns**: Each agent can focus on its specific domain.\n","docs/docs/tools/tool-descriptor-schemer.md":"# ToolDescriptorSchemer\n\n`ToolDescriptorSchemer` is an extension point that converts a `ToolDescriptor` into a JSON Schema object compatible with specific LLM providers. It can be implemented in both Kotlin and Java.\n\nKey points:\n\n- Location: `ai.koog.agents.core.tools.serialization.ToolDescriptorSchemer`\n- Contract: a single function `scheme(toolDescriptor: ToolDescriptor): JsonObject` or `generate(ToolDescriptor toolDescriptor): JsonObject` (Java)\n- Implementations provided:\n  - `OpenAICompatibleToolDescriptorSchemer` — generates schemas compatible with OpenAI‑style function/tool definitions.\n  - `OllamaToolDescriptorSchemer` — generates schemas compatible with Ollama tool JSON.\n\n<!--- INCLUDE\nimport ai.koog.agents.core.tools.ToolDescriptor\nimport ai.koog.agents.core.tools.ToolParameterDescriptor\nimport ai.koog.agents.core.tools.ToolParameterType\nimport kotlinx.serialization.json.JsonObject\n-->\n```kotlin\n// Interface\ninterface ToolDescriptorSchemaGenerator {\nfun generate(toolDescriptor: ToolDescriptor): JsonObject\n}\n```\n<!--- KNIT example-tool-descriptor-schemer-01.kt -->\n\n\n## Why use it\n\nIf you want to provide a custom scheme for existing or new LLM providers in Kotlin or Java, implement this interface to convert Koog’s `ToolDescriptor` into the expected JSON Schema format.\n\n## Implementation example\n\nBelow is a minimal custom implementation in both Kotlin and Java that renders only a subset of parameter types to illustrate how to plug into the SPI. Real implementations should cover all `ToolParameterType`s (String, Integer, Float, Boolean, Null, Enum, List, Object, AnyOf).\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.serialization.ToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    -->\n    ```kotlin\n    \n    class MinimalSchemer : ToolDescriptorSchemaGenerator {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public static class MinimalSchemer extends OpenAICompatibleToolDescriptorSchemaGenerator {\n        @Override\n        public JsonObject generate(ToolDescriptor toolDescriptor) {\n            Map<String, JsonElement> root = new LinkedHashMap<>();\n            root.put(\"type\", JsonPrimitive(\"object\"));\n\n            // properties\n            Map<String, JsonElement> props = new LinkedHashMap<>();\n            for (ToolParameterDescriptor p : concat(toolDescriptor.getRequiredParameters(), toolDescriptor.getOptionalParameters())) {\n                Map<String, JsonElement> prop = new LinkedHashMap<>();\n                prop.put(\"description\", JsonPrimitive(p.getDescription()));\n\n                ToolParameterType t = p.getType();\n                if (t == ToolParameterType.String.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                } else if (t == ToolParameterType.Integer.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"integer\"));\n                } else if (t instanceof ToolParameterType.Enum) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                    String[] entries = ((ToolParameterType.Enum) t).getEntries();\n                    List<JsonElement> enumVals = new ArrayList<>();\n                    for (String e : entries) enumVals.add(JsonPrimitive(e));\n                    prop.put(\"enum\", new JsonArray(enumVals));\n                } else {\n                    prop.put(\"type\", JsonPrimitive(\"string\")); // fallback for brevity\n                }\n\n                props.put(p.getName(), new JsonObject(prop));\n            }\n            root.put(\"properties\", new JsonObject(props));\n\n            // required array\n            List<JsonElement> required = new ArrayList<>();\n            for (ToolParameterDescriptor p : toolDescriptor.getRequiredParameters()) {\n                required.add(JsonPrimitive(p.getName()));\n            }\n            root.put(\"required\", new JsonArray(required));\n\n            return new JsonObject(root);\n        }\n\n        private static List<ToolParameterDescriptor> concat(List<ToolParameterDescriptor> a, List<ToolParameterDescriptor> b) {\n            List<ToolParameterDescriptor> res = new ArrayList<>(a.size() + b.size());\n            res.addAll(a);\n            res.addAll(b);\n            return res;\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-01.java -->\n\n## Using with a client\n\nTypically, you do not need to call a schemer directly. Koog clients accept a list of `ToolDescriptor` objects and apply the correct schemer internally when serializing requests for the provider.\n\nThe example below defines a simple tool and passes it to the OpenAI client. The client will use `OpenAICompatibleToolDescriptorSchemer` under the hood to build the JSON schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.prompt.executor.clients.openai.OpenAILLMClient\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.prompt.Prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    import kotlinx.coroutines.runBlocking\n    class MinimalSchemer : OpenAICompatibleToolDescriptorSchemaGenerator() {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    -->\n    ```kotlin\n    val client = OpenAILLMClient(apiKey = System.getenv(\"OPENAI_API_KEY\"), toolsConverter = MinimalSchemer())\n    \n    val getUserTool = ToolDescriptor(\n        name = \"get_user\",\n        description = \"Returns user profile by id\",\n        requiredParameters = listOf(\n            ToolParameterDescriptor(\n                name = \"id\",\n                description = \"User id\",\n                type = ToolParameterType.String\n            )\n        )\n    )\n    \n    val prompt = Prompt.build(id = \"p1\") { user(\"Hello\") }\n    val responses = runBlocking {\n        client.execute(\n            prompt = prompt,\n            model = OpenAIModels.Chat.GPT4o,\n            tools = listOf(getUserTool)\n        )\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Custom schemer extending the OpenAI-compatible one is Kotlin-only in the docs; for Java example we reuse MinimalSchemer from above.\n    OpenAILLMClient client = openAIClient(System.getenv(\"OPENAI_API_KEY\"), new OpenAIClientSettings(), null, null, new OpenAICompatibleToolDescriptorSchemaGenerator());\n    \n    ToolDescriptor getUserTool = new ToolDescriptor(\n        \"get_user\",\n        \"Returns user profile by id\",\n        Collections.singletonList(new ToolParameterDescriptor(\n            \"id\",\n            \"User id\",\n            ToolParameterType.String.INSTANCE\n        )),\n        Collections.emptyList()\n    );\n\n    Prompt prompt = Prompt.builder(\"p1\")\n        .user(\"Hello\")\n        .build();\n\n    List<Message.Response> responses = client.execute(prompt, OpenAIModels.Chat.GPT4o, java.util.List.of(getUserTool));\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-02.java -->\n\nIf you need direct access to the produced schema (for debugging or for a custom transport), you can instantiate the provider‑specific schemer and serialize the JSON yourself:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import kotlinx.serialization.json.Json\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    fun getUserTool(): ToolDescriptor {\n        return ToolDescriptor(\n            name = \"get_user\",\n            description = \"Returns user profile by id\",\n            requiredParameters = listOf(\n                ToolParameterDescriptor(\n                    name = \"id\",\n                    description = \"User id\",\n                    type = ToolParameterType.String\n                )\n            )\n        )\n    }\n    -->\n    \n    ```kotlin\n    val json = Json { prettyPrint = true }\n    val schema = OpenAICompatibleToolDescriptorSchemaGenerator().generate(getUserTool())\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-03.java -->\n","examples/simple-examples/src/main/kotlin/ai/koog/agents/example/a2a/README.md":"# Agent-to-Agent (A2A) Examples\n\nExamples demonstrating the A2A protocol for inter-agent communication with standardized message/task workflows, streaming responses, and artifact delivery.\n\n## Examples\n\n### Simple Joke Agent (`simplejoke/`)\n\nBasic message-based communication without tasks.\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9998)\n./gradlew runExampleSimpleJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleSimpleJokeClient\n```\n\n### Advanced Joke Agent (`advancedjoke/`)\n\nTask-based workflow with:\n- Interactive clarification (InputRequired state)\n- Artifact delivery for results\n- Graph-based agent strategy with documented nodes/edges\n- Streaming response events\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9999)\n./gradlew runExampleAdvancedJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleAdvancedJokeClient\n```\n\n## Key Patterns\n\n**Simple Agent:** `sendMessage()` → single response\n**Advanced Agent:** `sendMessageStreaming()` → Flow of events (Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent)\n\n**Task States:** Submitted → Working → InputRequired (optional) → Completed\n\nSee code comments in `JokeWriterAgentExecutor.kt` for detailed flow documentation.\n","examples/simple-examples/src/main/kotlin/ai/koog/agents/example/snapshot/sql/README.md":"# SQL Persistence Example\n\nDemonstrates using SQL databases as persistence backends for agent checkpoints through JetBrains Exposed ORM.\n\n## Quick Start\n\n1. **Start databases** (PostgreSQL/MySQL only)\n   ```bash\n   docker-compose up -d\n   ```\n\n2. **Run the examples**\n   ```bash\n   # PostgreSQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"postgres\"\n   \n   # MySQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"mysql\"\n   \n   # H2 example (embedded database)\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"h2\"\n   ```\n\n## What It Does\n\nThe example demonstrates:\n- Saving checkpoints to different SQL databases\n- Connection pooling with HikariCP (PostgreSQL/MySQL)\n- TTL-based checkpoint expiration\n- Multiple H2 database modes (in-memory, file-based, PostgreSQL-compatible)\n- Schema auto-creation\n\n## Database Schema\n\n```sql\nCREATE TABLE agent_checkpoints (\n    persistence_id VARCHAR(255),\n    checkpoint_id VARCHAR(255),\n    created_at BIGINT,\n    checkpoint_json TEXT,\n    ttl_timestamp BIGINT,\n    PRIMARY KEY (persistence_id, checkpoint_id)\n);\n```\n\n## Providers\n\n### PostgreSQL\n\n```kotlin\nPostgresPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:postgresql://localhost:5432/agents\",\n        driver = \"org.postgresql.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 3600  // Optional TTL\n)\n```\n\n### MySQL\n\n```kotlin\nMySQLPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:mysql://localhost:3306/agents\",\n        driver = \"com.mysql.cj.jdbc.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 7200\n)\n```\n\n### H2 (Embedded)\n\n```kotlin\n// In-memory (for testing)\nH2PersistenceStorageProvider.inMemory(\n    persistenceId = \"test-agent\",\n    databaseName = \"test_db\"\n)\n\n// File-based (for persistence)\nH2PersistenceStorageProvider.fileBased(\n    persistenceId = \"my-agent\",\n    filePath = \"./data/h2/agent_checkpoints\"\n)\n```\n### SQLite (Embedded)\n\n\n## Connection Pool Monitoring\n\nFor PostgreSQL/MySQL providers:\n\n```kotlin\nval stats = provider.getPoolStats()\nprintln(\"Active connections: ${stats.activeConnections}\")\nprintln(\"Pool utilization: ${stats.utilizationPercent}%\")\n```\n\n## Database UI\n\nAccess Adminer at http://localhost:8080:\n- **PostgreSQL**: Server: `postgres`, Username: `agent_user`, Password: `agent_pass`\n- **MySQL**: Server: `mysql`, Username: `agent_user`, Password: `agent_pass`\n\n## Environment Variables\n\n- `POSTGRES_URL` - Override PostgreSQL connection\n- `MYSQL_URL` - Override MySQL connection\n\n## Cleanup\n\n```bash\ndocker-compose down -v  # Stop and remove data\nrm -rf ./data          # Remove H2/SQLite files\n```\n","integration-tests/src/jvmTest/resources/skills/arithmetic-evaluator/SKILL.md":"---\nname: arithmetic-evaluator\ndescription: Evaluate arithmetic expressions and return numeric results. Use when a user asks an arithmetic question, requests calculation, or provides a math expression involving numbers, parentheses, and arithmetic operators.\n---\n\nEvaluate the arithmetic expression the user provides.\n\n1. Extract the arithmetic expression from the user request.\n2. If the expression is missing or ambiguous, ask for a clear arithmetic expression.\n3. Evaluate it by passing the expression to `scripts/evaluate_arithmetic_exp.py`\n4. Return the computed result clearly and concisely.\n\nIf script execution fails, explain that clearly and ask the user to provide a valid arithmetic expression.","integration-tests/src/jvmTest/resources/skills/weather-retrieval/SKILL.md":"---\nname: weather-retrieval\ndescription: Retrieve current weather information for a specific location. Use when a user asks for weather, temperature, conditions, forecast summary, or climate details for a city, region, or place.\n---\n\nGet weather details for the location the user provides.\n\n1. Extract the location from the user request.\n2. If the location is missing or ambiguous, ask for a clearer location name.\n3. Fetch the weather data by passing the location to `scripts/retrieve_weather.py`\n4. Parse the JSON output from the script.\n5. Return a concise summary that includes:\n    - location name\n    - temperature\n    - weather condition\n    - any notable context (for example, wind, humidity, or precipitation)\n\nIf the script fails or returns invalid data, explain that clearly and suggest trying a more specific location.\n","skills/Module.md":"# Module skills\n\nA module that provides core utilities for defining skill metadata, discovering skill content, and composing prompts.\n\n\n"},"files":{".claude/skills/add-java-code-snippets-in-docs/SKILL.md":"---\nname: add-java-code-snippets-in-docs\ndescription: Use when the user asks to add Java tabs/examples to mkdocs docs that already have Kotlin examples (files under docs/docs/**.md using === \"Kotlin\" / Knit INCLUDE+SUFFIX blocks). Handles mkdocs-material tabs, Knit directives, @JavaAPI bridge methods, and JVM factory classes.\nallowed-tools: Read, Edit, Write, Bash, Grep, Glob\n---\n\n## When to use\n\nAdding Java code blocks alongside existing Kotlin blocks in mkdocs documentation (`docs/docs/**.md`). Typical triggers: \"add Java examples to <file>\", \"add Java tabs\", \"complete the Java side of <doc>\", \"fix the Java block in <doc>\".\n\n**Do NOT use this skill for:**\n\n- Writing brand-new Kotlin docs (no precursor Kotlin block to mirror).\n- Splitting source files between JVM and non-JVM source sets — use the **`split-jvm-nonjvm`** skill instead.\n- Java examples outside the mkdocs/Knit pipeline.\n\n## Prerequisites\n\nJDK 17. Gradle 8.x fails cryptically on newer JDKs. If the default JDK is newer, prefix every `./gradlew` command in this workflow with `JAVA_HOME=/path/to/jdk-17`.\n\n## Before starting\n\nRead **one** reference doc to anchor on the established pattern:\n\n- `docs/docs/features/tracing.md` — full Tracing feature (blocks 1–6).\n- `docs/docs/agent-events.md` — custom `FeatureMessageProcessor` subclass (suspend bridge methods `handleMessage` / `handleClose`).\n- `docs/docs/testing.md` — `MockPromptExecutor` builder, tool mocking, comment-only blocks for Kotlin-only DSL.\n- `docs/docs/features/open-telemetry/index.md` — OpenTelemetry examples.\n- `docs/docs/features/open-telemetry/opentelemetry-langfuse-exporter.md` — Langfuse exporter.\n- `docs/docs/features/open-telemetry/opentelemetry-weave-exporter.md` — Weave exporter.\n\n## Workflow\n\nFor each Kotlin code block in the target `.md`:\n\n1. **Read** the Kotlin block: INCLUDE, visible code, SUFFIX, KNIT directive.\n2. **Check existing Java tab.** If absent → add one. If it has empty `/** */` placeholder content → replace entirely. If it has real content → update.\n3. **Consider Kotlin-side alignment.** If the Kotlin block uses Kotlin-only APIs (`kotlinx.io`, `MutableStateFlow`, `KLogger`, inline `Duration`), consider whether the Kotlin example should switch to a JVM `create()` factory (e.g., `TraceFeatureMessageFileWriter.create(path)`). This keeps the two examples structurally aligned. See `reference/factory-classes.md`.\n4. **Add `@JavaAPI` bridge methods if missing.** If the Java code needs a bridge that doesn't exist yet (e.g., a builder method on `MockExecutorBuilder`), add it before writing the Java tab. This may pause the doc work for a source-code change — flag it to the user before proceeding.\n5. **Write the Java tab** immediately after the Kotlin tab, using the template below.\n6. **If renaming Knit files** (e.g., hyphenated `example-feature-java-01.java` → camelCase `exampleFeatureJava01.java`), delete the old generated files first.\n7. **Generate and compile** (see Verification at the bottom).\n8. **Read the generated `.java`** to confirm correctness. Fix and repeat 7–8 on failure.\n\n## Mkdocs / Knit tab structure\n\nJava tabs go immediately after the Kotlin tab. All content inside a tab is indented 4 spaces. `<!--- INCLUDE -->`, `<!--- SUFFIX -->`, `<!--- KNIT -->` are Knit directives (HTML comments — invisible when rendered), not mkdocs syntax.\n\n```markdown\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ...\n    -->\n    ```kotlin\n    // visible code\n    ```\n    <!--- KNIT example-feature-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ...;\n    public class exampleFeatureJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // visible code\n    ```\n    <!--- KNIT exampleFeatureJava01.java -->\n```\n\n### KNIT naming — capital \"Java\" matters\n\n`docs/knit.code.include` has the check `<#if !knit.name?contains(\"Java\")>package ...`. Files with capital \"Java\" in the name skip the `package` declaration; lowercase \"java\" or hyphenated names emit a `package` line that breaks compilation.\n\n- ✅ `exampleFeatureJava01.java`, `exampleTestingJava13.java`\n- ❌ `example-feature-java-01.java`, `examplefeaturejava01.java`\n\n### Knit rules\n\n- INCLUDE + visible code + SUFFIX must concatenate into a valid Java file.\n- Hidden variables in Kotlin INCLUDE (`outputPath`, `input`, etc.) must also appear in Java INCLUDE.\n- Visible-code indentation in `.md` is 4 spaces (tab content). Knit strips that when generating.\n- Always import in INCLUDE; never use FQNs in visible code.\n- Never use file-level annotations (`@file:Suppress`, `@file:OptIn`) — scope to specific declarations.\n\n## Structural matching (the most important rule)\n\n**The Java visible code must match the Kotlin visible code in scope and structure.**\n\n- If Kotlin shows no `main()`, Java must hide `main()` in INCLUDE/SUFFIX.\n- If Kotlin shows `install(Feature) { ... }`, Java must show `.install(Feature.Feature, config -> { })`.\n- If Kotlin creates a writer before the agent, Java must too.\n- If Kotlin shows `agent.run(input)`, Java must too.\n\nRead both visible blocks side-by-side after writing. They should read as equivalent code in their respective languages.\n\n### When Kotlin uses APIs with no Java bridge\n\nSome Kotlin DSLs (graph testing: `testGraph`, `assertSubgraphByName`, `assertNodes`, `assertEdges`, `verifySubgraph`, `assertReachable`) have no `@JavaAPI` bridges. To check: grep for `@JavaAPI` in the source file — if absent, treat as Kotlin-only.\n\nWhen the Kotlin block uses an entirely Kotlin-only API:\n\n1. **Do NOT leave the Java tab empty.** Write a compilable Java class with explanatory comments.\n2. The class must have a valid `main` so it compiles.\n3. Comments explain what the Kotlin DSL does and the recommended Java alternative.\n\n```java\n// Graph structure testing (testGraph, assertSubgraphByName, assertEdges,\n// verifySubgraph, assertReachable) is available through the Kotlin testing DSL.\n//\n// In Java, test agent behavior by running and asserting on the result:\n//   String result = agent.run(\"test input\");\n//   assert \"expected\".equals(result);\n```\n\n## Deep references\n\nLoad these only when you need them:\n\n- **`reference/interop-tables.md`** — Kotlin↔Java mapping table, suspend bridges (`@JvmName(\"run\")`, `handleMessage`, `handleClose`), abstract-property rules, key source files to grep for `@JavaAPI`.\n- **`reference/factory-classes.md`** — JVM `create()` factory pattern, two-overload skeleton, type-conversion cheat sheet, when needed vs not, anti-patterns. Cross-references the `split-jvm-nonjvm` skill.\n- **`reference/pitfalls.md`** — `var`-vs-field, brace-counting in SUFFIX, `is`-prefixed property getters, `@JvmOverloads` requirement, docs classpath limits (no JUnit), legacy `/** */` placeholder, `MockExecutorBuilder` API.\n\n## Verification\n\nAfter each Java block:\n\n```bash\n./gradlew :docs:knit\n./gradlew :docs:compileJava\n```\n\nOn failure, read the generated file to diagnose:\n\n```bash\ncat docs/src/main/kotlin/exampleFeatureJavaNN.java\n```\n",".claude/skills/add-java-code-snippets-in-docs/reference/factory-classes.md":"# JVM `create()` factory classes\n\nLoad this file when the Kotlin block uses types that aren't callable from Java and you need to expose (or audit) a Java-friendly factory.\n\n## When a factory is needed\n\nKotlin constructors / APIs Java can't call cleanly:\n\n- Uses `kotlinx.io` types (`Sink`, `Path`, `SystemFileSystem`).\n- Uses inline value classes (`Duration`) — the constructor becomes private from Java.\n- Uses `KLogger` (kotlin-logging) instead of SLF4J `Logger`.\n- Has default parameters without `@JvmOverloads` — generates synthetic constructors with `DefaultConstructorMarker`.\n\nIf the Kotlin constructor uses only Java-compatible types and has `@JvmOverloads` (or no default params), Java can call it directly with `new`. Example:\n\n```java\n// No factory needed — constructor is Java-accessible\nnew TraceFeatureMessageRemoteWriter()\nnew TraceFeatureMessageRemoteWriter(connectionConfig)\n```\n\n## Pattern\n\nAdd a `companion object` with `create(...)` methods **on the class itself**, in the `jvmCommonMain` source set. The class must already live in `jvmCommonMain` (or have a JVM actual) for this to apply.\n\n> **Prerequisite:** if the class is still in `commonMain`, run the **`split-jvm-nonjvm`** skill first to produce the `expect class` + `jvmCommonMain` actual scaffolding. `split-jvm-nonjvm` requires the class to have no companion object at split time — so the order is: (1) split, then (2) add the companion `create()` on the JVM actual. If the class already has a companion in `commonMain`, raise it with the user before splitting.\n\nProvide two overloads of `create()`:\n\n1. **Kotlin-facing** — accepts the original Kotlin-only types (`kotlinx.io.Path`, `Sink`, `KLogger`, `Duration`). No JVM annotations needed; convenient sugar for Kotlin callers and keeps API symmetry.\n2. **Java-facing** — accepts only Java-compatible types (`java.nio.file.Path`, `java.io.OutputStream`, `org.slf4j.Logger`, primitives, etc.). Annotate with `@JavaAPI` (intent), `@JvmStatic` (static call site), and `@JvmOverloads` (one method per default-parameter combination). Convert to Kotlin-only types inside the body, then delegate to the primary constructor.\n\n## Skeleton\n\nSubstitute `Xxx` with the class name, `KotlinT` / `JavaT` with the actual type pair:\n\n```kotlin\n// In: jvmCommonMain/.../Xxx.kt\npublic class Xxx(/* primary constructor uses Kotlin-only types */) {\n    public companion object {\n        // (1) Kotlin-facing\n        public fun create(\n            param: KotlinT,\n            // ...defaulted params with Kotlin types\n        ): Xxx = Xxx(/* pass through */)\n\n        // (2) Java-facing\n        @JavaAPI\n        @JvmStatic\n        @JvmOverloads\n        public fun create(\n            param: JavaT,\n            // ...defaulted params with Java-friendly types\n        ): Xxx {\n            val kotlinParam = /* convert JavaT -> KotlinT */\n            return Xxx(/* pass converted values */)\n        }\n    }\n}\n```\n\nJava callers then write `Xxx.create(javaParam)` — no separate class, no `Continuation`, no `DefaultConstructorMarker`.\n\n## Conversion cheat sheet\n\n| Kotlin-only type | Java-facing replacement | Conversion inside `create()` |\n|------------------|-------------------------|------------------------------|\n| `kotlinx.io.files.Path` | `java.nio.file.Path` | `Path.of(javaPath.toString())` or use `SystemFileSystem` |\n| `kotlinx.io.Sink` | `java.io.OutputStream` (via opener lambda) | `outputStream.asSink().buffered()` |\n| `KLogger` (kotlin-logging) | `org.slf4j.Logger` | `KotlinLogging.logger(slf4jLogger)` |\n| `kotlin.time.Duration` | `long millis` / `java.time.Duration` | `millis.milliseconds` / `Duration.ofMillis(...).toKotlinDuration()` |\n| `(T) -> R` with Kotlin types | `Function<T, R>` or SAM with Java types | `{ t -> javaFn.apply(t) }` |\n\n## Reference implementations\n\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageFileWriter.kt` — `Path` / `Sink` ↔ `java.nio.file.Path` / `OutputStream`.\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageLogWriter.kt` — `KLogger` ↔ SLF4J `Logger`.\n\n## Anti-pattern: separate `*Jvm` object\n\nDo **not** introduce a separate `public object XxxJvm { ... }` alongside the original class. The convention is to put the Java-facing `create()` on the original class's companion. A `*Jvm` companion class would be redundant and inconsistent with the existing `TraceFeatureMessage*Writer` classes.\n",".claude/skills/add-java-code-snippets-in-docs/reference/interop-tables.md":"# Kotlin ↔ Java Interop Reference\n\nLoad this file when writing the Java side of a non-trivial Kotlin block: figuring out how a Kotlin construct maps to Java, or whether a `suspend` function has a Java-friendly bridge.\n\n## Quick mapping table\n\n| Kotlin | Java |\n|--------|------|\n| `AIAgent(...) { }` | `AIAgent.builder()...build()` |\n| `install(Feature) { }` | `.install(Feature.Feature, config -> { })` |\n| `install(Testing) { config() }` | `.install(Testing.Feature, config -> { })` (`ConfigureAction<T>` is a `fun interface`) |\n| `simpleOllamaAIExecutor()` | `PromptExecutor.builder().ollama().build()` |\n| `KotlinLogging.logger { }` | `LoggerFactory.getLogger(\"name\")` |\n| `val x = ...` (local) | `var x = ...` |\n| `val x = ...` (field) | `static Type x = ...` (never `static var`) |\n| `runBlocking { }` | not needed (`agent.run` is blocking in Java — see suspend bridges below) |\n| `config.addMessageProcessor(...)` | `config.addMessageProcessor(...)` |\n| `class Foo : Bar()` | `class Foo extends Bar` |\n| `when (x) { is Type -> ... }` | `if (x instanceof Type) { ... } else if ...` |\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` — see suspend bridges |\n| `override suspend fun close()` | `void handleClose()` — see suspend bridges |\n| `TraceFeatureMessageFileWriter(path, sinkOpener)` | `TraceFeatureMessageFileWriter.create(path)` — JVM `create()` overload taking `java.nio.file.Path` |\n| `TraceFeatureMessageLogWriter(klogger)` | `TraceFeatureMessageLogWriter.create(slf4jLogger)` — JVM `create()` overload taking SLF4J `Logger` |\n| `TraceFeatureMessageRemoteWriter(config)` | `new TraceFeatureMessageRemoteWriter()` (direct, no factory needed) |\n| `getMockExecutor(...) { mockLLMAnswer(...) }` | `MockPromptExecutor.builder(serializer).mockLLMAnswer(...).build()` |\n\n## Suspend functions and Java\n\nKotlin `suspend` functions compile to JVM methods with an extra `Continuation` parameter and `Object` return type. **Java code snippets must NEVER show `Continuation` parameters.** The codebase provides Java-friendly bridges.\n\n### Calling suspend methods from Java\n\nMethods annotated with `@JavaAPI` and `@JvmName` provide blocking wrappers. Use the Java-facing name directly:\n\n| Kotlin (suspend) | Java (blocking) |\n|------------------|-----------------|\n| `agent.run(input)` | `agent.run(input)` (calls `javaNonSuspendRun` via `@JvmName(\"run\")`) |\n| `processor.initialize()` | `processor.initialize()` (calls `javaNonSuspendInitialize` via `@JvmName`) |\n| `processor.onMessage(msg)` | `processor.onMessage(msg)` (calls `javaNonSuspendOnMessage` via `@JvmName`) |\n\nThese wrappers use `runBlockingIfRequired()` internally. Java code calls them like normal blocking methods.\n\n### Overriding suspend methods from Java\n\nWhen a Java class extends a Kotlin class with `suspend` methods to override, look for non-suspend bridge methods annotated with `@JavaAPI`:\n\n| Kotlin override | Java override |\n|-----------------|---------------|\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` |\n| `override suspend fun close()` | `void handleClose()` |\n\n**Why the names differ:** `@JvmName` cannot be used on `open` or `override` methods (Kotlin restriction — it would break polymorphism). So Java-facing override names can diverge from the Kotlin suspend names (`handleMessage` not `processMessage`).\n\n### Abstract properties with Kotlin types\n\nAbstract properties like `val isOpen: StateFlow<Boolean>` leak Kotlin coroutine types to Java. If the base class provides a default `open val` implementation (e.g., via `MutableStateFlow`), Java subclasses don't need to implement it. Check whether the property is `abstract` or `open` before writing the Java example.\n\n### Before writing a Java subclass example\n\n1. Check what `@JavaAPI` bridge methods exist on the class — grep for `@JavaAPI` in the `jvmCommonMain` actual.\n2. Check which members are `abstract` vs `open` — abstract ones MUST be implemented in Java.\n3. Never show `Continuation`, `Object` return types, or `Unit.INSTANCE` in Java — use the bridge methods.\n\n## `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention for boolean-like property names.\n\n## Key source files to grep\n\nWhen writing Java examples that use these classes, check the JVM actuals for available `@JavaAPI` bridge methods:\n\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/agent/AIAgent.kt` — `javaNonSuspendRun` with `@JvmName(\"run\")`.\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/feature/message/FeatureMessageProcessor.kt` — calling bridges (`javaNonSuspendInitialize`, `javaNonSuspendOnMessage`) and overriding bridges (`handleMessage`, `handleClose`).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorBuilder.kt` — Java builder for mock executor: `mockLLMAnswer()`, `mockLLMToolCall()`, `mockTool()` + inner builder classes.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockPromptExecutor.kt` — entry point: `MockPromptExecutor.builder(serializer)` with `@JvmStatic @JavaAPI`.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorDSLBuilder.kt` — Kotlin DSL builder (reference for understanding what the Java builder delegates to).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/feature/TestingFeature.kt` — `Testing.Feature` companion object (used in Java as `Testing.Feature`).\n- `agents/agents-core/src/jvmTest/java/ai/koog/agents/core/agent/JavaAPIAgentBuilderJavaTest.java` — reference Java test using `MockPromptExecutor`, `AIAgent.builder()`, and functional strategies.\n",".claude/skills/add-java-code-snippets-in-docs/reference/pitfalls.md":"# Pitfalls, testing API, and docs classpath\n\nLoad this file when something fails to compile, or when you're writing a `MockPromptExecutor`-based testing block.\n\n## Common pitfalls\n\n### `var` is only for local variables\n\nJava `var` (type inference) works only for local variables inside methods. For fields, use explicit types:\n\n```java\n// WRONG: static var logger = LoggerFactory.getLogger(\"x\");\n// RIGHT: static Logger logger = LoggerFactory.getLogger(\"x\");\n```\n\n### Inline value classes make constructors private from Java\n\nKotlin classes with `Duration`, `Duration?`, or other inline value class parameters in their constructors generate synthetic constructors with `DefaultConstructorMarker`. These are not callable from Java. Solution: add a JVM `create()` factory — see `factory-classes.md`.\n\n### Import management\n\nAlways add imports to the INCLUDE block. Never use fully-qualified names in visible code:\n\n```java\n// WRONG (in visible code): private static final org.slf4j.Logger logger = ...\n// RIGHT (in INCLUDE):      import org.slf4j.Logger;\n// RIGHT (in visible code): static Logger logger = ...\n```\n\n### `@JvmOverloads` check\n\nBefore calling a Kotlin constructor/method with default parameters from Java, verify it has `@JvmOverloads`. Without it, Java only sees the full-parameter version (or synthetic constructors with `DefaultConstructorMarker`).\n\n### SUFFIX brace counting\n\nCount opening braces in INCLUDE carefully. The SUFFIX must have exactly the matching number of closing braces. Off-by-one errors cause `Syntax error: Expecting a top level declaration` at the end of the generated file. After writing SUFFIX, mentally concatenate INCLUDE + visible code + SUFFIX and verify all braces match.\n\n### `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention.\n\n### Kotlin `expect`/`actual` classes\n\nWhen an `expect abstract class` in `commonMain` has `abstract` members, Java subclasses must implement them. Check the **JVM actual** (in `jvmCommonMain`), not the `expect` declaration — the actual may provide default implementations or bridge methods not visible in the `expect`.\n\n### Legacy `/** **/` placeholder pattern\n\nSome docs have existing Java tabs that wrap code in `/** */` block comments (making the code a Javadoc comment that trivially compiles). This is a legacy pattern — **always replace with real compilable Java code** or explanatory comments for Kotlin-only APIs.\n\n## Testing API: `MockPromptExecutor.builder`\n\nThe testing module (`agents-test`) provides Java-friendly builder APIs via `MockPromptExecutor.builder(serializer)`.\n\n### `MockExecutorBuilder` methods\n\n| Method | Description |\n|--------|-------------|\n| `mockLLMAnswer(text)` | Returns `MockLLMAnswerBuilder` — configure with `.asDefaultResponse()`, `.onRequestContains(pattern)`, `.onRequestEquals(pattern)`, `.onCondition(pred)`. |\n| `mockLLMToolCall(tool, args)` | Returns `MockLLMToolCallBuilder` — configure with `.onRequestEquals(pattern)`, `.onRequestContains(pattern)`, `.onCondition(pred)`. |\n| `mockTool(tool)` | Returns `MockToolBuilder` — configure with `.alwaysReturns(result)`, `.alwaysDoes(action)`, `.returns(result).onArguments(args)`, `.returns(result).onArgumentsMatching(pred)`. |\n| `clock(clock)` | Set custom clock. |\n| `tokenizer(tokenizer)` | Set custom tokenizer. |\n| `handleLastAssistantMessage(bool)` | Configure last-assistant-message handling. |\n| `build()` | Returns `PromptExecutor`. |\n\nAll sub-builders return `MockExecutorBuilder` for method chaining.\n\n### Full Java test pattern\n\n```java\nPromptExecutor mockLLMApi = MockPromptExecutor.builder(new JacksonSerializer())\n    .mockLLMToolCall(SayToUser.INSTANCE, new SayToUser.Args(positiveText))\n        .onRequestEquals(positiveText)\n    .mockLLMAnswer(positiveResponse).onRequestContains(positiveResponse)\n    .mockLLMAnswer(defaultText).asDefaultResponse()\n    .mockTool(SayToUser.INSTANCE).alwaysReturns(positiveResponse)\n    .build();\n\nvar agent = AIAgent.builder()\n    .promptExecutor(mockLLMApi)\n    .llmModel(OpenAIModels.Chat.GPT4o)\n    .install(Testing.Feature, config -> { })\n    .build();\n\nString result = agent.run(positiveText);\n```\n\n### Importing Kotlin objects in Java INCLUDE\n\nWhen Java code needs Kotlin objects defined in a Kotlin example's generated package, import from the generated package:\n\n```java\nimport ai.koog.agents.example.exampleTesting03.*;\n```\n\nThis gives access to tool definitions and other objects declared at the top level of the Kotlin example file.\n\n## Docs compilation environment\n\n### JDK version\n\nThe project requires JDK 17. If the default JDK is newer (e.g., 25), compilation fails with cryptic errors. Use:\n\n```bash\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:knit\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:compileJava\n```\n\n### Docs classpath constraints\n\nThe docs module has a limited classpath:\n\n- **JUnit is NOT available.** Do not use `import org.junit.jupiter.api.Assertions.*`. Use Java `assert` statements instead: `assert expected.equals(result) : \"message\";`\n- **JacksonSerializer IS available:** `import ai.koog.serialization.jackson.JacksonSerializer;`\n- **agents-core, agents-test, agents-ext ARE available:** agent builders, mock executors, `SayToUser` tool, etc.\n\nIf compilation fails with \"package X does not exist\", check `docs/build.gradle.kts` for available dependencies.\n",".claude/skills/split-jvm-nonjvm/SKILL.md":"---\nname: split-jvm-nonjvm\ndescription: |\n    Splits a class or several classes in the common source set commonMain into JVM and non-JVM parts into jvmCommonMain and nonJvmCommonMain. \n    This is used to provide some JVM-specific functionality for a class on JVM targets (e.g. Kotlin/JVM or Java backend or Android).\n---\n\nThe following example shows how to split a class into JVM and non-JVM parts.\nWhen using this skill, avoid unnecessarily looking at existing implementations of the split in the project, just follow this instruction.\nYou can, however, check existing implementations for reference if you have a complex case or otherwise stuck.\n\nHere's a sample class in a `commonMain` source set\n```kt\npackage com.example\n\n/**\n* A sample class with an external dependency, some logic and public methods\n * @param name Some public property\n * @param myRepo Some external dependency, private property\n*/\npublic class MyClass(\n    public val name: String,\n    private val myRepo: MyRepo,\n) {\n    /**\n     * Public API method\n     */\n    public fun doSomething(): String {\n        return fetchData()\n    }\n    \n    /**\n     * Private implementation logic\n     */\n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    /*\n     * Internal method\n     */\n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nIn the same package as `MyClass` in `commonMain`, create the following files. The original `MyClass.kt` file will be overwritten with the expect class (see step three).\n\nFirst, the API interface describing the contract. It should contain only public symbols from the `MyClass`.\nTake KDocs from the respective symbols in `MyClass` and add them to the interface.\n\n```kt\npackage com.example\n\n/**\n * API for [MyClass]\n */\npublic interface MyClassAPI {\n    /**\n     * Some public property\n     */\n    public val name: String\n    \n    /**\n     * Public API method\n     */\n    public fun doSomething(): String\n}\n```\n\nSecond, `MyClassImpl.kt` — implementation of the API interface providing the implementation logic. The filename matches the class name.\n\n```kt\ninternal class MyClassImpl(\n    override val name: String,\n    private val myRepo: MyRepo,\n) : MyClassAPI {\n    override fun doSomething(): String {\n        return fetchData()\n    }\n    \n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nThird, the original class becomes `expect` class\n`MyClass` should extend `MyClassAPI` and override all public symbols from `MyClassAPI`, without method bodies and property values, since `expect` classes can't have any.\nIf `MyClass` had any internal symbols, these should also be listed in the `expect` class along with their KDocs.\nIf `MyClass` had a KDoc, add it on top of the expect class without parameters/properties description.\nIf `MyClass` original KDoc described certain parameters/properties that it accepted in its constructor, add these to KDoc of the expect class secondary constructor.\nThis constructor KDoc can't have `@property` tag for parameters, use only `@param` for all parameters, even if the original was `@property`.\n\n```kt\npackage com.example\n\n/**\n * A sample class with an external dependency, some logic and public methods\n */\npublic expect class MyClass internal constructor(\n    delegate: MyClassImpl,\n) : MyClassAPI {\n    /**\n     * Constructs a new instance of [MyClass]\n     * \n     * @param name Some public property\n     * @param myRepo Some external dependency,\n     */\n    public constructor(\n        name: String,\n        myRepo: MyRepo,\n    )\n    \n    internal val delegate: MyClassImpl\n    \n    override val name: String\n    override fun doSomething(): String\n    \n    /**\n     * Internal method\n     */\n    internal fun doInternal(): String\n}\n```\n\nFourth, ensure that `jvmCommonMain` and `nonJvmCommonMain` source sets have a package directory for `MyClass`, i.e., `com.example`. If not, create missing package directories.\nThe source sets themselves are already declared in the Gradle convention plugin — you do NOT need to add them to the module's `build.gradle.kts` unless you need to add dependencies specifically for these source sets.\nThen, create `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` source sets.\n\nAfter these steps, the structure should look as follows, omitting irrelevant packages and files:\n```\ncommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n            MyClassAPI.kt\n            MyClassImpl.kt\njvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\nnonJvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n```\n\nFifth, in `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` create `actual` declarations for `MyClass`.\nInitially, they should be identical for both source sets.\nIt should use `by delegate` to delegate all public symbols from `MyClassAPI` to the implementation, and direct delegation to delegate internal symbols.\n\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    internal actual fun doInternal(): String = delegate.doInternal()\n}\n```\n\nSixth, optional, step.\nIf the users asked for certain JVM-specific/non-JVM-specific functionality, these actual classes might contain additional \npublic symbols not present in the API interface. Such additional public symbols should be documented properly and concisely with KDoc\n\nFor example, `jvmCommonMain` implementation that uses JVM-specific API\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    /**\n     * Gets environment variable value by [key]\n     */\n    public fun getEnv(key: String): String = System.getenv(key)\n}\n```\n\nSeventh, if `MyClass` had any tests in the original `commonTest` source set, use `MyClassImpl` in the test to verify common implementation.\nDo not rename the test, it should still be called e.g. `MyClassTest`.\nIf you need to test some platform-specific functionality, create tests in `jvmCommonTest` and/or `nonJvmCommonTest` source sets.\nKotlin tests for JVM-specific functionality should go to `jvmCommonTest`.\nIMPORTANT: if the test suite is written in Java, it MUST go to `jvmTest`, not `jvmCommonTest`. KMP only compiles Java code in `jvmMain`/`jvmTest` source sets.\n\nFinally, ensure that the implementation compiles by running:\n```sh\n./gradlew :mymodule:compileKotlinJvm\n./gradlew :mymodule:compileKotlinJs\n```\nReplace `:mymodule` with the actual Gradle module path derived from the file location (e.g., `agents/agents-core` → `:agents:agents-core`).\nIf both pass, the split is working correctly.\n\n### IntelliJ false positives\nDue to the experimental state of expect/actual classifiers, IntelliJ may report false positive errors such as \"no actual for expect\" or \"no expect for actual\". \nThese diagnostics should NOT be trusted. The only reliable verification is running the Gradle compile tasks listed above.\n\n## Important notes\n\n### Imports\nWhen creating the new files (API interface, Impl, expect class, actual classes), carry over all relevant imports from the original class.\n\n### Annotations\nClasses being split should not have annotations. If the original class has annotations (e.g., `@Serializable`, `@Deprecated`), ask the user explicitly how to handle them before proceeding.\n\n### Companion objects\nClasses being split should not have companion objects. If the original class has a companion object, ask the user explicitly how to handle it before proceeding.\n","AGENTS.md":"# Koog AI Agent Framework\n\nKoog is a Kotlin multiplatform framework for building AI agents with graph-based workflows.\nIt supports JVM and JS targets and integrates with multiple LLM providers\n(OpenAI, Anthropic, Google, OpenRouter, Ollama) and Model Context Protocol (MCP).\n\n## Project Structure\n\nThe project follows a modular architecture with a clear separation of concerns:\n\n```\nkoog/\n├── agents/\n│   ├── agents-core/           # Core abstractions (AIAgent, AIAgentStrategy, AIAgentEnvironment)\n│   ├── agents-tools/          # Tool infrastructure (Tool<TArgs, TResult>, ToolRegistry, AIAgentTool)\n│   ├── agents-features-*/     # Feature implementations (memory, tracing, event handling)\n│   ├── agents-mcp/           # Model Context Protocol integration\n│   └── agents-test/          # Testing utilities and framework\n├── prompt-*/                 # LLM interaction layer (executors, models, structured data)\n├── embeddings-*/             # Vector embedding support\n├── examples/                 # Reference implementations and usage patterns\n└── build.gradle.kts          # Root build configuration\n```\n\n## Build & Commands\n\n### Development Commands\n\n```bash\n# Full build including tests\n./gradlew build\n\n# Build without tests\n./gradlew assemble\n\n# Run all JVM tests\n./gradlew jvmTest\n\n# Run all JS tests  \n./gradlew jsTest\n\n# Test specific module\n./gradlew :agents:agents-core:jvmTest\n\n# Run specific test class\n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest\"\n\n# Run specific test method  \n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest.test AIAgent doesn't call tools by default\"\n\n# Compile test classes only (for faster iteration)\n./gradlew jvmTestClasses jsTestClasses\n```\n\n### Development Environment\n\n- **JDK**: 17+ required for JVM target\n- **Build System**: Gradle with version catalogs for dependency management\n- **Targets**: JVM, JavaScript (Kotlin Multiplatform), WASM\n- **IDE**: IntelliJ IDEA recommended with Kotlin plugin\n\n## Code Style\n\n- Follow [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html)\n- Use four spaces for indentation (consistent across all files)\n- Name test functions as `testXxx` (no backticks for readability)\n- Use descriptive variable and function names\n- Prefer functional programming patterns where appropriate\n- Use type-safe builders and DSLs for configuration\n- Document public APIs with KDoc comments\n- NEVER suppress compiler warnings without a good reason\n\n## Quality Gates\nRead and follow the Quality Gates section in /TESTING.md before considering any code change complete.\n\n## Architecture\n\n### Core Framework Components\n\n**AIAgent** — Main orchestrator that executes strategies in coroutine scopes, manages tools via ToolRegistry,\nruns features through AIAgentPipeline, and handles LLM communication via PromptExecutor.\n\n**AIAgentStrategy** — Graph-based execution logic that defines workflows as subgraphs with start/finish nodes,\nmanages tool selection strategy, and handles termination/error reporting.\n\n**ToolRegistry** — Centralized, type-safe tool management using a builder pattern: `ToolRegistry { tool(MyTool()) }`.\nSupports registry merging with `+` operator.\n\n**AIAgentFeature** — Extensible capabilities installed into AIAgentPipeline with configuration.\nFeatures have unique storage keys and can intercept agent lifecycle events.\n\n### Module Organization\n\n1. **agents-core**: Core abstractions (`AIAgent`, `AIAgentStrategy`, `AIAgentEnvironment`)\n2. **agents-tools**: Tool infrastructure (`Tool<TArgs, TResult>`, `ToolRegistry`, `AIAgentTool`)\n3. **agents-features-***: Feature implementations (memory, tracing, event handling)\n4. **agents-mcp**: Model Context Protocol integration\n5. **prompt-***: LLM interaction layer (executors, models, structured data)\n6. **embeddings-***: Vector embedding support\n7. **examples**: Reference implementations and usage patterns\n\n### Key Architectural Patterns\n\n- **State Machine Graphs**: Agents execute as node graphs with typed edges\n- **Feature Pipeline**: Extensible behavior via installable features with lifecycle hooks\n- **Environment Abstraction**: Safe tool execution context preventing direct tool calls\n- **Type Safety**: Generics ensure compile-time correctness for tool arguments/results\n- **Builder Patterns**: Fluent APIs for configuration throughout the framework\n\n## Testing\n\nThe framework provides comprehensive testing utilities in `agents-test` module:\n\n### LLM Response Mocking\n\n```kotlin\nval mockLLMApi = getMockExecutor(toolRegistry, eventHandler) {\n    mockLLMAnswer(\"Hello!\") onRequestContains \"Hello\"\n    mockLLMToolCall(CreateTool, CreateTool.Args(\"solve\")) onRequestEquals \"Solve task\"\n    mockLLMAnswer(\"Default response\").asDefaultResponse\n}\n```\n\n### Tool Behavior Mocking\n\n```kotlin\n// Simple return value\nmockTool(PositiveToneTool) alwaysReturns \"The text has a positive tone.\"\n\n// With additional actions\nmockTool(NegativeToneTool) alwaysTells {\n    println(\"Tool called\")\n    \"The text has a negative tone.\"\n}\n\n// Conditional responses\nmockTool(SearchTool) returns SearchTool.Result(\"Found\") onArgumentsMatching {\n    args.query.contains(\"important\")\n}\n```\n\n### Graph Structure Testing\n\n```kotlin\nAIAgent(...) {\n    withTesting()\n\n    testGraph(\"test\") {\n        val firstSubgraph = assertSubgraphByName<String, String>(\"first\")\n        val secondSubgraph = assertSubgraphByName<String, String>(\"second\")\n\n        assertEdges {\n            startNode() alwaysGoesTo firstSubgraph\n            firstSubgraph alwaysGoesTo secondSubgraph\n        }\n\n        verifySubgraph(firstSubgraph) {\n            val askLLM = assertNodeByName<String, Message.Response>(\"callLLM\")\n            assertNodes {\n                askLLM withInput \"Hello\" outputs Message.Assistant(\"Hello!\")\n            }\n        }\n    }\n}\n```\n\nFor comprehensive testing examples, see `agents/agents-test/TESTING.md`.\n\n## Security\n\n### API Key Management\n\n- **NEVER** commit API keys or secrets to the repository\n- Use environment variables for all sensitive configuration\n- Store test API keys in a local environment only\n- Required environment variables for integration tests:\n    - `ANTHROPIC_API_TEST_KEY`\n    - `GEMINI_API_TEST_KEY`\n    - `MISTRAL_AI_API_TEST_KEY`\n    - `OLLAMA_IMAGE_URL`\n    - `OPEN_AI_API_TEST_KEY`\n    - `OPEN_ROUTER_API_TEST_KEY`\n\n### Tool Execution Safety\n\n- Tools execute within controlled `AIAgentEnvironment` contexts\n- Direct tool calls are prevented outside agent execution\n- Use type-safe tool arguments to prevent injection attacks\n- Validate all external inputs in tool implementations\n\n### Dependency Security\n\n- Regularly update dependencies using Gradle version catalogs\n- Use specific version ranges to avoid supply chain attacks\n- Review dependencies for known vulnerabilities\n- Follow the principle of the least privilege in tool implementations\n\n## Configuration\n\n### Environment Setup\n\nSet environment variables for integration testing (never commit API keys):\n\n```bash\n# Export in your shell or IDE run configuration\nexport ANTHROPIC_API_TEST_KEY=your_key_here\nexport DEEPSEEK_API_TEST_KEY=your_key_here\nexport GEMINI_API_TEST_KEY=your_key_here\nexport MISTRAL_AI_API_TEST_KEY=your_key_here\nexport OLLAMA_IMAGE_URL=http://localhost:11434\nexport OPEN_AI_API_TEST_KEY=your_key_here\nexport OPEN_ROUTER_API_TEST_KEY=your_key_here\n\n# Or add to ~/.bashrc, ~/.zshrc, or IDE environment variables\n```\n\n### Gradle Configuration\n\n- Uses version catalogs (`gradle/libs.versions.toml`) for dependency management\n- Multiplatform configuration in `build.gradle.kts`\n- Test configuration supports both JVM and JS targets\n\n### Development Environment Requirements\n\n- **JDK**: 17+ (OpenJDK recommended)\n- **IDE**: IntelliJ IDEA with Kotlin Multiplatform plugin\n- **Optional**: Docker for Ollama local testing\n\n## Development Workflow\n\n### Branch Strategy\n\n- **develop**: All development (features and bug fixes)\n- **main**: Released versions only\n- Base all PRs against `develop` branch\n- Use descriptive branch names: `feature/agent-memory`, `fix/tool-registry-bug`\n\n### Code Quality\n\n- **ALWAYS** run `./gradlew build` before submitting PRs\n- Ensure all tests pass on JVM, JS, WASM targets\n- Follow established patterns in existing code\n- Add tests for new functionality\n- Update documentation for API changes\n\n### Commit Guidelines\n\n- Use conventional commit format: `feat:`, `fix:`, `docs:`, `test:`\n- Include issue references where applicable\n- Keep commits focused and atomic\n","CLAUDE.md":"AGENTS.md","a2a/CLAUDE.md":"# A2A Module Development Guidelines\n\n## Module Overview\n\nThe A2A (Agent-to-Agent) module is a **meta-module** within the larger Koog project that implements a comprehensive client and server library for the A2A protocol, a standardized communication protocol for AI agents based on the specification at https://a2a-protocol.org/latest/specification/.\n\n**Important**: Since this is a meta-module inside a bigger root project, the Gradle wrapper executable is located one directory up. All Gradle commands must use `../gradlew` when running from the a2a directory.\n\n### Purpose\n- **Agent Discovery**: Through AgentCard manifests describing agent capabilities and interfaces\n- **Message Exchange**: Standardized message format with support for different content types\n- **Task Management**: Long-running operations with state tracking and lifecycle management\n- **Push Notifications**: Asynchronous notifications for task updates\n- **Multiple Transport Protocols**: JSON-RPC, HTTP+JSON/REST with extensibility for gRPC\n- **Authentication & Security**: OpenAPI 3.0 compatible security schemes\n\n### Submodules Architecture\n\n1. **a2a-core**: Core abstractions, data models, and transport interfaces\n   - AgentCard, Message, Task, Event hierarchy\n   - Transport interfaces: ClientTransport, ServerTransport, RequestHandler\n   - No external dependencies beyond Kotlin stdlib and serialization\n\n2. **a2a-client**: High-level client library for communicating with A2A servers\n   - A2AClient wrapper, AgentCardResolver\n   - Capability validation, request/response handling\n   - Depends on: a2a-core, Ktor HTTP client\n\n3. **a2a-server**: Server-side implementation for hosting A2A agents\n   - A2AServer, AgentExecutor interface, session management\n   - Storage abstractions with in-memory implementations\n   - Depends on: a2a-core, coroutines, logging\n\n4. **a2a-transport**: Multiple transport protocol implementations\n   - a2a-transport-core-jsonrpc: JSON-RPC protocol base classes\n   - a2a-transport-client-jsonrpc-http: HTTP JSON-RPC client transport\n   - a2a-transport-server-jsonrpc-http: HTTP JSON-RPC server transport\n   - a2a-transport-*-rest: HTTP+JSON/REST protocol implementations\n\n## Technologies & Libraries\n\n### Core Dependencies (from gradle/libs.versions.toml)\n- **Kotlin Multiplatform**\n- **kotlinx-serialization**: JSON serialization for protocol messages\n- **kotlinx-coroutines**: Async/concurrent programming, Flow APIs\n- **kotlinx-datetime**: Timestamp handling in protocol messages\n- **ktor3**: HTTP client/server for transport implementations\n- **oshai-kotlin-logging**: Structured logging\n\n### Testing Libraries\n- **kotlin-test**: Multiplatform test framework\n- **kotest-assertions**: Rich assertion library for complex objects\n- **kotlinx-coroutines-test**: Testing coroutines with runTest\n- **testcontainers**: Docker-based integration testing\n- **logback-classic**: Runtime logging for tests\n\n### Platform Support\n- **JVM**: Full server and client support\n- **JS (Browser)**: Client-only support\n- **Future platforms**: Architecture ready for native support\n\n## Development Guidelines\n\n### Architecture Decisions\n⚠️ **CRITICAL**: Never make design and architecture decisions independently. Always ask the user for confirmation before:\n- Adding new transport protocols\n- Changing storage interfaces\n- Modifying core protocol message formats\n- Adding new authentication schemes\n- Changing session management behavior\n- Doing other sorts of major architectural changes\n\n### Code Style Conventions\n\n#### API Visibility\n- Use `explicitApi()` - all public APIs must have explicit visibility modifiers\n- Prefer `public` declarations for APIs, `internal` for implementation details\n- Use `@InternalA2AApi` annotation for APIs that are public but internal to the module\n\n#### Naming Conventions\n- Classes: PascalCase (`A2AServer`, `SessionManager`, `AgentExecutor`)\n- Interfaces: Same as classes, often ending in -or/-er for behaviors (`AgentExecutor`)\n- Functions: camelCase with descriptive names (`onSendMessage`, `getByContext`)\n- Properties: camelCase (`agentCard`, `sessionMutex`)\n\n#### Async Patterns\n- Use `suspend` functions for all async operations\n- Prefer `Flow` for streaming APIs (task events, message streams)\n- Use `RWLock` pattern: `rwLock.withReadLock/withWriteLock` for concurrent access\n- Use `Mutex` for single-threaded critical sections: `mutex.withLock`\n\n#### Error Handling\n- Use domain-specific exceptions (`A2AInternalErrorException`, `TaskOperationException`)\n- Propagate errors properly in async contexts\n- Include contextual information in exception messages\n\n#### KDoc Documentation\n- **Placement**: KDoc directly above declarations (classes, functions, properties)\n- **Constructor properties**: Document using `@param` tags in class KDoc\n- **Public class properties**: Document using `@property` tags in class KDoc\n- **Cross-references**: Use `[ClassName]`, `[ClassName.propertyName]` syntax for linking components\n- **Examples**: Include practical code examples for complex APIs\n- **Validation rules**: Document constraints with bullet points and clear explanations\n- **Exception documentation**: Use `@throws` with specific conditions\n- **Required**: All public APIs must have KDoc (enforced by `explicitApi()`)\n\n## Testing Requirements\n\n### Mandatory Test Execution\n⚠️ **ALWAYS** run the specific test suite using Gradle's jvmTest task to ensure changes work correctly:\n\n```bash\n# Run all tests in a specific module\n../gradlew :a2a:a2a-server:jvmTest\n\n# Run a specific test class\n../gradlew :a2a:a2a-server:jvmTest --tests \"ai.koog.a2a.server.tasks.InMemoryTaskStorageTest\"\n\n# Run a specific test method\n../gradlew :a2a:a2a-client:jvmTest --tests \"ai.koog.a2a.client.A2AClientIntegrationTest.test get agent card\"\n```\n\n### Testing Approach: Crucial Minimum\n- **Focus on core functionality** - test essential behavior, not implementation details\n- **Separate dedicated tests** - one test method per core scenario/edge case\n- **Avoid verbose, unnecessary tests** - don't test trivial getters/setters\n- **Test error conditions** - verify proper exception handling and error states\n\n#### Preferred Assertion Pattern\n**✅ DO**: Assert on whole objects when possible (especially data classes):\n```kotlin\nassertEquals(expectedTask, actualTask)  // Compares all properties\nassertEquals(listOf(expectedArtifact), retrieved?.artifacts)\n```\n\n**❌ AVOID**: Bunch of individual field assertions unless necessary:\n```kotlin\n// Avoid this pattern:\nassertEquals(expected.id, actual.id)\nassertEquals(expected.contextId, actual.contextId)\nassertEquals(expected.status, actual.status)\n// ... many more individual assertions\n```\n\n### Integration Tests\n- **Docker-based testing**: a2a-client uses testcontainers with Python A2A server\n- **Docker build dependency**: Integration tests automatically build required containers\n- **Test isolation**: Each test should be independent and clean up after itself\n\n### Test Structure Examples\n\n**Unit Tests** (InMemoryTaskStorageTest pattern):\n```kotlin\nclass InMemoryTaskStorageTest {\n    private lateinit var storage: InMemoryTaskStorage\n\n    @BeforeTest\n    fun setUp() {\n        storage = InMemoryTaskStorage()\n    }\n\n    @Test\n    fun testStoreAndRetrieveTask() = runTest {\n        val task = createTask(id = \"task-1\", contextId = \"context-1\")\n        storage.update(task)\n        val retrieved = storage.get(\"task-1\")\n\n        assertNotNull(retrieved)\n        assertEquals(task, retrieved)  // Whole object assertion\n    }\n}\n```\n\n**Integration Tests** (A2AClientIntegrationTest pattern):\n```kotlin\n@Testcontainers\nclass A2AClientIntegrationTest {\n    @Container\n    val testA2AServer = GenericContainer(\"test-python-a2a-server\")\n        .withExposedPorts(9999)\n        .waitingFor(Wait.forListeningPort())\n\n    @Test\n    fun `test get agent card`() = runTest {\n        val agentCard = client.getAgentCard()\n        val expectedAgentCard = AgentCard(...)\n        assertEquals(expectedAgentCard, agentCard)  // Full object comparison\n    }\n}\n```\n\n## Implementation Patterns\n\n### Transport Layer Abstraction\n- Implement `ClientTransport` interface for new client protocols\n- Implement `ServerTransport` and `RequestHandler` for new server protocols\n- Keep protocol logic separate from transport mechanism\n- Use suspend functions for all transport operations\n\n### Storage Interface Pattern\n```kotlin\npublic interface SomeStorage {\n    public suspend fun save(item: Item)\n    public suspend fun get(id: String): Item?\n    public suspend fun delete(id: String)\n}\n\n// In-memory implementation for testing/development\n@OptIn(InternalA2AApi::class)\npublic class InMemorySomeStorage : SomeStorage {\n    private val rwLock = RWLock()\n    private val items = mutableMapOf<String, Item>()\n\n    override suspend fun save(item: Item) = rwLock.withWriteLock {\n        items[item.id] = item\n    }\n}\n```\n\n### Session Management Pattern\n- Use `SessionEventProcessor` for event-driven session handling\n- Implement proper cleanup with `Closeable` interface\n- Use structured concurrency with `CoroutineScope`\n- Handle session lifecycle properly (start → active → closed)\n\n### Agent Executor Implementation\n```kotlin\npublic class MyAgentExecutor : AgentExecutor {\n    override suspend fun execute(\n        context: RequestContext<MessageSendParams>,\n        eventProcessor: SessionEventProcessor\n    ) {\n        // Process request and send events through eventProcessor\n        eventProcessor.sendTaskEvent(/* task event */)\n    }\n}\n```\n\n## Some Common Commands\n\n```bash\n# Build specific modules (a2a is a meta-module, build individual modules)\n../gradlew :a2a:a2a-core:assemble\n../gradlew :a2a:a2a-client:assemble\n../gradlew :a2a:a2a-server:assemble\n\n# Run JVM tests for specific modules\n../gradlew :a2a:a2a-core:jvmTest\n../gradlew :a2a:a2a-client:jvmTest\n../gradlew :a2a:a2a-server:jvmTest\n../gradlew :a2a:a2a-transport:a2a-transport-core-jsonrpc:jvmTest\n\n# Run JS tests for specific modules\n../gradlew :a2a:a2a-core:jsTest\n../gradlew :a2a:a2a-client:jsTest\n../gradlew :a2a:a2a-server:jsTest\n\n# Build all non-transport a2a modules at once\n../gradlew :a2a:a2a-core:assemble :a2a:a2a-client:assemble :a2a:a2a-server:assemble\n\n# Run all JVM tests across a2a modules\n../gradlew :a2a:a2a-core:jvmTest :a2a:a2a-client:jvmTest :a2a:a2a-server:jvmTest\n```\n","agents/agents-features/agents-features-opentelemetry/README.md":"# OpenTelemetry support\n\nThis guide provides comprehensive instructions on how to use OpenTelemetry with the Koog agentic framework for tracing and monitoring your AI agents.\n\n## Overview\n\nOpenTelemetry is an observability framework that provides tools for generating, collecting, and exporting telemetry data (metrics, logs, and traces) from your applications. The Koog OpenTelemetry feature allows you to instrument your AI agents to collect telemetry data, which can help you:\n\n- Monitor agent performance and behavior\n- Debug issues in complex agent workflows\n- Visualize the execution flow of your agents\n- Track LLM calls and tool usage\n- Count tokens (input, output, and total) for LLM calls to monitor usage and costs\n- Analyze agent behavior patterns\n\nKey OpenTelemetry concepts\n\n- **Spans**: Spans represent individual units of work or operations within a distributed trace. They indicate the beginning and end of a specific activity in an application, such as an agent execution, a function call, an LLM call, or a tool call.\n- **Exporters**: Exporters are components responsible for sending the collected telemetry data (spans, metrics, logs) to various backends or destinations.\n- **Collectors**: An OpenTelemetry Collector receives, processes, and exports telemetry data. It acts as an intermediary between your applications and your observability backend.\n\nThe OpenTelemetry feature in Koog automatically creates spans for various agent events, including:\n\n- Agent creation and invocation\n- Strategy execution\n- Node execution\n- LLM calls (inference)\n- Tool calls\n\n## Installation\n\nTo use OpenTelemetry with Koog, add the OpenTelemetry feature to your agent:\n\n```kotlin\nval agent = AIAgent(\n    executor = simpleOpenAIExecutor(apiKey),\n    llmModel = OpenAIModels.Chat.GPT4o,\n    systemPrompt = \"You are a helpful assistant.\",\n    installFeatures = {\n        install(OpenTelemetry) {\n            // Configuration options here\n        }\n    }\n)\n```\n\n## Configuration\n\n### Basic configuration\n\nHere is an example of installing the OpenTelemetry feature with a basic set of configuration items:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.feature.OpenTelemetry\nimport io.opentelemetry.exporter.logging.LoggingSpanExporter\n\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Java SDK exporter is bridged automatically on JVM\n}\n```\n\nThe example includes the following configuration methods:\n\n| Name               | Data type | Required | Description                                                                                     |\n|--------------------|-----------|----------|-------------------------------------------------------------------------------------------------|\n| `setServiceInfo`   | Unit      | No       | Sets the information about the service being instrumented, including service name and version.  |\n| `addSpanExporter`  | Unit      | No       | Registers an exporter wrapped in a [`batchSpanProcessor`][batchSpanProcessor] (OTel-recommended). |\n\nPlease see below the full list of available configuration properties:\n\n| Name                    | Data type       | Default value | Description                                                                                                              |\n|-------------------------|-----------------|---------------|--------------------------------------------------------------------------------------------------------------------------|\n| `serviceName`           | `String`        | `ai.koog`     | The name of the service being instrumented.                                                                              |\n| `serviceVersion`        | `String`        | `0.0.0`       | The version of the service being instrumented.                                                                           |\n| `isVerbose`             | `Boolean`       | `false`       | Whether to enable verbose logging for debugging OpenTelemetry configuration.                                             |\n| `isShutdownOnAgentClose`| `Boolean`       | `false`       | Whether the SDK is shut down when the owning agent closes.                                                               |\n| `tracer`                | `Tracer`        |               | Kotlin SDK [`Tracer`][Tracer] used for creating spans.                                                                  |\n\nConfiguration API:\n\n| Name                    | Arguments                                                | Description                                                                                                              |\n|-------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| `setServiceInfo`        | `serviceName: String, serviceVersion: String`            | Sets the service information including name and version.                                                                 |\n| `addSpanExporter`       | `exporter: SpanExporter` (Kotlin SDK or Java SDK on JVM) | Registers an exporter behind a `batchSpanProcessor`. On JVM the method is overloaded to accept Java-SDK exporters directly (bridged via the compat layer). |\n| `addSpanProcessor`      | `factory: TraceExportConfigDsl.() -> SpanProcessor`      | Registers a custom span processor (e.g., simple/composite/custom-batched) via the SDK's DSL.                             |\n| `addResourceAttributes` | `attributes: Map<String, Any>`                           | Adds resource attributes to provide additional context about the service. Supported value types: `String`, `Long`, `Double`, `Boolean`. |\n| `setVerbose`            | `verbose: Boolean`                                       | Enables or disables verbose logging for debugging OpenTelemetry configuration.                                           |\n| `setSdk`                | `openTelemetry: OpenTelemetry`                           | Injects a pre-built Kotlin OpenTelemetry SDK instance, bypassing internal configuration.                                 |\n| `setShutdownOnAgentClose`| `shutdownOnAgentClose: Boolean`                         | Toggles whether the SDK is shut down when the owning agent closes.                                                       |\n\nJVM-only members on [`OpenTelemetryConfig`][OpenTelemetryConfig] cover metrics (`addMetricExporter`, `addMetricFilter`, `meter`) — the Kotlin SDK 0.3.0 ships no metrics module, so metrics are sent through the Java SDK.\n\n### Advanced configuration\n\nFor more advanced configuration, you can customize resource attributes:\n\n```kotlin\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Add Logging exporter\n\n    // Add resource attributes (Map<String, Any>; supports String, Long, Double, Boolean)\n    addResourceAttributes(mapOf(\n        \"deployment.environment\" to \"production\",\n        \"custom.attribute\" to \"custom-value\"\n    ))\n}\n```\n\n### Custom span processors\n\n`addSpanExporter` always wraps in `batchSpanProcessor`. For full control over the processor — custom batching parameters, simple per-span export for tests, or a composite processor over several inner processors — use `addSpanProcessor` and the SDK's DSL-scoped factory functions:\n\n```kotlin\nimport io.opentelemetry.kotlin.tracing.export.batchSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.simpleSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.stdoutSpanExporter\n\ninstall(OpenTelemetry) {\n    // Stdout exporter wrapped in a simple (synchronous) processor — useful for local debugging.\n    addSpanProcessor { simpleSpanProcessor(stdoutSpanExporter()) }\n\n    // Custom batch parameters (default: 5s schedule delay, 512 max batch size).\n    addSpanProcessor { batchSpanProcessor(myExporter, scheduleDelayMs = 1000) }\n}\n```\n\n### Sdk configuration\n\nIf you already have an initialized [`OpenTelemetry`][OpenTelemetry] SDK instance — for example, configured elsewhere in your application or provided by a framework — you can pass it directly using `setSdk`. When using `setSdk` any other configuration methods like `addSpanExporter`, `addSpanProcessor`, `addResourceAttributes` are ignored, since the SDK is assumed to be fully configured.\n\n```kotlin\nimport io.opentelemetry.kotlin.OpenTelemetry\nimport io.opentelemetry.kotlin.createOpenTelemetry\n\nval sdk: OpenTelemetry = createOpenTelemetry { /* … */ } // Your preconfigured SDK\n\ninstall(OpenTelemetry) {\n    setSdk(sdk) // Use your existing OpenTelemetry instance\n}\n```\n\n#### Resource attributes\n\nResource attributes represent additional information about a process producing telemetry. This information can be\nincluded in an OpenTelemetry configuration in Koog using the `addResourceAttributes()` method that takes a\n`Map<String, Any>`. Supported value types are `String`, `Long`, `Double`, and `Boolean`.\n\nThe following default resource attributes are automatically added:\n\n- `service.name`: The name of the service being instrumented. Set to the value of `serviceName`.\n- `service.version`: The version of the service being instrumented. Set to the value of `serviceVersion`.\n- `service.instance.time`: The timestamp when the service instance was created.\n- `os.type`: The operating system type.\n- `os.version`: The operating system version.\n- `os.arch`: The operating system architecture.\n\nThe OpenTelemetry feature automatically adds various attributes to spans following the [OpenTelemetry Semantic Convention for GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/):\n\n### Common Attributes\n\n- `gen_ai.operation.name`: the type of operation being performed (e.g., \"create_agent\", \"invoke_agent\", \"chat\", \"execute_tool\")\n- `gen_ai.system`: the LLM provider being used\n- `gen_ai.agent.id`: the ID of the agent\n- `gen_ai.agent.name`: the name of the agent (when available)\n- `gen_ai.conversation.id`: the ID of the conversation/run\n- `gen_ai.request.model`: the LLM model being used\n- `gen_ai.request.temperature`: the temperature parameter for the LLM request\n- `error.type`: the type of error that occurred (when applicable)\n\n### Custom Attributes\n\nSome custom attributes specific to Koog are also added:\n\n- `koog.agent.strategy.name`: the name of the agent strategy\n- `koog.node.id`: the name of the node being executed\n\n### Span Types\n\nThe OpenTelemetry feature creates different types of spans for various operations. Each span type has a distinct purpose, parent–child relationship, and set of attributes and events.\n\n1. **Create Agent Span**\n    - Purpose: Long‑lived span representing the lifecycle of an agent instance (created once per agent ID).\n    - Emitted: When an agent is first started. Closed when the agent is closed.\n    - Parent: none. Children: **Invoke Agent Span** (one per run).\n    - Useful for: Grouping all agent runs and capturing static agent configuration.\n    - Key attributes:\n        - `gen_ai.operation.name` = `create_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.request.model`\n\n2. **Invoke Agent Span**\n    - Purpose: One concrete execution (run) of an agent.\n    - Emitted: At the start of a run. Closed on successful finish or error.\n    - Parent: **Create Agent Span**. Children: **Strategy Span** instances, and, indirectly, node, tool, and LLM spans within strategies.\n    - Useful for: Measuring run‑level timing, status, and grouping of all strategy, node, tool, and LLM activity for a run.\n    - Key attributes:\n        - `gen_ai.operation.name` = `invoke_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.conversation.id`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.response.finish_reasons` (derived from the final response in the session prompt: `Stop` for Assistant/Reasoning, `ToolCalls` for Tool.Call)\n\n3. **Strategy Span**\n    - Purpose: Execution of a strategy within an agent run.\n    - Emitted: At the start of strategy execution. Closed when the strategy completes or errors.\n    - Parent: **Invoke Agent Span**. Children: **Node Execute Span** instances.\n    - Useful for: Tracking strategy-level execution, grouping all node executions within a strategy, and measuring strategy performance.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.strategy.name`\n        - `koog.event.id`\n    - Note: This is a custom span type specific to Koog, not defined in the OpenTelemetry Semantic Conventions.\n\n4. **Node Execute Span**\n    - Purpose: Execution of a single node in the agent strategy.\n    - Emitted: Immediately before a node runs. Closed after the node completes or errors.\n    - Parent: **Strategy Span**. Children: **Inference Spans** and **Execute Tool Span** instances created by the node.\n    - Useful for: Understanding strategy flow and attributing LLM or tool work to a specific node.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.node.id`\n\n5. **Inference Span**\n    - Purpose: A single LLM call (prompt execution).\n    - Emitted: Before the LLM is invoked. Closed after responses are received.\n    - Parent: **Node Execute Span**.\n    - Events added:\n        - system, user, and assistant messages from the prompt\n        - tool choice and tool result messages\n        - moderation response (when present)\n    - Useful for: Tracking model usage, prompt parameters, messages, token usage, and finish reasons.\n    - Key attributes:\n        - `gen_ai.operation.name` = `chat`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.conversation.id`\n        - `gen_ai.request.model`\n        - `gen_ai.request.temperature`\n        - `gen_ai.request.max_tokens` (when available)\n        - `gen_ai.usage.input_tokens` (when available)\n        - `gen_ai.usage.output_tokens` (when available)\n        - `gen_ai.usage.total_tokens` (when available)\n        - `gen_ai.response.finish_reasons` (Stop, ToolCalls, etc.)\n        - `error.type` (on LLM call failure)\n        - `gen_ai.response.metadata` (when `ResponseMetaInfo.metadata` is present - serialized JSON containing free-form provider or application metadata)\n\n6. **Execute Tool Span**\n    - Purpose: Execution of a tool or function call triggered by the agent or LLM.\n    - Emitted: When a tool is called. Closed after the tool returns a result or fails with an error during validation or execution.\n    - Parent: **Node Execute Span**.\n    - Useful for: Auditing tool usage, inputs and outputs, and failure causes.\n    - Key attributes:\n        - `gen_ai.tool.name`\n        - `gen_ai.tool.description`\n        - `gen_ai.tool.arguments` (when available)\n        - `gen_ai.tool.call_id` (when available)\n        - `gen_ai.tool.output` (when available)\n        - `error.type` (on failure or validation error)\n\n## Exporters\n\nExporters send collected telemetry data to an OpenTelemetry Collector or other types of destinations or backend implementations.\n\n### OTLP Exporter example\n\nThe OTLP (OpenTelemetry Protocol) exporter sends telemetry data to an OpenTelemetry Collector. This is useful for integrating with systems like Jaeger, Zipkin, or Prometheus.\n\nTo add an OpenTelemetry Exporter, use the `addSpanExporter` function to a custom exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    // The default endpoint is http://localhost:4317\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n### Logging Exporter example\n\nA logging exporter that outputs trace information to the console is included by default. This type of export is useful\nfor development and debugging purposes.\n\n```kotlin\ninstall(OpenTelemetry) {\n    // The logging exporter is added by default\n    addSpanExporter(LoggingSpanExporter.create())\n}\n```\n\n## Integration with Jaeger\n\nJaeger is a popular distributed tracing system that works with OpenTelemetry. To use Jaeger with Koog:\n\n1. Start a Jaeger container:\n```yaml\n# docker-compose.yaml\nservices:\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:1.39\n    container_name: jaeger-all-in-one\n    environment:\n      - COLLECTOR_OTLP_ENABLED=true\n    ports:\n      - \"4317:4317\"  # OTLP gRPC port\n      - \"16686:16686\"  # Jaeger UI port\n```\n\n2. Configure your agent to use the OTLP exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n3. Access the Jaeger UI at `http://localhost:16686` to view your traces.\n\n## Examples\n\nThis section includes two examples of implementing OpenTelemetry support in your agent workflow. The basic example\nincludes quick OpenTelemetry setup with minimal configuration, while the advanced example shows how you can create a\nmore elaborate and customized OpenTelemetry configuration.\n\n### Basic example\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleGoogleAIExecutor(apiKey),\n        llmModel = GoogleModels.Gemini2_0Flash,\n        systemPrompt = \"You are a code assistant. Provide concise code examples.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                setServiceInfo(\"my-otlp-agent\", \"1.0.0\")\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Create python function that prints hello world\")\n    println(result)\n    \n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\n### Advanced example with custom exporters and attributes\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                // Configure service info\n                setServiceInfo(\"advanced-agent\", \"2.0.0\")\n\n                // Add resource attributes (Map<String, Any>)\n                addResourceAttributes(mapOf(\n                    \"deployment.environment\" to \"production\",\n                    \"custom.attribute\" to \"custom-value\"\n                ))\n\n                // Add exporters (Java SDK exporter bridged via the JVM addSpanExporter)\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Tell me about OpenTelemetry\")\n    println(result)\n\n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\nFor full sampling control or other SDK-level options, build your own [`OpenTelemetry`][OpenTelemetry] instance with [`createOpenTelemetry { … }`][createOpenTelemetry] and inject it via `setSdk`.\n\n### Datadog\n\n[Datadog](https://www.datadoghq.com/) supports direct OTLP intake for LLM Observability. To send traces to Datadog:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.integration.datadog.addDatadogExporter\n\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n}\n```\n\nThe exporter reads the `DD_API_KEY` environment variable by default. You can also pass it explicitly:\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter(\n        datadogApiKey = \"your-api-key\",\n        url = \"datadoghq.eu\",                    // defaults to datadoghq.com\n        resourceAttributes = mapOf(\n            \"env\" to \"production\",\n            \"service.name\" to \"my-agent\",\n        ),\n    )\n}\n```\n\nTo send traces to Datadog and another backend at the same time, register both via `addSpanExporter` (Datadog's helper handles its own registration internally; the second exporter goes through the public API):\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n    addSpanExporter(\n        OtlpHttpSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4318/v1/traces\")\n            .build()\n    )\n}\n```\n\n## Troubleshooting\n\n### Common Issues\n\n1. **No traces appearing in Jaeger UI**\n    - Ensure the Jaeger container is running and the OTLP port (4317) is accessible\n    - Check that the OTLP exporter is configured with the correct endpoint\n    - Make sure to wait a few seconds after agent execution for traces to be exported\n\n2. **Missing spans or incomplete traces**\n    - Verify that the agent execution completes successfully\n    - Ensure that you're not closing the application too quickly after agent execution\n    - Add a delay (e.g., `TimeUnit.SECONDS.sleep(10)`) after agent execution to allow time for spans to be exported\n\n3. **Excessive number of spans**\n    - Build a custom Kotlin OTel SDK with a sampler (e.g., `parentBased(root = traceIdRatioBased(0.1))`) and inject it via `setSdk(...)`.\n","agents/agents-features/agents-features-snapshot/README.md":"# AgentCheckpoint\n\nAgentCheckpoint is a feature that provides checkpoint functionality for AI agents in the Koog Agents framework. It allows saving and restoring the state of an agent at specific points during execution, enabling powerful capabilities such as:\n\n- Resuming agent execution from a specific point\n- Rolling back to previous states\n- Persisting agent state across sessions\n\n## Core Concepts\n\n### Checkpoints\n\nA checkpoint captures the complete state of an agent at a specific point in its execution, including:\n\n- Message history (all interactions between user, system, assistant, and tools)\n- Current node being executed\n- Input data for the current node\n- Timestamp of creation\n\nCheckpoints are identified by unique IDs and are associated with a specific agent.\n\n### Storage Providers\n\nAgentCheckpoint uses storage providers to save and retrieve checkpoints. The framework includes several built-in providers:\n\n- `InMemoryAgentCheckpointStorageProvider`: Stores checkpoints in memory (lost when the application restarts)\n- `FileAgentCheckpointStorageProvider`: Persists checkpoints to the file system\n- `NoAgentCheckpointStorageProvider`: A no-op implementation (default) that doesn't store checkpoints\n\nYou can also implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface.\n\n### Continuous Persistence\n\nThe feature can be configured to automatically create checkpoints after each node execution, ensuring that the agent's state is continuously persisted and can be recovered at any point.\n\n## Installation\n\nTo use the AgentCheckpoint feature, you need to add it to your agent's configuration:\n\n```kotlin\nval agent = aiAgent {\n    // ... other configuration\n    \n    features {\n        install(AgentCheckpoint) {\n            // Configure the feature\n            snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n            continuouslyPersistent() // Optional: create checkpoints after each node\n        }\n    }\n}\n```\n\n## Configuration Options\n\nThe AgentCheckpoint feature has two main configuration options:\n\n### Storage Provider\n\nSet the storage provider that will be used to save and retrieve checkpoints:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n}\n```\n\nAvailable built-in providers:\n- `InMemoryAgentCheckpointStorageProvider`: In-memory storage (non-persistent)\n- `FileAgentCheckpointStorageProvider`: File-based storage\n- `NoAgentCheckpointStorageProvider`: No-op provider (default)\n\n### Continuous Persistence\n\nEnable automatic checkpoint creation after each node execution:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    continuouslyPersistent()\n}\n```\n\nWhen enabled, the agent will automatically create a checkpoint after each node is executed, allowing for fine-grained recovery.\n\n## Basic Usage\n\n### Creating a Checkpoint\n\nCreate a checkpoint at a specific point in your agent's execution:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Create a checkpoint with the current state\n    val checkpoint = context.checkpoint().createCheckpoint(\n        agentId = context.id,\n        agentContext = context,\n        nodeId = \"current-node-id\",\n        lastInput = inputData\n    )\n    \n    // The checkpoint ID can be stored for later use\n    val checkpointId = checkpoint.checkpointId\n}\n```\n\n### Restoring from a Checkpoint\n\nRestore an agent's state from a specific checkpoint:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase, checkpointId: String) {\n    // Roll back to a specific checkpoint\n    context.checkpoint().rollbackToCheckpoint(checkpointId, context)\n    \n    // Or roll back to the latest checkpoint\n    context.checkpoint().rollbackToLatestCheckpoint(context)\n}\n```\n\n### Using Extension Functions\n\nThe feature provides convenient extension functions for working with checkpoints:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Access the checkpoint feature\n    val checkpointFeature = context.checkpoint()\n    \n    // Or perform an action with the checkpoint feature\n    context.withCheckpoints(context) { ctx ->\n        // 'this' is the checkpoint feature\n        createCheckpoint(ctx.id, ctx, \"node-id\", inputData)\n    }\n}\n```\n\n## Advanced Usage\n\n### Custom Storage Providers\n\nYou can implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface:\n\n```kotlin\nclass MyCustomStorageProvider : AgentCheckpointStorageProvider {\n    override suspend fun getCheckpoints(agentId: String): List<AgentCheckpointData> {\n        // Implementation\n    }\n    \n    override suspend fun saveCheckpoint(agentId: String, agentCheckpointData: AgentCheckpointData) {\n        // Implementation\n    }\n    \n    override suspend fun getLatestCheckpoint(agentId: String): AgentCheckpointData? {\n        // Implementation\n    }\n}\n```\n\nThen use your custom provider in the feature configuration:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(MyCustomStorageProvider())\n}\n```\n\n### Setting Execution Points\n\nFor advanced control, you can directly set the execution point of an agent:\n\n```kotlin\nfun example(context: AIAgentContextBase) {\n    context.checkpoint().setExecutionPoint(\n        agentContext = context,\n        nodeId = \"target-node-id\",\n        messageHistory = customMessageHistory,\n        input = customInput\n    )\n}\n```\n\nThis allows for more fine-grained control over the agent's state beyond just restoring from checkpoints.\n\n## Requirements\n\nThe AgentCheckpoint feature requires that all nodes in your agent's strategy have unique names. This is enforced when the feature is installed:\n\n```kotlin\nrequire(ctx.strategy.metadata.uniqueNames) { \n    \"Checkpoint feature requires unique node names in the strategy metadata\" \n}\n```\n\nMake sure to set `uniqueNames = true` in your strategy metadata when using this feature.\n","docs/docs/agents/basic-agents.md":"# Basic agents\n\nA basic agent uses a predefined strategy with a simple execution flow that works for most common use cases.\nIt accepts a string input (a question, request, or task description) and sends this input to the configured LLM.\nThe LLM may decide to call provided tools.\nThe agent will execute the tools and send the results back to the LLM.\nThis repeats until the LLM does not request any more tool calls and returns a string response.\nThe agent then outputs this response.\n\nIn [Graph-based agents](graph-based-agents.md),\nyou can see how to re-create the predefined strategy graph used by basic agents.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\n## Create a minimal agent\n\nTo create the most basic agent, instantiate [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html)\nand provide a [prompt executor](../prompts/prompt-executors.md) with a [language model](../model-capabilities.md#creating-a-model-llmodel-configuration):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n\n    This agent will expect a string as input and return a string as output.\n    To run the agent, use the `run()` function with some user input:\n\n    ```kotlin\n    fun main() = runBlocking {\n        val result = agent.run(\"Hello! How can you help me?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-basic-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n\n    This agent expects a string as input and returns a string as output.\n    To run the agent, use the `run()` method with some user input:\n\n    ```java\n    String result = agent.run(\"Hello! How can you help me?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleBasicJava01.java -->\n\nThe agent will return a generic answer, such as:\n\n```text\nI can assist with a wide range of topics and tasks. Here are some examples:\n\n1. **Answering questions**: I can provide information on various subjects, from science and history to entertainment and culture.\n2. **Generating text**: I can help with writing tasks, such as suggesting alternative phrases, providing definitions, or even creating entire articles or stories.\n3. **Translation**: I can translate text from one language to another, including popular languages such as Spanish, French, German, Chinese, and many more.\n4. **Conversation**: I can engage in natural-sounding conversations, using context and understanding to respond to questions and statements.\n5. **Brainstorming**: I can help generate ideas for creative projects, such as writing stories, composing music, or coming up with business ideas.\n6. **Learning**: I can help with language learning, explaining grammar rules, vocabulary, and pronunciation.\n7. **Calculations**: I can perform mathematical calculations, including basic arithmetic, algebra, and more advanced math concepts.\n\nWhat's on your mind? Do you have a specific question, topic, or task you'd like to tackle?\n```\n<!--- KNIT example-basic-01.txt -->\n\n## Add a system prompt\n\nProvide a [system message](../prompts/prompt-creation/index.md#system-message) to define the agent's role\nas well as the purpose, context, and instructions related to the task.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n    <!--- KNIT example-basic-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava02.java -->\n\nThe instructions in the system prompt will guide the agent's response:\n\n```text\nI'm here to help you navigate the wild world of internet memes!\n\nWhat's on your mind? Are you trying to understand a specific meme, need help finding a popular joke, or perhaps want some recommendations for trending memes? Let me know, and I'll do my best to provide you with some LOLs!\n```\n<!--- KNIT example-basic-02.txt -->\n\n## Configure LLM output\n\nYou can provide some [LLM parameters](../llm-parameters.md#llm-parameter-reference) directly to the agent constructor \n(Kotlin) or via the builder methods (Java) to customize the behavior of the LLM.\nFor example, use the `temperature` parameter to adjust the randomness of the generated responses:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7\n    )\n    ```\n    <!--- KNIT example-basic-java-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava03 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava03.java -->\n\nHere are some response examples with different temperature values:\n\n=== \"0.4\"\n    \n    ```text\n    I'm here to help you navigate the wild world of internet memes! Whether you're looking for explanations, examples, or just want to share a meme with someone, I'm your go-to expert. What's on your mind? Got a specific meme in mind that's got you curious? Or maybe you need some meme-related advice? Fire away!\n    ```\n    <!--- KNIT example-basic-03.txt -->\n\n=== \"0.7\"\n\n    ```text\n    I'm here to help you navigate the wild world of internet memes!\n    \n    What's on your mind? Need help understanding a specific meme, finding a popular joke or trend, or maybe even creating your own meme? Let's get this meme party started!\n    ```\n    <!--- KNIT example-basic-04.txt -->\n\n=== \"1.0\"\n\n    ```text\n    I'd be happy to help you navigate the wild world of internet memes!\n    \n    Whether you're looking for explanations of classic memes, suggestions for new ones to try out, or just want to discuss your favorite meme culture trends, I'm here to assist. What's on your mind?\n    \n    Do you have a specific question about memes (e.g., \"What does this meme mean?\"), or are you looking for some meme-related recommendations (e.g., \"Can you recommend a funny meme to share with friends?\"). Let me know how I can help!\n    ```\n    <!--- KNIT example-basic-05.txt -->\n\n## Add tools\n\nAgents can use [tools](../tools/index.md) to perform specific tasks.\n\nFirst, create a tool by annotating a function (Kotlin) or method (Java) with the [`@Tool`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools.annotations/-tool/index.html) annotation:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    ```\n\n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        }\n    )\n    ```\n    <!--- KNIT example-basic-03.kt -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n    If the agent decides to ask the user a question,\n    it can call this tool that writes to `stdout` via `println()` and reads from `stdin` via `readln()`.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava04 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n    ```\n    \n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```java\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava04.java -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n\nHere is an example interaction with the agent:\n\n```text\nAgent: Which meme would you like me to explain? Please choose from: Grumpy Cat, Success Kid, or Doge.\n\nUser: Explain Doge\n\nAgent:\n\n**Doge**\n\nDoge is a popular internet meme that originated on the imageboard website 4chan in 2013. The meme features a picture of a Shiba Inu dog with a caption written in a playful, exaggerated style.\n\nThe typical format of a Doge meme includes:\n\n* A photo of a Shiba Inu dog\n* A caption written in all capital letters, using a intentionally simplistic and childlike tone\n* Exaggerated or made-up words or phrases, often used to convey a humorous or nonsensical idea\n\nExamples of Doge memes might include:\n\n* \"Such wow. Such happy.\"\n* \"I had fun today!\"\n* \"Wow, I am good at napping.\"\n\nThe meme is known for its lighthearted and playful tone, and is often used to express excitement, happiness, or silliness. The meme has since become a cultural phenomenon, with countless variations and parodies emerging online.\n```\n<!--- KNIT example-basic-06.txt -->\n\n## Adjust agent iterations\n\nTo avoid infinite loops, Koog allows any agent to take a limited number of steps (50 by default).\nUse the `maxIterations` parameter to either increase this limit if you expect the agent to require more steps\n(such as tool calls and LLM requests) or decrease it for agents that require only a few steps.\nFor example, a simple agent described here is not likely to require more than 10 steps:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-basic-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava05 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava05.java -->\n\n!!! tip\n\n    Instead of passing the model, temperature, max iterations, and other parameters directly to the Kotlin constructor \n    or Java builder, you can also define and pass them as a separate configuration object.\n    For more information, see [Agent configuration](index.md#agent-configuration).\n\n## Handle events during agent runtime\n\nTo assist with testing and debugging, as well as making hooks for chained agent interactions,\nKoog provides the [EventHandler](https://api.koog.ai/agents/agents-features/agents-features-event-handler/ai.koog.agents.features.eventHandler.feature/-event-handler/index.html) feature.\n\n=== \"Kotlin\"\n\n    Call the `handleEvents()` function inside the agent constructor lambda to install the feature and register event handlers:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.features.eventHandler.feature.handleEvents\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    ){\n        handleEvents {\n            // Handle tool calls\n            onToolCallStarting { eventContext ->\n                println(\"Tool called: ${eventContext.toolName} with args ${eventContext.toolArgs}\")\n            }\n        }\n    }\n    ```\n    <!--- KNIT example-basic-05.kt -->\n\n=== \"Java\"\n    Use the `.install()` method on the agent builder to register event handlers with `EventHandler.Feature`:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.agents.features.eventHandler.feature.EventHandler;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava06 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .install(EventHandler.Feature, config -> {\n            config.onToolCallStarting(eventContext -> {\n                System.out.println(\"Tool called: \" + eventContext.getToolName() +\n                    \" with args \" + eventContext.getToolArgs());\n            });\n        })\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava06.java -->\n\nThe agent will now output something similar to the following when it calls the `askUser` tool:\n\n```text\nTool called: askUser with args {\"question\":\"Which meme would you like me to explain?\"}\n```\n<!--- KNIT example-basic-07.txt -->\n\nFor more information about Koog agent features, see [Features](../features/index.md).\n\n## Next steps\n\n- Learn more about building [graph-based agents](graph-based-agents.md) and [functional agents](functional-agents.md)\n","docs/docs/agents/functional-agents.md":"# Functional agents\n\nWith functional agents, you implement the logic as a function that handles user input,\ninteracts with LLMs, calls tools if necessary, and produces the final output.\nCompared to [graph-based agents](graph-based-agents.md),\nthis usually means faster prototyping with the following downsides:\n\n- Not easy to visualize\n- No state persistence\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to implement a functional strategy to quickly prototype some custom logic for your agent.\n\n## Create a minimal functional agent\n\nTo create a minimal functional agent, use the same [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html) interface as for a [basic agent](basic-agents.md) and pass an \ninstance of [`AIAgentFunctionalStrategy`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent-functional-strategy/index.html) to it.\nYou can define a functional strategy that expects an input and returns an output, makes one LLM call, then returns the \ncontent of the assistant message from the response.\n\nIn Kotlin, the most convenient way is to use the `functionalStrategy {...}` DSL method. In Java, you can use the `functionalStrategy` method on the `AIAgent` builder.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val strategy = functionalStrategy<String, String> { input ->\n        val response = requestLLM(input)\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgent.run(\"What is 12 × 9?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy(\"mathStrategy\", (AIAgentFunctionalContext context, String input) -> {\n            Message.Response response = context.requestLLM(input);\n            if (response instanceof Message.Assistant) {\n                return ((Message.Assistant) response).getContent();\n            }\n            return \"\";\n        })\n        .build();\n\n    String result = mathAgent.run(\"What is 12 × 9?\");\n    System.out.println(result);\n    ```\n   <!--- KNIT example-functional-agent-java-01.java -->\n\nThe agent can produce the following output:\n\n```text\nThe answer to 12 × 9 is 108.\n```\n<!--- KNIT example-functional-agent-01.txt -->\n\n## Make sequential LLM calls\n\nYou can extend the previous strategy to make multiple sequential LLM calls:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    -->\n    ```kotlin\n    fun Message.Assistant.text(): String =\n        parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // The first LLM call produces an initial draft based on the user input\n        val draft = requestLLM(\"Draft: $input\").text()\n        // The second LLM call improves the initial draft\n        val improved = requestLLM(\"Improve and clarify.\").text()\n        // The final LLM call formats the improved text and returns the result\n        requestLLM(\"Format the result as bold.\").text()\n    }\n    ```\n    <!--- KNIT example-functional-agent-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .systemPrompt(\"You are a precise math assistant.\")\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy((AIAgentFunctionalContext context, String input) -> {\n            // The first LLM call produces an initial draft based on the user input\n            Message.Response draftResponse = context.requestLLM(\"Draft: \" + input);\n            String draft = \"\";\n            if (draftResponse instanceof Message.Assistant) {\n                draft = ((Message.Assistant) draftResponse).getContent();\n            }\n\n            // The second LLM call improves the initial draft\n            Message.Response improvedResponse = context.requestLLM(\"Improve and clarify.\");\n            String improved = \"\";\n            if (improvedResponse instanceof Message.Assistant) {\n                improved = ((Message.Assistant) improvedResponse).getContent();\n            }\n\n            // The final LLM call formats the improved text and returns the result\n            Message.Response finalResponse = context.requestLLM(\"Format the result as bold.\");\n            if (finalResponse instanceof Message.Assistant) {\n                return ((Message.Assistant) finalResponse).getContent();\n            }\n            return \"\";\n        })\n        .build();\n    ```\n    <!--- KNIT example-functional-agent-java-02.java -->\n\nThe agent can produce the following output:\n\n```text\nTo calculate the product of 12 and 9, we multiply these two numbers together.\n\n12 × 9 = **108**\n```\n<!--- KNIT example-functional-agent-02.txt -->\n\n## Add tools\n\nIn many cases, a functional agent needs to complete specific tasks,\nsuch as reading and writing data, calling APIs, or performing other deterministic operations.\nIn Koog, you expose such capabilities as [tools](../tools/index.md) and let the LLM decide when to call them.\n\nHere is what you need to do:\n\n1. Create an [annotation-based tool](../tools/annotation-based-tools.md).\n2. Add it to a tool registry and pass the registry to the agent.\n3. Make sure the agent strategy can identify tool calls in LLM responses, execute the requested tools,\n   send their results back to the LLM, and repeat the process until there are no tool calls remaining.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n\n    val toolRegistry = ToolRegistry {\n        tool(MathTools()::multiply)\n    }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // Send the user input to the LLM\n        var response = requestLLM(input)\n\n        // Only loop while the LLM requests tools\n        var toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        while (toolCalls.isNotEmpty()) {\n            // Execute the tools and return the results\n            val results = executeTools(toolCalls)\n            // Send the tool results back to the LLM. The LLM may call more tools or return a final output\n            response = sendToolResults(results)\n            toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        }\n\n        // When no tool calls remain, extract and return the assistant message content from the response\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgentWithTools = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        toolRegistry = toolRegistry,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n\n    public static void main(String[] args) {\n        MathTools mathTools = new MathTools();\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(mathTools)\n            .build();\n\n        AIAgent<String, String> mathAgentWithTools = AIAgent.builder()\n            .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n            .llmModel(OllamaModels.Meta.LLAMA_3_2)\n            .toolRegistry(toolRegistry)\n            .functionalStrategy(\"mathWithTools\", (AIAgentFunctionalContext context, String input) -> {\n                // Send the user input to the LLM\n                List<Message.Response> responses = context.requestLLMMultiple(input);\n\n                // Only loop while the LLM requests tools\n                while (context.containsToolCalls(responses)) {\n                    // Extract tool calls from the response\n                    List<MessagePart.Tool.Call> pendingCalls = context.extractToolCalls(responses);\n                    // Execute the tools and return the results\n                    List<ReceivedToolResult> results = context.executeMultipleTools(pendingCalls, false);\n                    // Send the tool results back to the LLM\n                    responses = context.sendMultipleToolResults(results);\n                }\n\n                // Extract and return the assistant message content from the response\n                Message.Response finalResponse = responses.get(0);\n                if (finalResponse instanceof Message.Assistant) {\n                    return ((Message.Assistant) finalResponse).getContent();\n                }\n                return \"\";\n            })\n            .build();\n\n        String result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\");\n        System.out.println(result);\n    }\n    ```\n   <!--- KNIT example-functional-agent-java-03.java -->\n\nThe agent can produce the following output:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nThe result of multiplying 3 by 4 is 12. Multiplying 12 by 5 gives us a final answer of 60.\n```\n<!--- KNIT example-functional-agent-03.txt -->\n\n## Next steps\n\n- Learn how to create [graph-based agents](graph-based-agents.md)\n","docs/docs/agents/graph-based-agents.md":"# Graph-based agents\n\nWith graph-based agents, you model the behavior as an explicit state machine:\nnodes of a graph strategy represent actions (LLM calls, tool execution)\nand edges represent data flow between nodes.\n\nThe main advantages of graph-based agents are:\n\n- Easy to visualize\n- State persistence\n- Composable architecture\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to re-create the strategy graph used by [basic agents](basic-agents.md).\nIt sends a request to an LLM and then either outputs the response (if the LLM responded with an assistant message)\nor executes a tool (if the LLM requested a tool call).\nIn case of a tool call, the agent sends the tool result to the LLM\nand then either outputs the response or executes a tool.\n\nHere is an illustration of the strategy graph:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph TB\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    subgraph nodeExecuteTool\n        executeTool(Execute tool call)\n    end\n    \n    subgraph nodeSendToolResult\n        sendToolResult(Request LLM)\n    end\n    \n    Input --String--> llmRequest\n    llmRequest --Message.Assistant--> onToolCalls{{onToolCalls}}\n    llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}}\n    onTextMessage --String--> Output\n    onToolCalls --ToolCalls--> executeTool --ReceivedToolResults--> sendToolResult\n    sendToolResult --Message.Assistant--> onToolCalls\n    sendToolResult --Message.Assistant--> onTextMessage\n```\n<!--- KNIT example-graph-agents-01.txt -->\n\n\n## Build a strategy graph\n\nIn Koog, you implement a strategy using [`AIAgentGraphStrategyBuilder`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/-a-i-agent-graph-strategy-builder/index.html).\nJust like every node has an input and output type,\nthe strategy as a whole also defines some input and output type.\nThis example assumes that the input and output types are strings,\nwhich means the agent implementing this strategy will expect a string and return a string.\n\nTo create a strategy, use the [`strategy()`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/strategy.html) function with two generics as the input and output types,\nprovide a unique identifier for the strategy, and define the nodes and edges.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n        \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    ```\n    <!--- KNIT example-graph-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    ```\n    <!--- KNIT exampleGraphAgentsJava01.java -->\n\nThis example uses only [predefined nodes](../nodes-and-components.md),\nbut you can also create [custom nodes](../custom-nodes.md).\n\nEvery strategy graph must have a path from `nodeStart` to `nodeFinish` connected by [edges](../custom-strategy-graphs.md#edges).\nEdges can have conditions to determine when to follow a particular edge.\nEdges can also transform the output of the previous node before passing it to the next one.\nThis is necessary to connect nodes that have non-matching output and input types.\n\nIn the previous example, `onToolCalls { true }` means that the edge will follow\nonly if the previous node returned an assistant message containing at least one tool call (`MessagePart.Tool.Call`).\n\nWhen using `onTextMessage { true }`, the edge will follow\nonly if the previous node returned an assistant message containing text parts (`MessagePart.Text`).\nThis function also extracts and joins the text content of those parts,\neffectively transforming `Message.Assistant` to `String`, because `nodeFinish` expects a string.\n\n!!! tip\n\n    Instead of `onTextMessage { true }`, you can do the following:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onMessageParts(MessagePart.Text::class) transformed { it.joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-02.kt -->\n\n    Or:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onCondition { it is Message.Assistant } transformed { (it as Message.Assistant).parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-03.kt -->\n\n## Create and run the agent\n\nLet's create an agent instance with this strategy and run it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    \n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n\n    var promptExecutor = PromptExecutor.builder()\n        .ollama(\"http://localhost:11434\")\n        .build();\n\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .build();\n\n        String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n        System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava02.java -->\n\nWhen you run this agent, it will respond with something like this:\n\n```text\nTo calculate this, I'll follow the order of operations:\n\n1. Multiply 3 by 4: 3 * 4 = 12\n2. Multiply the result by 5: 12 * 5 = 60\n3. Add 10: 60 + 10 = 70\n4. Add 123: 70 + 123 = 193\n\nThe final answer is 193.\n```\n<!--- KNIT example-graph-agents-02.txt -->\n\nHowever, since this agent doesn't have any tools, the LLM never returns a tool call \nand simply generates the whole answer.\nThis is what effectively happens:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph LR\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    Input --String--> llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}} --String--> Output\n```\n<!--- KNIT example-graph-agents-03.txt -->\n\nEven though it is correct in this case, the answer will depend on the arithmetic abilities of the underlying LLM.\nTo make sure the calculations are correct, we should provide the agent with math tools.\nThen the LLM will be able to decide to call tools that perform the calculations deterministically.\n\n## Add tools\n\nDefine [tools](../tools/index.md) for performing math operations and add them to a [ToolRegistry](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    ```\n    <!--- KNIT example-graph-agents-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOllamaAIExecutor;\n    class exampleGraphAgentsJava03 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    @LLMDescription(\"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        public int add(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n            return a + b;\n        }\n\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n    public static void main(String[] args) {\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(new MathTools())\n            .build();\n    }\n    ```\n    <!--- KNIT exampleGraphAgentsJava03.java -->\n\nAdd the tool registry to the agent configuration:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy,\n        toolRegistry = toolRegistry\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava04 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class);\n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava04.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nThe output from the first operation was multiplied by 5:\n5 * 12 = 60\n\nThen, 10 was added to the result:\n60 + 10 = 70\n\nFinally, 123 was added to the result:\n70 + 123 = 193\n```\n<!--- KNIT example-graph-agents-04.txt -->\n\nAccording to this output, the agent correctly performed the calculations, but it only called the `multiply` tool once\ninstead of calling the corresponding tool for every operation.\nWe can help the agent by describing its role and providing instructions for using appropriate tools in the system prompt.\n\n## Provide a system prompt\n\nA [system prompt](../prompts/prompt-creation/index.md#system-message) defines the agent's role and instructions for performing tasks.\nIn our example, it is important to describe how the agent should process complex multistep calculations:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        systemPrompt = \"\"\"\n                    You are a simple calculator assistant.\n                    You can add and multiply two numbers using the 'add' and 'multiply' tools.\n                    When the user provides input, extract the numbers and operations they requested.\n                    Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result.\n                    Always respond with a clear, friendly message showing the calculation and result.\n                    \"\"\".trimIndent(),\n        toolRegistry = toolRegistry,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava05 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class); \n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\"); \n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .systemPrompt(\"You are a simple calculator assistant. You can add and multiply two numbers using the 'add' and 'multiply' tools. When the user provides input, extract the numbers and operations they requested. Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result. Always respond with a clear, friendly message showing the calculation and result.\")\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava05.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nAdding 60 and 10...\nAdding 70 and 123...\nThe final result is: 193\n```\n<!--- KNIT example-graph-agents-05.txt -->\n\nAs you can see, the agent now correctly calls the appropriate tool for each operation,\nensuring that it performs the calculations deterministically instead of risking a hallucinated result.\n\n## Next steps\n\n- Compare to [functional agents](functional-agents.md) and [planner agents](planner-agents/index.md)\n- Enhance your agent by [installing features](../features/index.md)\n- Improve the predictability and reliability with [structured output](../structured-output.md)\n","docs/docs/agents/index.md":"# Agents\n\nAI agents are autonomous systems that can reason, make decisions,\ninteract with the environment, and take actions to achieve a specific goal.\nIn Koog, an AI agent is more than just a wrapper around an LLM;\nit is a structured, type-safe state machine designed for the JVM ecosystem.\n\nKoog agents are built around the following core concepts:\n\n- A [prompt executor](../prompts/prompt-executors.md) manages and executes prompts,\n  enabling the agent to interact with LLMs for reasoning and decision-making.\n- A [strategy](../nodes-and-components.md) defines the agent's workflow.\n  It can be in the form of a directed graph, a function, or a planner.\n  See [Agent types](#agent-types).\n- An agent can use [tools](../tools/index.md) to interact with external data sources and services.\n- You can extend and enhance the functionality of AI agents using [features](../features/index.md).\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\n## Agent types\n\nDepending on the task you need to perform, Koog provides several agent types:\n\n- [Basic agents](basic-agents.md) are ideal for simple tasks that don't require any custom logic.\n  These agents implement a predefined strategy that works for most common use cases.\n- [Graph-based agents](graph-based-agents.md) provide full control and flexibility of the agent's workflow, state management, and visualization.\n- [Functional agents](functional-agents.md) enable you to quickly prototype custom logic as a function with access to the agent's context.\n- [Planner agents](planner-agents/index.md) can autonomously plan and execute multistep tasks through iterative cycles until they reach a desired final state.\n\n## Agent configuration\n\nAgent configuration defines the agent's execution parameters,\nincluding the initial prompt, language model, and iteration limits.\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\nFor simple agents, besides the mandatory prompt executor and language model,\nyou can specify the initial system prompt and some other parameters directly in the agent constructor:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        temperature = 0.7,\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-agent-config-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful assistant.\")\n        .temperature(0.7)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-01.java -->\n\nAlternatively, you can create an instance of [`AIAgentConfig`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent.config/-a-i-agent-config/index.html)\nto define the agent's behavior and parameters more granularly, then pass it to the agent constructor.\nThis enables you to define complex prompts with multiple messages,\nconversation history, LLM parameters, and additional execution parameters.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.prompt.params.LLMParams\n    -->\n    ```kotlin\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\n            id = \"assistant\",\n            params = LLMParams(\n                temperature = 0.7\n            )\n        ) {\n            system(\"You are a helpful assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 10\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        agentConfig = agentConfig\n    )\n    ```\n    <!--- KNIT example-agent-config-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    Prompt prompt = Prompt.builder(\"assistant\")\n        .system(\"You are a helpful assistant.\")\n        .build()\n        .withParams(new LLMParams(\n            0.7,         // temperature\n            null,        // maxTokens\n            1,           // numberOfChoices\n            null,        // speculation\n            null,        // schema\n            LLMParams.ToolChoice.Auto.INSTANCE, // toolChoice\n            null,        // user\n            null         // additionalProperties\n        ));\n\n    AIAgentConfig agentConfig = AIAgentConfig.builder(OpenAIModels.Chat.GPT4o)\n        .prompt(prompt)\n        .maxAgentIterations(10)\n        .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .agentConfig(agentConfig)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-02.java -->\n\nHere are the parameters of `AIAgentConfig`:\n\n- `prompt` defines the initial [prompt](../prompts/prompt-creation/index.md) and [LLM parameters](../llm-parameters.md).\n\n- `model` specifies the language model with which the agent interacts.\n  You can use one of the predefined models or [create a custom model configuration](../model-capabilities.md#creating-a-model-llmodel-configuration).\n\n- `maxAgentIterations` limits the maximum number of steps the agent can take before it terminates.\n  Each step is a [node](../nodes-and-components.md) in the agent's workflow.\n\n- `missingToolsConversionStrategy` defines a strategy for handling missing tools during agent execution.\n\n[//]: # (TODO write about missing tools in the TOols section and link from here)\n\n- `responseProcessor` can be used to define a custom response processor.\n  For example, it can moderate and validate the response content, change the response format, or log the response.\n\n[//]: # (TODO write about response processing somewhere?)\n","docs/docs/agents/planner-agents/goap-agents.md":"---\nstatus: beta\n---\n\n# GOAP agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nGOAP is an algorithmic planning approach that uses [A* search] find optimal action sequences\nthat satisfy the goal conditions while minimizing the total cost.\nUnlike [LLM-based planners](llm-based-planners.md) that use an LLM to generate plans,\na GOAP agent algorithmically discovers action sequences based on predefined goals and actions.\n\nGOAP planners work with three main concepts:\n\n- **State**: Represents the current state of the world.\n- **Actions**: Define what can be done, including preconditions, effects (beliefs), costs, and execution logic.\n- **Goals**: Define target conditions, heuristic costs, and value functions.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nIn Koog, you define a GOAP agent using a DSL by declaratively specifying the goals and actions.\n\nTo create a GOAP agent, you need to:\n\n1. Define the state as a data class with properties representing various aspects specific to your goal.\n2. Create a [GOAPPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner/index.html) instance using the [goap()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/goap.html) function.\n    1. Define actions with preconditions and beliefs using the [action()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/action.html) function.\n    2. Define goals with completion conditions using the [goal()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/goal.html) function.\n3. Wrap the planner with [AIAgentPlannerStrategy](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-a-i-agent-planner-strategy/index.html) and pass it to the [PlannerAIAgent](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-planner-a-i-agent/index.html) constructor.\n\n!!! note\n\n    The planner selects individual actions and their sequence.\n    Each action includes a precondition that must hold true for the action to be executed\n    and a belief that defines the predicted outcome.\n    For more information about beliefs, see [State beliefs compared to actual execution](#state-beliefs-compared-to-actual-execution).\n\nIn the following example, GOAP handles high-level planning for creating an article\n(outline → draft → review → publish),\nwhile the LLM performs the actual content generation within each action.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.planner.goap\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Define a state for content creation\n    data class ContentState(\n        val topic: String,\n        val hasOutline: Boolean = false,\n        val outline: String = \"\",\n        val hasDraft: Boolean = false,\n        val draft: String = \"\",\n        val hasReview: Boolean = false,\n        val isPublished: Boolean = false\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = draft\n    }\n\n    // Create GOAP planner with LLM-powered actions\n    val planner = goap(\"content-planner\", ::ContentState) {\n        // Define actions with preconditions and beliefs\n        action(\n            name = \"Create outline\",\n            precondition = { state -> !state.hasOutline },\n            belief = { state -> state.copy(hasOutline = true, outline = \"Outline\") },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to create the outline\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Create a detailed outline for an article about: ${state.topic}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasOutline = true, outline = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Write draft\",\n            precondition = { state -> state.hasOutline && !state.hasDraft },\n            belief = { state -> state.copy(hasDraft = true, draft = \"Draft\") },\n            cost = { 2.0 }\n        ) { ctx, state ->\n            // Use LLM to write the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Write an article based on this outline:\\n${state.outline}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasDraft = true, draft = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Review content\",\n            precondition = { state -> state.hasDraft && !state.hasReview },\n            belief = { state -> state.copy(hasReview = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to review the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Review this article and suggest improvements:\\n${state.draft}\")\n                }\n                requestLLM()\n            }\n            println(\"Review feedback: ${response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }}\")\n            state.copy(hasReview = true)\n        }\n\n        action(\n            name = \"Publish\",\n            precondition = { state -> state.hasReview && !state.isPublished },\n            belief = { state -> state.copy(isPublished = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            println(\"Publishing article...\")\n            state.copy(isPublished = true)\n        }\n\n        // Define the goal with a completion condition\n        goal(\n            name = \"Published article\",\n            description = \"Complete and publish the article\",\n            condition = { state -> state.isPublished }\n        )\n    }\n\n    // Create and run the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"writer\") {\n            system(\"You are a professional content writer.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 20\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = planner,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        val result = agent.run(\"The Future of AI in Software Development\")\n        println(\"Final state: $result\")\n    }\n    ```\n    <!--- KNIT example-goap-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGoapAgents01 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    // Define a state for content creation\n    static class ContentState extends GoapAgentState<String, String> {\n        public String topic;\n        public boolean hasOutline = false;\n        public String outline = \"\";\n        public boolean hasDraft = false;\n        public String draft = \"\";\n        public boolean hasReview = false;\n        public boolean isPublished = false;\n    \n        public ContentState(String topic) {\n            this.topic = topic;\n        }\n\n        @Override\n        public String getAgentInput() {\n            return topic;\n        }\n\n        public ContentState copy(boolean hasOutline, String outline, boolean hasDraft,\n                                 String draft, boolean hasReview, boolean isPublished) {\n            ContentState state = new ContentState(topic);\n            state.hasOutline = hasOutline;\n            state.outline = outline;\n            state.hasDraft = hasDraft;\n            state.draft = draft;\n            state.hasReview = hasReview;\n            state.isPublished = isPublished;\n            return state;\n        }\n\n        @Override\n        public String provideOutput() {\n            return draft;\n        }\n    }\n\n    public static void main(String[] args) {\n        var promptExecutor = PromptExecutor.builder()\n            .openAI(\"OPENAI_API_KEY\")\n            .build();\n\n        var strategy = Planners.goap(\"content-planner\", ContentState::new)\n            .action(\"Create outline\", builder -> builder\n                .precondition(state -> !state.hasOutline)\n                .belief(state -> state.copy(true, \"Outline\", false, \"\", false, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Create a detailed outline for an article about: \" + state.topic);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(true, response, state.hasDraft, state.draft,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Write draft\", builder -> builder\n                .precondition(state -> state.hasOutline && !state.hasDraft)\n                .belief(state -> state.copy(state.hasOutline, state.outline, true, \"Draft\", false, false))\n                .cost(state -> 2.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Write an article based on this outline:\\n\" + state.outline);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(state.hasOutline, state.outline, true, response,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Review content\", builder -> builder\n                .precondition(state -> state.hasDraft && !state.hasReview)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, true, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Review this article and suggest improvements:\\n\" + state.draft);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    System.out.println(\"Review feedback: \" + response);\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, true, state.isPublished);\n                })\n            )\n            .action(\"Publish\", builder -> builder\n                .precondition(state -> state.hasReview && !state.isPublished)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, state.hasReview, true))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    System.out.println(\"Publishing article...\");\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, state.hasReview, true);\n                })\n            )\n            .goal(\"Published article\", builder -> builder\n                .description(\"Complete and publish the article\")\n                .condition(state -> state.isPublished)\n            )\n            .build();\n\n        var agent = AIAgent.builder()\n            .plannerStrategy(strategy)\n            .promptExecutor(promptExecutor)\n            .llmModel(OpenAIModels.Chat.GPT4o)\n            .systemPrompt(\"You are a professional content writer.\")\n            .maxIterations(20)\n            .build();\n\n        String result = agent.run(\"The Future of AI in Software Development\");\n        System.out.println(\"Final state: \" + result);\n    }\n    ```\n    <!--- KNIT exampleGoapAgentsJava01.java -->\n    \n\n## Custom cost functions\n\nAs [A* search] uses cost as a factor in finding the optimal sequence of actions,\nyou can define custom cost functions for actions and goals to guide the planner:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val operationDone: Boolean = true,\n        val hasOptimization: Boolean = true\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Expensive operation\",\n        precondition = { true },\n        belief = { state -> state.copy(operationDone = true) },\n        cost = { state ->\n            // Dynamic cost based on state\n            if (state.hasOptimization) 1.0 else 10.0\n        }\n    ) { ctx, state ->\n        // Execute action\n        state.copy(operationDone = true)\n    }\n    ```\n    <!--- KNIT example-goap-agents-02.kt -->\n\n=== \"Java\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents02 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean operationDone = false;\n            public boolean hasOptimization = true;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean operationDone) {\n                MyState state = new MyState(topic);\n                state.operationDone = operationDone;\n                state.hasOptimization = this.hasOptimization;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Expensive operation\", builder -> builder\n        .precondition(state -> true)\n        .belief(state -> state.copy(true))\n        .cost(state -> {\n            // Dynamic cost based on state\n            return state.hasOptimization ? 1.0 : 10.0;\n        })\n        .execute((context, state) -> {\n            // Execute action\n            return state.copy(true);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava02.java -->\n\n## State beliefs compared to actual execution\n\nGOAP distinguishes between the concepts of beliefs (optimistic predictions) and actual execution:\n\n- **Belief**: What the planner thinks will happen, used for planning.\n- **Execution**: What actually happens, used for real state updates.\n\nThis allows the planner to make plans based on expected outcomes while handling actual results properly:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val taskComplete: Boolean = true,\n        val attempts: Int = 0\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    fun performComplexTask(): Boolean = true\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Attempt complex task\",\n        precondition = { state -> !state.taskComplete },\n        belief = { state ->\n            // Optimistic belief: task will succeed\n            state.copy(taskComplete = true)\n        },\n        cost = { 5.0 }\n    ) { ctx, state ->\n        // Actual execution might fail or have different results\n        val success = performComplexTask()\n        state.copy(\n            taskComplete = success,\n            attempts = state.attempts + 1\n        )\n    }\n    ```\n    <!--- KNIT example-goap-agents-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents03 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean taskComplete = false;\n            public int attempts = 0;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean taskComplete, int attempts) {\n                MyState state = new MyState(topic);\n                state.taskComplete = taskComplete;\n                state.attempts = attempts;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        static boolean performComplexTask() {\n            return true;\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Attempt complex task\", builder -> builder\n        .precondition(state -> !state.taskComplete)\n        .belief(state -> {\n            // Optimistic belief: task will succeed\n            return state.copy(true, state.attempts);\n        })\n        .cost(state -> 5.0)\n        .execute((context, state) -> {\n            // Actual execution might fail or have different results\n            boolean success = performComplexTask();\n            return state.copy(success, state.attempts + 1);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava03.java -->\n\n[A* search]: https://en.wikipedia.org/wiki/A*_search_algorithm\n","docs/docs/agents/planner-agents/index.md":"---\nstatus: beta\n---\n\n# Planner agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nPlanner agents are AI agents that plan and execute multistep tasks through iterative planning cycles.\nThey continuously build or update plans, execute steps, and check completion criteria against the current state.\n\nPlanner agents are suitable for complex tasks\nthat require breaking down a high-level goal into smaller, actionable steps\nand adapting the plan based on the results of each step.\n\nWhile with [graph-based agents](../graph-based-agents.md) you define all the nodes and edges,\nfor planner agents, you only define the actions (nodes) with typed inputs and outputs.\nThe planner creates reasonable edges that would be suitable to achieve the desired state,\nand it can also update the optimal path between steps.\nThis allows for a more dynamic approach which can be more powerful \nbut less controllable compared to graph-based agents.\n\nPlanner agents operate through an iterative planning cycle:\n\n1. The planner creates or updates a plan based on the current state.\n2. The planner executes a single step from the plan, updating the state.\n3. The planner determines whether the plan is completed according to the current state.\n    - If the plan is completed, the cycle ends.\n    - If the plan is not completed, the cycle repeats from the first step.\n\n```mermaid\ngraph LR\n  A[Create or update plan] --> B[\"Execute step and update state\"]\n  B --> C[\"Check completion\"]\n  C -->|Completed| D[[Done]]\n  C -->|\"Not completed\"| A\n```\n\nKoog provides two types of planner agents:\n\n- [LLM-based planners](llm-based-planners.md) use an LLM to create and update plans\n- [GOAP agents](goap-agents.md) use a special algorithm to determine the optimal action sequences\n","docs/docs/agents/planner-agents/llm-based-planners.md":"---\nstatus: beta\n---\n\n# LLM-based planners\n\n--8<-- \"versioning-snippets.md:beta\"\n\nLLM-based planners use LLMs to generate and evaluate plans.\nThey operate on a string-based state and execute steps through LLM requests.\nString-based state means that the agent state is a single string.\nAt every step, the agent accepts an initial state string and returns the final state string as the result.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nKoog provides two simple planners:\n\n- [SimpleLLMPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-planner/index.html)\n  generates a plan only once at the very beginning and then follows the plan until it is completed.\n  To include replanning, extend `SimpleLLMPlanner` and override the `assessPlan` method,\n  indicating when the agent should replan.\n- [SimpleLLMWithCriticPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-with-critic-planner/index.html)\n  implements the `assessPlan` method that uses an LLM to check the validity of the plan via an LLM request\n  and assess whether the agent should replan.\n\nThe following example shows how to create a simple planner agent using `SimpleLLMPlanner`:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy\n    import ai.koog.agents.core.planner.PlannerAIAgent\n    import ai.koog.agents.planner.llm.SimpleLLMPlanner\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    // Create the planner\n    val planner = SimpleLLMPlanner()\n\n    // Wrap it in a planner strategy\n    val strategy = AIAgentPlannerStrategy(\n        name = \"simple-planner\",\n        planner = planner\n    )\n\n    // Configure the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"planner\") {\n            system(\"You are a helpful planning assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 50\n    )\n\n    // Create the planner agent\n    val agent = PlannerAIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = strategy,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        // Run the agent with a task\n        val result = agent.run(\"Create a plan to organize a team meeting\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-llm-based-planners-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleLLMBasedPlanner01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create the planner strategy with LLM-based planner\n    AIAgentPlannerStrategy<String, String> strategy =\n        Planners.llmBased(\"simple-planner\")\n            .build();\n\n    // Create the OpenAI executor\n    var promptExecutor = PromptExecutor.builder()\n        .openAI(\"OPENAI_API_KEY\")\n        .build();\n\n    // Create the planner agent using AIAgent builder\n    AIAgent<String, String> agent = AIAgent.builder()\n        .plannerStrategy(strategy)\n        .promptExecutor(promptExecutor)\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful planning assistant.\")\n        .maxIterations(50)\n        .build();\n\n    // Run the agent with a task\n    String result = agent.run(\"Create a plan to organize a team meeting\");\n    System.out.println(result);\n    ```\n     <!--- KNIT exampleLLMBasedPlannerJava01.java -->\n\n## Next steps\n\n- Learn about [GOAP agents](goap-agents.md)\n","docs/docs/tools/annotation-based-tools.md":"# Annotation-based tools\n\nAnnotation-based tools provide a declarative way to expose functions and methods as tools for large language models (LLMs) in both Kotlin and Java.\nBy using annotations, you can transform any function or method into a tool that LLMs can understand and use.\n\nThis approach is useful when you need to expose existing functionality to LLMs in Kotlin or Java without implementing tool descriptions manually.\n\n!!! note\n    Annotation-based tools are JVM-only and not available for other platforms. For multiplatform support, use the [class-based tool API](class-based-tools.md).\n\n## Key annotations\n\nTo start using annotation-based tools in your project, you need to understand the following key annotations:\n\n| Annotation        | Description                                                             |\n|-------------------|-------------------------------------------------------------------------|\n| `@Tool`           | Marks functions that should be exposed as tools to LLMs.                |\n| `@LLMDescription` | Provides descriptive information about your tools and their components. |\n\n\n## @Tool annotation\n\nThe `@Tool` annotation is used to mark functions (Kotlin) or methods (Java) that should be exposed as tools to LLMs.\nThe functions and methods annotated with `@Tool` are collected by reflection from objects that implement the `ToolSet` interface. For details, see [Implement the ToolSet interface](#1-implement-the-toolset-interface).\n\n### Definition\n\n```kotlin\n@Target(AnnotationTarget.FUNCTION)\npublic annotation class Tool(val customName: String = \"\")\n```\n<!--- KNIT example-annotation-based-tools-01.txt -->\n\n### Parameters\n\n| <div style=\"width:100px\">Name</div> | Required | Description                                                                              |\n|-------------------------------------|----------|------------------------------------------------------------------------------------------|\n| `customName`                        | No       | Specifies a custom name for the tool. If not provided, the name of the function is used. |\n\n### Usage\n\nTo mark a function or method as a tool, apply the `@Tool` annotation to this function or method in a class that implements the `ToolSet` interface:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    \n        @Tool(customName = \"customToolName\")\n        fun anotherTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyToolSet implements ToolSet {\n        @Tool\n        public String myTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    \n        @Tool(customName = \"customToolName\")\n        public String anotherTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-01.java -->\n\n## @LLMDescription annotation\n\nThe `@LLMDescription` annotation provides descriptive information about code elements (classes, functions, methods, parameters, and so on) to LLMs.\nThis helps LLMs understand the purpose and usage of these elements.\n\n### Definition\n\n```kotlin\n@Target(\n    AnnotationTarget.PROPERTY,\n    AnnotationTarget.CLASS,\n    AnnotationTarget.TYPE,\n    AnnotationTarget.VALUE_PARAMETER,\n    AnnotationTarget.FUNCTION\n)\npublic annotation class LLMDescription(val description: String)\n```\n<!--- KNIT example-annotation-based-tools-02.txt -->\n\n### Parameters\n\n| Name          | Required | Description                                    |\n|---------------|----------|------------------------------------------------|\n| `description` | Yes      | A string that describes the annotated element. |\n\n### Usage\n\nThe `@LLMDescription` annotation can be applied at various levels. For example:\n\n* Function level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Performs a specific operation and returns the result\")\n    fun myTool(): String {\n        // Function implementation\n        return \"Result\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-02.kt -->\n\n=== \"Java\"\n\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Performs a specific operation and returns the result\")\n    public String myTool() {\n        // Function implementation\n        return \"Result\";\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-02.java -->\n\n    \n* Parameter level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Processes input data\")\n    fun processTool(\n        @LLMDescription(\"The input data to process\")\n        input: String,\n    \n        @LLMDescription(\"Optional configuration parameters\")\n        config: String = \"\"\n    ): String {\n        // Function implementation\n        return \"Processed: $input with config: $config\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Processes input data\")\n    public String processTool(\n            @LLMDescription(description = \"The input data to process\") String input,\n            @LLMDescription(description = \"Optional configuration parameters\") String config\n    ) {\n        // Function implementation\n        return \"Processed: \" + input + \" with config: \" + config;\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-03.java -->\n\n\n## Creating a tool\n\n### 1. Implement the ToolSet interface\n\nCreate a class that implements the [`ToolSet`](api:agents-tools::ai.koog.agents.core.tools.reflect.ToolSet) interface.\nThis interface marks your class as a container for tools.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-04.java -->\n\n### 2. Add tool functions\n\nAdd functions or methods to your class and annotate them with `@Tool` to expose them as tools:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        fun getWeather(location: String): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        public String getWeather(String location) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-05.java -->\n\n### 3. Add descriptions\n\nAdd `@LLMDescription` annotations to provide context for the LLM:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for getting weather information\")\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Get the current weather for a location\")\n        fun getWeather(\n            @LLMDescription(\"The city and state/country\")\n            location: String\n        ): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for getting weather information\")\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Get the current weather for a location\")\n        public String getWeather(\n                @LLMDescription(description = \"The city and state/country\") String location\n        ) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-06.java -->\n\n### 4. Use your tools with an agent\n\nNow you can use your tools with an agent:\n\n=== \"Kotlin\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleAnnotationBasedTools06.MyFirstToolSet\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    const val apiToken = \"\"\n    -->\n    ```kotlin\n    fun main() {\n        runBlocking {\n            // Create your tool set\n            val weatherTools = MyFirstToolSet()\n    \n            // Create an agent with your tools\n    \n            val agent = AIAgent(\n                promptExecutor = simpleOpenAIExecutor(apiToken),\n                systemPrompt = \"Provide weather information for a given location.\",\n                llmModel = OpenAIModels.Chat.GPT4o,\n                toolRegistry = ToolRegistry {\n                    tools(weatherTools)\n                }\n            )\n    \n            // The agent can now use your weather tools\n            agent.run(\"What's the weather like in New York?\")\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    String apiToken = System.getenv(\"OPENAI_API_KEY\");\n\n    // Create your tool set\n     MyFirstToolSet weatherTools = new MyFirstToolSet();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(weatherTools)\n        .build();\n\n    // Create an agent with your tools\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"Provide weather information for a given location.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(toolRegistry)\n        .build();\n\n    // The agent can now use your weather tools\n    String result = agent.run(\"What's the weather like in New York?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT example-annotation-based-tools-java-07.java -->\n\n## Usage examples\n\nHere are some real-world examples of tool annotations.\n\n### Basic example: Switch controller\n\nThis example shows a simple tool set for controlling a switch:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    class Switch(private var state: Boolean) {\n        fun switch(state: Boolean) {\n            this.state = state\n        }\n        fun isOn(): Boolean {\n            return state\n        }\n    }\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for controlling a switch\")\n    class SwitchTools(val switch: Switch) : ToolSet {\n        @Tool\n        @LLMDescription(\"Switches the state of the switch\")\n        fun switch(\n            @LLMDescription(\"The state to set (true for on, false for off)\")\n            state: Boolean\n        ): String {\n            switch.switch(state)\n            return \"Switched to ${if (state) \"on\" else \"off\"}\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Returns the current state of the switch\")\n        fun switchState(): String {\n            return \"Switch is ${if (switch.isOn()) \"on\" else \"off\"}\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-08.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class Switch {\n        private boolean state;\n\n        public Switch(boolean state) {\n            this.state = state;\n        }\n\n        // \"switch\" is a reserved keyword in Java, so we use a different method name\n        public void setState(boolean state) {\n            this.state = state;\n        }\n\n        public boolean isOn() {\n            return state;\n        }\n    }\n    \n    @LLMDescription(description = \"Tools for controlling a switch\")\n    public class SwitchTools implements ToolSet {\n        private final Switch sw;\n\n        public SwitchTools(Switch sw) {\n            this.sw = sw;\n        }\n\n        @Tool\n        @LLMDescription(description = \"Switches the state of the switch\")\n        public String switchStateTo(\n                @LLMDescription(description = \"The state to set (true for on, false for off)\") boolean state\n        ) {\n            sw.setState(state);\n            return \"Switched to \" + (state ? \"on\" : \"off\");\n        }\n\n        @Tool\n        @LLMDescription(description = \"Returns the current state of the switch\")\n        public String switchState() {\n            return \"Switch is \" + (sw.isOn() ? \"on\" : \"off\");\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-08.java -->\n\nWhen an LLM needs to control a switch, it can understand the following information from the provided description:\n\n- The purpose and functionality of the tools.\n- The required parameters for using the tools.\n- The acceptable values for each parameter.\n- The expected return values upon execution.\n\n### Advanced example: Diagnostic tools\n\nThis example shows a more complex tool set for device diagnostics:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing diagnostics and troubleshooting on devices\")\n    class DiagnosticToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Run diagnostic on a device to check its status and identify any issues\")\n        fun runDiagnostic(\n            @LLMDescription(\"The ID of the device to diagnose\")\n            deviceId: String,\n    \n            @LLMDescription(\"Additional information for the diagnostic (optional)\")\n            additionalInfo: String = \"\"\n        ): String {\n            // Implementation\n            return \"Diagnostic results for device $deviceId\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Analyze an error code to determine its meaning and possible solutions\")\n        fun analyzeError(\n            @LLMDescription(\"The error code to analyze (e.g., 'E1001')\")\n            errorCode: String\n        ): String {\n            // Implementation\n            return \"Analysis of error code $errorCode\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-09.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing diagnostics and troubleshooting on devices\")\n    public class DiagnosticToolSet implements ToolSet {\n        // Convenience overload (not exposed as a tool)\n        public String runDiagnostic(String deviceId) {\n            return runDiagnostic(deviceId, \"\");\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Run diagnostic on a device to check its status and identify any issues\")\n        public String runDiagnostic(\n                @LLMDescription(description = \"The ID of the device to diagnose\") String deviceId,\n                @LLMDescription(description = \"Additional information for the diagnostic (optional)\") String additionalInfo\n        ) {\n            // Implementation\n            return \"Diagnostic results for device \" + deviceId;\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Analyze an error code to determine its meaning and possible solutions\")\n        public String analyzeError(\n                @LLMDescription(description = \"The error code to analyze (e.g., 'E1001')\") String errorCode\n        ) {\n            // Implementation\n            return \"Analysis of error code \" + errorCode;\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-09.java -->\n\n\n## Best practices\n\n* **Provide clear descriptions**: write clear, concise descriptions that explain the purpose and behavior of tools, parameters, and return values.\n* **Describe all parameters**: add `@LLMDescription` to all parameters to help LLMs understand what each parameter is for.\n* **Use consistent naming**: use consistent naming conventions for tools and parameters to make them more intuitive.\n* **Group related tools**: group related tools in the same `ToolSet` implementation and provide a class-level description.\n* **Return informative results**: make sure tool return values provide clear information about the result of the operation.\n* **Handle errors gracefully**: include error handling in your tools and return informative error messages.\n* **Document default values**: when parameters have default values (Kotlin) or overloads (Java), document this in the description.\n* **Keep tools focused**: Each tool should perform a specific, well-defined task rather than trying to do too many things.\n\n## Troubleshooting common issues\n\nWhen working with tool annotations, you might encounter some common issues.\n\n### Tools not being recognized\n\nIf the agent does not recognize your tools, check the following:\n\n- Your class implements the `ToolSet` interface.\n- All tool functions or methods are annotated with `@Tool`.\n- Tool functions or methods have appropriate return types (`String` is recommended for simplicity).\n- Your tools are properly registered with the agent.\n\n### Unclear tool descriptions\n\nIf the LLM does not use your tools correctly or misunderstands their purpose, try the following:\n\n- Use primitive parameter types when possible (`String`, `Boolean`, `Int` in Kotlin, or `String`, `boolean`, `int` in Java).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n- Note that Java doesn't support default parameters. Use method overloading instead.\n\n### Parameter type issues\n\nIf the LLM provides incorrect parameter types, try the following:\n\n- Use simple parameter types when possible (`String`, `Boolean`, `Int`).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n\n### Performance issues\n\nIf your tools cause performance problems, try the following:\n\n- Keep tool implementations lightweight.\n- For resource-intensive operations, consider implementing asynchronous processing.\n- Cache results when appropriate.\n- Log tool usage to identify bottlenecks.\n","docs/docs/tools/built-in-tools.md":"# Built-in tools\n\nKoog provides built-in tools for Kotlin and Java to help you quickly prototype and experiment with agent-user interaction.\nThese tools are not intended for production use. To use them, add `ai.koog:agents-ext` to your dependencies.\nThe following built-in tools are available:\n\n| Tool              | <div style=\"width:115px\">Name</div> | Description                                                                                                              |\n|-------------------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| SayToUser         | `__say_to_user__`                   | Lets the agent send a message to the user. It prints the agent message to the console with the `Agent says: ` prefix.    |\n| AskUser           | `__ask_user__`                      | Lets the agent ask the user for input. It prints the agent message to the console and waits for user response.           |\n| ExitTool          | `__exit__`                          | Lets the agent finish the conversation and terminate the session.                                                        |\n| ReadFileTool      | `__read_file__`                     | Reads text file with optional line range selection. Returns formatted content with metadata using 0-based line indexing. |\n| EditFileTool      | `__edit_file__`                     | Makes a single, targeted text replacement in a file; can also create new files or fully replace contents.                |\n| ListDirectoryTool | `__list_directory__`                | Lists directory contents as a hierarchical tree with optional depth control and glob filtering.                          |\n| WriteFileTool     | `__write_file__`                    | Writes text content to a file (creating parent directories if needed).                                                   |\n\n\n## Registering built-in tools\n\nLike any other tool, a built-in tool must be added to the tool registry to become available for an agent. Here is an example:\n\n<!--- INCLUDE\nimport ai.koog.agents.core.agent.AIAgent\nimport ai.koog.agents.core.tools.ToolRegistry\nimport ai.koog.agents.ext.tool.SayToUser\nimport ai.koog.agents.ext.tool.AskUser\nimport ai.koog.agents.ext.tool.ExitTool\nimport ai.koog.agents.ext.tool.file.ListDirectoryTool\nimport ai.koog.agents.ext.tool.file.ReadFileTool\nimport ai.koog.agents.ext.tool.file.WriteFileTool\nimport ai.koog.prompt.executor.clients.openai.OpenAIModels\nimport ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\nimport ai.koog.rag.base.files.JVMFileSystemProvider\n\nconst val apiToken = \"\"\n\n-->\n```kotlin\n// Create a tool registry with all built-in tools\nval toolRegistry = ToolRegistry {\n    tool(SayToUser)\n    tool(AskUser)\n    tool(ExitTool)\n    tool(ReadFileTool(JVMFileSystemProvider.ReadOnly))\n    tool(ListDirectoryTool(JVMFileSystemProvider.ReadOnly))\n    tool(WriteFileTool(JVMFileSystemProvider.ReadWrite))\n}\n\n// Pass the registry when creating an agent\nval agent = AIAgent(\n    promptExecutor = simpleOpenAIExecutor(apiToken),\n    systemPrompt = \"You are a helpful assistant.\",\n    llmModel = OpenAIModels.Chat.GPT4o,\n    toolRegistry = toolRegistry\n)\n\n```\n<!--- KNIT example-built-in-tools-01.kt -->\n\nYou can create a comprehensive set of capabilities for your agent by combining built-in tools and custom tools within the same registry in both Kotlin and Java.\nTo learn more about custom tools, see [Annotation-based tools](annotation-based-tools.md) and [Class-based tools](class-based-tools.md).\n","docs/docs/tools/class-based-tools.md":"# Class-based tools\n\nThis section explains the API designed for scenarios that require enhanced flexibility and customized behavior.\nWith this approach in Kotlin, you have full control over a tool, including its parameters, metadata, execution logic, and how it is registered and invoked. In Java, tools are created using annotation-based methods with reflection-based registration.\n\nThis level of control is ideal for creating sophisticated tools that extend basic use cases, enabling seamless integration into agent sessions and workflows.\n\nThis page describes how to implement a tool in both Kotlin and Java, manage tools through registries, call them, and use within node-based agent architectures.\n\n!!! note\n    The API is multiplatform for Kotlin. Java tools are implemented using annotation-based methods and registered via reflection. This lets you use the same tools across different platforms in Kotlin, while Java provides full JVM interoperability.\n\n## Tool implementation\n\nThe Koog framework provides the following approaches for implementing tools:\n\nFor Kotlin:\n\n* Using the base class `Tool` for all tools. You should use this class when you need to return non-text results or require complete control over the tool behavior.\n* Using the `SimpleTool` class that extends the base `Tool` class and simplifies the creation of tools that return text results. You should use this approach for scenarios where the \n  tool only needs to return a text.\n\nBoth approaches use the same core components but differ in implementation and the results they return.\n\nFor Java:\n\n* Using annotation-based methods (`@Tool` and `@LLMDescription`) with reflection-based registration. This is the recommended approach for Java interoperability, as subclassing Kotlin's `Tool` or `SimpleTool` from Java is not supported due to suspend function limitations.\n\n### Tool class (Kotlin)\n\nThe [`Tool<Args, Result>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html) abstract class is the base class for creating tools in Kotlin.\nIt lets you create tools that accept specific argument types (`Args`) and return results of various types (`Result`).\n\nEach tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the tool.                                                                                                                                                                                                                                                                                                                                                             |\n| `Result`                                 | The serializable type of result that the tool returns. If you want to present tool results in a custom format, please inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) class and implement `textForLLM(): String` method                                                                                                           |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are deserialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                                                                                                                                                        |\n| `resultSerializer`                       | The overridden variable that defines how the result of the tool is deserialized. See also [resultSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/result-serializer.html). If you chose to inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) consider using `ToolResultUtils.toTextSerializer()` |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/>- `optionalParameters` (empty by default)<br/>See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html).                                                                                                                                |\n| `execute()`                              | The function that implements the logic of the tool. It takes arguments of type `Args` and returns a result of type `Result`. See also [execute()]().                                                                                                                                                                                                                                                                                  |\n\n!!! note \"Java Implementation\"\n    In Java, instead of subclassing `Tool<Args, Result>`, use annotation-based methods with `@Tool` and `@LLMDescription`. The framework handles serialization and registration automatically through reflection. For more\n    details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` property; in Java, use `@LLMDescription` annotations.\n\n#### Usage example\n\nHere is an example of a custom tool implementation using the `Tool` class that returns a numeric result:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    -->\n    ```kotlin\n    // Implement a simple calculator tool that adds two digits\n    object CalculatorTool : Tool<CalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"calculator\",\n        description = \"A simple calculator that can add two digits (0-9).\"\n    ) {\n\n        // Arguments for the calculator tool\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        ) {\n            init {\n                require(digit1 in 0..9) { \"digit1 must be a single digit (0-9)\" }\n                require(digit2 in 0..9) { \"digit2 must be a single digit (0-9)\" }\n            }\n        }\n\n        // Function to add two digits\n        override suspend fun execute(args: Args): Int = args.digit1 + args.digit2\n    }\n    ```\n    <!--- KNIT example-class-based-tools-01.kt -->\n\nAfter implementing your tool, you need to add it to a tool registry and then use it with an agent. For details, see [Tool registry](../tools/index.md#tool-registry).\n\nFor more details, see [API reference](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html).\n\n#### Reading the agent context from a tool\n\nTools that need the agent's full state (LLM context, run id, configuration, storage, ...) extend `AgentContextAwareTool<Args, Result>` instead of `Tool<Args, Result>`. The framework injects the live `AIAgentContext` driving the call, and the tool receives it as a typed parameter rather than reading it out of the argument schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.context.AIAgentContext\n    import ai.koog.agents.core.agent.tools.AgentContextAwareTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // A tool that reads the live AIAgentContext driving the call.\n    object TracingCalculatorTool : AgentContextAwareTool<TracingCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"tracing_calculator\",\n        description = \"Adds two digits and emits a log line tagged with the agent run id.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, context: AIAgentContext): Int {\n            val runId = context.runId\n            // ... use runId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-01.kt -->\n\n`AgentContextAwareTool` is dispatched by the framework via a per-call `ToolCallMetadata` side channel that the framework manages on the tool's behalf. Invoking such a tool outside an agent run throws `IllegalStateException` because no `AIAgentContext` was injected; production code should always go through `ContextualAgentEnvironment`, and unit tests can supply the context explicitly via `ToolCallMetadata.of(AgentContextAwareTool.AgentContextKey to context)`.\n\n#### Reading raw per-call metadata\n\nA small number of tools want to read caller- or feature-contributed entries that are *not* the agent context (for example a distributed-tracing span id contributed by an observability feature). These tools extend `ToolBase<Args, Result>` directly, which exposes the full `ToolCallMetadata` bag:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolBase\n    import ai.koog.agents.core.tools.ToolCallMetadata\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    object SpanAwareCalculatorTool : ToolBase<SpanAwareCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"span_aware_calculator\",\n        description = \"Adds two digits, propagating a tracing span id from caller or feature metadata.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, metadata: ToolCallMetadata): Int {\n            val traceSpanId = metadata[\"trace.span.id\"] as? String\n            // ... use traceSpanId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-02.kt -->\n\nCallers can pass metadata through `SafeTool.execute(args, serializer, metadata)` or directly through `AIAgentEnvironment.executeTool(toolCall, metadata)`. Features can contribute metadata for every tool call during installation by calling `pipeline.provideToolCallMetadata(this) { eventContext -> mapOf(...) }`. Caller-supplied metadata always wins over feature contributions on key collision.\n\nExisting tools that extend `Tool<Args, Result>` and override `execute(args)` continue to work unchanged: the framework dispatches them through the same path and discards any `ToolCallMetadata`. To opt in to metadata, switch to `AgentContextAwareTool` (typed context access) or `ToolBase` (raw bag access).\n\n### SimpleTool class (Kotlin)\n\nThe [`SimpleTool<Args>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/index.html) abstract class extends `Tool<Args, ToolResult.Text>` and simplifies the creation of tools that return text results.\n\nEach simple tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                              |\n|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the custom tool.                                                                                                                                                                                                                         |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are serialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                             |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/> - `optionalParameters` (empty by default)<br/> See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html). |\n| `doExecute()`                            | The overridden function that describes the main action performed by the tool. It takes arguments of type `Args` and returns a `String`. See also [doExecute()](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/do-execute.html).                                          |\n\n!!! note \"Java Implementation\"\n    In Java, the equivalent approach is to use annotation-based methods that return `String`. The framework automatically handles the text result wrapping. For more details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` and constructor parameters; in Java, use `@Tool` and `@LLMDescription` annotations.\n\n#### Usage example \n\nHere is an example of a custom tool implementation using `SimpleTool` in Kotlin:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.SimpleTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // Create a tool that casts a string expression to a double value\n    object CastToDoubleTool : SimpleTool<CastToDoubleTool.Args>(\n        argsType = typeToken<Args>(),\n        name = \"cast_to_double\",\n        description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"An expression to case to double\")\n            val expression: String,\n            @property:LLMDescription(\"A comment on how to process the expression\")\n            val comment: String\n        )\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): String {\n            return \"Result: ${castToDouble(args.expression)}, \" + \"the comment was: ${args.comment}\"\n        }\n\n        // Function to cast a string expression to a double value\n        private fun castToDouble(expression: String): Double {\n            return expression.toDoubleOrNull() ?: 0.0\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-02.kt -->\n\n### Annotation-based methods (Java)\n\nTo implement tools in Java, instead of subclassing `Tool` or `SimpleTool`, use annotation-based methods with `@Tool` and\n`@LLMDescription`. Koog handles serialization and registration automatically through reflection. To learn more about the\nimplementation, see Java examples below.\n\n#### Usage examples\n\nThis is an example of a tool implementation in Java, equivalent to using the `Tool` class in Kotlin.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent: implement the tool as a Java method and register it via ToolRegistry.builder().\n    // This is the recommended Java interop path instead of subclassing the Kotlin Tool base class.\n    public final class CalculatorTool {\n        private CalculatorTool() {}\n    \n        @Tool(customName = \"calculator\")\n        @LLMDescription(description = \"A simple calculator that can add two digits (0-9).\")\n        public static int calculator(\n                @LLMDescription(description = \"The first digit to add (0-9)\") int digit1,\n                @LLMDescription(description = \"The second digit to add (0-9)\") int digit2\n        ) {\n            if (digit1 < 0 || digit1 > 9) throw new IllegalArgumentException(\"digit1 must be a single digit (0-9)\");\n            if (digit2 < 0 || digit2 > 9) throw new IllegalArgumentException(\"digit2 must be a single digit (0-9)\");\n            return digit1 + digit2;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CalculatorTool.class.getMethod(\"calculator\", int.class, int.class))\n                .build();\n        }\n    }\n    // Note: Subclassing the Kotlin Tool<TArgs, TResult> and overriding a suspend execute(...) from Java is not supported.\n    // The Java interop uses reflection-based registration of Java methods as tools.\n    ```\n    <!--- KNIT example-class-based-tools-java-01.java -->\n\nHere is an example of a tool implementation in Java, equivalent to using the `SimpleTool` class in Kotlin. This example\nimplements a simple tool that returns a text result.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent of SimpleTool: provide a Java method and register it as a tool.\n    public final class CastToDoubleTool {\n        private CastToDoubleTool() {}\n    \n        @Tool(customName = \"cast_to_double\")\n        @LLMDescription(description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\")\n        public static String castToDouble(\n                @LLMDescription(description = \"An expression to case to double\") String expression,\n                @LLMDescription(description = \"A comment on how to process the expression\") String comment\n        ) {\n            double value;\n            try {\n                value = Double.parseDouble(expression);\n            } catch (Exception e) {\n                value = 0.0;\n            }\n            return \"Result: \" + value + \", the comment was: \" + comment;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CastToDoubleTool.class.getMethod(\"castToDouble\", String.class, String.class))\n                .build();\n        }\n    }\n    // Note: Extending Kotlin SimpleTool<TArgs> from Java is not required; registering a Java method is the idiomatic approach.\n    ```\n    <!--- KNIT example-class-based-tools-java-02.java -->\n\n### Sending tool result to LLM in custom format\n\nFor Kotlin:\n\nIf you are not happy with JSON results sent to LLM (in some cases, LLMs can work better if tool output is structured as Markdown, for instance), you have to follow the following steps:\n\n1. Implement `ToolResult.TextSerializable` interface, and override `textForLLM()` method\n2. Override `resultSerializer` using `ToolResultUtils.toTextSerializer<T>()`\n\nFor Java:\n\nReturn formatted text (such as Markdown) directly as a `String` from your annotated method. The framework handles this automatically.\n\n#### Example\n\nHere is an example showing custom formatted output in both Kotlin and Java:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.prompt.markdown.markdown\n    -->\n    ```kotlin\n    // A tool that edits file\n    object EditFile : Tool<EditFile.Args, EditFile.Result>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Result>(),\n        name = \"edit_file\",\n        description = \"Edits the given file\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        public data class Args(\n            val path: String,\n            val original: String,\n            val replacement: String\n        )\n\n        @Serializable\n        public data class Result(\n            private val patchApplyResult: PatchApplyResult\n        ) {\n\n            @Serializable\n            public sealed interface PatchApplyResult {\n                @Serializable\n                public data class Success(val updatedContent: String) : PatchApplyResult\n\n                @Serializable\n                public sealed class Failure(public val reason: String) : PatchApplyResult\n            }\n\n            // Textual output (in Markdown format) that will be visible to the LLM after the tool finishes.\n            fun textForLLM(): String = markdown {\n                if (patchApplyResult is PatchApplyResult.Success) {\n                    line {\n                        bold(\"Successfully\").text(\" edited file (patch applied)\")\n                    }\n                } else {\n                    line {\n                        text(\"File was \")\n                            .bold(\"not\")\n                            .text(\" modified (patch application failed: ${(patchApplyResult as PatchApplyResult.Failure).reason})\")\n                    }\n                }\n            }\n\n            override fun toString(): String = textForLLM()\n        }\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): Result {\n            return TODO(\"Implement file edit\")\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n\n    // Java equivalent: return Markdown text directly to the LLM from a Java method and register it as a tool.\n    // This avoids needing a custom serializable Result type (which would require Kotlin serialization support).\n    public final class EditFile {\n        private EditFile() {}\n\n        @Tool(customName = \"edit_file\")\n        @LLMDescription(description = \"Edits the given file\")\n        public static String editFile(\n                String path,\n                String original,\n                String replacement\n        ) {\n            // TODO: Implement file edit logic; below is a placeholder illustrating Markdown output\n            boolean success = false;\n            if (success) {\n                return \"**Successfully** edited file (patch applied)\";\n            } else {\n                return \"File was **not** modified (patch application failed: reason)\";\n            }\n        }\n\n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(EditFile.class.getMethod(\"editFile\", String.class, String.class, String.class))\n                .build();\n        }\n    }\n    // Note: If you need a structured custom Result object from Java, you must expose a Kotlin @Serializable type\n    // or another serializer-aware type. Returning String works out-of-the-box with Koog's Java interop.\n    ```\n    <!--- KNIT example-class-based-tools-java-03.java -->\n\nAfter implementing your tool in Kotlin or Java, you need to add it to a tool registry and then use it with an agent.\nFor details, see [Tool registry](../tools/index#tool-registry).\n","docs/docs/tools/index.md":"# Overview\n\n Agents use tools to perform specific tasks or access external systems.\n\n## Tool workflow\n\nThe Koog framework offers the following workflow for working with tools in Kotlin and Java:\n\n1. Create a custom tool or use one of the built-in tools.\n2. Add the tool to a tool registry.\n3. Pass the tool registry to an agent.\n4. Use the tool with the agent.\n\n### Available tool types\n\nThere are three types of tools in the Koog framework:\n\n- Built-in tools that provide functionality for agent-user interaction and conversation management. For details, see [Built-in tools](built-in-tools.md).\n- Annotation-based custom tools that let you expose functions as tools to LLMs. For details, see [Annotation-based tools](annotation-based-tools.md).\n- Custom tools that let you control tool parameters, metadata, execution logic, and how it is registered and invoked. For details, see [Class-based\n  tools](class-based-tools.md).\n\n### Tool registry\n\nBefore you can use a tool in an agent, you must add it to a tool registry.\nThe tool registry manages all tools available to the agent.\n\nThe key features of the tool registry:\n\n- Organizes tools.\n- Supports merging of multiple tool registries.\n- Provides methods to retrieve tools by name or type.\n\nTo learn more, see [ToolRegistry](api:agents-tools::ai.koog.agents.core.tools.ToolRegistry).\n\nHere is an example of how to create the tool registry and add the tool to it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    val myTool = MyToolSet()\n    -->\n    ```kotlin\n    val toolRegistry = ToolRegistry {\n        tools(myTool)\n    }\n    ```\n    <!--- KNIT example-tools-overview-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create an instance of your ToolSet\n    MyToolSet myTool = new MyToolSet();\n\n    // Build the ToolRegistry and register tools from the ToolSet\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(myTool)\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-01.java -->\n\nTo merge multiple tool registries, do the following:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class FirstToolSet : ToolSet {\n        @Tool\n        fun firstSampleTool(): String {\n            // Tool implementation\n            return \"First result\"\n        }\n    }\n    class SecondToolSet : ToolSet {\n        @Tool\n        fun secondSampleTool(): String {\n            // Tool implementation\n            return \"Second result\"\n        }\n    }\n    val firstSampleTool = FirstToolSet()\n    val secondSampleTool = SecondToolSet()\n    -->\n    ```kotlin\n    val firstToolRegistry = ToolRegistry {\n        tools(firstSampleTool)\n    }\n    \n    val secondToolRegistry = ToolRegistry {\n        tools(secondSampleTool)\n    }\n    \n    val newRegistry = firstToolRegistry + secondToolRegistry\n    ```\n    <!--- KNIT example-tools-overview-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create instances of your ToolSets\n    FirstToolSet firstSampleTool = new FirstToolSet();\n    SecondToolSet secondSampleTool = new SecondToolSet();\n\n    // Build separate tool registries\n    ToolRegistry firstToolRegistry = ToolRegistry.builder()\n        .tools(firstSampleTool)\n        .build();\n\n    ToolRegistry secondToolRegistry = ToolRegistry.builder()\n        .tools(secondSampleTool)\n        .build();\n\n    ToolRegistry newRegistry = firstToolRegistry.plus(secondToolRegistry);\n    ```\n    <!--- KNIT example-tools-overview-java-02.java -->\n\n### Passing tools to an agent\n\nTo enable an agent to use a tool, you need to provide a tool registry that contains this tool as an argument when creating the agent:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.example.exampleToolsOverview01.toolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Agent initialization\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        systemPrompt = \"You are a helpful assistant with strong mathematical skills.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        // Pass your tool registry to the agent\n        toolRegistry = toolRegistry\n    )\n    ```\n    <!--- KNIT example-tools-overview-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are a helpful assistant with strong mathematical skills.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(ToolRegistry.builder()\n            .tools(secondSampleTool)\n            .build()\n        )\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-03.java -->\n\n### Calling tools\n\nThere are several ways to call tools within your agent code. The recommended approach is to use the provided methods\nin the agent context rather than calling tools directly, as this ensures proper handling of tool operation within the\nagent environment.\n\n!!! tip\n    Ensure you have implemented proper [error handling](../features/agent-event-handlers.md) in your tools to prevent agent failure.\n\nThe tools are called within a specific session context represented by `AIAgentLLMWriteSession`.\nIt provides several methods for calling tools so that you can:\n\n- Call a tool with the given arguments.\n- Call a tool by its name and the given arguments.\n- Call a tool by the provided tool class and arguments.\n- Call a tool of the specified type with the given arguments.\n- Call a tool that returns a raw string result.\n\nFor more details, the API reference for [AIAgentLLMWriteSession](api:agents-core::ai.koog.agents.core.agent.session.AIAgentLLMWriteSession).\n\n#### Parallel tool calls\n\nYou can also call tools in parallel using the `toParallelToolCallsRaw` extension. For example:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.builder.node\n    import ai.koog.agents.core.tools.SimpleTool\n    import kotlinx.coroutines.flow.collect\n    import kotlinx.coroutines.flow.flow\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    @Serializable\n    data class Book(\n        val title: String,\n        val author: String,\n        val description: String\n    )\n    \n    class BookTool() : SimpleTool<Book>(\n        argsType = typeToken<Book>(),\n        name = NAME,\n        description = \"A tool to parse book information from Markdown\"\n    ) {\n        companion object {\n            const val NAME = \"book\"\n        }\n    \n        override suspend fun execute(args: Book): String {\n            println(\"${args.title} by ${args.author}:\\n ${args.description}\")\n            return \"Done\"\n        }\n    }\n    \n    val strategy = strategy<Unit, Unit>(\"strategy-name\") {\n    \n        /*...*/\n    \n        val myNode by node<Unit, Unit> { _ ->\n            llm.writeSession {\n                flow {\n                    emit(Book(\"Book 1\", \"Author 1\", \"Description 1\"))\n                }.toParallelToolCallsRaw(BookTool::class).collect()\n            }\n        }\n    }\n    \n    ```\n    <!--- KNIT example-tools-overview-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-04.java -->\n\n#### Calling tools from nodes\n\nWhen building agent workflows with nodes, you can use special nodes to call tools:\n\n* **nodeExecuteTool**: calls a single tool call and returns its result. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteTool).\n\n* **nodeExecuteSingleTool** that calls a specific tool with the provided arguments. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteSingleTool).\n\n* **nodeExecuteMultipleTools** that performs multiple tool calls and returns their results. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteMultipleTools).\n\n* **nodeLLMSendToolResult** that sends a tool result to the LLM and gets a response. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendToolResult).\n\n* **nodeLLMSendMultipleToolResults** that sends multiple tool results to the LLM. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendMultipleToolResults).\n\n## Using agents as tools\n\nThe framework provides the capability to convert any AI agent into a tool that can be used by other agents. \nThis powerful feature enables you to create hierarchical agent architectures where specialized agents can be called as tools by higher-level orchestrating agents.\n\n### Converting agents to tools\n\nTo convert an agent into a tool, use the `AIAgentService` and the `createAgentTool()` extension function:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.AIAgentService\n    import ai.koog.agents.core.agent.createAgentTool\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.serialization.typeToken\n    const val apiKey = \"\"\n    val analysisToolRegistry = ToolRegistry {}\n    -->\n    ```kotlin\n    // Create a specialized agent service, responsible for creating financial analysis agents.\n    val analysisAgentService = AIAgentService(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a financial analysis specialist.\",\n        toolRegistry = analysisToolRegistry\n    )\n    \n    // Create a tool that would run financial analysis agent once called.\n    val analysisAgentTool = analysisAgentService.createAgentTool(\n        agentName = \"analyzeTransactions\",\n        agentDescription = \"Performs financial transaction analysis\",\n        inputDescription = \"Transaction analysis request\",\n        inputType = typeToken<String>(),\n    )\n    ```\n    <!--- KNIT example-tools-overview-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-05.java -->\n\n\n### Using agent tools in other agents\n\nOnce converted to a tool, you can add the agent tool to another agent's tool registry:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleToolsOverview05.analysisAgentTool\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    const val apiKey = \"\"\n    -->\n    ```kotlin\n    // Create a coordinator agent that can use specialized agents as tools\n    val coordinatorAgent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You coordinate different specialized services.\",\n        toolRegistry = ToolRegistry {\n            tool(analysisAgentTool)\n            // Add other tools as needed\n        }\n    )\n    ```\n    <!--- KNIT example-tools-overview-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-06.java -->\n\n\n### Agent tool execution\n\nWhen an agent tool is called:\n\n1. The arguments are deserialized according to the input descriptor.\n2. The wrapped agent is executed with the deserialized input.\n3. The agent's output is serialized and returned as the tool result.\n\n### Benefits of agents as tools\n\n- **Modularity**: Break complex workflows into specialized agents.\n- **Reusability**: Use the same specialized agent across multiple coordinator agents.\n- **Separation of concerns**: Each agent can focus on its specific domain.\n","docs/docs/tools/tool-descriptor-schemer.md":"# ToolDescriptorSchemer\n\n`ToolDescriptorSchemer` is an extension point that converts a `ToolDescriptor` into a JSON Schema object compatible with specific LLM providers. It can be implemented in both Kotlin and Java.\n\nKey points:\n\n- Location: `ai.koog.agents.core.tools.serialization.ToolDescriptorSchemer`\n- Contract: a single function `scheme(toolDescriptor: ToolDescriptor): JsonObject` or `generate(ToolDescriptor toolDescriptor): JsonObject` (Java)\n- Implementations provided:\n  - `OpenAICompatibleToolDescriptorSchemer` — generates schemas compatible with OpenAI‑style function/tool definitions.\n  - `OllamaToolDescriptorSchemer` — generates schemas compatible with Ollama tool JSON.\n\n<!--- INCLUDE\nimport ai.koog.agents.core.tools.ToolDescriptor\nimport ai.koog.agents.core.tools.ToolParameterDescriptor\nimport ai.koog.agents.core.tools.ToolParameterType\nimport kotlinx.serialization.json.JsonObject\n-->\n```kotlin\n// Interface\ninterface ToolDescriptorSchemaGenerator {\nfun generate(toolDescriptor: ToolDescriptor): JsonObject\n}\n```\n<!--- KNIT example-tool-descriptor-schemer-01.kt -->\n\n\n## Why use it\n\nIf you want to provide a custom scheme for existing or new LLM providers in Kotlin or Java, implement this interface to convert Koog’s `ToolDescriptor` into the expected JSON Schema format.\n\n## Implementation example\n\nBelow is a minimal custom implementation in both Kotlin and Java that renders only a subset of parameter types to illustrate how to plug into the SPI. Real implementations should cover all `ToolParameterType`s (String, Integer, Float, Boolean, Null, Enum, List, Object, AnyOf).\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.serialization.ToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    -->\n    ```kotlin\n    \n    class MinimalSchemer : ToolDescriptorSchemaGenerator {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public static class MinimalSchemer extends OpenAICompatibleToolDescriptorSchemaGenerator {\n        @Override\n        public JsonObject generate(ToolDescriptor toolDescriptor) {\n            Map<String, JsonElement> root = new LinkedHashMap<>();\n            root.put(\"type\", JsonPrimitive(\"object\"));\n\n            // properties\n            Map<String, JsonElement> props = new LinkedHashMap<>();\n            for (ToolParameterDescriptor p : concat(toolDescriptor.getRequiredParameters(), toolDescriptor.getOptionalParameters())) {\n                Map<String, JsonElement> prop = new LinkedHashMap<>();\n                prop.put(\"description\", JsonPrimitive(p.getDescription()));\n\n                ToolParameterType t = p.getType();\n                if (t == ToolParameterType.String.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                } else if (t == ToolParameterType.Integer.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"integer\"));\n                } else if (t instanceof ToolParameterType.Enum) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                    String[] entries = ((ToolParameterType.Enum) t).getEntries();\n                    List<JsonElement> enumVals = new ArrayList<>();\n                    for (String e : entries) enumVals.add(JsonPrimitive(e));\n                    prop.put(\"enum\", new JsonArray(enumVals));\n                } else {\n                    prop.put(\"type\", JsonPrimitive(\"string\")); // fallback for brevity\n                }\n\n                props.put(p.getName(), new JsonObject(prop));\n            }\n            root.put(\"properties\", new JsonObject(props));\n\n            // required array\n            List<JsonElement> required = new ArrayList<>();\n            for (ToolParameterDescriptor p : toolDescriptor.getRequiredParameters()) {\n                required.add(JsonPrimitive(p.getName()));\n            }\n            root.put(\"required\", new JsonArray(required));\n\n            return new JsonObject(root);\n        }\n\n        private static List<ToolParameterDescriptor> concat(List<ToolParameterDescriptor> a, List<ToolParameterDescriptor> b) {\n            List<ToolParameterDescriptor> res = new ArrayList<>(a.size() + b.size());\n            res.addAll(a);\n            res.addAll(b);\n            return res;\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-01.java -->\n\n## Using with a client\n\nTypically, you do not need to call a schemer directly. Koog clients accept a list of `ToolDescriptor` objects and apply the correct schemer internally when serializing requests for the provider.\n\nThe example below defines a simple tool and passes it to the OpenAI client. The client will use `OpenAICompatibleToolDescriptorSchemer` under the hood to build the JSON schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.prompt.executor.clients.openai.OpenAILLMClient\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.prompt.Prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    import kotlinx.coroutines.runBlocking\n    class MinimalSchemer : OpenAICompatibleToolDescriptorSchemaGenerator() {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    -->\n    ```kotlin\n    val client = OpenAILLMClient(apiKey = System.getenv(\"OPENAI_API_KEY\"), toolsConverter = MinimalSchemer())\n    \n    val getUserTool = ToolDescriptor(\n        name = \"get_user\",\n        description = \"Returns user profile by id\",\n        requiredParameters = listOf(\n            ToolParameterDescriptor(\n                name = \"id\",\n                description = \"User id\",\n                type = ToolParameterType.String\n            )\n        )\n    )\n    \n    val prompt = Prompt.build(id = \"p1\") { user(\"Hello\") }\n    val responses = runBlocking {\n        client.execute(\n            prompt = prompt,\n            model = OpenAIModels.Chat.GPT4o,\n            tools = listOf(getUserTool)\n        )\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Custom schemer extending the OpenAI-compatible one is Kotlin-only in the docs; for Java example we reuse MinimalSchemer from above.\n    OpenAILLMClient client = openAIClient(System.getenv(\"OPENAI_API_KEY\"), new OpenAIClientSettings(), null, null, new OpenAICompatibleToolDescriptorSchemaGenerator());\n    \n    ToolDescriptor getUserTool = new ToolDescriptor(\n        \"get_user\",\n        \"Returns user profile by id\",\n        Collections.singletonList(new ToolParameterDescriptor(\n            \"id\",\n            \"User id\",\n            ToolParameterType.String.INSTANCE\n        )),\n        Collections.emptyList()\n    );\n\n    Prompt prompt = Prompt.builder(\"p1\")\n        .user(\"Hello\")\n        .build();\n\n    List<Message.Response> responses = client.execute(prompt, OpenAIModels.Chat.GPT4o, java.util.List.of(getUserTool));\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-02.java -->\n\nIf you need direct access to the produced schema (for debugging or for a custom transport), you can instantiate the provider‑specific schemer and serialize the JSON yourself:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import kotlinx.serialization.json.Json\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    fun getUserTool(): ToolDescriptor {\n        return ToolDescriptor(\n            name = \"get_user\",\n            description = \"Returns user profile by id\",\n            requiredParameters = listOf(\n                ToolParameterDescriptor(\n                    name = \"id\",\n                    description = \"User id\",\n                    type = ToolParameterType.String\n                )\n            )\n        )\n    }\n    -->\n    \n    ```kotlin\n    val json = Json { prettyPrint = true }\n    val schema = OpenAICompatibleToolDescriptorSchemaGenerator().generate(getUserTool())\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-03.java -->\n","examples/simple-examples/src/main/kotlin/ai/koog/agents/example/a2a/README.md":"# Agent-to-Agent (A2A) Examples\n\nExamples demonstrating the A2A protocol for inter-agent communication with standardized message/task workflows, streaming responses, and artifact delivery.\n\n## Examples\n\n### Simple Joke Agent (`simplejoke/`)\n\nBasic message-based communication without tasks.\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9998)\n./gradlew runExampleSimpleJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleSimpleJokeClient\n```\n\n### Advanced Joke Agent (`advancedjoke/`)\n\nTask-based workflow with:\n- Interactive clarification (InputRequired state)\n- Artifact delivery for results\n- Graph-based agent strategy with documented nodes/edges\n- Streaming response events\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9999)\n./gradlew runExampleAdvancedJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleAdvancedJokeClient\n```\n\n## Key Patterns\n\n**Simple Agent:** `sendMessage()` → single response\n**Advanced Agent:** `sendMessageStreaming()` → Flow of events (Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent)\n\n**Task States:** Submitted → Working → InputRequired (optional) → Completed\n\nSee code comments in `JokeWriterAgentExecutor.kt` for detailed flow documentation.\n","examples/simple-examples/src/main/kotlin/ai/koog/agents/example/snapshot/sql/README.md":"# SQL Persistence Example\n\nDemonstrates using SQL databases as persistence backends for agent checkpoints through JetBrains Exposed ORM.\n\n## Quick Start\n\n1. **Start databases** (PostgreSQL/MySQL only)\n   ```bash\n   docker-compose up -d\n   ```\n\n2. **Run the examples**\n   ```bash\n   # PostgreSQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"postgres\"\n   \n   # MySQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"mysql\"\n   \n   # H2 example (embedded database)\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"h2\"\n   ```\n\n## What It Does\n\nThe example demonstrates:\n- Saving checkpoints to different SQL databases\n- Connection pooling with HikariCP (PostgreSQL/MySQL)\n- TTL-based checkpoint expiration\n- Multiple H2 database modes (in-memory, file-based, PostgreSQL-compatible)\n- Schema auto-creation\n\n## Database Schema\n\n```sql\nCREATE TABLE agent_checkpoints (\n    persistence_id VARCHAR(255),\n    checkpoint_id VARCHAR(255),\n    created_at BIGINT,\n    checkpoint_json TEXT,\n    ttl_timestamp BIGINT,\n    PRIMARY KEY (persistence_id, checkpoint_id)\n);\n```\n\n## Providers\n\n### PostgreSQL\n\n```kotlin\nPostgresPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:postgresql://localhost:5432/agents\",\n        driver = \"org.postgresql.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 3600  // Optional TTL\n)\n```\n\n### MySQL\n\n```kotlin\nMySQLPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:mysql://localhost:3306/agents\",\n        driver = \"com.mysql.cj.jdbc.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 7200\n)\n```\n\n### H2 (Embedded)\n\n```kotlin\n// In-memory (for testing)\nH2PersistenceStorageProvider.inMemory(\n    persistenceId = \"test-agent\",\n    databaseName = \"test_db\"\n)\n\n// File-based (for persistence)\nH2PersistenceStorageProvider.fileBased(\n    persistenceId = \"my-agent\",\n    filePath = \"./data/h2/agent_checkpoints\"\n)\n```\n### SQLite (Embedded)\n\n\n## Connection Pool Monitoring\n\nFor PostgreSQL/MySQL providers:\n\n```kotlin\nval stats = provider.getPoolStats()\nprintln(\"Active connections: ${stats.activeConnections}\")\nprintln(\"Pool utilization: ${stats.utilizationPercent}%\")\n```\n\n## Database UI\n\nAccess Adminer at http://localhost:8080:\n- **PostgreSQL**: Server: `postgres`, Username: `agent_user`, Password: `agent_pass`\n- **MySQL**: Server: `mysql`, Username: `agent_user`, Password: `agent_pass`\n\n## Environment Variables\n\n- `POSTGRES_URL` - Override PostgreSQL connection\n- `MYSQL_URL` - Override MySQL connection\n\n## Cleanup\n\n```bash\ndocker-compose down -v  # Stop and remove data\nrm -rf ./data          # Remove H2/SQLite files\n```\n","integration-tests/src/jvmTest/resources/skills/arithmetic-evaluator/SKILL.md":"---\nname: arithmetic-evaluator\ndescription: Evaluate arithmetic expressions and return numeric results. Use when a user asks an arithmetic question, requests calculation, or provides a math expression involving numbers, parentheses, and arithmetic operators.\n---\n\nEvaluate the arithmetic expression the user provides.\n\n1. Extract the arithmetic expression from the user request.\n2. If the expression is missing or ambiguous, ask for a clear arithmetic expression.\n3. Evaluate it by passing the expression to `scripts/evaluate_arithmetic_exp.py`\n4. Return the computed result clearly and concisely.\n\nIf script execution fails, explain that clearly and ask the user to provide a valid arithmetic expression.","integration-tests/src/jvmTest/resources/skills/weather-retrieval/SKILL.md":"---\nname: weather-retrieval\ndescription: Retrieve current weather information for a specific location. Use when a user asks for weather, temperature, conditions, forecast summary, or climate details for a city, region, or place.\n---\n\nGet weather details for the location the user provides.\n\n1. Extract the location from the user request.\n2. If the location is missing or ambiguous, ask for a clearer location name.\n3. Fetch the weather data by passing the location to `scripts/retrieve_weather.py`\n4. Parse the JSON output from the script.\n5. Return a concise summary that includes:\n    - location name\n    - temperature\n    - weather condition\n    - any notable context (for example, wind, humidity, or precipitation)\n\nIf the script fails or returns invalid data, explain that clearly and suggest trying a more specific location.\n","skills/Module.md":"# Module skills\n\nA module that provides core utilities for defining skill metadata, discovering skill content, and composing prompts.\n\n\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Koog AI Agent Framework\n\nKoog is a Kotlin multiplatform framework for building AI agents with graph-based workflows.\nIt supports JVM and JS targets and integrates with multiple LLM providers\n(OpenAI, Anthropic, Google, OpenRouter, Ollama) and Model Context Protocol (MCP).\n\n## Project Structure\n\nThe project follows a modular architecture with a clear separation of concerns:\n\n```\nkoog/\n├── agents/\n│   ├── agents-core/           # Core abstractions (AIAgent, AIAgentStrategy, AIAgentEnvironment)\n│   ├── agents-tools/          # Tool infrastructure (Tool<TArgs, TResult>, ToolRegistry, AIAgentTool)\n│   ├── agents-features-*/     # Feature implementations (memory, tracing, event handling)\n│   ├── agents-mcp/           # Model Context Protocol integration\n│   └── agents-test/          # Testing utilities and framework\n├── prompt-*/                 # LLM interaction layer (executors, models, structured data)\n├── embeddings-*/             # Vector embedding support\n├── examples/                 # Reference implementations and usage patterns\n└── build.gradle.kts          # Root build configuration\n```\n\n## Build & Commands\n\n### Development Commands\n\n```bash\n# Full build including tests\n./gradlew build\n\n# Build without tests\n./gradlew assemble\n\n# Run all JVM tests\n./gradlew jvmTest\n\n# Run all JS tests  \n./gradlew jsTest\n\n# Test specific module\n./gradlew :agents:agents-core:jvmTest\n\n# Run specific test class\n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest\"\n\n# Run specific test method  \n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest.test AIAgent doesn't call tools by default\"\n\n# Compile test classes only (for faster iteration)\n./gradlew jvmTestClasses jsTestClasses\n```\n\n### Development Environment\n\n- **JDK**: 17+ required for JVM target\n- **Build System**: Gradle with version catalogs for dependency management\n- **Targets**: JVM, JavaScript (Kotlin Multiplatform), WASM\n- **IDE**: IntelliJ IDEA recommended with Kotlin plugin\n\n## Code Style\n\n- Follow [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html)\n- Use four spaces for indentation (consistent across all files)\n- Name test functions as `testXxx` (no backticks for readability)\n- Use descriptive variable and function names\n- Prefer functional programming patterns where appropriate\n- Use type-safe builders and DSLs for configuration\n- Document public APIs with KDoc comments\n- NEVER suppress compiler warnings without a good reason\n\n## Quality Gates\nRead and follow the Quality Gates section in /TESTING.md before considering any code change complete.\n\n## Architecture\n\n### Core Framework Components\n\n**AIAgent** — Main orchestrator that executes strategies in coroutine scopes, manages tools via ToolRegistry,\nruns features through AIAgentPipeline, and handles LLM communication via PromptExecutor.\n\n**AIAgentStrategy** — Graph-based execution logic that defines workflows as subgraphs with start/finish nodes,\nmanages tool selection strategy, and handles termination/error reporting.\n\n**ToolRegistry** — Centralized, type-safe tool management using a builder pattern: `ToolRegistry { tool(MyTool()) }`.\nSupports registry merging with `+` operator.\n\n**AIAgentFeature** — Extensible capabilities installed into AIAgentPipeline with configuration.\nFeatures have unique storage keys and can intercept agent lifecycle events.\n\n### Module Organization\n\n1. **agents-core**: Core abstractions (`AIAgent`, `AIAgentStrategy`, `AIAgentEnvironment`)\n2. **agents-tools**: Tool infrastructure (`Tool<TArgs, TResult>`, `ToolRegistry`, `AIAgentTool`)\n3. **agents-features-***: Feature implementations (memory, tracing, event handling)\n4. **agents-mcp**: Model Context Protocol integration\n5. **prompt-***: LLM interaction layer (executors, models, structured data)\n6. **embeddings-***: Vector embedding support\n7. **examples**: Reference implementations and usage patterns\n\n### Key Architectural Patterns\n\n- **State Machine Graphs**: Agents execute as node graphs with typed edges\n- **Feature Pipeline**: Extensible behavior via installable features with lifecycle hooks\n- **Environment Abstraction**: Safe tool execution context preventing direct tool calls\n- **Type Safety**: Generics ensure compile-time correctness for tool arguments/results\n- **Builder Patterns**: Fluent APIs for configuration throughout the framework\n\n## Testing\n\nThe framework provides comprehensive testing utilities in `agents-test` module:\n\n### LLM Response Mocking\n\n```kotlin\nval mockLLMApi = getMockExecutor(toolRegistry, eventHandler) {\n    mockLLMAnswer(\"Hello!\") onRequestContains \"Hello\"\n    mockLLMToolCall(CreateTool, CreateTool.Args(\"solve\")) onRequestEquals \"Solve task\"\n    mockLLMAnswer(\"Default response\").asDefaultResponse\n}\n```\n\n### Tool Behavior Mocking\n\n```kotlin\n// Simple return value\nmockTool(PositiveToneTool) alwaysReturns \"The text has a positive tone.\"\n\n// With additional actions\nmockTool(NegativeToneTool) alwaysTells {\n    println(\"Tool called\")\n    \"The text has a negative tone.\"\n}\n\n// Conditional responses\nmockTool(SearchTool) returns SearchTool.Result(\"Found\") onArgumentsMatching {\n    args.query.contains(\"important\")\n}\n```\n\n### Graph Structure Testing\n\n```kotlin\nAIAgent(...) {\n    withTesting()\n\n    testGraph(\"test\") {\n        val firstSubgraph = assertSubgraphByName<String, String>(\"first\")\n        val secondSubgraph = assertSubgraphByName<String, String>(\"second\")\n\n        assertEdges {\n            startNode() alwaysGoesTo firstSubgraph\n            firstSubgraph alwaysGoesTo secondSubgraph\n        }\n\n        verifySubgraph(firstSubgraph) {\n            val askLLM = assertNodeByName<String, Message.Response>(\"callLLM\")\n            assertNodes {\n                askLLM withInput \"Hello\" outputs Message.Assistant(\"Hello!\")\n            }\n        }\n    }\n}\n```\n\nFor comprehensive testing examples, see `agents/agents-test/TESTING.md`.\n\n## Security\n\n### API Key Management\n\n- **NEVER** commit API keys or secrets to the repository\n- Use environment variables for all sensitive configuration\n- Store test API keys in a local environment only\n- Required environment variables for integration tests:\n    - `ANTHROPIC_API_TEST_KEY`\n    - `GEMINI_API_TEST_KEY`\n    - `MISTRAL_AI_API_TEST_KEY`\n    - `OLLAMA_IMAGE_URL`\n    - `OPEN_AI_API_TEST_KEY`\n    - `OPEN_ROUTER_API_TEST_KEY`\n\n### Tool Execution Safety\n\n- Tools execute within controlled `AIAgentEnvironment` contexts\n- Direct tool calls are prevented outside agent execution\n- Use type-safe tool arguments to prevent injection attacks\n- Validate all external inputs in tool implementations\n\n### Dependency Security\n\n- Regularly update dependencies using Gradle version catalogs\n- Use specific version ranges to avoid supply chain attacks\n- Review dependencies for known vulnerabilities\n- Follow the principle of the least privilege in tool implementations\n\n## Configuration\n\n### Environment Setup\n\nSet environment variables for integration testing (never commit API keys):\n\n```bash\n# Export in your shell or IDE run configuration\nexport ANTHROPIC_API_TEST_KEY=your_key_here\nexport DEEPSEEK_API_TEST_KEY=your_key_here\nexport GEMINI_API_TEST_KEY=your_key_here\nexport MISTRAL_AI_API_TEST_KEY=your_key_here\nexport OLLAMA_IMAGE_URL=http://localhost:11434\nexport OPEN_AI_API_TEST_KEY=your_key_here\nexport OPEN_ROUTER_API_TEST_KEY=your_key_here\n\n# Or add to ~/.bashrc, ~/.zshrc, or IDE environment variables\n```\n\n### Gradle Configuration\n\n- Uses version catalogs (`gradle/libs.versions.toml`) for dependency management\n- Multiplatform configuration in `build.gradle.kts`\n- Test configuration supports both JVM and JS targets\n\n### Development Environment Requirements\n\n- **JDK**: 17+ (OpenJDK recommended)\n- **IDE**: IntelliJ IDEA with Kotlin Multiplatform plugin\n- **Optional**: Docker for Ollama local testing\n\n## Development Workflow\n\n### Branch Strategy\n\n- **develop**: All development (features and bug fixes)\n- **main**: Released versions only\n- Base all PRs against `develop` branch\n- Use descriptive branch names: `feature/agent-memory`, `fix/tool-registry-bug`\n\n### Code Quality\n\n- **ALWAYS** run `./gradlew build` before submitting PRs\n- Ensure all tests pass on JVM, JS, WASM targets\n- Follow established patterns in existing code\n- Add tests for new functionality\n- Update documentation for API changes\n\n### Commit Guidelines\n\n- Use conventional commit format: `feat:`, `fix:`, `docs:`, `test:`\n- Include issue references where applicable\n- Keep commits focused and atomic\n","isInternal":false,"tokens":1891,"sizeBytes":8558},{"name":"CLAUDE.md","path":"a2a/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/a2a/CLAUDE.md","title":"a2a - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# A2A Module Development Guidelines\n\n## Module Overview\n\nThe A2A (Agent-to-Agent) module is a **meta-module** within the larger Koog project that implements a comprehensive client and server library for the A2A protocol, a standardized communication protocol for AI agents based on the specification at https://a2a-protocol.org/latest/specification/.\n\n**Important**: Since this is a meta-module inside a bigger root project, the Gradle wrapper executable is located one directory up. All Gradle commands must use `../gradlew` when running from the a2a directory.\n\n### Purpose\n- **Agent Discovery**: Through AgentCard manifests describing agent capabilities and interfaces\n- **Message Exchange**: Standardized message format with support for different content types\n- **Task Management**: Long-running operations with state tracking and lifecycle management\n- **Push Notifications**: Asynchronous notifications for task updates\n- **Multiple Transport Protocols**: JSON-RPC, HTTP+JSON/REST with extensibility for gRPC\n- **Authentication & Security**: OpenAPI 3.0 compatible security schemes\n\n### Submodules Architecture\n\n1. **a2a-core**: Core abstractions, data models, and transport interfaces\n   - AgentCard, Message, Task, Event hierarchy\n   - Transport interfaces: ClientTransport, ServerTransport, RequestHandler\n   - No external dependencies beyond Kotlin stdlib and serialization\n\n2. **a2a-client**: High-level client library for communicating with A2A servers\n   - A2AClient wrapper, AgentCardResolver\n   - Capability validation, request/response handling\n   - Depends on: a2a-core, Ktor HTTP client\n\n3. **a2a-server**: Server-side implementation for hosting A2A agents\n   - A2AServer, AgentExecutor interface, session management\n   - Storage abstractions with in-memory implementations\n   - Depends on: a2a-core, coroutines, logging\n\n4. **a2a-transport**: Multiple transport protocol implementations\n   - a2a-transport-core-jsonrpc: JSON-RPC protocol base classes\n   - a2a-transport-client-jsonrpc-http: HTTP JSON-RPC client transport\n   - a2a-transport-server-jsonrpc-http: HTTP JSON-RPC server transport\n   - a2a-transport-*-rest: HTTP+JSON/REST protocol implementations\n\n## Technologies & Libraries\n\n### Core Dependencies (from gradle/libs.versions.toml)\n- **Kotlin Multiplatform**\n- **kotlinx-serialization**: JSON serialization for protocol messages\n- **kotlinx-coroutines**: Async/concurrent programming, Flow APIs\n- **kotlinx-datetime**: Timestamp handling in protocol messages\n- **ktor3**: HTTP client/server for transport implementations\n- **oshai-kotlin-logging**: Structured logging\n\n### Testing Libraries\n- **kotlin-test**: Multiplatform test framework\n- **kotest-assertions**: Rich assertion library for complex objects\n- **kotlinx-coroutines-test**: Testing coroutines with runTest\n- **testcontainers**: Docker-based integration testing\n- **logback-classic**: Runtime logging for tests\n\n### Platform Support\n- **JVM**: Full server and client support\n- **JS (Browser)**: Client-only support\n- **Future platforms**: Architecture ready for native support\n\n## Development Guidelines\n\n### Architecture Decisions\n⚠️ **CRITICAL**: Never make design and architecture decisions independently. Always ask the user for confirmation before:\n- Adding new transport protocols\n- Changing storage interfaces\n- Modifying core protocol message formats\n- Adding new authentication schemes\n- Changing session management behavior\n- Doing other sorts of major architectural changes\n\n### Code Style Conventions\n\n#### API Visibility\n- Use `explicitApi()` - all public APIs must have explicit visibility modifiers\n- Prefer `public` declarations for APIs, `internal` for implementation details\n- Use `@InternalA2AApi` annotation for APIs that are public but internal to the module\n\n#### Naming Conventions\n- Classes: PascalCase (`A2AServer`, `SessionManager`, `AgentExecutor`)\n- Interfaces: Same as classes, often ending in -or/-er for behaviors (`AgentExecutor`)\n- Functions: camelCase with descriptive names (`onSendMessage`, `getByContext`)\n- Properties: camelCase (`agentCard`, `sessionMutex`)\n\n#### Async Patterns\n- Use `suspend` functions for all async operations\n- Prefer `Flow` for streaming APIs (task events, message streams)\n- Use `RWLock` pattern: `rwLock.withReadLock/withWriteLock` for concurrent access\n- Use `Mutex` for single-threaded critical sections: `mutex.withLock`\n\n#### Error Handling\n- Use domain-specific exceptions (`A2AInternalErrorException`, `TaskOperationException`)\n- Propagate errors properly in async contexts\n- Include contextual information in exception messages\n\n#### KDoc Documentation\n- **Placement**: KDoc directly above declarations (classes, functions, properties)\n- **Constructor properties**: Document using `@param` tags in class KDoc\n- **Public class properties**: Document using `@property` tags in class KDoc\n- **Cross-references**: Use `[ClassName]`, `[ClassName.propertyName]` syntax for linking components\n- **Examples**: Include practical code examples for complex APIs\n- **Validation rules**: Document constraints with bullet points and clear explanations\n- **Exception documentation**: Use `@throws` with specific conditions\n- **Required**: All public APIs must have KDoc (enforced by `explicitApi()`)\n\n## Testing Requirements\n\n### Mandatory Test Execution\n⚠️ **ALWAYS** run the specific test suite using Gradle's jvmTest task to ensure changes work correctly:\n\n```bash\n# Run all tests in a specific module\n../gradlew :a2a:a2a-server:jvmTest\n\n# Run a specific test class\n../gradlew :a2a:a2a-server:jvmTest --tests \"ai.koog.a2a.server.tasks.InMemoryTaskStorageTest\"\n\n# Run a specific test method\n../gradlew :a2a:a2a-client:jvmTest --tests \"ai.koog.a2a.client.A2AClientIntegrationTest.test get agent card\"\n```\n\n### Testing Approach: Crucial Minimum\n- **Focus on core functionality** - test essential behavior, not implementation details\n- **Separate dedicated tests** - one test method per core scenario/edge case\n- **Avoid verbose, unnecessary tests** - don't test trivial getters/setters\n- **Test error conditions** - verify proper exception handling and error states\n\n#### Preferred Assertion Pattern\n**✅ DO**: Assert on whole objects when possible (especially data classes):\n```kotlin\nassertEquals(expectedTask, actualTask)  // Compares all properties\nassertEquals(listOf(expectedArtifact), retrieved?.artifacts)\n```\n\n**❌ AVOID**: Bunch of individual field assertions unless necessary:\n```kotlin\n// Avoid this pattern:\nassertEquals(expected.id, actual.id)\nassertEquals(expected.contextId, actual.contextId)\nassertEquals(expected.status, actual.status)\n// ... many more individual assertions\n```\n\n### Integration Tests\n- **Docker-based testing**: a2a-client uses testcontainers with Python A2A server\n- **Docker build dependency**: Integration tests automatically build required containers\n- **Test isolation**: Each test should be independent and clean up after itself\n\n### Test Structure Examples\n\n**Unit Tests** (InMemoryTaskStorageTest pattern):\n```kotlin\nclass InMemoryTaskStorageTest {\n    private lateinit var storage: InMemoryTaskStorage\n\n    @BeforeTest\n    fun setUp() {\n        storage = InMemoryTaskStorage()\n    }\n\n    @Test\n    fun testStoreAndRetrieveTask() = runTest {\n        val task = createTask(id = \"task-1\", contextId = \"context-1\")\n        storage.update(task)\n        val retrieved = storage.get(\"task-1\")\n\n        assertNotNull(retrieved)\n        assertEquals(task, retrieved)  // Whole object assertion\n    }\n}\n```\n\n**Integration Tests** (A2AClientIntegrationTest pattern):\n```kotlin\n@Testcontainers\nclass A2AClientIntegrationTest {\n    @Container\n    val testA2AServer = GenericContainer(\"test-python-a2a-server\")\n        .withExposedPorts(9999)\n        .waitingFor(Wait.forListeningPort())\n\n    @Test\n    fun `test get agent card`() = runTest {\n        val agentCard = client.getAgentCard()\n        val expectedAgentCard = AgentCard(...)\n        assertEquals(expectedAgentCard, agentCard)  // Full object comparison\n    }\n}\n```\n\n## Implementation Patterns\n\n### Transport Layer Abstraction\n- Implement `ClientTransport` interface for new client protocols\n- Implement `ServerTransport` and `RequestHandler` for new server protocols\n- Keep protocol logic separate from transport mechanism\n- Use suspend functions for all transport operations\n\n### Storage Interface Pattern\n```kotlin\npublic interface SomeStorage {\n    public suspend fun save(item: Item)\n    public suspend fun get(id: String): Item?\n    public suspend fun delete(id: String)\n}\n\n// In-memory implementation for testing/development\n@OptIn(InternalA2AApi::class)\npublic class InMemorySomeStorage : SomeStorage {\n    private val rwLock = RWLock()\n    private val items = mutableMapOf<String, Item>()\n\n    override suspend fun save(item: Item) = rwLock.withWriteLock {\n        items[item.id] = item\n    }\n}\n```\n\n### Session Management Pattern\n- Use `SessionEventProcessor` for event-driven session handling\n- Implement proper cleanup with `Closeable` interface\n- Use structured concurrency with `CoroutineScope`\n- Handle session lifecycle properly (start → active → closed)\n\n### Agent Executor Implementation\n```kotlin\npublic class MyAgentExecutor : AgentExecutor {\n    override suspend fun execute(\n        context: RequestContext<MessageSendParams>,\n        eventProcessor: SessionEventProcessor\n    ) {\n        // Process request and send events through eventProcessor\n        eventProcessor.sendTaskEvent(/* task event */)\n    }\n}\n```\n\n## Some Common Commands\n\n```bash\n# Build specific modules (a2a is a meta-module, build individual modules)\n../gradlew :a2a:a2a-core:assemble\n../gradlew :a2a:a2a-client:assemble\n../gradlew :a2a:a2a-server:assemble\n\n# Run JVM tests for specific modules\n../gradlew :a2a:a2a-core:jvmTest\n../gradlew :a2a:a2a-client:jvmTest\n../gradlew :a2a:a2a-server:jvmTest\n../gradlew :a2a:a2a-transport:a2a-transport-core-jsonrpc:jvmTest\n\n# Run JS tests for specific modules\n../gradlew :a2a:a2a-core:jsTest\n../gradlew :a2a:a2a-client:jsTest\n../gradlew :a2a:a2a-server:jsTest\n\n# Build all non-transport a2a modules at once\n../gradlew :a2a:a2a-core:assemble :a2a:a2a-client:assemble :a2a:a2a-server:assemble\n\n# Run all JVM tests across a2a modules\n../gradlew :a2a:a2a-core:jvmTest :a2a:a2a-client:jvmTest :a2a:a2a-server:jvmTest\n```\n","isInternal":false,"tokens":2378,"sizeBytes":10305},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"AGENTS.md","isInternal":false,"tokens":3,"sizeBytes":9},{"name":"factory-classes.md","path":".claude/skills/add-java-code-snippets-in-docs/reference/factory-classes.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/.claude/skills/add-java-code-snippets-in-docs/reference/factory-classes.md","title":"Reference Skill","category":"anthropic-skill","format":"markdown","content":"# JVM `create()` factory classes\n\nLoad this file when the Kotlin block uses types that aren't callable from Java and you need to expose (or audit) a Java-friendly factory.\n\n## When a factory is needed\n\nKotlin constructors / APIs Java can't call cleanly:\n\n- Uses `kotlinx.io` types (`Sink`, `Path`, `SystemFileSystem`).\n- Uses inline value classes (`Duration`) — the constructor becomes private from Java.\n- Uses `KLogger` (kotlin-logging) instead of SLF4J `Logger`.\n- Has default parameters without `@JvmOverloads` — generates synthetic constructors with `DefaultConstructorMarker`.\n\nIf the Kotlin constructor uses only Java-compatible types and has `@JvmOverloads` (or no default params), Java can call it directly with `new`. Example:\n\n```java\n// No factory needed — constructor is Java-accessible\nnew TraceFeatureMessageRemoteWriter()\nnew TraceFeatureMessageRemoteWriter(connectionConfig)\n```\n\n## Pattern\n\nAdd a `companion object` with `create(...)` methods **on the class itself**, in the `jvmCommonMain` source set. The class must already live in `jvmCommonMain` (or have a JVM actual) for this to apply.\n\n> **Prerequisite:** if the class is still in `commonMain`, run the **`split-jvm-nonjvm`** skill first to produce the `expect class` + `jvmCommonMain` actual scaffolding. `split-jvm-nonjvm` requires the class to have no companion object at split time — so the order is: (1) split, then (2) add the companion `create()` on the JVM actual. If the class already has a companion in `commonMain`, raise it with the user before splitting.\n\nProvide two overloads of `create()`:\n\n1. **Kotlin-facing** — accepts the original Kotlin-only types (`kotlinx.io.Path`, `Sink`, `KLogger`, `Duration`). No JVM annotations needed; convenient sugar for Kotlin callers and keeps API symmetry.\n2. **Java-facing** — accepts only Java-compatible types (`java.nio.file.Path`, `java.io.OutputStream`, `org.slf4j.Logger`, primitives, etc.). Annotate with `@JavaAPI` (intent), `@JvmStatic` (static call site), and `@JvmOverloads` (one method per default-parameter combination). Convert to Kotlin-only types inside the body, then delegate to the primary constructor.\n\n## Skeleton\n\nSubstitute `Xxx` with the class name, `KotlinT` / `JavaT` with the actual type pair:\n\n```kotlin\n// In: jvmCommonMain/.../Xxx.kt\npublic class Xxx(/* primary constructor uses Kotlin-only types */) {\n    public companion object {\n        // (1) Kotlin-facing\n        public fun create(\n            param: KotlinT,\n            // ...defaulted params with Kotlin types\n        ): Xxx = Xxx(/* pass through */)\n\n        // (2) Java-facing\n        @JavaAPI\n        @JvmStatic\n        @JvmOverloads\n        public fun create(\n            param: JavaT,\n            // ...defaulted params with Java-friendly types\n        ): Xxx {\n            val kotlinParam = /* convert JavaT -> KotlinT */\n            return Xxx(/* pass converted values */)\n        }\n    }\n}\n```\n\nJava callers then write `Xxx.create(javaParam)` — no separate class, no `Continuation`, no `DefaultConstructorMarker`.\n\n## Conversion cheat sheet\n\n| Kotlin-only type | Java-facing replacement | Conversion inside `create()` |\n|------------------|-------------------------|------------------------------|\n| `kotlinx.io.files.Path` | `java.nio.file.Path` | `Path.of(javaPath.toString())` or use `SystemFileSystem` |\n| `kotlinx.io.Sink` | `java.io.OutputStream` (via opener lambda) | `outputStream.asSink().buffered()` |\n| `KLogger` (kotlin-logging) | `org.slf4j.Logger` | `KotlinLogging.logger(slf4jLogger)` |\n| `kotlin.time.Duration` | `long millis` / `java.time.Duration` | `millis.milliseconds` / `Duration.ofMillis(...).toKotlinDuration()` |\n| `(T) -> R` with Kotlin types | `Function<T, R>` or SAM with Java types | `{ t -> javaFn.apply(t) }` |\n\n## Reference implementations\n\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageFileWriter.kt` — `Path` / `Sink` ↔ `java.nio.file.Path` / `OutputStream`.\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageLogWriter.kt` — `KLogger` ↔ SLF4J `Logger`.\n\n## Anti-pattern: separate `*Jvm` object\n\nDo **not** introduce a separate `public object XxxJvm { ... }` alongside the original class. The convention is to put the Java-facing `create()` on the original class's companion. A `*Jvm` companion class would be redundant and inconsistent with the existing `TraceFeatureMessage*Writer` classes.\n","isInternal":false,"tokens":1085,"sizeBytes":4455},{"name":"interop-tables.md","path":".claude/skills/add-java-code-snippets-in-docs/reference/interop-tables.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/.claude/skills/add-java-code-snippets-in-docs/reference/interop-tables.md","title":"Reference Skill","category":"anthropic-skill","format":"markdown","content":"# Kotlin ↔ Java Interop Reference\n\nLoad this file when writing the Java side of a non-trivial Kotlin block: figuring out how a Kotlin construct maps to Java, or whether a `suspend` function has a Java-friendly bridge.\n\n## Quick mapping table\n\n| Kotlin | Java |\n|--------|------|\n| `AIAgent(...) { }` | `AIAgent.builder()...build()` |\n| `install(Feature) { }` | `.install(Feature.Feature, config -> { })` |\n| `install(Testing) { config() }` | `.install(Testing.Feature, config -> { })` (`ConfigureAction<T>` is a `fun interface`) |\n| `simpleOllamaAIExecutor()` | `PromptExecutor.builder().ollama().build()` |\n| `KotlinLogging.logger { }` | `LoggerFactory.getLogger(\"name\")` |\n| `val x = ...` (local) | `var x = ...` |\n| `val x = ...` (field) | `static Type x = ...` (never `static var`) |\n| `runBlocking { }` | not needed (`agent.run` is blocking in Java — see suspend bridges below) |\n| `config.addMessageProcessor(...)` | `config.addMessageProcessor(...)` |\n| `class Foo : Bar()` | `class Foo extends Bar` |\n| `when (x) { is Type -> ... }` | `if (x instanceof Type) { ... } else if ...` |\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` — see suspend bridges |\n| `override suspend fun close()` | `void handleClose()` — see suspend bridges |\n| `TraceFeatureMessageFileWriter(path, sinkOpener)` | `TraceFeatureMessageFileWriter.create(path)` — JVM `create()` overload taking `java.nio.file.Path` |\n| `TraceFeatureMessageLogWriter(klogger)` | `TraceFeatureMessageLogWriter.create(slf4jLogger)` — JVM `create()` overload taking SLF4J `Logger` |\n| `TraceFeatureMessageRemoteWriter(config)` | `new TraceFeatureMessageRemoteWriter()` (direct, no factory needed) |\n| `getMockExecutor(...) { mockLLMAnswer(...) }` | `MockPromptExecutor.builder(serializer).mockLLMAnswer(...).build()` |\n\n## Suspend functions and Java\n\nKotlin `suspend` functions compile to JVM methods with an extra `Continuation` parameter and `Object` return type. **Java code snippets must NEVER show `Continuation` parameters.** The codebase provides Java-friendly bridges.\n\n### Calling suspend methods from Java\n\nMethods annotated with `@JavaAPI` and `@JvmName` provide blocking wrappers. Use the Java-facing name directly:\n\n| Kotlin (suspend) | Java (blocking) |\n|------------------|-----------------|\n| `agent.run(input)` | `agent.run(input)` (calls `javaNonSuspendRun` via `@JvmName(\"run\")`) |\n| `processor.initialize()` | `processor.initialize()` (calls `javaNonSuspendInitialize` via `@JvmName`) |\n| `processor.onMessage(msg)` | `processor.onMessage(msg)` (calls `javaNonSuspendOnMessage` via `@JvmName`) |\n\nThese wrappers use `runBlockingIfRequired()` internally. Java code calls them like normal blocking methods.\n\n### Overriding suspend methods from Java\n\nWhen a Java class extends a Kotlin class with `suspend` methods to override, look for non-suspend bridge methods annotated with `@JavaAPI`:\n\n| Kotlin override | Java override |\n|-----------------|---------------|\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` |\n| `override suspend fun close()` | `void handleClose()` |\n\n**Why the names differ:** `@JvmName` cannot be used on `open` or `override` methods (Kotlin restriction — it would break polymorphism). So Java-facing override names can diverge from the Kotlin suspend names (`handleMessage` not `processMessage`).\n\n### Abstract properties with Kotlin types\n\nAbstract properties like `val isOpen: StateFlow<Boolean>` leak Kotlin coroutine types to Java. If the base class provides a default `open val` implementation (e.g., via `MutableStateFlow`), Java subclasses don't need to implement it. Check whether the property is `abstract` or `open` before writing the Java example.\n\n### Before writing a Java subclass example\n\n1. Check what `@JavaAPI` bridge methods exist on the class — grep for `@JavaAPI` in the `jvmCommonMain` actual.\n2. Check which members are `abstract` vs `open` — abstract ones MUST be implemented in Java.\n3. Never show `Continuation`, `Object` return types, or `Unit.INSTANCE` in Java — use the bridge methods.\n\n## `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention for boolean-like property names.\n\n## Key source files to grep\n\nWhen writing Java examples that use these classes, check the JVM actuals for available `@JavaAPI` bridge methods:\n\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/agent/AIAgent.kt` — `javaNonSuspendRun` with `@JvmName(\"run\")`.\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/feature/message/FeatureMessageProcessor.kt` — calling bridges (`javaNonSuspendInitialize`, `javaNonSuspendOnMessage`) and overriding bridges (`handleMessage`, `handleClose`).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorBuilder.kt` — Java builder for mock executor: `mockLLMAnswer()`, `mockLLMToolCall()`, `mockTool()` + inner builder classes.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockPromptExecutor.kt` — entry point: `MockPromptExecutor.builder(serializer)` with `@JvmStatic @JavaAPI`.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorDSLBuilder.kt` — Kotlin DSL builder (reference for understanding what the Java builder delegates to).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/feature/TestingFeature.kt` — `Testing.Feature` companion object (used in Java as `Testing.Feature`).\n- `agents/agents-core/src/jvmTest/java/ai/koog/agents/core/agent/JavaAPIAgentBuilderJavaTest.java` — reference Java test using `MockPromptExecutor`, `AIAgent.builder()`, and functional strategies.\n","isInternal":false,"tokens":1428,"sizeBytes":5797},{"name":"pitfalls.md","path":".claude/skills/add-java-code-snippets-in-docs/reference/pitfalls.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/.claude/skills/add-java-code-snippets-in-docs/reference/pitfalls.md","title":"Reference Skill","category":"anthropic-skill","format":"markdown","content":"# Pitfalls, testing API, and docs classpath\n\nLoad this file when something fails to compile, or when you're writing a `MockPromptExecutor`-based testing block.\n\n## Common pitfalls\n\n### `var` is only for local variables\n\nJava `var` (type inference) works only for local variables inside methods. For fields, use explicit types:\n\n```java\n// WRONG: static var logger = LoggerFactory.getLogger(\"x\");\n// RIGHT: static Logger logger = LoggerFactory.getLogger(\"x\");\n```\n\n### Inline value classes make constructors private from Java\n\nKotlin classes with `Duration`, `Duration?`, or other inline value class parameters in their constructors generate synthetic constructors with `DefaultConstructorMarker`. These are not callable from Java. Solution: add a JVM `create()` factory — see `factory-classes.md`.\n\n### Import management\n\nAlways add imports to the INCLUDE block. Never use fully-qualified names in visible code:\n\n```java\n// WRONG (in visible code): private static final org.slf4j.Logger logger = ...\n// RIGHT (in INCLUDE):      import org.slf4j.Logger;\n// RIGHT (in visible code): static Logger logger = ...\n```\n\n### `@JvmOverloads` check\n\nBefore calling a Kotlin constructor/method with default parameters from Java, verify it has `@JvmOverloads`. Without it, Java only sees the full-parameter version (or synthetic constructors with `DefaultConstructorMarker`).\n\n### SUFFIX brace counting\n\nCount opening braces in INCLUDE carefully. The SUFFIX must have exactly the matching number of closing braces. Off-by-one errors cause `Syntax error: Expecting a top level declaration` at the end of the generated file. After writing SUFFIX, mentally concatenate INCLUDE + visible code + SUFFIX and verify all braces match.\n\n### `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention.\n\n### Kotlin `expect`/`actual` classes\n\nWhen an `expect abstract class` in `commonMain` has `abstract` members, Java subclasses must implement them. Check the **JVM actual** (in `jvmCommonMain`), not the `expect` declaration — the actual may provide default implementations or bridge methods not visible in the `expect`.\n\n### Legacy `/** **/` placeholder pattern\n\nSome docs have existing Java tabs that wrap code in `/** */` block comments (making the code a Javadoc comment that trivially compiles). This is a legacy pattern — **always replace with real compilable Java code** or explanatory comments for Kotlin-only APIs.\n\n## Testing API: `MockPromptExecutor.builder`\n\nThe testing module (`agents-test`) provides Java-friendly builder APIs via `MockPromptExecutor.builder(serializer)`.\n\n### `MockExecutorBuilder` methods\n\n| Method | Description |\n|--------|-------------|\n| `mockLLMAnswer(text)` | Returns `MockLLMAnswerBuilder` — configure with `.asDefaultResponse()`, `.onRequestContains(pattern)`, `.onRequestEquals(pattern)`, `.onCondition(pred)`. |\n| `mockLLMToolCall(tool, args)` | Returns `MockLLMToolCallBuilder` — configure with `.onRequestEquals(pattern)`, `.onRequestContains(pattern)`, `.onCondition(pred)`. |\n| `mockTool(tool)` | Returns `MockToolBuilder` — configure with `.alwaysReturns(result)`, `.alwaysDoes(action)`, `.returns(result).onArguments(args)`, `.returns(result).onArgumentsMatching(pred)`. |\n| `clock(clock)` | Set custom clock. |\n| `tokenizer(tokenizer)` | Set custom tokenizer. |\n| `handleLastAssistantMessage(bool)` | Configure last-assistant-message handling. |\n| `build()` | Returns `PromptExecutor`. |\n\nAll sub-builders return `MockExecutorBuilder` for method chaining.\n\n### Full Java test pattern\n\n```java\nPromptExecutor mockLLMApi = MockPromptExecutor.builder(new JacksonSerializer())\n    .mockLLMToolCall(SayToUser.INSTANCE, new SayToUser.Args(positiveText))\n        .onRequestEquals(positiveText)\n    .mockLLMAnswer(positiveResponse).onRequestContains(positiveResponse)\n    .mockLLMAnswer(defaultText).asDefaultResponse()\n    .mockTool(SayToUser.INSTANCE).alwaysReturns(positiveResponse)\n    .build();\n\nvar agent = AIAgent.builder()\n    .promptExecutor(mockLLMApi)\n    .llmModel(OpenAIModels.Chat.GPT4o)\n    .install(Testing.Feature, config -> { })\n    .build();\n\nString result = agent.run(positiveText);\n```\n\n### Importing Kotlin objects in Java INCLUDE\n\nWhen Java code needs Kotlin objects defined in a Kotlin example's generated package, import from the generated package:\n\n```java\nimport ai.koog.agents.example.exampleTesting03.*;\n```\n\nThis gives access to tool definitions and other objects declared at the top level of the Kotlin example file.\n\n## Docs compilation environment\n\n### JDK version\n\nThe project requires JDK 17. If the default JDK is newer (e.g., 25), compilation fails with cryptic errors. Use:\n\n```bash\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:knit\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:compileJava\n```\n\n### Docs classpath constraints\n\nThe docs module has a limited classpath:\n\n- **JUnit is NOT available.** Do not use `import org.junit.jupiter.api.Assertions.*`. Use Java `assert` statements instead: `assert expected.equals(result) : \"message\";`\n- **JacksonSerializer IS available:** `import ai.koog.serialization.jackson.JacksonSerializer;`\n- **agents-core, agents-test, agents-ext ARE available:** agent builders, mock executors, `SayToUser` tool, etc.\n\nIf compilation fails with \"package X does not exist\", check `docs/build.gradle.kts` for available dependencies.\n","isInternal":false,"tokens":1229,"sizeBytes":5440},{"name":"SKILL.md","path":".claude/skills/add-java-code-snippets-in-docs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/.claude/skills/add-java-code-snippets-in-docs/SKILL.md","title":"add-java-code-snippets-in-docs","category":"anthropic-skill","format":"markdown","content":"---\nname: add-java-code-snippets-in-docs\ndescription: Use when the user asks to add Java tabs/examples to mkdocs docs that already have Kotlin examples (files under docs/docs/**.md using === \"Kotlin\" / Knit INCLUDE+SUFFIX blocks). Handles mkdocs-material tabs, Knit directives, @JavaAPI bridge methods, and JVM factory classes.\nallowed-tools: Read, Edit, Write, Bash, Grep, Glob\n---\n\n## When to use\n\nAdding Java code blocks alongside existing Kotlin blocks in mkdocs documentation (`docs/docs/**.md`). Typical triggers: \"add Java examples to <file>\", \"add Java tabs\", \"complete the Java side of <doc>\", \"fix the Java block in <doc>\".\n\n**Do NOT use this skill for:**\n\n- Writing brand-new Kotlin docs (no precursor Kotlin block to mirror).\n- Splitting source files between JVM and non-JVM source sets — use the **`split-jvm-nonjvm`** skill instead.\n- Java examples outside the mkdocs/Knit pipeline.\n\n## Prerequisites\n\nJDK 17. Gradle 8.x fails cryptically on newer JDKs. If the default JDK is newer, prefix every `./gradlew` command in this workflow with `JAVA_HOME=/path/to/jdk-17`.\n\n## Before starting\n\nRead **one** reference doc to anchor on the established pattern:\n\n- `docs/docs/features/tracing.md` — full Tracing feature (blocks 1–6).\n- `docs/docs/agent-events.md` — custom `FeatureMessageProcessor` subclass (suspend bridge methods `handleMessage` / `handleClose`).\n- `docs/docs/testing.md` — `MockPromptExecutor` builder, tool mocking, comment-only blocks for Kotlin-only DSL.\n- `docs/docs/features/open-telemetry/index.md` — OpenTelemetry examples.\n- `docs/docs/features/open-telemetry/opentelemetry-langfuse-exporter.md` — Langfuse exporter.\n- `docs/docs/features/open-telemetry/opentelemetry-weave-exporter.md` — Weave exporter.\n\n## Workflow\n\nFor each Kotlin code block in the target `.md`:\n\n1. **Read** the Kotlin block: INCLUDE, visible code, SUFFIX, KNIT directive.\n2. **Check existing Java tab.** If absent → add one. If it has empty `/** */` placeholder content → replace entirely. If it has real content → update.\n3. **Consider Kotlin-side alignment.** If the Kotlin block uses Kotlin-only APIs (`kotlinx.io`, `MutableStateFlow`, `KLogger`, inline `Duration`), consider whether the Kotlin example should switch to a JVM `create()` factory (e.g., `TraceFeatureMessageFileWriter.create(path)`). This keeps the two examples structurally aligned. See `reference/factory-classes.md`.\n4. **Add `@JavaAPI` bridge methods if missing.** If the Java code needs a bridge that doesn't exist yet (e.g., a builder method on `MockExecutorBuilder`), add it before writing the Java tab. This may pause the doc work for a source-code change — flag it to the user before proceeding.\n5. **Write the Java tab** immediately after the Kotlin tab, using the template below.\n6. **If renaming Knit files** (e.g., hyphenated `example-feature-java-01.java` → camelCase `exampleFeatureJava01.java`), delete the old generated files first.\n7. **Generate and compile** (see Verification at the bottom).\n8. **Read the generated `.java`** to confirm correctness. Fix and repeat 7–8 on failure.\n\n## Mkdocs / Knit tab structure\n\nJava tabs go immediately after the Kotlin tab. All content inside a tab is indented 4 spaces. `<!--- INCLUDE -->`, `<!--- SUFFIX -->`, `<!--- KNIT -->` are Knit directives (HTML comments — invisible when rendered), not mkdocs syntax.\n\n```markdown\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ...\n    -->\n    ```kotlin\n    // visible code\n    ```\n    <!--- KNIT example-feature-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ...;\n    public class exampleFeatureJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // visible code\n    ```\n    <!--- KNIT exampleFeatureJava01.java -->\n```\n\n### KNIT naming — capital \"Java\" matters\n\n`docs/knit.code.include` has the check `<#if !knit.name?contains(\"Java\")>package ...`. Files with capital \"Java\" in the name skip the `package` declaration; lowercase \"java\" or hyphenated names emit a `package` line that breaks compilation.\n\n- ✅ `exampleFeatureJava01.java`, `exampleTestingJava13.java`\n- ❌ `example-feature-java-01.java`, `examplefeaturejava01.java`\n\n### Knit rules\n\n- INCLUDE + visible code + SUFFIX must concatenate into a valid Java file.\n- Hidden variables in Kotlin INCLUDE (`outputPath`, `input`, etc.) must also appear in Java INCLUDE.\n- Visible-code indentation in `.md` is 4 spaces (tab content). Knit strips that when generating.\n- Always import in INCLUDE; never use FQNs in visible code.\n- Never use file-level annotations (`@file:Suppress`, `@file:OptIn`) — scope to specific declarations.\n\n## Structural matching (the most important rule)\n\n**The Java visible code must match the Kotlin visible code in scope and structure.**\n\n- If Kotlin shows no `main()`, Java must hide `main()` in INCLUDE/SUFFIX.\n- If Kotlin shows `install(Feature) { ... }`, Java must show `.install(Feature.Feature, config -> { })`.\n- If Kotlin creates a writer before the agent, Java must too.\n- If Kotlin shows `agent.run(input)`, Java must too.\n\nRead both visible blocks side-by-side after writing. They should read as equivalent code in their respective languages.\n\n### When Kotlin uses APIs with no Java bridge\n\nSome Kotlin DSLs (graph testing: `testGraph`, `assertSubgraphByName`, `assertNodes`, `assertEdges`, `verifySubgraph`, `assertReachable`) have no `@JavaAPI` bridges. To check: grep for `@JavaAPI` in the source file — if absent, treat as Kotlin-only.\n\nWhen the Kotlin block uses an entirely Kotlin-only API:\n\n1. **Do NOT leave the Java tab empty.** Write a compilable Java class with explanatory comments.\n2. The class must have a valid `main` so it compiles.\n3. Comments explain what the Kotlin DSL does and the recommended Java alternative.\n\n```java\n// Graph structure testing (testGraph, assertSubgraphByName, assertEdges,\n// verifySubgraph, assertReachable) is available through the Kotlin testing DSL.\n//\n// In Java, test agent behavior by running and asserting on the result:\n//   String result = agent.run(\"test input\");\n//   assert \"expected\".equals(result);\n```\n\n## Deep references\n\nLoad these only when you need them:\n\n- **`reference/interop-tables.md`** — Kotlin↔Java mapping table, suspend bridges (`@JvmName(\"run\")`, `handleMessage`, `handleClose`), abstract-property rules, key source files to grep for `@JavaAPI`.\n- **`reference/factory-classes.md`** — JVM `create()` factory pattern, two-overload skeleton, type-conversion cheat sheet, when needed vs not, anti-patterns. Cross-references the `split-jvm-nonjvm` skill.\n- **`reference/pitfalls.md`** — `var`-vs-field, brace-counting in SUFFIX, `is`-prefixed property getters, `@JvmOverloads` requirement, docs classpath limits (no JUnit), legacy `/** */` placeholder, `MockExecutorBuilder` API.\n\n## Verification\n\nAfter each Java block:\n\n```bash\n./gradlew :docs:knit\n./gradlew :docs:compileJava\n```\n\nOn failure, read the generated file to diagnose:\n\n```bash\ncat docs/src/main/kotlin/exampleFeatureJavaNN.java\n```\n","frontmatter":{"name":"add-java-code-snippets-in-docs","description":"Use when the user asks to add Java tabs/examples to mkdocs docs that already have Kotlin examples (files under docs/docs/**.md using === \"Kotlin\" / Knit INCLUDE+SUFFIX blocks). Handles mkdocs-material tabs, Knit directives, @JavaAPI bridge methods, and JVM factory classes.","allowed-tools":"Read, Edit, Write, Bash, Grep, Glob","allowedTools":["Read,","Edit,","Write,","Bash,","Grep,","Glob"]},"allowedTools":["Read,","Edit,","Write,","Bash,","Grep,","Glob"],"isInternal":false,"tokens":1717,"sizeBytes":7052},{"name":"SKILL.md","path":".claude/skills/split-jvm-nonjvm/SKILL.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/.claude/skills/split-jvm-nonjvm/SKILL.md","title":"split-jvm-nonjvm","category":"anthropic-skill","format":"markdown","content":"---\nname: split-jvm-nonjvm\ndescription: |\n    Splits a class or several classes in the common source set commonMain into JVM and non-JVM parts into jvmCommonMain and nonJvmCommonMain. \n    This is used to provide some JVM-specific functionality for a class on JVM targets (e.g. Kotlin/JVM or Java backend or Android).\n---\n\nThe following example shows how to split a class into JVM and non-JVM parts.\nWhen using this skill, avoid unnecessarily looking at existing implementations of the split in the project, just follow this instruction.\nYou can, however, check existing implementations for reference if you have a complex case or otherwise stuck.\n\nHere's a sample class in a `commonMain` source set\n```kt\npackage com.example\n\n/**\n* A sample class with an external dependency, some logic and public methods\n * @param name Some public property\n * @param myRepo Some external dependency, private property\n*/\npublic class MyClass(\n    public val name: String,\n    private val myRepo: MyRepo,\n) {\n    /**\n     * Public API method\n     */\n    public fun doSomething(): String {\n        return fetchData()\n    }\n    \n    /**\n     * Private implementation logic\n     */\n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    /*\n     * Internal method\n     */\n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nIn the same package as `MyClass` in `commonMain`, create the following files. The original `MyClass.kt` file will be overwritten with the expect class (see step three).\n\nFirst, the API interface describing the contract. It should contain only public symbols from the `MyClass`.\nTake KDocs from the respective symbols in `MyClass` and add them to the interface.\n\n```kt\npackage com.example\n\n/**\n * API for [MyClass]\n */\npublic interface MyClassAPI {\n    /**\n     * Some public property\n     */\n    public val name: String\n    \n    /**\n     * Public API method\n     */\n    public fun doSomething(): String\n}\n```\n\nSecond, `MyClassImpl.kt` — implementation of the API interface providing the implementation logic. The filename matches the class name.\n\n```kt\ninternal class MyClassImpl(\n    override val name: String,\n    private val myRepo: MyRepo,\n) : MyClassAPI {\n    override fun doSomething(): String {\n        return fetchData()\n    }\n    \n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nThird, the original class becomes `expect` class\n`MyClass` should extend `MyClassAPI` and override all public symbols from `MyClassAPI`, without method bodies and property values, since `expect` classes can't have any.\nIf `MyClass` had any internal symbols, these should also be listed in the `expect` class along with their KDocs.\nIf `MyClass` had a KDoc, add it on top of the expect class without parameters/properties description.\nIf `MyClass` original KDoc described certain parameters/properties that it accepted in its constructor, add these to KDoc of the expect class secondary constructor.\nThis constructor KDoc can't have `@property` tag for parameters, use only `@param` for all parameters, even if the original was `@property`.\n\n```kt\npackage com.example\n\n/**\n * A sample class with an external dependency, some logic and public methods\n */\npublic expect class MyClass internal constructor(\n    delegate: MyClassImpl,\n) : MyClassAPI {\n    /**\n     * Constructs a new instance of [MyClass]\n     * \n     * @param name Some public property\n     * @param myRepo Some external dependency,\n     */\n    public constructor(\n        name: String,\n        myRepo: MyRepo,\n    )\n    \n    internal val delegate: MyClassImpl\n    \n    override val name: String\n    override fun doSomething(): String\n    \n    /**\n     * Internal method\n     */\n    internal fun doInternal(): String\n}\n```\n\nFourth, ensure that `jvmCommonMain` and `nonJvmCommonMain` source sets have a package directory for `MyClass`, i.e., `com.example`. If not, create missing package directories.\nThe source sets themselves are already declared in the Gradle convention plugin — you do NOT need to add them to the module's `build.gradle.kts` unless you need to add dependencies specifically for these source sets.\nThen, create `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` source sets.\n\nAfter these steps, the structure should look as follows, omitting irrelevant packages and files:\n```\ncommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n            MyClassAPI.kt\n            MyClassImpl.kt\njvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\nnonJvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n```\n\nFifth, in `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` create `actual` declarations for `MyClass`.\nInitially, they should be identical for both source sets.\nIt should use `by delegate` to delegate all public symbols from `MyClassAPI` to the implementation, and direct delegation to delegate internal symbols.\n\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    internal actual fun doInternal(): String = delegate.doInternal()\n}\n```\n\nSixth, optional, step.\nIf the users asked for certain JVM-specific/non-JVM-specific functionality, these actual classes might contain additional \npublic symbols not present in the API interface. Such additional public symbols should be documented properly and concisely with KDoc\n\nFor example, `jvmCommonMain` implementation that uses JVM-specific API\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    /**\n     * Gets environment variable value by [key]\n     */\n    public fun getEnv(key: String): String = System.getenv(key)\n}\n```\n\nSeventh, if `MyClass` had any tests in the original `commonTest` source set, use `MyClassImpl` in the test to verify common implementation.\nDo not rename the test, it should still be called e.g. `MyClassTest`.\nIf you need to test some platform-specific functionality, create tests in `jvmCommonTest` and/or `nonJvmCommonTest` source sets.\nKotlin tests for JVM-specific functionality should go to `jvmCommonTest`.\nIMPORTANT: if the test suite is written in Java, it MUST go to `jvmTest`, not `jvmCommonTest`. KMP only compiles Java code in `jvmMain`/`jvmTest` source sets.\n\nFinally, ensure that the implementation compiles by running:\n```sh\n./gradlew :mymodule:compileKotlinJvm\n./gradlew :mymodule:compileKotlinJs\n```\nReplace `:mymodule` with the actual Gradle module path derived from the file location (e.g., `agents/agents-core` → `:agents:agents-core`).\nIf both pass, the split is working correctly.\n\n### IntelliJ false positives\nDue to the experimental state of expect/actual classifiers, IntelliJ may report false positive errors such as \"no actual for expect\" or \"no expect for actual\". \nThese diagnostics should NOT be trusted. The only reliable verification is running the Gradle compile tasks listed above.\n\n## Important notes\n\n### Imports\nWhen creating the new files (API interface, Impl, expect class, actual classes), carry over all relevant imports from the original class.\n\n### Annotations\nClasses being split should not have annotations. If the original class has annotations (e.g., `@Serializable`, `@Deprecated`), ask the user explicitly how to handle them before proceeding.\n\n### Companion objects\nClasses being split should not have companion objects. If the original class has a companion object, ask the user explicitly how to handle it before proceeding.\n","frontmatter":{"name":"split-jvm-nonjvm","description":"Splits a class or several classes in the common source set commonMain into JVM and non-JVM parts into jvmCommonMain and nonJvmCommonMain. \nThis is used to provide some JVM-specific functionality for a class on JVM targets (e.g. Kotlin/JVM or Java backend or Android).\n"},"isInternal":false,"tokens":1808,"sizeBytes":8133},{"name":"annotation-based-tools.md","path":"docs/docs/tools/annotation-based-tools.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/tools/annotation-based-tools.md","title":"Tools Skill","category":"anthropic-skill","format":"markdown","content":"# Annotation-based tools\n\nAnnotation-based tools provide a declarative way to expose functions and methods as tools for large language models (LLMs) in both Kotlin and Java.\nBy using annotations, you can transform any function or method into a tool that LLMs can understand and use.\n\nThis approach is useful when you need to expose existing functionality to LLMs in Kotlin or Java without implementing tool descriptions manually.\n\n!!! note\n    Annotation-based tools are JVM-only and not available for other platforms. For multiplatform support, use the [class-based tool API](class-based-tools.md).\n\n## Key annotations\n\nTo start using annotation-based tools in your project, you need to understand the following key annotations:\n\n| Annotation        | Description                                                             |\n|-------------------|-------------------------------------------------------------------------|\n| `@Tool`           | Marks functions that should be exposed as tools to LLMs.                |\n| `@LLMDescription` | Provides descriptive information about your tools and their components. |\n\n\n## @Tool annotation\n\nThe `@Tool` annotation is used to mark functions (Kotlin) or methods (Java) that should be exposed as tools to LLMs.\nThe functions and methods annotated with `@Tool` are collected by reflection from objects that implement the `ToolSet` interface. For details, see [Implement the ToolSet interface](#1-implement-the-toolset-interface).\n\n### Definition\n\n```kotlin\n@Target(AnnotationTarget.FUNCTION)\npublic annotation class Tool(val customName: String = \"\")\n```\n<!--- KNIT example-annotation-based-tools-01.txt -->\n\n### Parameters\n\n| <div style=\"width:100px\">Name</div> | Required | Description                                                                              |\n|-------------------------------------|----------|------------------------------------------------------------------------------------------|\n| `customName`                        | No       | Specifies a custom name for the tool. If not provided, the name of the function is used. |\n\n### Usage\n\nTo mark a function or method as a tool, apply the `@Tool` annotation to this function or method in a class that implements the `ToolSet` interface:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    \n        @Tool(customName = \"customToolName\")\n        fun anotherTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyToolSet implements ToolSet {\n        @Tool\n        public String myTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    \n        @Tool(customName = \"customToolName\")\n        public String anotherTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-01.java -->\n\n## @LLMDescription annotation\n\nThe `@LLMDescription` annotation provides descriptive information about code elements (classes, functions, methods, parameters, and so on) to LLMs.\nThis helps LLMs understand the purpose and usage of these elements.\n\n### Definition\n\n```kotlin\n@Target(\n    AnnotationTarget.PROPERTY,\n    AnnotationTarget.CLASS,\n    AnnotationTarget.TYPE,\n    AnnotationTarget.VALUE_PARAMETER,\n    AnnotationTarget.FUNCTION\n)\npublic annotation class LLMDescription(val description: String)\n```\n<!--- KNIT example-annotation-based-tools-02.txt -->\n\n### Parameters\n\n| Name          | Required | Description                                    |\n|---------------|----------|------------------------------------------------|\n| `description` | Yes      | A string that describes the annotated element. |\n\n### Usage\n\nThe `@LLMDescription` annotation can be applied at various levels. For example:\n\n* Function level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Performs a specific operation and returns the result\")\n    fun myTool(): String {\n        // Function implementation\n        return \"Result\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-02.kt -->\n\n=== \"Java\"\n\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Performs a specific operation and returns the result\")\n    public String myTool() {\n        // Function implementation\n        return \"Result\";\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-02.java -->\n\n    \n* Parameter level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Processes input data\")\n    fun processTool(\n        @LLMDescription(\"The input data to process\")\n        input: String,\n    \n        @LLMDescription(\"Optional configuration parameters\")\n        config: String = \"\"\n    ): String {\n        // Function implementation\n        return \"Processed: $input with config: $config\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Processes input data\")\n    public String processTool(\n            @LLMDescription(description = \"The input data to process\") String input,\n            @LLMDescription(description = \"Optional configuration parameters\") String config\n    ) {\n        // Function implementation\n        return \"Processed: \" + input + \" with config: \" + config;\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-03.java -->\n\n\n## Creating a tool\n\n### 1. Implement the ToolSet interface\n\nCreate a class that implements the [`ToolSet`](api:agents-tools::ai.koog.agents.core.tools.reflect.ToolSet) interface.\nThis interface marks your class as a container for tools.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-04.java -->\n\n### 2. Add tool functions\n\nAdd functions or methods to your class and annotate them with `@Tool` to expose them as tools:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        fun getWeather(location: String): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        public String getWeather(String location) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-05.java -->\n\n### 3. Add descriptions\n\nAdd `@LLMDescription` annotations to provide context for the LLM:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for getting weather information\")\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Get the current weather for a location\")\n        fun getWeather(\n            @LLMDescription(\"The city and state/country\")\n            location: String\n        ): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for getting weather information\")\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Get the current weather for a location\")\n        public String getWeather(\n                @LLMDescription(description = \"The city and state/country\") String location\n        ) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-06.java -->\n\n### 4. Use your tools with an agent\n\nNow you can use your tools with an agent:\n\n=== \"Kotlin\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleAnnotationBasedTools06.MyFirstToolSet\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    const val apiToken = \"\"\n    -->\n    ```kotlin\n    fun main() {\n        runBlocking {\n            // Create your tool set\n            val weatherTools = MyFirstToolSet()\n    \n            // Create an agent with your tools\n    \n            val agent = AIAgent(\n                promptExecutor = simpleOpenAIExecutor(apiToken),\n                systemPrompt = \"Provide weather information for a given location.\",\n                llmModel = OpenAIModels.Chat.GPT4o,\n                toolRegistry = ToolRegistry {\n                    tools(weatherTools)\n                }\n            )\n    \n            // The agent can now use your weather tools\n            agent.run(\"What's the weather like in New York?\")\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    String apiToken = System.getenv(\"OPENAI_API_KEY\");\n\n    // Create your tool set\n     MyFirstToolSet weatherTools = new MyFirstToolSet();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(weatherTools)\n        .build();\n\n    // Create an agent with your tools\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"Provide weather information for a given location.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(toolRegistry)\n        .build();\n\n    // The agent can now use your weather tools\n    String result = agent.run(\"What's the weather like in New York?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT example-annotation-based-tools-java-07.java -->\n\n## Usage examples\n\nHere are some real-world examples of tool annotations.\n\n### Basic example: Switch controller\n\nThis example shows a simple tool set for controlling a switch:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    class Switch(private var state: Boolean) {\n        fun switch(state: Boolean) {\n            this.state = state\n        }\n        fun isOn(): Boolean {\n            return state\n        }\n    }\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for controlling a switch\")\n    class SwitchTools(val switch: Switch) : ToolSet {\n        @Tool\n        @LLMDescription(\"Switches the state of the switch\")\n        fun switch(\n            @LLMDescription(\"The state to set (true for on, false for off)\")\n            state: Boolean\n        ): String {\n            switch.switch(state)\n            return \"Switched to ${if (state) \"on\" else \"off\"}\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Returns the current state of the switch\")\n        fun switchState(): String {\n            return \"Switch is ${if (switch.isOn()) \"on\" else \"off\"}\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-08.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class Switch {\n        private boolean state;\n\n        public Switch(boolean state) {\n            this.state = state;\n        }\n\n        // \"switch\" is a reserved keyword in Java, so we use a different method name\n        public void setState(boolean state) {\n            this.state = state;\n        }\n\n        public boolean isOn() {\n            return state;\n        }\n    }\n    \n    @LLMDescription(description = \"Tools for controlling a switch\")\n    public class SwitchTools implements ToolSet {\n        private final Switch sw;\n\n        public SwitchTools(Switch sw) {\n            this.sw = sw;\n        }\n\n        @Tool\n        @LLMDescription(description = \"Switches the state of the switch\")\n        public String switchStateTo(\n                @LLMDescription(description = \"The state to set (true for on, false for off)\") boolean state\n        ) {\n            sw.setState(state);\n            return \"Switched to \" + (state ? \"on\" : \"off\");\n        }\n\n        @Tool\n        @LLMDescription(description = \"Returns the current state of the switch\")\n        public String switchState() {\n            return \"Switch is \" + (sw.isOn() ? \"on\" : \"off\");\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-08.java -->\n\nWhen an LLM needs to control a switch, it can understand the following information from the provided description:\n\n- The purpose and functionality of the tools.\n- The required parameters for using the tools.\n- The acceptable values for each parameter.\n- The expected return values upon execution.\n\n### Advanced example: Diagnostic tools\n\nThis example shows a more complex tool set for device diagnostics:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing diagnostics and troubleshooting on devices\")\n    class DiagnosticToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Run diagnostic on a device to check its status and identify any issues\")\n        fun runDiagnostic(\n            @LLMDescription(\"The ID of the device to diagnose\")\n            deviceId: String,\n    \n            @LLMDescription(\"Additional information for the diagnostic (optional)\")\n            additionalInfo: String = \"\"\n        ): String {\n            // Implementation\n            return \"Diagnostic results for device $deviceId\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Analyze an error code to determine its meaning and possible solutions\")\n        fun analyzeError(\n            @LLMDescription(\"The error code to analyze (e.g., 'E1001')\")\n            errorCode: String\n        ): String {\n            // Implementation\n            return \"Analysis of error code $errorCode\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-09.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing diagnostics and troubleshooting on devices\")\n    public class DiagnosticToolSet implements ToolSet {\n        // Convenience overload (not exposed as a tool)\n        public String runDiagnostic(String deviceId) {\n            return runDiagnostic(deviceId, \"\");\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Run diagnostic on a device to check its status and identify any issues\")\n        public String runDiagnostic(\n                @LLMDescription(description = \"The ID of the device to diagnose\") String deviceId,\n                @LLMDescription(description = \"Additional information for the diagnostic (optional)\") String additionalInfo\n        ) {\n            // Implementation\n            return \"Diagnostic results for device \" + deviceId;\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Analyze an error code to determine its meaning and possible solutions\")\n        public String analyzeError(\n                @LLMDescription(description = \"The error code to analyze (e.g., 'E1001')\") String errorCode\n        ) {\n            // Implementation\n            return \"Analysis of error code \" + errorCode;\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-09.java -->\n\n\n## Best practices\n\n* **Provide clear descriptions**: write clear, concise descriptions that explain the purpose and behavior of tools, parameters, and return values.\n* **Describe all parameters**: add `@LLMDescription` to all parameters to help LLMs understand what each parameter is for.\n* **Use consistent naming**: use consistent naming conventions for tools and parameters to make them more intuitive.\n* **Group related tools**: group related tools in the same `ToolSet` implementation and provide a class-level description.\n* **Return informative results**: make sure tool return values provide clear information about the result of the operation.\n* **Handle errors gracefully**: include error handling in your tools and return informative error messages.\n* **Document default values**: when parameters have default values (Kotlin) or overloads (Java), document this in the description.\n* **Keep tools focused**: Each tool should perform a specific, well-defined task rather than trying to do too many things.\n\n## Troubleshooting common issues\n\nWhen working with tool annotations, you might encounter some common issues.\n\n### Tools not being recognized\n\nIf the agent does not recognize your tools, check the following:\n\n- Your class implements the `ToolSet` interface.\n- All tool functions or methods are annotated with `@Tool`.\n- Tool functions or methods have appropriate return types (`String` is recommended for simplicity).\n- Your tools are properly registered with the agent.\n\n### Unclear tool descriptions\n\nIf the LLM does not use your tools correctly or misunderstands their purpose, try the following:\n\n- Use primitive parameter types when possible (`String`, `Boolean`, `Int` in Kotlin, or `String`, `boolean`, `int` in Java).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n- Note that Java doesn't support default parameters. Use method overloading instead.\n\n### Parameter type issues\n\nIf the LLM provides incorrect parameter types, try the following:\n\n- Use simple parameter types when possible (`String`, `Boolean`, `Int`).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n\n### Performance issues\n\nIf your tools cause performance problems, try the following:\n\n- Keep tool implementations lightweight.\n- For resource-intensive operations, consider implementing asynchronous processing.\n- Cache results when appropriate.\n- Log tool usage to identify bottlenecks.\n","isInternal":false,"tokens":4298,"sizeBytes":20097},{"name":"built-in-tools.md","path":"docs/docs/tools/built-in-tools.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/tools/built-in-tools.md","title":"Tools Skill","category":"anthropic-skill","format":"markdown","content":"# Built-in tools\n\nKoog provides built-in tools for Kotlin and Java to help you quickly prototype and experiment with agent-user interaction.\nThese tools are not intended for production use. To use them, add `ai.koog:agents-ext` to your dependencies.\nThe following built-in tools are available:\n\n| Tool              | <div style=\"width:115px\">Name</div> | Description                                                                                                              |\n|-------------------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| SayToUser         | `__say_to_user__`                   | Lets the agent send a message to the user. It prints the agent message to the console with the `Agent says: ` prefix.    |\n| AskUser           | `__ask_user__`                      | Lets the agent ask the user for input. It prints the agent message to the console and waits for user response.           |\n| ExitTool          | `__exit__`                          | Lets the agent finish the conversation and terminate the session.                                                        |\n| ReadFileTool      | `__read_file__`                     | Reads text file with optional line range selection. Returns formatted content with metadata using 0-based line indexing. |\n| EditFileTool      | `__edit_file__`                     | Makes a single, targeted text replacement in a file; can also create new files or fully replace contents.                |\n| ListDirectoryTool | `__list_directory__`                | Lists directory contents as a hierarchical tree with optional depth control and glob filtering.                          |\n| WriteFileTool     | `__write_file__`                    | Writes text content to a file (creating parent directories if needed).                                                   |\n\n\n## Registering built-in tools\n\nLike any other tool, a built-in tool must be added to the tool registry to become available for an agent. Here is an example:\n\n<!--- INCLUDE\nimport ai.koog.agents.core.agent.AIAgent\nimport ai.koog.agents.core.tools.ToolRegistry\nimport ai.koog.agents.ext.tool.SayToUser\nimport ai.koog.agents.ext.tool.AskUser\nimport ai.koog.agents.ext.tool.ExitTool\nimport ai.koog.agents.ext.tool.file.ListDirectoryTool\nimport ai.koog.agents.ext.tool.file.ReadFileTool\nimport ai.koog.agents.ext.tool.file.WriteFileTool\nimport ai.koog.prompt.executor.clients.openai.OpenAIModels\nimport ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\nimport ai.koog.rag.base.files.JVMFileSystemProvider\n\nconst val apiToken = \"\"\n\n-->\n```kotlin\n// Create a tool registry with all built-in tools\nval toolRegistry = ToolRegistry {\n    tool(SayToUser)\n    tool(AskUser)\n    tool(ExitTool)\n    tool(ReadFileTool(JVMFileSystemProvider.ReadOnly))\n    tool(ListDirectoryTool(JVMFileSystemProvider.ReadOnly))\n    tool(WriteFileTool(JVMFileSystemProvider.ReadWrite))\n}\n\n// Pass the registry when creating an agent\nval agent = AIAgent(\n    promptExecutor = simpleOpenAIExecutor(apiToken),\n    systemPrompt = \"You are a helpful assistant.\",\n    llmModel = OpenAIModels.Chat.GPT4o,\n    toolRegistry = toolRegistry\n)\n\n```\n<!--- KNIT example-built-in-tools-01.kt -->\n\nYou can create a comprehensive set of capabilities for your agent by combining built-in tools and custom tools within the same registry in both Kotlin and Java.\nTo learn more about custom tools, see [Annotation-based tools](annotation-based-tools.md) and [Class-based tools](class-based-tools.md).\n","isInternal":false,"tokens":741,"sizeBytes":3595},{"name":"class-based-tools.md","path":"docs/docs/tools/class-based-tools.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/tools/class-based-tools.md","title":"Tools Skill","category":"anthropic-skill","format":"markdown","content":"# Class-based tools\n\nThis section explains the API designed for scenarios that require enhanced flexibility and customized behavior.\nWith this approach in Kotlin, you have full control over a tool, including its parameters, metadata, execution logic, and how it is registered and invoked. In Java, tools are created using annotation-based methods with reflection-based registration.\n\nThis level of control is ideal for creating sophisticated tools that extend basic use cases, enabling seamless integration into agent sessions and workflows.\n\nThis page describes how to implement a tool in both Kotlin and Java, manage tools through registries, call them, and use within node-based agent architectures.\n\n!!! note\n    The API is multiplatform for Kotlin. Java tools are implemented using annotation-based methods and registered via reflection. This lets you use the same tools across different platforms in Kotlin, while Java provides full JVM interoperability.\n\n## Tool implementation\n\nThe Koog framework provides the following approaches for implementing tools:\n\nFor Kotlin:\n\n* Using the base class `Tool` for all tools. You should use this class when you need to return non-text results or require complete control over the tool behavior.\n* Using the `SimpleTool` class that extends the base `Tool` class and simplifies the creation of tools that return text results. You should use this approach for scenarios where the \n  tool only needs to return a text.\n\nBoth approaches use the same core components but differ in implementation and the results they return.\n\nFor Java:\n\n* Using annotation-based methods (`@Tool` and `@LLMDescription`) with reflection-based registration. This is the recommended approach for Java interoperability, as subclassing Kotlin's `Tool` or `SimpleTool` from Java is not supported due to suspend function limitations.\n\n### Tool class (Kotlin)\n\nThe [`Tool<Args, Result>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html) abstract class is the base class for creating tools in Kotlin.\nIt lets you create tools that accept specific argument types (`Args`) and return results of various types (`Result`).\n\nEach tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the tool.                                                                                                                                                                                                                                                                                                                                                             |\n| `Result`                                 | The serializable type of result that the tool returns. If you want to present tool results in a custom format, please inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) class and implement `textForLLM(): String` method                                                                                                           |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are deserialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                                                                                                                                                        |\n| `resultSerializer`                       | The overridden variable that defines how the result of the tool is deserialized. See also [resultSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/result-serializer.html). If you chose to inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) consider using `ToolResultUtils.toTextSerializer()` |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/>- `optionalParameters` (empty by default)<br/>See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html).                                                                                                                                |\n| `execute()`                              | The function that implements the logic of the tool. It takes arguments of type `Args` and returns a result of type `Result`. See also [execute()]().                                                                                                                                                                                                                                                                                  |\n\n!!! note \"Java Implementation\"\n    In Java, instead of subclassing `Tool<Args, Result>`, use annotation-based methods with `@Tool` and `@LLMDescription`. The framework handles serialization and registration automatically through reflection. For more\n    details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` property; in Java, use `@LLMDescription` annotations.\n\n#### Usage example\n\nHere is an example of a custom tool implementation using the `Tool` class that returns a numeric result:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    -->\n    ```kotlin\n    // Implement a simple calculator tool that adds two digits\n    object CalculatorTool : Tool<CalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"calculator\",\n        description = \"A simple calculator that can add two digits (0-9).\"\n    ) {\n\n        // Arguments for the calculator tool\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        ) {\n            init {\n                require(digit1 in 0..9) { \"digit1 must be a single digit (0-9)\" }\n                require(digit2 in 0..9) { \"digit2 must be a single digit (0-9)\" }\n            }\n        }\n\n        // Function to add two digits\n        override suspend fun execute(args: Args): Int = args.digit1 + args.digit2\n    }\n    ```\n    <!--- KNIT example-class-based-tools-01.kt -->\n\nAfter implementing your tool, you need to add it to a tool registry and then use it with an agent. For details, see [Tool registry](../tools/index.md#tool-registry).\n\nFor more details, see [API reference](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html).\n\n#### Reading the agent context from a tool\n\nTools that need the agent's full state (LLM context, run id, configuration, storage, ...) extend `AgentContextAwareTool<Args, Result>` instead of `Tool<Args, Result>`. The framework injects the live `AIAgentContext` driving the call, and the tool receives it as a typed parameter rather than reading it out of the argument schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.context.AIAgentContext\n    import ai.koog.agents.core.agent.tools.AgentContextAwareTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // A tool that reads the live AIAgentContext driving the call.\n    object TracingCalculatorTool : AgentContextAwareTool<TracingCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"tracing_calculator\",\n        description = \"Adds two digits and emits a log line tagged with the agent run id.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, context: AIAgentContext): Int {\n            val runId = context.runId\n            // ... use runId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-01.kt -->\n\n`AgentContextAwareTool` is dispatched by the framework via a per-call `ToolCallMetadata` side channel that the framework manages on the tool's behalf. Invoking such a tool outside an agent run throws `IllegalStateException` because no `AIAgentContext` was injected; production code should always go through `ContextualAgentEnvironment`, and unit tests can supply the context explicitly via `ToolCallMetadata.of(AgentContextAwareTool.AgentContextKey to context)`.\n\n#### Reading raw per-call metadata\n\nA small number of tools want to read caller- or feature-contributed entries that are *not* the agent context (for example a distributed-tracing span id contributed by an observability feature). These tools extend `ToolBase<Args, Result>` directly, which exposes the full `ToolCallMetadata` bag:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolBase\n    import ai.koog.agents.core.tools.ToolCallMetadata\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    object SpanAwareCalculatorTool : ToolBase<SpanAwareCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"span_aware_calculator\",\n        description = \"Adds two digits, propagating a tracing span id from caller or feature metadata.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, metadata: ToolCallMetadata): Int {\n            val traceSpanId = metadata[\"trace.span.id\"] as? String\n            // ... use traceSpanId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-02.kt -->\n\nCallers can pass metadata through `SafeTool.execute(args, serializer, metadata)` or directly through `AIAgentEnvironment.executeTool(toolCall, metadata)`. Features can contribute metadata for every tool call during installation by calling `pipeline.provideToolCallMetadata(this) { eventContext -> mapOf(...) }`. Caller-supplied metadata always wins over feature contributions on key collision.\n\nExisting tools that extend `Tool<Args, Result>` and override `execute(args)` continue to work unchanged: the framework dispatches them through the same path and discards any `ToolCallMetadata`. To opt in to metadata, switch to `AgentContextAwareTool` (typed context access) or `ToolBase` (raw bag access).\n\n### SimpleTool class (Kotlin)\n\nThe [`SimpleTool<Args>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/index.html) abstract class extends `Tool<Args, ToolResult.Text>` and simplifies the creation of tools that return text results.\n\nEach simple tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                              |\n|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the custom tool.                                                                                                                                                                                                                         |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are serialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                             |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/> - `optionalParameters` (empty by default)<br/> See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html). |\n| `doExecute()`                            | The overridden function that describes the main action performed by the tool. It takes arguments of type `Args` and returns a `String`. See also [doExecute()](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/do-execute.html).                                          |\n\n!!! note \"Java Implementation\"\n    In Java, the equivalent approach is to use annotation-based methods that return `String`. The framework automatically handles the text result wrapping. For more details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` and constructor parameters; in Java, use `@Tool` and `@LLMDescription` annotations.\n\n#### Usage example \n\nHere is an example of a custom tool implementation using `SimpleTool` in Kotlin:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.SimpleTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // Create a tool that casts a string expression to a double value\n    object CastToDoubleTool : SimpleTool<CastToDoubleTool.Args>(\n        argsType = typeToken<Args>(),\n        name = \"cast_to_double\",\n        description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"An expression to case to double\")\n            val expression: String,\n            @property:LLMDescription(\"A comment on how to process the expression\")\n            val comment: String\n        )\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): String {\n            return \"Result: ${castToDouble(args.expression)}, \" + \"the comment was: ${args.comment}\"\n        }\n\n        // Function to cast a string expression to a double value\n        private fun castToDouble(expression: String): Double {\n            return expression.toDoubleOrNull() ?: 0.0\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-02.kt -->\n\n### Annotation-based methods (Java)\n\nTo implement tools in Java, instead of subclassing `Tool` or `SimpleTool`, use annotation-based methods with `@Tool` and\n`@LLMDescription`. Koog handles serialization and registration automatically through reflection. To learn more about the\nimplementation, see Java examples below.\n\n#### Usage examples\n\nThis is an example of a tool implementation in Java, equivalent to using the `Tool` class in Kotlin.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent: implement the tool as a Java method and register it via ToolRegistry.builder().\n    // This is the recommended Java interop path instead of subclassing the Kotlin Tool base class.\n    public final class CalculatorTool {\n        private CalculatorTool() {}\n    \n        @Tool(customName = \"calculator\")\n        @LLMDescription(description = \"A simple calculator that can add two digits (0-9).\")\n        public static int calculator(\n                @LLMDescription(description = \"The first digit to add (0-9)\") int digit1,\n                @LLMDescription(description = \"The second digit to add (0-9)\") int digit2\n        ) {\n            if (digit1 < 0 || digit1 > 9) throw new IllegalArgumentException(\"digit1 must be a single digit (0-9)\");\n            if (digit2 < 0 || digit2 > 9) throw new IllegalArgumentException(\"digit2 must be a single digit (0-9)\");\n            return digit1 + digit2;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CalculatorTool.class.getMethod(\"calculator\", int.class, int.class))\n                .build();\n        }\n    }\n    // Note: Subclassing the Kotlin Tool<TArgs, TResult> and overriding a suspend execute(...) from Java is not supported.\n    // The Java interop uses reflection-based registration of Java methods as tools.\n    ```\n    <!--- KNIT example-class-based-tools-java-01.java -->\n\nHere is an example of a tool implementation in Java, equivalent to using the `SimpleTool` class in Kotlin. This example\nimplements a simple tool that returns a text result.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent of SimpleTool: provide a Java method and register it as a tool.\n    public final class CastToDoubleTool {\n        private CastToDoubleTool() {}\n    \n        @Tool(customName = \"cast_to_double\")\n        @LLMDescription(description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\")\n        public static String castToDouble(\n                @LLMDescription(description = \"An expression to case to double\") String expression,\n                @LLMDescription(description = \"A comment on how to process the expression\") String comment\n        ) {\n            double value;\n            try {\n                value = Double.parseDouble(expression);\n            } catch (Exception e) {\n                value = 0.0;\n            }\n            return \"Result: \" + value + \", the comment was: \" + comment;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CastToDoubleTool.class.getMethod(\"castToDouble\", String.class, String.class))\n                .build();\n        }\n    }\n    // Note: Extending Kotlin SimpleTool<TArgs> from Java is not required; registering a Java method is the idiomatic approach.\n    ```\n    <!--- KNIT example-class-based-tools-java-02.java -->\n\n### Sending tool result to LLM in custom format\n\nFor Kotlin:\n\nIf you are not happy with JSON results sent to LLM (in some cases, LLMs can work better if tool output is structured as Markdown, for instance), you have to follow the following steps:\n\n1. Implement `ToolResult.TextSerializable` interface, and override `textForLLM()` method\n2. Override `resultSerializer` using `ToolResultUtils.toTextSerializer<T>()`\n\nFor Java:\n\nReturn formatted text (such as Markdown) directly as a `String` from your annotated method. The framework handles this automatically.\n\n#### Example\n\nHere is an example showing custom formatted output in both Kotlin and Java:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.prompt.markdown.markdown\n    -->\n    ```kotlin\n    // A tool that edits file\n    object EditFile : Tool<EditFile.Args, EditFile.Result>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Result>(),\n        name = \"edit_file\",\n        description = \"Edits the given file\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        public data class Args(\n            val path: String,\n            val original: String,\n            val replacement: String\n        )\n\n        @Serializable\n        public data class Result(\n            private val patchApplyResult: PatchApplyResult\n        ) {\n\n            @Serializable\n            public sealed interface PatchApplyResult {\n                @Serializable\n                public data class Success(val updatedContent: String) : PatchApplyResult\n\n                @Serializable\n                public sealed class Failure(public val reason: String) : PatchApplyResult\n            }\n\n            // Textual output (in Markdown format) that will be visible to the LLM after the tool finishes.\n            fun textForLLM(): String = markdown {\n                if (patchApplyResult is PatchApplyResult.Success) {\n                    line {\n                        bold(\"Successfully\").text(\" edited file (patch applied)\")\n                    }\n                } else {\n                    line {\n                        text(\"File was \")\n                            .bold(\"not\")\n                            .text(\" modified (patch application failed: ${(patchApplyResult as PatchApplyResult.Failure).reason})\")\n                    }\n                }\n            }\n\n            override fun toString(): String = textForLLM()\n        }\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): Result {\n            return TODO(\"Implement file edit\")\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n\n    // Java equivalent: return Markdown text directly to the LLM from a Java method and register it as a tool.\n    // This avoids needing a custom serializable Result type (which would require Kotlin serialization support).\n    public final class EditFile {\n        private EditFile() {}\n\n        @Tool(customName = \"edit_file\")\n        @LLMDescription(description = \"Edits the given file\")\n        public static String editFile(\n                String path,\n                String original,\n                String replacement\n        ) {\n            // TODO: Implement file edit logic; below is a placeholder illustrating Markdown output\n            boolean success = false;\n            if (success) {\n                return \"**Successfully** edited file (patch applied)\";\n            } else {\n                return \"File was **not** modified (patch application failed: reason)\";\n            }\n        }\n\n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(EditFile.class.getMethod(\"editFile\", String.class, String.class, String.class))\n                .build();\n        }\n    }\n    // Note: If you need a structured custom Result object from Java, you must expose a Kotlin @Serializable type\n    // or another serializer-aware type. Returning String works out-of-the-box with Koog's Java interop.\n    ```\n    <!--- KNIT example-class-based-tools-java-03.java -->\n\nAfter implementing your tool in Kotlin or Java, you need to add it to a tool registry and then use it with an agent.\nFor details, see [Tool registry](../tools/index#tool-registry).\n","isInternal":false,"tokens":4963,"sizeBytes":25745},{"name":"index.md","path":"docs/docs/tools/index.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/tools/index.md","title":"Tools Skill","category":"anthropic-skill","format":"markdown","content":"# Overview\n\n Agents use tools to perform specific tasks or access external systems.\n\n## Tool workflow\n\nThe Koog framework offers the following workflow for working with tools in Kotlin and Java:\n\n1. Create a custom tool or use one of the built-in tools.\n2. Add the tool to a tool registry.\n3. Pass the tool registry to an agent.\n4. Use the tool with the agent.\n\n### Available tool types\n\nThere are three types of tools in the Koog framework:\n\n- Built-in tools that provide functionality for agent-user interaction and conversation management. For details, see [Built-in tools](built-in-tools.md).\n- Annotation-based custom tools that let you expose functions as tools to LLMs. For details, see [Annotation-based tools](annotation-based-tools.md).\n- Custom tools that let you control tool parameters, metadata, execution logic, and how it is registered and invoked. For details, see [Class-based\n  tools](class-based-tools.md).\n\n### Tool registry\n\nBefore you can use a tool in an agent, you must add it to a tool registry.\nThe tool registry manages all tools available to the agent.\n\nThe key features of the tool registry:\n\n- Organizes tools.\n- Supports merging of multiple tool registries.\n- Provides methods to retrieve tools by name or type.\n\nTo learn more, see [ToolRegistry](api:agents-tools::ai.koog.agents.core.tools.ToolRegistry).\n\nHere is an example of how to create the tool registry and add the tool to it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    val myTool = MyToolSet()\n    -->\n    ```kotlin\n    val toolRegistry = ToolRegistry {\n        tools(myTool)\n    }\n    ```\n    <!--- KNIT example-tools-overview-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create an instance of your ToolSet\n    MyToolSet myTool = new MyToolSet();\n\n    // Build the ToolRegistry and register tools from the ToolSet\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(myTool)\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-01.java -->\n\nTo merge multiple tool registries, do the following:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class FirstToolSet : ToolSet {\n        @Tool\n        fun firstSampleTool(): String {\n            // Tool implementation\n            return \"First result\"\n        }\n    }\n    class SecondToolSet : ToolSet {\n        @Tool\n        fun secondSampleTool(): String {\n            // Tool implementation\n            return \"Second result\"\n        }\n    }\n    val firstSampleTool = FirstToolSet()\n    val secondSampleTool = SecondToolSet()\n    -->\n    ```kotlin\n    val firstToolRegistry = ToolRegistry {\n        tools(firstSampleTool)\n    }\n    \n    val secondToolRegistry = ToolRegistry {\n        tools(secondSampleTool)\n    }\n    \n    val newRegistry = firstToolRegistry + secondToolRegistry\n    ```\n    <!--- KNIT example-tools-overview-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create instances of your ToolSets\n    FirstToolSet firstSampleTool = new FirstToolSet();\n    SecondToolSet secondSampleTool = new SecondToolSet();\n\n    // Build separate tool registries\n    ToolRegistry firstToolRegistry = ToolRegistry.builder()\n        .tools(firstSampleTool)\n        .build();\n\n    ToolRegistry secondToolRegistry = ToolRegistry.builder()\n        .tools(secondSampleTool)\n        .build();\n\n    ToolRegistry newRegistry = firstToolRegistry.plus(secondToolRegistry);\n    ```\n    <!--- KNIT example-tools-overview-java-02.java -->\n\n### Passing tools to an agent\n\nTo enable an agent to use a tool, you need to provide a tool registry that contains this tool as an argument when creating the agent:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.example.exampleToolsOverview01.toolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Agent initialization\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        systemPrompt = \"You are a helpful assistant with strong mathematical skills.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        // Pass your tool registry to the agent\n        toolRegistry = toolRegistry\n    )\n    ```\n    <!--- KNIT example-tools-overview-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are a helpful assistant with strong mathematical skills.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(ToolRegistry.builder()\n            .tools(secondSampleTool)\n            .build()\n        )\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-03.java -->\n\n### Calling tools\n\nThere are several ways to call tools within your agent code. The recommended approach is to use the provided methods\nin the agent context rather than calling tools directly, as this ensures proper handling of tool operation within the\nagent environment.\n\n!!! tip\n    Ensure you have implemented proper [error handling](../features/agent-event-handlers.md) in your tools to prevent agent failure.\n\nThe tools are called within a specific session context represented by `AIAgentLLMWriteSession`.\nIt provides several methods for calling tools so that you can:\n\n- Call a tool with the given arguments.\n- Call a tool by its name and the given arguments.\n- Call a tool by the provided tool class and arguments.\n- Call a tool of the specified type with the given arguments.\n- Call a tool that returns a raw string result.\n\nFor more details, the API reference for [AIAgentLLMWriteSession](api:agents-core::ai.koog.agents.core.agent.session.AIAgentLLMWriteSession).\n\n#### Parallel tool calls\n\nYou can also call tools in parallel using the `toParallelToolCallsRaw` extension. For example:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.builder.node\n    import ai.koog.agents.core.tools.SimpleTool\n    import kotlinx.coroutines.flow.collect\n    import kotlinx.coroutines.flow.flow\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    @Serializable\n    data class Book(\n        val title: String,\n        val author: String,\n        val description: String\n    )\n    \n    class BookTool() : SimpleTool<Book>(\n        argsType = typeToken<Book>(),\n        name = NAME,\n        description = \"A tool to parse book information from Markdown\"\n    ) {\n        companion object {\n            const val NAME = \"book\"\n        }\n    \n        override suspend fun execute(args: Book): String {\n            println(\"${args.title} by ${args.author}:\\n ${args.description}\")\n            return \"Done\"\n        }\n    }\n    \n    val strategy = strategy<Unit, Unit>(\"strategy-name\") {\n    \n        /*...*/\n    \n        val myNode by node<Unit, Unit> { _ ->\n            llm.writeSession {\n                flow {\n                    emit(Book(\"Book 1\", \"Author 1\", \"Description 1\"))\n                }.toParallelToolCallsRaw(BookTool::class).collect()\n            }\n        }\n    }\n    \n    ```\n    <!--- KNIT example-tools-overview-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-04.java -->\n\n#### Calling tools from nodes\n\nWhen building agent workflows with nodes, you can use special nodes to call tools:\n\n* **nodeExecuteTool**: calls a single tool call and returns its result. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteTool).\n\n* **nodeExecuteSingleTool** that calls a specific tool with the provided arguments. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteSingleTool).\n\n* **nodeExecuteMultipleTools** that performs multiple tool calls and returns their results. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteMultipleTools).\n\n* **nodeLLMSendToolResult** that sends a tool result to the LLM and gets a response. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendToolResult).\n\n* **nodeLLMSendMultipleToolResults** that sends multiple tool results to the LLM. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendMultipleToolResults).\n\n## Using agents as tools\n\nThe framework provides the capability to convert any AI agent into a tool that can be used by other agents. \nThis powerful feature enables you to create hierarchical agent architectures where specialized agents can be called as tools by higher-level orchestrating agents.\n\n### Converting agents to tools\n\nTo convert an agent into a tool, use the `AIAgentService` and the `createAgentTool()` extension function:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.AIAgentService\n    import ai.koog.agents.core.agent.createAgentTool\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.serialization.typeToken\n    const val apiKey = \"\"\n    val analysisToolRegistry = ToolRegistry {}\n    -->\n    ```kotlin\n    // Create a specialized agent service, responsible for creating financial analysis agents.\n    val analysisAgentService = AIAgentService(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a financial analysis specialist.\",\n        toolRegistry = analysisToolRegistry\n    )\n    \n    // Create a tool that would run financial analysis agent once called.\n    val analysisAgentTool = analysisAgentService.createAgentTool(\n        agentName = \"analyzeTransactions\",\n        agentDescription = \"Performs financial transaction analysis\",\n        inputDescription = \"Transaction analysis request\",\n        inputType = typeToken<String>(),\n    )\n    ```\n    <!--- KNIT example-tools-overview-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-05.java -->\n\n\n### Using agent tools in other agents\n\nOnce converted to a tool, you can add the agent tool to another agent's tool registry:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleToolsOverview05.analysisAgentTool\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    const val apiKey = \"\"\n    -->\n    ```kotlin\n    // Create a coordinator agent that can use specialized agents as tools\n    val coordinatorAgent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You coordinate different specialized services.\",\n        toolRegistry = ToolRegistry {\n            tool(analysisAgentTool)\n            // Add other tools as needed\n        }\n    )\n    ```\n    <!--- KNIT example-tools-overview-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-06.java -->\n\n\n### Agent tool execution\n\nWhen an agent tool is called:\n\n1. The arguments are deserialized according to the input descriptor.\n2. The wrapped agent is executed with the deserialized input.\n3. The agent's output is serialized and returned as the tool result.\n\n### Benefits of agents as tools\n\n- **Modularity**: Break complex workflows into specialized agents.\n- **Reusability**: Use the same specialized agent across multiple coordinator agents.\n- **Separation of concerns**: Each agent can focus on its specific domain.\n","isInternal":false,"tokens":2891,"sizeBytes":12735},{"name":"tool-descriptor-schemer.md","path":"docs/docs/tools/tool-descriptor-schemer.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/tools/tool-descriptor-schemer.md","title":"Tools Skill","category":"anthropic-skill","format":"markdown","content":"# ToolDescriptorSchemer\n\n`ToolDescriptorSchemer` is an extension point that converts a `ToolDescriptor` into a JSON Schema object compatible with specific LLM providers. It can be implemented in both Kotlin and Java.\n\nKey points:\n\n- Location: `ai.koog.agents.core.tools.serialization.ToolDescriptorSchemer`\n- Contract: a single function `scheme(toolDescriptor: ToolDescriptor): JsonObject` or `generate(ToolDescriptor toolDescriptor): JsonObject` (Java)\n- Implementations provided:\n  - `OpenAICompatibleToolDescriptorSchemer` — generates schemas compatible with OpenAI‑style function/tool definitions.\n  - `OllamaToolDescriptorSchemer` — generates schemas compatible with Ollama tool JSON.\n\n<!--- INCLUDE\nimport ai.koog.agents.core.tools.ToolDescriptor\nimport ai.koog.agents.core.tools.ToolParameterDescriptor\nimport ai.koog.agents.core.tools.ToolParameterType\nimport kotlinx.serialization.json.JsonObject\n-->\n```kotlin\n// Interface\ninterface ToolDescriptorSchemaGenerator {\nfun generate(toolDescriptor: ToolDescriptor): JsonObject\n}\n```\n<!--- KNIT example-tool-descriptor-schemer-01.kt -->\n\n\n## Why use it\n\nIf you want to provide a custom scheme for existing or new LLM providers in Kotlin or Java, implement this interface to convert Koog’s `ToolDescriptor` into the expected JSON Schema format.\n\n## Implementation example\n\nBelow is a minimal custom implementation in both Kotlin and Java that renders only a subset of parameter types to illustrate how to plug into the SPI. Real implementations should cover all `ToolParameterType`s (String, Integer, Float, Boolean, Null, Enum, List, Object, AnyOf).\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.serialization.ToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    -->\n    ```kotlin\n    \n    class MinimalSchemer : ToolDescriptorSchemaGenerator {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public static class MinimalSchemer extends OpenAICompatibleToolDescriptorSchemaGenerator {\n        @Override\n        public JsonObject generate(ToolDescriptor toolDescriptor) {\n            Map<String, JsonElement> root = new LinkedHashMap<>();\n            root.put(\"type\", JsonPrimitive(\"object\"));\n\n            // properties\n            Map<String, JsonElement> props = new LinkedHashMap<>();\n            for (ToolParameterDescriptor p : concat(toolDescriptor.getRequiredParameters(), toolDescriptor.getOptionalParameters())) {\n                Map<String, JsonElement> prop = new LinkedHashMap<>();\n                prop.put(\"description\", JsonPrimitive(p.getDescription()));\n\n                ToolParameterType t = p.getType();\n                if (t == ToolParameterType.String.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                } else if (t == ToolParameterType.Integer.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"integer\"));\n                } else if (t instanceof ToolParameterType.Enum) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                    String[] entries = ((ToolParameterType.Enum) t).getEntries();\n                    List<JsonElement> enumVals = new ArrayList<>();\n                    for (String e : entries) enumVals.add(JsonPrimitive(e));\n                    prop.put(\"enum\", new JsonArray(enumVals));\n                } else {\n                    prop.put(\"type\", JsonPrimitive(\"string\")); // fallback for brevity\n                }\n\n                props.put(p.getName(), new JsonObject(prop));\n            }\n            root.put(\"properties\", new JsonObject(props));\n\n            // required array\n            List<JsonElement> required = new ArrayList<>();\n            for (ToolParameterDescriptor p : toolDescriptor.getRequiredParameters()) {\n                required.add(JsonPrimitive(p.getName()));\n            }\n            root.put(\"required\", new JsonArray(required));\n\n            return new JsonObject(root);\n        }\n\n        private static List<ToolParameterDescriptor> concat(List<ToolParameterDescriptor> a, List<ToolParameterDescriptor> b) {\n            List<ToolParameterDescriptor> res = new ArrayList<>(a.size() + b.size());\n            res.addAll(a);\n            res.addAll(b);\n            return res;\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-01.java -->\n\n## Using with a client\n\nTypically, you do not need to call a schemer directly. Koog clients accept a list of `ToolDescriptor` objects and apply the correct schemer internally when serializing requests for the provider.\n\nThe example below defines a simple tool and passes it to the OpenAI client. The client will use `OpenAICompatibleToolDescriptorSchemer` under the hood to build the JSON schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.prompt.executor.clients.openai.OpenAILLMClient\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.prompt.Prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    import kotlinx.coroutines.runBlocking\n    class MinimalSchemer : OpenAICompatibleToolDescriptorSchemaGenerator() {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    -->\n    ```kotlin\n    val client = OpenAILLMClient(apiKey = System.getenv(\"OPENAI_API_KEY\"), toolsConverter = MinimalSchemer())\n    \n    val getUserTool = ToolDescriptor(\n        name = \"get_user\",\n        description = \"Returns user profile by id\",\n        requiredParameters = listOf(\n            ToolParameterDescriptor(\n                name = \"id\",\n                description = \"User id\",\n                type = ToolParameterType.String\n            )\n        )\n    )\n    \n    val prompt = Prompt.build(id = \"p1\") { user(\"Hello\") }\n    val responses = runBlocking {\n        client.execute(\n            prompt = prompt,\n            model = OpenAIModels.Chat.GPT4o,\n            tools = listOf(getUserTool)\n        )\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Custom schemer extending the OpenAI-compatible one is Kotlin-only in the docs; for Java example we reuse MinimalSchemer from above.\n    OpenAILLMClient client = openAIClient(System.getenv(\"OPENAI_API_KEY\"), new OpenAIClientSettings(), null, null, new OpenAICompatibleToolDescriptorSchemaGenerator());\n    \n    ToolDescriptor getUserTool = new ToolDescriptor(\n        \"get_user\",\n        \"Returns user profile by id\",\n        Collections.singletonList(new ToolParameterDescriptor(\n            \"id\",\n            \"User id\",\n            ToolParameterType.String.INSTANCE\n        )),\n        Collections.emptyList()\n    );\n\n    Prompt prompt = Prompt.builder(\"p1\")\n        .user(\"Hello\")\n        .build();\n\n    List<Message.Response> responses = client.execute(prompt, OpenAIModels.Chat.GPT4o, java.util.List.of(getUserTool));\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-02.java -->\n\nIf you need direct access to the produced schema (for debugging or for a custom transport), you can instantiate the provider‑specific schemer and serialize the JSON yourself:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import kotlinx.serialization.json.Json\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    fun getUserTool(): ToolDescriptor {\n        return ToolDescriptor(\n            name = \"get_user\",\n            description = \"Returns user profile by id\",\n            requiredParameters = listOf(\n                ToolParameterDescriptor(\n                    name = \"id\",\n                    description = \"User id\",\n                    type = ToolParameterType.String\n                )\n            )\n        )\n    }\n    -->\n    \n    ```kotlin\n    val json = Json { prettyPrint = true }\n    val schema = OpenAICompatibleToolDescriptorSchemaGenerator().generate(getUserTool())\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-03.java -->\n","isInternal":false,"tokens":2287,"sizeBytes":11503},{"name":"SKILL.md","path":"integration-tests/src/jvmTest/resources/skills/arithmetic-evaluator/SKILL.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/integration-tests/src/jvmTest/resources/skills/arithmetic-evaluator/SKILL.md","title":"arithmetic-evaluator","category":"anthropic-skill","format":"markdown","content":"---\nname: arithmetic-evaluator\ndescription: Evaluate arithmetic expressions and return numeric results. Use when a user asks an arithmetic question, requests calculation, or provides a math expression involving numbers, parentheses, and arithmetic operators.\n---\n\nEvaluate the arithmetic expression the user provides.\n\n1. Extract the arithmetic expression from the user request.\n2. If the expression is missing or ambiguous, ask for a clear arithmetic expression.\n3. Evaluate it by passing the expression to `scripts/evaluate_arithmetic_exp.py`\n4. Return the computed result clearly and concisely.\n\nIf script execution fails, explain that clearly and ask the user to provide a valid arithmetic expression.","frontmatter":{"name":"arithmetic-evaluator","description":"Evaluate arithmetic expressions and return numeric results. Use when a user asks an arithmetic question, requests calculation, or provides a math expression involving numbers, parentheses, and arithmetic operators."},"isInternal":false,"tokens":129,"sizeBytes":705},{"name":"SKILL.md","path":"integration-tests/src/jvmTest/resources/skills/weather-retrieval/SKILL.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/integration-tests/src/jvmTest/resources/skills/weather-retrieval/SKILL.md","title":"weather-retrieval","category":"anthropic-skill","format":"markdown","content":"---\nname: weather-retrieval\ndescription: Retrieve current weather information for a specific location. Use when a user asks for weather, temperature, conditions, forecast summary, or climate details for a city, region, or place.\n---\n\nGet weather details for the location the user provides.\n\n1. Extract the location from the user request.\n2. If the location is missing or ambiguous, ask for a clearer location name.\n3. Fetch the weather data by passing the location to `scripts/retrieve_weather.py`\n4. Parse the JSON output from the script.\n5. Return a concise summary that includes:\n    - location name\n    - temperature\n    - weather condition\n    - any notable context (for example, wind, humidity, or precipitation)\n\nIf the script fails or returns invalid data, explain that clearly and suggest trying a more specific location.\n","frontmatter":{"name":"weather-retrieval","description":"Retrieve current weather information for a specific location. Use when a user asks for weather, temperature, conditions, forecast summary, or climate details for a city, region, or place."},"isInternal":false,"tokens":171,"sizeBytes":831},{"name":"Module.md","path":"skills/Module.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/skills/Module.md","title":"Skills Skill","category":"anthropic-skill","format":"markdown","content":"# Module skills\n\nA module that provides core utilities for defining skill metadata, discovering skill content, and composing prompts.\n\n\n","isInternal":false,"tokens":23,"sizeBytes":136},{"name":"README.md","path":"agents/agents-features/agents-features-opentelemetry/README.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/agents/agents-features/agents-features-opentelemetry/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# OpenTelemetry support\n\nThis guide provides comprehensive instructions on how to use OpenTelemetry with the Koog agentic framework for tracing and monitoring your AI agents.\n\n## Overview\n\nOpenTelemetry is an observability framework that provides tools for generating, collecting, and exporting telemetry data (metrics, logs, and traces) from your applications. The Koog OpenTelemetry feature allows you to instrument your AI agents to collect telemetry data, which can help you:\n\n- Monitor agent performance and behavior\n- Debug issues in complex agent workflows\n- Visualize the execution flow of your agents\n- Track LLM calls and tool usage\n- Count tokens (input, output, and total) for LLM calls to monitor usage and costs\n- Analyze agent behavior patterns\n\nKey OpenTelemetry concepts\n\n- **Spans**: Spans represent individual units of work or operations within a distributed trace. They indicate the beginning and end of a specific activity in an application, such as an agent execution, a function call, an LLM call, or a tool call.\n- **Exporters**: Exporters are components responsible for sending the collected telemetry data (spans, metrics, logs) to various backends or destinations.\n- **Collectors**: An OpenTelemetry Collector receives, processes, and exports telemetry data. It acts as an intermediary between your applications and your observability backend.\n\nThe OpenTelemetry feature in Koog automatically creates spans for various agent events, including:\n\n- Agent creation and invocation\n- Strategy execution\n- Node execution\n- LLM calls (inference)\n- Tool calls\n\n## Installation\n\nTo use OpenTelemetry with Koog, add the OpenTelemetry feature to your agent:\n\n```kotlin\nval agent = AIAgent(\n    executor = simpleOpenAIExecutor(apiKey),\n    llmModel = OpenAIModels.Chat.GPT4o,\n    systemPrompt = \"You are a helpful assistant.\",\n    installFeatures = {\n        install(OpenTelemetry) {\n            // Configuration options here\n        }\n    }\n)\n```\n\n## Configuration\n\n### Basic configuration\n\nHere is an example of installing the OpenTelemetry feature with a basic set of configuration items:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.feature.OpenTelemetry\nimport io.opentelemetry.exporter.logging.LoggingSpanExporter\n\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Java SDK exporter is bridged automatically on JVM\n}\n```\n\nThe example includes the following configuration methods:\n\n| Name               | Data type | Required | Description                                                                                     |\n|--------------------|-----------|----------|-------------------------------------------------------------------------------------------------|\n| `setServiceInfo`   | Unit      | No       | Sets the information about the service being instrumented, including service name and version.  |\n| `addSpanExporter`  | Unit      | No       | Registers an exporter wrapped in a [`batchSpanProcessor`][batchSpanProcessor] (OTel-recommended). |\n\nPlease see below the full list of available configuration properties:\n\n| Name                    | Data type       | Default value | Description                                                                                                              |\n|-------------------------|-----------------|---------------|--------------------------------------------------------------------------------------------------------------------------|\n| `serviceName`           | `String`        | `ai.koog`     | The name of the service being instrumented.                                                                              |\n| `serviceVersion`        | `String`        | `0.0.0`       | The version of the service being instrumented.                                                                           |\n| `isVerbose`             | `Boolean`       | `false`       | Whether to enable verbose logging for debugging OpenTelemetry configuration.                                             |\n| `isShutdownOnAgentClose`| `Boolean`       | `false`       | Whether the SDK is shut down when the owning agent closes.                                                               |\n| `tracer`                | `Tracer`        |               | Kotlin SDK [`Tracer`][Tracer] used for creating spans.                                                                  |\n\nConfiguration API:\n\n| Name                    | Arguments                                                | Description                                                                                                              |\n|-------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| `setServiceInfo`        | `serviceName: String, serviceVersion: String`            | Sets the service information including name and version.                                                                 |\n| `addSpanExporter`       | `exporter: SpanExporter` (Kotlin SDK or Java SDK on JVM) | Registers an exporter behind a `batchSpanProcessor`. On JVM the method is overloaded to accept Java-SDK exporters directly (bridged via the compat layer). |\n| `addSpanProcessor`      | `factory: TraceExportConfigDsl.() -> SpanProcessor`      | Registers a custom span processor (e.g., simple/composite/custom-batched) via the SDK's DSL.                             |\n| `addResourceAttributes` | `attributes: Map<String, Any>`                           | Adds resource attributes to provide additional context about the service. Supported value types: `String`, `Long`, `Double`, `Boolean`. |\n| `setVerbose`            | `verbose: Boolean`                                       | Enables or disables verbose logging for debugging OpenTelemetry configuration.                                           |\n| `setSdk`                | `openTelemetry: OpenTelemetry`                           | Injects a pre-built Kotlin OpenTelemetry SDK instance, bypassing internal configuration.                                 |\n| `setShutdownOnAgentClose`| `shutdownOnAgentClose: Boolean`                         | Toggles whether the SDK is shut down when the owning agent closes.                                                       |\n\nJVM-only members on [`OpenTelemetryConfig`][OpenTelemetryConfig] cover metrics (`addMetricExporter`, `addMetricFilter`, `meter`) — the Kotlin SDK 0.3.0 ships no metrics module, so metrics are sent through the Java SDK.\n\n### Advanced configuration\n\nFor more advanced configuration, you can customize resource attributes:\n\n```kotlin\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Add Logging exporter\n\n    // Add resource attributes (Map<String, Any>; supports String, Long, Double, Boolean)\n    addResourceAttributes(mapOf(\n        \"deployment.environment\" to \"production\",\n        \"custom.attribute\" to \"custom-value\"\n    ))\n}\n```\n\n### Custom span processors\n\n`addSpanExporter` always wraps in `batchSpanProcessor`. For full control over the processor — custom batching parameters, simple per-span export for tests, or a composite processor over several inner processors — use `addSpanProcessor` and the SDK's DSL-scoped factory functions:\n\n```kotlin\nimport io.opentelemetry.kotlin.tracing.export.batchSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.simpleSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.stdoutSpanExporter\n\ninstall(OpenTelemetry) {\n    // Stdout exporter wrapped in a simple (synchronous) processor — useful for local debugging.\n    addSpanProcessor { simpleSpanProcessor(stdoutSpanExporter()) }\n\n    // Custom batch parameters (default: 5s schedule delay, 512 max batch size).\n    addSpanProcessor { batchSpanProcessor(myExporter, scheduleDelayMs = 1000) }\n}\n```\n\n### Sdk configuration\n\nIf you already have an initialized [`OpenTelemetry`][OpenTelemetry] SDK instance — for example, configured elsewhere in your application or provided by a framework — you can pass it directly using `setSdk`. When using `setSdk` any other configuration methods like `addSpanExporter`, `addSpanProcessor`, `addResourceAttributes` are ignored, since the SDK is assumed to be fully configured.\n\n```kotlin\nimport io.opentelemetry.kotlin.OpenTelemetry\nimport io.opentelemetry.kotlin.createOpenTelemetry\n\nval sdk: OpenTelemetry = createOpenTelemetry { /* … */ } // Your preconfigured SDK\n\ninstall(OpenTelemetry) {\n    setSdk(sdk) // Use your existing OpenTelemetry instance\n}\n```\n\n#### Resource attributes\n\nResource attributes represent additional information about a process producing telemetry. This information can be\nincluded in an OpenTelemetry configuration in Koog using the `addResourceAttributes()` method that takes a\n`Map<String, Any>`. Supported value types are `String`, `Long`, `Double`, and `Boolean`.\n\nThe following default resource attributes are automatically added:\n\n- `service.name`: The name of the service being instrumented. Set to the value of `serviceName`.\n- `service.version`: The version of the service being instrumented. Set to the value of `serviceVersion`.\n- `service.instance.time`: The timestamp when the service instance was created.\n- `os.type`: The operating system type.\n- `os.version`: The operating system version.\n- `os.arch`: The operating system architecture.\n\nThe OpenTelemetry feature automatically adds various attributes to spans following the [OpenTelemetry Semantic Convention for GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/):\n\n### Common Attributes\n\n- `gen_ai.operation.name`: the type of operation being performed (e.g., \"create_agent\", \"invoke_agent\", \"chat\", \"execute_tool\")\n- `gen_ai.system`: the LLM provider being used\n- `gen_ai.agent.id`: the ID of the agent\n- `gen_ai.agent.name`: the name of the agent (when available)\n- `gen_ai.conversation.id`: the ID of the conversation/run\n- `gen_ai.request.model`: the LLM model being used\n- `gen_ai.request.temperature`: the temperature parameter for the LLM request\n- `error.type`: the type of error that occurred (when applicable)\n\n### Custom Attributes\n\nSome custom attributes specific to Koog are also added:\n\n- `koog.agent.strategy.name`: the name of the agent strategy\n- `koog.node.id`: the name of the node being executed\n\n### Span Types\n\nThe OpenTelemetry feature creates different types of spans for various operations. Each span type has a distinct purpose, parent–child relationship, and set of attributes and events.\n\n1. **Create Agent Span**\n    - Purpose: Long‑lived span representing the lifecycle of an agent instance (created once per agent ID).\n    - Emitted: When an agent is first started. Closed when the agent is closed.\n    - Parent: none. Children: **Invoke Agent Span** (one per run).\n    - Useful for: Grouping all agent runs and capturing static agent configuration.\n    - Key attributes:\n        - `gen_ai.operation.name` = `create_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.request.model`\n\n2. **Invoke Agent Span**\n    - Purpose: One concrete execution (run) of an agent.\n    - Emitted: At the start of a run. Closed on successful finish or error.\n    - Parent: **Create Agent Span**. Children: **Strategy Span** instances, and, indirectly, node, tool, and LLM spans within strategies.\n    - Useful for: Measuring run‑level timing, status, and grouping of all strategy, node, tool, and LLM activity for a run.\n    - Key attributes:\n        - `gen_ai.operation.name` = `invoke_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.conversation.id`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.response.finish_reasons` (derived from the final response in the session prompt: `Stop` for Assistant/Reasoning, `ToolCalls` for Tool.Call)\n\n3. **Strategy Span**\n    - Purpose: Execution of a strategy within an agent run.\n    - Emitted: At the start of strategy execution. Closed when the strategy completes or errors.\n    - Parent: **Invoke Agent Span**. Children: **Node Execute Span** instances.\n    - Useful for: Tracking strategy-level execution, grouping all node executions within a strategy, and measuring strategy performance.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.strategy.name`\n        - `koog.event.id`\n    - Note: This is a custom span type specific to Koog, not defined in the OpenTelemetry Semantic Conventions.\n\n4. **Node Execute Span**\n    - Purpose: Execution of a single node in the agent strategy.\n    - Emitted: Immediately before a node runs. Closed after the node completes or errors.\n    - Parent: **Strategy Span**. Children: **Inference Spans** and **Execute Tool Span** instances created by the node.\n    - Useful for: Understanding strategy flow and attributing LLM or tool work to a specific node.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.node.id`\n\n5. **Inference Span**\n    - Purpose: A single LLM call (prompt execution).\n    - Emitted: Before the LLM is invoked. Closed after responses are received.\n    - Parent: **Node Execute Span**.\n    - Events added:\n        - system, user, and assistant messages from the prompt\n        - tool choice and tool result messages\n        - moderation response (when present)\n    - Useful for: Tracking model usage, prompt parameters, messages, token usage, and finish reasons.\n    - Key attributes:\n        - `gen_ai.operation.name` = `chat`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.conversation.id`\n        - `gen_ai.request.model`\n        - `gen_ai.request.temperature`\n        - `gen_ai.request.max_tokens` (when available)\n        - `gen_ai.usage.input_tokens` (when available)\n        - `gen_ai.usage.output_tokens` (when available)\n        - `gen_ai.usage.total_tokens` (when available)\n        - `gen_ai.response.finish_reasons` (Stop, ToolCalls, etc.)\n        - `error.type` (on LLM call failure)\n        - `gen_ai.response.metadata` (when `ResponseMetaInfo.metadata` is present - serialized JSON containing free-form provider or application metadata)\n\n6. **Execute Tool Span**\n    - Purpose: Execution of a tool or function call triggered by the agent or LLM.\n    - Emitted: When a tool is called. Closed after the tool returns a result or fails with an error during validation or execution.\n    - Parent: **Node Execute Span**.\n    - Useful for: Auditing tool usage, inputs and outputs, and failure causes.\n    - Key attributes:\n        - `gen_ai.tool.name`\n        - `gen_ai.tool.description`\n        - `gen_ai.tool.arguments` (when available)\n        - `gen_ai.tool.call_id` (when available)\n        - `gen_ai.tool.output` (when available)\n        - `error.type` (on failure or validation error)\n\n## Exporters\n\nExporters send collected telemetry data to an OpenTelemetry Collector or other types of destinations or backend implementations.\n\n### OTLP Exporter example\n\nThe OTLP (OpenTelemetry Protocol) exporter sends telemetry data to an OpenTelemetry Collector. This is useful for integrating with systems like Jaeger, Zipkin, or Prometheus.\n\nTo add an OpenTelemetry Exporter, use the `addSpanExporter` function to a custom exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    // The default endpoint is http://localhost:4317\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n### Logging Exporter example\n\nA logging exporter that outputs trace information to the console is included by default. This type of export is useful\nfor development and debugging purposes.\n\n```kotlin\ninstall(OpenTelemetry) {\n    // The logging exporter is added by default\n    addSpanExporter(LoggingSpanExporter.create())\n}\n```\n\n## Integration with Jaeger\n\nJaeger is a popular distributed tracing system that works with OpenTelemetry. To use Jaeger with Koog:\n\n1. Start a Jaeger container:\n```yaml\n# docker-compose.yaml\nservices:\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:1.39\n    container_name: jaeger-all-in-one\n    environment:\n      - COLLECTOR_OTLP_ENABLED=true\n    ports:\n      - \"4317:4317\"  # OTLP gRPC port\n      - \"16686:16686\"  # Jaeger UI port\n```\n\n2. Configure your agent to use the OTLP exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n3. Access the Jaeger UI at `http://localhost:16686` to view your traces.\n\n## Examples\n\nThis section includes two examples of implementing OpenTelemetry support in your agent workflow. The basic example\nincludes quick OpenTelemetry setup with minimal configuration, while the advanced example shows how you can create a\nmore elaborate and customized OpenTelemetry configuration.\n\n### Basic example\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleGoogleAIExecutor(apiKey),\n        llmModel = GoogleModels.Gemini2_0Flash,\n        systemPrompt = \"You are a code assistant. Provide concise code examples.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                setServiceInfo(\"my-otlp-agent\", \"1.0.0\")\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Create python function that prints hello world\")\n    println(result)\n    \n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\n### Advanced example with custom exporters and attributes\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                // Configure service info\n                setServiceInfo(\"advanced-agent\", \"2.0.0\")\n\n                // Add resource attributes (Map<String, Any>)\n                addResourceAttributes(mapOf(\n                    \"deployment.environment\" to \"production\",\n                    \"custom.attribute\" to \"custom-value\"\n                ))\n\n                // Add exporters (Java SDK exporter bridged via the JVM addSpanExporter)\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Tell me about OpenTelemetry\")\n    println(result)\n\n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\nFor full sampling control or other SDK-level options, build your own [`OpenTelemetry`][OpenTelemetry] instance with [`createOpenTelemetry { … }`][createOpenTelemetry] and inject it via `setSdk`.\n\n### Datadog\n\n[Datadog](https://www.datadoghq.com/) supports direct OTLP intake for LLM Observability. To send traces to Datadog:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.integration.datadog.addDatadogExporter\n\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n}\n```\n\nThe exporter reads the `DD_API_KEY` environment variable by default. You can also pass it explicitly:\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter(\n        datadogApiKey = \"your-api-key\",\n        url = \"datadoghq.eu\",                    // defaults to datadoghq.com\n        resourceAttributes = mapOf(\n            \"env\" to \"production\",\n            \"service.name\" to \"my-agent\",\n        ),\n    )\n}\n```\n\nTo send traces to Datadog and another backend at the same time, register both via `addSpanExporter` (Datadog's helper handles its own registration internally; the second exporter goes through the public API):\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n    addSpanExporter(\n        OtlpHttpSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4318/v1/traces\")\n            .build()\n    )\n}\n```\n\n## Troubleshooting\n\n### Common Issues\n\n1. **No traces appearing in Jaeger UI**\n    - Ensure the Jaeger container is running and the OTLP port (4317) is accessible\n    - Check that the OTLP exporter is configured with the correct endpoint\n    - Make sure to wait a few seconds after agent execution for traces to be exported\n\n2. **Missing spans or incomplete traces**\n    - Verify that the agent execution completes successfully\n    - Ensure that you're not closing the application too quickly after agent execution\n    - Add a delay (e.g., `TimeUnit.SECONDS.sleep(10)`) after agent execution to allow time for spans to be exported\n\n3. **Excessive number of spans**\n    - Build a custom Kotlin OTel SDK with a sampler (e.g., `parentBased(root = traceIdRatioBased(0.1))`) and inject it via `setSdk(...)`.\n","isInternal":false,"tokens":4432,"sizeBytes":20846},{"name":"README.md","path":"agents/agents-features/agents-features-snapshot/README.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/agents/agents-features/agents-features-snapshot/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# AgentCheckpoint\n\nAgentCheckpoint is a feature that provides checkpoint functionality for AI agents in the Koog Agents framework. It allows saving and restoring the state of an agent at specific points during execution, enabling powerful capabilities such as:\n\n- Resuming agent execution from a specific point\n- Rolling back to previous states\n- Persisting agent state across sessions\n\n## Core Concepts\n\n### Checkpoints\n\nA checkpoint captures the complete state of an agent at a specific point in its execution, including:\n\n- Message history (all interactions between user, system, assistant, and tools)\n- Current node being executed\n- Input data for the current node\n- Timestamp of creation\n\nCheckpoints are identified by unique IDs and are associated with a specific agent.\n\n### Storage Providers\n\nAgentCheckpoint uses storage providers to save and retrieve checkpoints. The framework includes several built-in providers:\n\n- `InMemoryAgentCheckpointStorageProvider`: Stores checkpoints in memory (lost when the application restarts)\n- `FileAgentCheckpointStorageProvider`: Persists checkpoints to the file system\n- `NoAgentCheckpointStorageProvider`: A no-op implementation (default) that doesn't store checkpoints\n\nYou can also implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface.\n\n### Continuous Persistence\n\nThe feature can be configured to automatically create checkpoints after each node execution, ensuring that the agent's state is continuously persisted and can be recovered at any point.\n\n## Installation\n\nTo use the AgentCheckpoint feature, you need to add it to your agent's configuration:\n\n```kotlin\nval agent = aiAgent {\n    // ... other configuration\n    \n    features {\n        install(AgentCheckpoint) {\n            // Configure the feature\n            snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n            continuouslyPersistent() // Optional: create checkpoints after each node\n        }\n    }\n}\n```\n\n## Configuration Options\n\nThe AgentCheckpoint feature has two main configuration options:\n\n### Storage Provider\n\nSet the storage provider that will be used to save and retrieve checkpoints:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n}\n```\n\nAvailable built-in providers:\n- `InMemoryAgentCheckpointStorageProvider`: In-memory storage (non-persistent)\n- `FileAgentCheckpointStorageProvider`: File-based storage\n- `NoAgentCheckpointStorageProvider`: No-op provider (default)\n\n### Continuous Persistence\n\nEnable automatic checkpoint creation after each node execution:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    continuouslyPersistent()\n}\n```\n\nWhen enabled, the agent will automatically create a checkpoint after each node is executed, allowing for fine-grained recovery.\n\n## Basic Usage\n\n### Creating a Checkpoint\n\nCreate a checkpoint at a specific point in your agent's execution:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Create a checkpoint with the current state\n    val checkpoint = context.checkpoint().createCheckpoint(\n        agentId = context.id,\n        agentContext = context,\n        nodeId = \"current-node-id\",\n        lastInput = inputData\n    )\n    \n    // The checkpoint ID can be stored for later use\n    val checkpointId = checkpoint.checkpointId\n}\n```\n\n### Restoring from a Checkpoint\n\nRestore an agent's state from a specific checkpoint:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase, checkpointId: String) {\n    // Roll back to a specific checkpoint\n    context.checkpoint().rollbackToCheckpoint(checkpointId, context)\n    \n    // Or roll back to the latest checkpoint\n    context.checkpoint().rollbackToLatestCheckpoint(context)\n}\n```\n\n### Using Extension Functions\n\nThe feature provides convenient extension functions for working with checkpoints:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Access the checkpoint feature\n    val checkpointFeature = context.checkpoint()\n    \n    // Or perform an action with the checkpoint feature\n    context.withCheckpoints(context) { ctx ->\n        // 'this' is the checkpoint feature\n        createCheckpoint(ctx.id, ctx, \"node-id\", inputData)\n    }\n}\n```\n\n## Advanced Usage\n\n### Custom Storage Providers\n\nYou can implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface:\n\n```kotlin\nclass MyCustomStorageProvider : AgentCheckpointStorageProvider {\n    override suspend fun getCheckpoints(agentId: String): List<AgentCheckpointData> {\n        // Implementation\n    }\n    \n    override suspend fun saveCheckpoint(agentId: String, agentCheckpointData: AgentCheckpointData) {\n        // Implementation\n    }\n    \n    override suspend fun getLatestCheckpoint(agentId: String): AgentCheckpointData? {\n        // Implementation\n    }\n}\n```\n\nThen use your custom provider in the feature configuration:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(MyCustomStorageProvider())\n}\n```\n\n### Setting Execution Points\n\nFor advanced control, you can directly set the execution point of an agent:\n\n```kotlin\nfun example(context: AIAgentContextBase) {\n    context.checkpoint().setExecutionPoint(\n        agentContext = context,\n        nodeId = \"target-node-id\",\n        messageHistory = customMessageHistory,\n        input = customInput\n    )\n}\n```\n\nThis allows for more fine-grained control over the agent's state beyond just restoring from checkpoints.\n\n## Requirements\n\nThe AgentCheckpoint feature requires that all nodes in your agent's strategy have unique names. This is enforced when the feature is installed:\n\n```kotlin\nrequire(ctx.strategy.metadata.uniqueNames) { \n    \"Checkpoint feature requires unique node names in the strategy metadata\" \n}\n```\n\nMake sure to set `uniqueNames = true` in your strategy metadata when using this feature.\n","isInternal":false,"tokens":1146,"sizeBytes":5850},{"name":"basic-agents.md","path":"docs/docs/agents/basic-agents.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/basic-agents.md","title":"Subagent: basic-agents","category":"subagent-persona","format":"markdown","content":"# Basic agents\n\nA basic agent uses a predefined strategy with a simple execution flow that works for most common use cases.\nIt accepts a string input (a question, request, or task description) and sends this input to the configured LLM.\nThe LLM may decide to call provided tools.\nThe agent will execute the tools and send the results back to the LLM.\nThis repeats until the LLM does not request any more tool calls and returns a string response.\nThe agent then outputs this response.\n\nIn [Graph-based agents](graph-based-agents.md),\nyou can see how to re-create the predefined strategy graph used by basic agents.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\n## Create a minimal agent\n\nTo create the most basic agent, instantiate [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html)\nand provide a [prompt executor](../prompts/prompt-executors.md) with a [language model](../model-capabilities.md#creating-a-model-llmodel-configuration):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n\n    This agent will expect a string as input and return a string as output.\n    To run the agent, use the `run()` function with some user input:\n\n    ```kotlin\n    fun main() = runBlocking {\n        val result = agent.run(\"Hello! How can you help me?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-basic-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n\n    This agent expects a string as input and returns a string as output.\n    To run the agent, use the `run()` method with some user input:\n\n    ```java\n    String result = agent.run(\"Hello! How can you help me?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleBasicJava01.java -->\n\nThe agent will return a generic answer, such as:\n\n```text\nI can assist with a wide range of topics and tasks. Here are some examples:\n\n1. **Answering questions**: I can provide information on various subjects, from science and history to entertainment and culture.\n2. **Generating text**: I can help with writing tasks, such as suggesting alternative phrases, providing definitions, or even creating entire articles or stories.\n3. **Translation**: I can translate text from one language to another, including popular languages such as Spanish, French, German, Chinese, and many more.\n4. **Conversation**: I can engage in natural-sounding conversations, using context and understanding to respond to questions and statements.\n5. **Brainstorming**: I can help generate ideas for creative projects, such as writing stories, composing music, or coming up with business ideas.\n6. **Learning**: I can help with language learning, explaining grammar rules, vocabulary, and pronunciation.\n7. **Calculations**: I can perform mathematical calculations, including basic arithmetic, algebra, and more advanced math concepts.\n\nWhat's on your mind? Do you have a specific question, topic, or task you'd like to tackle?\n```\n<!--- KNIT example-basic-01.txt -->\n\n## Add a system prompt\n\nProvide a [system message](../prompts/prompt-creation/index.md#system-message) to define the agent's role\nas well as the purpose, context, and instructions related to the task.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n    <!--- KNIT example-basic-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava02.java -->\n\nThe instructions in the system prompt will guide the agent's response:\n\n```text\nI'm here to help you navigate the wild world of internet memes!\n\nWhat's on your mind? Are you trying to understand a specific meme, need help finding a popular joke, or perhaps want some recommendations for trending memes? Let me know, and I'll do my best to provide you with some LOLs!\n```\n<!--- KNIT example-basic-02.txt -->\n\n## Configure LLM output\n\nYou can provide some [LLM parameters](../llm-parameters.md#llm-parameter-reference) directly to the agent constructor \n(Kotlin) or via the builder methods (Java) to customize the behavior of the LLM.\nFor example, use the `temperature` parameter to adjust the randomness of the generated responses:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7\n    )\n    ```\n    <!--- KNIT example-basic-java-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava03 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava03.java -->\n\nHere are some response examples with different temperature values:\n\n=== \"0.4\"\n    \n    ```text\n    I'm here to help you navigate the wild world of internet memes! Whether you're looking for explanations, examples, or just want to share a meme with someone, I'm your go-to expert. What's on your mind? Got a specific meme in mind that's got you curious? Or maybe you need some meme-related advice? Fire away!\n    ```\n    <!--- KNIT example-basic-03.txt -->\n\n=== \"0.7\"\n\n    ```text\n    I'm here to help you navigate the wild world of internet memes!\n    \n    What's on your mind? Need help understanding a specific meme, finding a popular joke or trend, or maybe even creating your own meme? Let's get this meme party started!\n    ```\n    <!--- KNIT example-basic-04.txt -->\n\n=== \"1.0\"\n\n    ```text\n    I'd be happy to help you navigate the wild world of internet memes!\n    \n    Whether you're looking for explanations of classic memes, suggestions for new ones to try out, or just want to discuss your favorite meme culture trends, I'm here to assist. What's on your mind?\n    \n    Do you have a specific question about memes (e.g., \"What does this meme mean?\"), or are you looking for some meme-related recommendations (e.g., \"Can you recommend a funny meme to share with friends?\"). Let me know how I can help!\n    ```\n    <!--- KNIT example-basic-05.txt -->\n\n## Add tools\n\nAgents can use [tools](../tools/index.md) to perform specific tasks.\n\nFirst, create a tool by annotating a function (Kotlin) or method (Java) with the [`@Tool`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools.annotations/-tool/index.html) annotation:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    ```\n\n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        }\n    )\n    ```\n    <!--- KNIT example-basic-03.kt -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n    If the agent decides to ask the user a question,\n    it can call this tool that writes to `stdout` via `println()` and reads from `stdin` via `readln()`.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava04 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n    ```\n    \n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```java\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava04.java -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n\nHere is an example interaction with the agent:\n\n```text\nAgent: Which meme would you like me to explain? Please choose from: Grumpy Cat, Success Kid, or Doge.\n\nUser: Explain Doge\n\nAgent:\n\n**Doge**\n\nDoge is a popular internet meme that originated on the imageboard website 4chan in 2013. The meme features a picture of a Shiba Inu dog with a caption written in a playful, exaggerated style.\n\nThe typical format of a Doge meme includes:\n\n* A photo of a Shiba Inu dog\n* A caption written in all capital letters, using a intentionally simplistic and childlike tone\n* Exaggerated or made-up words or phrases, often used to convey a humorous or nonsensical idea\n\nExamples of Doge memes might include:\n\n* \"Such wow. Such happy.\"\n* \"I had fun today!\"\n* \"Wow, I am good at napping.\"\n\nThe meme is known for its lighthearted and playful tone, and is often used to express excitement, happiness, or silliness. The meme has since become a cultural phenomenon, with countless variations and parodies emerging online.\n```\n<!--- KNIT example-basic-06.txt -->\n\n## Adjust agent iterations\n\nTo avoid infinite loops, Koog allows any agent to take a limited number of steps (50 by default).\nUse the `maxIterations` parameter to either increase this limit if you expect the agent to require more steps\n(such as tool calls and LLM requests) or decrease it for agents that require only a few steps.\nFor example, a simple agent described here is not likely to require more than 10 steps:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-basic-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava05 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava05.java -->\n\n!!! tip\n\n    Instead of passing the model, temperature, max iterations, and other parameters directly to the Kotlin constructor \n    or Java builder, you can also define and pass them as a separate configuration object.\n    For more information, see [Agent configuration](index.md#agent-configuration).\n\n## Handle events during agent runtime\n\nTo assist with testing and debugging, as well as making hooks for chained agent interactions,\nKoog provides the [EventHandler](https://api.koog.ai/agents/agents-features/agents-features-event-handler/ai.koog.agents.features.eventHandler.feature/-event-handler/index.html) feature.\n\n=== \"Kotlin\"\n\n    Call the `handleEvents()` function inside the agent constructor lambda to install the feature and register event handlers:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.features.eventHandler.feature.handleEvents\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    ){\n        handleEvents {\n            // Handle tool calls\n            onToolCallStarting { eventContext ->\n                println(\"Tool called: ${eventContext.toolName} with args ${eventContext.toolArgs}\")\n            }\n        }\n    }\n    ```\n    <!--- KNIT example-basic-05.kt -->\n\n=== \"Java\"\n    Use the `.install()` method on the agent builder to register event handlers with `EventHandler.Feature`:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.agents.features.eventHandler.feature.EventHandler;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava06 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .install(EventHandler.Feature, config -> {\n            config.onToolCallStarting(eventContext -> {\n                System.out.println(\"Tool called: \" + eventContext.getToolName() +\n                    \" with args \" + eventContext.getToolArgs());\n            });\n        })\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava06.java -->\n\nThe agent will now output something similar to the following when it calls the `askUser` tool:\n\n```text\nTool called: askUser with args {\"question\":\"Which meme would you like me to explain?\"}\n```\n<!--- KNIT example-basic-07.txt -->\n\nFor more information about Koog agent features, see [Features](../features/index.md).\n\n## Next steps\n\n- Learn more about building [graph-based agents](graph-based-agents.md) and [functional agents](functional-agents.md)\n","isInternal":false,"tokens":5226,"sizeBytes":22518},{"name":"functional-agents.md","path":"docs/docs/agents/functional-agents.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/functional-agents.md","title":"Subagent: functional-agents","category":"subagent-persona","format":"markdown","content":"# Functional agents\n\nWith functional agents, you implement the logic as a function that handles user input,\ninteracts with LLMs, calls tools if necessary, and produces the final output.\nCompared to [graph-based agents](graph-based-agents.md),\nthis usually means faster prototyping with the following downsides:\n\n- Not easy to visualize\n- No state persistence\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to implement a functional strategy to quickly prototype some custom logic for your agent.\n\n## Create a minimal functional agent\n\nTo create a minimal functional agent, use the same [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html) interface as for a [basic agent](basic-agents.md) and pass an \ninstance of [`AIAgentFunctionalStrategy`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent-functional-strategy/index.html) to it.\nYou can define a functional strategy that expects an input and returns an output, makes one LLM call, then returns the \ncontent of the assistant message from the response.\n\nIn Kotlin, the most convenient way is to use the `functionalStrategy {...}` DSL method. In Java, you can use the `functionalStrategy` method on the `AIAgent` builder.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val strategy = functionalStrategy<String, String> { input ->\n        val response = requestLLM(input)\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgent.run(\"What is 12 × 9?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy(\"mathStrategy\", (AIAgentFunctionalContext context, String input) -> {\n            Message.Response response = context.requestLLM(input);\n            if (response instanceof Message.Assistant) {\n                return ((Message.Assistant) response).getContent();\n            }\n            return \"\";\n        })\n        .build();\n\n    String result = mathAgent.run(\"What is 12 × 9?\");\n    System.out.println(result);\n    ```\n   <!--- KNIT example-functional-agent-java-01.java -->\n\nThe agent can produce the following output:\n\n```text\nThe answer to 12 × 9 is 108.\n```\n<!--- KNIT example-functional-agent-01.txt -->\n\n## Make sequential LLM calls\n\nYou can extend the previous strategy to make multiple sequential LLM calls:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    -->\n    ```kotlin\n    fun Message.Assistant.text(): String =\n        parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // The first LLM call produces an initial draft based on the user input\n        val draft = requestLLM(\"Draft: $input\").text()\n        // The second LLM call improves the initial draft\n        val improved = requestLLM(\"Improve and clarify.\").text()\n        // The final LLM call formats the improved text and returns the result\n        requestLLM(\"Format the result as bold.\").text()\n    }\n    ```\n    <!--- KNIT example-functional-agent-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .systemPrompt(\"You are a precise math assistant.\")\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy((AIAgentFunctionalContext context, String input) -> {\n            // The first LLM call produces an initial draft based on the user input\n            Message.Response draftResponse = context.requestLLM(\"Draft: \" + input);\n            String draft = \"\";\n            if (draftResponse instanceof Message.Assistant) {\n                draft = ((Message.Assistant) draftResponse).getContent();\n            }\n\n            // The second LLM call improves the initial draft\n            Message.Response improvedResponse = context.requestLLM(\"Improve and clarify.\");\n            String improved = \"\";\n            if (improvedResponse instanceof Message.Assistant) {\n                improved = ((Message.Assistant) improvedResponse).getContent();\n            }\n\n            // The final LLM call formats the improved text and returns the result\n            Message.Response finalResponse = context.requestLLM(\"Format the result as bold.\");\n            if (finalResponse instanceof Message.Assistant) {\n                return ((Message.Assistant) finalResponse).getContent();\n            }\n            return \"\";\n        })\n        .build();\n    ```\n    <!--- KNIT example-functional-agent-java-02.java -->\n\nThe agent can produce the following output:\n\n```text\nTo calculate the product of 12 and 9, we multiply these two numbers together.\n\n12 × 9 = **108**\n```\n<!--- KNIT example-functional-agent-02.txt -->\n\n## Add tools\n\nIn many cases, a functional agent needs to complete specific tasks,\nsuch as reading and writing data, calling APIs, or performing other deterministic operations.\nIn Koog, you expose such capabilities as [tools](../tools/index.md) and let the LLM decide when to call them.\n\nHere is what you need to do:\n\n1. Create an [annotation-based tool](../tools/annotation-based-tools.md).\n2. Add it to a tool registry and pass the registry to the agent.\n3. Make sure the agent strategy can identify tool calls in LLM responses, execute the requested tools,\n   send their results back to the LLM, and repeat the process until there are no tool calls remaining.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n\n    val toolRegistry = ToolRegistry {\n        tool(MathTools()::multiply)\n    }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // Send the user input to the LLM\n        var response = requestLLM(input)\n\n        // Only loop while the LLM requests tools\n        var toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        while (toolCalls.isNotEmpty()) {\n            // Execute the tools and return the results\n            val results = executeTools(toolCalls)\n            // Send the tool results back to the LLM. The LLM may call more tools or return a final output\n            response = sendToolResults(results)\n            toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        }\n\n        // When no tool calls remain, extract and return the assistant message content from the response\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgentWithTools = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        toolRegistry = toolRegistry,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n\n    public static void main(String[] args) {\n        MathTools mathTools = new MathTools();\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(mathTools)\n            .build();\n\n        AIAgent<String, String> mathAgentWithTools = AIAgent.builder()\n            .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n            .llmModel(OllamaModels.Meta.LLAMA_3_2)\n            .toolRegistry(toolRegistry)\n            .functionalStrategy(\"mathWithTools\", (AIAgentFunctionalContext context, String input) -> {\n                // Send the user input to the LLM\n                List<Message.Response> responses = context.requestLLMMultiple(input);\n\n                // Only loop while the LLM requests tools\n                while (context.containsToolCalls(responses)) {\n                    // Extract tool calls from the response\n                    List<MessagePart.Tool.Call> pendingCalls = context.extractToolCalls(responses);\n                    // Execute the tools and return the results\n                    List<ReceivedToolResult> results = context.executeMultipleTools(pendingCalls, false);\n                    // Send the tool results back to the LLM\n                    responses = context.sendMultipleToolResults(results);\n                }\n\n                // Extract and return the assistant message content from the response\n                Message.Response finalResponse = responses.get(0);\n                if (finalResponse instanceof Message.Assistant) {\n                    return ((Message.Assistant) finalResponse).getContent();\n                }\n                return \"\";\n            })\n            .build();\n\n        String result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\");\n        System.out.println(result);\n    }\n    ```\n   <!--- KNIT example-functional-agent-java-03.java -->\n\nThe agent can produce the following output:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nThe result of multiplying 3 by 4 is 12. Multiplying 12 by 5 gives us a final answer of 60.\n```\n<!--- KNIT example-functional-agent-03.txt -->\n\n## Next steps\n\n- Learn how to create [graph-based agents](graph-based-agents.md)\n","isInternal":false,"tokens":2662,"sizeBytes":11881},{"name":"graph-based-agents.md","path":"docs/docs/agents/graph-based-agents.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/graph-based-agents.md","title":"Subagent: graph-based-agents","category":"subagent-persona","format":"markdown","content":"# Graph-based agents\n\nWith graph-based agents, you model the behavior as an explicit state machine:\nnodes of a graph strategy represent actions (LLM calls, tool execution)\nand edges represent data flow between nodes.\n\nThe main advantages of graph-based agents are:\n\n- Easy to visualize\n- State persistence\n- Composable architecture\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to re-create the strategy graph used by [basic agents](basic-agents.md).\nIt sends a request to an LLM and then either outputs the response (if the LLM responded with an assistant message)\nor executes a tool (if the LLM requested a tool call).\nIn case of a tool call, the agent sends the tool result to the LLM\nand then either outputs the response or executes a tool.\n\nHere is an illustration of the strategy graph:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph TB\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    subgraph nodeExecuteTool\n        executeTool(Execute tool call)\n    end\n    \n    subgraph nodeSendToolResult\n        sendToolResult(Request LLM)\n    end\n    \n    Input --String--> llmRequest\n    llmRequest --Message.Assistant--> onToolCalls{{onToolCalls}}\n    llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}}\n    onTextMessage --String--> Output\n    onToolCalls --ToolCalls--> executeTool --ReceivedToolResults--> sendToolResult\n    sendToolResult --Message.Assistant--> onToolCalls\n    sendToolResult --Message.Assistant--> onTextMessage\n```\n<!--- KNIT example-graph-agents-01.txt -->\n\n\n## Build a strategy graph\n\nIn Koog, you implement a strategy using [`AIAgentGraphStrategyBuilder`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/-a-i-agent-graph-strategy-builder/index.html).\nJust like every node has an input and output type,\nthe strategy as a whole also defines some input and output type.\nThis example assumes that the input and output types are strings,\nwhich means the agent implementing this strategy will expect a string and return a string.\n\nTo create a strategy, use the [`strategy()`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/strategy.html) function with two generics as the input and output types,\nprovide a unique identifier for the strategy, and define the nodes and edges.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n        \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    ```\n    <!--- KNIT example-graph-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    ```\n    <!--- KNIT exampleGraphAgentsJava01.java -->\n\nThis example uses only [predefined nodes](../nodes-and-components.md),\nbut you can also create [custom nodes](../custom-nodes.md).\n\nEvery strategy graph must have a path from `nodeStart` to `nodeFinish` connected by [edges](../custom-strategy-graphs.md#edges).\nEdges can have conditions to determine when to follow a particular edge.\nEdges can also transform the output of the previous node before passing it to the next one.\nThis is necessary to connect nodes that have non-matching output and input types.\n\nIn the previous example, `onToolCalls { true }` means that the edge will follow\nonly if the previous node returned an assistant message containing at least one tool call (`MessagePart.Tool.Call`).\n\nWhen using `onTextMessage { true }`, the edge will follow\nonly if the previous node returned an assistant message containing text parts (`MessagePart.Text`).\nThis function also extracts and joins the text content of those parts,\neffectively transforming `Message.Assistant` to `String`, because `nodeFinish` expects a string.\n\n!!! tip\n\n    Instead of `onTextMessage { true }`, you can do the following:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onMessageParts(MessagePart.Text::class) transformed { it.joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-02.kt -->\n\n    Or:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onCondition { it is Message.Assistant } transformed { (it as Message.Assistant).parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-03.kt -->\n\n## Create and run the agent\n\nLet's create an agent instance with this strategy and run it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    \n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n\n    var promptExecutor = PromptExecutor.builder()\n        .ollama(\"http://localhost:11434\")\n        .build();\n\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .build();\n\n        String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n        System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava02.java -->\n\nWhen you run this agent, it will respond with something like this:\n\n```text\nTo calculate this, I'll follow the order of operations:\n\n1. Multiply 3 by 4: 3 * 4 = 12\n2. Multiply the result by 5: 12 * 5 = 60\n3. Add 10: 60 + 10 = 70\n4. Add 123: 70 + 123 = 193\n\nThe final answer is 193.\n```\n<!--- KNIT example-graph-agents-02.txt -->\n\nHowever, since this agent doesn't have any tools, the LLM never returns a tool call \nand simply generates the whole answer.\nThis is what effectively happens:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph LR\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    Input --String--> llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}} --String--> Output\n```\n<!--- KNIT example-graph-agents-03.txt -->\n\nEven though it is correct in this case, the answer will depend on the arithmetic abilities of the underlying LLM.\nTo make sure the calculations are correct, we should provide the agent with math tools.\nThen the LLM will be able to decide to call tools that perform the calculations deterministically.\n\n## Add tools\n\nDefine [tools](../tools/index.md) for performing math operations and add them to a [ToolRegistry](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    ```\n    <!--- KNIT example-graph-agents-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOllamaAIExecutor;\n    class exampleGraphAgentsJava03 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    @LLMDescription(\"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        public int add(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n            return a + b;\n        }\n\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n    public static void main(String[] args) {\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(new MathTools())\n            .build();\n    }\n    ```\n    <!--- KNIT exampleGraphAgentsJava03.java -->\n\nAdd the tool registry to the agent configuration:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy,\n        toolRegistry = toolRegistry\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava04 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class);\n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava04.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nThe output from the first operation was multiplied by 5:\n5 * 12 = 60\n\nThen, 10 was added to the result:\n60 + 10 = 70\n\nFinally, 123 was added to the result:\n70 + 123 = 193\n```\n<!--- KNIT example-graph-agents-04.txt -->\n\nAccording to this output, the agent correctly performed the calculations, but it only called the `multiply` tool once\ninstead of calling the corresponding tool for every operation.\nWe can help the agent by describing its role and providing instructions for using appropriate tools in the system prompt.\n\n## Provide a system prompt\n\nA [system prompt](../prompts/prompt-creation/index.md#system-message) defines the agent's role and instructions for performing tasks.\nIn our example, it is important to describe how the agent should process complex multistep calculations:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        systemPrompt = \"\"\"\n                    You are a simple calculator assistant.\n                    You can add and multiply two numbers using the 'add' and 'multiply' tools.\n                    When the user provides input, extract the numbers and operations they requested.\n                    Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result.\n                    Always respond with a clear, friendly message showing the calculation and result.\n                    \"\"\".trimIndent(),\n        toolRegistry = toolRegistry,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava05 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class); \n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\"); \n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .systemPrompt(\"You are a simple calculator assistant. You can add and multiply two numbers using the 'add' and 'multiply' tools. When the user provides input, extract the numbers and operations they requested. Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result. Always respond with a clear, friendly message showing the calculation and result.\")\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava05.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nAdding 60 and 10...\nAdding 70 and 123...\nThe final result is: 193\n```\n<!--- KNIT example-graph-agents-05.txt -->\n\nAs you can see, the agent now correctly calls the appropriate tool for each operation,\nensuring that it performs the calculations deterministically instead of risking a hallucinated result.\n\n## Next steps\n\n- Compare to [functional agents](functional-agents.md) and [planner agents](planner-agents/index.md)\n- Enhance your agent by [installing features](../features/index.md)\n- Improve the predictability and reliability with [structured output](../structured-output.md)\n","isInternal":false,"tokens":7132,"sizeBytes":31151},{"name":"index.md","path":"docs/docs/agents/index.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/index.md","title":"Subagent: index","category":"subagent-persona","format":"markdown","content":"# Agents\n\nAI agents are autonomous systems that can reason, make decisions,\ninteract with the environment, and take actions to achieve a specific goal.\nIn Koog, an AI agent is more than just a wrapper around an LLM;\nit is a structured, type-safe state machine designed for the JVM ecosystem.\n\nKoog agents are built around the following core concepts:\n\n- A [prompt executor](../prompts/prompt-executors.md) manages and executes prompts,\n  enabling the agent to interact with LLMs for reasoning and decision-making.\n- A [strategy](../nodes-and-components.md) defines the agent's workflow.\n  It can be in the form of a directed graph, a function, or a planner.\n  See [Agent types](#agent-types).\n- An agent can use [tools](../tools/index.md) to interact with external data sources and services.\n- You can extend and enhance the functionality of AI agents using [features](../features/index.md).\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\n## Agent types\n\nDepending on the task you need to perform, Koog provides several agent types:\n\n- [Basic agents](basic-agents.md) are ideal for simple tasks that don't require any custom logic.\n  These agents implement a predefined strategy that works for most common use cases.\n- [Graph-based agents](graph-based-agents.md) provide full control and flexibility of the agent's workflow, state management, and visualization.\n- [Functional agents](functional-agents.md) enable you to quickly prototype custom logic as a function with access to the agent's context.\n- [Planner agents](planner-agents/index.md) can autonomously plan and execute multistep tasks through iterative cycles until they reach a desired final state.\n\n## Agent configuration\n\nAgent configuration defines the agent's execution parameters,\nincluding the initial prompt, language model, and iteration limits.\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\nFor simple agents, besides the mandatory prompt executor and language model,\nyou can specify the initial system prompt and some other parameters directly in the agent constructor:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        temperature = 0.7,\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-agent-config-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful assistant.\")\n        .temperature(0.7)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-01.java -->\n\nAlternatively, you can create an instance of [`AIAgentConfig`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent.config/-a-i-agent-config/index.html)\nto define the agent's behavior and parameters more granularly, then pass it to the agent constructor.\nThis enables you to define complex prompts with multiple messages,\nconversation history, LLM parameters, and additional execution parameters.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.prompt.params.LLMParams\n    -->\n    ```kotlin\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\n            id = \"assistant\",\n            params = LLMParams(\n                temperature = 0.7\n            )\n        ) {\n            system(\"You are a helpful assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 10\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        agentConfig = agentConfig\n    )\n    ```\n    <!--- KNIT example-agent-config-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    Prompt prompt = Prompt.builder(\"assistant\")\n        .system(\"You are a helpful assistant.\")\n        .build()\n        .withParams(new LLMParams(\n            0.7,         // temperature\n            null,        // maxTokens\n            1,           // numberOfChoices\n            null,        // speculation\n            null,        // schema\n            LLMParams.ToolChoice.Auto.INSTANCE, // toolChoice\n            null,        // user\n            null         // additionalProperties\n        ));\n\n    AIAgentConfig agentConfig = AIAgentConfig.builder(OpenAIModels.Chat.GPT4o)\n        .prompt(prompt)\n        .maxAgentIterations(10)\n        .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .agentConfig(agentConfig)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-02.java -->\n\nHere are the parameters of `AIAgentConfig`:\n\n- `prompt` defines the initial [prompt](../prompts/prompt-creation/index.md) and [LLM parameters](../llm-parameters.md).\n\n- `model` specifies the language model with which the agent interacts.\n  You can use one of the predefined models or [create a custom model configuration](../model-capabilities.md#creating-a-model-llmodel-configuration).\n\n- `maxAgentIterations` limits the maximum number of steps the agent can take before it terminates.\n  Each step is a [node](../nodes-and-components.md) in the agent's workflow.\n\n- `missingToolsConversionStrategy` defines a strategy for handling missing tools during agent execution.\n\n[//]: # (TODO write about missing tools in the TOols section and link from here)\n\n- `responseProcessor` can be used to define a custom response processor.\n  For example, it can moderate and validate the response content, change the response format, or log the response.\n\n[//]: # (TODO write about response processing somewhere?)\n","isInternal":false,"tokens":1467,"sizeBytes":6463},{"name":"goap-agents.md","path":"docs/docs/agents/planner-agents/goap-agents.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/planner-agents/goap-agents.md","title":"Subagent: goap-agents","category":"subagent-persona","format":"markdown","content":"---\nstatus: beta\n---\n\n# GOAP agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nGOAP is an algorithmic planning approach that uses [A* search] find optimal action sequences\nthat satisfy the goal conditions while minimizing the total cost.\nUnlike [LLM-based planners](llm-based-planners.md) that use an LLM to generate plans,\na GOAP agent algorithmically discovers action sequences based on predefined goals and actions.\n\nGOAP planners work with three main concepts:\n\n- **State**: Represents the current state of the world.\n- **Actions**: Define what can be done, including preconditions, effects (beliefs), costs, and execution logic.\n- **Goals**: Define target conditions, heuristic costs, and value functions.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nIn Koog, you define a GOAP agent using a DSL by declaratively specifying the goals and actions.\n\nTo create a GOAP agent, you need to:\n\n1. Define the state as a data class with properties representing various aspects specific to your goal.\n2. Create a [GOAPPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner/index.html) instance using the [goap()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/goap.html) function.\n    1. Define actions with preconditions and beliefs using the [action()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/action.html) function.\n    2. Define goals with completion conditions using the [goal()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/goal.html) function.\n3. Wrap the planner with [AIAgentPlannerStrategy](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-a-i-agent-planner-strategy/index.html) and pass it to the [PlannerAIAgent](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-planner-a-i-agent/index.html) constructor.\n\n!!! note\n\n    The planner selects individual actions and their sequence.\n    Each action includes a precondition that must hold true for the action to be executed\n    and a belief that defines the predicted outcome.\n    For more information about beliefs, see [State beliefs compared to actual execution](#state-beliefs-compared-to-actual-execution).\n\nIn the following example, GOAP handles high-level planning for creating an article\n(outline → draft → review → publish),\nwhile the LLM performs the actual content generation within each action.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.planner.goap\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Define a state for content creation\n    data class ContentState(\n        val topic: String,\n        val hasOutline: Boolean = false,\n        val outline: String = \"\",\n        val hasDraft: Boolean = false,\n        val draft: String = \"\",\n        val hasReview: Boolean = false,\n        val isPublished: Boolean = false\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = draft\n    }\n\n    // Create GOAP planner with LLM-powered actions\n    val planner = goap(\"content-planner\", ::ContentState) {\n        // Define actions with preconditions and beliefs\n        action(\n            name = \"Create outline\",\n            precondition = { state -> !state.hasOutline },\n            belief = { state -> state.copy(hasOutline = true, outline = \"Outline\") },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to create the outline\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Create a detailed outline for an article about: ${state.topic}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasOutline = true, outline = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Write draft\",\n            precondition = { state -> state.hasOutline && !state.hasDraft },\n            belief = { state -> state.copy(hasDraft = true, draft = \"Draft\") },\n            cost = { 2.0 }\n        ) { ctx, state ->\n            // Use LLM to write the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Write an article based on this outline:\\n${state.outline}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasDraft = true, draft = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Review content\",\n            precondition = { state -> state.hasDraft && !state.hasReview },\n            belief = { state -> state.copy(hasReview = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to review the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Review this article and suggest improvements:\\n${state.draft}\")\n                }\n                requestLLM()\n            }\n            println(\"Review feedback: ${response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }}\")\n            state.copy(hasReview = true)\n        }\n\n        action(\n            name = \"Publish\",\n            precondition = { state -> state.hasReview && !state.isPublished },\n            belief = { state -> state.copy(isPublished = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            println(\"Publishing article...\")\n            state.copy(isPublished = true)\n        }\n\n        // Define the goal with a completion condition\n        goal(\n            name = \"Published article\",\n            description = \"Complete and publish the article\",\n            condition = { state -> state.isPublished }\n        )\n    }\n\n    // Create and run the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"writer\") {\n            system(\"You are a professional content writer.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 20\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = planner,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        val result = agent.run(\"The Future of AI in Software Development\")\n        println(\"Final state: $result\")\n    }\n    ```\n    <!--- KNIT example-goap-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGoapAgents01 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    // Define a state for content creation\n    static class ContentState extends GoapAgentState<String, String> {\n        public String topic;\n        public boolean hasOutline = false;\n        public String outline = \"\";\n        public boolean hasDraft = false;\n        public String draft = \"\";\n        public boolean hasReview = false;\n        public boolean isPublished = false;\n    \n        public ContentState(String topic) {\n            this.topic = topic;\n        }\n\n        @Override\n        public String getAgentInput() {\n            return topic;\n        }\n\n        public ContentState copy(boolean hasOutline, String outline, boolean hasDraft,\n                                 String draft, boolean hasReview, boolean isPublished) {\n            ContentState state = new ContentState(topic);\n            state.hasOutline = hasOutline;\n            state.outline = outline;\n            state.hasDraft = hasDraft;\n            state.draft = draft;\n            state.hasReview = hasReview;\n            state.isPublished = isPublished;\n            return state;\n        }\n\n        @Override\n        public String provideOutput() {\n            return draft;\n        }\n    }\n\n    public static void main(String[] args) {\n        var promptExecutor = PromptExecutor.builder()\n            .openAI(\"OPENAI_API_KEY\")\n            .build();\n\n        var strategy = Planners.goap(\"content-planner\", ContentState::new)\n            .action(\"Create outline\", builder -> builder\n                .precondition(state -> !state.hasOutline)\n                .belief(state -> state.copy(true, \"Outline\", false, \"\", false, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Create a detailed outline for an article about: \" + state.topic);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(true, response, state.hasDraft, state.draft,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Write draft\", builder -> builder\n                .precondition(state -> state.hasOutline && !state.hasDraft)\n                .belief(state -> state.copy(state.hasOutline, state.outline, true, \"Draft\", false, false))\n                .cost(state -> 2.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Write an article based on this outline:\\n\" + state.outline);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(state.hasOutline, state.outline, true, response,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Review content\", builder -> builder\n                .precondition(state -> state.hasDraft && !state.hasReview)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, true, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Review this article and suggest improvements:\\n\" + state.draft);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    System.out.println(\"Review feedback: \" + response);\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, true, state.isPublished);\n                })\n            )\n            .action(\"Publish\", builder -> builder\n                .precondition(state -> state.hasReview && !state.isPublished)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, state.hasReview, true))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    System.out.println(\"Publishing article...\");\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, state.hasReview, true);\n                })\n            )\n            .goal(\"Published article\", builder -> builder\n                .description(\"Complete and publish the article\")\n                .condition(state -> state.isPublished)\n            )\n            .build();\n\n        var agent = AIAgent.builder()\n            .plannerStrategy(strategy)\n            .promptExecutor(promptExecutor)\n            .llmModel(OpenAIModels.Chat.GPT4o)\n            .systemPrompt(\"You are a professional content writer.\")\n            .maxIterations(20)\n            .build();\n\n        String result = agent.run(\"The Future of AI in Software Development\");\n        System.out.println(\"Final state: \" + result);\n    }\n    ```\n    <!--- KNIT exampleGoapAgentsJava01.java -->\n    \n\n## Custom cost functions\n\nAs [A* search] uses cost as a factor in finding the optimal sequence of actions,\nyou can define custom cost functions for actions and goals to guide the planner:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val operationDone: Boolean = true,\n        val hasOptimization: Boolean = true\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Expensive operation\",\n        precondition = { true },\n        belief = { state -> state.copy(operationDone = true) },\n        cost = { state ->\n            // Dynamic cost based on state\n            if (state.hasOptimization) 1.0 else 10.0\n        }\n    ) { ctx, state ->\n        // Execute action\n        state.copy(operationDone = true)\n    }\n    ```\n    <!--- KNIT example-goap-agents-02.kt -->\n\n=== \"Java\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents02 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean operationDone = false;\n            public boolean hasOptimization = true;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean operationDone) {\n                MyState state = new MyState(topic);\n                state.operationDone = operationDone;\n                state.hasOptimization = this.hasOptimization;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Expensive operation\", builder -> builder\n        .precondition(state -> true)\n        .belief(state -> state.copy(true))\n        .cost(state -> {\n            // Dynamic cost based on state\n            return state.hasOptimization ? 1.0 : 10.0;\n        })\n        .execute((context, state) -> {\n            // Execute action\n            return state.copy(true);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava02.java -->\n\n## State beliefs compared to actual execution\n\nGOAP distinguishes between the concepts of beliefs (optimistic predictions) and actual execution:\n\n- **Belief**: What the planner thinks will happen, used for planning.\n- **Execution**: What actually happens, used for real state updates.\n\nThis allows the planner to make plans based on expected outcomes while handling actual results properly:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val taskComplete: Boolean = true,\n        val attempts: Int = 0\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    fun performComplexTask(): Boolean = true\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Attempt complex task\",\n        precondition = { state -> !state.taskComplete },\n        belief = { state ->\n            // Optimistic belief: task will succeed\n            state.copy(taskComplete = true)\n        },\n        cost = { 5.0 }\n    ) { ctx, state ->\n        // Actual execution might fail or have different results\n        val success = performComplexTask()\n        state.copy(\n            taskComplete = success,\n            attempts = state.attempts + 1\n        )\n    }\n    ```\n    <!--- KNIT example-goap-agents-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents03 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean taskComplete = false;\n            public int attempts = 0;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean taskComplete, int attempts) {\n                MyState state = new MyState(topic);\n                state.taskComplete = taskComplete;\n                state.attempts = attempts;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        static boolean performComplexTask() {\n            return true;\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Attempt complex task\", builder -> builder\n        .precondition(state -> !state.taskComplete)\n        .belief(state -> {\n            // Optimistic belief: task will succeed\n            return state.copy(true, state.attempts);\n        })\n        .cost(state -> 5.0)\n        .execute((context, state) -> {\n            // Actual execution might fail or have different results\n            boolean success = performComplexTask();\n            return state.copy(success, state.attempts + 1);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava03.java -->\n\n[A* search]: https://en.wikipedia.org/wiki/A*_search_algorithm\n","frontmatter":{"status":"beta"},"isInternal":false,"tokens":4253,"sizeBytes":20167},{"name":"index.md","path":"docs/docs/agents/planner-agents/index.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/planner-agents/index.md","title":"Subagent: index","category":"subagent-persona","format":"markdown","content":"---\nstatus: beta\n---\n\n# Planner agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nPlanner agents are AI agents that plan and execute multistep tasks through iterative planning cycles.\nThey continuously build or update plans, execute steps, and check completion criteria against the current state.\n\nPlanner agents are suitable for complex tasks\nthat require breaking down a high-level goal into smaller, actionable steps\nand adapting the plan based on the results of each step.\n\nWhile with [graph-based agents](../graph-based-agents.md) you define all the nodes and edges,\nfor planner agents, you only define the actions (nodes) with typed inputs and outputs.\nThe planner creates reasonable edges that would be suitable to achieve the desired state,\nand it can also update the optimal path between steps.\nThis allows for a more dynamic approach which can be more powerful \nbut less controllable compared to graph-based agents.\n\nPlanner agents operate through an iterative planning cycle:\n\n1. The planner creates or updates a plan based on the current state.\n2. The planner executes a single step from the plan, updating the state.\n3. The planner determines whether the plan is completed according to the current state.\n    - If the plan is completed, the cycle ends.\n    - If the plan is not completed, the cycle repeats from the first step.\n\n```mermaid\ngraph LR\n  A[Create or update plan] --> B[\"Execute step and update state\"]\n  B --> C[\"Check completion\"]\n  C -->|Completed| D[[Done]]\n  C -->|\"Not completed\"| A\n```\n\nKoog provides two types of planner agents:\n\n- [LLM-based planners](llm-based-planners.md) use an LLM to create and update plans\n- [GOAP agents](goap-agents.md) use a special algorithm to determine the optimal action sequences\n","frontmatter":{"status":"beta"},"isInternal":false,"tokens":381,"sizeBytes":1741},{"name":"llm-based-planners.md","path":"docs/docs/agents/planner-agents/llm-based-planners.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/docs/docs/agents/planner-agents/llm-based-planners.md","title":"Subagent: llm-based-planners","category":"subagent-persona","format":"markdown","content":"---\nstatus: beta\n---\n\n# LLM-based planners\n\n--8<-- \"versioning-snippets.md:beta\"\n\nLLM-based planners use LLMs to generate and evaluate plans.\nThey operate on a string-based state and execute steps through LLM requests.\nString-based state means that the agent state is a single string.\nAt every step, the agent accepts an initial state string and returns the final state string as the result.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nKoog provides two simple planners:\n\n- [SimpleLLMPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-planner/index.html)\n  generates a plan only once at the very beginning and then follows the plan until it is completed.\n  To include replanning, extend `SimpleLLMPlanner` and override the `assessPlan` method,\n  indicating when the agent should replan.\n- [SimpleLLMWithCriticPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-with-critic-planner/index.html)\n  implements the `assessPlan` method that uses an LLM to check the validity of the plan via an LLM request\n  and assess whether the agent should replan.\n\nThe following example shows how to create a simple planner agent using `SimpleLLMPlanner`:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy\n    import ai.koog.agents.core.planner.PlannerAIAgent\n    import ai.koog.agents.planner.llm.SimpleLLMPlanner\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    // Create the planner\n    val planner = SimpleLLMPlanner()\n\n    // Wrap it in a planner strategy\n    val strategy = AIAgentPlannerStrategy(\n        name = \"simple-planner\",\n        planner = planner\n    )\n\n    // Configure the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"planner\") {\n            system(\"You are a helpful planning assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 50\n    )\n\n    // Create the planner agent\n    val agent = PlannerAIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = strategy,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        // Run the agent with a task\n        val result = agent.run(\"Create a plan to organize a team meeting\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-llm-based-planners-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleLLMBasedPlanner01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create the planner strategy with LLM-based planner\n    AIAgentPlannerStrategy<String, String> strategy =\n        Planners.llmBased(\"simple-planner\")\n            .build();\n\n    // Create the OpenAI executor\n    var promptExecutor = PromptExecutor.builder()\n        .openAI(\"OPENAI_API_KEY\")\n        .build();\n\n    // Create the planner agent using AIAgent builder\n    AIAgent<String, String> agent = AIAgent.builder()\n        .plannerStrategy(strategy)\n        .promptExecutor(promptExecutor)\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful planning assistant.\")\n        .maxIterations(50)\n        .build();\n\n    // Run the agent with a task\n    String result = agent.run(\"Create a plan to organize a team meeting\");\n    System.out.println(result);\n    ```\n     <!--- KNIT exampleLLMBasedPlannerJava01.java -->\n\n## Next steps\n\n- Learn about [GOAP agents](goap-agents.md)\n","frontmatter":{"status":"beta"},"isInternal":false,"tokens":1048,"sizeBytes":4202},{"name":"README.md","path":"examples/simple-examples/src/main/kotlin/ai/koog/agents/example/a2a/README.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/examples/simple-examples/src/main/kotlin/ai/koog/agents/example/a2a/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# Agent-to-Agent (A2A) Examples\n\nExamples demonstrating the A2A protocol for inter-agent communication with standardized message/task workflows, streaming responses, and artifact delivery.\n\n## Examples\n\n### Simple Joke Agent (`simplejoke/`)\n\nBasic message-based communication without tasks.\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9998)\n./gradlew runExampleSimpleJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleSimpleJokeClient\n```\n\n### Advanced Joke Agent (`advancedjoke/`)\n\nTask-based workflow with:\n- Interactive clarification (InputRequired state)\n- Artifact delivery for results\n- Graph-based agent strategy with documented nodes/edges\n- Streaming response events\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9999)\n./gradlew runExampleAdvancedJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleAdvancedJokeClient\n```\n\n## Key Patterns\n\n**Simple Agent:** `sendMessage()` → single response\n**Advanced Agent:** `sendMessageStreaming()` → Flow of events (Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent)\n\n**Task States:** Submitted → Working → InputRequired (optional) → Completed\n\nSee code comments in `JokeWriterAgentExecutor.kt` for detailed flow documentation.\n","isInternal":false,"tokens":278,"sizeBytes":1212},{"name":"README.md","path":"examples/simple-examples/src/main/kotlin/ai/koog/agents/example/snapshot/sql/README.md","rawUrl":"https://raw.githubusercontent.com/JetBrains/koog/HEAD/examples/simple-examples/src/main/kotlin/ai/koog/agents/example/snapshot/sql/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# SQL Persistence Example\n\nDemonstrates using SQL databases as persistence backends for agent checkpoints through JetBrains Exposed ORM.\n\n## Quick Start\n\n1. **Start databases** (PostgreSQL/MySQL only)\n   ```bash\n   docker-compose up -d\n   ```\n\n2. **Run the examples**\n   ```bash\n   # PostgreSQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"postgres\"\n   \n   # MySQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"mysql\"\n   \n   # H2 example (embedded database)\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"h2\"\n   ```\n\n## What It Does\n\nThe example demonstrates:\n- Saving checkpoints to different SQL databases\n- Connection pooling with HikariCP (PostgreSQL/MySQL)\n- TTL-based checkpoint expiration\n- Multiple H2 database modes (in-memory, file-based, PostgreSQL-compatible)\n- Schema auto-creation\n\n## Database Schema\n\n```sql\nCREATE TABLE agent_checkpoints (\n    persistence_id VARCHAR(255),\n    checkpoint_id VARCHAR(255),\n    created_at BIGINT,\n    checkpoint_json TEXT,\n    ttl_timestamp BIGINT,\n    PRIMARY KEY (persistence_id, checkpoint_id)\n);\n```\n\n## Providers\n\n### PostgreSQL\n\n```kotlin\nPostgresPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:postgresql://localhost:5432/agents\",\n        driver = \"org.postgresql.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 3600  // Optional TTL\n)\n```\n\n### MySQL\n\n```kotlin\nMySQLPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:mysql://localhost:3306/agents\",\n        driver = \"com.mysql.cj.jdbc.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 7200\n)\n```\n\n### H2 (Embedded)\n\n```kotlin\n// In-memory (for testing)\nH2PersistenceStorageProvider.inMemory(\n    persistenceId = \"test-agent\",\n    databaseName = \"test_db\"\n)\n\n// File-based (for persistence)\nH2PersistenceStorageProvider.fileBased(\n    persistenceId = \"my-agent\",\n    filePath = \"./data/h2/agent_checkpoints\"\n)\n```\n### SQLite (Embedded)\n\n\n## Connection Pool Monitoring\n\nFor PostgreSQL/MySQL providers:\n\n```kotlin\nval stats = provider.getPoolStats()\nprintln(\"Active connections: ${stats.activeConnections}\")\nprintln(\"Pool utilization: ${stats.utilizationPercent}%\")\n```\n\n## Database UI\n\nAccess Adminer at http://localhost:8080:\n- **PostgreSQL**: Server: `postgres`, Username: `agent_user`, Password: `agent_pass`\n- **MySQL**: Server: `mysql`, Username: `agent_user`, Password: `agent_pass`\n\n## Environment Variables\n\n- `POSTGRES_URL` - Override PostgreSQL connection\n- `MYSQL_URL` - Override MySQL connection\n\n## Cleanup\n\n```bash\ndocker-compose down -v  # Stop and remove data\nrm -rf ./data          # Remove H2/SQLite files\n```\n","isInternal":false,"tokens":671,"sizeBytes":2806}],"systemPromptSnippet":"<yakaai_skills repo=\"JetBrains/koog\">\n<!-- File: AGENTS.md (Tokens: ~1891 | Category: root-instruction) -->\n# Koog AI Agent Framework\n\nKoog is a Kotlin multiplatform framework for building AI agents with graph-based workflows.\nIt supports JVM and JS targets and integrates with multiple LLM providers\n(OpenAI, Anthropic, Google, OpenRouter, Ollama) and Model Context Protocol (MCP).\n\n## Project Structure\n\nThe project follows a modular architecture with a clear separation of concerns:\n\n```\nkoog/\n├── agents/\n│   ├── agents-core/           # Core abstractions (AIAgent, AIAgentStrategy, AIAgentEnvironment)\n│   ├── agents-tools/          # Tool infrastructure (Tool<TArgs, TResult>, ToolRegistry, AIAgentTool)\n│   ├── agents-features-*/     # Feature implementations (memory, tracing, event handling)\n│   ├── agents-mcp/           # Model Context Protocol integration\n│   └── agents-test/          # Testing utilities and framework\n├── prompt-*/                 # LLM interaction layer (executors, models, structured data)\n├── embeddings-*/             # Vector embedding support\n├── examples/                 # Reference implementations and usage patterns\n└── build.gradle.kts          # Root build configuration\n```\n\n## Build & Commands\n\n### Development Commands\n\n```bash\n# Full build including tests\n./gradlew build\n\n# Build without tests\n./gradlew assemble\n\n# Run all JVM tests\n./gradlew jvmTest\n\n# Run all JS tests  \n./gradlew jsTest\n\n# Test specific module\n./gradlew :agents:agents-core:jvmTest\n\n# Run specific test class\n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest\"\n\n# Run specific test method  \n./gradlew jvmTest --tests \"ai.koog.agents.test.SimpleAgentMockedTest.test AIAgent doesn't call tools by default\"\n\n# Compile test classes only (for faster iteration)\n./gradlew jvmTestClasses jsTestClasses\n```\n\n### Development Environment\n\n- **JDK**: 17+ required for JVM target\n- **Build System**: Gradle with version catalogs for dependency management\n- **Targets**: JVM, JavaScript (Kotlin Multiplatform), WASM\n- **IDE**: IntelliJ IDEA recommended with Kotlin plugin\n\n## Code Style\n\n- Follow [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html)\n- Use four spaces for indentation (consistent across all files)\n- Name test functions as `testXxx` (no backticks for readability)\n- Use descriptive variable and function names\n- Prefer functional programming patterns where appropriate\n- Use type-safe builders and DSLs for configuration\n- Document public APIs with KDoc comments\n- NEVER suppress compiler warnings without a good reason\n\n## Quality Gates\nRead and follow the Quality Gates section in /TESTING.md before considering any code change complete.\n\n## Architecture\n\n### Core Framework Components\n\n**AIAgent** — Main orchestrator that executes strategies in coroutine scopes, manages tools via ToolRegistry,\nruns features through AIAgentPipeline, and handles LLM communication via PromptExecutor.\n\n**AIAgentStrategy** — Graph-based execution logic that defines workflows as subgraphs with start/finish nodes,\nmanages tool selection strategy, and handles termination/error reporting.\n\n**ToolRegistry** — Centralized, type-safe tool management using a builder pattern: `ToolRegistry { tool(MyTool()) }`.\nSupports registry merging with `+` operator.\n\n**AIAgentFeature** — Extensible capabilities installed into AIAgentPipeline with configuration.\nFeatures have unique storage keys and can intercept agent lifecycle events.\n\n### Module Organization\n\n1. **agents-core**: Core abstractions (`AIAgent`, `AIAgentStrategy`, `AIAgentEnvironment`)\n2. **agents-tools**: Tool infrastructure (`Tool<TArgs, TResult>`, `ToolRegistry`, `AIAgentTool`)\n3. **agents-features-***: Feature implementations (memory, tracing, event handling)\n4. **agents-mcp**: Model Context Protocol integration\n5. **prompt-***: LLM interaction layer (executors, models, structured data)\n6. **embeddings-***: Vector embedding support\n7. **examples**: Reference implementations and usage patterns\n\n### Key Architectural Patterns\n\n- **State Machine Graphs**: Agents execute as node graphs with typed edges\n- **Feature Pipeline**: Extensible behavior via installable features with lifecycle hooks\n- **Environment Abstraction**: Safe tool execution context preventing direct tool calls\n- **Type Safety**: Generics ensure compile-time correctness for tool arguments/results\n- **Builder Patterns**: Fluent APIs for configuration throughout the framework\n\n## Testing\n\nThe framework provides comprehensive testing utilities in `agents-test` module:\n\n### LLM Response Mocking\n\n```kotlin\nval mockLLMApi = getMockExecutor(toolRegistry, eventHandler) {\n    mockLLMAnswer(\"Hello!\") onRequestContains \"Hello\"\n    mockLLMToolCall(CreateTool, CreateTool.Args(\"solve\")) onRequestEquals \"Solve task\"\n    mockLLMAnswer(\"Default response\").asDefaultResponse\n}\n```\n\n### Tool Behavior Mocking\n\n```kotlin\n// Simple return value\nmockTool(PositiveToneTool) alwaysReturns \"The text has a positive tone.\"\n\n// With additional actions\nmockTool(NegativeToneTool) alwaysTells {\n    println(\"Tool called\")\n    \"The text has a negative tone.\"\n}\n\n// Conditional responses\nmockTool(SearchTool) returns SearchTool.Result(\"Found\") onArgumentsMatching {\n    args.query.contains(\"important\")\n}\n```\n\n### Graph Structure Testing\n\n```kotlin\nAIAgent(...) {\n    withTesting()\n\n    testGraph(\"test\") {\n        val firstSubgraph = assertSubgraphByName<String, String>(\"first\")\n        val secondSubgraph = assertSubgraphByName<String, String>(\"second\")\n\n        assertEdges {\n            startNode() alwaysGoesTo firstSubgraph\n            firstSubgraph alwaysGoesTo secondSubgraph\n        }\n\n        verifySubgraph(firstSubgraph) {\n            val askLLM = assertNodeByName<String, Message.Response>(\"callLLM\")\n            assertNodes {\n                askLLM withInput \"Hello\" outputs Message.Assistant(\"Hello!\")\n            }\n        }\n    }\n}\n```\n\nFor comprehensive testing examples, see `agents/agents-test/TESTING.md`.\n\n## Security\n\n### API Key Management\n\n- **NEVER** commit API keys or secrets to the repository\n- Use environment variables for all sensitive configuration\n- Store test API keys in a local environment only\n- Required environment variables for integration tests:\n    - `ANTHROPIC_API_TEST_KEY`\n    - `GEMINI_API_TEST_KEY`\n    - `MISTRAL_AI_API_TEST_KEY`\n    - `OLLAMA_IMAGE_URL`\n    - `OPEN_AI_API_TEST_KEY`\n    - `OPEN_ROUTER_API_TEST_KEY`\n\n### Tool Execution Safety\n\n- Tools execute within controlled `AIAgentEnvironment` contexts\n- Direct tool calls are prevented outside agent execution\n- Use type-safe tool arguments to prevent injection attacks\n- Validate all external inputs in tool implementations\n\n### Dependency Security\n\n- Regularly update dependencies using Gradle version catalogs\n- Use specific version ranges to avoid supply chain attacks\n- Review dependencies for known vulnerabilities\n- Follow the principle of the least privilege in tool implementations\n\n## Configuration\n\n### Environment Setup\n\nSet environment variables for integration testing (never commit API keys):\n\n```bash\n# Export in your shell or IDE run configuration\nexport ANTHROPIC_API_TEST_KEY=your_key_here\nexport DEEPSEEK_API_TEST_KEY=your_key_here\nexport GEMINI_API_TEST_KEY=your_key_here\nexport MISTRAL_AI_API_TEST_KEY=your_key_here\nexport OLLAMA_IMAGE_URL=http://localhost:11434\nexport OPEN_AI_API_TEST_KEY=your_key_here\nexport OPEN_ROUTER_API_TEST_KEY=your_key_here\n\n# Or add to ~/.bashrc, ~/.zshrc, or IDE environment variables\n```\n\n### Gradle Configuration\n\n- Uses version catalogs (`gradle/libs.versions.toml`) for dependency management\n- Multiplatform configuration in `build.gradle.kts`\n- Test configuration supports both JVM and JS targets\n\n### Development Environment Requirements\n\n- **JDK**: 17+ (OpenJDK recommended)\n- **IDE**: IntelliJ IDEA with Kotlin Multiplatform plugin\n- **Optional**: Docker for Ollama local testing\n\n## Development Workflow\n\n### Branch Strategy\n\n- **develop**: All development (features and bug fixes)\n- **main**: Released versions only\n- Base all PRs against `develop` branch\n- Use descriptive branch names: `feature/agent-memory`, `fix/tool-registry-bug`\n\n### Code Quality\n\n- **ALWAYS** run `./gradlew build` before submitting PRs\n- Ensure all tests pass on JVM, JS, WASM targets\n- Follow established patterns in existing code\n- Add tests for new functionality\n- Update documentation for API changes\n\n### Commit Guidelines\n\n- Use conventional commit format: `feat:`, `fix:`, `docs:`, `test:`\n- Include issue references where applicable\n- Keep commits focused and atomic\n\n<!-- File: a2a/CLAUDE.md (Tokens: ~2378 | Category: claude-rule) -->\n# A2A Module Development Guidelines\n\n## Module Overview\n\nThe A2A (Agent-to-Agent) module is a **meta-module** within the larger Koog project that implements a comprehensive client and server library for the A2A protocol, a standardized communication protocol for AI agents based on the specification at https://a2a-protocol.org/latest/specification/.\n\n**Important**: Since this is a meta-module inside a bigger root project, the Gradle wrapper executable is located one directory up. All Gradle commands must use `../gradlew` when running from the a2a directory.\n\n### Purpose\n- **Agent Discovery**: Through AgentCard manifests describing agent capabilities and interfaces\n- **Message Exchange**: Standardized message format with support for different content types\n- **Task Management**: Long-running operations with state tracking and lifecycle management\n- **Push Notifications**: Asynchronous notifications for task updates\n- **Multiple Transport Protocols**: JSON-RPC, HTTP+JSON/REST with extensibility for gRPC\n- **Authentication & Security**: OpenAPI 3.0 compatible security schemes\n\n### Submodules Architecture\n\n1. **a2a-core**: Core abstractions, data models, and transport interfaces\n   - AgentCard, Message, Task, Event hierarchy\n   - Transport interfaces: ClientTransport, ServerTransport, RequestHandler\n   - No external dependencies beyond Kotlin stdlib and serialization\n\n2. **a2a-client**: High-level client library for communicating with A2A servers\n   - A2AClient wrapper, AgentCardResolver\n   - Capability validation, request/response handling\n   - Depends on: a2a-core, Ktor HTTP client\n\n3. **a2a-server**: Server-side implementation for hosting A2A agents\n   - A2AServer, AgentExecutor interface, session management\n   - Storage abstractions with in-memory implementations\n   - Depends on: a2a-core, coroutines, logging\n\n4. **a2a-transport**: Multiple transport protocol implementations\n   - a2a-transport-core-jsonrpc: JSON-RPC protocol base classes\n   - a2a-transport-client-jsonrpc-http: HTTP JSON-RPC client transport\n   - a2a-transport-server-jsonrpc-http: HTTP JSON-RPC server transport\n   - a2a-transport-*-rest: HTTP+JSON/REST protocol implementations\n\n## Technologies & Libraries\n\n### Core Dependencies (from gradle/libs.versions.toml)\n- **Kotlin Multiplatform**\n- **kotlinx-serialization**: JSON serialization for protocol messages\n- **kotlinx-coroutines**: Async/concurrent programming, Flow APIs\n- **kotlinx-datetime**: Timestamp handling in protocol messages\n- **ktor3**: HTTP client/server for transport implementations\n- **oshai-kotlin-logging**: Structured logging\n\n### Testing Libraries\n- **kotlin-test**: Multiplatform test framework\n- **kotest-assertions**: Rich assertion library for complex objects\n- **kotlinx-coroutines-test**: Testing coroutines with runTest\n- **testcontainers**: Docker-based integration testing\n- **logback-classic**: Runtime logging for tests\n\n### Platform Support\n- **JVM**: Full server and client support\n- **JS (Browser)**: Client-only support\n- **Future platforms**: Architecture ready for native support\n\n## Development Guidelines\n\n### Architecture Decisions\n⚠️ **CRITICAL**: Never make design and architecture decisions independently. Always ask the user for confirmation before:\n- Adding new transport protocols\n- Changing storage interfaces\n- Modifying core protocol message formats\n- Adding new authentication schemes\n- Changing session management behavior\n- Doing other sorts of major architectural changes\n\n### Code Style Conventions\n\n#### API Visibility\n- Use `explicitApi()` - all public APIs must have explicit visibility modifiers\n- Prefer `public` declarations for APIs, `internal` for implementation details\n- Use `@InternalA2AApi` annotation for APIs that are public but internal to the module\n\n#### Naming Conventions\n- Classes: PascalCase (`A2AServer`, `SessionManager`, `AgentExecutor`)\n- Interfaces: Same as classes, often ending in -or/-er for behaviors (`AgentExecutor`)\n- Functions: camelCase with descriptive names (`onSendMessage`, `getByContext`)\n- Properties: camelCase (`agentCard`, `sessionMutex`)\n\n#### Async Patterns\n- Use `suspend` functions for all async operations\n- Prefer `Flow` for streaming APIs (task events, message streams)\n- Use `RWLock` pattern: `rwLock.withReadLock/withWriteLock` for concurrent access\n- Use `Mutex` for single-threaded critical sections: `mutex.withLock`\n\n#### Error Handling\n- Use domain-specific exceptions (`A2AInternalErrorException`, `TaskOperationException`)\n- Propagate errors properly in async contexts\n- Include contextual information in exception messages\n\n#### KDoc Documentation\n- **Placement**: KDoc directly above declarations (classes, functions, properties)\n- **Constructor properties**: Document using `@param` tags in class KDoc\n- **Public class properties**: Document using `@property` tags in class KDoc\n- **Cross-references**: Use `[ClassName]`, `[ClassName.propertyName]` syntax for linking components\n- **Examples**: Include practical code examples for complex APIs\n- **Validation rules**: Document constraints with bullet points and clear explanations\n- **Exception documentation**: Use `@throws` with specific conditions\n- **Required**: All public APIs must have KDoc (enforced by `explicitApi()`)\n\n## Testing Requirements\n\n### Mandatory Test Execution\n⚠️ **ALWAYS** run the specific test suite using Gradle's jvmTest task to ensure changes work correctly:\n\n```bash\n# Run all tests in a specific module\n../gradlew :a2a:a2a-server:jvmTest\n\n# Run a specific test class\n../gradlew :a2a:a2a-server:jvmTest --tests \"ai.koog.a2a.server.tasks.InMemoryTaskStorageTest\"\n\n# Run a specific test method\n../gradlew :a2a:a2a-client:jvmTest --tests \"ai.koog.a2a.client.A2AClientIntegrationTest.test get agent card\"\n```\n\n### Testing Approach: Crucial Minimum\n- **Focus on core functionality** - test essential behavior, not implementation details\n- **Separate dedicated tests** - one test method per core scenario/edge case\n- **Avoid verbose, unnecessary tests** - don't test trivial getters/setters\n- **Test error conditions** - verify proper exception handling and error states\n\n#### Preferred Assertion Pattern\n**✅ DO**: Assert on whole objects when possible (especially data classes):\n```kotlin\nassertEquals(expectedTask, actualTask)  // Compares all properties\nassertEquals(listOf(expectedArtifact), retrieved?.artifacts)\n```\n\n**❌ AVOID**: Bunch of individual field assertions unless necessary:\n```kotlin\n// Avoid this pattern:\nassertEquals(expected.id, actual.id)\nassertEquals(expected.contextId, actual.contextId)\nassertEquals(expected.status, actual.status)\n// ... many more individual assertions\n```\n\n### Integration Tests\n- **Docker-based testing**: a2a-client uses testcontainers with Python A2A server\n- **Docker build dependency**: Integration tests automatically build required containers\n- **Test isolation**: Each test should be independent and clean up after itself\n\n### Test Structure Examples\n\n**Unit Tests** (InMemoryTaskStorageTest pattern):\n```kotlin\nclass InMemoryTaskStorageTest {\n    private lateinit var storage: InMemoryTaskStorage\n\n    @BeforeTest\n    fun setUp() {\n        storage = InMemoryTaskStorage()\n    }\n\n    @Test\n    fun testStoreAndRetrieveTask() = runTest {\n        val task = createTask(id = \"task-1\", contextId = \"context-1\")\n        storage.update(task)\n        val retrieved = storage.get(\"task-1\")\n\n        assertNotNull(retrieved)\n        assertEquals(task, retrieved)  // Whole object assertion\n    }\n}\n```\n\n**Integration Tests** (A2AClientIntegrationTest pattern):\n```kotlin\n@Testcontainers\nclass A2AClientIntegrationTest {\n    @Container\n    val testA2AServer = GenericContainer(\"test-python-a2a-server\")\n        .withExposedPorts(9999)\n        .waitingFor(Wait.forListeningPort())\n\n    @Test\n    fun `test get agent card`() = runTest {\n        val agentCard = client.getAgentCard()\n        val expectedAgentCard = AgentCard(...)\n        assertEquals(expectedAgentCard, agentCard)  // Full object comparison\n    }\n}\n```\n\n## Implementation Patterns\n\n### Transport Layer Abstraction\n- Implement `ClientTransport` interface for new client protocols\n- Implement `ServerTransport` and `RequestHandler` for new server protocols\n- Keep protocol logic separate from transport mechanism\n- Use suspend functions for all transport operations\n\n### Storage Interface Pattern\n```kotlin\npublic interface SomeStorage {\n    public suspend fun save(item: Item)\n    public suspend fun get(id: String): Item?\n    public suspend fun delete(id: String)\n}\n\n// In-memory implementation for testing/development\n@OptIn(InternalA2AApi::class)\npublic class InMemorySomeStorage : SomeStorage {\n    private val rwLock = RWLock()\n    private val items = mutableMapOf<String, Item>()\n\n    override suspend fun save(item: Item) = rwLock.withWriteLock {\n        items[item.id] = item\n    }\n}\n```\n\n### Session Management Pattern\n- Use `SessionEventProcessor` for event-driven session handling\n- Implement proper cleanup with `Closeable` interface\n- Use structured concurrency with `CoroutineScope`\n- Handle session lifecycle properly (start → active → closed)\n\n### Agent Executor Implementation\n```kotlin\npublic class MyAgentExecutor : AgentExecutor {\n    override suspend fun execute(\n        context: RequestContext<MessageSendParams>,\n        eventProcessor: SessionEventProcessor\n    ) {\n        // Process request and send events through eventProcessor\n        eventProcessor.sendTaskEvent(/* task event */)\n    }\n}\n```\n\n## Some Common Commands\n\n```bash\n# Build specific modules (a2a is a meta-module, build individual modules)\n../gradlew :a2a:a2a-core:assemble\n../gradlew :a2a:a2a-client:assemble\n../gradlew :a2a:a2a-server:assemble\n\n# Run JVM tests for specific modules\n../gradlew :a2a:a2a-core:jvmTest\n../gradlew :a2a:a2a-client:jvmTest\n../gradlew :a2a:a2a-server:jvmTest\n../gradlew :a2a:a2a-transport:a2a-transport-core-jsonrpc:jvmTest\n\n# Run JS tests for specific modules\n../gradlew :a2a:a2a-core:jsTest\n../gradlew :a2a:a2a-client:jsTest\n../gradlew :a2a:a2a-server:jsTest\n\n# Build all non-transport a2a modules at once\n../gradlew :a2a:a2a-core:assemble :a2a:a2a-client:assemble :a2a:a2a-server:assemble\n\n# Run all JVM tests across a2a modules\n../gradlew :a2a:a2a-core:jvmTest :a2a:a2a-client:jvmTest :a2a:a2a-server:jvmTest\n```\n\n<!-- File: CLAUDE.md (Tokens: ~3 | Category: claude-rule) -->\nAGENTS.md\n\n<!-- File: .claude/skills/add-java-code-snippets-in-docs/reference/factory-classes.md (Tokens: ~1085 | Category: anthropic-skill) -->\n# JVM `create()` factory classes\n\nLoad this file when the Kotlin block uses types that aren't callable from Java and you need to expose (or audit) a Java-friendly factory.\n\n## When a factory is needed\n\nKotlin constructors / APIs Java can't call cleanly:\n\n- Uses `kotlinx.io` types (`Sink`, `Path`, `SystemFileSystem`).\n- Uses inline value classes (`Duration`) — the constructor becomes private from Java.\n- Uses `KLogger` (kotlin-logging) instead of SLF4J `Logger`.\n- Has default parameters without `@JvmOverloads` — generates synthetic constructors with `DefaultConstructorMarker`.\n\nIf the Kotlin constructor uses only Java-compatible types and has `@JvmOverloads` (or no default params), Java can call it directly with `new`. Example:\n\n```java\n// No factory needed — constructor is Java-accessible\nnew TraceFeatureMessageRemoteWriter()\nnew TraceFeatureMessageRemoteWriter(connectionConfig)\n```\n\n## Pattern\n\nAdd a `companion object` with `create(...)` methods **on the class itself**, in the `jvmCommonMain` source set. The class must already live in `jvmCommonMain` (or have a JVM actual) for this to apply.\n\n> **Prerequisite:** if the class is still in `commonMain`, run the **`split-jvm-nonjvm`** skill first to produce the `expect class` + `jvmCommonMain` actual scaffolding. `split-jvm-nonjvm` requires the class to have no companion object at split time — so the order is: (1) split, then (2) add the companion `create()` on the JVM actual. If the class already has a companion in `commonMain`, raise it with the user before splitting.\n\nProvide two overloads of `create()`:\n\n1. **Kotlin-facing** — accepts the original Kotlin-only types (`kotlinx.io.Path`, `Sink`, `KLogger`, `Duration`). No JVM annotations needed; convenient sugar for Kotlin callers and keeps API symmetry.\n2. **Java-facing** — accepts only Java-compatible types (`java.nio.file.Path`, `java.io.OutputStream`, `org.slf4j.Logger`, primitives, etc.). Annotate with `@JavaAPI` (intent), `@JvmStatic` (static call site), and `@JvmOverloads` (one method per default-parameter combination). Convert to Kotlin-only types inside the body, then delegate to the primary constructor.\n\n## Skeleton\n\nSubstitute `Xxx` with the class name, `KotlinT` / `JavaT` with the actual type pair:\n\n```kotlin\n// In: jvmCommonMain/.../Xxx.kt\npublic class Xxx(/* primary constructor uses Kotlin-only types */) {\n    public companion object {\n        // (1) Kotlin-facing\n        public fun create(\n            param: KotlinT,\n            // ...defaulted params with Kotlin types\n        ): Xxx = Xxx(/* pass through */)\n\n        // (2) Java-facing\n        @JavaAPI\n        @JvmStatic\n        @JvmOverloads\n        public fun create(\n            param: JavaT,\n            // ...defaulted params with Java-friendly types\n        ): Xxx {\n            val kotlinParam = /* convert JavaT -> KotlinT */\n            return Xxx(/* pass converted values */)\n        }\n    }\n}\n```\n\nJava callers then write `Xxx.create(javaParam)` — no separate class, no `Continuation`, no `DefaultConstructorMarker`.\n\n## Conversion cheat sheet\n\n| Kotlin-only type | Java-facing replacement | Conversion inside `create()` |\n|------------------|-------------------------|------------------------------|\n| `kotlinx.io.files.Path` | `java.nio.file.Path` | `Path.of(javaPath.toString())` or use `SystemFileSystem` |\n| `kotlinx.io.Sink` | `java.io.OutputStream` (via opener lambda) | `outputStream.asSink().buffered()` |\n| `KLogger` (kotlin-logging) | `org.slf4j.Logger` | `KotlinLogging.logger(slf4jLogger)` |\n| `kotlin.time.Duration` | `long millis` / `java.time.Duration` | `millis.milliseconds` / `Duration.ofMillis(...).toKotlinDuration()` |\n| `(T) -> R` with Kotlin types | `Function<T, R>` or SAM with Java types | `{ t -> javaFn.apply(t) }` |\n\n## Reference implementations\n\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageFileWriter.kt` — `Path` / `Sink` ↔ `java.nio.file.Path` / `OutputStream`.\n- `agents/agents-features/agents-features-trace/src/jvmCommonMain/.../TraceFeatureMessageLogWriter.kt` — `KLogger` ↔ SLF4J `Logger`.\n\n## Anti-pattern: separate `*Jvm` object\n\nDo **not** introduce a separate `public object XxxJvm { ... }` alongside the original class. The convention is to put the Java-facing `create()` on the original class's companion. A `*Jvm` companion class would be redundant and inconsistent with the existing `TraceFeatureMessage*Writer` classes.\n\n<!-- File: .claude/skills/add-java-code-snippets-in-docs/reference/interop-tables.md (Tokens: ~1428 | Category: anthropic-skill) -->\n# Kotlin ↔ Java Interop Reference\n\nLoad this file when writing the Java side of a non-trivial Kotlin block: figuring out how a Kotlin construct maps to Java, or whether a `suspend` function has a Java-friendly bridge.\n\n## Quick mapping table\n\n| Kotlin | Java |\n|--------|------|\n| `AIAgent(...) { }` | `AIAgent.builder()...build()` |\n| `install(Feature) { }` | `.install(Feature.Feature, config -> { })` |\n| `install(Testing) { config() }` | `.install(Testing.Feature, config -> { })` (`ConfigureAction<T>` is a `fun interface`) |\n| `simpleOllamaAIExecutor()` | `PromptExecutor.builder().ollama().build()` |\n| `KotlinLogging.logger { }` | `LoggerFactory.getLogger(\"name\")` |\n| `val x = ...` (local) | `var x = ...` |\n| `val x = ...` (field) | `static Type x = ...` (never `static var`) |\n| `runBlocking { }` | not needed (`agent.run` is blocking in Java — see suspend bridges below) |\n| `config.addMessageProcessor(...)` | `config.addMessageProcessor(...)` |\n| `class Foo : Bar()` | `class Foo extends Bar` |\n| `when (x) { is Type -> ... }` | `if (x instanceof Type) { ... } else if ...` |\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` — see suspend bridges |\n| `override suspend fun close()` | `void handleClose()` — see suspend bridges |\n| `TraceFeatureMessageFileWriter(path, sinkOpener)` | `TraceFeatureMessageFileWriter.create(path)` — JVM `create()` overload taking `java.nio.file.Path` |\n| `TraceFeatureMessageLogWriter(klogger)` | `TraceFeatureMessageLogWriter.create(slf4jLogger)` — JVM `create()` overload taking SLF4J `Logger` |\n| `TraceFeatureMessageRemoteWriter(config)` | `new TraceFeatureMessageRemoteWriter()` (direct, no factory needed) |\n| `getMockExecutor(...) { mockLLMAnswer(...) }` | `MockPromptExecutor.builder(serializer).mockLLMAnswer(...).build()` |\n\n## Suspend functions and Java\n\nKotlin `suspend` functions compile to JVM methods with an extra `Continuation` parameter and `Object` return type. **Java code snippets must NEVER show `Continuation` parameters.** The codebase provides Java-friendly bridges.\n\n### Calling suspend methods from Java\n\nMethods annotated with `@JavaAPI` and `@JvmName` provide blocking wrappers. Use the Java-facing name directly:\n\n| Kotlin (suspend) | Java (blocking) |\n|------------------|-----------------|\n| `agent.run(input)` | `agent.run(input)` (calls `javaNonSuspendRun` via `@JvmName(\"run\")`) |\n| `processor.initialize()` | `processor.initialize()` (calls `javaNonSuspendInitialize` via `@JvmName`) |\n| `processor.onMessage(msg)` | `processor.onMessage(msg)` (calls `javaNonSuspendOnMessage` via `@JvmName`) |\n\nThese wrappers use `runBlockingIfRequired()` internally. Java code calls them like normal blocking methods.\n\n### Overriding suspend methods from Java\n\nWhen a Java class extends a Kotlin class with `suspend` methods to override, look for non-suspend bridge methods annotated with `@JavaAPI`:\n\n| Kotlin override | Java override |\n|-----------------|---------------|\n| `override suspend fun processMessage(msg)` | `void handleMessage(FeatureMessage msg)` |\n| `override suspend fun close()` | `void handleClose()` |\n\n**Why the names differ:** `@JvmName` cannot be used on `open` or `override` methods (Kotlin restriction — it would break polymorphism). So Java-facing override names can diverge from the Kotlin suspend names (`handleMessage` not `processMessage`).\n\n### Abstract properties with Kotlin types\n\nAbstract properties like `val isOpen: StateFlow<Boolean>` leak Kotlin coroutine types to Java. If the base class provides a default `open val` implementation (e.g., via `MutableStateFlow`), Java subclasses don't need to implement it. Check whether the property is `abstract` or `open` before writing the Java example.\n\n### Before writing a Java subclass example\n\n1. Check what `@JavaAPI` bridge methods exist on the class — grep for `@JavaAPI` in the `jvmCommonMain` actual.\n2. Check which members are `abstract` vs `open` — abstract ones MUST be implemented in Java.\n3. Never show `Continuation`, `Object` return types, or `Unit.INSTANCE` in Java — use the bridge methods.\n\n## `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention for boolean-like property names.\n\n## Key source files to grep\n\nWhen writing Java examples that use these classes, check the JVM actuals for available `@JavaAPI` bridge methods:\n\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/agent/AIAgent.kt` — `javaNonSuspendRun` with `@JvmName(\"run\")`.\n- `agents/agents-core/src/jvmCommonMain/kotlin/ai/koog/agents/core/feature/message/FeatureMessageProcessor.kt` — calling bridges (`javaNonSuspendInitialize`, `javaNonSuspendOnMessage`) and overriding bridges (`handleMessage`, `handleClose`).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorBuilder.kt` — Java builder for mock executor: `mockLLMAnswer()`, `mockLLMToolCall()`, `mockTool()` + inner builder classes.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockPromptExecutor.kt` — entry point: `MockPromptExecutor.builder(serializer)` with `@JvmStatic @JavaAPI`.\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/tools/MockExecutorDSLBuilder.kt` — Kotlin DSL builder (reference for understanding what the Java builder delegates to).\n- `agents/agents-test/src/commonMain/kotlin/ai/koog/agents/testing/feature/TestingFeature.kt` — `Testing.Feature` companion object (used in Java as `Testing.Feature`).\n- `agents/agents-core/src/jvmTest/java/ai/koog/agents/core/agent/JavaAPIAgentBuilderJavaTest.java` — reference Java test using `MockPromptExecutor`, `AIAgent.builder()`, and functional strategies.\n\n<!-- File: .claude/skills/add-java-code-snippets-in-docs/reference/pitfalls.md (Tokens: ~1229 | Category: anthropic-skill) -->\n# Pitfalls, testing API, and docs classpath\n\nLoad this file when something fails to compile, or when you're writing a `MockPromptExecutor`-based testing block.\n\n## Common pitfalls\n\n### `var` is only for local variables\n\nJava `var` (type inference) works only for local variables inside methods. For fields, use explicit types:\n\n```java\n// WRONG: static var logger = LoggerFactory.getLogger(\"x\");\n// RIGHT: static Logger logger = LoggerFactory.getLogger(\"x\");\n```\n\n### Inline value classes make constructors private from Java\n\nKotlin classes with `Duration`, `Duration?`, or other inline value class parameters in their constructors generate synthetic constructors with `DefaultConstructorMarker`. These are not callable from Java. Solution: add a JVM `create()` factory — see `factory-classes.md`.\n\n### Import management\n\nAlways add imports to the INCLUDE block. Never use fully-qualified names in visible code:\n\n```java\n// WRONG (in visible code): private static final org.slf4j.Logger logger = ...\n// RIGHT (in INCLUDE):      import org.slf4j.Logger;\n// RIGHT (in visible code): static Logger logger = ...\n```\n\n### `@JvmOverloads` check\n\nBefore calling a Kotlin constructor/method with default parameters from Java, verify it has `@JvmOverloads`. Without it, Java only sees the full-parameter version (or synthetic constructors with `DefaultConstructorMarker`).\n\n### SUFFIX brace counting\n\nCount opening braces in INCLUDE carefully. The SUFFIX must have exactly the matching number of closing braces. Off-by-one errors cause `Syntax error: Expecting a top level declaration` at the end of the generated file. After writing SUFFIX, mentally concatenate INCLUDE + visible code + SUFFIX and verify all braces match.\n\n### `is`-prefixed Kotlin properties\n\nKotlin `val isOpen: StateFlow<Boolean>` generates Java getter `isOpen()` (not `getIsOpen()`). The `is` prefix follows JavaBeans convention.\n\n### Kotlin `expect`/`actual` classes\n\nWhen an `expect abstract class` in `commonMain` has `abstract` members, Java subclasses must implement them. Check the **JVM actual** (in `jvmCommonMain`), not the `expect` declaration — the actual may provide default implementations or bridge methods not visible in the `expect`.\n\n### Legacy `/** **/` placeholder pattern\n\nSome docs have existing Java tabs that wrap code in `/** */` block comments (making the code a Javadoc comment that trivially compiles). This is a legacy pattern — **always replace with real compilable Java code** or explanatory comments for Kotlin-only APIs.\n\n## Testing API: `MockPromptExecutor.builder`\n\nThe testing module (`agents-test`) provides Java-friendly builder APIs via `MockPromptExecutor.builder(serializer)`.\n\n### `MockExecutorBuilder` methods\n\n| Method | Description |\n|--------|-------------|\n| `mockLLMAnswer(text)` | Returns `MockLLMAnswerBuilder` — configure with `.asDefaultResponse()`, `.onRequestContains(pattern)`, `.onRequestEquals(pattern)`, `.onCondition(pred)`. |\n| `mockLLMToolCall(tool, args)` | Returns `MockLLMToolCallBuilder` — configure with `.onRequestEquals(pattern)`, `.onRequestContains(pattern)`, `.onCondition(pred)`. |\n| `mockTool(tool)` | Returns `MockToolBuilder` — configure with `.alwaysReturns(result)`, `.alwaysDoes(action)`, `.returns(result).onArguments(args)`, `.returns(result).onArgumentsMatching(pred)`. |\n| `clock(clock)` | Set custom clock. |\n| `tokenizer(tokenizer)` | Set custom tokenizer. |\n| `handleLastAssistantMessage(bool)` | Configure last-assistant-message handling. |\n| `build()` | Returns `PromptExecutor`. |\n\nAll sub-builders return `MockExecutorBuilder` for method chaining.\n\n### Full Java test pattern\n\n```java\nPromptExecutor mockLLMApi = MockPromptExecutor.builder(new JacksonSerializer())\n    .mockLLMToolCall(SayToUser.INSTANCE, new SayToUser.Args(positiveText))\n        .onRequestEquals(positiveText)\n    .mockLLMAnswer(positiveResponse).onRequestContains(positiveResponse)\n    .mockLLMAnswer(defaultText).asDefaultResponse()\n    .mockTool(SayToUser.INSTANCE).alwaysReturns(positiveResponse)\n    .build();\n\nvar agent = AIAgent.builder()\n    .promptExecutor(mockLLMApi)\n    .llmModel(OpenAIModels.Chat.GPT4o)\n    .install(Testing.Feature, config -> { })\n    .build();\n\nString result = agent.run(positiveText);\n```\n\n### Importing Kotlin objects in Java INCLUDE\n\nWhen Java code needs Kotlin objects defined in a Kotlin example's generated package, import from the generated package:\n\n```java\nimport ai.koog.agents.example.exampleTesting03.*;\n```\n\nThis gives access to tool definitions and other objects declared at the top level of the Kotlin example file.\n\n## Docs compilation environment\n\n### JDK version\n\nThe project requires JDK 17. If the default JDK is newer (e.g., 25), compilation fails with cryptic errors. Use:\n\n```bash\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:knit\nJAVA_HOME=/path/to/jdk-17 ./gradlew :docs:compileJava\n```\n\n### Docs classpath constraints\n\nThe docs module has a limited classpath:\n\n- **JUnit is NOT available.** Do not use `import org.junit.jupiter.api.Assertions.*`. Use Java `assert` statements instead: `assert expected.equals(result) : \"message\";`\n- **JacksonSerializer IS available:** `import ai.koog.serialization.jackson.JacksonSerializer;`\n- **agents-core, agents-test, agents-ext ARE available:** agent builders, mock executors, `SayToUser` tool, etc.\n\nIf compilation fails with \"package X does not exist\", check `docs/build.gradle.kts` for available dependencies.\n\n<!-- File: .claude/skills/add-java-code-snippets-in-docs/SKILL.md (Tokens: ~1717 | Category: anthropic-skill) -->\n---\nname: add-java-code-snippets-in-docs\ndescription: Use when the user asks to add Java tabs/examples to mkdocs docs that already have Kotlin examples (files under docs/docs/**.md using === \"Kotlin\" / Knit INCLUDE+SUFFIX blocks). Handles mkdocs-material tabs, Knit directives, @JavaAPI bridge methods, and JVM factory classes.\nallowed-tools: Read, Edit, Write, Bash, Grep, Glob\n---\n\n## When to use\n\nAdding Java code blocks alongside existing Kotlin blocks in mkdocs documentation (`docs/docs/**.md`). Typical triggers: \"add Java examples to <file>\", \"add Java tabs\", \"complete the Java side of <doc>\", \"fix the Java block in <doc>\".\n\n**Do NOT use this skill for:**\n\n- Writing brand-new Kotlin docs (no precursor Kotlin block to mirror).\n- Splitting source files between JVM and non-JVM source sets — use the **`split-jvm-nonjvm`** skill instead.\n- Java examples outside the mkdocs/Knit pipeline.\n\n## Prerequisites\n\nJDK 17. Gradle 8.x fails cryptically on newer JDKs. If the default JDK is newer, prefix every `./gradlew` command in this workflow with `JAVA_HOME=/path/to/jdk-17`.\n\n## Before starting\n\nRead **one** reference doc to anchor on the established pattern:\n\n- `docs/docs/features/tracing.md` — full Tracing feature (blocks 1–6).\n- `docs/docs/agent-events.md` — custom `FeatureMessageProcessor` subclass (suspend bridge methods `handleMessage` / `handleClose`).\n- `docs/docs/testing.md` — `MockPromptExecutor` builder, tool mocking, comment-only blocks for Kotlin-only DSL.\n- `docs/docs/features/open-telemetry/index.md` — OpenTelemetry examples.\n- `docs/docs/features/open-telemetry/opentelemetry-langfuse-exporter.md` — Langfuse exporter.\n- `docs/docs/features/open-telemetry/opentelemetry-weave-exporter.md` — Weave exporter.\n\n## Workflow\n\nFor each Kotlin code block in the target `.md`:\n\n1. **Read** the Kotlin block: INCLUDE, visible code, SUFFIX, KNIT directive.\n2. **Check existing Java tab.** If absent → add one. If it has empty `/** */` placeholder content → replace entirely. If it has real content → update.\n3. **Consider Kotlin-side alignment.** If the Kotlin block uses Kotlin-only APIs (`kotlinx.io`, `MutableStateFlow`, `KLogger`, inline `Duration`), consider whether the Kotlin example should switch to a JVM `create()` factory (e.g., `TraceFeatureMessageFileWriter.create(path)`). This keeps the two examples structurally aligned. See `reference/factory-classes.md`.\n4. **Add `@JavaAPI` bridge methods if missing.** If the Java code needs a bridge that doesn't exist yet (e.g., a builder method on `MockExecutorBuilder`), add it before writing the Java tab. This may pause the doc work for a source-code change — flag it to the user before proceeding.\n5. **Write the Java tab** immediately after the Kotlin tab, using the template below.\n6. **If renaming Knit files** (e.g., hyphenated `example-feature-java-01.java` → camelCase `exampleFeatureJava01.java`), delete the old generated files first.\n7. **Generate and compile** (see Verification at the bottom).\n8. **Read the generated `.java`** to confirm correctness. Fix and repeat 7–8 on failure.\n\n## Mkdocs / Knit tab structure\n\nJava tabs go immediately after the Kotlin tab. All content inside a tab is indented 4 spaces. `<!--- INCLUDE -->`, `<!--- SUFFIX -->`, `<!--- KNIT -->` are Knit directives (HTML comments — invisible when rendered), not mkdocs syntax.\n\n```markdown\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ...\n    -->\n    ```kotlin\n    // visible code\n    ```\n    <!--- KNIT example-feature-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ...;\n    public class exampleFeatureJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // visible code\n    ```\n    <!--- KNIT exampleFeatureJava01.java -->\n```\n\n### KNIT naming — capital \"Java\" matters\n\n`docs/knit.code.include` has the check `<#if !knit.name?contains(\"Java\")>package ...`. Files with capital \"Java\" in the name skip the `package` declaration; lowercase \"java\" or hyphenated names emit a `package` line that breaks compilation.\n\n- ✅ `exampleFeatureJava01.java`, `exampleTestingJava13.java`\n- ❌ `example-feature-java-01.java`, `examplefeaturejava01.java`\n\n### Knit rules\n\n- INCLUDE + visible code + SUFFIX must concatenate into a valid Java file.\n- Hidden variables in Kotlin INCLUDE (`outputPath`, `input`, etc.) must also appear in Java INCLUDE.\n- Visible-code indentation in `.md` is 4 spaces (tab content). Knit strips that when generating.\n- Always import in INCLUDE; never use FQNs in visible code.\n- Never use file-level annotations (`@file:Suppress`, `@file:OptIn`) — scope to specific declarations.\n\n## Structural matching (the most important rule)\n\n**The Java visible code must match the Kotlin visible code in scope and structure.**\n\n- If Kotlin shows no `main()`, Java must hide `main()` in INCLUDE/SUFFIX.\n- If Kotlin shows `install(Feature) { ... }`, Java must show `.install(Feature.Feature, config -> { })`.\n- If Kotlin creates a writer before the agent, Java must too.\n- If Kotlin shows `agent.run(input)`, Java must too.\n\nRead both visible blocks side-by-side after writing. They should read as equivalent code in their respective languages.\n\n### When Kotlin uses APIs with no Java bridge\n\nSome Kotlin DSLs (graph testing: `testGraph`, `assertSubgraphByName`, `assertNodes`, `assertEdges`, `verifySubgraph`, `assertReachable`) have no `@JavaAPI` bridges. To check: grep for `@JavaAPI` in the source file — if absent, treat as Kotlin-only.\n\nWhen the Kotlin block uses an entirely Kotlin-only API:\n\n1. **Do NOT leave the Java tab empty.** Write a compilable Java class with explanatory comments.\n2. The class must have a valid `main` so it compiles.\n3. Comments explain what the Kotlin DSL does and the recommended Java alternative.\n\n```java\n// Graph structure testing (testGraph, assertSubgraphByName, assertEdges,\n// verifySubgraph, assertReachable) is available through the Kotlin testing DSL.\n//\n// In Java, test agent behavior by running and asserting on the result:\n//   String result = agent.run(\"test input\");\n//   assert \"expected\".equals(result);\n```\n\n## Deep references\n\nLoad these only when you need them:\n\n- **`reference/interop-tables.md`** — Kotlin↔Java mapping table, suspend bridges (`@JvmName(\"run\")`, `handleMessage`, `handleClose`), abstract-property rules, key source files to grep for `@JavaAPI`.\n- **`reference/factory-classes.md`** — JVM `create()` factory pattern, two-overload skeleton, type-conversion cheat sheet, when needed vs not, anti-patterns. Cross-references the `split-jvm-nonjvm` skill.\n- **`reference/pitfalls.md`** — `var`-vs-field, brace-counting in SUFFIX, `is`-prefixed property getters, `@JvmOverloads` requirement, docs classpath limits (no JUnit), legacy `/** */` placeholder, `MockExecutorBuilder` API.\n\n## Verification\n\nAfter each Java block:\n\n```bash\n./gradlew :docs:knit\n./gradlew :docs:compileJava\n```\n\nOn failure, read the generated file to diagnose:\n\n```bash\ncat docs/src/main/kotlin/exampleFeatureJavaNN.java\n```\n\n<!-- File: .claude/skills/split-jvm-nonjvm/SKILL.md (Tokens: ~1808 | Category: anthropic-skill) -->\n---\nname: split-jvm-nonjvm\ndescription: |\n    Splits a class or several classes in the common source set commonMain into JVM and non-JVM parts into jvmCommonMain and nonJvmCommonMain. \n    This is used to provide some JVM-specific functionality for a class on JVM targets (e.g. Kotlin/JVM or Java backend or Android).\n---\n\nThe following example shows how to split a class into JVM and non-JVM parts.\nWhen using this skill, avoid unnecessarily looking at existing implementations of the split in the project, just follow this instruction.\nYou can, however, check existing implementations for reference if you have a complex case or otherwise stuck.\n\nHere's a sample class in a `commonMain` source set\n```kt\npackage com.example\n\n/**\n* A sample class with an external dependency, some logic and public methods\n * @param name Some public property\n * @param myRepo Some external dependency, private property\n*/\npublic class MyClass(\n    public val name: String,\n    private val myRepo: MyRepo,\n) {\n    /**\n     * Public API method\n     */\n    public fun doSomething(): String {\n        return fetchData()\n    }\n    \n    /**\n     * Private implementation logic\n     */\n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    /*\n     * Internal method\n     */\n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nIn the same package as `MyClass` in `commonMain`, create the following files. The original `MyClass.kt` file will be overwritten with the expect class (see step three).\n\nFirst, the API interface describing the contract. It should contain only public symbols from the `MyClass`.\nTake KDocs from the respective symbols in `MyClass` and add them to the interface.\n\n```kt\npackage com.example\n\n/**\n * API for [MyClass]\n */\npublic interface MyClassAPI {\n    /**\n     * Some public property\n     */\n    public val name: String\n    \n    /**\n     * Public API method\n     */\n    public fun doSomething(): String\n}\n```\n\nSecond, `MyClassImpl.kt` — implementation of the API interface providing the implementation logic. The filename matches the class name.\n\n```kt\ninternal class MyClassImpl(\n    override val name: String,\n    private val myRepo: MyRepo,\n) : MyClassAPI {\n    override fun doSomething(): String {\n        return fetchData()\n    }\n    \n    private fun fetchData(): String {\n        return myRepo.getData()\n    }\n    \n    internal fun doInternal(): String {\n        return \"Internal\"\n    }\n}\n```\n\nThird, the original class becomes `expect` class\n`MyClass` should extend `MyClassAPI` and override all public symbols from `MyClassAPI`, without method bodies and property values, since `expect` classes can't have any.\nIf `MyClass` had any internal symbols, these should also be listed in the `expect` class along with their KDocs.\nIf `MyClass` had a KDoc, add it on top of the expect class without parameters/properties description.\nIf `MyClass` original KDoc described certain parameters/properties that it accepted in its constructor, add these to KDoc of the expect class secondary constructor.\nThis constructor KDoc can't have `@property` tag for parameters, use only `@param` for all parameters, even if the original was `@property`.\n\n```kt\npackage com.example\n\n/**\n * A sample class with an external dependency, some logic and public methods\n */\npublic expect class MyClass internal constructor(\n    delegate: MyClassImpl,\n) : MyClassAPI {\n    /**\n     * Constructs a new instance of [MyClass]\n     * \n     * @param name Some public property\n     * @param myRepo Some external dependency,\n     */\n    public constructor(\n        name: String,\n        myRepo: MyRepo,\n    )\n    \n    internal val delegate: MyClassImpl\n    \n    override val name: String\n    override fun doSomething(): String\n    \n    /**\n     * Internal method\n     */\n    internal fun doInternal(): String\n}\n```\n\nFourth, ensure that `jvmCommonMain` and `nonJvmCommonMain` source sets have a package directory for `MyClass`, i.e., `com.example`. If not, create missing package directories.\nThe source sets themselves are already declared in the Gradle convention plugin — you do NOT need to add them to the module's `build.gradle.kts` unless you need to add dependencies specifically for these source sets.\nThen, create `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` source sets.\n\nAfter these steps, the structure should look as follows, omitting irrelevant packages and files:\n```\ncommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n            MyClassAPI.kt\n            MyClassImpl.kt\njvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\nnonJvmCommonMain/\n    kotlin/\n        com/example/\n            MyClass.kt\n```\n\nFifth, in `MyClass.kt` files in `jvmCommonMain` and `nonJvmCommonMain` create `actual` declarations for `MyClass`.\nInitially, they should be identical for both source sets.\nIt should use `by delegate` to delegate all public symbols from `MyClassAPI` to the implementation, and direct delegation to delegate internal symbols.\n\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    internal actual fun doInternal(): String = delegate.doInternal()\n}\n```\n\nSixth, optional, step.\nIf the users asked for certain JVM-specific/non-JVM-specific functionality, these actual classes might contain additional \npublic symbols not present in the API interface. Such additional public symbols should be documented properly and concisely with KDoc\n\nFor example, `jvmCommonMain` implementation that uses JVM-specific API\n```kt\n// Suppress IntelliJ warnings\n@file:Suppress(\"MissingKDocForPublicAPI\")\n\npackage com.example\n\npublic actual class MyClass internal actual constructor(\n    internal actual val delegate: MyClassImpl,\n) : MyClassAPI by delegate {\n    public actual constructor(\n        name: String,\n        myRepo: MyRepo,\n    ) : this(\n        delegate = MyClassImpl(name, myRepo)\n    )\n    \n    /**\n     * Gets environment variable value by [key]\n     */\n    public fun getEnv(key: String): String = System.getenv(key)\n}\n```\n\nSeventh, if `MyClass` had any tests in the original `commonTest` source set, use `MyClassImpl` in the test to verify common implementation.\nDo not rename the test, it should still be called e.g. `MyClassTest`.\nIf you need to test some platform-specific functionality, create tests in `jvmCommonTest` and/or `nonJvmCommonTest` source sets.\nKotlin tests for JVM-specific functionality should go to `jvmCommonTest`.\nIMPORTANT: if the test suite is written in Java, it MUST go to `jvmTest`, not `jvmCommonTest`. KMP only compiles Java code in `jvmMain`/`jvmTest` source sets.\n\nFinally, ensure that the implementation compiles by running:\n```sh\n./gradlew :mymodule:compileKotlinJvm\n./gradlew :mymodule:compileKotlinJs\n```\nReplace `:mymodule` with the actual Gradle module path derived from the file location (e.g., `agents/agents-core` → `:agents:agents-core`).\nIf both pass, the split is working correctly.\n\n### IntelliJ false positives\nDue to the experimental state of expect/actual classifiers, IntelliJ may report false positive errors such as \"no actual for expect\" or \"no expect for actual\". \nThese diagnostics should NOT be trusted. The only reliable verification is running the Gradle compile tasks listed above.\n\n## Important notes\n\n### Imports\nWhen creating the new files (API interface, Impl, expect class, actual classes), carry over all relevant imports from the original class.\n\n### Annotations\nClasses being split should not have annotations. If the original class has annotations (e.g., `@Serializable`, `@Deprecated`), ask the user explicitly how to handle them before proceeding.\n\n### Companion objects\nClasses being split should not have companion objects. If the original class has a companion object, ask the user explicitly how to handle it before proceeding.\n\n<!-- File: docs/docs/tools/annotation-based-tools.md (Tokens: ~4298 | Category: anthropic-skill) -->\n# Annotation-based tools\n\nAnnotation-based tools provide a declarative way to expose functions and methods as tools for large language models (LLMs) in both Kotlin and Java.\nBy using annotations, you can transform any function or method into a tool that LLMs can understand and use.\n\nThis approach is useful when you need to expose existing functionality to LLMs in Kotlin or Java without implementing tool descriptions manually.\n\n!!! note\n    Annotation-based tools are JVM-only and not available for other platforms. For multiplatform support, use the [class-based tool API](class-based-tools.md).\n\n## Key annotations\n\nTo start using annotation-based tools in your project, you need to understand the following key annotations:\n\n| Annotation        | Description                                                             |\n|-------------------|-------------------------------------------------------------------------|\n| `@Tool`           | Marks functions that should be exposed as tools to LLMs.                |\n| `@LLMDescription` | Provides descriptive information about your tools and their components. |\n\n\n## @Tool annotation\n\nThe `@Tool` annotation is used to mark functions (Kotlin) or methods (Java) that should be exposed as tools to LLMs.\nThe functions and methods annotated with `@Tool` are collected by reflection from objects that implement the `ToolSet` interface. For details, see [Implement the ToolSet interface](#1-implement-the-toolset-interface).\n\n### Definition\n\n```kotlin\n@Target(AnnotationTarget.FUNCTION)\npublic annotation class Tool(val customName: String = \"\")\n```\n<!--- KNIT example-annotation-based-tools-01.txt -->\n\n### Parameters\n\n| <div style=\"width:100px\">Name</div> | Required | Description                                                                              |\n|-------------------------------------|----------|------------------------------------------------------------------------------------------|\n| `customName`                        | No       | Specifies a custom name for the tool. If not provided, the name of the function is used. |\n\n### Usage\n\nTo mark a function or method as a tool, apply the `@Tool` annotation to this function or method in a class that implements the `ToolSet` interface:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    \n        @Tool(customName = \"customToolName\")\n        fun anotherTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyToolSet implements ToolSet {\n        @Tool\n        public String myTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    \n        @Tool(customName = \"customToolName\")\n        public String anotherTool() {\n            // Tool implementation\n            return \"Result\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-01.java -->\n\n## @LLMDescription annotation\n\nThe `@LLMDescription` annotation provides descriptive information about code elements (classes, functions, methods, parameters, and so on) to LLMs.\nThis helps LLMs understand the purpose and usage of these elements.\n\n### Definition\n\n```kotlin\n@Target(\n    AnnotationTarget.PROPERTY,\n    AnnotationTarget.CLASS,\n    AnnotationTarget.TYPE,\n    AnnotationTarget.VALUE_PARAMETER,\n    AnnotationTarget.FUNCTION\n)\npublic annotation class LLMDescription(val description: String)\n```\n<!--- KNIT example-annotation-based-tools-02.txt -->\n\n### Parameters\n\n| Name          | Required | Description                                    |\n|---------------|----------|------------------------------------------------|\n| `description` | Yes      | A string that describes the annotated element. |\n\n### Usage\n\nThe `@LLMDescription` annotation can be applied at various levels. For example:\n\n* Function level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Performs a specific operation and returns the result\")\n    fun myTool(): String {\n        // Function implementation\n        return \"Result\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-02.kt -->\n\n=== \"Java\"\n\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Performs a specific operation and returns the result\")\n    public String myTool() {\n        // Function implementation\n        return \"Result\";\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-02.java -->\n\n    \n* Parameter level:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Processes input data\")\n    fun processTool(\n        @LLMDescription(\"The input data to process\")\n        input: String,\n    \n        @LLMDescription(\"Optional configuration parameters\")\n        config: String = \"\"\n    ): String {\n        // Function implementation\n        return \"Processed: $input with config: $config\"\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @Tool\n    @LLMDescription(description = \"Processes input data\")\n    public String processTool(\n            @LLMDescription(description = \"The input data to process\") String input,\n            @LLMDescription(description = \"Optional configuration parameters\") String config\n    ) {\n        // Function implementation\n        return \"Processed: \" + input + \" with config: \" + config;\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-03.java -->\n\n\n## Creating a tool\n\n### 1. Implement the ToolSet interface\n\nCreate a class that implements the [`ToolSet`](api:agents-tools::ai.koog.agents.core.tools.reflect.ToolSet) interface.\nThis interface marks your class as a container for tools.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        // Tools will go here\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-04.java -->\n\n### 2. Add tool functions\n\nAdd functions or methods to your class and annotate them with `@Tool` to expose them as tools:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        fun getWeather(location: String): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        public String getWeather(String location) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-05.java -->\n\n### 3. Add descriptions\n\nAdd `@LLMDescription` annotations to provide context for the LLM:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for getting weather information\")\n    class MyFirstToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Get the current weather for a location\")\n        fun getWeather(\n            @LLMDescription(\"The city and state/country\")\n            location: String\n        ): String {\n            // In a real implementation, you would call a weather API\n            return \"The weather in $location is sunny and 72°F\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for getting weather information\")\n    public class MyFirstToolSet implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Get the current weather for a location\")\n        public String getWeather(\n                @LLMDescription(description = \"The city and state/country\") String location\n        ) {\n            // In a real implementation, you would call a weather API\n            return \"The weather in \" + location + \" is sunny and 72°F\";\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-06.java -->\n\n### 4. Use your tools with an agent\n\nNow you can use your tools with an agent:\n\n=== \"Kotlin\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleAnnotationBasedTools06.MyFirstToolSet\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    const val apiToken = \"\"\n    -->\n    ```kotlin\n    fun main() {\n        runBlocking {\n            // Create your tool set\n            val weatherTools = MyFirstToolSet()\n    \n            // Create an agent with your tools\n    \n            val agent = AIAgent(\n                promptExecutor = simpleOpenAIExecutor(apiToken),\n                systemPrompt = \"Provide weather information for a given location.\",\n                llmModel = OpenAIModels.Chat.GPT4o,\n                toolRegistry = ToolRegistry {\n                    tools(weatherTools)\n                }\n            )\n    \n            // The agent can now use your weather tools\n            agent.run(\"What's the weather like in New York?\")\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    String apiToken = System.getenv(\"OPENAI_API_KEY\");\n\n    // Create your tool set\n     MyFirstToolSet weatherTools = new MyFirstToolSet();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(weatherTools)\n        .build();\n\n    // Create an agent with your tools\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"Provide weather information for a given location.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(toolRegistry)\n        .build();\n\n    // The agent can now use your weather tools\n    String result = agent.run(\"What's the weather like in New York?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT example-annotation-based-tools-java-07.java -->\n\n## Usage examples\n\nHere are some real-world examples of tool annotations.\n\n### Basic example: Switch controller\n\nThis example shows a simple tool set for controlling a switch:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    class Switch(private var state: Boolean) {\n        fun switch(state: Boolean) {\n            this.state = state\n        }\n        fun isOn(): Boolean {\n            return state\n        }\n    }\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for controlling a switch\")\n    class SwitchTools(val switch: Switch) : ToolSet {\n        @Tool\n        @LLMDescription(\"Switches the state of the switch\")\n        fun switch(\n            @LLMDescription(\"The state to set (true for on, false for off)\")\n            state: Boolean\n        ): String {\n            switch.switch(state)\n            return \"Switched to ${if (state) \"on\" else \"off\"}\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Returns the current state of the switch\")\n        fun switchState(): String {\n            return \"Switch is ${if (switch.isOn()) \"on\" else \"off\"}\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-08.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public class Switch {\n        private boolean state;\n\n        public Switch(boolean state) {\n            this.state = state;\n        }\n\n        // \"switch\" is a reserved keyword in Java, so we use a different method name\n        public void setState(boolean state) {\n            this.state = state;\n        }\n\n        public boolean isOn() {\n            return state;\n        }\n    }\n    \n    @LLMDescription(description = \"Tools for controlling a switch\")\n    public class SwitchTools implements ToolSet {\n        private final Switch sw;\n\n        public SwitchTools(Switch sw) {\n            this.sw = sw;\n        }\n\n        @Tool\n        @LLMDescription(description = \"Switches the state of the switch\")\n        public String switchStateTo(\n                @LLMDescription(description = \"The state to set (true for on, false for off)\") boolean state\n        ) {\n            sw.setState(state);\n            return \"Switched to \" + (state ? \"on\" : \"off\");\n        }\n\n        @Tool\n        @LLMDescription(description = \"Returns the current state of the switch\")\n        public String switchState() {\n            return \"Switch is \" + (sw.isOn() ? \"on\" : \"off\");\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-08.java -->\n\nWhen an LLM needs to control a switch, it can understand the following information from the provided description:\n\n- The purpose and functionality of the tools.\n- The required parameters for using the tools.\n- The acceptable values for each parameter.\n- The expected return values upon execution.\n\n### Advanced example: Diagnostic tools\n\nThis example shows a more complex tool set for device diagnostics:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing diagnostics and troubleshooting on devices\")\n    class DiagnosticToolSet : ToolSet {\n        @Tool\n        @LLMDescription(\"Run diagnostic on a device to check its status and identify any issues\")\n        fun runDiagnostic(\n            @LLMDescription(\"The ID of the device to diagnose\")\n            deviceId: String,\n    \n            @LLMDescription(\"Additional information for the diagnostic (optional)\")\n            additionalInfo: String = \"\"\n        ): String {\n            // Implementation\n            return \"Diagnostic results for device $deviceId\"\n        }\n    \n        @Tool\n        @LLMDescription(\"Analyze an error code to determine its meaning and possible solutions\")\n        fun analyzeError(\n            @LLMDescription(\"The error code to analyze (e.g., 'E1001')\")\n            errorCode: String\n        ): String {\n            // Implementation\n            return \"Analysis of error code $errorCode\"\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-09.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing diagnostics and troubleshooting on devices\")\n    public class DiagnosticToolSet implements ToolSet {\n        // Convenience overload (not exposed as a tool)\n        public String runDiagnostic(String deviceId) {\n            return runDiagnostic(deviceId, \"\");\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Run diagnostic on a device to check its status and identify any issues\")\n        public String runDiagnostic(\n                @LLMDescription(description = \"The ID of the device to diagnose\") String deviceId,\n                @LLMDescription(description = \"Additional information for the diagnostic (optional)\") String additionalInfo\n        ) {\n            // Implementation\n            return \"Diagnostic results for device \" + deviceId;\n        }\n    \n        @Tool\n        @LLMDescription(description = \"Analyze an error code to determine its meaning and possible solutions\")\n        public String analyzeError(\n                @LLMDescription(description = \"The error code to analyze (e.g., 'E1001')\") String errorCode\n        ) {\n            // Implementation\n            return \"Analysis of error code \" + errorCode;\n        }\n    }\n    ```\n    <!--- KNIT example-annotation-based-tools-java-09.java -->\n\n\n## Best practices\n\n* **Provide clear descriptions**: write clear, concise descriptions that explain the purpose and behavior of tools, parameters, and return values.\n* **Describe all parameters**: add `@LLMDescription` to all parameters to help LLMs understand what each parameter is for.\n* **Use consistent naming**: use consistent naming conventions for tools and parameters to make them more intuitive.\n* **Group related tools**: group related tools in the same `ToolSet` implementation and provide a class-level description.\n* **Return informative results**: make sure tool return values provide clear information about the result of the operation.\n* **Handle errors gracefully**: include error handling in your tools and return informative error messages.\n* **Document default values**: when parameters have default values (Kotlin) or overloads (Java), document this in the description.\n* **Keep tools focused**: Each tool should perform a specific, well-defined task rather than trying to do too many things.\n\n## Troubleshooting common issues\n\nWhen working with tool annotations, you might encounter some common issues.\n\n### Tools not being recognized\n\nIf the agent does not recognize your tools, check the following:\n\n- Your class implements the `ToolSet` interface.\n- All tool functions or methods are annotated with `@Tool`.\n- Tool functions or methods have appropriate return types (`String` is recommended for simplicity).\n- Your tools are properly registered with the agent.\n\n### Unclear tool descriptions\n\nIf the LLM does not use your tools correctly or misunderstands their purpose, try the following:\n\n- Use primitive parameter types when possible (`String`, `Boolean`, `Int` in Kotlin, or `String`, `boolean`, `int` in Java).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n- Note that Java doesn't support default parameters. Use method overloading instead.\n\n### Parameter type issues\n\nIf the LLM provides incorrect parameter types, try the following:\n\n- Use simple parameter types when possible (`String`, `Boolean`, `Int`).\n- Clearly describe the expected format in the parameter description.\n- For complex types, consider using `String` parameters with a specific format and parse them in your tool.\n- Include examples of valid inputs in your parameter descriptions.\n\n### Performance issues\n\nIf your tools cause performance problems, try the following:\n\n- Keep tool implementations lightweight.\n- For resource-intensive operations, consider implementing asynchronous processing.\n- Cache results when appropriate.\n- Log tool usage to identify bottlenecks.\n\n<!-- File: docs/docs/tools/built-in-tools.md (Tokens: ~741 | Category: anthropic-skill) -->\n# Built-in tools\n\nKoog provides built-in tools for Kotlin and Java to help you quickly prototype and experiment with agent-user interaction.\nThese tools are not intended for production use. To use them, add `ai.koog:agents-ext` to your dependencies.\nThe following built-in tools are available:\n\n| Tool              | <div style=\"width:115px\">Name</div> | Description                                                                                                              |\n|-------------------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| SayToUser         | `__say_to_user__`                   | Lets the agent send a message to the user. It prints the agent message to the console with the `Agent says: ` prefix.    |\n| AskUser           | `__ask_user__`                      | Lets the agent ask the user for input. It prints the agent message to the console and waits for user response.           |\n| ExitTool          | `__exit__`                          | Lets the agent finish the conversation and terminate the session.                                                        |\n| ReadFileTool      | `__read_file__`                     | Reads text file with optional line range selection. Returns formatted content with metadata using 0-based line indexing. |\n| EditFileTool      | `__edit_file__`                     | Makes a single, targeted text replacement in a file; can also create new files or fully replace contents.                |\n| ListDirectoryTool | `__list_directory__`                | Lists directory contents as a hierarchical tree with optional depth control and glob filtering.                          |\n| WriteFileTool     | `__write_file__`                    | Writes text content to a file (creating parent directories if needed).                                                   |\n\n\n## Registering built-in tools\n\nLike any other tool, a built-in tool must be added to the tool registry to become available for an agent. Here is an example:\n\n<!--- INCLUDE\nimport ai.koog.agents.core.agent.AIAgent\nimport ai.koog.agents.core.tools.ToolRegistry\nimport ai.koog.agents.ext.tool.SayToUser\nimport ai.koog.agents.ext.tool.AskUser\nimport ai.koog.agents.ext.tool.ExitTool\nimport ai.koog.agents.ext.tool.file.ListDirectoryTool\nimport ai.koog.agents.ext.tool.file.ReadFileTool\nimport ai.koog.agents.ext.tool.file.WriteFileTool\nimport ai.koog.prompt.executor.clients.openai.OpenAIModels\nimport ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\nimport ai.koog.rag.base.files.JVMFileSystemProvider\n\nconst val apiToken = \"\"\n\n-->\n```kotlin\n// Create a tool registry with all built-in tools\nval toolRegistry = ToolRegistry {\n    tool(SayToUser)\n    tool(AskUser)\n    tool(ExitTool)\n    tool(ReadFileTool(JVMFileSystemProvider.ReadOnly))\n    tool(ListDirectoryTool(JVMFileSystemProvider.ReadOnly))\n    tool(WriteFileTool(JVMFileSystemProvider.ReadWrite))\n}\n\n// Pass the registry when creating an agent\nval agent = AIAgent(\n    promptExecutor = simpleOpenAIExecutor(apiToken),\n    systemPrompt = \"You are a helpful assistant.\",\n    llmModel = OpenAIModels.Chat.GPT4o,\n    toolRegistry = toolRegistry\n)\n\n```\n<!--- KNIT example-built-in-tools-01.kt -->\n\nYou can create a comprehensive set of capabilities for your agent by combining built-in tools and custom tools within the same registry in both Kotlin and Java.\nTo learn more about custom tools, see [Annotation-based tools](annotation-based-tools.md) and [Class-based tools](class-based-tools.md).\n\n<!-- File: docs/docs/tools/class-based-tools.md (Tokens: ~4963 | Category: anthropic-skill) -->\n# Class-based tools\n\nThis section explains the API designed for scenarios that require enhanced flexibility and customized behavior.\nWith this approach in Kotlin, you have full control over a tool, including its parameters, metadata, execution logic, and how it is registered and invoked. In Java, tools are created using annotation-based methods with reflection-based registration.\n\nThis level of control is ideal for creating sophisticated tools that extend basic use cases, enabling seamless integration into agent sessions and workflows.\n\nThis page describes how to implement a tool in both Kotlin and Java, manage tools through registries, call them, and use within node-based agent architectures.\n\n!!! note\n    The API is multiplatform for Kotlin. Java tools are implemented using annotation-based methods and registered via reflection. This lets you use the same tools across different platforms in Kotlin, while Java provides full JVM interoperability.\n\n## Tool implementation\n\nThe Koog framework provides the following approaches for implementing tools:\n\nFor Kotlin:\n\n* Using the base class `Tool` for all tools. You should use this class when you need to return non-text results or require complete control over the tool behavior.\n* Using the `SimpleTool` class that extends the base `Tool` class and simplifies the creation of tools that return text results. You should use this approach for scenarios where the \n  tool only needs to return a text.\n\nBoth approaches use the same core components but differ in implementation and the results they return.\n\nFor Java:\n\n* Using annotation-based methods (`@Tool` and `@LLMDescription`) with reflection-based registration. This is the recommended approach for Java interoperability, as subclassing Kotlin's `Tool` or `SimpleTool` from Java is not supported due to suspend function limitations.\n\n### Tool class (Kotlin)\n\nThe [`Tool<Args, Result>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html) abstract class is the base class for creating tools in Kotlin.\nIt lets you create tools that accept specific argument types (`Args`) and return results of various types (`Result`).\n\nEach tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the tool.                                                                                                                                                                                                                                                                                                                                                             |\n| `Result`                                 | The serializable type of result that the tool returns. If you want to present tool results in a custom format, please inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) class and implement `textForLLM(): String` method                                                                                                           |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are deserialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                                                                                                                                                        |\n| `resultSerializer`                       | The overridden variable that defines how the result of the tool is deserialized. See also [resultSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/result-serializer.html). If you chose to inherit [ToolResult.TextSerializable](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-result/-text-serializable/index.html) consider using `ToolResultUtils.toTextSerializer()` |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/>- `optionalParameters` (empty by default)<br/>See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html).                                                                                                                                |\n| `execute()`                              | The function that implements the logic of the tool. It takes arguments of type `Args` and returns a result of type `Result`. See also [execute()]().                                                                                                                                                                                                                                                                                  |\n\n!!! note \"Java Implementation\"\n    In Java, instead of subclassing `Tool<Args, Result>`, use annotation-based methods with `@Tool` and `@LLMDescription`. The framework handles serialization and registration automatically through reflection. For more\n    details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` property; in Java, use `@LLMDescription` annotations.\n\n#### Usage example\n\nHere is an example of a custom tool implementation using the `Tool` class that returns a numeric result:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    -->\n    ```kotlin\n    // Implement a simple calculator tool that adds two digits\n    object CalculatorTool : Tool<CalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"calculator\",\n        description = \"A simple calculator that can add two digits (0-9).\"\n    ) {\n\n        // Arguments for the calculator tool\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        ) {\n            init {\n                require(digit1 in 0..9) { \"digit1 must be a single digit (0-9)\" }\n                require(digit2 in 0..9) { \"digit2 must be a single digit (0-9)\" }\n            }\n        }\n\n        // Function to add two digits\n        override suspend fun execute(args: Args): Int = args.digit1 + args.digit2\n    }\n    ```\n    <!--- KNIT example-class-based-tools-01.kt -->\n\nAfter implementing your tool, you need to add it to a tool registry and then use it with an agent. For details, see [Tool registry](../tools/index.md#tool-registry).\n\nFor more details, see [API reference](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/index.html).\n\n#### Reading the agent context from a tool\n\nTools that need the agent's full state (LLM context, run id, configuration, storage, ...) extend `AgentContextAwareTool<Args, Result>` instead of `Tool<Args, Result>`. The framework injects the live `AIAgentContext` driving the call, and the tool receives it as a typed parameter rather than reading it out of the argument schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.context.AIAgentContext\n    import ai.koog.agents.core.agent.tools.AgentContextAwareTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // A tool that reads the live AIAgentContext driving the call.\n    object TracingCalculatorTool : AgentContextAwareTool<TracingCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"tracing_calculator\",\n        description = \"Adds two digits and emits a log line tagged with the agent run id.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, context: AIAgentContext): Int {\n            val runId = context.runId\n            // ... use runId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-01.kt -->\n\n`AgentContextAwareTool` is dispatched by the framework via a per-call `ToolCallMetadata` side channel that the framework manages on the tool's behalf. Invoking such a tool outside an agent run throws `IllegalStateException` because no `AIAgentContext` was injected; production code should always go through `ContextualAgentEnvironment`, and unit tests can supply the context explicitly via `ToolCallMetadata.of(AgentContextAwareTool.AgentContextKey to context)`.\n\n#### Reading raw per-call metadata\n\nA small number of tools want to read caller- or feature-contributed entries that are *not* the agent context (for example a distributed-tracing span id contributed by an observability feature). These tools extend `ToolBase<Args, Result>` directly, which exposes the full `ToolCallMetadata` bag:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolBase\n    import ai.koog.agents.core.tools.ToolCallMetadata\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    object SpanAwareCalculatorTool : ToolBase<SpanAwareCalculatorTool.Args, Int>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Int>(),\n        name = \"span_aware_calculator\",\n        description = \"Adds two digits, propagating a tracing span id from caller or feature metadata.\"\n    ) {\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"The first digit to add (0-9)\")\n            val digit1: Int,\n            @property:LLMDescription(\"The second digit to add (0-9)\")\n            val digit2: Int\n        )\n\n        override suspend fun execute(args: Args, metadata: ToolCallMetadata): Int {\n            val traceSpanId = metadata[\"trace.span.id\"] as? String\n            // ... use traceSpanId for cross-cutting context (logging, tracing, correlation)\n            return args.digit1 + args.digit2\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-metadata-02.kt -->\n\nCallers can pass metadata through `SafeTool.execute(args, serializer, metadata)` or directly through `AIAgentEnvironment.executeTool(toolCall, metadata)`. Features can contribute metadata for every tool call during installation by calling `pipeline.provideToolCallMetadata(this) { eventContext -> mapOf(...) }`. Caller-supplied metadata always wins over feature contributions on key collision.\n\nExisting tools that extend `Tool<Args, Result>` and override `execute(args)` continue to work unchanged: the framework dispatches them through the same path and discards any `ToolCallMetadata`. To opt in to metadata, switch to `AgentContextAwareTool` (typed context access) or `ToolBase` (raw bag access).\n\n### SimpleTool class (Kotlin)\n\nThe [`SimpleTool<Args>`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/index.html) abstract class extends `Tool<Args, ToolResult.Text>` and simplifies the creation of tools that return text results.\n\nEach simple tool consists of the following components:\n\n| <div style=\"width:110px\">Component</div> | Description                                                                                                                                                                                                                                                                                              |\n|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Args`                                   | The serializable data class that defines arguments required for the custom tool.                                                                                                                                                                                                                         |\n| `argsSerializer`                         | The overridden variable that defines how the arguments for the tool are serialized. See also [argsSerializer](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/args-serializer.html).                                                                                             |\n| `descriptor`                             | The overridden variable that specifies tool metadata:<br/>- `name`<br/>- `description`<br/>- `requiredParameters` (empty by default)<br/> - `optionalParameters` (empty by default)<br/> See also [descriptor](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool/descriptor.html). |\n| `doExecute()`                            | The overridden function that describes the main action performed by the tool. It takes arguments of type `Args` and returns a `String`. See also [doExecute()](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-simple-tool/do-execute.html).                                          |\n\n!!! note \"Java Implementation\"\n    In Java, the equivalent approach is to use annotation-based methods that return `String`. The framework automatically handles the text result wrapping. For more details, see [Annotation-based methods](#annotation-based-methods-java) below.\n\n!!! tip\n    Ensure your tools have clear descriptions and well-defined parameter names to make it easier for the LLM to understand and use them properly. In Kotlin, use the `descriptor` and constructor parameters; in Java, use `@Tool` and `@LLMDescription` annotations.\n\n#### Usage example \n\nHere is an example of a custom tool implementation using `SimpleTool` in Kotlin:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.SimpleTool\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    // Create a tool that casts a string expression to a double value\n    object CastToDoubleTool : SimpleTool<CastToDoubleTool.Args>(\n        argsType = typeToken<Args>(),\n        name = \"cast_to_double\",\n        description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        data class Args(\n            @property:LLMDescription(\"An expression to case to double\")\n            val expression: String,\n            @property:LLMDescription(\"A comment on how to process the expression\")\n            val comment: String\n        )\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): String {\n            return \"Result: ${castToDouble(args.expression)}, \" + \"the comment was: ${args.comment}\"\n        }\n\n        // Function to cast a string expression to a double value\n        private fun castToDouble(expression: String): Double {\n            return expression.toDoubleOrNull() ?: 0.0\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-02.kt -->\n\n### Annotation-based methods (Java)\n\nTo implement tools in Java, instead of subclassing `Tool` or `SimpleTool`, use annotation-based methods with `@Tool` and\n`@LLMDescription`. Koog handles serialization and registration automatically through reflection. To learn more about the\nimplementation, see Java examples below.\n\n#### Usage examples\n\nThis is an example of a tool implementation in Java, equivalent to using the `Tool` class in Kotlin.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent: implement the tool as a Java method and register it via ToolRegistry.builder().\n    // This is the recommended Java interop path instead of subclassing the Kotlin Tool base class.\n    public final class CalculatorTool {\n        private CalculatorTool() {}\n    \n        @Tool(customName = \"calculator\")\n        @LLMDescription(description = \"A simple calculator that can add two digits (0-9).\")\n        public static int calculator(\n                @LLMDescription(description = \"The first digit to add (0-9)\") int digit1,\n                @LLMDescription(description = \"The second digit to add (0-9)\") int digit2\n        ) {\n            if (digit1 < 0 || digit1 > 9) throw new IllegalArgumentException(\"digit1 must be a single digit (0-9)\");\n            if (digit2 < 0 || digit2 > 9) throw new IllegalArgumentException(\"digit2 must be a single digit (0-9)\");\n            return digit1 + digit2;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CalculatorTool.class.getMethod(\"calculator\", int.class, int.class))\n                .build();\n        }\n    }\n    // Note: Subclassing the Kotlin Tool<TArgs, TResult> and overriding a suspend execute(...) from Java is not supported.\n    // The Java interop uses reflection-based registration of Java methods as tools.\n    ```\n    <!--- KNIT example-class-based-tools-java-01.java -->\n\nHere is an example of a tool implementation in Java, equivalent to using the `SimpleTool` class in Kotlin. This example\nimplements a simple tool that returns a text result.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Java equivalent of SimpleTool: provide a Java method and register it as a tool.\n    public final class CastToDoubleTool {\n        private CastToDoubleTool() {}\n    \n        @Tool(customName = \"cast_to_double\")\n        @LLMDescription(description = \"casts the passed expression to double or returns 0.0 if the expression is not castable\")\n        public static String castToDouble(\n                @LLMDescription(description = \"An expression to case to double\") String expression,\n                @LLMDescription(description = \"A comment on how to process the expression\") String comment\n        ) {\n            double value;\n            try {\n                value = Double.parseDouble(expression);\n            } catch (Exception e) {\n                value = 0.0;\n            }\n            return \"Result: \" + value + \", the comment was: \" + comment;\n        }\n    \n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(CastToDoubleTool.class.getMethod(\"castToDouble\", String.class, String.class))\n                .build();\n        }\n    }\n    // Note: Extending Kotlin SimpleTool<TArgs> from Java is not required; registering a Java method is the idiomatic approach.\n    ```\n    <!--- KNIT example-class-based-tools-java-02.java -->\n\n### Sending tool result to LLM in custom format\n\nFor Kotlin:\n\nIf you are not happy with JSON results sent to LLM (in some cases, LLMs can work better if tool output is structured as Markdown, for instance), you have to follow the following steps:\n\n1. Implement `ToolResult.TextSerializable` interface, and override `textForLLM()` method\n2. Override `resultSerializer` using `ToolResultUtils.toTextSerializer<T>()`\n\nFor Java:\n\nReturn formatted text (such as Markdown) directly as a `String` from your annotated method. The framework handles this automatically.\n\n#### Example\n\nHere is an example showing custom formatted output in both Kotlin and Java:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.Tool\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.prompt.markdown.markdown\n    -->\n    ```kotlin\n    // A tool that edits file\n    object EditFile : Tool<EditFile.Args, EditFile.Result>(\n        argsType = typeToken<Args>(),\n        resultType = typeToken<Result>(),\n        name = \"edit_file\",\n        description = \"Edits the given file\"\n    ) {\n        // Define tool arguments\n        @Serializable\n        public data class Args(\n            val path: String,\n            val original: String,\n            val replacement: String\n        )\n\n        @Serializable\n        public data class Result(\n            private val patchApplyResult: PatchApplyResult\n        ) {\n\n            @Serializable\n            public sealed interface PatchApplyResult {\n                @Serializable\n                public data class Success(val updatedContent: String) : PatchApplyResult\n\n                @Serializable\n                public sealed class Failure(public val reason: String) : PatchApplyResult\n            }\n\n            // Textual output (in Markdown format) that will be visible to the LLM after the tool finishes.\n            fun textForLLM(): String = markdown {\n                if (patchApplyResult is PatchApplyResult.Success) {\n                    line {\n                        bold(\"Successfully\").text(\" edited file (patch applied)\")\n                    }\n                } else {\n                    line {\n                        text(\"File was \")\n                            .bold(\"not\")\n                            .text(\" modified (patch application failed: ${(patchApplyResult as PatchApplyResult.Failure).reason})\")\n                    }\n                }\n            }\n\n            override fun toString(): String = textForLLM()\n        }\n\n        // Function that executes the tool with the provided arguments\n        override suspend fun execute(args: Args): Result {\n            return TODO(\"Implement file edit\")\n        }\n    }\n    ```\n    <!--- KNIT example-class-based-tools-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n\n    // Java equivalent: return Markdown text directly to the LLM from a Java method and register it as a tool.\n    // This avoids needing a custom serializable Result type (which would require Kotlin serialization support).\n    public final class EditFile {\n        private EditFile() {}\n\n        @Tool(customName = \"edit_file\")\n        @LLMDescription(description = \"Edits the given file\")\n        public static String editFile(\n                String path,\n                String original,\n                String replacement\n        ) {\n            // TODO: Implement file edit logic; below is a placeholder illustrating Markdown output\n            boolean success = false;\n            if (success) {\n                return \"**Successfully** edited file (patch applied)\";\n            } else {\n                return \"File was **not** modified (patch application failed: reason)\";\n            }\n        }\n\n        public static ToolRegistry registry() throws NoSuchMethodException {\n            return ToolRegistry.builder()\n                .tool(EditFile.class.getMethod(\"editFile\", String.class, String.class, String.class))\n                .build();\n        }\n    }\n    // Note: If you need a structured custom Result object from Java, you must expose a Kotlin @Serializable type\n    // or another serializer-aware type. Returning String works out-of-the-box with Koog's Java interop.\n    ```\n    <!--- KNIT example-class-based-tools-java-03.java -->\n\nAfter implementing your tool in Kotlin or Java, you need to add it to a tool registry and then use it with an agent.\nFor details, see [Tool registry](../tools/index#tool-registry).\n\n<!-- File: docs/docs/tools/index.md (Tokens: ~2891 | Category: anthropic-skill) -->\n# Overview\n\n Agents use tools to perform specific tasks or access external systems.\n\n## Tool workflow\n\nThe Koog framework offers the following workflow for working with tools in Kotlin and Java:\n\n1. Create a custom tool or use one of the built-in tools.\n2. Add the tool to a tool registry.\n3. Pass the tool registry to an agent.\n4. Use the tool with the agent.\n\n### Available tool types\n\nThere are three types of tools in the Koog framework:\n\n- Built-in tools that provide functionality for agent-user interaction and conversation management. For details, see [Built-in tools](built-in-tools.md).\n- Annotation-based custom tools that let you expose functions as tools to LLMs. For details, see [Annotation-based tools](annotation-based-tools.md).\n- Custom tools that let you control tool parameters, metadata, execution logic, and how it is registered and invoked. For details, see [Class-based\n  tools](class-based-tools.md).\n\n### Tool registry\n\nBefore you can use a tool in an agent, you must add it to a tool registry.\nThe tool registry manages all tools available to the agent.\n\nThe key features of the tool registry:\n\n- Organizes tools.\n- Supports merging of multiple tool registries.\n- Provides methods to retrieve tools by name or type.\n\nTo learn more, see [ToolRegistry](api:agents-tools::ai.koog.agents.core.tools.ToolRegistry).\n\nHere is an example of how to create the tool registry and add the tool to it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class MyToolSet : ToolSet {\n        @Tool\n        fun myTool(): String {\n            // Tool implementation\n            return \"Result\"\n        }\n    }\n    val myTool = MyToolSet()\n    -->\n    ```kotlin\n    val toolRegistry = ToolRegistry {\n        tools(myTool)\n    }\n    ```\n    <!--- KNIT example-tools-overview-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create an instance of your ToolSet\n    MyToolSet myTool = new MyToolSet();\n\n    // Build the ToolRegistry and register tools from the ToolSet\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n        .tools(myTool)\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-01.java -->\n\nTo merge multiple tool registries, do the following:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    class FirstToolSet : ToolSet {\n        @Tool\n        fun firstSampleTool(): String {\n            // Tool implementation\n            return \"First result\"\n        }\n    }\n    class SecondToolSet : ToolSet {\n        @Tool\n        fun secondSampleTool(): String {\n            // Tool implementation\n            return \"Second result\"\n        }\n    }\n    val firstSampleTool = FirstToolSet()\n    val secondSampleTool = SecondToolSet()\n    -->\n    ```kotlin\n    val firstToolRegistry = ToolRegistry {\n        tools(firstSampleTool)\n    }\n    \n    val secondToolRegistry = ToolRegistry {\n        tools(secondSampleTool)\n    }\n    \n    val newRegistry = firstToolRegistry + secondToolRegistry\n    ```\n    <!--- KNIT example-tools-overview-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Create instances of your ToolSets\n    FirstToolSet firstSampleTool = new FirstToolSet();\n    SecondToolSet secondSampleTool = new SecondToolSet();\n\n    // Build separate tool registries\n    ToolRegistry firstToolRegistry = ToolRegistry.builder()\n        .tools(firstSampleTool)\n        .build();\n\n    ToolRegistry secondToolRegistry = ToolRegistry.builder()\n        .tools(secondSampleTool)\n        .build();\n\n    ToolRegistry newRegistry = firstToolRegistry.plus(secondToolRegistry);\n    ```\n    <!--- KNIT example-tools-overview-java-02.java -->\n\n### Passing tools to an agent\n\nTo enable an agent to use a tool, you need to provide a tool registry that contains this tool as an argument when creating the agent:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.example.exampleToolsOverview01.toolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Agent initialization\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        systemPrompt = \"You are a helpful assistant with strong mathematical skills.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        // Pass your tool registry to the agent\n        toolRegistry = toolRegistry\n    )\n    ```\n    <!--- KNIT example-tools-overview-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are a helpful assistant with strong mathematical skills.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .toolRegistry(ToolRegistry.builder()\n            .tools(secondSampleTool)\n            .build()\n        )\n        .build();\n    ```\n    <!--- KNIT example-tools-overview-java-03.java -->\n\n### Calling tools\n\nThere are several ways to call tools within your agent code. The recommended approach is to use the provided methods\nin the agent context rather than calling tools directly, as this ensures proper handling of tool operation within the\nagent environment.\n\n!!! tip\n    Ensure you have implemented proper [error handling](../features/agent-event-handlers.md) in your tools to prevent agent failure.\n\nThe tools are called within a specific session context represented by `AIAgentLLMWriteSession`.\nIt provides several methods for calling tools so that you can:\n\n- Call a tool with the given arguments.\n- Call a tool by its name and the given arguments.\n- Call a tool by the provided tool class and arguments.\n- Call a tool of the specified type with the given arguments.\n- Call a tool that returns a raw string result.\n\nFor more details, the API reference for [AIAgentLLMWriteSession](api:agents-core::ai.koog.agents.core.agent.session.AIAgentLLMWriteSession).\n\n#### Parallel tool calls\n\nYou can also call tools in parallel using the `toParallelToolCallsRaw` extension. For example:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.builder.node\n    import ai.koog.agents.core.tools.SimpleTool\n    import kotlinx.coroutines.flow.collect\n    import kotlinx.coroutines.flow.flow\n    import ai.koog.serialization.typeToken\n    import kotlinx.serialization.Serializable\n    -->\n    ```kotlin\n    @Serializable\n    data class Book(\n        val title: String,\n        val author: String,\n        val description: String\n    )\n    \n    class BookTool() : SimpleTool<Book>(\n        argsType = typeToken<Book>(),\n        name = NAME,\n        description = \"A tool to parse book information from Markdown\"\n    ) {\n        companion object {\n            const val NAME = \"book\"\n        }\n    \n        override suspend fun execute(args: Book): String {\n            println(\"${args.title} by ${args.author}:\\n ${args.description}\")\n            return \"Done\"\n        }\n    }\n    \n    val strategy = strategy<Unit, Unit>(\"strategy-name\") {\n    \n        /*...*/\n    \n        val myNode by node<Unit, Unit> { _ ->\n            llm.writeSession {\n                flow {\n                    emit(Book(\"Book 1\", \"Author 1\", \"Description 1\"))\n                }.toParallelToolCallsRaw(BookTool::class).collect()\n            }\n        }\n    }\n    \n    ```\n    <!--- KNIT example-tools-overview-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-04.java -->\n\n#### Calling tools from nodes\n\nWhen building agent workflows with nodes, you can use special nodes to call tools:\n\n* **nodeExecuteTool**: calls a single tool call and returns its result. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteTool).\n\n* **nodeExecuteSingleTool** that calls a specific tool with the provided arguments. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteSingleTool).\n\n* **nodeExecuteMultipleTools** that performs multiple tool calls and returns their results. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeExecuteMultipleTools).\n\n* **nodeLLMSendToolResult** that sends a tool result to the LLM and gets a response. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendToolResult).\n\n* **nodeLLMSendMultipleToolResults** that sends multiple tool results to the LLM. For details, see [API reference](api:agents-core::ai.koog.agents.core.dsl.extension.nodeLLMSendMultipleToolResults).\n\n## Using agents as tools\n\nThe framework provides the capability to convert any AI agent into a tool that can be used by other agents. \nThis powerful feature enables you to create hierarchical agent architectures where specialized agents can be called as tools by higher-level orchestrating agents.\n\n### Converting agents to tools\n\nTo convert an agent into a tool, use the `AIAgentService` and the `createAgentTool()` extension function:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.AIAgentService\n    import ai.koog.agents.core.agent.createAgentTool\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.serialization.typeToken\n    const val apiKey = \"\"\n    val analysisToolRegistry = ToolRegistry {}\n    -->\n    ```kotlin\n    // Create a specialized agent service, responsible for creating financial analysis agents.\n    val analysisAgentService = AIAgentService(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a financial analysis specialist.\",\n        toolRegistry = analysisToolRegistry\n    )\n    \n    // Create a tool that would run financial analysis agent once called.\n    val analysisAgentTool = analysisAgentService.createAgentTool(\n        agentName = \"analyzeTransactions\",\n        agentDescription = \"Performs financial transaction analysis\",\n        inputDescription = \"Transaction analysis request\",\n        inputType = typeToken<String>(),\n    )\n    ```\n    <!--- KNIT example-tools-overview-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-05.java -->\n\n\n### Using agent tools in other agents\n\nOnce converted to a tool, you can add the agent tool to another agent's tool registry:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.example.exampleToolsOverview05.analysisAgentTool\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    const val apiKey = \"\"\n    -->\n    ```kotlin\n    // Create a coordinator agent that can use specialized agents as tools\n    val coordinatorAgent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You coordinate different specialized services.\",\n        toolRegistry = ToolRegistry {\n            tool(analysisAgentTool)\n            // Add other tools as needed\n        }\n    )\n    ```\n    <!--- KNIT example-tools-overview-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tools-overview-java-06.java -->\n\n\n### Agent tool execution\n\nWhen an agent tool is called:\n\n1. The arguments are deserialized according to the input descriptor.\n2. The wrapped agent is executed with the deserialized input.\n3. The agent's output is serialized and returned as the tool result.\n\n### Benefits of agents as tools\n\n- **Modularity**: Break complex workflows into specialized agents.\n- **Reusability**: Use the same specialized agent across multiple coordinator agents.\n- **Separation of concerns**: Each agent can focus on its specific domain.\n\n<!-- File: docs/docs/tools/tool-descriptor-schemer.md (Tokens: ~2287 | Category: anthropic-skill) -->\n# ToolDescriptorSchemer\n\n`ToolDescriptorSchemer` is an extension point that converts a `ToolDescriptor` into a JSON Schema object compatible with specific LLM providers. It can be implemented in both Kotlin and Java.\n\nKey points:\n\n- Location: `ai.koog.agents.core.tools.serialization.ToolDescriptorSchemer`\n- Contract: a single function `scheme(toolDescriptor: ToolDescriptor): JsonObject` or `generate(ToolDescriptor toolDescriptor): JsonObject` (Java)\n- Implementations provided:\n  - `OpenAICompatibleToolDescriptorSchemer` — generates schemas compatible with OpenAI‑style function/tool definitions.\n  - `OllamaToolDescriptorSchemer` — generates schemas compatible with Ollama tool JSON.\n\n<!--- INCLUDE\nimport ai.koog.agents.core.tools.ToolDescriptor\nimport ai.koog.agents.core.tools.ToolParameterDescriptor\nimport ai.koog.agents.core.tools.ToolParameterType\nimport kotlinx.serialization.json.JsonObject\n-->\n```kotlin\n// Interface\ninterface ToolDescriptorSchemaGenerator {\nfun generate(toolDescriptor: ToolDescriptor): JsonObject\n}\n```\n<!--- KNIT example-tool-descriptor-schemer-01.kt -->\n\n\n## Why use it\n\nIf you want to provide a custom scheme for existing or new LLM providers in Kotlin or Java, implement this interface to convert Koog’s `ToolDescriptor` into the expected JSON Schema format.\n\n## Implementation example\n\nBelow is a minimal custom implementation in both Kotlin and Java that renders only a subset of parameter types to illustrate how to plug into the SPI. Real implementations should cover all `ToolParameterType`s (String, Integer, Float, Boolean, Null, Enum, List, Object, AnyOf).\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.agents.core.tools.serialization.ToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    -->\n    ```kotlin\n    \n    class MinimalSchemer : ToolDescriptorSchemaGenerator {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    public static class MinimalSchemer extends OpenAICompatibleToolDescriptorSchemaGenerator {\n        @Override\n        public JsonObject generate(ToolDescriptor toolDescriptor) {\n            Map<String, JsonElement> root = new LinkedHashMap<>();\n            root.put(\"type\", JsonPrimitive(\"object\"));\n\n            // properties\n            Map<String, JsonElement> props = new LinkedHashMap<>();\n            for (ToolParameterDescriptor p : concat(toolDescriptor.getRequiredParameters(), toolDescriptor.getOptionalParameters())) {\n                Map<String, JsonElement> prop = new LinkedHashMap<>();\n                prop.put(\"description\", JsonPrimitive(p.getDescription()));\n\n                ToolParameterType t = p.getType();\n                if (t == ToolParameterType.String.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                } else if (t == ToolParameterType.Integer.INSTANCE) {\n                    prop.put(\"type\", JsonPrimitive(\"integer\"));\n                } else if (t instanceof ToolParameterType.Enum) {\n                    prop.put(\"type\", JsonPrimitive(\"string\"));\n                    String[] entries = ((ToolParameterType.Enum) t).getEntries();\n                    List<JsonElement> enumVals = new ArrayList<>();\n                    for (String e : entries) enumVals.add(JsonPrimitive(e));\n                    prop.put(\"enum\", new JsonArray(enumVals));\n                } else {\n                    prop.put(\"type\", JsonPrimitive(\"string\")); // fallback for brevity\n                }\n\n                props.put(p.getName(), new JsonObject(prop));\n            }\n            root.put(\"properties\", new JsonObject(props));\n\n            // required array\n            List<JsonElement> required = new ArrayList<>();\n            for (ToolParameterDescriptor p : toolDescriptor.getRequiredParameters()) {\n                required.add(JsonPrimitive(p.getName()));\n            }\n            root.put(\"required\", new JsonArray(required));\n\n            return new JsonObject(root);\n        }\n\n        private static List<ToolParameterDescriptor> concat(List<ToolParameterDescriptor> a, List<ToolParameterDescriptor> b) {\n            List<ToolParameterDescriptor> res = new ArrayList<>(a.size() + b.size());\n            res.addAll(a);\n            res.addAll(b);\n            return res;\n        }\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-01.java -->\n\n## Using with a client\n\nTypically, you do not need to call a schemer directly. Koog clients accept a list of `ToolDescriptor` objects and apply the correct schemer internally when serializing requests for the provider.\n\nThe example below defines a simple tool and passes it to the OpenAI client. The client will use `OpenAICompatibleToolDescriptorSchemer` under the hood to build the JSON schema.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.prompt.executor.clients.openai.OpenAILLMClient\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    import ai.koog.prompt.Prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import kotlinx.serialization.json.JsonPrimitive\n    import kotlinx.serialization.json.JsonObject\n    import kotlinx.serialization.json.buildJsonObject\n    import kotlinx.serialization.json.put\n    import kotlinx.serialization.json.putJsonArray\n    import kotlinx.serialization.json.putJsonObject\n    import kotlinx.coroutines.runBlocking\n    class MinimalSchemer : OpenAICompatibleToolDescriptorSchemaGenerator() {\n        override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {\n            put(\"type\", \"object\")\n            putJsonObject(\"properties\") {\n                (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->\n                    put(p.name, buildJsonObject {\n                        put(\"description\", p.description)\n                        when (val t = p.type) {\n                            ToolParameterType.String -> put(\"type\", \"string\")\n                            ToolParameterType.Integer -> put(\"type\", \"integer\")\n                            is ToolParameterType.Enum -> {\n                                put(\"type\", \"string\")\n                                putJsonArray(\"enum\") { t.entries.forEach { add(JsonPrimitive(it)) } }\n                            }\n                            else -> put(\"type\", \"string\") // fallback for brevity\n                        }\n                    })\n                }\n            }\n            putJsonArray(\"required\") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }\n        }\n    }\n    -->\n    ```kotlin\n    val client = OpenAILLMClient(apiKey = System.getenv(\"OPENAI_API_KEY\"), toolsConverter = MinimalSchemer())\n    \n    val getUserTool = ToolDescriptor(\n        name = \"get_user\",\n        description = \"Returns user profile by id\",\n        requiredParameters = listOf(\n            ToolParameterDescriptor(\n                name = \"id\",\n                description = \"User id\",\n                type = ToolParameterType.String\n            )\n        )\n    )\n    \n    val prompt = Prompt.build(id = \"p1\") { user(\"Hello\") }\n    val responses = runBlocking {\n        client.execute(\n            prompt = prompt,\n            model = OpenAIModels.Chat.GPT4o,\n            tools = listOf(getUserTool)\n        )\n    }\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    // Custom schemer extending the OpenAI-compatible one is Kotlin-only in the docs; for Java example we reuse MinimalSchemer from above.\n    OpenAILLMClient client = openAIClient(System.getenv(\"OPENAI_API_KEY\"), new OpenAIClientSettings(), null, null, new OpenAICompatibleToolDescriptorSchemaGenerator());\n    \n    ToolDescriptor getUserTool = new ToolDescriptor(\n        \"get_user\",\n        \"Returns user profile by id\",\n        Collections.singletonList(new ToolParameterDescriptor(\n            \"id\",\n            \"User id\",\n            ToolParameterType.String.INSTANCE\n        )),\n        Collections.emptyList()\n    );\n\n    Prompt prompt = Prompt.builder(\"p1\")\n        .user(\"Hello\")\n        .build();\n\n    List<Message.Response> responses = client.execute(prompt, OpenAIModels.Chat.GPT4o, java.util.List.of(getUserTool));\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-02.java -->\n\nIf you need direct access to the produced schema (for debugging or for a custom transport), you can instantiate the provider‑specific schemer and serialize the JSON yourself:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import kotlinx.serialization.json.Json\n    import ai.koog.prompt.executor.clients.openai.base.OpenAICompatibleToolDescriptorSchemaGenerator\n    import ai.koog.agents.core.tools.ToolDescriptor\n    import ai.koog.agents.core.tools.ToolParameterDescriptor\n    import ai.koog.agents.core.tools.ToolParameterType\n    fun getUserTool(): ToolDescriptor {\n        return ToolDescriptor(\n            name = \"get_user\",\n            description = \"Returns user profile by id\",\n            requiredParameters = listOf(\n                ToolParameterDescriptor(\n                    name = \"id\",\n                    description = \"User id\",\n                    type = ToolParameterType.String\n                )\n            )\n        )\n    }\n    -->\n    \n    ```kotlin\n    val json = Json { prettyPrint = true }\n    val schema = OpenAICompatibleToolDescriptorSchemaGenerator().generate(getUserTool())\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    ```\n    <!--- KNIT example-tool-descriptor-schemer-java-03.java -->\n\n<!-- File: integration-tests/src/jvmTest/resources/skills/arithmetic-evaluator/SKILL.md (Tokens: ~129 | Category: anthropic-skill) -->\n---\nname: arithmetic-evaluator\ndescription: Evaluate arithmetic expressions and return numeric results. Use when a user asks an arithmetic question, requests calculation, or provides a math expression involving numbers, parentheses, and arithmetic operators.\n---\n\nEvaluate the arithmetic expression the user provides.\n\n1. Extract the arithmetic expression from the user request.\n2. If the expression is missing or ambiguous, ask for a clear arithmetic expression.\n3. Evaluate it by passing the expression to `scripts/evaluate_arithmetic_exp.py`\n4. Return the computed result clearly and concisely.\n\nIf script execution fails, explain that clearly and ask the user to provide a valid arithmetic expression.\n\n<!-- File: integration-tests/src/jvmTest/resources/skills/weather-retrieval/SKILL.md (Tokens: ~171 | Category: anthropic-skill) -->\n---\nname: weather-retrieval\ndescription: Retrieve current weather information for a specific location. Use when a user asks for weather, temperature, conditions, forecast summary, or climate details for a city, region, or place.\n---\n\nGet weather details for the location the user provides.\n\n1. Extract the location from the user request.\n2. If the location is missing or ambiguous, ask for a clearer location name.\n3. Fetch the weather data by passing the location to `scripts/retrieve_weather.py`\n4. Parse the JSON output from the script.\n5. Return a concise summary that includes:\n    - location name\n    - temperature\n    - weather condition\n    - any notable context (for example, wind, humidity, or precipitation)\n\nIf the script fails or returns invalid data, explain that clearly and suggest trying a more specific location.\n\n<!-- File: skills/Module.md (Tokens: ~23 | Category: anthropic-skill) -->\n# Module skills\n\nA module that provides core utilities for defining skill metadata, discovering skill content, and composing prompts.\n\n<!-- File: agents/agents-features/agents-features-opentelemetry/README.md (Tokens: ~4432 | Category: subagent-persona) -->\n# OpenTelemetry support\n\nThis guide provides comprehensive instructions on how to use OpenTelemetry with the Koog agentic framework for tracing and monitoring your AI agents.\n\n## Overview\n\nOpenTelemetry is an observability framework that provides tools for generating, collecting, and exporting telemetry data (metrics, logs, and traces) from your applications. The Koog OpenTelemetry feature allows you to instrument your AI agents to collect telemetry data, which can help you:\n\n- Monitor agent performance and behavior\n- Debug issues in complex agent workflows\n- Visualize the execution flow of your agents\n- Track LLM calls and tool usage\n- Count tokens (input, output, and total) for LLM calls to monitor usage and costs\n- Analyze agent behavior patterns\n\nKey OpenTelemetry concepts\n\n- **Spans**: Spans represent individual units of work or operations within a distributed trace. They indicate the beginning and end of a specific activity in an application, such as an agent execution, a function call, an LLM call, or a tool call.\n- **Exporters**: Exporters are components responsible for sending the collected telemetry data (spans, metrics, logs) to various backends or destinations.\n- **Collectors**: An OpenTelemetry Collector receives, processes, and exports telemetry data. It acts as an intermediary between your applications and your observability backend.\n\nThe OpenTelemetry feature in Koog automatically creates spans for various agent events, including:\n\n- Agent creation and invocation\n- Strategy execution\n- Node execution\n- LLM calls (inference)\n- Tool calls\n\n## Installation\n\nTo use OpenTelemetry with Koog, add the OpenTelemetry feature to your agent:\n\n```kotlin\nval agent = AIAgent(\n    executor = simpleOpenAIExecutor(apiKey),\n    llmModel = OpenAIModels.Chat.GPT4o,\n    systemPrompt = \"You are a helpful assistant.\",\n    installFeatures = {\n        install(OpenTelemetry) {\n            // Configuration options here\n        }\n    }\n)\n```\n\n## Configuration\n\n### Basic configuration\n\nHere is an example of installing the OpenTelemetry feature with a basic set of configuration items:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.feature.OpenTelemetry\nimport io.opentelemetry.exporter.logging.LoggingSpanExporter\n\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Java SDK exporter is bridged automatically on JVM\n}\n```\n\nThe example includes the following configuration methods:\n\n| Name               | Data type | Required | Description                                                                                     |\n|--------------------|-----------|----------|-------------------------------------------------------------------------------------------------|\n| `setServiceInfo`   | Unit      | No       | Sets the information about the service being instrumented, including service name and version.  |\n| `addSpanExporter`  | Unit      | No       | Registers an exporter wrapped in a [`batchSpanProcessor`][batchSpanProcessor] (OTel-recommended). |\n\nPlease see below the full list of available configuration properties:\n\n| Name                    | Data type       | Default value | Description                                                                                                              |\n|-------------------------|-----------------|---------------|--------------------------------------------------------------------------------------------------------------------------|\n| `serviceName`           | `String`        | `ai.koog`     | The name of the service being instrumented.                                                                              |\n| `serviceVersion`        | `String`        | `0.0.0`       | The version of the service being instrumented.                                                                           |\n| `isVerbose`             | `Boolean`       | `false`       | Whether to enable verbose logging for debugging OpenTelemetry configuration.                                             |\n| `isShutdownOnAgentClose`| `Boolean`       | `false`       | Whether the SDK is shut down when the owning agent closes.                                                               |\n| `tracer`                | `Tracer`        |               | Kotlin SDK [`Tracer`][Tracer] used for creating spans.                                                                  |\n\nConfiguration API:\n\n| Name                    | Arguments                                                | Description                                                                                                              |\n|-------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| `setServiceInfo`        | `serviceName: String, serviceVersion: String`            | Sets the service information including name and version.                                                                 |\n| `addSpanExporter`       | `exporter: SpanExporter` (Kotlin SDK or Java SDK on JVM) | Registers an exporter behind a `batchSpanProcessor`. On JVM the method is overloaded to accept Java-SDK exporters directly (bridged via the compat layer). |\n| `addSpanProcessor`      | `factory: TraceExportConfigDsl.() -> SpanProcessor`      | Registers a custom span processor (e.g., simple/composite/custom-batched) via the SDK's DSL.                             |\n| `addResourceAttributes` | `attributes: Map<String, Any>`                           | Adds resource attributes to provide additional context about the service. Supported value types: `String`, `Long`, `Double`, `Boolean`. |\n| `setVerbose`            | `verbose: Boolean`                                       | Enables or disables verbose logging for debugging OpenTelemetry configuration.                                           |\n| `setSdk`                | `openTelemetry: OpenTelemetry`                           | Injects a pre-built Kotlin OpenTelemetry SDK instance, bypassing internal configuration.                                 |\n| `setShutdownOnAgentClose`| `shutdownOnAgentClose: Boolean`                         | Toggles whether the SDK is shut down when the owning agent closes.                                                       |\n\nJVM-only members on [`OpenTelemetryConfig`][OpenTelemetryConfig] cover metrics (`addMetricExporter`, `addMetricFilter`, `meter`) — the Kotlin SDK 0.3.0 ships no metrics module, so metrics are sent through the Java SDK.\n\n### Advanced configuration\n\nFor more advanced configuration, you can customize resource attributes:\n\n```kotlin\ninstall(OpenTelemetry) {\n    setServiceInfo(\"my-agent-service\", \"1.0.0\")    // Set your service configuration\n    addSpanExporter(LoggingSpanExporter.create())  // Add Logging exporter\n\n    // Add resource attributes (Map<String, Any>; supports String, Long, Double, Boolean)\n    addResourceAttributes(mapOf(\n        \"deployment.environment\" to \"production\",\n        \"custom.attribute\" to \"custom-value\"\n    ))\n}\n```\n\n### Custom span processors\n\n`addSpanExporter` always wraps in `batchSpanProcessor`. For full control over the processor — custom batching parameters, simple per-span export for tests, or a composite processor over several inner processors — use `addSpanProcessor` and the SDK's DSL-scoped factory functions:\n\n```kotlin\nimport io.opentelemetry.kotlin.tracing.export.batchSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.simpleSpanProcessor\nimport io.opentelemetry.kotlin.tracing.export.stdoutSpanExporter\n\ninstall(OpenTelemetry) {\n    // Stdout exporter wrapped in a simple (synchronous) processor — useful for local debugging.\n    addSpanProcessor { simpleSpanProcessor(stdoutSpanExporter()) }\n\n    // Custom batch parameters (default: 5s schedule delay, 512 max batch size).\n    addSpanProcessor { batchSpanProcessor(myExporter, scheduleDelayMs = 1000) }\n}\n```\n\n### Sdk configuration\n\nIf you already have an initialized [`OpenTelemetry`][OpenTelemetry] SDK instance — for example, configured elsewhere in your application or provided by a framework — you can pass it directly using `setSdk`. When using `setSdk` any other configuration methods like `addSpanExporter`, `addSpanProcessor`, `addResourceAttributes` are ignored, since the SDK is assumed to be fully configured.\n\n```kotlin\nimport io.opentelemetry.kotlin.OpenTelemetry\nimport io.opentelemetry.kotlin.createOpenTelemetry\n\nval sdk: OpenTelemetry = createOpenTelemetry { /* … */ } // Your preconfigured SDK\n\ninstall(OpenTelemetry) {\n    setSdk(sdk) // Use your existing OpenTelemetry instance\n}\n```\n\n#### Resource attributes\n\nResource attributes represent additional information about a process producing telemetry. This information can be\nincluded in an OpenTelemetry configuration in Koog using the `addResourceAttributes()` method that takes a\n`Map<String, Any>`. Supported value types are `String`, `Long`, `Double`, and `Boolean`.\n\nThe following default resource attributes are automatically added:\n\n- `service.name`: The name of the service being instrumented. Set to the value of `serviceName`.\n- `service.version`: The version of the service being instrumented. Set to the value of `serviceVersion`.\n- `service.instance.time`: The timestamp when the service instance was created.\n- `os.type`: The operating system type.\n- `os.version`: The operating system version.\n- `os.arch`: The operating system architecture.\n\nThe OpenTelemetry feature automatically adds various attributes to spans following the [OpenTelemetry Semantic Convention for GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/):\n\n### Common Attributes\n\n- `gen_ai.operation.name`: the type of operation being performed (e.g., \"create_agent\", \"invoke_agent\", \"chat\", \"execute_tool\")\n- `gen_ai.system`: the LLM provider being used\n- `gen_ai.agent.id`: the ID of the agent\n- `gen_ai.agent.name`: the name of the agent (when available)\n- `gen_ai.conversation.id`: the ID of the conversation/run\n- `gen_ai.request.model`: the LLM model being used\n- `gen_ai.request.temperature`: the temperature parameter for the LLM request\n- `error.type`: the type of error that occurred (when applicable)\n\n### Custom Attributes\n\nSome custom attributes specific to Koog are also added:\n\n- `koog.agent.strategy.name`: the name of the agent strategy\n- `koog.node.id`: the name of the node being executed\n\n### Span Types\n\nThe OpenTelemetry feature creates different types of spans for various operations. Each span type has a distinct purpose, parent–child relationship, and set of attributes and events.\n\n1. **Create Agent Span**\n    - Purpose: Long‑lived span representing the lifecycle of an agent instance (created once per agent ID).\n    - Emitted: When an agent is first started. Closed when the agent is closed.\n    - Parent: none. Children: **Invoke Agent Span** (one per run).\n    - Useful for: Grouping all agent runs and capturing static agent configuration.\n    - Key attributes:\n        - `gen_ai.operation.name` = `create_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.request.model`\n\n2. **Invoke Agent Span**\n    - Purpose: One concrete execution (run) of an agent.\n    - Emitted: At the start of a run. Closed on successful finish or error.\n    - Parent: **Create Agent Span**. Children: **Strategy Span** instances, and, indirectly, node, tool, and LLM spans within strategies.\n    - Useful for: Measuring run‑level timing, status, and grouping of all strategy, node, tool, and LLM activity for a run.\n    - Key attributes:\n        - `gen_ai.operation.name` = `invoke_agent`\n        - `gen_ai.agent.id`\n        - `gen_ai.conversation.id`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.response.finish_reasons` (derived from the final response in the session prompt: `Stop` for Assistant/Reasoning, `ToolCalls` for Tool.Call)\n\n3. **Strategy Span**\n    - Purpose: Execution of a strategy within an agent run.\n    - Emitted: At the start of strategy execution. Closed when the strategy completes or errors.\n    - Parent: **Invoke Agent Span**. Children: **Node Execute Span** instances.\n    - Useful for: Tracking strategy-level execution, grouping all node executions within a strategy, and measuring strategy performance.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.strategy.name`\n        - `koog.event.id`\n    - Note: This is a custom span type specific to Koog, not defined in the OpenTelemetry Semantic Conventions.\n\n4. **Node Execute Span**\n    - Purpose: Execution of a single node in the agent strategy.\n    - Emitted: Immediately before a node runs. Closed after the node completes or errors.\n    - Parent: **Strategy Span**. Children: **Inference Spans** and **Execute Tool Span** instances created by the node.\n    - Useful for: Understanding strategy flow and attributing LLM or tool work to a specific node.\n    - Key attributes:\n        - `gen_ai.conversation.id`\n        - `koog.node.id`\n\n5. **Inference Span**\n    - Purpose: A single LLM call (prompt execution).\n    - Emitted: Before the LLM is invoked. Closed after responses are received.\n    - Parent: **Node Execute Span**.\n    - Events added:\n        - system, user, and assistant messages from the prompt\n        - tool choice and tool result messages\n        - moderation response (when present)\n    - Useful for: Tracking model usage, prompt parameters, messages, token usage, and finish reasons.\n    - Key attributes:\n        - `gen_ai.operation.name` = `chat`\n        - `gen_ai.system` (LLM provider)\n        - `gen_ai.conversation.id`\n        - `gen_ai.request.model`\n        - `gen_ai.request.temperature`\n        - `gen_ai.request.max_tokens` (when available)\n        - `gen_ai.usage.input_tokens` (when available)\n        - `gen_ai.usage.output_tokens` (when available)\n        - `gen_ai.usage.total_tokens` (when available)\n        - `gen_ai.response.finish_reasons` (Stop, ToolCalls, etc.)\n        - `error.type` (on LLM call failure)\n        - `gen_ai.response.metadata` (when `ResponseMetaInfo.metadata` is present - serialized JSON containing free-form provider or application metadata)\n\n6. **Execute Tool Span**\n    - Purpose: Execution of a tool or function call triggered by the agent or LLM.\n    - Emitted: When a tool is called. Closed after the tool returns a result or fails with an error during validation or execution.\n    - Parent: **Node Execute Span**.\n    - Useful for: Auditing tool usage, inputs and outputs, and failure causes.\n    - Key attributes:\n        - `gen_ai.tool.name`\n        - `gen_ai.tool.description`\n        - `gen_ai.tool.arguments` (when available)\n        - `gen_ai.tool.call_id` (when available)\n        - `gen_ai.tool.output` (when available)\n        - `error.type` (on failure or validation error)\n\n## Exporters\n\nExporters send collected telemetry data to an OpenTelemetry Collector or other types of destinations or backend implementations.\n\n### OTLP Exporter example\n\nThe OTLP (OpenTelemetry Protocol) exporter sends telemetry data to an OpenTelemetry Collector. This is useful for integrating with systems like Jaeger, Zipkin, or Prometheus.\n\nTo add an OpenTelemetry Exporter, use the `addSpanExporter` function to a custom exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    // The default endpoint is http://localhost:4317\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n### Logging Exporter example\n\nA logging exporter that outputs trace information to the console is included by default. This type of export is useful\nfor development and debugging purposes.\n\n```kotlin\ninstall(OpenTelemetry) {\n    // The logging exporter is added by default\n    addSpanExporter(LoggingSpanExporter.create())\n}\n```\n\n## Integration with Jaeger\n\nJaeger is a popular distributed tracing system that works with OpenTelemetry. To use Jaeger with Koog:\n\n1. Start a Jaeger container:\n```yaml\n# docker-compose.yaml\nservices:\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:1.39\n    container_name: jaeger-all-in-one\n    environment:\n      - COLLECTOR_OTLP_ENABLED=true\n    ports:\n      - \"4317:4317\"  # OTLP gRPC port\n      - \"16686:16686\"  # Jaeger UI port\n```\n\n2. Configure your agent to use the OTLP exporter:\n```kotlin\ninstall(OpenTelemetry) {\n    addSpanExporter(\n        OtlpGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4317\")\n            .build()\n    )\n}\n```\n\n3. Access the Jaeger UI at `http://localhost:16686` to view your traces.\n\n## Examples\n\nThis section includes two examples of implementing OpenTelemetry support in your agent workflow. The basic example\nincludes quick OpenTelemetry setup with minimal configuration, while the advanced example shows how you can create a\nmore elaborate and customized OpenTelemetry configuration.\n\n### Basic example\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleGoogleAIExecutor(apiKey),\n        llmModel = GoogleModels.Gemini2_0Flash,\n        systemPrompt = \"You are a code assistant. Provide concise code examples.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                setServiceInfo(\"my-otlp-agent\", \"1.0.0\")\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Create python function that prints hello world\")\n    println(result)\n    \n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\n### Advanced example with custom exporters and attributes\n\n```kotlin\nsuspend fun main() {\n    val apiKey = \"your-api-key\"\n    val agent = AIAgent(\n        executor = simpleOpenAIExecutor(apiKey),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        installFeatures = {\n            install(OpenTelemetry) {\n                // Configure service info\n                setServiceInfo(\"advanced-agent\", \"2.0.0\")\n\n                // Add resource attributes (Map<String, Any>)\n                addResourceAttributes(mapOf(\n                    \"deployment.environment\" to \"production\",\n                    \"custom.attribute\" to \"custom-value\"\n                ))\n\n                // Add exporters (Java SDK exporter bridged via the JVM addSpanExporter)\n                addSpanExporter(LoggingSpanExporter.create())\n            }\n        }\n    )\n\n    val result = agent.run(\"Tell me about OpenTelemetry\")\n    println(result)\n\n    // Wait for telemetry data to be exported\n    TimeUnit.SECONDS.sleep(10)\n}\n```\n\nFor full sampling control or other SDK-level options, build your own [`OpenTelemetry`][OpenTelemetry] instance with [`createOpenTelemetry { … }`][createOpenTelemetry] and inject it via `setSdk`.\n\n### Datadog\n\n[Datadog](https://www.datadoghq.com/) supports direct OTLP intake for LLM Observability. To send traces to Datadog:\n\n```kotlin\nimport ai.koog.agents.features.opentelemetry.integration.datadog.addDatadogExporter\n\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n}\n```\n\nThe exporter reads the `DD_API_KEY` environment variable by default. You can also pass it explicitly:\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter(\n        datadogApiKey = \"your-api-key\",\n        url = \"datadoghq.eu\",                    // defaults to datadoghq.com\n        resourceAttributes = mapOf(\n            \"env\" to \"production\",\n            \"service.name\" to \"my-agent\",\n        ),\n    )\n}\n```\n\nTo send traces to Datadog and another backend at the same time, register both via `addSpanExporter` (Datadog's helper handles its own registration internally; the second exporter goes through the public API):\n\n```kotlin\ninstall(OpenTelemetry) {\n    addDatadogExporter()\n    addSpanExporter(\n        OtlpHttpSpanExporter.builder()\n            .setEndpoint(\"http://localhost:4318/v1/traces\")\n            .build()\n    )\n}\n```\n\n## Troubleshooting\n\n### Common Issues\n\n1. **No traces appearing in Jaeger UI**\n    - Ensure the Jaeger container is running and the OTLP port (4317) is accessible\n    - Check that the OTLP exporter is configured with the correct endpoint\n    - Make sure to wait a few seconds after agent execution for traces to be exported\n\n2. **Missing spans or incomplete traces**\n    - Verify that the agent execution completes successfully\n    - Ensure that you're not closing the application too quickly after agent execution\n    - Add a delay (e.g., `TimeUnit.SECONDS.sleep(10)`) after agent execution to allow time for spans to be exported\n\n3. **Excessive number of spans**\n    - Build a custom Kotlin OTel SDK with a sampler (e.g., `parentBased(root = traceIdRatioBased(0.1))`) and inject it via `setSdk(...)`.\n\n<!-- File: agents/agents-features/agents-features-snapshot/README.md (Tokens: ~1146 | Category: subagent-persona) -->\n# AgentCheckpoint\n\nAgentCheckpoint is a feature that provides checkpoint functionality for AI agents in the Koog Agents framework. It allows saving and restoring the state of an agent at specific points during execution, enabling powerful capabilities such as:\n\n- Resuming agent execution from a specific point\n- Rolling back to previous states\n- Persisting agent state across sessions\n\n## Core Concepts\n\n### Checkpoints\n\nA checkpoint captures the complete state of an agent at a specific point in its execution, including:\n\n- Message history (all interactions between user, system, assistant, and tools)\n- Current node being executed\n- Input data for the current node\n- Timestamp of creation\n\nCheckpoints are identified by unique IDs and are associated with a specific agent.\n\n### Storage Providers\n\nAgentCheckpoint uses storage providers to save and retrieve checkpoints. The framework includes several built-in providers:\n\n- `InMemoryAgentCheckpointStorageProvider`: Stores checkpoints in memory (lost when the application restarts)\n- `FileAgentCheckpointStorageProvider`: Persists checkpoints to the file system\n- `NoAgentCheckpointStorageProvider`: A no-op implementation (default) that doesn't store checkpoints\n\nYou can also implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface.\n\n### Continuous Persistence\n\nThe feature can be configured to automatically create checkpoints after each node execution, ensuring that the agent's state is continuously persisted and can be recovered at any point.\n\n## Installation\n\nTo use the AgentCheckpoint feature, you need to add it to your agent's configuration:\n\n```kotlin\nval agent = aiAgent {\n    // ... other configuration\n    \n    features {\n        install(AgentCheckpoint) {\n            // Configure the feature\n            snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n            continuouslyPersistent() // Optional: create checkpoints after each node\n        }\n    }\n}\n```\n\n## Configuration Options\n\nThe AgentCheckpoint feature has two main configuration options:\n\n### Storage Provider\n\nSet the storage provider that will be used to save and retrieve checkpoints:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(InMemoryAgentCheckpointStorageProvider())\n}\n```\n\nAvailable built-in providers:\n- `InMemoryAgentCheckpointStorageProvider`: In-memory storage (non-persistent)\n- `FileAgentCheckpointStorageProvider`: File-based storage\n- `NoAgentCheckpointStorageProvider`: No-op provider (default)\n\n### Continuous Persistence\n\nEnable automatic checkpoint creation after each node execution:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    continuouslyPersistent()\n}\n```\n\nWhen enabled, the agent will automatically create a checkpoint after each node is executed, allowing for fine-grained recovery.\n\n## Basic Usage\n\n### Creating a Checkpoint\n\nCreate a checkpoint at a specific point in your agent's execution:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Create a checkpoint with the current state\n    val checkpoint = context.checkpoint().createCheckpoint(\n        agentId = context.id,\n        agentContext = context,\n        nodeId = \"current-node-id\",\n        lastInput = inputData\n    )\n    \n    // The checkpoint ID can be stored for later use\n    val checkpointId = checkpoint.checkpointId\n}\n```\n\n### Restoring from a Checkpoint\n\nRestore an agent's state from a specific checkpoint:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase, checkpointId: String) {\n    // Roll back to a specific checkpoint\n    context.checkpoint().rollbackToCheckpoint(checkpointId, context)\n    \n    // Or roll back to the latest checkpoint\n    context.checkpoint().rollbackToLatestCheckpoint(context)\n}\n```\n\n### Using Extension Functions\n\nThe feature provides convenient extension functions for working with checkpoints:\n\n```kotlin\nsuspend fun example(context: AIAgentContextBase) {\n    // Access the checkpoint feature\n    val checkpointFeature = context.checkpoint()\n    \n    // Or perform an action with the checkpoint feature\n    context.withCheckpoints(context) { ctx ->\n        // 'this' is the checkpoint feature\n        createCheckpoint(ctx.id, ctx, \"node-id\", inputData)\n    }\n}\n```\n\n## Advanced Usage\n\n### Custom Storage Providers\n\nYou can implement custom storage providers by implementing the `AgentCheckpointStorageProvider` interface:\n\n```kotlin\nclass MyCustomStorageProvider : AgentCheckpointStorageProvider {\n    override suspend fun getCheckpoints(agentId: String): List<AgentCheckpointData> {\n        // Implementation\n    }\n    \n    override suspend fun saveCheckpoint(agentId: String, agentCheckpointData: AgentCheckpointData) {\n        // Implementation\n    }\n    \n    override suspend fun getLatestCheckpoint(agentId: String): AgentCheckpointData? {\n        // Implementation\n    }\n}\n```\n\nThen use your custom provider in the feature configuration:\n\n```kotlin\ninstall(AgentCheckpoint) {\n    snapshotProvider(MyCustomStorageProvider())\n}\n```\n\n### Setting Execution Points\n\nFor advanced control, you can directly set the execution point of an agent:\n\n```kotlin\nfun example(context: AIAgentContextBase) {\n    context.checkpoint().setExecutionPoint(\n        agentContext = context,\n        nodeId = \"target-node-id\",\n        messageHistory = customMessageHistory,\n        input = customInput\n    )\n}\n```\n\nThis allows for more fine-grained control over the agent's state beyond just restoring from checkpoints.\n\n## Requirements\n\nThe AgentCheckpoint feature requires that all nodes in your agent's strategy have unique names. This is enforced when the feature is installed:\n\n```kotlin\nrequire(ctx.strategy.metadata.uniqueNames) { \n    \"Checkpoint feature requires unique node names in the strategy metadata\" \n}\n```\n\nMake sure to set `uniqueNames = true` in your strategy metadata when using this feature.\n\n<!-- File: docs/docs/agents/basic-agents.md (Tokens: ~5226 | Category: subagent-persona) -->\n# Basic agents\n\nA basic agent uses a predefined strategy with a simple execution flow that works for most common use cases.\nIt accepts a string input (a question, request, or task description) and sends this input to the configured LLM.\nThe LLM may decide to call provided tools.\nThe agent will execute the tools and send the results back to the LLM.\nThis repeats until the LLM does not request any more tool calls and returns a string response.\nThe agent then outputs this response.\n\nIn [Graph-based agents](graph-based-agents.md),\nyou can see how to re-create the predefined strategy graph used by basic agents.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\n## Create a minimal agent\n\nTo create the most basic agent, instantiate [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html)\nand provide a [prompt executor](../prompts/prompt-executors.md) with a [language model](../model-capabilities.md#creating-a-model-llmodel-configuration):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n\n    This agent will expect a string as input and return a string as output.\n    To run the agent, use the `run()` function with some user input:\n\n    ```kotlin\n    fun main() = runBlocking {\n        val result = agent.run(\"Hello! How can you help me?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-basic-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n\n    This agent expects a string as input and returns a string as output.\n    To run the agent, use the `run()` method with some user input:\n\n    ```java\n    String result = agent.run(\"Hello! How can you help me?\");\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleBasicJava01.java -->\n\nThe agent will return a generic answer, such as:\n\n```text\nI can assist with a wide range of topics and tasks. Here are some examples:\n\n1. **Answering questions**: I can provide information on various subjects, from science and history to entertainment and culture.\n2. **Generating text**: I can help with writing tasks, such as suggesting alternative phrases, providing definitions, or even creating entire articles or stories.\n3. **Translation**: I can translate text from one language to another, including popular languages such as Spanish, French, German, Chinese, and many more.\n4. **Conversation**: I can engage in natural-sounding conversations, using context and understanding to respond to questions and statements.\n5. **Brainstorming**: I can help generate ideas for creative projects, such as writing stories, composing music, or coming up with business ideas.\n6. **Learning**: I can help with language learning, explaining grammar rules, vocabulary, and pronunciation.\n7. **Calculations**: I can perform mathematical calculations, including basic arithmetic, algebra, and more advanced math concepts.\n\nWhat's on your mind? Do you have a specific question, topic, or task you'd like to tackle?\n```\n<!--- KNIT example-basic-01.txt -->\n\n## Add a system prompt\n\nProvide a [system message](../prompts/prompt-creation/index.md#system-message) to define the agent's role\nas well as the purpose, context, and instructions related to the task.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o\n    )\n    ```\n    <!--- KNIT example-basic-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava02.java -->\n\nThe instructions in the system prompt will guide the agent's response:\n\n```text\nI'm here to help you navigate the wild world of internet memes!\n\nWhat's on your mind? Are you trying to understand a specific meme, need help finding a popular joke, or perhaps want some recommendations for trending memes? Let me know, and I'll do my best to provide you with some LOLs!\n```\n<!--- KNIT example-basic-02.txt -->\n\n## Configure LLM output\n\nYou can provide some [LLM parameters](../llm-parameters.md#llm-parameter-reference) directly to the agent constructor \n(Kotlin) or via the builder methods (Java) to customize the behavior of the LLM.\nFor example, use the `temperature` parameter to adjust the randomness of the generated responses:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7\n    )\n    ```\n    <!--- KNIT example-basic-java-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava03 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava03.java -->\n\nHere are some response examples with different temperature values:\n\n=== \"0.4\"\n    \n    ```text\n    I'm here to help you navigate the wild world of internet memes! Whether you're looking for explanations, examples, or just want to share a meme with someone, I'm your go-to expert. What's on your mind? Got a specific meme in mind that's got you curious? Or maybe you need some meme-related advice? Fire away!\n    ```\n    <!--- KNIT example-basic-03.txt -->\n\n=== \"0.7\"\n\n    ```text\n    I'm here to help you navigate the wild world of internet memes!\n    \n    What's on your mind? Need help understanding a specific meme, finding a popular joke or trend, or maybe even creating your own meme? Let's get this meme party started!\n    ```\n    <!--- KNIT example-basic-04.txt -->\n\n=== \"1.0\"\n\n    ```text\n    I'd be happy to help you navigate the wild world of internet memes!\n    \n    Whether you're looking for explanations of classic memes, suggestions for new ones to try out, or just want to discuss your favorite meme culture trends, I'm here to assist. What's on your mind?\n    \n    Do you have a specific question about memes (e.g., \"What does this meme mean?\"), or are you looking for some meme-related recommendations (e.g., \"Can you recommend a funny meme to share with friends?\"). Let me know how I can help!\n    ```\n    <!--- KNIT example-basic-05.txt -->\n\n## Add tools\n\nAgents can use [tools](../tools/index.md) to perform specific tasks.\n\nFirst, create a tool by annotating a function (Kotlin) or method (Java) with the [`@Tool`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools.annotations/-tool/index.html) annotation:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    -->\n    ```kotlin\n    @Tool\n    @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    ```\n\n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        }\n    )\n    ```\n    <!--- KNIT example-basic-03.kt -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n    If the agent decides to ask the user a question,\n    it can call this tool that writes to `stdout` via `println()` and reads from `stdin` via `readln()`.\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava04 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n    ```\n    \n    Then, use the [`ToolRegistry`](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html) to make this tool available to the agent:\n\n    ```java\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava04.java -->\n\n    In the example, `askUser` is a tool that helps the agent maintain a conversation with the user via printing and reading from the console.\n\nHere is an example interaction with the agent:\n\n```text\nAgent: Which meme would you like me to explain? Please choose from: Grumpy Cat, Success Kid, or Doge.\n\nUser: Explain Doge\n\nAgent:\n\n**Doge**\n\nDoge is a popular internet meme that originated on the imageboard website 4chan in 2013. The meme features a picture of a Shiba Inu dog with a caption written in a playful, exaggerated style.\n\nThe typical format of a Doge meme includes:\n\n* A photo of a Shiba Inu dog\n* A caption written in all capital letters, using a intentionally simplistic and childlike tone\n* Exaggerated or made-up words or phrases, often used to convey a humorous or nonsensical idea\n\nExamples of Doge memes might include:\n\n* \"Such wow. Such happy.\"\n* \"I had fun today!\"\n* \"Wow, I am good at napping.\"\n\nThe meme is known for its lighthearted and playful tone, and is often used to express excitement, happiness, or silliness. The meme has since become a cultural phenomenon, with countless variations and parodies emerging online.\n```\n<!--- KNIT example-basic-06.txt -->\n\n## Adjust agent iterations\n\nTo avoid infinite loops, Koog allows any agent to take a limited number of steps (50 by default).\nUse the `maxIterations` parameter to either increase this limit if you expect the agent to require more steps\n(such as tool calls and LLM requests) or decrease it for agents that require only a few steps.\nFor example, a simple agent described here is not likely to require more than 10 steps:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-basic-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava05 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava05.java -->\n\n!!! tip\n\n    Instead of passing the model, temperature, max iterations, and other parameters directly to the Kotlin constructor \n    or Java builder, you can also define and pass them as a separate configuration object.\n    For more information, see [Agent configuration](index.md#agent-configuration).\n\n## Handle events during agent runtime\n\nTo assist with testing and debugging, as well as making hooks for chained agent interactions,\nKoog provides the [EventHandler](https://api.koog.ai/agents/agents-features/agents-features-event-handler/ai.koog.agents.features.eventHandler.feature/-event-handler/index.html) feature.\n\n=== \"Kotlin\"\n\n    Call the `handleEvents()` function inside the agent constructor lambda to install the feature and register event handlers:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.features.eventHandler.feature.handleEvents\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    @Tool\n    @LLMDescription(\"Asks the user a question by sending it to stdout and returns the answer from stdin\")\n    fun askUser(\n        @LLMDescription(\"Question from the agent\")\n        question: String\n    ): String {\n        println(question)\n        return readln()\n    }\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        systemPrompt = \"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\",\n        llmModel = OpenAIModels.Chat.GPT4o,\n        temperature = 0.7,\n        toolRegistry = ToolRegistry {\n            tool(::askUser)\n        },\n        maxIterations = 10\n    ){\n        handleEvents {\n            // Handle tool calls\n            onToolCallStarting { eventContext ->\n                println(\"Tool called: ${eventContext.toolName} with args ${eventContext.toolArgs}\")\n            }\n        }\n    }\n    ```\n    <!--- KNIT example-basic-05.kt -->\n\n=== \"Java\"\n    Use the `.install()` method on the agent builder to register event handlers with `EventHandler.Feature`:\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.agents.features.eventHandler.feature.EventHandler;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import java.util.Scanner;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;\n    class exampleBasicJava06 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create a ToolSet class\n    class UserConversationTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Ask the user a question by sending it to stdout and return the answer from stdin\")\n        public String askUser(\n            @LLMDescription(\"Question from the agent\")\n            String question\n        ) {\n            System.out.println(question);\n            Scanner scanner = new Scanner(System.in);\n            return scanner.nextLine();\n        }\n    }\n\n    // In main method:\n    UserConversationTools askUser = new UserConversationTools();\n\n    ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(askUser)\n            .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .systemPrompt(\"You are an expert in internet memes. Be helpful, friendly, and answer user questions concisely, showing your knowledge of memes.\")\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .temperature(0.7)\n        .toolRegistry(toolRegistry)\n        .maxIterations(10)\n        .install(EventHandler.Feature, config -> {\n            config.onToolCallStarting(eventContext -> {\n                System.out.println(\"Tool called: \" + eventContext.getToolName() +\n                    \" with args \" + eventContext.getToolArgs());\n            });\n        })\n        .build();\n    ```\n    <!--- KNIT exampleBasicJava06.java -->\n\nThe agent will now output something similar to the following when it calls the `askUser` tool:\n\n```text\nTool called: askUser with args {\"question\":\"Which meme would you like me to explain?\"}\n```\n<!--- KNIT example-basic-07.txt -->\n\nFor more information about Koog agent features, see [Features](../features/index.md).\n\n## Next steps\n\n- Learn more about building [graph-based agents](graph-based-agents.md) and [functional agents](functional-agents.md)\n\n<!-- File: docs/docs/agents/functional-agents.md (Tokens: ~2662 | Category: subagent-persona) -->\n# Functional agents\n\nWith functional agents, you implement the logic as a function that handles user input,\ninteracts with LLMs, calls tools if necessary, and produces the final output.\nCompared to [graph-based agents](graph-based-agents.md),\nthis usually means faster prototyping with the following downsides:\n\n- Not easy to visualize\n- No state persistence\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to implement a functional strategy to quickly prototype some custom logic for your agent.\n\n## Create a minimal functional agent\n\nTo create a minimal functional agent, use the same [`AIAgent`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent/index.html) interface as for a [basic agent](basic-agents.md) and pass an \ninstance of [`AIAgentFunctionalStrategy`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent/-a-i-agent-functional-strategy/index.html) to it.\nYou can define a functional strategy that expects an input and returns an output, makes one LLM call, then returns the \ncontent of the assistant message from the response.\n\nIn Kotlin, the most convenient way is to use the `functionalStrategy {...}` DSL method. In Java, you can use the `functionalStrategy` method on the `AIAgent` builder.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val strategy = functionalStrategy<String, String> { input ->\n        val response = requestLLM(input)\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgent.run(\"What is 12 × 9?\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy(\"mathStrategy\", (AIAgentFunctionalContext context, String input) -> {\n            Message.Response response = context.requestLLM(input);\n            if (response instanceof Message.Assistant) {\n                return ((Message.Assistant) response).getContent();\n            }\n            return \"\";\n        })\n        .build();\n\n    String result = mathAgent.run(\"What is 12 × 9?\");\n    System.out.println(result);\n    ```\n   <!--- KNIT example-functional-agent-java-01.java -->\n\nThe agent can produce the following output:\n\n```text\nThe answer to 12 × 9 is 108.\n```\n<!--- KNIT example-functional-agent-01.txt -->\n\n## Make sequential LLM calls\n\nYou can extend the previous strategy to make multiple sequential LLM calls:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    -->\n    ```kotlin\n    fun Message.Assistant.text(): String =\n        parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // The first LLM call produces an initial draft based on the user input\n        val draft = requestLLM(\"Draft: $input\").text()\n        // The second LLM call improves the initial draft\n        val improved = requestLLM(\"Improve and clarify.\").text()\n        // The final LLM call formats the improved text and returns the result\n        requestLLM(\"Format the result as bold.\").text()\n    }\n    ```\n    <!--- KNIT example-functional-agent-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(simpleOllamaAIExecutor(\"http://localhost:11434\"))\n        .systemPrompt(\"You are a precise math assistant.\")\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .functionalStrategy((AIAgentFunctionalContext context, String input) -> {\n            // The first LLM call produces an initial draft based on the user input\n            Message.Response draftResponse = context.requestLLM(\"Draft: \" + input);\n            String draft = \"\";\n            if (draftResponse instanceof Message.Assistant) {\n                draft = ((Message.Assistant) draftResponse).getContent();\n            }\n\n            // The second LLM call improves the initial draft\n            Message.Response improvedResponse = context.requestLLM(\"Improve and clarify.\");\n            String improved = \"\";\n            if (improvedResponse instanceof Message.Assistant) {\n                improved = ((Message.Assistant) improvedResponse).getContent();\n            }\n\n            // The final LLM call formats the improved text and returns the result\n            Message.Response finalResponse = context.requestLLM(\"Format the result as bold.\");\n            if (finalResponse instanceof Message.Assistant) {\n                return ((Message.Assistant) finalResponse).getContent();\n            }\n            return \"\";\n        })\n        .build();\n    ```\n    <!--- KNIT example-functional-agent-java-02.java -->\n\nThe agent can produce the following output:\n\n```text\nTo calculate the product of 12 and 9, we multiply these two numbers together.\n\n12 × 9 = **108**\n```\n<!--- KNIT example-functional-agent-02.txt -->\n\n## Add tools\n\nIn many cases, a functional agent needs to complete specific tasks,\nsuch as reading and writing data, calling APIs, or performing other deterministic operations.\nIn Koog, you expose such capabilities as [tools](../tools/index.md) and let the LLM decide when to call them.\n\nHere is what you need to do:\n\n1. Create an [annotation-based tool](../tools/annotation-based-tools.md).\n2. Add it to a tool registry and pass the registry to the agent.\n3. Make sure the agent strategy can identify tool calls in LLM responses, execute the requested tools,\n   send their results back to the LLM, and repeat the process until there are no tool calls remaining.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.functionalStrategy\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n\n    val toolRegistry = ToolRegistry {\n        tool(MathTools()::multiply)\n    }\n\n    val strategy = functionalStrategy<String, String> { input ->\n        // Send the user input to the LLM\n        var response = requestLLM(input)\n\n        // Only loop while the LLM requests tools\n        var toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        while (toolCalls.isNotEmpty()) {\n            // Execute the tools and return the results\n            val results = executeTools(toolCalls)\n            // Send the tool results back to the LLM. The LLM may call more tools or return a final output\n            response = sendToolResults(results)\n            toolCalls = response.parts.filterIsInstance<MessagePart.Tool.Call>()\n        }\n\n        // When no tool calls remain, extract and return the assistant message content from the response\n        response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }\n    }\n\n    val mathAgentWithTools = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        toolRegistry = toolRegistry,\n        strategy = strategy\n    )\n\n    fun main() = runBlocking {\n        val result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-functional-agent-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    @LLMDescription(description = \"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(description = \"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n\n    public static void main(String[] args) {\n        MathTools mathTools = new MathTools();\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(mathTools)\n            .build();\n\n        AIAgent<String, String> mathAgentWithTools = AIAgent.builder()\n            .promptExecutor(SimpleLLMExecutorsKt.simpleOllamaAIExecutor(\"http://localhost:11434\"))\n            .llmModel(OllamaModels.Meta.LLAMA_3_2)\n            .toolRegistry(toolRegistry)\n            .functionalStrategy(\"mathWithTools\", (AIAgentFunctionalContext context, String input) -> {\n                // Send the user input to the LLM\n                List<Message.Response> responses = context.requestLLMMultiple(input);\n\n                // Only loop while the LLM requests tools\n                while (context.containsToolCalls(responses)) {\n                    // Extract tool calls from the response\n                    List<MessagePart.Tool.Call> pendingCalls = context.extractToolCalls(responses);\n                    // Execute the tools and return the results\n                    List<ReceivedToolResult> results = context.executeMultipleTools(pendingCalls, false);\n                    // Send the tool results back to the LLM\n                    responses = context.sendMultipleToolResults(results);\n                }\n\n                // Extract and return the assistant message content from the response\n                Message.Response finalResponse = responses.get(0);\n                if (finalResponse instanceof Message.Assistant) {\n                    return ((Message.Assistant) finalResponse).getContent();\n                }\n                return \"\";\n            })\n            .build();\n\n        String result = mathAgentWithTools.run(\"Multiply 3 by 4, then multiply the result by 5.\");\n        System.out.println(result);\n    }\n    ```\n   <!--- KNIT example-functional-agent-java-03.java -->\n\nThe agent can produce the following output:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nThe result of multiplying 3 by 4 is 12. Multiplying 12 by 5 gives us a final answer of 60.\n```\n<!--- KNIT example-functional-agent-03.txt -->\n\n## Next steps\n\n- Learn how to create [graph-based agents](graph-based-agents.md)\n\n<!-- File: docs/docs/agents/graph-based-agents.md (Tokens: ~7132 | Category: subagent-persona) -->\n# Graph-based agents\n\nWith graph-based agents, you model the behavior as an explicit state machine:\nnodes of a graph strategy represent actions (LLM calls, tool execution)\nand edges represent data flow between nodes.\n\nThe main advantages of graph-based agents are:\n\n- Easy to visualize\n- State persistence\n- Composable architecture\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you are running Llama 3.2 locally via Ollama.\n\nThis page describes how to re-create the strategy graph used by [basic agents](basic-agents.md).\nIt sends a request to an LLM and then either outputs the response (if the LLM responded with an assistant message)\nor executes a tool (if the LLM requested a tool call).\nIn case of a tool call, the agent sends the tool result to the LLM\nand then either outputs the response or executes a tool.\n\nHere is an illustration of the strategy graph:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph TB\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    subgraph nodeExecuteTool\n        executeTool(Execute tool call)\n    end\n    \n    subgraph nodeSendToolResult\n        sendToolResult(Request LLM)\n    end\n    \n    Input --String--> llmRequest\n    llmRequest --Message.Assistant--> onToolCalls{{onToolCalls}}\n    llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}}\n    onTextMessage --String--> Output\n    onToolCalls --ToolCalls--> executeTool --ReceivedToolResults--> sendToolResult\n    sendToolResult --Message.Assistant--> onToolCalls\n    sendToolResult --Message.Assistant--> onTextMessage\n```\n<!--- KNIT example-graph-agents-01.txt -->\n\n\n## Build a strategy graph\n\nIn Koog, you implement a strategy using [`AIAgentGraphStrategyBuilder`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/-a-i-agent-graph-strategy-builder/index.html).\nJust like every node has an input and output type,\nthe strategy as a whole also defines some input and output type.\nThis example assumes that the input and output types are strings,\nwhich means the agent implementing this strategy will expect a string and return a string.\n\nTo create a strategy, use the [`strategy()`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.dsl.builder/strategy.html) function with two generics as the input and output types,\nprovide a unique identifier for the strategy, and define the nodes and edges.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n        \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    ```\n    <!--- KNIT example-graph-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    ```\n    <!--- KNIT exampleGraphAgentsJava01.java -->\n\nThis example uses only [predefined nodes](../nodes-and-components.md),\nbut you can also create [custom nodes](../custom-nodes.md).\n\nEvery strategy graph must have a path from `nodeStart` to `nodeFinish` connected by [edges](../custom-strategy-graphs.md#edges).\nEdges can have conditions to determine when to follow a particular edge.\nEdges can also transform the output of the previous node before passing it to the next one.\nThis is necessary to connect nodes that have non-matching output and input types.\n\nIn the previous example, `onToolCalls { true }` means that the edge will follow\nonly if the previous node returned an assistant message containing at least one tool call (`MessagePart.Tool.Call`).\n\nWhen using `onTextMessage { true }`, the edge will follow\nonly if the previous node returned an assistant message containing text parts (`MessagePart.Text`).\nThis function also extracts and joins the text content of those parts,\neffectively transforming `Message.Assistant` to `String`, because `nodeFinish` expects a string.\n\n!!! tip\n\n    Instead of `onTextMessage { true }`, you can do the following:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onMessageParts(MessagePart.Text::class) transformed { it.joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-02.kt -->\n\n    Or:\n\n    <!--- INCLUDE\n    import ai.koog.prompt.message.Message\n    import ai.koog.prompt.message.MessagePart\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```kotlin\n    onCondition { it is Message.Assistant } transformed { (it as Message.Assistant).parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { part -> part.text } }\n    ```\n    <!--- KNIT example-graph-agents-03.kt -->\n\n## Create and run the agent\n\nLet's create an agent instance with this strategy and run it:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    \n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-04.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava02 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n        .withInput(String.class)\n        .withOutput(String.class);\n\n    var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n    var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n    var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(calculatorAgentStrategy.nodeStart)\n        .to(nodeSendInput)\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendInput)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n    calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(calculatorAgentStrategy.nodeFinish)\n        .onTextMessage()\n        .build());\n    calculatorAgentStrategy.edge(AIAgentEdge.builder()\n        .from(nodeSendToolResult)\n        .to(nodeExecuteTool)\n        .onToolCalls()\n        .build());\n\n    var promptExecutor = PromptExecutor.builder()\n        .ollama(\"http://localhost:11434\")\n        .build();\n\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .build();\n\n        String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n        System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava02.java -->\n\nWhen you run this agent, it will respond with something like this:\n\n```text\nTo calculate this, I'll follow the order of operations:\n\n1. Multiply 3 by 4: 3 * 4 = 12\n2. Multiply the result by 5: 12 * 5 = 60\n3. Add 10: 60 + 10 = 70\n4. Add 123: 70 + 123 = 193\n\nThe final answer is 193.\n```\n<!--- KNIT example-graph-agents-02.txt -->\n\nHowever, since this agent doesn't have any tools, the LLM never returns a tool call \nand simply generates the whole answer.\nThis is what effectively happens:\n\n```mermaid\n---\nconfig:\n  flowchart:\n    defaultRenderer: \"elk\"\n---\ngraph LR\n    subgraph nodeStart\n        Input\n    end\n    \n    subgraph nodeFinish\n        Output\n    end\n    \n    subgraph nodeSendInput\n        llmRequest(Request LLM)\n    end\n    \n    Input --String--> llmRequest --Message.Assistant--> onTextMessage{{onTextMessage}} --String--> Output\n```\n<!--- KNIT example-graph-agents-03.txt -->\n\nEven though it is correct in this case, the answer will depend on the arithmetic abilities of the underlying LLM.\nTo make sure the calculations are correct, we should provide the agent with math tools.\nThen the LLM will be able to decide to call tools that perform the calculations deterministically.\n\n## Add tools\n\nDefine [tools](../tools/index.md) for performing math operations and add them to a [ToolRegistry](https://api.koog.ai/agents/agents-tools/ai.koog.agents.core.tools/-tool-registry/index.html):\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    -->\n    ```kotlin\n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    ```\n    <!--- KNIT example-graph-agents-05.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOllamaAIExecutor;\n    class exampleGraphAgentsJava03 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    @LLMDescription(\"Tools for performing math operations\")\n    public static class MathTools implements ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        public int add(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n            return a + b;\n        }\n\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        public int multiply(int a, int b) {\n            // This is not necessary, but it helps to see the tool call in the console output\n            System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n            return a * b;\n        }\n    }\n    public static void main(String[] args) {\n        ToolRegistry toolRegistry = ToolRegistry.builder()\n            .tools(new MathTools())\n            .build();\n    }\n    ```\n    <!--- KNIT exampleGraphAgentsJava03.java -->\n\nAdd the tool registry to the agent configuration:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        strategy = calculatorAgentStrategy,\n        toolRegistry = toolRegistry\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-06.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava04 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class);\n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\");\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava04.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nThe output from the first operation was multiplied by 5:\n5 * 12 = 60\n\nThen, 10 was added to the result:\n60 + 10 = 70\n\nFinally, 123 was added to the result:\n70 + 123 = 193\n```\n<!--- KNIT example-graph-agents-04.txt -->\n\nAccording to this output, the agent correctly performed the calculations, but it only called the `multiply` tool once\ninstead of calling the corresponding tool for every operation.\nWe can help the agent by describing its role and providing instructions for using appropriate tools in the system prompt.\n\n## Provide a system prompt\n\nA [system prompt](../prompts/prompt-creation/index.md#system-message) defines the agent's role and instructions for performing tasks.\nIn our example, it is important to describe how the agent should process complex multistep calculations:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.dsl.builder.strategy\n    import ai.koog.agents.core.dsl.extension.*\n    import ai.koog.agents.core.dsl.extension.nodeExecuteTools\n    import ai.koog.agents.core.dsl.extension.nodeLLMRequest\n    import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResults\n    import ai.koog.agents.core.tools.ToolRegistry\n    import ai.koog.agents.core.tools.annotations.LLMDescription\n    import ai.koog.agents.core.tools.annotations.Tool\n    import ai.koog.agents.core.tools.reflect.ToolSet\n    import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor\n    import ai.koog.prompt.executor.ollama.client.OllamaModels\n    import kotlinx.coroutines.runBlocking\n    \n    @LLMDescription(\"Tools for performing math operations\")\n    class MathTools : ToolSet {\n        @Tool\n        @LLMDescription(\"Adds two numbers and returns the result\")\n        fun add(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Adding $a and $b...\")\n            return a + b\n        }\n        @Tool\n        @LLMDescription(\"Multiplies two numbers and returns the result\")\n        fun multiply(a: Int, b: Int): Int {\n            // This is not necessary, but it helps to see the tool call in the console output\n            println(\"Multiplying $a and $b...\")\n            return a * b\n        }\n    }\n    \n    val toolRegistry = ToolRegistry {\n        tools(MathTools())\n    }\n    \n    val calculatorAgentStrategy = strategy<String, String>(\"Simple calculator\") {\n        val nodeSendInput by nodeLLMRequest()\n        val nodeExecuteTool by nodeExecuteTools()\n        val nodeSendToolResult by nodeLLMSendToolResults()\n    \n        edge(nodeStart forwardTo nodeSendInput)\n        edge(nodeSendInput forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendInput forwardTo nodeExecuteTool onToolCalls { true })\n        edge(nodeExecuteTool forwardTo nodeSendToolResult)\n        edge(nodeSendToolResult forwardTo nodeFinish onTextMessage { true })\n        edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCalls { true })\n    }\n    -->\n    ```kotlin\n    val mathAgent = AIAgent(\n        promptExecutor = simpleOllamaAIExecutor(),\n        llmModel = OllamaModels.Meta.LLAMA_3_2,\n        systemPrompt = \"\"\"\n                    You are a simple calculator assistant.\n                    You can add and multiply two numbers using the 'add' and 'multiply' tools.\n                    When the user provides input, extract the numbers and operations they requested.\n                    Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result.\n                    Always respond with a clear, friendly message showing the calculation and result.\n                    \"\"\".trimIndent(),\n        toolRegistry = toolRegistry,\n        strategy = calculatorAgentStrategy\n    )\n    \n    fun main() = runBlocking {\n        val result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-graph-agents-07.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.agent.entity.AIAgentEdge;\n    import ai.koog.agents.core.agent.entity.AIAgentGraphStrategy;\n    import ai.koog.agents.core.agent.entity.AIAgentNode;\n    import ai.koog.agents.core.tools.ToolRegistry;\n    import ai.koog.agents.core.tools.annotations.LLMDescription;\n    import ai.koog.agents.core.tools.annotations.Tool;\n    import ai.koog.agents.core.tools.reflect.ToolSet;\n    import ai.koog.prompt.executor.ollama.client.OllamaModels;\n    import ai.koog.prompt.message.Message;\n    import ai.koog.prompt.message.MessagePart;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import java.util.stream.Collectors;\n    class exampleGraphAgentsJava05 {\n        @LLMDescription(\"Tools for performing math operations\")\n        public static class MathTools implements ToolSet {\n            @Tool\n            @LLMDescription(\"Adds two numbers and returns the result\")\n            public int add(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Adding \" + a + \" and \" + b + \"...\");\n                return a + b;\n        }\n            @Tool\n            @LLMDescription(\"Multiplies two numbers and returns the result\")\n            public int multiply(int a, int b) {\n                // This is not necessary, but it helps to see the tool call in the console output\n                System.out.println(\"Multiplying \" + a + \" and \" + b + \"...\");\n                return a * b;\n            }\n        }\n        public static void main(String[] args) {\n            ToolRegistry toolRegistry = ToolRegistry.builder()\n                .tools(new MathTools())\n                .build();\n            var calculatorAgentStrategy = AIAgentGraphStrategy.builder(\"Simple calculator\")\n                .withInput(String.class)\n                .withOutput(String.class); \n            var nodeSendInput = AIAgentNode.llmRequest(\"nodeSendInput\");\n            var nodeExecuteTool = AIAgentNode.executeTools(\"nodeExecuteTool\");\n            var nodeSendToolResult = AIAgentNode.llmSendToolResults(\"nodeSendToolResult\"); \n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(calculatorAgentStrategy.nodeStart)\n                .to(nodeSendInput)\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendInput)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            calculatorAgentStrategy.edge(nodeExecuteTool, nodeSendToolResult);\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(calculatorAgentStrategy.nodeFinish)\n                .onTextMessage()\n                .build());\n            calculatorAgentStrategy.edge(AIAgentEdge.builder()\n                .from(nodeSendToolResult)\n                .to(nodeExecuteTool)\n                .onToolCalls()\n                .build());\n            var promptExecutor = PromptExecutor.builder()\n                .ollama(\"http://localhost:11434\")\n                .build();\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    AIAgent<String, String> mathAgent = AIAgent.builder()\n        .promptExecutor(promptExecutor)\n        .llmModel(OllamaModels.Meta.LLAMA_3_2)\n        .systemPrompt(\"You are a simple calculator assistant. You can add and multiply two numbers using the 'add' and 'multiply' tools. When the user provides input, extract the numbers and operations they requested. Use the appropriate tool for the first operation, then the next one, and so on, until you calculate the result. Always respond with a clear, friendly message showing the calculation and result.\")\n        .graphStrategy(calculatorAgentStrategy.build())\n        .toolRegistry(toolRegistry)\n        .build();\n\n    String result = mathAgent.run(\"Multiply 3 by 4, then multiply the result by 5, then add 10, then add 123.\", null);\n    System.out.println(result);\n    ```\n    <!--- KNIT exampleGraphAgentsJava05.java -->\n\nWhen you run the agent now, it will respond with something like this:\n\n```text\nMultiplying 3 and 4...\nMultiplying 12 and 5...\nAdding 60 and 10...\nAdding 70 and 123...\nThe final result is: 193\n```\n<!--- KNIT example-graph-agents-05.txt -->\n\nAs you can see, the agent now correctly calls the appropriate tool for each operation,\nensuring that it performs the calculations deterministically instead of risking a hallucinated result.\n\n## Next steps\n\n- Compare to [functional agents](functional-agents.md) and [planner agents](planner-agents/index.md)\n- Enhance your agent by [installing features](../features/index.md)\n- Improve the predictability and reliability with [structured output](../structured-output.md)\n\n<!-- File: docs/docs/agents/index.md (Tokens: ~1467 | Category: subagent-persona) -->\n# Agents\n\nAI agents are autonomous systems that can reason, make decisions,\ninteract with the environment, and take actions to achieve a specific goal.\nIn Koog, an AI agent is more than just a wrapper around an LLM;\nit is a structured, type-safe state machine designed for the JVM ecosystem.\n\nKoog agents are built around the following core concepts:\n\n- A [prompt executor](../prompts/prompt-executors.md) manages and executes prompts,\n  enabling the agent to interact with LLMs for reasoning and decision-making.\n- A [strategy](../nodes-and-components.md) defines the agent's workflow.\n  It can be in the form of a directed graph, a function, or a planner.\n  See [Agent types](#agent-types).\n- An agent can use [tools](../tools/index.md) to interact with external data sources and services.\n- You can extend and enhance the functionality of AI agents using [features](../features/index.md).\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\n## Agent types\n\nDepending on the task you need to perform, Koog provides several agent types:\n\n- [Basic agents](basic-agents.md) are ideal for simple tasks that don't require any custom logic.\n  These agents implement a predefined strategy that works for most common use cases.\n- [Graph-based agents](graph-based-agents.md) provide full control and flexibility of the agent's workflow, state management, and visualization.\n- [Functional agents](functional-agents.md) enable you to quickly prototype custom logic as a function with access to the agent's context.\n- [Planner agents](planner-agents/index.md) can autonomously plan and execute multistep tasks through iterative cycles until they reach a desired final state.\n\n## Agent configuration\n\nAgent configuration defines the agent's execution parameters,\nincluding the initial prompt, language model, and iteration limits.\n\n!!! tip\n\n    For information about creating and running a minimal agent, see [Quickstart](../quickstart.md).\n\nFor simple agents, besides the mandatory prompt executor and language model,\nyou can specify the initial system prompt and some other parameters directly in the agent constructor:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")),\n        llmModel = OpenAIModels.Chat.GPT4o,\n        systemPrompt = \"You are a helpful assistant.\",\n        temperature = 0.7,\n        maxIterations = 10\n    )\n    ```\n    <!--- KNIT example-agent-config-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"YOUR_API_KEY\")))\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful assistant.\")\n        .temperature(0.7)\n        .maxIterations(10)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-01.java -->\n\nAlternatively, you can create an instance of [`AIAgentConfig`](https://api.koog.ai/agents/agents-core/ai.koog.agents.core.agent.config/-a-i-agent-config/index.html)\nto define the agent's behavior and parameters more granularly, then pass it to the agent constructor.\nThis enables you to define complex prompts with multiple messages,\nconversation history, LLM parameters, and additional execution parameters.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import ai.koog.prompt.params.LLMParams\n    -->\n    ```kotlin\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\n            id = \"assistant\",\n            params = LLMParams(\n                temperature = 0.7\n            )\n        ) {\n            system(\"You are a helpful assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 10\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        agentConfig = agentConfig\n    )\n    ```\n    <!--- KNIT example-agent-config-02.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    /**\n    -->\n    <!--- SUFFIX\n    **/\n    -->\n    ```java\n    Prompt prompt = Prompt.builder(\"assistant\")\n        .system(\"You are a helpful assistant.\")\n        .build()\n        .withParams(new LLMParams(\n            0.7,         // temperature\n            null,        // maxTokens\n            1,           // numberOfChoices\n            null,        // speculation\n            null,        // schema\n            LLMParams.ToolChoice.Auto.INSTANCE, // toolChoice\n            null,        // user\n            null         // additionalProperties\n        ));\n\n    AIAgentConfig agentConfig = AIAgentConfig.builder(OpenAIModels.Chat.GPT4o)\n        .prompt(prompt)\n        .maxAgentIterations(10)\n        .build();\n\n    AIAgent<String, String> agent = AIAgent.builder()\n        .promptExecutor(simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")))\n        .agentConfig(agentConfig)\n        .build();\n    ```\n    <!--- KNIT example-agent-config-java-02.java -->\n\nHere are the parameters of `AIAgentConfig`:\n\n- `prompt` defines the initial [prompt](../prompts/prompt-creation/index.md) and [LLM parameters](../llm-parameters.md).\n\n- `model` specifies the language model with which the agent interacts.\n  You can use one of the predefined models or [create a custom model configuration](../model-capabilities.md#creating-a-model-llmodel-configuration).\n\n- `maxAgentIterations` limits the maximum number of steps the agent can take before it terminates.\n  Each step is a [node](../nodes-and-components.md) in the agent's workflow.\n\n- `missingToolsConversionStrategy` defines a strategy for handling missing tools during agent execution.\n\n[//]: # (TODO write about missing tools in the TOols section and link from here)\n\n- `responseProcessor` can be used to define a custom response processor.\n  For example, it can moderate and validate the response content, change the response format, or log the response.\n\n[//]: # (TODO write about response processing somewhere?)\n\n<!-- File: docs/docs/agents/planner-agents/goap-agents.md (Tokens: ~4253 | Category: subagent-persona) -->\n---\nstatus: beta\n---\n\n# GOAP agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nGOAP is an algorithmic planning approach that uses [A* search] find optimal action sequences\nthat satisfy the goal conditions while minimizing the total cost.\nUnlike [LLM-based planners](llm-based-planners.md) that use an LLM to generate plans,\na GOAP agent algorithmically discovers action sequences based on predefined goals and actions.\n\nGOAP planners work with three main concepts:\n\n- **State**: Represents the current state of the world.\n- **Actions**: Define what can be done, including preconditions, effects (beliefs), costs, and execution logic.\n- **Goals**: Define target conditions, heuristic costs, and value functions.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nIn Koog, you define a GOAP agent using a DSL by declaratively specifying the goals and actions.\n\nTo create a GOAP agent, you need to:\n\n1. Define the state as a data class with properties representing various aspects specific to your goal.\n2. Create a [GOAPPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner/index.html) instance using the [goap()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/goap.html) function.\n    1. Define actions with preconditions and beliefs using the [action()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/action.html) function.\n    2. Define goals with completion conditions using the [goal()](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.goap/-g-o-a-p-planner-builder/goal.html) function.\n3. Wrap the planner with [AIAgentPlannerStrategy](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-a-i-agent-planner-strategy/index.html) and pass it to the [PlannerAIAgent](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner/-planner-a-i-agent/index.html) constructor.\n\n!!! note\n\n    The planner selects individual actions and their sequence.\n    Each action includes a precondition that must hold true for the action to be executed\n    and a belief that defines the predicted outcome.\n    For more information about beliefs, see [State beliefs compared to actual execution](#state-beliefs-compared-to-actual-execution).\n\nIn the following example, GOAP handles high-level planning for creating an article\n(outline → draft → review → publish),\nwhile the LLM performs the actual content generation within each action.\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.planner.goap\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.message.MessagePart\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    -->\n    ```kotlin\n    // Define a state for content creation\n    data class ContentState(\n        val topic: String,\n        val hasOutline: Boolean = false,\n        val outline: String = \"\",\n        val hasDraft: Boolean = false,\n        val draft: String = \"\",\n        val hasReview: Boolean = false,\n        val isPublished: Boolean = false\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = draft\n    }\n\n    // Create GOAP planner with LLM-powered actions\n    val planner = goap(\"content-planner\", ::ContentState) {\n        // Define actions with preconditions and beliefs\n        action(\n            name = \"Create outline\",\n            precondition = { state -> !state.hasOutline },\n            belief = { state -> state.copy(hasOutline = true, outline = \"Outline\") },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to create the outline\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Create a detailed outline for an article about: ${state.topic}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasOutline = true, outline = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Write draft\",\n            precondition = { state -> state.hasOutline && !state.hasDraft },\n            belief = { state -> state.copy(hasDraft = true, draft = \"Draft\") },\n            cost = { 2.0 }\n        ) { ctx, state ->\n            // Use LLM to write the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Write an article based on this outline:\\n${state.outline}\")\n                }\n                requestLLM()\n            }\n            state.copy(hasDraft = true, draft = response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text })\n        }\n\n        action(\n            name = \"Review content\",\n            precondition = { state -> state.hasDraft && !state.hasReview },\n            belief = { state -> state.copy(hasReview = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            // Use LLM to review the draft\n            val response = ctx.llm.writeSession {\n                appendPrompt {\n                    user(\"Review this article and suggest improvements:\\n${state.draft}\")\n                }\n                requestLLM()\n            }\n            println(\"Review feedback: ${response.parts.filterIsInstance<MessagePart.Text>().joinToString(\"\\n\") { it.text }}\")\n            state.copy(hasReview = true)\n        }\n\n        action(\n            name = \"Publish\",\n            precondition = { state -> state.hasReview && !state.isPublished },\n            belief = { state -> state.copy(isPublished = true) },\n            cost = { 1.0 }\n        ) { ctx, state ->\n            println(\"Publishing article...\")\n            state.copy(isPublished = true)\n        }\n\n        // Define the goal with a completion condition\n        goal(\n            name = \"Published article\",\n            description = \"Complete and publish the article\",\n            condition = { state -> state.isPublished }\n        )\n    }\n\n    // Create and run the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"writer\") {\n            system(\"You are a professional content writer.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 20\n    )\n\n    val agent = AIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = planner,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        val result = agent.run(\"The Future of AI in Software Development\")\n        println(\"Final state: $result\")\n    }\n    ```\n    <!--- KNIT example-goap-agents-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    import ai.koog.prompt.message.MessagePart;\n    import java.util.stream.Collectors;\n    class exampleGoapAgents01 {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```java\n    // Define a state for content creation\n    static class ContentState extends GoapAgentState<String, String> {\n        public String topic;\n        public boolean hasOutline = false;\n        public String outline = \"\";\n        public boolean hasDraft = false;\n        public String draft = \"\";\n        public boolean hasReview = false;\n        public boolean isPublished = false;\n    \n        public ContentState(String topic) {\n            this.topic = topic;\n        }\n\n        @Override\n        public String getAgentInput() {\n            return topic;\n        }\n\n        public ContentState copy(boolean hasOutline, String outline, boolean hasDraft,\n                                 String draft, boolean hasReview, boolean isPublished) {\n            ContentState state = new ContentState(topic);\n            state.hasOutline = hasOutline;\n            state.outline = outline;\n            state.hasDraft = hasDraft;\n            state.draft = draft;\n            state.hasReview = hasReview;\n            state.isPublished = isPublished;\n            return state;\n        }\n\n        @Override\n        public String provideOutput() {\n            return draft;\n        }\n    }\n\n    public static void main(String[] args) {\n        var promptExecutor = PromptExecutor.builder()\n            .openAI(\"OPENAI_API_KEY\")\n            .build();\n\n        var strategy = Planners.goap(\"content-planner\", ContentState::new)\n            .action(\"Create outline\", builder -> builder\n                .precondition(state -> !state.hasOutline)\n                .belief(state -> state.copy(true, \"Outline\", false, \"\", false, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Create a detailed outline for an article about: \" + state.topic);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(true, response, state.hasDraft, state.draft,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Write draft\", builder -> builder\n                .precondition(state -> state.hasOutline && !state.hasDraft)\n                .belief(state -> state.copy(state.hasOutline, state.outline, true, \"Draft\", false, false))\n                .cost(state -> 2.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Write an article based on this outline:\\n\" + state.outline);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    return state.copy(state.hasOutline, state.outline, true, response,\n                                    state.hasReview, state.isPublished);\n                })\n            )\n            .action(\"Review content\", builder -> builder\n                .precondition(state -> state.hasDraft && !state.hasReview)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, true, false))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    String response = context.llm().writeSession(session -> {\n                        session.appendPrompt(prompt -> {\n                            prompt.user(\"Review this article and suggest improvements:\\n\" + state.draft);\n                            return null;\n                        });\n                        return session.requestLLM().getParts().stream()\n                            .filter(p -> p instanceof MessagePart.Text)\n                            .map(p -> ((MessagePart.Text) p).getText())\n                            .collect(Collectors.joining());\n                    });\n                    System.out.println(\"Review feedback: \" + response);\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, true, state.isPublished);\n                })\n            )\n            .action(\"Publish\", builder -> builder\n                .precondition(state -> state.hasReview && !state.isPublished)\n                .belief(state -> state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                           state.draft, state.hasReview, true))\n                .cost(state -> 1.0)\n                .execute((context, state) -> {\n                    System.out.println(\"Publishing article...\");\n                    return state.copy(state.hasOutline, state.outline, state.hasDraft,\n                                    state.draft, state.hasReview, true);\n                })\n            )\n            .goal(\"Published article\", builder -> builder\n                .description(\"Complete and publish the article\")\n                .condition(state -> state.isPublished)\n            )\n            .build();\n\n        var agent = AIAgent.builder()\n            .plannerStrategy(strategy)\n            .promptExecutor(promptExecutor)\n            .llmModel(OpenAIModels.Chat.GPT4o)\n            .systemPrompt(\"You are a professional content writer.\")\n            .maxIterations(20)\n            .build();\n\n        String result = agent.run(\"The Future of AI in Software Development\");\n        System.out.println(\"Final state: \" + result);\n    }\n    ```\n    <!--- KNIT exampleGoapAgentsJava01.java -->\n    \n\n## Custom cost functions\n\nAs [A* search] uses cost as a factor in finding the optimal sequence of actions,\nyou can define custom cost functions for actions and goals to guide the planner:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val operationDone: Boolean = true,\n        val hasOptimization: Boolean = true\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Expensive operation\",\n        precondition = { true },\n        belief = { state -> state.copy(operationDone = true) },\n        cost = { state ->\n            // Dynamic cost based on state\n            if (state.hasOptimization) 1.0 else 10.0\n        }\n    ) { ctx, state ->\n        // Execute action\n        state.copy(operationDone = true)\n    }\n    ```\n    <!--- KNIT example-goap-agents-02.kt -->\n\n=== \"Java\"\n    \n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents02 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean operationDone = false;\n            public boolean hasOptimization = true;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean operationDone) {\n                MyState state = new MyState(topic);\n                state.operationDone = operationDone;\n                state.hasOptimization = this.hasOptimization;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Expensive operation\", builder -> builder\n        .precondition(state -> true)\n        .belief(state -> state.copy(true))\n        .cost(state -> {\n            // Dynamic cost based on state\n            return state.hasOptimization ? 1.0 : 10.0;\n        })\n        .execute((context, state) -> {\n            // Execute action\n            return state.copy(true);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava02.java -->\n\n## State beliefs compared to actual execution\n\nGOAP distinguishes between the concepts of beliefs (optimistic predictions) and actual execution:\n\n- **Belief**: What the planner thinks will happen, used for planning.\n- **Execution**: What actually happens, used for real state updates.\n\nThis allows the planner to make plans based on expected outcomes while handling actual results properly:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.planner.goap.GoapAgentState\n    import ai.koog.agents.planner.goap\n    data class MyState(\n        val topic: String,\n        val taskComplete: Boolean = true,\n        val attempts: Int = 0\n    ): GoapAgentState<String, String>() {\n        override val agentInput = topic\n        override fun provideOutput(): String = \"\"\n    }\n    fun performComplexTask(): Boolean = true\n    val planner = goap(\"content-planner\", ::MyState) {\n    -->\n    <!--- SUFFIX\n    }\n    -->\n    ```kotlin\n    action(\n        name = \"Attempt complex task\",\n        precondition = { state -> !state.taskComplete },\n        belief = { state ->\n            // Optimistic belief: task will succeed\n            state.copy(taskComplete = true)\n        },\n        cost = { 5.0 }\n    ) { ctx, state ->\n        // Actual execution might fail or have different results\n        val success = performComplexTask()\n        state.copy(\n            taskComplete = success,\n            attempts = state.attempts + 1\n        )\n    }\n    ```\n    <!--- KNIT example-goap-agents-03.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.agents.planner.goap.GoapAgentState;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleGoapAgents03 {\n        public static class MyState extends GoapAgentState<String, String> {\n            public String topic;\n            public boolean taskComplete = false;\n            public int attempts = 0;\n            public MyState(String topic) {\n                this.topic = topic;\n            }\n            @Override\n            public String getAgentInput() {\n                return topic;\n            }\n            public MyState copy(boolean taskComplete, int attempts) {\n                MyState state = new MyState(topic);\n                state.taskComplete = taskComplete;\n                state.attempts = attempts;\n                return state;\n            }\n            @Override\n            public String provideOutput() {\n                return \"\";\n            }\n        }\n        static boolean performComplexTask() {\n            return true;\n        }\n        public static void main(String[] args) {\n            var planner = Planners.goap(\"content-planner\", MyState::new)\n    -->\n    <!--- SUFFIX\n            .build();\n        }\n    }\n    -->\n    ```java\n    .action(\"Attempt complex task\", builder -> builder\n        .precondition(state -> !state.taskComplete)\n        .belief(state -> {\n            // Optimistic belief: task will succeed\n            return state.copy(true, state.attempts);\n        })\n        .cost(state -> 5.0)\n        .execute((context, state) -> {\n            // Actual execution might fail or have different results\n            boolean success = performComplexTask();\n            return state.copy(success, state.attempts + 1);\n        })\n    )\n    ```\n    <!--- KNIT exampleGoapAgentsJava03.java -->\n\n[A* search]: https://en.wikipedia.org/wiki/A*_search_algorithm\n\n<!-- File: docs/docs/agents/planner-agents/index.md (Tokens: ~381 | Category: subagent-persona) -->\n---\nstatus: beta\n---\n\n# Planner agents\n\n--8<-- \"versioning-snippets.md:beta\"\n\nPlanner agents are AI agents that plan and execute multistep tasks through iterative planning cycles.\nThey continuously build or update plans, execute steps, and check completion criteria against the current state.\n\nPlanner agents are suitable for complex tasks\nthat require breaking down a high-level goal into smaller, actionable steps\nand adapting the plan based on the results of each step.\n\nWhile with [graph-based agents](../graph-based-agents.md) you define all the nodes and edges,\nfor planner agents, you only define the actions (nodes) with typed inputs and outputs.\nThe planner creates reasonable edges that would be suitable to achieve the desired state,\nand it can also update the optimal path between steps.\nThis allows for a more dynamic approach which can be more powerful \nbut less controllable compared to graph-based agents.\n\nPlanner agents operate through an iterative planning cycle:\n\n1. The planner creates or updates a plan based on the current state.\n2. The planner executes a single step from the plan, updating the state.\n3. The planner determines whether the plan is completed according to the current state.\n    - If the plan is completed, the cycle ends.\n    - If the plan is not completed, the cycle repeats from the first step.\n\n```mermaid\ngraph LR\n  A[Create or update plan] --> B[\"Execute step and update state\"]\n  B --> C[\"Check completion\"]\n  C -->|Completed| D[[Done]]\n  C -->|\"Not completed\"| A\n```\n\nKoog provides two types of planner agents:\n\n- [LLM-based planners](llm-based-planners.md) use an LLM to create and update plans\n- [GOAP agents](goap-agents.md) use a special algorithm to determine the optimal action sequences\n\n<!-- File: docs/docs/agents/planner-agents/llm-based-planners.md (Tokens: ~1048 | Category: subagent-persona) -->\n---\nstatus: beta\n---\n\n# LLM-based planners\n\n--8<-- \"versioning-snippets.md:beta\"\n\nLLM-based planners use LLMs to generate and evaluate plans.\nThey operate on a string-based state and execute steps through LLM requests.\nString-based state means that the agent state is a single string.\nAt every step, the agent accepts an initial state string and returns the final state string as the result.\n\n??? note \"Prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:prerequisites\"\n\n    --8<-- \"quickstart-snippets.md:dependencies\"\n\n    --8<-- \"quickstart-snippets.md:api-key\"\n\n    Examples on this page assume that you have set the `OPENAI_API_KEY` environment variable.\n\nKoog provides two simple planners:\n\n- [SimpleLLMPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-planner/index.html)\n  generates a plan only once at the very beginning and then follows the plan until it is completed.\n  To include replanning, extend `SimpleLLMPlanner` and override the `assessPlan` method,\n  indicating when the agent should replan.\n- [SimpleLLMWithCriticPlanner](https://api.koog.ai/agents/agents-planner/ai.koog.agents.planner.llm/-simple-l-l-m-with-critic-planner/index.html)\n  implements the `assessPlan` method that uses an LLM to check the validity of the plan via an LLM request\n  and assess whether the agent should replan.\n\nThe following example shows how to create a simple planner agent using `SimpleLLMPlanner`:\n\n=== \"Kotlin\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.config.AIAgentConfig\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy\n    import ai.koog.agents.core.planner.PlannerAIAgent\n    import ai.koog.agents.planner.llm.SimpleLLMPlanner\n    import ai.koog.prompt.dsl.prompt\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels\n    import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor\n    import kotlinx.coroutines.runBlocking\n    -->\n    ```kotlin\n    // Create the planner\n    val planner = SimpleLLMPlanner()\n\n    // Wrap it in a planner strategy\n    val strategy = AIAgentPlannerStrategy(\n        name = \"simple-planner\",\n        planner = planner\n    )\n\n    // Configure the agent\n    val agentConfig = AIAgentConfig(\n        prompt = prompt(\"planner\") {\n            system(\"You are a helpful planning assistant.\")\n        },\n        model = OpenAIModels.Chat.GPT4o,\n        maxAgentIterations = 50\n    )\n\n    // Create the planner agent\n    val agent = PlannerAIAgent(\n        promptExecutor = simpleOpenAIExecutor(System.getenv(\"OPENAI_API_KEY\")),\n        strategy = strategy,\n        agentConfig = agentConfig\n    )\n\n    suspend fun main() {\n        // Run the agent with a task\n        val result = agent.run(\"Create a plan to organize a team meeting\")\n        println(result)\n    }\n    ```\n    <!--- KNIT example-llm-based-planners-01.kt -->\n\n=== \"Java\"\n\n    <!--- INCLUDE\n    import ai.koog.agents.core.agent.AIAgent;\n    import ai.koog.agents.core.planner.AIAgentPlannerStrategy;\n    import ai.koog.agents.planner.Planners;\n    import ai.koog.prompt.executor.clients.openai.OpenAIModels;\n    import ai.koog.prompt.executor.model.PromptExecutor;\n    class exampleLLMBasedPlanner01 {\n        public static void main(String[] args) {\n    -->\n    <!--- SUFFIX\n        }\n    }\n    -->\n    ```java\n    // Create the planner strategy with LLM-based planner\n    AIAgentPlannerStrategy<String, String> strategy =\n        Planners.llmBased(\"simple-planner\")\n            .build();\n\n    // Create the OpenAI executor\n    var promptExecutor = PromptExecutor.builder()\n        .openAI(\"OPENAI_API_KEY\")\n        .build();\n\n    // Create the planner agent using AIAgent builder\n    AIAgent<String, String> agent = AIAgent.builder()\n        .plannerStrategy(strategy)\n        .promptExecutor(promptExecutor)\n        .llmModel(OpenAIModels.Chat.GPT4o)\n        .systemPrompt(\"You are a helpful planning assistant.\")\n        .maxIterations(50)\n        .build();\n\n    // Run the agent with a task\n    String result = agent.run(\"Create a plan to organize a team meeting\");\n    System.out.println(result);\n    ```\n     <!--- KNIT exampleLLMBasedPlannerJava01.java -->\n\n## Next steps\n\n- Learn about [GOAP agents](goap-agents.md)\n\n<!-- File: examples/simple-examples/src/main/kotlin/ai/koog/agents/example/a2a/README.md (Tokens: ~278 | Category: subagent-persona) -->\n# Agent-to-Agent (A2A) Examples\n\nExamples demonstrating the A2A protocol for inter-agent communication with standardized message/task workflows, streaming responses, and artifact delivery.\n\n## Examples\n\n### Simple Joke Agent (`simplejoke/`)\n\nBasic message-based communication without tasks.\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9998)\n./gradlew runExampleSimpleJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleSimpleJokeClient\n```\n\n### Advanced Joke Agent (`advancedjoke/`)\n\nTask-based workflow with:\n- Interactive clarification (InputRequired state)\n- Artifact delivery for results\n- Graph-based agent strategy with documented nodes/edges\n- Streaming response events\n\n**Run:**\n```bash\n# Terminal 1: Start server (port 9999)\n./gradlew runExampleAdvancedJokeServer\n\n# Terminal 2: Run client\n./gradlew runExampleAdvancedJokeClient\n```\n\n## Key Patterns\n\n**Simple Agent:** `sendMessage()` → single response\n**Advanced Agent:** `sendMessageStreaming()` → Flow of events (Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent)\n\n**Task States:** Submitted → Working → InputRequired (optional) → Completed\n\nSee code comments in `JokeWriterAgentExecutor.kt` for detailed flow documentation.\n\n<!-- File: examples/simple-examples/src/main/kotlin/ai/koog/agents/example/snapshot/sql/README.md (Tokens: ~671 | Category: subagent-persona) -->\n# SQL Persistence Example\n\nDemonstrates using SQL databases as persistence backends for agent checkpoints through JetBrains Exposed ORM.\n\n## Quick Start\n\n1. **Start databases** (PostgreSQL/MySQL only)\n   ```bash\n   docker-compose up -d\n   ```\n\n2. **Run the examples**\n   ```bash\n   # PostgreSQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"postgres\"\n   \n   # MySQL example\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"mysql\"\n   \n   # H2 example (embedded database)\n   ./gradlew :examples:runExampleSQLPersistentAgent --args=\"h2\"\n   ```\n\n## What It Does\n\nThe example demonstrates:\n- Saving checkpoints to different SQL databases\n- Connection pooling with HikariCP (PostgreSQL/MySQL)\n- TTL-based checkpoint expiration\n- Multiple H2 database modes (in-memory, file-based, PostgreSQL-compatible)\n- Schema auto-creation\n\n## Database Schema\n\n```sql\nCREATE TABLE agent_checkpoints (\n    persistence_id VARCHAR(255),\n    checkpoint_id VARCHAR(255),\n    created_at BIGINT,\n    checkpoint_json TEXT,\n    ttl_timestamp BIGINT,\n    PRIMARY KEY (persistence_id, checkpoint_id)\n);\n```\n\n## Providers\n\n### PostgreSQL\n\n```kotlin\nPostgresPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:postgresql://localhost:5432/agents\",\n        driver = \"org.postgresql.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 3600  // Optional TTL\n)\n```\n\n### MySQL\n\n```kotlin\nMySQLPersistenceStorageProvider(\n    persistenceId = \"my-agent\",\n    database = Database.connect(\n        url = \"jdbc:mysql://localhost:3306/agents\",\n        driver = \"com.mysql.cj.jdbc.Driver\",\n        user = \"agent_user\",\n        password = \"agent_pass\"\n    ),\n    ttlSeconds = 7200\n)\n```\n\n### H2 (Embedded)\n\n```kotlin\n// In-memory (for testing)\nH2PersistenceStorageProvider.inMemory(\n    persistenceId = \"test-agent\",\n    databaseName = \"test_db\"\n)\n\n// File-based (for persistence)\nH2PersistenceStorageProvider.fileBased(\n    persistenceId = \"my-agent\",\n    filePath = \"./data/h2/agent_checkpoints\"\n)\n```\n### SQLite (Embedded)\n\n\n## Connection Pool Monitoring\n\nFor PostgreSQL/MySQL providers:\n\n```kotlin\nval stats = provider.getPoolStats()\nprintln(\"Active connections: ${stats.activeConnections}\")\nprintln(\"Pool utilization: ${stats.utilizationPercent}%\")\n```\n\n## Database UI\n\nAccess Adminer at http://localhost:8080:\n- **PostgreSQL**: Server: `postgres`, Username: `agent_user`, Password: `agent_pass`\n- **MySQL**: Server: `mysql`, Username: `agent_user`, Password: `agent_pass`\n\n## Environment Variables\n\n- `POSTGRES_URL` - Override PostgreSQL connection\n- `MYSQL_URL` - Override MySQL connection\n\n## Cleanup\n\n```bash\ndocker-compose down -v  # Stop and remove data\nrm -rf ./data          # Remove H2/SQLite files\n```\n</yakaai_skills>"}