From e6dc9a2d5e39cb5f5ccc9b0b7a9bf6ba47784d5e Mon Sep 17 00:00:00 2001 From: Zhoujie628 Date: Sun, 13 Sep 2026 00:19:28 +0800 Subject: [PATCH 1/3] chore(release): prepare 0.0.7 artifact coordinates Signed-off-by: Zhoujie628 --- CHANGELOG.md | 11 ++++++++++- README.md | 12 +++++++----- docs/en/DEVELOPER_GUIDE.md | 5 ++++- docs/en/INTEGRATION_GUIDE.md | 13 ++++++++----- docs/zh/DEVELOPER_GUIDE.md | 5 ++++- docs/zh/INTEGRATION_GUIDE.md | 13 ++++++++----- pom.xml | 2 +- samples/pom.xml | 2 +- .../engine/examples/SpringWorkbenchApplication.java | 2 +- spring-boot-starter/pom.xml | 4 ++-- 10 files changed, 46 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb76b381..39c0770e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +- Set the source-build version to `0.0.7-SNAPSHOT`. Maven Central version `0.0.6` was published from the same source + commit as `0.0.5`; it does not contain the APIs listed in this Unreleased section. +- Preserve the published `spring-boot-starter` artifact coordinate. The temporary, unpublished + `spring-boot-a2a-starter` name is not a consumable Maven Central coordinate. + - Add `WorkflowEngineClient.sendTask(...)` for task execution outside a DAG. It reuses the normal task state machine, keeps one task/context through Negotiation-T, and supports a per-call `NegotiationStrategy`. Local interaction failures cancel a known non-final remote task before @@ -11,6 +16,10 @@ setting as `a2a.notification-ack-timeout-seconds` / `A2A_NOTIFICATION_ACK_TIMEOUT_SECONDS`. +## [0.0.6] — 2026-09-09 + +- Published from the same source commit as `0.0.5`; no APIs or behavior changed. + ## [0.0.5] — 2026-09-09 ### Breaking: per-edge conditional routing @@ -73,4 +82,4 @@ - Keep documentation host-neutral; sample class and AgentCard identifiers remain unchanged. Published Maven artifacts use the version selected by the release tag. The default source-build -`revision=1.0.0` is a development version, not the version to use when consuming Maven Central artifacts. +`revision=0.0.7-SNAPSHOT` is not a Maven Central release version. diff --git a/README.md b/README.md index dd510ca1..0da5fcd1 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,9 @@ subscription remains open until the host-defined terminal event, explicit cancel ### 1. Add Maven dependency -The current engine release is `0.0.6` on [Maven Central](https://repo.maven.apache.org/maven2/net/openan/workflow/sdk/). +Maven Central currently contains `0.0.6`; it was published from the same source commit as `0.0.5` and adds no APIs. +The current source tree builds as `0.0.7-SNAPSHOT`. APIs listed under `Unreleased` in the changelog, including `sendTask` and +HTTP runtime customization, require this source build (or an internal snapshot repository) until the next release. A2A-T SDK `1.1.0` is published to Maven Central. Maven resolves it automatically; no SDK source checkout or local SDK build is required. The engine depends only on @@ -51,7 +53,7 @@ upgrade guidance. net.openan.workflow.sdk workflow-engine - 0.0.6 + 0.0.7-SNAPSHOT ``` @@ -60,8 +62,8 @@ For Spring Boot server-side integration: ```xml net.openan.workflow.sdk - spring-boot-a2a-starter - 0.0.6 + spring-boot-starter + 0.0.7-SNAPSHOT ``` @@ -162,7 +164,7 @@ graph TD | Module | Description | |-----------------------|---------------------------------------------------------------| | `workflow-engine` | Core SDK: workflow execution, A2A transport, extensions, auth | -| `spring-boot-a2a-starter` | Spring Boot auto-configuration for A2A server side | +| `spring-boot-starter` | Spring Boot auto-configuration for A2A server side | | `samples` | Demo applications (embedded + Spring Boot variants) | ## License diff --git a/docs/en/DEVELOPER_GUIDE.md b/docs/en/DEVELOPER_GUIDE.md index 2e8da7b9..1e9c2dbd 100644 --- a/docs/en/DEVELOPER_GUIDE.md +++ b/docs/en/DEVELOPER_GUIDE.md @@ -11,10 +11,13 @@ Add to your `pom.xml`: net.openan.workflow.sdk workflow-engine - 0.0.6 + 0.0.7-SNAPSHOT ``` +`0.0.7-SNAPSHOT` is the current source-build version and must be installed locally or supplied by an internal snapshot +repository. Maven Central `0.0.6` is built from the same source as `0.0.5` and does not contain Unreleased APIs. + The engine pulls in A2A protocol transports and a2a-t-core only. Host agents generating A2A-T content explicitly add a2a-t-client; dispatched-agent services that validate received content also add a2a-t-server. diff --git a/docs/en/INTEGRATION_GUIDE.md b/docs/en/INTEGRATION_GUIDE.md index d1d5f9f3..c9c3b648 100644 --- a/docs/en/INTEGRATION_GUIDE.md +++ b/docs/en/INTEGRATION_GUIDE.md @@ -22,10 +22,13 @@ callbacks own A2A-T generation, semantic validation, schemas and any LLM calls. net.openan.workflow.sdk workflow-engine -0.0.6 +0.0.7-SNAPSHOT ``` +`0.0.7-SNAPSHOT` is the current source-build version. Install it locally or resolve it from an internal snapshot +repository. Maven Central `0.0.6` is built from the same source as `0.0.5` and does not include Unreleased APIs. + ## 4. Quick Start Four steps: define workflow -> load AgentCard -> implement ControlPoint -> execute. @@ -221,7 +224,7 @@ configuration without modifying system properties. Never log plaintext or the ke **Encrypt a password** -Build the jar with `mvn -pl workflow-engine -am -Drevision=0.0.6 package`; commands below run from the repository root. +Build the jar with `mvn -pl workflow-engine -am package`; commands below run from the repository root. `set` is Windows cmd syntax (PowerShell: `$env:A2AT_CRED_KEY='...'`). This CLI needs only the SDK jar and JDK. Use disposable example values here: command-line passwords/keys can appear in shell history and process listings. For production, obtain secrets securely in the host and use the Java encryption API. @@ -229,10 +232,10 @@ For production, obtain secrets securely in the host and use the Java encryption ```bash # Option 1: set env var first set A2AT_CRED_KEY=4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b -java -cp workflow-engine/target/workflow-engine-0.0.6.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" +java -cp workflow-engine/target/workflow-engine-0.0.7-SNAPSHOT.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" # Option 2: pass key as second argument -java -cp workflow-engine/target/workflow-engine-0.0.6.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" 4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b +java -cp workflow-engine/target/workflow-engine-0.0.7-SNAPSHOT.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" 4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b ``` Output: @@ -248,7 +251,7 @@ Paste the output into the `value` field of the credentials JSON. 1. Generate a new key: `openssl rand -hex 32` 2. Update the host secret store / explicit `credentialEncryptionKey`, or its OS/JVM `A2AT_CRED_KEY` 3. Re-encrypt all passwords: - `java -cp workflow-engine/target/workflow-engine-0.0.6.jar dev.openan.workflow.engine.client.CredentialCrypto "plaintext" new-key` + `java -cp workflow-engine/target/workflow-engine-0.0.7-SNAPSHOT.jar dev.openan.workflow.engine.client.CredentialCrypto "plaintext" new-key` 4. Update the `enc:...` results in the credentials JSON file > The `.env` file should not be committed to version control. Add it to `.gitignore`. diff --git a/docs/zh/DEVELOPER_GUIDE.md b/docs/zh/DEVELOPER_GUIDE.md index 04092ab1..6fb029ec 100644 --- a/docs/zh/DEVELOPER_GUIDE.md +++ b/docs/zh/DEVELOPER_GUIDE.md @@ -10,10 +10,13 @@ net.openan.workflow.sdk workflow-engine - 0.0.6 + 0.0.7-SNAPSHOT ``` +`0.0.7-SNAPSHOT` 是当前源码构建版本,使用前需要先安装到本地 Maven 仓库,或由内部快照仓库提供。Maven Central +中的 `0.0.6` 与 `0.0.5` 来自同一份源码,不包含 Unreleased 章节中的新接口。 + 引擎会传递性引入 A2A 协议 SDK(`a2a-java-sdk-client`,含 REST、JSON-RPC、gRPC 传输) 和最小 A2A-T 核心(`a2a-t-core`)。生成 A2A-T 内容的宿主智能体显式依赖 a2a-t-client,校验接收内容的被调度智能体服务另引入 a2a-t-server。 ## 2. 核心概念 diff --git a/docs/zh/INTEGRATION_GUIDE.md b/docs/zh/INTEGRATION_GUIDE.md index aaa0dcc4..410607ad 100644 --- a/docs/zh/INTEGRATION_GUIDE.md +++ b/docs/zh/INTEGRATION_GUIDE.md @@ -20,10 +20,13 @@ A2A-T 工作流执行引擎是一个 Java SDK,用于基于 A2A 协议和 A2A-T net.openan.workflow.sdk workflow-engine -0.0.6 +0.0.7-SNAPSHOT ``` +`0.0.7-SNAPSHOT` 是当前源码构建版本,使用前需要先安装到本地 Maven 仓库,或由内部快照仓库提供。Maven Central +中的 `0.0.6` 与 `0.0.5` 来自同一份源码,不包含 Unreleased 章节中的新接口。 + ## 4. 快速上手 整个集成过程分四步:定义工作流 -> 加载 AgentCard -> 实现 ControlPoint -> 执行。 @@ -212,17 +215,17 @@ A2AT_CRED_KEY=4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b **加密密码** -先执行 `mvn -pl workflow-engine -am -Drevision=0.0.6 package`,以下命令在仓库根目录运行,仅需 SDK jar 和 JDK。 +先执行 `mvn -pl workflow-engine -am package`,以下命令在仓库根目录运行,仅需 SDK jar 和 JDK。 `set` 是 Windows cmd 语法,PowerShell 应使用 `$env:A2AT_CRED_KEY='...'`。 以下参数仅用于演示;命令行口令/密钥可能进入终端历史和进程参数。生产应由集成方安全读取密钥并调用 Java 加密 API。 ```bash # 方式一:先设置环境变量 set A2AT_CRED_KEY=4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b -java -cp workflow-engine/target/workflow-engine-0.0.6.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" +java -cp workflow-engine/target/workflow-engine-0.0.7-SNAPSHOT.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" # 方式二:密钥作为第二个参数 -java -cp workflow-engine/target/workflow-engine-0.0.6.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" 4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b +java -cp workflow-engine/target/workflow-engine-0.0.7-SNAPSHOT.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" 4f8a2b1c3d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b ``` 输出: @@ -237,7 +240,7 @@ enc:uHQcTeKZMVNRM9Ga:o5vm4weRozBXBs04phrLq7j7+/yRVyDsrw== 1. 生成新密钥:`openssl rand -hex 32` 2. 更新集成方密钥存储及显式 `credentialEncryptionKey`,或 OS/JVM 的 `A2AT_CRED_KEY`;引擎不自动加载 `.env` -3. 用新密钥重新加密所有密码:`java -cp workflow-engine/target/workflow-engine-0.0.6.jar dev.openan.workflow.engine.client.CredentialCrypto "明文密码" 新密钥` +3. 用新密钥重新加密所有密码:`java -cp workflow-engine/target/workflow-engine-0.0.7-SNAPSHOT.jar dev.openan.workflow.engine.client.CredentialCrypto "明文密码" 新密钥` 4. 将新的 `enc:...` 结果更新到凭证 JSON 文件 > `.env` 文件不应提交到版本库,建议加入 `.gitignore`。 diff --git a/pom.xml b/pom.xml index 047ee09a..57388365 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ - 1.0.0 + 0.0.7-SNAPSHOT 17 UTF-8 1.2.0.Final diff --git a/samples/pom.xml b/samples/pom.xml index 9ecdd8e8..36765e17 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -52,7 +52,7 @@ net.openan.workflow.sdk - spring-boot-a2a-starter + spring-boot-starter ${revision} diff --git a/samples/src/main/java/dev/openan/workflow/engine/examples/SpringWorkbenchApplication.java b/samples/src/main/java/dev/openan/workflow/engine/examples/SpringWorkbenchApplication.java index 8b5c3c5b..3c40164e 100644 --- a/samples/src/main/java/dev/openan/workflow/engine/examples/SpringWorkbenchApplication.java +++ b/samples/src/main/java/dev/openan/workflow/engine/examples/SpringWorkbenchApplication.java @@ -31,7 +31,7 @@ * Spring Boot Workbench Agent -- northbound A2A server. * *

This is the Workbench Agent itself: a Spring Boot service that exposes A2A-T endpoints - * (message:send, message:stream) via the {@code spring-boot-a2a-starter} auto-configuration. The + * (message:send, message:stream) via the {@code spring-boot-starter} auto-configuration. The * business logic lives in {@link * dev.openan.workflow.engine.examples.workbench.SpringWorkbenchExecutor} (implements {@code * AgentExecutor}). diff --git a/spring-boot-starter/pom.xml b/spring-boot-starter/pom.xml index 4a964944..4340889c 100644 --- a/spring-boot-starter/pom.xml +++ b/spring-boot-starter/pom.xml @@ -12,10 +12,10 @@ ../pom.xml - spring-boot-a2a-starter + spring-boot-starter jar - spring-boot-a2a-starter + spring-boot-starter Spring Boot auto-configuration for the A2A-T server side. Integration partners add this dependency and implement AgentExecutor -- all server-side beans (RestHandler, DefaultRequestHandler, event bus) From 19c80cb7272c6a617c8e4636b47ad898e121c7ef Mon Sep 17 00:00:00 2001 From: Zhoujie628 Date: Sun, 13 Sep 2026 00:19:43 +0800 Subject: [PATCH 2/3] feat(client): support HTTP transport decoration Signed-off-by: Zhoujie628 --- docs/en/API_REFERENCE.md | 25 ++++++++-- docs/zh/API_REFERENCE.md | 22 +++++++-- .../client/DefaultA2AJavaClientRuntime.java | 21 +++++++- .../client/RemoteA2AErrorResponseTest.java | 48 +++++++++++++++++++ 4 files changed, 106 insertions(+), 10 deletions(-) diff --git a/docs/en/API_REFERENCE.md b/docs/en/API_REFERENCE.md index 5ab6e536..a601c4f3 100644 --- a/docs/en/API_REFERENCE.md +++ b/docs/en/API_REFERENCE.md @@ -181,7 +181,8 @@ a2a-t-core only, never content generation or semantic validation. ### A2AJavaClientRuntime -Runtime seam for A2A SDK message transport. Implement to customize HTTP transport behavior. +Runtime seam for A2A SDK message transport. Implement this interface to replace the transport completely, including a +vendor SDK or a non-HTTP channel. ```java public interface A2AJavaClientRuntime { @@ -195,7 +196,23 @@ public interface A2AJavaClientRuntime { } ``` -A default implementation is provided. Implement this interface only if you need custom HTTP transport. +The engine provides `DefaultA2AJavaClientRuntime`. If an integration only needs to rewrite URIs or add gateway headers, +subclass the default runtime and decorate its configured `A2AHttpClient`; do not reimplement message dispatch, SSE, or +standard A2A error handling: + +```java +public final class GatewayRuntime extends DefaultA2AJavaClientRuntime { + @Override + protected A2AHttpClient customizeHttpClient(A2AHttpClient httpClient) { + return new GatewayHttpClientDecorator(httpClient); + } +} +``` + +The client passed to the hook already contains the engine's TLS, protocol logging, and error handling. A decorator must +preserve request headers, the JSON body, and SSE message/error/completion callbacks, and must return a non-null client. +The hook applies only to REST/JSON-RPC HTTP transports; implement `A2AJavaClientRuntime` for gRPC or a completely +different transport. ### ConversationScopedA2AJavaClientRuntime @@ -635,9 +652,9 @@ not add private negotiation-state keys to the wire message. --- -## spring-boot-a2a-starter Module +## spring-boot-starter Module -The `spring-boot-a2a-starter` module provides Spring Boot auto-configuration for the A2A **server** side (not the +The `spring-boot-starter` module provides Spring Boot auto-configuration for the A2A **server** side (not the client/workflow side). In a Spring Boot web application, set `a2at.server.enabled=true` to register the A2A SDK server components as Spring beans. diff --git a/docs/zh/API_REFERENCE.md b/docs/zh/API_REFERENCE.md index 6d2c7909..46aa4d81 100644 --- a/docs/zh/API_REFERENCE.md +++ b/docs/zh/API_REFERENCE.md @@ -168,7 +168,7 @@ String,Object>) 读取并检查规范协商上下文。只有 a2a-t-core 依赖 ### A2AJavaClientRuntime -A2A SDK 消息传输运行时接口。实现此类可自定义 HTTP 传输行为。 +A2A SDK 消息传输运行时接口。完全替换传输实现(包括厂商 SDK 或非 HTTP 通道)时实现此接口。 ```java public interface A2AJavaClientRuntime { @@ -182,7 +182,21 @@ public interface A2AJavaClientRuntime { } ``` -引擎提供默认实现。仅在需要自定义 HTTP 传输时实现此接口。 +引擎提供默认实现 `DefaultA2AJavaClientRuntime`。如果只需改写 URI 或补充网关 Header,应继承默认实现并装饰已配置好的 +`A2AHttpClient`,不需要重写消息发送、SSE 和标准 A2A 错误处理: + +```java +public final class GatewayRuntime extends DefaultA2AJavaClientRuntime { + @Override + protected A2AHttpClient customizeHttpClient(A2AHttpClient httpClient) { + return new GatewayHttpClientDecorator(httpClient); + } +} +``` + +传入钩子的客户端已经带有引擎配置的 TLS、协议日志与错误处理。装饰器必须原样保留请求 Header、JSON Body、SSE +事件/异常/完成回调,并返回非 null 客户端。该钩子只作用于 REST/JSON-RPC HTTP 通道;gRPC 或完全不同的传输应实现 +`A2AJavaClientRuntime`。 ### ConversationScopedA2AJavaClientRuntime @@ -597,9 +611,9 @@ getReceivedMessages() 是保留层级的响应来源,getOutputs() 为便利投 --- -## spring-boot-a2a-starter 模块 +## spring-boot-starter 模块 -`spring-boot-a2a-starter` 模块为 A2A **服务端**(非客户端/工作流侧)提供 Spring Boot 自动配置。在 Spring Boot Web 应用中配置 +`spring-boot-starter` 模块为 A2A **服务端**(非客户端/工作流侧)提供 Spring Boot 自动配置。在 Spring Boot Web 应用中配置 `a2at.server.enabled=true` 后,才会将 A2A SDK 服务端组件注册为 Spring Bean。 ### A2AProperties diff --git a/workflow-engine/src/main/java/dev/openan/workflow/engine/client/DefaultA2AJavaClientRuntime.java b/workflow-engine/src/main/java/dev/openan/workflow/engine/client/DefaultA2AJavaClientRuntime.java index 14ca7bcb..fd35d6af 100644 --- a/workflow-engine/src/main/java/dev/openan/workflow/engine/client/DefaultA2AJavaClientRuntime.java +++ b/workflow-engine/src/main/java/dev/openan/workflow/engine/client/DefaultA2AJavaClientRuntime.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; @@ -698,8 +699,24 @@ private A2AHttpClient createHttpClient() { crlPath, Duration.ofSeconds(60), httpClientExecutor); - return new A2AErrorDetectingHttpClient( - new JdkA2AHttpClient(new ObservedHttpClient(httpClient))); + return customizeHttpClient( + new A2AErrorDetectingHttpClient( + new JdkA2AHttpClient(new ObservedHttpClient(httpClient)))); + } + + /** + * Customizes the fully configured HTTP client used by REST and JSON-RPC transports. + * + *

The supplied client already applies TLS, protocol observation, and A2A error-envelope + * handling. Subclasses may decorate it to integrate an HTTP gateway (for example, URI rewriting + * or gateway-specific headers) while preserving those behaviors. The default implementation + * returns the client unchanged. + * + * @param httpClient fully configured transport client + * @return the client to use; never {@code null} + */ + protected A2AHttpClient customizeHttpClient(A2AHttpClient httpClient) { + return Objects.requireNonNull(httpClient, "httpClient"); } private record StreamClientKey(String agentName, String contextId) {} diff --git a/workflow-engine/src/test/java/dev/openan/workflow/engine/client/RemoteA2AErrorResponseTest.java b/workflow-engine/src/test/java/dev/openan/workflow/engine/client/RemoteA2AErrorResponseTest.java index c157baf4..04be1a97 100644 --- a/workflow-engine/src/test/java/dev/openan/workflow/engine/client/RemoteA2AErrorResponseTest.java +++ b/workflow-engine/src/test/java/dev/openan/workflow/engine/client/RemoteA2AErrorResponseTest.java @@ -30,6 +30,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.a2aproject.sdk.client.http.A2AHttpClient; import org.a2aproject.sdk.client.http.JdkA2AHttpClient; import org.a2aproject.sdk.spec.*; import org.junit.jupiter.api.Test; @@ -191,4 +192,51 @@ void nonStreamingResponsesPreserveTheStandardA2AError() throws Exception { server.stop(0); } } + + @Test + void runtimeUsesTheHttpClientCustomizationHookWithoutBypassingErrorDetection() throws Exception { + HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0); + server.createContext("/", exchange -> { + exchange.getRequestBody().readAllBytes(); + byte[] body = ("data: " + error(400) + "\n\n").getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().set("Content-Type", "text/event-stream"); + exchange.sendResponseHeaders(200, 0); + exchange.getResponseBody().write(body); + exchange.close(); + }); + server.start(); + var runtime = new CustomizingRuntime(); + try { + AgentCard card = AgentCard.builder().name("customized-runtime").description("test").version("1") + .capabilities(AgentCapabilities.builder().streaming(true).build()) + .defaultInputModes(List.of("text/plain")).defaultOutputModes(List.of("text/plain")) + .skills(List.of()).supportedInterfaces(List.of(new AgentInterface("HTTP+JSON", + "http://127.0.0.1:" + server.getAddress().getPort() + "/a2a/json"))).build(); + MessageSendParams params = MessageSendParams.builder().message(Message.builder() + .role(Message.Role.ROLE_USER).messageId("test").parts(new TextPart("diagnose")).build()).build(); + + var thrown = assertThrows(RuntimeException.class, + () -> runtime.sendMessage(card, params, null, ignored -> {}, null)); + + assertNotNull(RemoteA2AErrorException.findIn(thrown)); + assertEquals(1, runtime.customizations.get()); + } finally { + runtime.close(); + server.stop(0); + } + } + + private static final class CustomizingRuntime extends DefaultA2AJavaClientRuntime { + private final AtomicInteger customizations = new AtomicInteger(); + + private CustomizingRuntime() { + super(true, null, 5, "HTTP+JSON"); + } + + @Override + protected A2AHttpClient customizeHttpClient(A2AHttpClient httpClient) { + customizations.incrementAndGet(); + return super.customizeHttpClient(httpClient); + } + } } From dfe5d1cee16661071c9738eab84113ed3ee76055 Mon Sep 17 00:00:00 2001 From: Zhoujie628 Date: Sun, 13 Sep 2026 00:19:52 +0800 Subject: [PATCH 3/3] fix(samples): handle CRLF negotiation prompts Signed-off-by: Zhoujie628 --- .../engine/examples/testsupport/OfflineA2ATLlmClient.java | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/src/test/java/dev/openan/workflow/engine/examples/testsupport/OfflineA2ATLlmClient.java b/samples/src/test/java/dev/openan/workflow/engine/examples/testsupport/OfflineA2ATLlmClient.java index 02eef707..d942270e 100644 --- a/samples/src/test/java/dev/openan/workflow/engine/examples/testsupport/OfflineA2ATLlmClient.java +++ b/samples/src/test/java/dev/openan/workflow/engine/examples/testsupport/OfflineA2ATLlmClient.java @@ -206,6 +206,7 @@ private static String label(String source, String name) { /** Only actual inputs, never explanatory examples or template guidance. */ private static String validationInput(String prompt) { + prompt = prompt.replace("\r\n", "\n").replace('\r', '\n'); int start = prompt.indexOf("[input]\n"); if (start >= 0) { start += "[input]\n".length();