@hanphone/dsh-a2a 0.2.0 → 0.3.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 +101 -106
- package/README.zh.md +50 -74
- package/cordis.patch.yml +4 -7
- package/lib/client.js +217 -73
- package/lib/index.js +1258 -867
- package/lib/types/api.d.ts +40 -23
- package/lib/types/api.d.ts.map +1 -1
- package/lib/types/api.js +58 -23
- package/lib/types/api.js.map +1 -1
- package/lib/types/client/index.d.ts +50 -19
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/commands.d.ts +4 -2
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +59 -46
- package/lib/types/commands.js.map +1 -1
- package/lib/types/events.d.ts +32 -9
- package/lib/types/events.d.ts.map +1 -1
- package/lib/types/index.d.ts +21 -32
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +173 -272
- package/lib/types/index.js.map +1 -1
- package/lib/types/outbound/calls.d.ts.map +1 -1
- package/lib/types/outbound/calls.js +8 -6
- package/lib/types/outbound/calls.js.map +1 -1
- package/lib/types/protocol.d.ts +174 -109
- package/lib/types/protocol.d.ts.map +1 -1
- package/lib/types/protocol.js +58 -34
- package/lib/types/protocol.js.map +1 -1
- package/lib/types/server/a2a-server.d.ts +4 -0
- package/lib/types/server/a2a-server.d.ts.map +1 -1
- package/lib/types/server/a2a-server.js +6 -2
- package/lib/types/server/a2a-server.js.map +1 -1
- package/lib/types/server/card.d.ts +6 -30
- package/lib/types/server/card.d.ts.map +1 -1
- package/lib/types/server/card.js +13 -49
- package/lib/types/server/card.js.map +1 -1
- package/lib/types/server/exec/agent-runtime.d.ts +3 -0
- package/lib/types/server/exec/agent-runtime.d.ts.map +1 -1
- package/lib/types/server/exec/agent-runtime.js +2 -1
- package/lib/types/server/exec/agent-runtime.js.map +1 -1
- package/lib/types/server/routes.d.ts +5 -1
- package/lib/types/server/routes.d.ts.map +1 -1
- package/lib/types/server/routes.js +9 -5
- package/lib/types/server/routes.js.map +1 -1
- package/lib/types/server/store.d.ts +35 -1
- package/lib/types/server/store.d.ts.map +1 -1
- package/lib/types/server/store.js +10 -0
- package/lib/types/server/store.js.map +1 -1
- package/lib/types/servers/inbound-manager.d.ts +131 -0
- package/lib/types/servers/inbound-manager.d.ts.map +1 -0
- package/lib/types/servers/inbound-manager.js +312 -0
- package/lib/types/servers/inbound-manager.js.map +1 -0
- package/lib/types/servers/outbound-manager.d.ts +129 -0
- package/lib/types/servers/outbound-manager.d.ts.map +1 -0
- package/lib/types/servers/outbound-manager.js +238 -0
- package/lib/types/servers/outbound-manager.js.map +1 -0
- package/lib/types/service.d.ts +106 -40
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +36 -27
- package/lib/types/service.js.map +1 -1
- package/package.json +2 -2
- package/src/api.ts +67 -53
- package/src/client/index.ts +309 -148
- package/src/commands.ts +59 -43
- package/src/events.ts +14 -7
- package/src/index.ts +199 -310
- package/src/outbound/calls.ts +8 -6
- package/src/protocol.ts +204 -95
- package/src/server/a2a-server.ts +9 -2
- package/src/server/card.ts +13 -62
- package/src/server/exec/agent-runtime.ts +5 -2
- package/src/server/routes.ts +6 -4
- package/src/server/store.ts +45 -4
- package/src/servers/inbound-manager.ts +385 -0
- package/src/servers/outbound-manager.ts +289 -0
- package/src/service.ts +125 -40
- package/lib/tsconfig.client.tsbuildinfo +0 -1
- package/lib/tsconfig.tsbuildinfo +0 -1
package/src/protocol.ts
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent2Agent (A2A) Protocol v1.0 — types,
|
|
3
|
-
* error codes
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* truth
|
|
2
|
+
* Agent2Agent (A2A) Protocol v1.0.1 — types, JSON-RPC methods, task states,
|
|
3
|
+
* error codes and JSON serialization, transcribed from the official
|
|
4
|
+
* `a2aproject/A2A` specification (Apache-2.0), cross-checked against
|
|
5
|
+
* `specification/a2a.proto` and `docs/specification.md` (JSON-RPC binding,
|
|
6
|
+
* ADR-001 ProtoJSON). This file is the plugin's single source of protocol
|
|
7
|
+
* truth for the JSON-RPC over HTTP binding. Other bindings (gRPC, REST) are
|
|
8
|
+
* not provided.
|
|
8
9
|
* @module dsh-a2a/protocol
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
|
-
/** Task lifecycle states (spec `TaskState
|
|
12
|
+
/** Task lifecycle states (spec `TaskState`; JSON = SCREAMING_SNAKE_CASE per ADR-001). */
|
|
12
13
|
export enum TaskState {
|
|
13
|
-
SUBMITTED = '
|
|
14
|
-
WORKING = '
|
|
15
|
-
INPUT_REQUIRED = '
|
|
16
|
-
AUTH_REQUIRED = '
|
|
17
|
-
COMPLETED = '
|
|
18
|
-
FAILED = '
|
|
19
|
-
CANCELED = '
|
|
20
|
-
REJECTED = '
|
|
14
|
+
SUBMITTED = 'TASK_STATE_SUBMITTED',
|
|
15
|
+
WORKING = 'TASK_STATE_WORKING',
|
|
16
|
+
INPUT_REQUIRED = 'TASK_STATE_INPUT_REQUIRED',
|
|
17
|
+
AUTH_REQUIRED = 'TASK_STATE_AUTH_REQUIRED',
|
|
18
|
+
COMPLETED = 'TASK_STATE_COMPLETED',
|
|
19
|
+
FAILED = 'TASK_STATE_FAILED',
|
|
20
|
+
CANCELED = 'TASK_STATE_CANCELED',
|
|
21
|
+
REJECTED = 'TASK_STATE_REJECTED',
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/** States that settle a task; a settled task no longer transitions. */
|
|
@@ -28,72 +29,150 @@ export const TERMINAL_STATES: ReadonlySet<TaskState> = new Set([
|
|
|
28
29
|
TaskState.REJECTED,
|
|
29
30
|
])
|
|
30
31
|
|
|
32
|
+
/** Interrupted (non-terminal, still owed) states. */
|
|
33
|
+
export const INTERRUPTED_STATES: ReadonlySet<TaskState> = new Set([
|
|
34
|
+
TaskState.INPUT_REQUIRED,
|
|
35
|
+
TaskState.AUTH_REQUIRED,
|
|
36
|
+
])
|
|
37
|
+
|
|
31
38
|
/** @returns whether the state settles the task. */
|
|
32
39
|
export function isTerminal(state: TaskState): boolean {
|
|
33
40
|
return TERMINAL_STATES.has(state)
|
|
34
41
|
}
|
|
35
42
|
|
|
36
|
-
/** Message sender role (spec `Role`). */
|
|
43
|
+
/** Message sender role (spec `Role`; JSON = `ROLE_*` per ADR-001). */
|
|
37
44
|
export enum Role {
|
|
38
|
-
USER = '
|
|
39
|
-
AGENT = '
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
USER = 'ROLE_USER',
|
|
46
|
+
AGENT = 'ROLE_AGENT',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Base properties common to every message part. */
|
|
50
|
+
export interface PartBase {
|
|
51
|
+
readonly metadata?: Record<string, unknown> | null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Conveys plain textual content. */
|
|
55
|
+
export interface TextPart extends PartBase {
|
|
56
|
+
readonly text: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Conveys a file: raw bytes (base64 in JSON), a URL, or both. */
|
|
60
|
+
export interface FilePart extends PartBase {
|
|
61
|
+
readonly raw?: string
|
|
62
|
+
readonly url?: string
|
|
63
|
+
readonly filename?: string
|
|
64
|
+
readonly mediaType?: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Conveys structured data (a JSON value). */
|
|
68
|
+
export interface DataPart extends PartBase {
|
|
69
|
+
readonly data: unknown
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A container for a section of communication content (spec `Part`). */
|
|
73
|
+
export type Part = TextPart | FilePart | DataPart
|
|
74
|
+
|
|
75
|
+
/** A message in a task's conversation (spec `Message`). */
|
|
57
76
|
export interface Message {
|
|
77
|
+
/** Unique id created by the message creator. */
|
|
58
78
|
readonly messageId: string
|
|
59
|
-
|
|
79
|
+
/** Associates this message with a context. */
|
|
60
80
|
readonly contextId?: string
|
|
61
|
-
/**
|
|
81
|
+
/** Associates this message with a task. */
|
|
62
82
|
readonly taskId?: string
|
|
83
|
+
readonly role: Role
|
|
63
84
|
readonly parts: readonly Part[]
|
|
64
|
-
readonly metadata?: Record<string, unknown>
|
|
85
|
+
readonly metadata?: Record<string, unknown> | null
|
|
86
|
+
/** Extension URIs present or contributed. */
|
|
87
|
+
readonly extensions?: readonly string[]
|
|
88
|
+
/** Task ids this message references for additional context. */
|
|
89
|
+
readonly referenceTaskIds?: readonly string[]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Configuration of a send-message request (spec `SendMessageConfiguration`). */
|
|
93
|
+
export interface SendMessageConfiguration {
|
|
94
|
+
/** Media types the client accepts for response parts. */
|
|
95
|
+
readonly acceptedOutputModes?: readonly string[]
|
|
96
|
+
/** Push notification config; task id empty when sent with SendMessage. */
|
|
97
|
+
readonly taskPushNotificationConfig?: TaskPushNotificationConfig
|
|
98
|
+
/** Max history messages to return; 0 = none; unset = no limit. */
|
|
99
|
+
readonly historyLength?: number
|
|
100
|
+
/** true = return immediately after task creation; false (default) = block to terminal/interrupted. */
|
|
101
|
+
readonly returnImmediately?: boolean
|
|
65
102
|
}
|
|
66
103
|
|
|
67
|
-
/**
|
|
104
|
+
/** A container for the status of a task. */
|
|
68
105
|
export interface TaskStatus {
|
|
69
106
|
readonly state: TaskState
|
|
70
|
-
readonly message?: Message
|
|
107
|
+
readonly message?: Message | null
|
|
108
|
+
/** ISO 8601 timestamp when the status was recorded. */
|
|
71
109
|
readonly timestamp: string
|
|
72
110
|
}
|
|
73
111
|
|
|
74
|
-
/** A chunk of task output. */
|
|
112
|
+
/** A chunk of task output (spec `Artifact`). */
|
|
75
113
|
export interface Artifact {
|
|
76
114
|
readonly name?: string
|
|
77
|
-
readonly parts: readonly Part[]
|
|
78
115
|
readonly artifactId?: string
|
|
116
|
+
readonly parts: readonly Part[]
|
|
117
|
+
readonly metadata?: Record<string, unknown> | null
|
|
79
118
|
}
|
|
80
119
|
|
|
81
|
-
/**
|
|
120
|
+
/** The core unit of action for A2A (spec `Task`). */
|
|
82
121
|
export interface Task {
|
|
122
|
+
/** Server-generated id for a new task. */
|
|
83
123
|
readonly id: string
|
|
84
124
|
readonly contextId?: string
|
|
85
125
|
readonly status: TaskStatus
|
|
86
126
|
readonly artifacts?: readonly Artifact[]
|
|
87
127
|
readonly history?: readonly Message[]
|
|
88
|
-
readonly metadata?: Record<string, unknown>
|
|
128
|
+
readonly metadata?: Record<string, unknown> | null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Task status transition event (spec `TaskStatusUpdateEvent`). */
|
|
132
|
+
export interface TaskStatusUpdateEvent {
|
|
133
|
+
readonly taskId: string
|
|
134
|
+
readonly contextId?: string
|
|
135
|
+
readonly status: TaskStatus
|
|
136
|
+
readonly metadata?: Record<string, unknown> | null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Task artifact update event (spec `TaskArtifactUpdateEvent`). */
|
|
140
|
+
export interface TaskArtifactUpdateEvent {
|
|
141
|
+
readonly taskId: string
|
|
142
|
+
readonly contextId?: string
|
|
143
|
+
readonly artifact: Artifact
|
|
144
|
+
readonly lastChunk?: boolean
|
|
145
|
+
readonly metadata?: Record<string, unknown> | null
|
|
89
146
|
}
|
|
90
147
|
|
|
91
|
-
/**
|
|
148
|
+
/** Streaming payload variants (spec `StreamResponse` oneof). */
|
|
149
|
+
export type StreamResponse =
|
|
150
|
+
| { readonly task: Task }
|
|
151
|
+
| { readonly message: Message }
|
|
152
|
+
| { readonly statusUpdate: TaskStatusUpdateEvent }
|
|
153
|
+
| { readonly artifactUpdate: TaskArtifactUpdateEvent }
|
|
154
|
+
|
|
155
|
+
// ── push notifications (spec §Push Notification Objects) ──────────────────
|
|
156
|
+
|
|
157
|
+
/** Push notification configuration (spec `TaskPushNotificationConfig`). */
|
|
158
|
+
export interface TaskPushNotificationConfig {
|
|
159
|
+
readonly id?: string
|
|
160
|
+
readonly url: string
|
|
161
|
+
readonly token?: string | null
|
|
162
|
+
readonly authentication?: {
|
|
163
|
+
readonly schemes: readonly string[]
|
|
164
|
+
readonly credentials?: string | null
|
|
165
|
+
} | null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── JSON-RPC 2.0 frames ───────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/** JSON-RPC 2.0 message base. */
|
|
92
171
|
export interface JsonRpcRequest {
|
|
93
172
|
readonly jsonrpc: '2.0'
|
|
94
173
|
readonly id?: string | number | null
|
|
95
174
|
readonly method: string
|
|
96
|
-
readonly params?: unknown
|
|
175
|
+
readonly params?: Record<string, unknown> | null
|
|
97
176
|
}
|
|
98
177
|
|
|
99
178
|
export interface JsonRpcSuccess {
|
|
@@ -116,64 +195,91 @@ export interface JsonRpcError {
|
|
|
116
195
|
|
|
117
196
|
export type JsonRpcResponse = JsonRpcSuccess | JsonRpcError
|
|
118
197
|
|
|
119
|
-
|
|
198
|
+
// ── Agent Card (spec §8 Agent Discovery) ──────────────────────────────────
|
|
199
|
+
|
|
200
|
+
/** A skill advertised by an agent (spec `AgentSkill`). */
|
|
120
201
|
export interface AgentSkill {
|
|
121
202
|
readonly id: string
|
|
122
|
-
|
|
123
|
-
readonly
|
|
124
|
-
|
|
125
|
-
readonly
|
|
126
|
-
readonly
|
|
127
|
-
readonly
|
|
203
|
+
/** Human-readable name. Required by the protocol. */
|
|
204
|
+
readonly name: string
|
|
205
|
+
/** Detailed description (CommonMark may be used). */
|
|
206
|
+
readonly description?: string | null
|
|
207
|
+
readonly tags?: readonly string[] | null
|
|
208
|
+
readonly examples?: readonly string[] | null
|
|
209
|
+
readonly inputModes?: readonly string[] | null
|
|
210
|
+
readonly outputModes?: readonly string[] | null
|
|
128
211
|
}
|
|
129
212
|
|
|
213
|
+
/** Optional A2A protocol features (spec `AgentCapabilities`). */
|
|
130
214
|
export interface AgentCapabilities {
|
|
215
|
+
/** `SendStreamingMessage` + `SubscribeToTask` support. */
|
|
131
216
|
readonly streaming?: boolean
|
|
217
|
+
/** Push notification webhooks. */
|
|
132
218
|
readonly pushNotifications?: boolean
|
|
219
|
+
/** Extended Agent Card support. */
|
|
220
|
+
readonly extendedAgentCard?: boolean
|
|
133
221
|
readonly stateTransitionHistory?: boolean
|
|
134
|
-
readonly extensions?: readonly string[]
|
|
135
222
|
}
|
|
136
223
|
|
|
137
|
-
/**
|
|
224
|
+
/** Information about the providing organization (spec `AgentProvider`). */
|
|
225
|
+
export interface AgentProvider {
|
|
226
|
+
readonly organization: string
|
|
227
|
+
readonly url?: string | null
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Authentication requirements of the agent's endpoint (spec `AgentAuthentication`). */
|
|
231
|
+
export interface AgentAuthentication {
|
|
232
|
+
/** Scheme names, e.g. "Bearer", "OAuth2", "ApiKey". Empty = no A2A-level auth. */
|
|
233
|
+
readonly schemes: readonly string[]
|
|
234
|
+
/** Non-secret scheme configuration; MUST NOT contain plaintext secrets. */
|
|
235
|
+
readonly credentials?: string | null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** One supported interface of an agent (spec `AgentInterface`). */
|
|
138
239
|
export interface AgentInterface {
|
|
139
240
|
readonly url: string
|
|
140
|
-
readonly protocolBinding?: 'JSONRPC' | '
|
|
241
|
+
readonly protocolBinding?: 'JSONRPC' | 'GRPC' | 'HTTP+JSON' | string
|
|
141
242
|
readonly protocolVersion?: string
|
|
142
|
-
readonly
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Bearer-token security scheme advertised by the AgentCard. */
|
|
146
|
-
export interface AgentSecurityScheme {
|
|
147
|
-
readonly type: 'http'
|
|
148
|
-
readonly scheme: 'bearer'
|
|
149
|
-
readonly description?: string
|
|
243
|
+
readonly tenant?: string
|
|
150
244
|
}
|
|
151
245
|
|
|
152
|
-
/** The discovery manifest of an A2A agent. */
|
|
246
|
+
/** The discovery manifest of an A2A agent (spec `AgentCard`). */
|
|
153
247
|
export interface AgentCard {
|
|
154
248
|
readonly name: string
|
|
155
|
-
readonly description
|
|
249
|
+
readonly description?: string | null
|
|
250
|
+
readonly supportedInterfaces?: readonly AgentInterface[]
|
|
251
|
+
readonly provider?: AgentProvider | null
|
|
252
|
+
readonly iconUrl?: string | null
|
|
156
253
|
readonly version: string
|
|
157
|
-
readonly
|
|
158
|
-
readonly
|
|
159
|
-
readonly
|
|
160
|
-
readonly
|
|
254
|
+
readonly documentationUrl?: string | null
|
|
255
|
+
readonly capabilities: AgentCapabilities
|
|
256
|
+
readonly securitySchemes?: Record<string, unknown> | null
|
|
257
|
+
readonly securityRequirements?: readonly unknown[] | null
|
|
161
258
|
readonly defaultInputModes?: readonly string[]
|
|
162
259
|
readonly defaultOutputModes?: readonly string[]
|
|
163
|
-
readonly
|
|
164
|
-
readonly securityRequirements?: readonly Record<string, readonly string[]>[]
|
|
165
|
-
readonly supportedInterfaces?: readonly AgentInterface[]
|
|
166
|
-
readonly custom?: Record<string, unknown>
|
|
260
|
+
readonly skills: readonly AgentSkill[]
|
|
167
261
|
}
|
|
168
262
|
|
|
169
|
-
|
|
170
|
-
export type StreamResponse =
|
|
171
|
-
| { readonly statusUpdate: { readonly taskId: string; readonly contextId?: string; readonly status: TaskStatus } }
|
|
172
|
-
| { readonly artifactUpdate: { readonly taskId: string; readonly contextId?: string; readonly artifact: Artifact; readonly lastChunk?: boolean } }
|
|
173
|
-
| { readonly task: Task }
|
|
174
|
-
| { readonly error: { readonly code: number; readonly message: string } }
|
|
263
|
+
// ── JSON-RPC error codes (spec §error codes + §9.5) ───────────────────────
|
|
175
264
|
|
|
176
|
-
/**
|
|
265
|
+
/** JSON-RPC + A2A error codes. */
|
|
266
|
+
export const A2A_ERROR_CODES = {
|
|
267
|
+
PARSE_ERROR: -32700,
|
|
268
|
+
INVALID_REQUEST: -32600,
|
|
269
|
+
METHOD_NOT_FOUND: -32601,
|
|
270
|
+
INVALID_PARAMS: -32602,
|
|
271
|
+
INTERNAL_ERROR: -32603,
|
|
272
|
+
TASK_NOT_FOUND: -32001,
|
|
273
|
+
TASK_CANCEL_NOT_ALLOWED: -32002,
|
|
274
|
+
PUSH_NOTIFICATION_NOT_SUPPORTED: -32003,
|
|
275
|
+
UNSUPPORTED_OPERATION: -32004,
|
|
276
|
+
CONTENT_TYPE_NOT_SUPPORTED: -32005,
|
|
277
|
+
STREAMING_NOT_SUPPORTED: -32006,
|
|
278
|
+
VERSION_NOT_SUPPORTED: -32007,
|
|
279
|
+
INVALID_AGENT_RESPONSE: -32008,
|
|
280
|
+
} as const
|
|
281
|
+
|
|
282
|
+
/** A2A v1.0 JSON-RPC method names (spec §5.3 Method Mapping Reference). */
|
|
177
283
|
export const A2A_METHODS = {
|
|
178
284
|
sendMessage: 'SendMessage',
|
|
179
285
|
sendStreamingMessage: 'SendStreamingMessage',
|
|
@@ -181,30 +287,33 @@ export const A2A_METHODS = {
|
|
|
181
287
|
listTasks: 'ListTasks',
|
|
182
288
|
cancelTask: 'CancelTask',
|
|
183
289
|
subscribeToTask: 'SubscribeToTask',
|
|
290
|
+
createTaskPushNotificationConfig: 'CreateTaskPushNotificationConfig',
|
|
291
|
+
getTaskPushNotificationConfig: 'GetTaskPushNotificationConfig',
|
|
292
|
+
listTaskPushNotificationConfigs: 'ListTaskPushNotificationConfigs',
|
|
293
|
+
deleteTaskPushNotificationConfig: 'DeleteTaskPushNotificationConfig',
|
|
184
294
|
getExtendedAgentCard: 'GetExtendedAgentCard',
|
|
185
295
|
} as const
|
|
186
296
|
|
|
187
|
-
/**
|
|
188
|
-
export const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
INTERNAL_ERROR: -32603,
|
|
193
|
-
UNAUTHORIZED: -32000,
|
|
194
|
-
TASK_NOT_FOUND: -32001,
|
|
195
|
-
TASK_CANCEL_NOT_ALLOWED: -32002,
|
|
196
|
-
AGENT_CARD_NOT_FOUND: -32004,
|
|
197
|
-
AGENT_CARD_SIGNATURE_INVALID: -32005,
|
|
198
|
-
} as const
|
|
297
|
+
/** The A2A protocol version this plugin implements (header + interface). */
|
|
298
|
+
export const PROTOCOL_VERSION = '1.0'
|
|
299
|
+
|
|
300
|
+
/** The well-known AgentCard content type (spec §14.1). */
|
|
301
|
+
export const A2A_JSON_MEDIA_TYPE = 'application/a2a+json'
|
|
199
302
|
|
|
200
|
-
/**
|
|
303
|
+
/**
|
|
304
|
+
* Read a message's text parts as a single string (model-facing convenience).
|
|
305
|
+
* File parts render as their URL/name; data parts as JSON.
|
|
306
|
+
*/
|
|
201
307
|
export function partsToText(parts: readonly Part[] | undefined): string {
|
|
202
308
|
if (!parts) return ''
|
|
203
309
|
return parts
|
|
204
310
|
.map((p) => {
|
|
205
311
|
if ('text' in p && p.text) return p.text
|
|
206
312
|
if ('data' in p && p.data !== undefined) return JSON.stringify(p.data)
|
|
207
|
-
if ('
|
|
313
|
+
if ('raw' in p || 'url' in p || 'filename' in p) {
|
|
314
|
+
const f = p as FilePart
|
|
315
|
+
return f.url ?? `[file ${f.filename ?? f.mediaType ?? 'binary'}]`
|
|
316
|
+
}
|
|
208
317
|
return ''
|
|
209
318
|
})
|
|
210
319
|
.filter(Boolean)
|
package/src/server/a2a-server.ts
CHANGED
|
@@ -51,6 +51,8 @@ export interface A2AServerOptions {
|
|
|
51
51
|
readonly gate: (input: GateInput) => Promise<GateResult>
|
|
52
52
|
readonly onInbound?: (facts: InboundFacts) => void
|
|
53
53
|
readonly onTaskSettled?: (taskId: string) => void
|
|
54
|
+
/** AgentCard route path for this instance; default = well-known A2A path. */
|
|
55
|
+
readonly cardPath?: string
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/** Structural request the server handles (HTTP adapter fills it). */
|
|
@@ -84,6 +86,11 @@ export class A2AServer {
|
|
|
84
86
|
this.card = next
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
/** The AgentCard route path for this instance. */
|
|
90
|
+
cardPath(): string {
|
|
91
|
+
return this.opts.cardPath ?? '/.well-known/agent-card.json'
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
/**
|
|
88
95
|
* Abort a running task by control path (facade /a2a task cancel): abort the
|
|
89
96
|
* executor's signal, settle the task CANCELED, and wake stream waiters.
|
|
@@ -110,7 +117,7 @@ export class A2AServer {
|
|
|
110
117
|
/** Route one inbound HTTP request (GET card, POST JSON-RPC). */
|
|
111
118
|
async handle(req: ServerRequest, body: string): Promise<ServerResponseSpec> {
|
|
112
119
|
const path = (req.url ?? '').split('?')[0] ?? ''
|
|
113
|
-
if (req.method === 'GET' && path ===
|
|
120
|
+
if (req.method === 'GET' && path === this.cardPath()) {
|
|
114
121
|
return { status: 200, contentType: 'application/json', body: JSON.stringify(this.card) }
|
|
115
122
|
}
|
|
116
123
|
if (req.method !== 'POST' || path !== endpointOf(this.card)) {
|
|
@@ -120,7 +127,7 @@ export class A2AServer {
|
|
|
120
127
|
return {
|
|
121
128
|
status: 401,
|
|
122
129
|
contentType: 'application/json',
|
|
123
|
-
body: JSON.stringify(rpcError(null,
|
|
130
|
+
body: JSON.stringify(rpcError(null, -32040, 'Unauthorized')),
|
|
124
131
|
headers: { 'WWW-Authenticate': 'Bearer' },
|
|
125
132
|
}
|
|
126
133
|
}
|
package/src/server/card.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgentCard assembly for the inbound half:
|
|
3
|
-
*
|
|
4
|
-
* `
|
|
2
|
+
* AgentCard assembly for the inbound half: identity and skill DECLARATION at
|
|
3
|
+
* the instance level. The skill list is exactly the creator-declared
|
|
4
|
+
* `AgentSkill[]` stored on the inbound server instance — no derivation from
|
|
5
|
+
* the live tool registry (the v0.2 white-list mechanism is removed). The
|
|
6
|
+
* advertised card is a pure function of the instance record plus its base URL
|
|
7
|
+
* and endpoint path.
|
|
5
8
|
* @module dsh-a2a/server/card
|
|
6
9
|
*/
|
|
7
10
|
|
|
@@ -11,62 +14,6 @@ import {
|
|
|
11
14
|
type AgentSkill,
|
|
12
15
|
} from '../protocol.ts'
|
|
13
16
|
|
|
14
|
-
/** Structural slice of `ctx.tools` used for derivation. */
|
|
15
|
-
export interface ToolGetter {
|
|
16
|
-
get(name: string): { readonly name: string; readonly description?: string } | undefined
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Derivation policy for inbound skills. */
|
|
20
|
-
export interface CardSkillPolicy {
|
|
21
|
-
/** Tool ids (as registered on ctx.tools) exposed as inbound skills. */
|
|
22
|
-
readonly ids: readonly string[]
|
|
23
|
-
/** Ids to remove after derivation (defense in depth). */
|
|
24
|
-
readonly exclude: readonly string[]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Derive the inbound skill list from the live tool registry.
|
|
29
|
-
*
|
|
30
|
-
* Every configured id must resolve to a registered tool — a missing referent
|
|
31
|
-
* fails the derivation loudly (the plugin's misconfiguration contract) with
|
|
32
|
-
* all missing ids listed. The built-in `chat` skill is always present so the
|
|
33
|
-
* server answers before any tool is exposed.
|
|
34
|
-
*
|
|
35
|
-
* @param tools - the tool registry getter.
|
|
36
|
-
* @param policy - explicit-id derivation policy.
|
|
37
|
-
* @returns the derived skills, `chat` first, configured tools after, excludes applied.
|
|
38
|
-
*/
|
|
39
|
-
export function deriveSkills(tools: ToolGetter, policy: CardSkillPolicy): AgentSkill[] {
|
|
40
|
-
const excluded = new Set(policy.exclude)
|
|
41
|
-
const skills: AgentSkill[] = [
|
|
42
|
-
{
|
|
43
|
-
id: 'chat',
|
|
44
|
-
name: 'chat',
|
|
45
|
-
description: 'Conversational assistance over a DSH agent session.',
|
|
46
|
-
tags: ['chat'],
|
|
47
|
-
},
|
|
48
|
-
]
|
|
49
|
-
const missing: string[] = []
|
|
50
|
-
for (const id of policy.ids) {
|
|
51
|
-
if (excluded.has(id)) continue
|
|
52
|
-
const tool = tools.get(id)
|
|
53
|
-
if (tool === undefined) {
|
|
54
|
-
missing.push(id)
|
|
55
|
-
continue
|
|
56
|
-
}
|
|
57
|
-
skills.push({
|
|
58
|
-
id,
|
|
59
|
-
name: id,
|
|
60
|
-
description: tool.description ?? `Expose the DSH tool \`${id}\` over A2A.`,
|
|
61
|
-
tags: ['tool'],
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
if (missing.length > 0) {
|
|
65
|
-
throw new Error(`dsh-a2a: server.skills.ids reference unregistered tools: ${missing.join(', ')}`)
|
|
66
|
-
}
|
|
67
|
-
return skills
|
|
68
|
-
}
|
|
69
|
-
|
|
70
17
|
/** Identity options for the advertised AgentCard. */
|
|
71
18
|
export interface CardOptions {
|
|
72
19
|
readonly baseUrl: string
|
|
@@ -91,15 +38,19 @@ export function buildCard(options: CardOptions): AgentCard {
|
|
|
91
38
|
description: options.description,
|
|
92
39
|
version: options.version,
|
|
93
40
|
provider: { url: 'https://deepseek.com', organization: 'DeepSeek' },
|
|
94
|
-
capabilities: { streaming: true, pushNotifications: false,
|
|
41
|
+
capabilities: { streaming: true, pushNotifications: false, extendedAgentCard: true, stateTransitionHistory: false },
|
|
95
42
|
defaultInputModes: ['text/plain'],
|
|
96
43
|
defaultOutputModes: ['text/plain'],
|
|
97
44
|
skills: options.skills,
|
|
98
45
|
supportedInterfaces: [iface],
|
|
99
46
|
...(options.authToken !== undefined
|
|
100
47
|
? {
|
|
101
|
-
securitySchemes: {
|
|
102
|
-
|
|
48
|
+
securitySchemes: {
|
|
49
|
+
bearer: {
|
|
50
|
+
httpAuthSecurityScheme: { scheme: 'bearer', description: 'Shared bearer token' },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
securityRequirements: [{ schemes: { bearer: { list: ['bearer'] } } }],
|
|
103
54
|
}
|
|
104
55
|
: {}),
|
|
105
56
|
}
|
|
@@ -54,7 +54,7 @@ export interface AgentRegistryLike {
|
|
|
54
54
|
|
|
55
55
|
/** Structural slice of `ctx.agentPresets` (preset roster). */
|
|
56
56
|
export interface AgentPresetsLike {
|
|
57
|
-
resolve(id?: string): Promise<{ readonly id: string }>
|
|
57
|
+
resolve(id?: string): Promise<{ readonly id: string; readonly name?: string }>
|
|
58
58
|
mount(agentCtx: unknown, id: string): Promise<unknown>
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -66,6 +66,8 @@ export interface AgentRuntimeOptions {
|
|
|
66
66
|
readonly resolveAgentOptions?: () => { readonly provider?: string; readonly model?: string; readonly maxTokens?: number } | undefined
|
|
67
67
|
/** Preset roster; when present every spawned session joins the default preset. */
|
|
68
68
|
readonly agentPresets?: AgentPresetsLike
|
|
69
|
+
/** When set, every spawned session joins THIS preset id instead of the deployment default. */
|
|
70
|
+
readonly presetId?: () => string | undefined
|
|
69
71
|
/** Once-per-context hook after the first prompt lands (cosmetic naming). */
|
|
70
72
|
readonly onSessionOpened?: (info: { readonly sessionId: string; readonly contextId: string; readonly firstPrompt: string }) => void | Promise<void>
|
|
71
73
|
}
|
|
@@ -100,7 +102,8 @@ export class ContextSessionPool {
|
|
|
100
102
|
let presetId: string | undefined
|
|
101
103
|
let setup: ((agentCtx: unknown) => Promise<void>) | undefined
|
|
102
104
|
if (presets !== undefined) {
|
|
103
|
-
|
|
105
|
+
const wanted = this.opts.presetId?.() ?? undefined
|
|
106
|
+
presetId = (wanted !== undefined ? await presets.resolve(wanted) : await presets.resolve()).id
|
|
104
107
|
setup = async (agentCtx: unknown): Promise<void> => {
|
|
105
108
|
await presets.mount(agentCtx, presetId!)
|
|
106
109
|
}
|
package/src/server/routes.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface WebServerLike {
|
|
|
17
17
|
}): () => void
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const DEFAULT_CARD_PATH = '/.well-known/agent-card.json'
|
|
21
21
|
|
|
22
22
|
/** Runtime-registered HTTP routes for one A2A server. */
|
|
23
23
|
export class A2aRoutes {
|
|
@@ -27,6 +27,8 @@ export class A2aRoutes {
|
|
|
27
27
|
constructor(
|
|
28
28
|
private readonly webServer: WebServerLike,
|
|
29
29
|
private readonly server: A2AServer,
|
|
30
|
+
/** AgentCard route path; default = the well-known A2A path. */
|
|
31
|
+
private readonly cardPath: string = DEFAULT_CARD_PATH,
|
|
30
32
|
) {}
|
|
31
33
|
|
|
32
34
|
get active(): boolean {
|
|
@@ -39,7 +41,7 @@ export class A2aRoutes {
|
|
|
39
41
|
this.disposers.push(
|
|
40
42
|
this.webServer.register({
|
|
41
43
|
kind: 'exact',
|
|
42
|
-
path:
|
|
44
|
+
path: this.cardPath,
|
|
43
45
|
handler: (_req, res) => {
|
|
44
46
|
res.writeHead(200, { 'content-type': 'application/json' })
|
|
45
47
|
res.end(JSON.stringify(this.server.card))
|
|
@@ -100,8 +102,8 @@ export class A2aRoutes {
|
|
|
100
102
|
return
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
// GET AgentCard at
|
|
104
|
-
const out = await this.server.handle(toServerReq(req), path ===
|
|
105
|
+
// GET AgentCard at this instance's card path; anything else is the server's call.
|
|
106
|
+
const out = await this.server.handle(toServerReq(req), path === this.cardPath ? '' : '')
|
|
105
107
|
res.writeHead(out.status, { 'content-type': out.contentType, ...(out.headers ?? {}) })
|
|
106
108
|
res.end(out.body)
|
|
107
109
|
}
|