@gaunt-sloth/agent 2.0.0-alpha.8 → 2.0.0-beta.0
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/README.md +73 -23
- package/cli-acp.js +21 -24
- package/dist/builtInToolsConfig.d.ts +17 -3
- package/dist/builtInToolsConfig.js +43 -41
- package/dist/builtInToolsConfig.js.map +1 -1
- package/dist/core/debugCapture.d.ts +7 -49
- package/dist/core/debugCapture.js +1 -1
- package/dist/core/debugCapture.js.map +1 -1
- package/dist/core/resolveAgentFactory.d.ts +10 -9
- package/dist/core/resolveAgentFactory.js +11 -12
- package/dist/core/resolveAgentFactory.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/OAuthClientProviderImpl.js.map +1 -1
- package/dist/mcp/tlsTrust.d.ts +61 -0
- package/dist/mcp/tlsTrust.js +109 -0
- package/dist/mcp/tlsTrust.js.map +1 -0
- package/dist/middleware/binaryContentInjectionMiddleware.d.ts +8 -1
- package/dist/middleware/binaryContentInjectionMiddleware.js +11 -2
- package/dist/middleware/binaryContentInjectionMiddleware.js.map +1 -1
- package/dist/middleware/frontendImageInjectionMiddleware.d.ts +107 -0
- package/dist/middleware/frontendImageInjectionMiddleware.js +167 -0
- package/dist/middleware/frontendImageInjectionMiddleware.js.map +1 -0
- package/dist/middleware/registry.js +49 -1
- package/dist/middleware/registry.js.map +1 -1
- package/dist/middleware/types.d.ts +16 -2
- package/dist/modules/a2a/A2AClientWrapper.d.ts +56 -1
- package/dist/modules/a2a/A2AClientWrapper.js +131 -31
- package/dist/modules/a2a/A2AClientWrapper.js.map +1 -1
- package/dist/modules/acp/acpAgentApp.d.ts +57 -0
- package/dist/modules/acp/acpAgentApp.js +378 -0
- package/dist/modules/acp/acpAgentApp.js.map +1 -0
- package/dist/modules/acp/acpAgentAppV1.d.ts +52 -0
- package/dist/modules/acp/acpAgentAppV1.js +307 -0
- package/dist/modules/acp/acpAgentAppV1.js.map +1 -0
- package/dist/modules/acp/acpCommon.d.ts +167 -0
- package/dist/modules/acp/acpCommon.js +282 -0
- package/dist/modules/acp/acpCommon.js.map +1 -0
- package/dist/modules/acp/acpPermissions.d.ts +65 -0
- package/dist/modules/acp/acpPermissions.js +162 -0
- package/dist/modules/acp/acpPermissions.js.map +1 -0
- package/dist/modules/acp/acpPermissionsV1.d.ts +45 -0
- package/dist/modules/acp/acpPermissionsV1.js +110 -0
- package/dist/modules/acp/acpPermissionsV1.js.map +1 -0
- package/dist/modules/acp/acpRouter.d.ts +41 -0
- package/dist/modules/acp/acpRouter.js +48 -0
- package/dist/modules/acp/acpRouter.js.map +1 -0
- package/dist/modules/acp/acpStdio.d.ts +47 -0
- package/dist/modules/acp/acpStdio.js +71 -0
- package/dist/modules/acp/acpStdio.js.map +1 -0
- package/dist/modules/acp/acpToolCalls.d.ts +93 -0
- package/dist/modules/acp/acpToolCalls.js +193 -0
- package/dist/modules/acp/acpToolCalls.js.map +1 -0
- package/dist/modules/acp/acpUpdates.d.ts +48 -0
- package/dist/modules/acp/acpUpdates.js +142 -0
- package/dist/modules/acp/acpUpdates.js.map +1 -0
- package/dist/modules/acp/acpUpdatesV1.d.ts +61 -0
- package/dist/modules/acp/acpUpdatesV1.js +162 -0
- package/dist/modules/acp/acpUpdatesV1.js.map +1 -0
- package/dist/modules/apiAgUiModule.d.ts +87 -0
- package/dist/modules/apiAgUiModule.js +193 -28
- package/dist/modules/apiAgUiModule.js.map +1 -1
- package/dist/modules/interactiveSessionModule.js +497 -51
- package/dist/modules/interactiveSessionModule.js.map +1 -1
- package/dist/modules/slashCommands.d.ts +582 -0
- package/dist/modules/slashCommands.js +1001 -0
- package/dist/modules/slashCommands.js.map +1 -0
- package/dist/resolvers.js +119 -9
- package/dist/resolvers.js.map +1 -1
- package/dist/tools/GthCustomToolkit.js +95 -11
- package/dist/tools/GthCustomToolkit.js.map +1 -1
- package/dist/tools/GthDevToolkit.d.ts +8 -5
- package/dist/tools/GthDevToolkit.js +89 -43
- package/dist/tools/GthDevToolkit.js.map +1 -1
- package/dist/tools/GthFileSystemToolkit.d.ts +63 -0
- package/dist/tools/GthFileSystemToolkit.js +506 -105
- package/dist/tools/GthFileSystemToolkit.js.map +1 -1
- package/dist/tools/McpResourceTool.d.ts +31 -0
- package/dist/tools/McpResourceTool.js +106 -0
- package/dist/tools/McpResourceTool.js.map +1 -0
- package/dist/tools/gthChecklistTool.d.ts +30 -0
- package/dist/tools/gthChecklistTool.js +80 -0
- package/dist/tools/gthChecklistTool.js.map +1 -0
- package/dist/tools/gthGrepTool.d.ts +54 -0
- package/dist/tools/gthGrepTool.js +482 -0
- package/dist/tools/gthGrepTool.js.map +1 -0
- package/dist/tools/shell/env.js +1 -1
- package/dist/tools/shell/env.js.map +1 -1
- package/dist/tools/shell/outputBuffer.js.map +1 -1
- package/dist/tools/shell/workDir.d.ts +1 -10
- package/dist/tools/shell/workDir.js +10 -36
- package/dist/tools/shell/workDir.js.map +1 -1
- package/dist/utils/mcpAuthError.d.ts +36 -0
- package/dist/utils/mcpAuthError.js +89 -0
- package/dist/utils/mcpAuthError.js.map +1 -0
- package/dist/utils/mcpUtils.js +16 -0
- package/dist/utils/mcpUtils.js.map +1 -1
- package/package.json +13 -13
- package/dist/core/GthDeepAgent.d.ts +0 -181
- package/dist/core/GthDeepAgent.js +0 -682
- package/dist/core/GthDeepAgent.js.map +0 -1
- package/dist/core/deepAgentPermissions.d.ts +0 -115
- package/dist/core/deepAgentPermissions.js +0 -282
- package/dist/core/deepAgentPermissions.js.map +0 -1
- package/dist/core/gthAcpServer.d.ts +0 -27
- package/dist/core/gthAcpServer.js +0 -82
- package/dist/core/gthAcpServer.js.map +0 -1
- package/dist/core/gthDeepAgentFactory.d.ts +0 -8
- package/dist/core/gthDeepAgentFactory.js +0 -9
- package/dist/core/gthDeepAgentFactory.js.map +0 -1
- package/dist/modules/acpModule.d.ts +0 -40
- package/dist/modules/acpModule.js +0 -78
- package/dist/modules/acpModule.js.map +0 -1
- package/dist/tools/shell/allowlist.d.ts +0 -11
- package/dist/tools/shell/allowlist.js +0 -12
- package/dist/tools/shell/allowlist.js.map +0 -1
- package/dist/tools/shell/arity.d.ts +0 -11
- package/dist/tools/shell/arity.js +0 -12
- package/dist/tools/shell/arity.js.map +0 -1
- package/dist/tools/shell/hardline.d.ts +0 -15
- package/dist/tools/shell/hardline.js +0 -88
- package/dist/tools/shell/hardline.js.map +0 -1
- package/dist/tools/shell/normalize.d.ts +0 -10
- package/dist/tools/shell/normalize.js +0 -11
- package/dist/tools/shell/normalize.js.map +0 -1
|
@@ -10,7 +10,7 @@ import { AgentMiddleware } from 'langchain';
|
|
|
10
10
|
/**
|
|
11
11
|
* Predefined middleware types that can be configured via JSON config.
|
|
12
12
|
*/
|
|
13
|
-
export type PredefinedMiddlewareName = 'anthropic-prompt-caching' | 'summarization' | 'binary-content-injection';
|
|
13
|
+
export type PredefinedMiddlewareName = 'anthropic-prompt-caching' | 'summarization' | 'binary-content-injection' | 'frontend-image-injection';
|
|
14
14
|
/**
|
|
15
15
|
* Configuration for Anthropic prompt caching middleware.
|
|
16
16
|
*/
|
|
@@ -70,6 +70,18 @@ export interface ImageFormatTransformConfig {
|
|
|
70
70
|
*/
|
|
71
71
|
export interface BinaryContentInjectionConfig {
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Configuration for frontend image injection middleware (RC-22).
|
|
75
|
+
* Converts a frontend capture tool's `{mimeType,data}` tool result into a vision HumanMessage the
|
|
76
|
+
* model can see. Opt-in — resolved only when named in `config.middleware`.
|
|
77
|
+
*/
|
|
78
|
+
export interface FrontendImageInjectionConfig {
|
|
79
|
+
/**
|
|
80
|
+
* Name of the frontend capture tool whose result is converted into a vision message.
|
|
81
|
+
* Defaults to `capture_image`.
|
|
82
|
+
*/
|
|
83
|
+
toolName?: string;
|
|
84
|
+
}
|
|
73
85
|
/**
|
|
74
86
|
* Union type of all predefined middleware configurations.
|
|
75
87
|
*/
|
|
@@ -79,7 +91,9 @@ export type PredefinedMiddlewareConfig = ({
|
|
|
79
91
|
name: 'summarization';
|
|
80
92
|
} & SummarizationConfig) | ({
|
|
81
93
|
name: 'binary-content-injection';
|
|
82
|
-
} & BinaryContentInjectionConfig)
|
|
94
|
+
} & BinaryContentInjectionConfig) | ({
|
|
95
|
+
name: 'frontend-image-injection';
|
|
96
|
+
} & FrontendImageInjectionConfig);
|
|
83
97
|
/**
|
|
84
98
|
* Middleware configuration that can be specified in JSON or JS config.
|
|
85
99
|
* - String: Name of predefined middleware with default settings
|
|
@@ -5,12 +5,33 @@ export interface A2AClientConfig {
|
|
|
5
5
|
/** URL endpoint for the A2A agent */
|
|
6
6
|
agentUrl: string;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* A single A2A turn's result with its conversation-continuity handles (BATCH-14). Unlike the
|
|
10
|
+
* text-only {@link A2AClientWrapper.sendMessage}, this carries the `contextId`/`taskId` the server
|
|
11
|
+
* returned so a caller can thread them into follow-up turns (an ADK agent keeps conversational
|
|
12
|
+
* context by `contextId`). Both are optional because a response may be a bare `Message` (which
|
|
13
|
+
* carries them only optionally) or a `Task` (which always carries `contextId`, and `id` as the
|
|
14
|
+
* taskId).
|
|
15
|
+
*/
|
|
16
|
+
export interface A2AMessageResult {
|
|
17
|
+
/** The agent's answer text, extracted from the response's text parts. */
|
|
18
|
+
text: string;
|
|
19
|
+
/** The A2A context id to thread into subsequent turns for conversational continuity. */
|
|
20
|
+
contextId?: string;
|
|
21
|
+
/** The A2A task id, when the response was a Task (`Task.id`) or a task-scoped Message. */
|
|
22
|
+
taskId?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Optional conversation-continuity handles to attach to an outgoing A2A message (BATCH-14). */
|
|
25
|
+
export interface A2ASendContext {
|
|
26
|
+
contextId?: string;
|
|
27
|
+
taskId?: string;
|
|
28
|
+
}
|
|
8
29
|
/**
|
|
9
30
|
* Wrapper around the A2A SDK client for communicating with external agents.
|
|
10
31
|
* @experimental
|
|
11
32
|
*/
|
|
12
33
|
export declare class A2AClientWrapper {
|
|
13
|
-
private
|
|
34
|
+
private clientPromise;
|
|
14
35
|
private config;
|
|
15
36
|
constructor(config: A2AClientConfig);
|
|
16
37
|
/**
|
|
@@ -19,4 +40,38 @@ export declare class A2AClientWrapper {
|
|
|
19
40
|
* @returns The agent's response as a string
|
|
20
41
|
*/
|
|
21
42
|
sendMessage(messageText: string): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* BATCH-14 — send one message and return its text ALONG WITH the A2A `contextId`/`taskId` needed to
|
|
45
|
+
* thread a multi-turn conversation. Reads the real `@a2a-js/sdk` result shapes rather than a
|
|
46
|
+
* loose envelope:
|
|
47
|
+
*
|
|
48
|
+
* - A **Message** result carries its text in `parts` and `contextId`/`taskId` directly (both
|
|
49
|
+
* optional, and empty-string when absent in the v1.0 proto model).
|
|
50
|
+
* - A **Task** result — what Google ADK agents commonly return — carries text in
|
|
51
|
+
* `status.message.parts` (falling back to the last artifact's parts), `contextId` (required), and
|
|
52
|
+
* the taskId as `Task.id`.
|
|
53
|
+
*
|
|
54
|
+
* `context.contextId`/`taskId` are attached to the OUTGOING message only when defined, so a
|
|
55
|
+
* first-turn send stays byte-identical to a contextless one.
|
|
56
|
+
*
|
|
57
|
+
* @param messageText - The message to send to the agent.
|
|
58
|
+
* @param context - Optional context/task ids to continue an existing conversation.
|
|
59
|
+
* @returns The agent's text answer plus the context/task ids from its response.
|
|
60
|
+
*/
|
|
61
|
+
sendMessageWithContext(messageText: string, context?: A2ASendContext): Promise<A2AMessageResult>;
|
|
62
|
+
/** Build the outgoing `SendMessageRequest`. The v1.0 data model is generated from the A2A
|
|
63
|
+
* protobufs, so every field is required rather than optional: `role` is the numeric {@link Role}
|
|
64
|
+
* enum (not `'user'`), a text part is `{ content: { $case: 'text', value } }` (not
|
|
65
|
+
* `{ kind: 'text', text }`), and the continuity handles are empty strings rather than absent
|
|
66
|
+
* when there is no conversation to continue. */
|
|
67
|
+
private static buildSendRequest;
|
|
68
|
+
/** Normalize a `Message | Task` A2A result into text + continuity handles (BATCH-14). Static +
|
|
69
|
+
* tolerant of missing optional fields so it can be reused and unit-tested directly. */
|
|
70
|
+
private static extractResult;
|
|
71
|
+
/** v1.0 dropped the `kind` discriminator that told a Message from a Task, so discriminate
|
|
72
|
+
* structurally: `messageId` is required on every Message and never present on a Task. */
|
|
73
|
+
private static isTask;
|
|
74
|
+
/** Concatenate the text of every {@link https://github.com/a2aproject/A2A TextPart} in `parts`
|
|
75
|
+
* (ignoring file/data parts), or `''` when there is no text part. */
|
|
76
|
+
private static extractText;
|
|
22
77
|
}
|
|
@@ -3,20 +3,48 @@
|
|
|
3
3
|
* Wrapper for A2A (Agent-to-Agent) protocol client.
|
|
4
4
|
* @experimental A2A support is experimental and may change.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { ClientFactory, ClientFactoryOptions, DefaultAgentCardResolver, JsonRpcTransportFactory, RestTransportFactory, } from '@a2a-js/sdk/client';
|
|
7
|
+
import { Role } from '@a2a-js/sdk';
|
|
7
8
|
import { debugLog, debugLogError } from '@gaunt-sloth/core/utils/debugUtils.js';
|
|
8
9
|
import { v4 as uuidv4 } from 'uuid';
|
|
10
|
+
/** The A2A spec's well-known agent-card path. The SDK's resolver needs the full CARD url, whereas
|
|
11
|
+
* {@link A2AClientConfig.agentUrl} is the agent's BASE url — so we append this ourselves and hand
|
|
12
|
+
* the resolver an empty relative path, keeping the fetched card URL byte-identical to what the
|
|
13
|
+
* pre-1.0 `A2AClient` string constructor produced. (Letting the resolver append its own default
|
|
14
|
+
* would resolve the path RELATIVE to the base url, silently dropping a base path segment: for
|
|
15
|
+
* `http://host/a2a` it would fetch `http://host/.well-known/agent-card.json`.) */
|
|
16
|
+
const AGENT_CARD_PATH = '.well-known/agent-card.json';
|
|
9
17
|
/**
|
|
10
18
|
* Wrapper around the A2A SDK client for communicating with external agents.
|
|
11
19
|
* @experimental
|
|
12
20
|
*/
|
|
13
21
|
export class A2AClientWrapper {
|
|
14
|
-
|
|
22
|
+
clientPromise;
|
|
15
23
|
config;
|
|
16
24
|
constructor(config) {
|
|
17
25
|
this.config = config;
|
|
18
|
-
//
|
|
19
|
-
|
|
26
|
+
// `@a2a-js/sdk` 1.0 removed the `A2AClient` class outright; a client is now built by a
|
|
27
|
+
// `ClientFactory` from the agent card. `legacyCompat` is enabled on the card resolver AND on
|
|
28
|
+
// every transport factory so we keep talking to agents still on protocol v0.3 — which is what
|
|
29
|
+
// Google ADK agents (our only real A2A peer today, see adk-eval-it/) currently speak. With it
|
|
30
|
+
// on, the resolver detects a v0.3-shaped card, translates it to the v1.0 model and stamps each
|
|
31
|
+
// synthesized interface `protocolVersion: '0.3'`, and the matching factory then instantiates
|
|
32
|
+
// the v0.3 wire transport. A v1.0 agent is served by the native transports unchanged, so this
|
|
33
|
+
// wrapper speaks to both.
|
|
34
|
+
const factory = new ClientFactory(ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
|
|
35
|
+
cardResolver: new DefaultAgentCardResolver({ legacyCompat: { enabled: true } }),
|
|
36
|
+
transports: [
|
|
37
|
+
new JsonRpcTransportFactory({ legacyCompat: { enabled: true } }),
|
|
38
|
+
new RestTransportFactory({ legacyCompat: { enabled: true } }),
|
|
39
|
+
],
|
|
40
|
+
}));
|
|
41
|
+
// Client construction is async (it fetches the card), so we hold the promise and await it per
|
|
42
|
+
// send. Empty path = "the base url IS the card url" — see AGENT_CARD_PATH.
|
|
43
|
+
const cardUrl = `${config.agentUrl.replace(/\/+$/, '')}/${AGENT_CARD_PATH}`;
|
|
44
|
+
this.clientPromise = factory.createFromUrl(cardUrl, '');
|
|
45
|
+
// Guard against an unhandled rejection if the wrapper is constructed but never used; real
|
|
46
|
+
// callers still observe the rejection when they await `clientPromise` inside a send method.
|
|
47
|
+
void this.clientPromise.catch(() => undefined);
|
|
20
48
|
}
|
|
21
49
|
/**
|
|
22
50
|
* Sends a message to the A2A agent and returns the response.
|
|
@@ -26,38 +54,110 @@ export class A2AClientWrapper {
|
|
|
26
54
|
async sendMessage(messageText) {
|
|
27
55
|
debugLog(`Sending message to A2A agent ${this.config.agentId} at ${this.config.agentUrl}: ${messageText}`);
|
|
28
56
|
try {
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
messageId: messageId,
|
|
34
|
-
role: 'user',
|
|
35
|
-
parts: [{ kind: 'text', text: messageText }],
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
debugLog(`Received response from A2A agent: ${JSON.stringify(response)}`);
|
|
39
|
-
// Check for error response
|
|
40
|
-
if ('error' in response) {
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
throw new Error(`A2A Error: ${JSON.stringify(response.error)}`);
|
|
43
|
-
}
|
|
44
|
-
// Extract text from response
|
|
45
|
-
// The result is likely a TaskStatus which contains a message
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
-
const result = response.result;
|
|
48
|
-
if (result.message && result.message.parts && result.message.parts.length > 0) {
|
|
49
|
-
// Assuming the first part is text for now
|
|
50
|
-
return result.message.parts[0].text || JSON.stringify(result.message.parts);
|
|
51
|
-
}
|
|
52
|
-
else if (result.state) {
|
|
53
|
-
return `Task state: ${result.state}`;
|
|
54
|
-
}
|
|
55
|
-
return JSON.stringify(result);
|
|
57
|
+
const client = await this.clientPromise;
|
|
58
|
+
const result = await client.sendMessage(A2AClientWrapper.buildSendRequest(messageText, uuidv4()));
|
|
59
|
+
debugLog(`Received response from A2A agent: ${JSON.stringify(result)}`);
|
|
60
|
+
return A2AClientWrapper.extractResult(result).text;
|
|
56
61
|
}
|
|
57
62
|
catch (error) {
|
|
58
63
|
debugLogError('Error sending message to A2A agent', error);
|
|
59
64
|
throw error;
|
|
60
65
|
}
|
|
61
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* BATCH-14 — send one message and return its text ALONG WITH the A2A `contextId`/`taskId` needed to
|
|
69
|
+
* thread a multi-turn conversation. Reads the real `@a2a-js/sdk` result shapes rather than a
|
|
70
|
+
* loose envelope:
|
|
71
|
+
*
|
|
72
|
+
* - A **Message** result carries its text in `parts` and `contextId`/`taskId` directly (both
|
|
73
|
+
* optional, and empty-string when absent in the v1.0 proto model).
|
|
74
|
+
* - A **Task** result — what Google ADK agents commonly return — carries text in
|
|
75
|
+
* `status.message.parts` (falling back to the last artifact's parts), `contextId` (required), and
|
|
76
|
+
* the taskId as `Task.id`.
|
|
77
|
+
*
|
|
78
|
+
* `context.contextId`/`taskId` are attached to the OUTGOING message only when defined, so a
|
|
79
|
+
* first-turn send stays byte-identical to a contextless one.
|
|
80
|
+
*
|
|
81
|
+
* @param messageText - The message to send to the agent.
|
|
82
|
+
* @param context - Optional context/task ids to continue an existing conversation.
|
|
83
|
+
* @returns The agent's text answer plus the context/task ids from its response.
|
|
84
|
+
*/
|
|
85
|
+
async sendMessageWithContext(messageText, context) {
|
|
86
|
+
debugLog(`Sending message (with context) to A2A agent ${this.config.agentId} at ` +
|
|
87
|
+
`${this.config.agentUrl}: ${messageText}` +
|
|
88
|
+
(context?.contextId ? ` [contextId=${context.contextId}]` : ''));
|
|
89
|
+
try {
|
|
90
|
+
const client = await this.clientPromise;
|
|
91
|
+
const result = await client.sendMessage(A2AClientWrapper.buildSendRequest(messageText, uuidv4(), context));
|
|
92
|
+
debugLog(`Received response from A2A agent: ${JSON.stringify(result)}`);
|
|
93
|
+
return A2AClientWrapper.extractResult(result);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
debugLogError('Error sending message (with context) to A2A agent', error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Build the outgoing `SendMessageRequest`. The v1.0 data model is generated from the A2A
|
|
101
|
+
* protobufs, so every field is required rather than optional: `role` is the numeric {@link Role}
|
|
102
|
+
* enum (not `'user'`), a text part is `{ content: { $case: 'text', value } }` (not
|
|
103
|
+
* `{ kind: 'text', text }`), and the continuity handles are empty strings rather than absent
|
|
104
|
+
* when there is no conversation to continue. */
|
|
105
|
+
static buildSendRequest(messageText, messageId, context) {
|
|
106
|
+
const message = {
|
|
107
|
+
messageId,
|
|
108
|
+
contextId: context?.contextId ?? '',
|
|
109
|
+
taskId: context?.taskId ?? '',
|
|
110
|
+
role: Role.ROLE_USER,
|
|
111
|
+
parts: [
|
|
112
|
+
{
|
|
113
|
+
content: { $case: 'text', value: messageText },
|
|
114
|
+
metadata: undefined,
|
|
115
|
+
filename: '',
|
|
116
|
+
mediaType: 'text/plain',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
metadata: undefined,
|
|
120
|
+
extensions: [],
|
|
121
|
+
referenceTaskIds: [],
|
|
122
|
+
};
|
|
123
|
+
return { tenant: '', message, configuration: undefined, metadata: undefined };
|
|
124
|
+
}
|
|
125
|
+
/** Normalize a `Message | Task` A2A result into text + continuity handles (BATCH-14). Static +
|
|
126
|
+
* tolerant of missing optional fields so it can be reused and unit-tested directly. */
|
|
127
|
+
static extractResult(result) {
|
|
128
|
+
if (A2AClientWrapper.isTask(result)) {
|
|
129
|
+
const statusParts = result.status?.message?.parts;
|
|
130
|
+
const artifactParts = result.artifacts?.[result.artifacts.length - 1]?.parts;
|
|
131
|
+
const text = A2AClientWrapper.extractText(statusParts ?? artifactParts) || JSON.stringify(result);
|
|
132
|
+
return {
|
|
133
|
+
text,
|
|
134
|
+
contextId: result.contextId || undefined,
|
|
135
|
+
taskId: result.id || undefined,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const text = A2AClientWrapper.extractText(result?.parts) || JSON.stringify(result);
|
|
139
|
+
// The proto model uses '' (not undefined) for an absent id; normalize back to undefined so a
|
|
140
|
+
// caller can't thread an empty handle into the next turn.
|
|
141
|
+
return {
|
|
142
|
+
text,
|
|
143
|
+
contextId: result?.contextId || undefined,
|
|
144
|
+
taskId: result?.taskId || undefined,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** v1.0 dropped the `kind` discriminator that told a Message from a Task, so discriminate
|
|
148
|
+
* structurally: `messageId` is required on every Message and never present on a Task. */
|
|
149
|
+
static isTask(result) {
|
|
150
|
+
return typeof result?.messageId !== 'string';
|
|
151
|
+
}
|
|
152
|
+
/** Concatenate the text of every {@link https://github.com/a2aproject/A2A TextPart} in `parts`
|
|
153
|
+
* (ignoring file/data parts), or `''` when there is no text part. */
|
|
154
|
+
static extractText(parts) {
|
|
155
|
+
if (!parts || parts.length === 0)
|
|
156
|
+
return '';
|
|
157
|
+
return parts
|
|
158
|
+
.filter((part) => part?.content?.$case === 'text')
|
|
159
|
+
.map((part) => part.content?.value)
|
|
160
|
+
.join('\n');
|
|
161
|
+
}
|
|
62
162
|
}
|
|
63
163
|
//# sourceMappingURL=A2AClientWrapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AClientWrapper.js","sourceRoot":"","sources":["../../../src/modules/a2a/A2AClientWrapper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"A2AClientWrapper.js","sourceRoot":"","sources":["../../../src/modules/a2a/A2AClientWrapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Gaunt Sloth's ACP (Agent Client Protocol) **v2** agent, built on the official SDK's
|
|
4
|
+
* `experimental/v2` surface.
|
|
5
|
+
*
|
|
6
|
+
* ## One of two dialects
|
|
7
|
+
*
|
|
8
|
+
* v1 is ACP's stable protocol and v2 is a draft, and the agent serves **both**: `acpRouter.ts`
|
|
9
|
+
* reads the `protocolVersion` on the first `initialize` and hands the connection to the matching
|
|
10
|
+
* app, so a host gets the dialect it asked for. This file is the v2 half; `acpAgentAppV1.ts` is
|
|
11
|
+
* the v1 half. Serving only v2 would cut the surface off from every shipping editor that speaks
|
|
12
|
+
* ACP today — measured against Zed 1.15.0 stable, which sends `protocolVersion: 1` as a literal
|
|
13
|
+
* with no setting to change it.
|
|
14
|
+
*
|
|
15
|
+
* ## Shape
|
|
16
|
+
*
|
|
17
|
+
* v2's agent side is a fluent app builder: `acp.agent()` returns an {@link AgentApp} on which
|
|
18
|
+
* typed handlers are registered by ACP method name, and `connect()` serves a client — either over
|
|
19
|
+
* a transport stream or, for tests, directly against a `ClientApp`. The SDK's v1 entry point offers
|
|
20
|
+
* the same builder, which is why the two dialects read alike even though their handlers differ.
|
|
21
|
+
*
|
|
22
|
+
* ## The prompt lifecycle is the part that looks wrong until you read the spec
|
|
23
|
+
*
|
|
24
|
+
* This is also the sharpest difference from v1, which answers the prompt request with the stop
|
|
25
|
+
* reason itself. In v2 `session/prompt` returns `{}` **as soon as the prompt is accepted** — not
|
|
26
|
+
* when the turn finishes.
|
|
27
|
+
* Progress, tool calls and the final stop reason all arrive as `session/update` notifications, and
|
|
28
|
+
* the turn is over when an idle `state_update` carrying a stop reason lands. That is what makes
|
|
29
|
+
* cancellation expressible at all: a cancelled turn reports `cancelled` on a `state_update`, while
|
|
30
|
+
* the prompt request it belongs to was answered long before. A client that waited on the prompt
|
|
31
|
+
* response for the result would hang through every tool call.
|
|
32
|
+
*
|
|
33
|
+
* ## Why a runner and not an agent
|
|
34
|
+
*
|
|
35
|
+
* Each session drives a {@link GthAgentRunner}, not a bare agent. The runner is where the
|
|
36
|
+
* tool-approval gate lives, so this is also the surface where `session/request_permission` is
|
|
37
|
+
* wired — see `acpPermissions.ts` for why that is not optional.
|
|
38
|
+
*
|
|
39
|
+
* ## One workspace per process
|
|
40
|
+
*
|
|
41
|
+
* Config discovery, the filesystem tools' allowed root, grep and the shell tool ALL resolve
|
|
42
|
+
* through `getCurrentWorkDir()`, a process-global. So the workspace is bound by the first
|
|
43
|
+
* `session/new` and a later `session/new` naming a different `cwd` is refused rather than served
|
|
44
|
+
* from the wrong root. An editor spawns one agent process per project, which is exactly the shape
|
|
45
|
+
* this serves; the alternative — letting the second session silently re-root the first session's
|
|
46
|
+
* tools — is the kind of quiet wrongness that is worse than an error a host can report.
|
|
47
|
+
*/
|
|
48
|
+
import * as acp from '@agentclientprotocol/sdk/experimental/v2';
|
|
49
|
+
import { ACP_AGENT_NAME, ACP_AGENT_TITLE, announceAcpStart, isSameWorkspace } from '#src/modules/acp/acpCommon.js';
|
|
50
|
+
import type { AcpAgentAppOptions } from '#src/modules/acp/acpCommon.js';
|
|
51
|
+
export { ACP_AGENT_NAME, ACP_AGENT_TITLE, announceAcpStart, isSameWorkspace };
|
|
52
|
+
export type { AcpAgentAppOptions };
|
|
53
|
+
/**
|
|
54
|
+
* Builds the ACP v2 agent app. Register-and-return only: nothing is connected and no config is
|
|
55
|
+
* read until a client connects and creates a session.
|
|
56
|
+
*/
|
|
57
|
+
export declare function createAcpAgentApp(options?: AcpAgentAppOptions): acp.AgentApp;
|