Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ All notable changes to Agents.KT are documented here. The format follows [Keep a

## [Unreleased]

### Added — `agents-kt-dir`: AGNTCY DIR directory client (#4520, PRD §12.6) — AGNTCY interop

`DirClient` is a typed Kotlin client for the AGNTCY [DIR](https://github.com/agntcy/dir) content-addressed
directory `StoreService`, over generated grpc-kotlin coroutine stubs: `connect(host, port).use { dir -> val cid
= dir.push(agent.toOasfRecord(...)); val json = dir.pull(cid) }`. `push`/`pushAll` publish OASF records and
return their CIDs; `pull` fetches the record JSON by CID; `lookup` resolves metadata; `delete` removes. The
**directory** pillar of the AGNTCY epic (#4517), beside OASF export/import (#4518/#4519) and Identity-verify
(#4521). The record body is carried as a `google.protobuf.Struct` (JSON is the contract — no OASF protos), via
protobuf's canonical `JsonFormat` (whole numbers stay integral, e.g. `{"id":1003}`). Protos are vendored as a
**trimmed, wire-compatible** subset (same package/service/RPC/field-numbers; the `buf.validate` options and
unused referrer RPCs dropped) so the buf/validate closure isn't needed. **Auth:** plaintext (dev), TLS, and
OIDC **bearer**; SPIFFE/mTLS via a caller-supplied `ManagedChannel` (`fromChannel`). New feature module
`agents-kt-dir` (package `agents_engine.agntcy.dir`) so the grpc/protobuf/netty graph stays out of core. 4
tests (in-process gRPC round trip). RoutingService/SearchService (network discovery) and OCI referrers are
follow-ups; with this, the AGNTCY epic's core is complete.

### Added — OASF record import + validate: `fromOasfRecord()` (#4519, PRD §12.6) — AGNTCY interop

`fromOasfRecord(json)` parses + validates an OASF 1.0.0 record into the typed `OasfRecord` — the read side of
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ These APIs work in `main`, are unit-tested, and are exercised by integration tes
- **Web-grounded search tool (`perplexitySearch`)** — `tools { +perplexitySearchTool(perplexityKey) }` lets an agent reasoning on its *own* model (Claude/OpenAI/Ollama/…) fetch live, cited facts from Perplexity's Sonar API. The tool is `untrustedOutput = true`, so results are auto-wrapped in the `{"trusted":false}` envelope and the model is warned to treat them as data, not instructions (#642) — web search is the canonical prompt-injection vector. The result renders the answer plus a numbered source list parsed from `search_results[]` (citations land in both the model context and the JSONL audit row). Controls via `perplexitySearchOptions { mode = SearchMode.ACADEMIC; recency = SearchRecency.WEEK; allowDomains("arxiv.org"); contextSize = SearchContextSize.HIGH; structuredOutput(MyType::class) }` map to `search_mode` / `search_recency_filter` / `search_domain_filter` / `web_search_options` / `response_format` json_schema (#3674). Key from `.secrets/perplexity-key`. See [docs/providers.md](docs/providers.md#web-grounded-search-tool-perplexitysearch-3676--3677).
- **NLWeb endpoint tool (`nlwebSearch`)** — `tools { +nlwebSearchTool(baseUrl = "https://example.com") }` lets an agent query an [NLWeb](https://github.com/nlweb-ai/NLWeb) endpoint — a website's natural-language interface over its **schema.org**-structured content — and fold the ranked, typed results into context (#4541, PRD §12.9). Like `perplexitySearch` it is `untrustedOutput = true` (fetched web content is treated as data, not instructions). `nlwebSearchOptions`-style args via `NlWebSearchOptions(site = "podcasts", mode = NlWebMode.GENERATE)`. NLWeb endpoints need no API key. (Every NLWeb endpoint is also an MCP server, so an NLWeb `/mcp` URL is equally consumable through the existing MCP client — this tool is the zero-wiring `/ask`-over-HTTP path.)
- **Serve an NLWeb endpoint (`NlWebServer`)** — `NlWebServer.from(agent).start()` exposes the NLWeb `POST /ask` contract (`{query, site?, mode}` → ranked schema.org `results[]`), so agents.kt is consumable by NLWeb clients — the **serve** side to `nlwebSearch`'s **consume** side (#4542). Same `from(agent)` shape, loopback-only JDK-`HttpServer` posture, and threat model as `McpServer.from(agent)` / `A2AServer.from(agent)` (`127.0.0.1`, optional bearer, front with a gateway). The query is the agent's input; an `NlWebSearchResult` output is served verbatim (ranked schema.org results), any other output becomes the `summary` answer — back the agent's retrieval with the RAG `EmbeddingStore` seam (`:agents-kt-rag`) or whatever you like. agents.kt now serves the agentic web three ways: MCP, A2A, and NLWeb.
- **AGNTCY interop (OASF record + Identity badge)** — `agent.toOasfRecord(version, authors, locators)` exports an [AGNTCY](https://github.com/agntcy) [OASF](https://github.com/agntcy/oasf) 1.0.0 discovery record (the third exporter beside `agent.json` and the A2A AgentCard; skills carry taxonomy uids via the opt-in `.oasf("agent_orchestration/multi_agent_planning")` annotation against a vendored, drift-checked taxonomy — #4518, PRD §12.6). The trust side ships in the `:agents-kt-identity` module: `IdentityVerifier.verify(compactJws, jwks)` validates an AGNTCY Identity **badge** (a JOSE/JWS-secured W3C Verifiable Credential) against an issuer's `/.well-known/jwks.json`, fail-closed via `nimbus-jose-jwt` (rejects `alg: none`, `HS*` algorithm-confusion, expiry, tamper, wrong/unknown key — #4521). Verify-only; issuance is deferred. Remaining AGNTCY work (DIR directory client, OASF import) is tracked under epic #4517.
- **AGNTCY interop (OASF record + DIR directory + Identity badge)** — `agent.toOasfRecord(version, authors, locators)` exports an [AGNTCY](https://github.com/agntcy) [OASF](https://github.com/agntcy/oasf) 1.0.0 discovery record (the third exporter beside `agent.json` and the A2A AgentCard; skills carry taxonomy uids via the opt-in `.oasf("agent_orchestration/multi_agent_planning")` annotation against a vendored, drift-checked taxonomy), and `fromOasfRecord(json)` imports + fail-closed-validates it back (#4518/#4519, PRD §12.6). The `:agents-kt-dir` module publishes/discovers records in the AGNTCY **DIR** content-addressed directory: `DirClient.connect(host, port).use { dir -> val cid = dir.push(agent.toOasfRecord(...)); val json = dir.pull(cid) }` over generated grpc-kotlin `StoreService` stubs (#4520). The trust side ships in `:agents-kt-identity`: `IdentityVerifier.verify(compactJws, jwks)` validates an AGNTCY Identity **badge** (a JOSE/JWS-secured W3C Verifiable Credential) against an issuer's `/.well-known/jwks.json`, fail-closed via `nimbus-jose-jwt` (rejects `alg: none`, `HS*` algorithm-confusion, expiry, tamper, wrong/unknown key — #4521). Verify-only; issuance deferred. DIR Routing/Search + OCI referrers are follow-ups under epic #4517.
- **Prompt caching across providers** — `agent { caching { enabled = true; cacheSystemPrompt = true; cacheToolDefs = true; cacheConversation = Rolling; ttl = 1.hours; cacheable("doc-id") { ... } } }`. Vendor-neutral DSL drives Anthropic's explicit `cache_control` breakpoints (#2658), OpenAI / DeepSeek automatic prefix caching with a stable `prompt_cache_key` routing hint (#2659 / #2661), Ollama / vLLM / SGLang engine-level KV-cache reuse (no-op hints, #2662), and surfaces cache reads + writes + hit-rate on `TokenUsage` (#2663). A prefix-stability guard (#2657) detects silent cache-busters — timestamps, UUIDs, non-deterministic ordering inside cacheable segments — and warns before you pay for a single non-cached run. Off by default; non-breaking. See [docs/caching.md](docs/caching.md).
- **JSONL audit exporter** — `:agents-kt-observability` writes append-only, one-line-per-event audit rows with `requestId`, `sessionId`, `manifestHash`, agent/skill/tool ids, event type, provider, and model; raw arguments/results are omitted by default (#1914). See [docs/observability.md](docs/observability.md).
- **ObservabilityBridge adapters** — `.observe(OtelBridge(tracer))` maps runtime events to OTel spans (#1908), `.observe(LangSmithBridge(apiKey, project))` maps the same events to LangSmith run trees (#1909), and `.observe(LangfuseBridge(publicKey, secretKey))` maps them to Langfuse traces, generations, spans, and events (#1910), while keeping core vendor-free. See [docs/observability.md](docs/observability.md).
Expand Down
81 changes: 81 additions & 0 deletions agents-kt-dir/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import com.google.protobuf.gradle.id

plugins {
kotlin("jvm")
id("com.google.protobuf") version "0.10.0"
}

group = "ai.deep-code"
version = rootProject.version

repositories {
mavenCentral()
}

dependencyLocking {
lockAllConfigurations()
}

configurations.all {
resolutionStrategy {
force(
"org.bouncycastle:bcprov-jdk18on:1.84",
"org.bouncycastle:bcpg-jdk18on:1.84",
"org.bouncycastle:bcpkix-jdk18on:1.84",
"org.bouncycastle:bcutil-jdk18on:1.84",
)
}
}

private val grpcVersion = "1.82.0"
private val protobufVersion = "4.35.1"
private val grpcKotlinVersion = "1.4.1"

dependencies {
api(project(":"))

// #4520 — generated gRPC stubs + runtime. Kept in this feature module so the (large) grpc/protobuf
// dependency graph never reaches core.
api("io.grpc:grpc-stub:$grpcVersion")
api("io.grpc:grpc-protobuf:$grpcVersion")
api("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion")
api("com.google.protobuf:protobuf-kotlin:$protobufVersion")
// Canonical google.protobuf.Struct <-> JSON converter (the DIR record payload is a Struct).
api("com.google.protobuf:protobuf-java-util:$protobufVersion")
// grpc-kotlin streaming stubs are Flow-based — coroutines must be on the compile classpath.
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion")

// javax.annotation.Generated, referenced by protoc-gen-grpc-java output (compile-time only).
compileOnly("org.apache.tomcat:annotations-api:6.0.53")

testImplementation(kotlin("test"))
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0")
// In-process gRPC server/channel for hermetic round-trip tests (no network, no real DIR daemon).
testImplementation("io.grpc:grpc-inprocess:$grpcVersion")
}

protobuf {
protoc { artifact = "com.google.protobuf:protoc:$protobufVersion" }
plugins {
id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" }
id("grpckt") { artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpcKotlinVersion:jdk8@jar" }
}
generateProtoTasks {
all().forEach { task ->
task.plugins {
id("grpc")
id("grpckt")
}
task.builtins { id("kotlin") }
}
}
}

kotlin {
jvmToolchain(21)
}

tasks.test {
useJUnitPlatform()
}
71 changes: 71 additions & 0 deletions agents-kt-dir/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.android:annotations:4.1.1.4=runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.api.grpc:proto-google-common-protos:2.64.1=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.code.gson:gson:2.13.2=runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.code.gson:gson:2.8.9=compileProtoPath
com.google.errorprone:error_prone_annotations:2.48.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.guava:failureaccess:1.0.3=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.guava:guava:33.5.0-android=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.j2objc:j2objc-annotations:3.1=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.protobuf:protobuf-java-util:4.35.1=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.protobuf:protobuf-java:4.35.1=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.protobuf:protobuf-kotlin:4.35.1=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
com.google.protobuf:protoc:4.35.1=protobufToolsLocator_protoc
io.grpc:grpc-api:1.82.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-context:1.82.0=runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-core:1.82.0=runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-inprocess:1.82.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-kotlin-stub:1.4.1=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-netty-shaded:1.82.0=runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.82.0=compileProtoPath,runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.82.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-stub:1.82.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
io.grpc:grpc-util:1.82.0=runtimeClasspath,testRuntimeClasspath
io.grpc:protoc-gen-grpc-java:1.82.0=protobufToolsLocator_grpc
io.grpc:protoc-gen-grpc-kotlin:1.4.1=protobufToolsLocator_grpckt
io.perfmark:perfmark-api:0.27.0=runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
javax.annotation:javax.annotation-api:1.3.2=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.apache.tomcat:annotations-api:6.0.53=compileClasspath,compileProtoPath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.27=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-build-tools-compat:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-cri-impl:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-impl:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-compiler-runner:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-daemon-client:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:2.4.0=kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-script-runtime:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-scripting-common:2.4.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.4.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.4.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-jvm:2.4.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-stdlib:2.4.0=compileClasspath,compileProtoPath,kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-test-junit5:2.4.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-test:2.4.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-tooling-core:2.4.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.11.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.11.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jetbrains:annotations:13.0=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath
org.jetbrains:annotations:23.0.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.jline:jline:4.1.3=compileProtoPath,runtimeClasspath,testCompileProtoPath,testRuntimeClasspath
org.jspecify:jspecify:1.0.0=compileClasspath,compileProtoPath,runtimeClasspath,testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.10.1=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.10.1=testCompileProtoPath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.10.1=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.10.1=testCompileProtoPath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.10.1=testCompileProtoPath,testRuntimeClasspath
org.junit:junit-bom:5.10.1=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testCompileProtoPath,testRuntimeClasspath
empty=annotationProcessor,implementationDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,protobuf,testAnnotationProcessor,testImplementationDependenciesMetadata,testKotlinScriptDefExtensions,testProtobuf
Loading
Loading