From 038671779d8a4a1664f7bad00cae4247540d0886 Mon Sep 17 00:00:00 2001 From: jinison <507827762@qq.com> Date: Tue, 15 Sep 2026 15:05:02 +0800 Subject: [PATCH 1/2] fix(aistio): rename task.submit_result tool to satisfy OpenAI function-name rules AgentTaskOutcomeTool registers the tool as task.submit_result, but OpenAI-compatible APIs (OpenAI, DeepSeek) require function names to match ^[a-zA-Z0-9_-]+$. The dotted name makes every AgentTask conversation fail with HTTP 400: Invalid 'tools[i].function.name': string does not match pattern Rename to task_submit_result in the tool registration and in the five prompt/checks inside HarnessAgentTaskStarter that reference it, keeping the model instructions and toolkit membership check consistent. --- .../aistio/adapter/AgentTaskOutcomeTool.java | 2 +- .../aistio/adapter/HarnessAgentTaskStarter.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/AgentTaskOutcomeTool.java b/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/AgentTaskOutcomeTool.java index 6d40a491e4..1421e9c5d4 100644 --- a/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/AgentTaskOutcomeTool.java +++ b/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/AgentTaskOutcomeTool.java @@ -29,7 +29,7 @@ /** Captures intent. Only the adapter is allowed to commit the physical AgentTask lifecycle. */ public final class AgentTaskOutcomeTool { @Tool( - name = "task.submit_result", + name = "task_submit_result", description = "Submit the actual outcome of this AgentTask, then end your turn. succeeded" + " requires the full deliverable in result, not a plan or promise. waiting" diff --git a/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskStarter.java b/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskStarter.java index c7d6bac145..be152d9f2f 100644 --- a/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskStarter.java +++ b/agentscope-extensions/agentscope-extensions-aistio/src/main/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskStarter.java @@ -132,7 +132,7 @@ private void execute(AgentTaskAssignment assignment) { + " available CollaborationClient actions are registered as tools with" + " the exact names shown in availableActions. The adapter owns" + " task.complete and task.fail; do not call them. Before returning," - + " call task.submit_result with an explicit business outcome and the" + + " call task_submit_result with an explicit business outcome and the" + " actual deliverable. A promise to do work later is not completion." + " Check the tool capabilities before delegating: spawning a subagent" + " does not add missing web access." @@ -259,7 +259,7 @@ private AgentTaskOutcome runToOutcome( next = message( "The turn ended without a business outcome. Do the remaining work," - + " or call task.submit_result with blocked and the concrete" + + " or call task_submit_result with blocked and the concrete" + " missing capability. Do not submit a plan or waiting promise" + " as successful research."); continue; @@ -433,7 +433,7 @@ private void registerCollaborationTools( Set availableActions) { Object toolkit = runtimeAgent.getToolkit(); synchronized (toolkit) { - if (!runtimeAgent.getToolkit().getToolNames().contains("task.submit_result")) { + if (!runtimeAgent.getToolkit().getToolNames().contains("task_submit_result")) { runtimeAgent.getToolkit().registerTool(new AgentTaskOutcomeTool()); } for (JsonNode definition : @@ -466,12 +466,12 @@ static String roleInstructions(JsonNode envelope, List inputIds) { return " You are a Team worker, not its coordinator. Do not create or accept child" + " Issues and do not call run.node.complete, run.node.fail, or run.replan." + " Complete only the assigned work and submit its result using" - + " task.submit_result; the adapter will complete this AgentTask."; + + " task_submit_result; the adapter will complete this AgentTask."; } if (inputIds.isEmpty()) { return " You are the Team leader's initial task. If you delegate child work, return" + " immediately after issue.child.create succeeds by calling" - + " task.submit_result with waiting, a reason and the returned AgentTask" + + " task_submit_result with waiting, a reason and the returned AgentTask" + " IDs; do not wait through local session/task tools and do not call" + " run.node.complete yet. The control plane will deliver a fresh leader" + " follow-up when a worker result arrives. If no work is delegated, call" From c972c1ef8d4254f9c787161bcc7ab7a939fea1e9 Mon Sep 17 00:00:00 2001 From: jinison <507827762@qq.com> Date: Tue, 15 Sep 2026 15:44:18 +0800 Subject: [PATCH 2/2] test(aistio): cover the renamed task_submit_result tool name Codecov flagged the changed lines in HarnessAgentTaskStarter as uncovered. Add coverage for the rename: - AgentTaskCollaborationToolTest asserts the worker and leader role instructions reference task_submit_result. - HarnessAgentTaskOutcomeTest dispatches a second task on the same toolkit so the already-registered branch of registerCollaborationTools is exercised. --- .../AgentTaskCollaborationToolTest.java | 6 +++ .../adapter/HarnessAgentTaskOutcomeTest.java | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/AgentTaskCollaborationToolTest.java b/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/AgentTaskCollaborationToolTest.java index 5886cca97f..cddedba044 100644 --- a/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/AgentTaskCollaborationToolTest.java +++ b/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/AgentTaskCollaborationToolTest.java @@ -59,9 +59,15 @@ void roleInstructionsSeparateLeaderHandoffFromWorkerExecution() throws Exception assertTrue( HarnessAgentTaskStarter.roleInstructions(worker, List.of()) .contains("Team worker, not its coordinator")); + assertTrue( + HarnessAgentTaskStarter.roleInstructions(worker, List.of()) + .contains("task_submit_result")); assertTrue( HarnessAgentTaskStarter.roleInstructions(leader, List.of()) .contains("return immediately after issue.child.create succeeds")); + assertTrue( + HarnessAgentTaskStarter.roleInstructions(leader, List.of()) + .contains("task_submit_result with waiting")); String followUp = HarnessAgentTaskStarter.roleInstructions(leader, List.of("input-1")); assertTrue(followUp.contains("leader follow-up")); assertTrue(followUp.contains("Never send those mutations in parallel")); diff --git a/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskOutcomeTest.java b/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskOutcomeTest.java index 206489e98b..d2b6baf5be 100644 --- a/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskOutcomeTest.java +++ b/agentscope-extensions/agentscope-extensions-aistio/src/test/java/io/agentscope/extensions/aistio/adapter/HarnessAgentTaskOutcomeTest.java @@ -78,6 +78,54 @@ private HarnessAgentTaskStarter starter() throws Exception { return new HarnessAgentTaskStarter(() -> agent, client); } + @Test + void sameToolkitRegistersOutcomeToolOnlyOnce() throws Exception { + var starter = starter(); + when(agent.call(any(Msg.class), any(RuntimeContext.class))) + .thenAnswer( + invocation -> { + RuntimeContext ctx = invocation.getArgument(1); + ctx.get(AgentTaskOutcome.State.class) + .submit( + new AgentTaskOutcome( + "succeeded", "delivered", "", List.of())); + return Mono.just( + Msg.builder() + .role(MsgRole.ASSISTANT) + .textContent("done") + .build()); + }); + starter.start(assignment).block(); + // A second dispatch on the same toolkit must hit the "already registered" branch of + // registerCollaborationTools and still complete normally. + AgentTaskAssignment second = + new AgentTaskAssignment( + "attempt-2", + "task", + "run", + "node", + 1, + "dispatch", + "", + "secret-token", + "attempt-secret", + "assigned-session", + new byte[0], + 1); + starter.start(second).block(); + verify(agent, times(2)).call(any(Msg.class), any(RuntimeContext.class)); + verify(client, times(2)) + .finish( + eq("task"), + eq("secret-token"), + eq(4L), + eq("succeeded"), + eq(""), + eq("delivered"), + eq(List.of()), + eq(List.of())); + } + @Test void plainWaitingPromiseCannotBecomeSuccessfulBusinessCompletion() throws Exception { var starter = starter();