Merge pull request #5885 from deepseek-harness/fix/team-send-tool-receipt
fix(agent-team): omit message IDs from send tool results
This commit is contained in:
commit
12b8066c28
14 files changed
+30
-28
No files matched your search
@@ -264,8 +264,8 @@
|
||||
en: f9457f033f75f00a
|
||||
zh: a2d60ec44118c34c
|
||||
/tool-schema-catalog/deepseek-ai-dsh-experimental-tool-agent-team/send-message:
|
||||
en: 0746bb11dae8db55
|
||||
zh: 8b634add76c4d88a
|
||||
en: ec50e6a0b86aecc6
|
||||
zh: 29c8728536a5adf1
|
||||
/tool-schema-catalog/deepseek-ai-dsh-experimental-tool-agent-team/spawn-teammate:
|
||||
en: 02d6cc746e234b99
|
||||
zh: 49bc60ac098bca48
|
||||
|
||||
@@ -2257,7 +2257,7 @@ Source: [`packages/experimental/tool-agent-team/src/index.ts`](../packages/exper
|
||||
|
||||
### `send_message`
|
||||
|
||||
Send one message to another Team member and return its accepted message id. A running target receives it at the nearest step boundary; an inactive target starts or resumes a turn.
|
||||
Send one message to another Team member. A running target receives it at the nearest step boundary; an inactive target starts or resumes a turn.
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -2263,7 +2263,7 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后,
|
||||
|
||||
### `send_message`
|
||||
|
||||
向另一名 Team member 发送一条消息,并返回获准消息 id。running target 会在最近的步骤边界收到消息;inactive target 会启动或恢复一个 turn。
|
||||
向另一名 Team member 发送一条消息。running target 会在最近的步骤边界收到消息;inactive target 会启动或恢复一个 turn。
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
en: 04d7dcbdc1679eae
|
||||
zh: 8b083641a9262d80
|
||||
/agent-teams-messages-use-target-inboxes/change:
|
||||
en: 94d62cf9d98270ab
|
||||
zh: c589725cd35ccd06
|
||||
en: 9968decf3dd340af
|
||||
zh: 736a32caac4b5103
|
||||
/agent-teams-messages-use-target-inboxes/migration:
|
||||
en: f7811928da727edc
|
||||
zh: 9a28b6b745725a6d
|
||||
en: 7ffd442bfd55769e
|
||||
zh: ffb11a143f11ce7f
|
||||
@@ -9,7 +9,7 @@ English | [中文](guide.zh.md)
|
||||
|
||||
## Change
|
||||
|
||||
The experimental Agent Teams plugin sends new messages directly to the target Agent inbox instead of retaining a separate Team mailbox. `send_message` returns only `messageId`; it no longer returns an `accepted` or `queued` status. Acceptance does not confirm model processing or a synchronous storage flush. Failed sends are not retained for retry, and explicit resends are not deduplicated.
|
||||
The experimental Agent Teams plugin sends new messages directly to the target Agent inbox instead of retaining a separate Team mailbox. `send_message` returns only `{ "sent": true }`; it exposes neither a message ID nor an `accepted` or `queued` status. Acceptance does not confirm model processing or a synchronous storage flush. Failed sends are not retained for retry, and explicit resends are not deduplicated.
|
||||
|
||||
The `maxPendingMessagesPerMember` configuration key is removed. Historical `team/message/queued` and `team/message/delivered` records remain readable, but queued messages that were not delivered before upgrading are never dispatched. Roster and task state remain durable.
|
||||
|
||||
@@ -17,5 +17,5 @@ The `maxPendingMessagesPerMember` configuration key is removed. Historical `team
|
||||
|
||||
1. Before upgrading, let pending Team messages finish and confirm that their targets received them. After upgrading, explicitly resend any required undelivered message; check the target's history first to avoid duplicates. Do not edit historical Session records.
|
||||
2. Remove `maxPendingMessagesPerMember` from the `@deepseek-ai/dsh-experimental-agent-team` plugin configuration in your `cordis.yml` or overlays. There is no replacement Team queue limit; `maxMessageBytes` still bounds each complete sender-framed message.
|
||||
3. Update any consumer of `send_message` results to use `messageId` alone and handle send errors explicitly. Do not treat success as completed target work.
|
||||
4. Start your Team-enabled profile, send a message to another member, and confirm that the tool returns `messageId` and that the target receives it. Consult the [package reference](../../../../packages/experimental/agent-team/README.md#use-this-package) for delivery semantics.
|
||||
3. Update any consumer of `send_message` results to read `sent` instead of message IDs or delivery statuses, and handle send errors explicitly. Do not treat success as completed target work. The `ctx.agentTeams.sendMessage()` service still returns `messageId`.
|
||||
4. Start your Team-enabled profile, send a message to another member, and confirm that the tool returns `{ "sent": true }` and that the target receives it. Consult the [package reference](../../../../packages/experimental/agent-team/README.md#use-this-package) for delivery semantics.
|
||||
@@ -9,7 +9,7 @@ description: "Agent Teams 移除待投递消息数量限制,并停止投递历
|
||||
|
||||
## 变更
|
||||
|
||||
实验性 Agent Teams 插件将新消息直接发送到目标 Agent 收件箱,不再保留独立的 Team 邮箱。`send_message` 仅返回 `messageId`,不再返回 `accepted` 或 `queued` 状态。接收成功不表示模型已处理消息,也不保证同步刷新到存储。失败的发送不会保留以供重试,显式重发也不会去重。
|
||||
实验性 Agent Teams 插件将新消息直接发送到目标 Agent 收件箱,不再保留独立的 Team 邮箱。`send_message` 仅返回 `{ "sent": true }`,不暴露消息 ID,也不返回 `accepted` 或 `queued` 状态。接收成功不表示模型已处理消息,也不保证同步刷新到存储。失败的发送不会保留以供重试,显式重发也不会去重。
|
||||
|
||||
配置键 `maxPendingMessagesPerMember` 已移除。历史 `team/message/queued` 和 `team/message/delivered` 记录仍可读取,但升级前尚未投递的排队消息永远不会被派发。成员名单与任务状态仍然持久保存。
|
||||
|
||||
@@ -17,5 +17,5 @@ description: "Agent Teams 移除待投递消息数量限制,并停止投递历
|
||||
|
||||
1. 升级前,让待投递的 Team 消息完成投递,并确认目标已收到。升级后,显式重发仍需处理的未投递消息;先检查目标历史,避免重复。不要编辑历史 Session 记录。
|
||||
2. 从 `cordis.yml` 或覆盖配置中 `@deepseek-ai/dsh-experimental-agent-team` 插件的配置移除 `maxPendingMessagesPerMember`。没有替代的 Team 队列限制;`maxMessageBytes` 仍限制包含发送者标识的完整消息大小。
|
||||
3. 更新所有读取 `send_message` 结果的调用方,仅使用 `messageId`,并显式处理发送错误。不要把发送成功视为目标工作已完成。
|
||||
4. 启动启用 Team 的 profile,向其他成员发送消息,确认工具返回 `messageId` 且目标收到消息。投递语义参见[包参考](../../../../packages/experimental/agent-team/README.zh.md#use-this-package)。
|
||||
3. 更新所有读取 `send_message` 结果的调用方,改为读取 `sent` 而非消息 ID 或投递状态,并显式处理发送错误。不要把发送成功视为目标工作已完成。`ctx.agentTeams.sendMessage()` 服务仍返回 `messageId`。
|
||||
4. 启动启用 Team 的 profile,向其他成员发送消息,确认工具返回 `{ "sent": true }` 且目标收到消息。投递语义参见[包参考](../../../../packages/experimental/agent-team/README.zh.md#use-this-package)。
|
||||
@@ -27,8 +27,8 @@
|
||||
en: be42811bbcc3b438
|
||||
zh: 014bd055ca694315
|
||||
/deepseek-ai-dsh-experimental-tool-agent-team/use-this-package/what-success-and-failure-look-like:
|
||||
en: 259e9d39e1d1579a
|
||||
zh: c61feed86e1ba2d2
|
||||
en: e49a980372c55892
|
||||
zh: d4bcdd36278d040d
|
||||
/deepseek-ai-dsh-experimental-tool-agent-team/understand-the-implementation:
|
||||
en: 3952eb1d9de1f988
|
||||
zh: 789d51b88f41b710
|
||||
|
||||
@@ -65,7 +65,7 @@ Creation and listing results identify members by `target`, with no member Sessio
|
||||
|
||||
### What success and failure look like
|
||||
|
||||
Sending a message succeeds as soon as it is safely stored: the result is `accepted` (delivered now) or `queued` (waiting), and a queued message must not be resent. `wait_agent` returns `noProgress` right away when no other member is running or provisioning, telling the caller to wake a teammate first; otherwise it waits for the next change and the caller re-reads state afterward. Task edits based on an outdated revision are rejected rather than overwriting newer work.
|
||||
`send_message` returns `{ "sent": true }` after inbox acceptance, without exposing the message ID. Success does not confirm recipient processing or a synchronous storage flush; send failures are reported as tool errors. `wait_agent` returns `noProgress` right away when no other member is running or provisioning, telling the caller to wake a teammate first; otherwise it waits for the next change and the caller re-reads state afterward. Task edits based on an outdated revision are rejected rather than overwriting newer work.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ kind: "package-reference"
|
||||
|
||||
### 成功与失败的表现
|
||||
|
||||
发送消息在安全存储后即成功:结果为 `accepted`(已立即送达)或 `queued`(等待中),排队的消息绝不能重发。当没有其他成员 running 或 provisioning 时,`wait_agent` 会立即返回 `noProgress`,提示调用方先唤醒 teammate;否则它会等待下一次变化,调用方随后重新读取状态。基于过期 revision 的任务编辑会被拒绝,而不是覆盖更新的成果。
|
||||
`send_message` 在收件箱接收消息后返回 `{ "sent": true }`,不暴露消息 ID。成功不表示接收方已处理消息,也不保证同步刷新到存储;发送失败会作为工具错误报告。当没有其他成员 running 或 provisioning 时,`wait_agent` 会立即返回 `noProgress`,提示调用方先唤醒 teammate;否则它会等待下一次变化,调用方随后重新读取状态。基于过期 revision 的任务编辑会被拒绝,而不是覆盖更新的成果。
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ The Team Lead and all teammates share the same working directory and filesystem.
|
||||
|
||||
Prefer read/edit/write for file changes. If a file operation returns FS_STALE_VERSION, read the current file, rebase your intended change onto the new content, and retry. Bash, formatters, code generators, and scripts are not fully protected by the filesystem version guard; coordinate them explicitly and have the Lead review the final diff and run tests.
|
||||
|
||||
Use the target returned by spawn_teammate or list_agents for send_message and interrupt_agent, or as owner when assigning or filtering shared tasks. send_message steers a running target at its nearest step boundary and starts or resumes an inactive target. inactive means no turn is executing; it does not describe task completion, success, failure, or waiting for other agents. provisioning means member creation is in progress; failed means member creation failed. A peer message starts with its sender name. A successful send returns the accepted message id; it does not mean the recipient has processed it. Shared-task workflow is list, get, claim with the current revision, perform the work, then complete. Task readiness never starts an owner. Before wait_agent, use list_agents and make sure another required member is running or provisioning; use send_message first when the required member is inactive. wait_agent observes only changes after that call starts, never wakes a member, and returns noProgress immediately when no other member can produce a change. Re-list after wakeup or timeout. The Lead must wait for required teammates before giving the final answer.`
|
||||
Use the target returned by spawn_teammate or list_agents for send_message and interrupt_agent, or as owner when assigning or filtering shared tasks. send_message steers a running target at its nearest step boundary and starts or resumes an inactive target. inactive means no turn is executing; it does not describe task completion, success, failure, or waiting for other agents. provisioning means member creation is in progress; failed means member creation failed. A peer message starts with its sender name. A successful send does not mean the recipient has processed it. Shared-task workflow is list, get, claim with the current revision, perform the work, then complete. Task readiness never starts an owner. Before wait_agent, use list_agents and make sure another required member is running or provisioning; use send_message first when the required member is inactive. wait_agent observes only changes after that call starts, never wakes a member, and returns noProgress immediately when no other member can produce a change. Re-list after wakeup or timeout. The Lead must wait for required teammates before giving the final answer.`
|
||||
|
||||
const ACTIVE_WAIT_STATUSES: ReadonlySet<TeamMemberView['status']> = new Set(['running', 'provisioning'])
|
||||
const NO_ACTIVE_PEER_MESSAGE = 'No other Team member is running or provisioning. wait_agent cannot make progress or wake inactive teammates. Re-list with list_agents and team_task_list, then use send_message to wake each required inactive teammate before waiting again.'
|
||||
@@ -97,7 +97,7 @@ const SEND_VALUE_SCHEMA = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
messageId: { type: 'string', required: true },
|
||||
sent: { type: 'boolean', required: true, const: true },
|
||||
},
|
||||
} as const
|
||||
|
||||
@@ -213,18 +213,19 @@ To message another teammate, use send_message({ target: "<teammate name>", messa
|
||||
|
||||
register(scoped.tools.register(defineTool({
|
||||
name: 'send_message',
|
||||
description: 'Send one message to another Team member and return its accepted message id. A running target receives it at the nearest step boundary; an inactive target starts or resumes a turn.',
|
||||
description: 'Send one message to another Team member. A running target receives it at the nearest step boundary; an inactive target starts or resumes a turn.',
|
||||
parameters: {
|
||||
target: { type: 'string', required: true, description: 'Member target returned by spawn_teammate or list_agents, including lead.' },
|
||||
message: { type: 'string', required: true, description: 'Self-contained message for the target.' },
|
||||
},
|
||||
output: jsonOutput(SEND_VALUE_SCHEMA),
|
||||
execute(args, exec) {
|
||||
return ctx.agentTeams.sendMessage(callingAgent(exec.agent, 'send_message'), {
|
||||
async execute(args, exec) {
|
||||
await ctx.agentTeams.sendMessage(callingAgent(exec.agent, 'send_message'), {
|
||||
target: args.target,
|
||||
content: [{ type: 'text', text: args.message }],
|
||||
signal: exec.signal,
|
||||
})
|
||||
return { sent: true }
|
||||
},
|
||||
})))
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ describe('dsh-tool-team', () => {
|
||||
expect(JSON.parse(text(tasks))).toMatchObject({ tasks: [{ id: task.id, ownerName: member.target }] })
|
||||
const sent = await execute(ctx, lead, 'send_message', { target: member.target, message: 'review the diff' })
|
||||
expect(sent.isError).toBe(false)
|
||||
expect(JSON.parse(text(sent))).toEqual({ sent: true })
|
||||
const interrupted = await execute(ctx, lead, 'interrupt_agent', { target: listed[1]!.target })
|
||||
expect(interrupted.isError).toBe(false)
|
||||
await child.whenIdle()
|
||||
@@ -483,10 +484,10 @@ describe('dsh-tool-team', () => {
|
||||
expect(text(roster)).toBe(JSON.stringify(JSON.parse(text(roster))))
|
||||
const peer = await execute(ctx, child, 'send_message', { target: 'lead', message: 'progress report' })
|
||||
expect(peer.isError).toBe(false)
|
||||
expect(JSON.parse(text(peer))).toHaveProperty('messageId')
|
||||
expect(JSON.parse(text(peer))).toEqual({ sent: true })
|
||||
const followup = await execute(ctx, child, 'send_message', { target: 'lead', message: 'review the report' })
|
||||
expect(followup.isError).toBe(false)
|
||||
expect(JSON.parse(text(followup))).toHaveProperty('messageId')
|
||||
expect(JSON.parse(text(followup))).toEqual({ sent: true })
|
||||
await lead.whenIdle()
|
||||
|
||||
const created = await execute(ctx, lead, 'team_task_create', {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{"type":"step/start","data":{"turn":1,"step":3}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-3","name":"send_message","arguments":"{\"target\":\"reviewer\",\"message\":\"Review the direct inbox delivery.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1790429366017,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1790429366017,"index":0,"dt":[],"id":"direct-3","name":"send_message","args":["{\"target\":\"reviewer\",\"message\":\"Review the direct inbox delivery.\"}"]},{"type":"chunk","time":1790429366017,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-3","name":"send_message","arguments":"{\"target\":\"reviewer\",\"message\":\"Review the direct inbox delivery.\"}"}}},{"type":"chunk","time":1790429366017,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1790429366017,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":3,"callId":"direct-3","name":"send_message","arguments":"{\"target\":\"reviewer\",\"message\":\"Review the direct inbox delivery.\"}"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"direct-3"},"toolCallId":"direct-3","content":[{"type":"text","text":"{\"messageId\":\"{{message:17}}\"}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[27],"surfaceOp":"append"}
|
||||
{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"direct-3"},"toolCallId":"direct-3","content":[{"type":"text","text":"{\"sent\":true}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[27],"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":3}}
|
||||
{"type":"step/start","data":{"turn":1,"step":4}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-4","name":"interrupt_agent","arguments":"{\"target\":\"reviewer\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1790429366033,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1790429366033,"index":0,"dt":[],"id":"direct-4","name":"interrupt_agent","args":["{\"target\":\"reviewer\"}"]},{"type":"chunk","time":1790429366033,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-4","name":"interrupt_agent","arguments":"{\"target\":\"reviewer\"}"}}},{"type":"chunk","time":1790429366033,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1790429366033,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
|
||||
|
||||
@@ -11,7 +11,7 @@ The Team Lead and all teammates share the same working directory and filesystem.
|
||||
|
||||
Prefer read/edit/write for file changes. If a file operation returns FS_STALE_VERSION, read the current file, rebase your intended change onto the new content, and retry. Bash, formatters, code generators, and scripts are not fully protected by the filesystem version guard; coordinate them explicitly and have the Lead review the final diff and run tests.
|
||||
|
||||
Use the target returned by spawn_teammate or list_agents for send_message and interrupt_agent, or as owner when assigning or filtering shared tasks. send_message steers a running target at its nearest step boundary and starts or resumes an inactive target. inactive means no turn is executing; it does not describe task completion, success, failure, or waiting for other agents. provisioning means member creation is in progress; failed means member creation failed. A peer message starts with its sender name. A successful send returns the accepted message id; it does not mean the recipient has processed it. Shared-task workflow is list, get, claim with the current revision, perform the work, then complete. Task readiness never starts an owner. Before wait_agent, use list_agents and make sure another required member is running or provisioning; use send_message first when the required member is inactive. wait_agent observes only changes after that call starts, never wakes a member, and returns noProgress immediately when no other member can produce a change. Re-list after wakeup or timeout. The Lead must wait for required teammates before giving the final answer.
|
||||
Use the target returned by spawn_teammate or list_agents for send_message and interrupt_agent, or as owner when assigning or filtering shared tasks. send_message steers a running target at its nearest step boundary and starts or resumes an inactive target. inactive means no turn is executing; it does not describe task completion, success, failure, or waiting for other agents. provisioning means member creation is in progress; failed means member creation failed. A peer message starts with its sender name. A successful send does not mean the recipient has processed it. Shared-task workflow is list, get, claim with the current revision, perform the work, then complete. Task readiness never starts an owner. Before wait_agent, use list_agents and make sure another required member is running or provisioning; use send_message first when the required member is inactive. wait_agent observes only changes after that call starts, never wakes a member, and returns noProgress immediately when no other member can produce a change. Re-list after wakeup or timeout. The Lead must wait for required teammates before giving the final answer.
|
||||
|
||||
Check the [exit code: N] marker on every bash result; investigate failures before moving on.
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
},
|
||||
{
|
||||
"name": "send_message",
|
||||
"description": "Send one message to another Team member and return its accepted message id. A running target receives it at the nearest step boundary; an inactive target starts or resumes a turn.",
|
||||
"description": "Send one message to another Team member. A running target receives it at the nearest step boundary; an inactive target starts or resumes a turn.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Reference in new issue
Block a user