@tencent-ai/codebuddy-code 2.139.0-dev.c47174a.202608271521 → 2.139.0-dev.d4a09ae.202608281607
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codebuddy-headless.js +94 -93
- package/dist/codebuddy.js +140 -139
- package/dist-server/codebuddy.js +2 -2
- package/dist-server/types/node/a2a/a2a-gate.d.ts +30 -3
- package/dist-server/types/node/a2a/a2a-protocol.d.ts +56 -63
- package/dist-server/types/node/control-signal/control-signal-protocol.d.ts +0 -36
- package/dist-server/types/node/tools/a2a-get-task-tool.d.ts +83 -7
- package/dist-server/types/node/tools/a2a-send-message-tool.d.ts +10 -3
- package/dist-server/types/node/tools/a2a-tool-guards.d.ts +2 -1
- package/package.json +1 -1
- package/product.cloudhosted.json +2 -2
- package/product.internal.json +2 -2
- package/product.ioa.json +2 -2
- package/product.json +22 -10
- package/product.selfhosted.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AbTestService } from '@genie/product';
|
|
2
|
+
import type { Logger } from '@genie/telemetry';
|
|
2
3
|
/**
|
|
3
4
|
* Env var that force-enables outbound A2A independent of cloud product
|
|
4
5
|
* config — set it truthy (`1`/`true`/`yes`/`on`, see `BooleanUtils.isTruthy`)
|
|
@@ -13,12 +14,33 @@ import type { AbTestService } from '@genie/product';
|
|
|
13
14
|
* authority stays with cloud config (see CODEBUDDY.md §10's kill-switch
|
|
14
15
|
* semantics) so an operator can still remotely disable A2A even on hosts
|
|
15
16
|
* that happen to have this env var lingering in their process environment.
|
|
17
|
+
*
|
|
18
|
+
* Independent of, and AND'd with, `ENV_REMOTE_AGENTS_URL` below — this env
|
|
19
|
+
* var only ever forces the *feature switch* on, never bypasses the
|
|
20
|
+
* "is there actually a discovery URL configured" requirement.
|
|
16
21
|
*/
|
|
17
22
|
export declare const ENV_A2A_OUTBOUND_ENABLED = "CODEBUDDY_A2A_OUTBOUND_ENABLED";
|
|
23
|
+
/**
|
|
24
|
+
* Discovery source for outbound A2A agents (issue #99451 Part B). Read LIVE
|
|
25
|
+
* from `process.env` at the point of use — see `A2aAgentDiscoveryService`'s
|
|
26
|
+
* doc comment for why this must never be cached earlier (prewarm
|
|
27
|
+
* per-session env only lands at `activate`).
|
|
28
|
+
*
|
|
29
|
+
* ANDed into `isA2AOutboundEnabled()`: even if the feature switch is on, the
|
|
30
|
+
* gate stays off with no discovery source configured — there would be
|
|
31
|
+
* nothing to discover, and the 4 A2A tools would otherwise expose
|
|
32
|
+
* themselves to the model only to fail on first use.
|
|
33
|
+
*/
|
|
34
|
+
export declare const ENV_REMOTE_AGENTS_URL = "CODEBUDDY_REMOTE_AGENTS_URL";
|
|
18
35
|
/**
|
|
19
36
|
* Resolve whether outbound A2A is enabled for the current process.
|
|
20
37
|
*
|
|
21
|
-
*
|
|
38
|
+
* `CODEBUDDY_REMOTE_AGENTS_URL` is checked FIRST — cheap, synchronous, and
|
|
39
|
+
* lets us skip the `AbTestService` round trip entirely (and log a specific
|
|
40
|
+
* "off because no URL" reason, distinct from "off because the feature flag
|
|
41
|
+
* is off") when there's no discovery source configured at all.
|
|
42
|
+
*
|
|
43
|
+
* Feature-switch check order (first hit wins):
|
|
22
44
|
* 1. `CODEBUDDY_A2A_OUTBOUND_ENABLED` truthy → enabled, no `AbTestService`
|
|
23
45
|
* round trip needed.
|
|
24
46
|
* 2. `ProductFeature.A2AOutbound` via `AbTestService` (existing behavior).
|
|
@@ -27,5 +49,10 @@ export declare const ENV_A2A_OUTBOUND_ENABLED = "CODEBUDDY_A2A_OUTBOUND_ENABLED"
|
|
|
27
49
|
* `abTestService` is `undefined` in unassembled-container unit tests;
|
|
28
50
|
* those paths fall back to disabled, matching the same conservative
|
|
29
51
|
* default.
|
|
52
|
+
*
|
|
53
|
+
* `logger` is optional (debug-level only — this is called from every A2A
|
|
54
|
+
* tool's `isEnabled`/`execute`, so it must stay quiet by default) and used
|
|
55
|
+
* solely for the Logging section's "why is the gate off" diagnostic — it
|
|
56
|
+
* does not affect the returned value.
|
|
30
57
|
*/
|
|
31
|
-
export declare function isA2AOutboundEnabled(abTestService?: AbTestService): Promise<boolean>;
|
|
58
|
+
export declare function isA2AOutboundEnabled(abTestService?: AbTestService, logger?: Pick<Logger, 'debug'>): Promise<boolean>;
|
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
* Outbound A2A (Agent2Agent) protocol surface — CloudAgent Phase 1.
|
|
3
3
|
*
|
|
4
4
|
* Scope (see issue #99451): agent-cli acts ONLY as an outbound A2A client /
|
|
5
|
-
* orchestrator. It discovers Agent Cards from a
|
|
6
|
-
*
|
|
7
|
-
* via the official `@a2a-js/sdk`. There is no agent-cli A2A server, no
|
|
5
|
+
* orchestrator. It discovers Agent Cards from a discovery URL supplied via
|
|
6
|
+
* the `CODEBUDDY_REMOTE_AGENTS_URL` env var (see `a2a-gate.ts`) and invokes
|
|
7
|
+
* them via the official `@a2a-js/sdk`. There is no agent-cli A2A server, no
|
|
8
8
|
* remote-agent callback, and no A2A streaming (`sendMessageStream`/
|
|
9
|
-
* `subscribeToTask`) in Phase 1 — `sendMessage` always
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* (the model re-invokes `A2AGetTask` itself when it wants to check again —
|
|
13
|
-
* see `A2AGetTaskTool`).
|
|
9
|
+
* `subscribeToTask`) in Phase 1 — `sendMessage` always returns immediately
|
|
10
|
+
* with whatever the initial response is, and `A2AGetTaskTool` owns a
|
|
11
|
+
* bounded poll loop for non-terminal tasks (see that tool's doc comment).
|
|
14
12
|
*
|
|
15
|
-
* Discovery is LAZY
|
|
16
|
-
*
|
|
17
|
-
* `
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
13
|
+
* Discovery is LAZY: the actual network fetch happens on first A2A tool
|
|
14
|
+
* use, gated by `A2aDiscoveryReadyService` (single shared readiness gate,
|
|
15
|
+
* mirrors `McpReadyService` — see that file's doc comment for why a shared
|
|
16
|
+
* gate beats each tool independently deciding whether to fetch). The
|
|
17
|
+
* discovery URL itself is read live from `process.env` at `discover()`-call
|
|
18
|
+
* time (issue #99451 Part B) — never cached earlier, per this repo's
|
|
19
|
+
* prewarm convention (see `CODEBUDDY.md` §1.1's "env 消费同理" section):
|
|
20
|
+
* a prewarmed standby process only gets the real per-session env merged in
|
|
21
|
+
* at `activate`, and discovery's first use always happens after that point.
|
|
22
22
|
*
|
|
23
23
|
* State is process-global (like `SdkInitializeStateService`, not
|
|
24
24
|
* per-ACP-session like `AgentScopedMcpManager`): CloudAgent's real
|
|
@@ -30,35 +30,25 @@
|
|
|
30
30
|
* All Symbols/interfaces live here per the repo's `xxx-protocol.ts` convention
|
|
31
31
|
* (Symbol + interface; implementations live in sibling files).
|
|
32
32
|
*/
|
|
33
|
-
import type { RemoteAgentsConfig } from '../control-signal/control-signal-protocol';
|
|
34
33
|
export declare const A2aAgentRegistry: unique symbol;
|
|
35
34
|
export declare const A2aClientService: unique symbol;
|
|
36
35
|
export declare const A2aAgentDiscoveryService: unique symbol;
|
|
37
36
|
export declare const A2aDiscoveryReadyService: unique symbol;
|
|
38
|
-
/**
|
|
39
|
-
* Auth headers applied to an agent's own `sendMessage`/`getTask`/`cancelTask`
|
|
40
|
-
* calls. Internal registry shape — NOT part of the wire protocol anymore
|
|
41
|
-
* (see `RemoteAgentsConfig` in control-signal-protocol.ts for the actual
|
|
42
|
-
* host-supplied contract). `A2aAgentDiscoveryService` populates this from
|
|
43
|
-
* the single discovery-level `auth` config shared across every discovered
|
|
44
|
-
* agent (raw A2A `AgentCard`s carry no credentials of their own).
|
|
45
|
-
*/
|
|
46
|
-
export interface A2AAgentAuthConfig {
|
|
47
|
-
headers?: Record<string, string>;
|
|
48
|
-
}
|
|
49
37
|
/**
|
|
50
38
|
* Internal input shape for `A2aAgentRegistry.register()` — NOT part of the
|
|
51
39
|
* wire protocol (see module doc comment: the host only supplies a discovery
|
|
52
40
|
* URL now, not this shape directly). `A2aAgentDiscoveryService` maps each
|
|
53
41
|
* fetched raw `AgentCard` into one of these, deriving `agentId` from the
|
|
54
|
-
* card's own `name` field.
|
|
42
|
+
* card's own `name` field. No per-agent auth: the discovery URL (and every
|
|
43
|
+
* discovered agent's own calls, via the same proxy path) is routed through
|
|
44
|
+
* our own infrastructure, which handles auth uniformly (issue #99451 Part
|
|
45
|
+
* B) — see `A2aAgentDiscoveryService`'s doc comment.
|
|
55
46
|
*/
|
|
56
47
|
export interface A2AAgentDefinition {
|
|
57
48
|
agentId: string;
|
|
58
49
|
agentCard: Record<string, unknown>;
|
|
59
50
|
/** Additional natural-language aliases for discovery, beyond the card's own name/description/skills. */
|
|
60
51
|
aliases?: string[];
|
|
61
|
-
auth?: A2AAgentAuthConfig;
|
|
62
52
|
}
|
|
63
53
|
/**
|
|
64
54
|
* URI of the private codebuddy.ai extension that lets a caller pin a
|
|
@@ -82,9 +72,6 @@ export interface A2aRegisteredAgent {
|
|
|
82
72
|
agentId: string;
|
|
83
73
|
agentCard: A2aAgentCard;
|
|
84
74
|
aliases: string[];
|
|
85
|
-
auth?: {
|
|
86
|
-
headers?: Record<string, string>;
|
|
87
|
-
};
|
|
88
75
|
}
|
|
89
76
|
/**
|
|
90
77
|
* Minimal shape of an A2A v1.0 AgentCard this registry actually reads.
|
|
@@ -163,18 +150,11 @@ export interface A2aAgentRegistry {
|
|
|
163
150
|
*/
|
|
164
151
|
export interface A2aAgentDiscoveryService {
|
|
165
152
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*/
|
|
172
|
-
setConfig(config: RemoteAgentsConfig | undefined): void;
|
|
173
|
-
/**
|
|
174
|
-
* Fetch the configured URL, parse the response as a JSON array of A2A
|
|
175
|
-
* v1.0 `AgentCard` objects, map each into an `A2AAgentDefinition`
|
|
176
|
-
* (`agentId` derived from `agentCard.name`, `auth` from the shared
|
|
177
|
-
* discovery-level config), and register the result via
|
|
153
|
+
* Fetch `CODEBUDDY_REMOTE_AGENTS_URL` (read live from `process.env` at
|
|
154
|
+
* call time — see module doc comment on why this must never be cached
|
|
155
|
+
* earlier), parse the response as a JSON array of A2A v1.0 `AgentCard`
|
|
156
|
+
* objects, map each into an `A2AAgentDefinition` (`agentId` derived
|
|
157
|
+
* from `agentCard.name`), and register the result via
|
|
178
158
|
* `A2aAgentRegistry.register()`. Always performs a fresh fetch when
|
|
179
159
|
* called — caching/dedup against repeated calls is `A2aDiscoveryReadyService`'s
|
|
180
160
|
* job, not this service's.
|
|
@@ -184,7 +164,7 @@ export interface A2aAgentDiscoveryService {
|
|
|
184
164
|
* `A2AAgentSearchTool`'s existing empty-registry response, not a thrown
|
|
185
165
|
* error.
|
|
186
166
|
*
|
|
187
|
-
* No-op (resolves immediately) when
|
|
167
|
+
* No-op (resolves immediately) when the env var is unset/empty.
|
|
188
168
|
*/
|
|
189
169
|
discover(options?: {
|
|
190
170
|
signal?: AbortSignal;
|
|
@@ -192,7 +172,7 @@ export interface A2aAgentDiscoveryService {
|
|
|
192
172
|
}
|
|
193
173
|
/**
|
|
194
174
|
* Single shared readiness gate ensuring the discovery fetch runs exactly
|
|
195
|
-
* once
|
|
175
|
+
* once per process lifetime no matter which of the 4 A2A tools is the
|
|
196
176
|
* first to need it. Mirrors `McpReadyService`'s "trigger once, let every
|
|
197
177
|
* caller await the same in-flight promise" shape — deliberately NOT
|
|
198
178
|
* duplicated per tool (an earlier draft of this refactor called
|
|
@@ -204,19 +184,18 @@ export interface A2aAgentDiscoveryService {
|
|
|
204
184
|
export interface A2aDiscoveryReadyService {
|
|
205
185
|
/**
|
|
206
186
|
* Triggers `A2aAgentDiscoveryService.discover()` if not already
|
|
207
|
-
* started
|
|
208
|
-
*
|
|
209
|
-
*
|
|
187
|
+
* started, and awaits it. Idempotent and safe to call from every A2A
|
|
188
|
+
* tool's guard — concurrent callers await the same in-flight fetch
|
|
189
|
+
* rather than each starting their own.
|
|
210
190
|
*/
|
|
211
191
|
waitForDiscovery(): Promise<void>;
|
|
212
192
|
/**
|
|
213
193
|
* Drop the cached in-flight/completed fetch so the NEXT `waitForDiscovery()`
|
|
214
|
-
* call triggers a fresh `discover()`.
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* agent list forever.
|
|
194
|
+
* call triggers a fresh `discover()`. Test/process-teardown use — in
|
|
195
|
+
* production there is exactly one discovery source per process
|
|
196
|
+
* lifetime (issue #99451 Part B: `CODEBUDDY_REMOTE_AGENTS_URL` is a
|
|
197
|
+
* process-env value, not something that changes mid-session), so
|
|
198
|
+
* nothing in the normal request path calls this anymore.
|
|
220
199
|
*/
|
|
221
200
|
reset(): void;
|
|
222
201
|
}
|
|
@@ -245,7 +224,17 @@ export interface A2aSendMessageResult {
|
|
|
245
224
|
/** Raw SDK response, retained for `ToolResult.rawResponse` (diagnostics only, never shown raw to the model). */
|
|
246
225
|
raw: unknown;
|
|
247
226
|
}
|
|
248
|
-
export type A2aTaskStatus = 'submitted' | 'working' | 'completed' | 'failed' | 'canceled' | 'rejected' | 'input-required' | 'auth-required'
|
|
227
|
+
export type A2aTaskStatus = 'submitted' | 'working' | 'completed' | 'failed' | 'canceled' | 'rejected' | 'input-required' | 'auth-required'
|
|
228
|
+
/**
|
|
229
|
+
* The wire's `TASK_STATE_UNSPECIFIED` (a legal, spec-defined "unknown or
|
|
230
|
+
* indeterminate state" per the A2A `TaskState` enum) or an enum value this
|
|
231
|
+
* SDK version doesn't recognize yet (`TaskState.UNRECOGNIZED`) — NOT an
|
|
232
|
+
* agent-cli-side error. Non-terminal: `A2AGetTaskTool` keeps polling
|
|
233
|
+
* through it exactly like `submitted`/`working`, on the theory that a
|
|
234
|
+
* spec-compliant remote agent reporting this will eventually resolve to a
|
|
235
|
+
* real state.
|
|
236
|
+
*/
|
|
237
|
+
| 'unknown';
|
|
249
238
|
export interface A2aArtifactSummary {
|
|
250
239
|
name?: string;
|
|
251
240
|
mediaType?: string;
|
|
@@ -273,16 +262,20 @@ export interface A2aTaskSnapshot {
|
|
|
273
262
|
/**
|
|
274
263
|
* Thin wrapper around `@a2a-js/sdk`'s `ClientFactory` + `Client`. Owns the
|
|
275
264
|
* per-`agentId` `Client` cache so repeated calls to the same agent within the
|
|
276
|
-
* process lifetime reuse the negotiated transport.
|
|
277
|
-
*
|
|
278
|
-
*
|
|
265
|
+
* process lifetime reuse the negotiated transport. `sendMessage`/`getTask`/
|
|
266
|
+
* `cancelTask` are each a single RPC call — whatever status the remote agent
|
|
267
|
+
* returns (terminal or not) is returned as-is, unmodified; no polling loop
|
|
268
|
+
* and no `sendMessageStream`/`subscribeToTask` live here (issue #99451 Phase
|
|
269
|
+
* 2: the poll loop for a non-terminal task lives exclusively in
|
|
270
|
+
* `A2AGetTaskTool`, one layer up — see that tool's doc comment).
|
|
279
271
|
*/
|
|
280
272
|
export interface A2aClientService {
|
|
281
273
|
/**
|
|
282
|
-
* Send a message to `agentId` (optionally targeting `skillId`) and
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
274
|
+
* Send a message to `agentId` (optionally targeting `skillId`) and
|
|
275
|
+
* return whatever the single `message/send` RPC call gets back —
|
|
276
|
+
* terminal, `working`, `input-required`, anything — unmodified.
|
|
277
|
+
* `contextId`/`taskId` are supplied by the caller to continue a prior
|
|
278
|
+
* turn; omit both to start a new task/context.
|
|
286
279
|
*/
|
|
287
280
|
sendMessage(agentId: string, params: {
|
|
288
281
|
text: string;
|
|
@@ -120,35 +120,6 @@ export interface SdkCapabilities {
|
|
|
120
120
|
form?: boolean;
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Host-managed authentication reference for the outbound A2A discovery
|
|
125
|
-
* endpoint. Headers are forwarded on the discovery fetch itself AND reused
|
|
126
|
-
* for every discovered agent's own `sendMessage`/`getTask`/`cancelTask`
|
|
127
|
-
* calls (raw A2A `AgentCard`s carry no credentials of their own — see
|
|
128
|
-
* `RemoteAgentsConfig`'s doc comment) — never exposed to the model (not in
|
|
129
|
-
* tool schemas, prompts, or logs).
|
|
130
|
-
*/
|
|
131
|
-
export interface RemoteAgentsAuthConfig {
|
|
132
|
-
headers?: Record<string, string>;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Outbound A2A agent discovery source, supplied by a trusted host (e.g.
|
|
136
|
-
* CloudAgent) at session start via the SDK `initialize` control_request.
|
|
137
|
-
* Replaces the old inline `a2aAgents` array (issue #99451 Phase 1): instead
|
|
138
|
-
* of the host pushing a fully-resolved agent list, it hands the CLI ONE
|
|
139
|
-
* discovery URL that returns a plain array of standard A2A v1.0 `AgentCard`
|
|
140
|
-
* objects (no host-specific wrapper shape) — the CLI fetches it itself,
|
|
141
|
-
* lazily, on first A2A tool use (see `A2aAgentDiscoveryService`).
|
|
142
|
-
*
|
|
143
|
-
* `agentId` (the stable key used in `@Agent`/`@Agent.Skill` routing) is
|
|
144
|
-
* derived from each card's own `name` field, since a standard `AgentCard`
|
|
145
|
-
* has no separate host-assigned identifier.
|
|
146
|
-
*/
|
|
147
|
-
export interface RemoteAgentsConfig {
|
|
148
|
-
/** Discovery endpoint. Expected to return a JSON array of A2A v1.0 AgentCard objects. */
|
|
149
|
-
url: string;
|
|
150
|
-
auth?: RemoteAgentsAuthConfig;
|
|
151
|
-
}
|
|
152
123
|
/**
|
|
153
124
|
* 初始化请求
|
|
154
125
|
*/
|
|
@@ -168,13 +139,6 @@ export interface ControlInitializeRequest {
|
|
|
168
139
|
capabilities?: SdkCapabilities;
|
|
169
140
|
/** 是否有 prompt 将在初始化后发送(用于 resume 时决定是否回放历史) */
|
|
170
141
|
hasPrompt?: boolean;
|
|
171
|
-
/**
|
|
172
|
-
* 出站 A2A agent 发现源(CloudAgent 一期:仅同步出站调用)。由 AbTestService 的
|
|
173
|
-
* `ProductFeature.A2AOutbound` 端到端门控;关闭时完全忽略此字段,不发起任何
|
|
174
|
-
* 网络请求。真正的 fetch 发生在模型首次调用任一 A2A 工具时(懒加载),initialize
|
|
175
|
-
* 握手本身只存配置,不阻塞在网络 I/O 上。
|
|
176
|
-
*/
|
|
177
|
-
remoteAgentsConfig?: RemoteAgentsConfig;
|
|
178
142
|
}
|
|
179
143
|
/**
|
|
180
144
|
* 初始化响应
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { AbTestService } from '@genie/product';
|
|
2
|
+
import { Logger } from '@genie/telemetry';
|
|
2
3
|
import { RunContext } from '@openai/agents';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { A2aAgentRegistry, A2aClientService, A2aDiscoveryReadyService } from '../a2a/a2a-protocol';
|
|
6
|
+
import { FunctionCallItem } from '../agent/openai-agents-types';
|
|
5
7
|
import { Session } from '../session/session-protocol';
|
|
6
8
|
import { BaseTool } from './base-tool';
|
|
7
9
|
import { Tool, ToolCallArgumentRenderer, ToolNames, ToolResult } from './tool-protocol';
|
|
@@ -15,13 +17,46 @@ declare const A2AGetTaskParameters: z.ZodObject<{
|
|
|
15
17
|
agent_id: string;
|
|
16
18
|
task_id: string;
|
|
17
19
|
}>;
|
|
20
|
+
export declare const ENV_POLL_INTERVAL_MS = "CODEBUDDY_A2A_POLL_INTERVAL_MS";
|
|
21
|
+
export declare const ENV_POLL_BACKOFF_MULTIPLIER = "CODEBUDDY_A2A_POLL_BACKOFF_MULTIPLIER";
|
|
22
|
+
export declare const ENV_POLL_MAX_INTERVAL_MS = "CODEBUDDY_A2A_POLL_MAX_INTERVAL_MS";
|
|
23
|
+
export declare const ENV_POLL_MAX_ATTEMPTS = "CODEBUDDY_A2A_POLL_MAX_ATTEMPTS";
|
|
24
|
+
export declare const ENV_POLL_MAX_DURATION_MS = "CODEBUDDY_A2A_POLL_MAX_DURATION_MS";
|
|
25
|
+
export interface A2APollPolicy {
|
|
26
|
+
/** Initial poll interval, ms. Default 2s. */
|
|
27
|
+
initialIntervalMs: number;
|
|
28
|
+
/** Exponential backoff multiplier applied after each poll. Default 1.5x. */
|
|
29
|
+
backoffMultiplier: number;
|
|
30
|
+
/** Interval cap the backoff schedule never exceeds. Default 30s. */
|
|
31
|
+
maxIntervalMs: number;
|
|
32
|
+
/** Hard cap on poll attempts — a secondary, independent circuit breaker from `maxDurationMs`. Default 60. */
|
|
33
|
+
maxAttempts: number;
|
|
34
|
+
/** Overall wall-clock budget for the whole loop, ms. Default 15 minutes. */
|
|
35
|
+
maxDurationMs: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the effective poll policy, applying any env overrides on top of
|
|
39
|
+
* `DEFAULT_POLL_POLICY`. Read fresh on every `execute()` call — not cached
|
|
40
|
+
* — so a test/host can change the env between calls.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolvePollPolicy(): A2APollPolicy;
|
|
18
43
|
/**
|
|
19
|
-
* A2AGetTask —
|
|
20
|
-
* (issue #99451
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
44
|
+
* A2AGetTask — wait for a previously created outbound A2A task to finish
|
|
45
|
+
* (issue #99451 CloudAgent Phase 1; Phase 2 revises this from a bare
|
|
46
|
+
* single-shot query into the tool that owns the ENTIRE bounded, streamed
|
|
47
|
+
* poll loop — see the design doc's "A.7 Tool ownership split" section).
|
|
48
|
+
*
|
|
49
|
+
* Every non-terminal status — including `input-required`/`auth-required` —
|
|
50
|
+
* is relayed (A.2's live-progress mechanism, reusing the foreground Bash
|
|
51
|
+
* tool's `session.inputSubject.next({status: 'in_progress', ...})` pattern)
|
|
52
|
+
* and polled straight through with exponential backoff (A.3), never treated
|
|
53
|
+
* as a stop condition (A.5): the remote agent's own resolution path for
|
|
54
|
+
* those states (e.g. a WeCom approval card) is out of band from agent-cli
|
|
55
|
+
* entirely, so there is nothing for this tool to pause and ask the model
|
|
56
|
+
* about. Only the 4 true terminal states (`completed`/`failed`/`canceled`/
|
|
57
|
+
* `rejected`) or one of the two independent circuit breakers
|
|
58
|
+
* (`maxAttempts`/`maxDurationMs`, both env-overridable — see
|
|
59
|
+
* `resolvePollPolicy()`) stop the loop.
|
|
25
60
|
*
|
|
26
61
|
* Restricted to the main session, same recursive-delegation guard as
|
|
27
62
|
* `A2ASendMessageTool` (see that tool's doc comment).
|
|
@@ -31,6 +66,7 @@ export declare class A2AGetTaskTool extends BaseTool implements Tool {
|
|
|
31
66
|
protected readonly client?: A2aClientService;
|
|
32
67
|
protected readonly abTestService?: AbTestService;
|
|
33
68
|
protected readonly discoveryReadyService?: A2aDiscoveryReadyService;
|
|
69
|
+
protected readonly logger: Logger;
|
|
34
70
|
name: ToolNames;
|
|
35
71
|
description: string;
|
|
36
72
|
needsApproval: boolean;
|
|
@@ -48,6 +84,46 @@ export declare class A2AGetTaskTool extends BaseTool implements Tool {
|
|
|
48
84
|
isEnabled: (args: {
|
|
49
85
|
runContext: RunContext<Session>;
|
|
50
86
|
}) => Promise<boolean>;
|
|
51
|
-
execute(input: z.infer<typeof A2AGetTaskParameters>, context: RunContext<Session
|
|
87
|
+
execute(input: z.infer<typeof A2AGetTaskParameters>, context: RunContext<Session>, details?: {
|
|
88
|
+
toolCall: FunctionCallItem;
|
|
89
|
+
}): Promise<ToolResult>;
|
|
90
|
+
private pollOnce;
|
|
91
|
+
/**
|
|
92
|
+
* A.2's live-progress relay — the SAME `session.inputSubject.next({...})`
|
|
93
|
+
* shape the foreground Bash tool uses for its own streaming updates
|
|
94
|
+
* (`bash-tool.ts`'s `updateToolResult()`), reusing the tool call's own
|
|
95
|
+
* `callId` and a single `streamingResultId` held for the whole loop.
|
|
96
|
+
* `providerData.toolResult.rawResponse.a2a` carries the FULL snapshot
|
|
97
|
+
* (including `snapshot.raw`'s structured `parts`/`metadata`), not just
|
|
98
|
+
* the derived text — required for a frontend that knows how to render a
|
|
99
|
+
* structured interrupt payload (e.g. a WeCom `wecom_part_type: 'approval'`
|
|
100
|
+
* card, see A.2) to have the data it needs.
|
|
101
|
+
*
|
|
102
|
+
* **Also pushes the SAME item to `session.combineSubject`** — traced
|
|
103
|
+
* empirically (issue #99451 Phase 2 e2e work) that `inputSubject.next()`
|
|
104
|
+
* alone, exactly as Bash's own `updateToolResult()` does it, never
|
|
105
|
+
* reaches either transport's actual subscription: both
|
|
106
|
+
* `stream-json-view.ts` and `acp-agent.ts` subscribe ONLY to
|
|
107
|
+
* `session.combineSubject` (`{type: 'input', data: item}`), and nothing
|
|
108
|
+
* bridges `inputSubject` into `combineSubject` except
|
|
109
|
+
* `SessionManager.addHistory()`'s explicit dual-push for REAL history
|
|
110
|
+
* items — which a live, ephemeral, non-terminal relay frame like this
|
|
111
|
+
* one deliberately is not (it's never persisted to `session.history`).
|
|
112
|
+
* Confirmed live: Bash's OWN in-progress streaming updates do NOT
|
|
113
|
+
* currently reach stream-json `-p` output either (only its terminal
|
|
114
|
+
* result does) — that's a pre-existing gap in `bash-tool.ts`, out of
|
|
115
|
+
* scope to fix here, but exactly why this method can't just copy Bash's
|
|
116
|
+
* `inputSubject`-only call and call it done: A.2's actual requirement is
|
|
117
|
+
* multiple live relay frames on BOTH transports, so this pushes to both
|
|
118
|
+
* subjects explicitly rather than reproducing that gap.
|
|
119
|
+
*/
|
|
120
|
+
private relayProgress;
|
|
121
|
+
/**
|
|
122
|
+
* Fallback result when a circuit breaker (not a terminal status) stops
|
|
123
|
+
* the loop. Per A.5: surfaces the last observed status/clarification —
|
|
124
|
+
* useful context even though it wasn't treated as a stop condition, the
|
|
125
|
+
* loop stopped because of the budget, not because of an interrupt.
|
|
126
|
+
*/
|
|
127
|
+
private buildBudgetExceededResult;
|
|
52
128
|
}
|
|
53
129
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AbTestService } from '@genie/product';
|
|
2
|
+
import { Logger } from '@genie/telemetry';
|
|
2
3
|
import { RunContext } from '@openai/agents';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { A2aAgentRegistry, A2aClientService, A2aDiscoveryReadyService } from '../a2a/a2a-protocol';
|
|
@@ -20,9 +21,14 @@ declare const A2ASendMessageParameters: z.ZodObject<{
|
|
|
20
21
|
}>;
|
|
21
22
|
export declare function continuationKey(agentId: string, skillId: string | undefined): string;
|
|
22
23
|
/**
|
|
23
|
-
* A2ASendMessage — invoke one outbound A2A agent
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* A2ASendMessage — invoke one outbound A2A agent (issue #99451 CloudAgent
|
|
25
|
+
* Phase 1; ownership split revised in Phase 2, see the design doc's "A.7
|
|
26
|
+
* Tool ownership split" section). Single, non-looping RPC call: makes
|
|
27
|
+
* exactly one `message/send` request and returns whatever it gets back to
|
|
28
|
+
* the model UNMODIFIED — terminal, `working`, `input-required`, anything.
|
|
29
|
+
* No polling, no streaming, no waiting for a terminal state — that's
|
|
30
|
+
* `A2AGetTaskTool`'s job (see its doc comment), which this tool's own
|
|
31
|
+
* description tells the model to call when the result isn't final.
|
|
26
32
|
*
|
|
27
33
|
* Restricted to the main session: subagent/team sessions never see this tool
|
|
28
34
|
* enabled, which is the enforcement point that prevents A2A-triggered
|
|
@@ -36,6 +42,7 @@ export declare class A2ASendMessageTool extends BaseTool implements Tool {
|
|
|
36
42
|
protected readonly client?: A2aClientService;
|
|
37
43
|
protected readonly abTestService?: AbTestService;
|
|
38
44
|
protected readonly discoveryReadyService?: A2aDiscoveryReadyService;
|
|
45
|
+
protected readonly logger: Logger;
|
|
39
46
|
name: ToolNames;
|
|
40
47
|
description: string;
|
|
41
48
|
needsApproval: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AbTestService } from '@genie/product';
|
|
2
|
+
import type { Logger } from '@genie/telemetry';
|
|
2
3
|
import { A2aAgentRegistry, A2aDiscoveryReadyService, A2aRegisteredAgent } from '../a2a/a2a-protocol';
|
|
3
4
|
import { Session } from '../session/session-protocol';
|
|
4
5
|
import { ToolErrorCode } from './tool-error-codes';
|
|
@@ -28,7 +29,7 @@ export declare function isSubSession(session: Session | undefined): boolean;
|
|
|
28
29
|
* assembled (e.g. a headless build without the A2A feature), matching this
|
|
29
30
|
* function's existing tolerance for missing components via `hasRequiredComponents`.
|
|
30
31
|
*/
|
|
31
|
-
export declare function assertA2AToolAllowed(hasRequiredComponents: boolean, session: Session | undefined, abTestService: AbTestService | undefined, discoveryReadyService?: A2aDiscoveryReadyService): Promise<void>;
|
|
32
|
+
export declare function assertA2AToolAllowed(hasRequiredComponents: boolean, session: Session | undefined, abTestService: AbTestService | undefined, discoveryReadyService?: A2aDiscoveryReadyService, logger?: Pick<Logger, 'debug'>): Promise<void>;
|
|
32
33
|
/** Look up `agentId` in the registry, throwing a consistent "not registered" error (with the list of available agents) if missing. */
|
|
33
34
|
export declare function requireA2AAgent(registry: A2aAgentRegistry, agentId: string): A2aRegisteredAgent;
|
|
34
35
|
/** Classify a thrown A2aClientService error into the right ToolErrorCode by matching known message patterns (see a2a-client-service.ts's doc comment on why we match text rather than `instanceof` typed SDK errors — this SDK version's JSON-RPC transport doesn't reconstruct semantic error subclasses). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tencent-ai/codebuddy-code",
|
|
3
|
-
"version": "2.139.0-dev.
|
|
3
|
+
"version": "2.139.0-dev.d4a09ae.202608281607",
|
|
4
4
|
"description": "Use CodeBuddy, Tencent's AI assistant, right from your terminal. CodeBuddy can understand your codebase, edit files, run terminal commands, and handle entire workflows for you.",
|
|
5
5
|
"main": "lib/node/index.js",
|
|
6
6
|
"typings": "lib/node/index.d.ts",
|
package/product.cloudhosted.json
CHANGED
|
@@ -824,6 +824,6 @@
|
|
|
824
824
|
"SelectImage": true,
|
|
825
825
|
"SkipToolCallSupportCheck": true
|
|
826
826
|
},
|
|
827
|
-
"commit": "
|
|
828
|
-
"date": "2026-08-
|
|
827
|
+
"commit": "d4a09ae479e13e4ffdc70d77b5f1bd584aeb6128",
|
|
828
|
+
"date": "2026-08-28T08:06:20.634Z"
|
|
829
829
|
}
|
package/product.internal.json
CHANGED
package/product.ioa.json
CHANGED
package/product.json
CHANGED
|
@@ -1239,6 +1239,22 @@
|
|
|
1239
1239
|
"name": "tool-deferexecutetool-description",
|
|
1240
1240
|
"template": "Execute a deferred tool by name. Use this to invoke tools discovered via ToolSearch without needing them in the active tools list.\n\nUsage:\n- First use ToolSearch to discover a tool and learn its parameter schema\n- Then call this tool with the exact tool name and parameters\n- Parameters are validated against the tool's schema before execution\n- The target tool's permission checks and hooks are applied normally\n\nExample:\nDeferExecuteTool({ toolName: \"ImageGen\", params: { prompt: \"a sunset over mountains\" } })\n\nNotes:\n- If parameter validation fails, a detailed error with the expected schema is returned\n- You can skip ToolSearch if you already know the tool name and parameters from a previous turn\n- This tool follows standard permission checks (may require approval depending on permissions configuration)\n"
|
|
1241
1241
|
},
|
|
1242
|
+
{
|
|
1243
|
+
"name": "tool-a2aagentsearch-description",
|
|
1244
|
+
"template": "Search for outbound A2A (Agent2Agent) agents authorized for this conversation.\n\nUse this to discover which agents are available before calling A2ASendMessage:\n- Pass `query` with a natural-language description of the task (\"translate a document\", \"look up an order status\") to search by name/description/skill.\n- Pass `agent_id` to look up one agent directly when you already know its exact id (e.g. from an explicit @Agent mention).\n- Omit both to list every agent currently authorized.\n\nResults include each agent's `agent_id`, name, description, and declared skills (with their own `id`/name/description). A `supports_skill_routing` flag tells you whether `A2ASendMessage`'s `skill_id` parameter will actually be honored by that agent — if false, send the task as a plain message and let the agent dispatch it itself.\n\nThis tool never contacts the network; it only reads the agents registered for this session.\n"
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
"name": "tool-a2asendmessage-description",
|
|
1248
|
+
"template": "Send a message to one outbound A2A (Agent2Agent) agent authorized for this conversation.\n\nParameters:\n- `agent_id` (required): exact id from A2AAgentSearch or an explicit @Agent mention.\n- `message` (required): a concise, self-contained task or question. Do not paste the whole conversation — state the goal and only the context the agent actually needs (the agent has no visibility into this conversation otherwise).\n- `skill_id` (optional): route directly to one declared skill, for @Agent.Skill-style precise dispatch. Only effective when A2AAgentSearch reported `supports_skill_routing: true` for that agent; otherwise this is ignored.\n\nThis call is NOT synchronous: it returns immediately with whatever single reply the agent gave, and does not wait for the task to finish. Check the returned status: only completed/failed/canceled/rejected are final. For ANY other status — working, submitted, input-required, or auth-required — you MUST immediately call A2AGetTask with the returned task_id, in the same turn, before doing anything else with this task. Do not wait, and do not ask the user for permission first.\n\nauth-required and input-required are NOT a signal to stop and hand off to the user by themselves — calling A2AGetTask to check status is just reading state, not performing a login or filling credentials on the user's behalf, so it does not conflict with any policy against automating logins. A2AGetTask polls automatically with backoff, relays the agent's own request (e.g. \"please sign in\", a clarifying question) to the user exactly once, and keeps waiting for the remote task to advance — it will surface the eventual outcome once the user (or the agent) resolves whatever it was waiting on.\n\nBehavior notes:\n- Calling the same `agent_id` (and `skill_id`, if used) again in a later turn continues the same conversation with that agent automatically — you don't need to restate prior context.\n- To message multiple agents in one turn, call this tool once per agent as parallel tool calls — one agent failing does not affect the others.\n- Any files or links the agent produced are returned as part of the result text.\n- Not available inside a sub-agent or team-member session — only the main conversation can call outbound A2A agents.\n"
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
"name": "tool-a2agettask-description",
|
|
1252
|
+
"template": "Wait for a previously created outbound A2A task to finish, polling automatically.\n\nParameters:\n- `agent_id` (required): the same agentId passed to the original A2ASendMessage call.\n- `task_id` (required): the taskId from a prior A2ASendMessage or A2AGetTask result.\n\nThis call polls the remote task's real, current state with backoff, showing live progress as it goes, up to a bounded time/attempt limit. It returns once the task reaches a final state (completed/failed/canceled/rejected), or a \"still running\" result if the bound is hit first — if you get \"still running\", immediately call A2AGetTask again with the same task_id to keep waiting; do not give up after one bounded call.\n\nIf the task is waiting on external input or auth (input-required/auth-required), that request is relayed to the user once and polling continues with backoff — the next call/round of polling reflects the task's real state at that later point in time, not a repeat of what you already saw. Checking status this way is just reading state, not performing a login or filling credentials on the user's behalf, so calling this while the task is waiting on the user to complete a login/approval step out of band does not conflict with any policy against automating logins.\n\nNot available inside a sub-agent or team-member session — only the main conversation can query outbound A2A tasks.\n"
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
"name": "tool-a2acanceltask-description",
|
|
1256
|
+
"template": "Request cancellation of a previously created outbound A2A task.\n\nParameters:\n- `agent_id` (required): the same agentId passed to the original A2ASendMessage call.\n- `task_id` (required): the taskId from a prior A2ASendMessage or A2AGetTask result.\n\nCancellation is a best-effort request, not a guarantee. If the task has already reached a terminal state (completed/failed/canceled/rejected), the call fails and the tool result will say so — that is expected, not an error to retry. Use A2AGetTask if you need to check the task's status before deciding whether to cancel it.\n\nNot available inside a sub-agent or team-member session — only the main conversation can cancel outbound A2A tasks.\n"
|
|
1257
|
+
},
|
|
1242
1258
|
{
|
|
1243
1259
|
"name": "tool-describetool-description",
|
|
1244
1260
|
"template": "Get detailed information about a specific tool by name.\n\nUse this when you already know the tool name and need its full definition (description and input schema). This is more efficient than searching when the tool name is known.\n\nUsage:\n- Provide the exact tool name (case-sensitive)\n- Returns the tool's full description and parameter schema\n- The tool is automatically activated for use in subsequent messages\n\nIf the tool is not found, consider using ToolSearch to discover available tools by description.\n"
|
|
@@ -2197,25 +2213,21 @@
|
|
|
2197
2213
|
},
|
|
2198
2214
|
{
|
|
2199
2215
|
"name": "A2AAgentSearch",
|
|
2200
|
-
"description": "tool-a2aagentsearch-description"
|
|
2201
|
-
"deferLoading": true
|
|
2216
|
+
"description": "tool-a2aagentsearch-description"
|
|
2202
2217
|
},
|
|
2203
2218
|
{
|
|
2204
2219
|
"name": "A2ASendMessage",
|
|
2205
|
-
"description": "tool-a2asendmessage-description"
|
|
2206
|
-
"deferLoading": true
|
|
2220
|
+
"description": "tool-a2asendmessage-description"
|
|
2207
2221
|
},
|
|
2208
2222
|
{
|
|
2209
2223
|
"name": "A2AGetTask",
|
|
2210
|
-
"description": "tool-a2agettask-description"
|
|
2211
|
-
"deferLoading": true
|
|
2224
|
+
"description": "tool-a2agettask-description"
|
|
2212
2225
|
},
|
|
2213
2226
|
{
|
|
2214
2227
|
"name": "A2ACancelTask",
|
|
2215
|
-
"description": "tool-a2acanceltask-description"
|
|
2216
|
-
"deferLoading": true
|
|
2228
|
+
"description": "tool-a2acanceltask-description"
|
|
2217
2229
|
}
|
|
2218
2230
|
],
|
|
2219
|
-
"commit": "
|
|
2220
|
-
"date": "2026-08-
|
|
2231
|
+
"commit": "d4a09ae479e13e4ffdc70d77b5f1bd584aeb6128",
|
|
2232
|
+
"date": "2026-08-28T08:06:20.642Z"
|
|
2221
2233
|
}
|
package/product.selfhosted.json
CHANGED
|
@@ -361,6 +361,6 @@
|
|
|
361
361
|
"ScheduledTasks": true,
|
|
362
362
|
"SkipToolCallSupportCheck": true
|
|
363
363
|
},
|
|
364
|
-
"commit": "
|
|
365
|
-
"date": "2026-08-
|
|
364
|
+
"commit": "d4a09ae479e13e4ffdc70d77b5f1bd584aeb6128",
|
|
365
|
+
"date": "2026-08-28T08:06:20.646Z"
|
|
366
366
|
}
|