@probelabs/visor 0.1.169 → 0.1.170

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.
Files changed (48) hide show
  1. package/README.md +64 -7
  2. package/defaults/assistant.yaml +1 -1
  3. package/defaults/code-talk.yaml +2 -2
  4. package/dist/defaults/assistant.yaml +1 -1
  5. package/dist/defaults/code-talk.yaml +2 -2
  6. package/dist/docs/a2a-provider.md +672 -0
  7. package/dist/docs/architecture.md +174 -12
  8. package/dist/docs/commands.md +36 -0
  9. package/dist/docs/configuration.md +1 -0
  10. package/dist/docs/index.md +9 -2
  11. package/dist/docs/pluggable.md +16 -1
  12. package/dist/index.js +4 -4
  13. package/dist/output/traces/{run-2026-03-07T13-37-21-566Z.ndjson → run-2026-03-07T15-43-18-430Z.ndjson} +84 -84
  14. package/dist/output/traces/{run-2026-03-07T13-37-59-420Z.ndjson → run-2026-03-07T15-43-56-196Z.ndjson} +1818 -1818
  15. package/dist/sdk/{a2a-frontend-XFCSNQR5.mjs → a2a-frontend-IPLHACI6.mjs} +2 -2
  16. package/dist/sdk/{check-provider-registry-XEU5BSRJ.mjs → check-provider-registry-STRAOYRJ.mjs} +5 -5
  17. package/dist/sdk/{chunk-VPPW2TFI.mjs → chunk-47WAHGHK.mjs} +3 -3
  18. package/dist/sdk/{chunk-ROMY3ZN3.mjs → chunk-NYQTQYGU.mjs} +15 -15
  19. package/dist/sdk/{chunk-WGZNS5IB.mjs → chunk-WNLCRRQO.mjs} +2 -2
  20. package/dist/sdk/{chunk-HBT572VG.mjs → chunk-ZM7ALGTE.mjs} +2 -2
  21. package/dist/sdk/{chunk-HBT572VG.mjs.map → chunk-ZM7ALGTE.mjs.map} +1 -1
  22. package/dist/sdk/{failure-condition-evaluator-WYDAZT3H.mjs → failure-condition-evaluator-T67YFO2Z.mjs} +3 -3
  23. package/dist/sdk/{github-frontend-BVM7MHBJ.mjs → github-frontend-BAPXDLBB.mjs} +3 -3
  24. package/dist/sdk/{host-MHYGIPDP.mjs → host-K6IZWJG3.mjs} +3 -3
  25. package/dist/sdk/{routing-K2U7U3OO.mjs → routing-SAGHEUOA.mjs} +4 -4
  26. package/dist/sdk/{schedule-tool-RYYNPLDH.mjs → schedule-tool-TGWPINHO.mjs} +5 -5
  27. package/dist/sdk/{schedule-tool-handler-NFNY6BVX.mjs → schedule-tool-handler-OEBLE5AB.mjs} +5 -5
  28. package/dist/sdk/sdk.js +1 -1
  29. package/dist/sdk/sdk.js.map +1 -1
  30. package/dist/sdk/sdk.mjs +4 -4
  31. package/dist/sdk/{trace-helpers-DQYOGQT5.mjs → trace-helpers-M7RVAZQ2.mjs} +2 -2
  32. package/dist/sdk/{workflow-check-provider-OM5L5FJX.mjs → workflow-check-provider-VDSZR7Y5.mjs} +5 -5
  33. package/dist/traces/{run-2026-03-07T13-37-21-566Z.ndjson → run-2026-03-07T15-43-18-430Z.ndjson} +84 -84
  34. package/dist/traces/{run-2026-03-07T13-37-59-420Z.ndjson → run-2026-03-07T15-43-56-196Z.ndjson} +1818 -1818
  35. package/package.json +1 -1
  36. /package/dist/sdk/{a2a-frontend-XFCSNQR5.mjs.map → a2a-frontend-IPLHACI6.mjs.map} +0 -0
  37. /package/dist/sdk/{check-provider-registry-XEU5BSRJ.mjs.map → check-provider-registry-STRAOYRJ.mjs.map} +0 -0
  38. /package/dist/sdk/{chunk-VPPW2TFI.mjs.map → chunk-47WAHGHK.mjs.map} +0 -0
  39. /package/dist/sdk/{chunk-ROMY3ZN3.mjs.map → chunk-NYQTQYGU.mjs.map} +0 -0
  40. /package/dist/sdk/{chunk-WGZNS5IB.mjs.map → chunk-WNLCRRQO.mjs.map} +0 -0
  41. /package/dist/sdk/{failure-condition-evaluator-WYDAZT3H.mjs.map → failure-condition-evaluator-T67YFO2Z.mjs.map} +0 -0
  42. /package/dist/sdk/{github-frontend-BVM7MHBJ.mjs.map → github-frontend-BAPXDLBB.mjs.map} +0 -0
  43. /package/dist/sdk/{host-MHYGIPDP.mjs.map → host-K6IZWJG3.mjs.map} +0 -0
  44. /package/dist/sdk/{routing-K2U7U3OO.mjs.map → routing-SAGHEUOA.mjs.map} +0 -0
  45. /package/dist/sdk/{schedule-tool-RYYNPLDH.mjs.map → schedule-tool-TGWPINHO.mjs.map} +0 -0
  46. /package/dist/sdk/{schedule-tool-handler-NFNY6BVX.mjs.map → schedule-tool-handler-OEBLE5AB.mjs.map} +0 -0
  47. /package/dist/sdk/{trace-helpers-DQYOGQT5.mjs.map → trace-helpers-M7RVAZQ2.mjs.map} +0 -0
  48. /package/dist/sdk/{workflow-check-provider-OM5L5FJX.mjs.map → workflow-check-provider-VDSZR7Y5.mjs.map} +0 -0
@@ -0,0 +1,672 @@
1
+ # A2A Provider (Agent-to-Agent Protocol)
2
+
3
+ Visor implements the [A2A (Agent-to-Agent) protocol](https://github.com/google/A2A) for agent interoperability. This enables two modes of operation:
4
+
5
+ - **Server mode** — Visor exposes workflows as a discoverable A2A agent that external systems can call
6
+ - **Client mode** — Visor calls external A2A agents as workflow steps using `type: a2a`
7
+
8
+ ### A2A vs MCP
9
+
10
+ | | A2A | MCP |
11
+ |---|---|---|
12
+ | **Pattern** | Agent-to-Agent task delegation | Agent-to-Tool function calls |
13
+ | **Communication** | Stateful tasks with lifecycle | Stateless function invocation |
14
+ | **Input** | Natural language + structured data | Typed JSON Schema parameters |
15
+ | **Discovery** | Agent Card at `/.well-known/agent-card.json` | Tool listing via `tools/list` |
16
+ | **Multi-turn** | Built-in (input_required state) | Not applicable |
17
+
18
+ Use MCP when you need deterministic tool calls with typed schemas. Use A2A when you need to delegate complex tasks to another agent that may require multiple turns or long-running execution.
19
+
20
+ ---
21
+
22
+ ## Quick Start
23
+
24
+ ### Server: Expose a Workflow as an A2A Agent
25
+
26
+ ```yaml
27
+ # .visor.yaml
28
+ version: "1.0"
29
+
30
+ agent_protocol:
31
+ enabled: true
32
+ protocol: a2a
33
+ port: 9000
34
+ agent_card_inline:
35
+ name: "Review Agent"
36
+ description: "AI code review"
37
+ skills:
38
+ - id: review
39
+ name: Code Review
40
+ description: Review code for issues
41
+ default_workflow: review
42
+
43
+ steps:
44
+ review:
45
+ type: ai
46
+ prompt: "Review the submitted code for issues"
47
+ on: [manual]
48
+ ```
49
+
50
+ ```bash
51
+ # Start the A2A server
52
+ visor --a2a --config .visor.yaml
53
+
54
+ # Test with curl
55
+ curl http://localhost:9000/.well-known/agent-card.json
56
+ curl -X POST http://localhost:9000/message:send \
57
+ -H "Content-Type: application/json" \
58
+ -d '{"message": {"message_id": "1", "role": "user", "parts": [{"text": "Review my code"}]}}'
59
+ ```
60
+
61
+ ### Client: Call an External A2A Agent
62
+
63
+ ```yaml
64
+ steps:
65
+ scan:
66
+ type: a2a
67
+ agent_url: "http://compliance-agent:9000"
68
+ message: "Review PR #{{ pr.number }}: {{ pr.title }}"
69
+ blocking: true
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Server Mode: Visor as an A2A Agent
75
+
76
+ ### Enabling the Server
77
+
78
+ Start with the `--a2a` CLI flag:
79
+
80
+ ```bash
81
+ visor --a2a --config .visor.yaml
82
+ ```
83
+
84
+ Or enable in configuration:
85
+
86
+ ```yaml
87
+ agent_protocol:
88
+ enabled: true
89
+ protocol: a2a
90
+ port: 9000 # default: 9000
91
+ host: "0.0.0.0" # default: 0.0.0.0
92
+ ```
93
+
94
+ A2A can run alongside other modes:
95
+
96
+ ```bash
97
+ visor --a2a --slack --config .visor.yaml # A2A + Slack simultaneously
98
+ ```
99
+
100
+ ### Agent Card
101
+
102
+ The Agent Card describes your agent to external clients. Define it inline or load from a file:
103
+
104
+ ```yaml
105
+ # Inline
106
+ agent_protocol:
107
+ agent_card_inline:
108
+ name: "Code Review Agent"
109
+ description: "AI-powered code review"
110
+ version: "1.0.0"
111
+ provider:
112
+ organization: "My Org"
113
+ url: "https://myorg.com"
114
+ skills:
115
+ - id: security
116
+ name: Security Review
117
+ description: OWASP Top 10 analysis
118
+ tags: [security, owasp]
119
+ - id: performance
120
+ name: Performance Review
121
+ description: Performance bottleneck detection
122
+ tags: [performance]
123
+ supported_interfaces:
124
+ - url: "http://localhost:9000"
125
+ protocol_binding: "a2a/v1"
126
+ capabilities:
127
+ streaming: false
128
+ push_notifications: false
129
+
130
+ # Or load from file
131
+ agent_protocol:
132
+ agent_card: "./agent-card.json"
133
+ ```
134
+
135
+ The Agent Card is served publicly at `GET /.well-known/agent-card.json` (no authentication required).
136
+
137
+ ### Skill Routing
138
+
139
+ Map incoming A2A skill IDs to internal Visor workflows:
140
+
141
+ ```yaml
142
+ agent_protocol:
143
+ skill_routing:
144
+ security: security-review # A2A skill "security" → workflow "security-review"
145
+ performance: performance-review
146
+ default_workflow: general-review # Fallback when skill not matched
147
+ ```
148
+
149
+ When a client sends a message with `metadata.skill_id: "security"`, Visor routes it to the `security-review` workflow. Unmatched skills use `default_workflow`.
150
+
151
+ ### Authentication
152
+
153
+ Configure authentication for inbound requests:
154
+
155
+ ```yaml
156
+ agent_protocol:
157
+ auth:
158
+ type: bearer # bearer | api_key | none
159
+ token_env: AGENT_AUTH_TOKEN # Environment variable containing the token
160
+ ```
161
+
162
+ **Bearer token:**
163
+ ```yaml
164
+ auth:
165
+ type: bearer
166
+ token_env: AGENT_AUTH_TOKEN # Checked via Authorization: Bearer <token>
167
+ ```
168
+
169
+ **API key:**
170
+ ```yaml
171
+ auth:
172
+ type: api_key
173
+ key_env: AGENT_API_KEY
174
+ header_name: X-API-Key # default: x-api-key
175
+ param_name: api_key # also checked as query parameter
176
+ ```
177
+
178
+ > The Agent Card endpoint (`/.well-known/agent-card.json`) is always public regardless of auth configuration.
179
+
180
+ ### HTTP Endpoints
181
+
182
+ | Method | Path | Auth | Description |
183
+ |--------|------|------|-------------|
184
+ | `GET` | `/.well-known/agent-card.json` | No | Agent Card for discovery |
185
+ | `POST` | `/message:send` | Yes | Submit a task / send a message |
186
+ | `GET` | `/tasks/{id}` | Yes | Get task by ID |
187
+ | `GET` | `/tasks` | Yes | List all tasks |
188
+ | `POST` | `/tasks/{id}:cancel` | Yes | Cancel a running task |
189
+
190
+ ### Task Lifecycle
191
+
192
+ Tasks follow a state machine with these transitions:
193
+
194
+ ```
195
+ ┌─────────────┐
196
+ │ submitted │
197
+ └──────┬──────┘
198
+ ┌────────────┼────────────┐
199
+ v v v
200
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
201
+ │ working │ │ canceled │ │ rejected │
202
+ └────┬─────┘ └──────────┘ └──────────┘
203
+ ┌─────┬───┼───┬─────────┐
204
+ v v v v v
205
+ ┌─────────┐ │ ┌───┐ │ ┌────────────────┐
206
+ │completed│ │ │ │ │ │input_required │──→ working (resumed)
207
+ └─────────┘ │ │ │ │ └────────────────┘
208
+ ┌─────┘ │ │ └──────────┐
209
+ v v v v
210
+ ┌──────────┐ ┌──────────┐ ┌────────────────┐
211
+ │ failed │ │ canceled │ │ auth_required │──→ working (resumed)
212
+ └──────────┘ └──────────┘ └────────────────┘
213
+ ```
214
+
215
+ **Terminal states:** `completed`, `failed`, `canceled`, `rejected`
216
+
217
+ | State | Description |
218
+ |-------|-------------|
219
+ | `submitted` | Task created, waiting to be picked up |
220
+ | `working` | Workflow engine is processing the task |
221
+ | `input_required` | Agent needs additional input from the client |
222
+ | `auth_required` | Agent needs authentication credentials |
223
+ | `completed` | Task finished successfully |
224
+ | `failed` | Task execution encountered an error |
225
+ | `canceled` | Task was canceled by the client |
226
+ | `rejected` | Task was rejected at submission |
227
+
228
+ ### Task Queue
229
+
230
+ For async execution, configure the task queue:
231
+
232
+ ```yaml
233
+ agent_protocol:
234
+ queue:
235
+ poll_interval: 1000 # Poll database every N ms (default: 1000)
236
+ max_concurrent: 5 # Max concurrent tasks (default: 5)
237
+ stale_claim_timeout: 300000 # Worker timeout in ms (default: 300000)
238
+ task_ttl: "7d" # Task retention period (default: 7d)
239
+ ```
240
+
241
+ **Blocking vs async:**
242
+ - **Blocking** (default): The `POST /message:send` request waits for the workflow to complete and returns the full task with artifacts.
243
+ - **Async**: Set `configuration.blocking: false` in the request. Returns the task ID immediately; the client polls `GET /tasks/{id}` for status updates.
244
+
245
+ ### TLS
246
+
247
+ For production deployments:
248
+
249
+ ```yaml
250
+ agent_protocol:
251
+ tls:
252
+ cert: "/path/to/cert.pem"
253
+ key: "/path/to/key.pem"
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Client Mode: Calling External A2A Agents
259
+
260
+ Use `type: a2a` to call external A2A-compatible agents from your workflow.
261
+
262
+ ### Basic Usage
263
+
264
+ ```yaml
265
+ steps:
266
+ compliance-scan:
267
+ type: a2a
268
+ agent_url: "http://compliance-agent:9000"
269
+ message: |
270
+ Review PR #{{ pr.number }}: {{ pr.title }}
271
+ Files changed: {{ files | size }}
272
+ blocking: true
273
+ timeout: 60000
274
+ ```
275
+
276
+ ### Agent Discovery
277
+
278
+ Specify the agent endpoint in one of two ways (mutually exclusive):
279
+
280
+ ```yaml
281
+ # Option 1: Direct URL to the agent
282
+ agent_url: "http://agent.example.com:9000"
283
+
284
+ # Option 2: URL to the Agent Card (endpoint resolved from card)
285
+ agent_card: "https://agent.example.com/.well-known/agent-card.json"
286
+ ```
287
+
288
+ When using `agent_card`, Visor fetches the card, caches it for 5 minutes, and resolves the endpoint from `supported_interfaces`.
289
+
290
+ ### Authentication
291
+
292
+ ```yaml
293
+ steps:
294
+ scan:
295
+ type: a2a
296
+ agent_url: "http://agent:9000"
297
+ message: "Review this code"
298
+ auth:
299
+ scheme: bearer # bearer | api_key
300
+ token_env: AGENT_TOKEN # Environment variable with the token
301
+ # header_name: X-API-Key # For api_key scheme (default: x-api-key)
302
+ ```
303
+
304
+ ### Message Construction
305
+
306
+ **Text message** (Liquid template):
307
+ ```yaml
308
+ message: |
309
+ Review PR #{{ pr.number }}: {{ pr.title }}
310
+ Author: {{ pr.author }}
311
+ ```
312
+
313
+ **Structured data** (Liquid-templated key-value pairs sent as data parts):
314
+ ```yaml
315
+ data:
316
+ repo_context: '{{ pr.repo | json }}'
317
+ file_list: '{{ files | json }}'
318
+ ```
319
+
320
+ **File attachments:**
321
+ ```yaml
322
+ files:
323
+ - url: "https://example.com/requirements.txt"
324
+ media_type: "text/plain"
325
+ filename: "requirements.txt"
326
+ ```
327
+
328
+ ### Polling and Multi-Turn
329
+
330
+ ```yaml
331
+ steps:
332
+ interactive-agent:
333
+ type: a2a
334
+ agent_url: "http://agent:9000"
335
+ message: "Analyze {{ pr.title }}"
336
+
337
+ blocking: true # Wait for completion (default: true)
338
+ timeout: 300000 # Max wait in ms (default: 300000)
339
+ poll_interval: 2000 # Poll frequency in ms (default: 2000)
340
+
341
+ # Multi-turn conversation
342
+ max_turns: 3 # Max conversation turns (default: 1)
343
+ on_input_required: | # Auto-reply when agent asks for more info
344
+ Here is additional context:
345
+ {{ pr.body }}
346
+ ```
347
+
348
+ When the agent enters `input_required` state, Visor automatically sends the `on_input_required` template as a follow-up message. This continues up to `max_turns`.
349
+
350
+ ### Output Transformation
351
+
352
+ Transform agent responses into structured issues with JavaScript:
353
+
354
+ ```yaml
355
+ steps:
356
+ scan:
357
+ type: a2a
358
+ agent_url: "http://agent:9000"
359
+ message: "Security scan"
360
+ transform_js: |
361
+ return {
362
+ issues: (output.issues || []).map(function(i) {
363
+ return {
364
+ file: i.file,
365
+ line: i.line || 0,
366
+ ruleId: 'a2a/' + (i.ruleId || 'issue'),
367
+ message: i.message,
368
+ severity: i.severity || 'warning'
369
+ };
370
+ })
371
+ };
372
+ ```
373
+
374
+ ### Error Handling
375
+
376
+ A2A errors are surfaced as issues with `ruleId: a2a/error`:
377
+
378
+ | Error | When |
379
+ |-------|------|
380
+ | `A2ATimeoutError` | Task didn't complete within `timeout` |
381
+ | `A2AMaxTurnsExceededError` | Conversation exceeded `max_turns` |
382
+ | `A2AInputRequiredError` | Agent needs input but `max_turns` exhausted |
383
+ | `A2AAuthRequiredError` | Agent requires authentication credentials |
384
+ | `A2ATaskFailedError` | Task execution failed on the remote agent |
385
+ | `A2ATaskRejectedError` | Task was rejected or canceled |
386
+ | `A2ARequestError` | HTTP request to the agent failed |
387
+ | `AgentCardFetchError` | Failed to fetch Agent Card |
388
+ | `InvalidAgentCardError` | Agent Card is malformed |
389
+
390
+ ---
391
+
392
+ ## Task Management CLI
393
+
394
+ Monitor and manage A2A tasks with `visor tasks`:
395
+
396
+ ```bash
397
+ visor tasks # List all tasks (alias for list)
398
+ visor tasks list # List tasks
399
+ visor tasks list --state working # Filter by state
400
+ visor tasks list --agent security-review # Filter by workflow
401
+ visor tasks list --limit 50 # Show more results
402
+ visor tasks list --output json # JSON output
403
+ visor tasks list --watch # Live refresh every 2s
404
+ visor tasks stats # Queue summary statistics
405
+ visor tasks stats --output json # Stats as JSON
406
+ visor tasks cancel <task-id> # Cancel a running task
407
+ visor tasks help # Show usage
408
+ ```
409
+
410
+ **Flags:**
411
+
412
+ | Flag | Description | Default |
413
+ |------|-------------|---------|
414
+ | `--state <state>` | Filter by task state | all |
415
+ | `--agent <workflow-id>` | Filter by workflow | all |
416
+ | `--limit <n>` | Number of tasks to show | 20 |
417
+ | `--output <format>` | Output format: `table`, `json`, `markdown` | table |
418
+ | `--watch` | Refresh every 2 seconds | off |
419
+
420
+ ---
421
+
422
+ ## Configuration Reference
423
+
424
+ ### Server-Side: `agent_protocol`
425
+
426
+ | Key | Type | Default | Description |
427
+ |-----|------|---------|-------------|
428
+ | `enabled` | boolean | `false` | Enable the A2A server |
429
+ | `protocol` | string | `"a2a"` | Protocol binding |
430
+ | `port` | number | `9000` | HTTP listen port |
431
+ | `host` | string | `"0.0.0.0"` | HTTP bind address |
432
+ | `public_url` | string | auto | Public URL for Agent Card |
433
+ | `agent_card_inline` | object | - | Inline Agent Card definition |
434
+ | `agent_card` | string | - | Path to Agent Card JSON file |
435
+ | `auth` | object | - | Authentication config |
436
+ | `auth.type` | string | - | `bearer`, `api_key`, or `none` |
437
+ | `auth.token_env` | string | - | Env var for bearer token |
438
+ | `auth.key_env` | string | - | Env var for API key |
439
+ | `auth.header_name` | string | `"x-api-key"` | Custom header for API key |
440
+ | `auth.param_name` | string | `"api_key"` | Query parameter for API key |
441
+ | `skill_routing` | object | - | Map of skill_id to workflow name |
442
+ | `default_workflow` | string | - | Fallback workflow for unmatched skills |
443
+ | `task_ttl` | string | `"7d"` | Task retention period |
444
+ | `queue.poll_interval` | number | `1000` | Queue poll interval in ms |
445
+ | `queue.max_concurrent` | number | `5` | Max concurrent task executions |
446
+ | `queue.stale_claim_timeout` | number | `300000` | Worker stale claim timeout in ms |
447
+ | `tls.cert` | string | - | Path to TLS certificate |
448
+ | `tls.key` | string | - | Path to TLS private key |
449
+
450
+ ### Client-Side: `type: a2a` Check Provider
451
+
452
+ | Key | Type | Default | Required | Description |
453
+ |-----|------|---------|----------|-------------|
454
+ | `agent_url` | string | - | one of | Direct agent endpoint URL |
455
+ | `agent_card` | string | - | one of | URL to Agent Card |
456
+ | `message` | string | - | yes | Liquid template for the message text |
457
+ | `data` | object | - | no | Liquid-templated structured data parts |
458
+ | `files` | array | - | no | File attachments (`url`, `media_type`, `filename`) |
459
+ | `auth.scheme` | string | - | no | `bearer` or `api_key` |
460
+ | `auth.token_env` | string | - | no | Env var containing auth token |
461
+ | `auth.header_name` | string | `"x-api-key"` | no | Custom header for API key |
462
+ | `blocking` | boolean | `true` | no | Wait for task completion |
463
+ | `timeout` | number | `300000` | no | Max wait time in ms |
464
+ | `poll_interval` | number | `2000` | no | Poll interval in ms |
465
+ | `max_turns` | number | `1` | no | Max conversation turns |
466
+ | `on_input_required` | string | - | no | Liquid template for auto-reply |
467
+ | `transform_js` | string | - | no | JavaScript output transformation |
468
+ | `accepted_output_modes` | string[] | `["text/plain", "application/json"]` | no | Accepted MIME types |
469
+
470
+ ---
471
+
472
+ ## Examples
473
+
474
+ ### Server: Code Review Agent
475
+
476
+ Full example from [examples/a2a-agent-example.yaml](../examples/a2a-agent-example.yaml):
477
+
478
+ ```yaml
479
+ version: "1.0"
480
+
481
+ agent_protocol:
482
+ enabled: true
483
+ protocol: a2a
484
+ port: 9000
485
+ host: "0.0.0.0"
486
+
487
+ agent_card_inline:
488
+ name: "Code Review Agent"
489
+ description: "AI-powered code review agent built with Visor"
490
+ version: "1.0.0"
491
+ provider:
492
+ organization: "Visor"
493
+ skills:
494
+ - id: security
495
+ name: Security Review
496
+ description: Analyze code for security vulnerabilities (OWASP Top 10)
497
+ tags: [security, owasp]
498
+ - id: performance
499
+ name: Performance Review
500
+ description: Analyze code for performance issues and bottlenecks
501
+ tags: [performance]
502
+ supported_interfaces:
503
+ - url: "http://localhost:9000"
504
+ protocol_binding: "a2a/v1"
505
+ capabilities:
506
+ streaming: false
507
+ push_notifications: false
508
+
509
+ auth:
510
+ type: bearer
511
+ token_env: AGENT_AUTH_TOKEN
512
+
513
+ skill_routing:
514
+ security: security-review
515
+ performance: performance-review
516
+ default_workflow: general-review
517
+
518
+ task_ttl: "7d"
519
+ queue:
520
+ max_concurrent: 5
521
+
522
+ steps:
523
+ security-review:
524
+ type: ai
525
+ prompt: |
526
+ Perform a security review focusing on OWASP Top 10 vulnerabilities.
527
+ Check for SQL injection, XSS, CSRF, and authentication issues.
528
+ on: [manual]
529
+
530
+ performance-review:
531
+ type: ai
532
+ prompt: |
533
+ Review for performance issues: N+1 queries, memory leaks,
534
+ unnecessary allocations, and algorithmic complexity.
535
+ on: [manual]
536
+
537
+ general-review:
538
+ type: ai
539
+ prompt: |
540
+ General code quality review: naming, structure, error handling,
541
+ and adherence to project conventions.
542
+ on: [manual]
543
+ ```
544
+
545
+ ```bash
546
+ # Start
547
+ visor --a2a --config examples/a2a-agent-example.yaml
548
+
549
+ # Discover
550
+ curl http://localhost:9000/.well-known/agent-card.json | jq .
551
+
552
+ # Send a task targeting the security skill
553
+ curl -X POST http://localhost:9000/message:send \
554
+ -H "Content-Type: application/json" \
555
+ -H "Authorization: Bearer $AGENT_AUTH_TOKEN" \
556
+ -d '{
557
+ "message": {
558
+ "message_id": "msg-1",
559
+ "role": "user",
560
+ "parts": [{"text": "Review auth.py for security issues"}],
561
+ "metadata": {"skill_id": "security"}
562
+ }
563
+ }'
564
+
565
+ # Monitor tasks
566
+ visor tasks list --watch
567
+ ```
568
+
569
+ ### Client: Multi-Agent Composition
570
+
571
+ Chain multiple A2A agents and aggregate results:
572
+
573
+ ```yaml
574
+ steps:
575
+ compliance-scan:
576
+ type: a2a
577
+ agent_url: "http://compliance-agent:9000"
578
+ message: "Check compliance for PR #{{ pr.number }}"
579
+ blocking: true
580
+
581
+ security-scan:
582
+ type: a2a
583
+ agent_url: "http://security-agent:9000"
584
+ message: "Security review for PR #{{ pr.number }}"
585
+ blocking: true
586
+
587
+ summarize:
588
+ type: ai
589
+ depends_on: [compliance-scan, security-scan]
590
+ prompt: |
591
+ Summarize findings from two agents:
592
+ Compliance: {{ outputs["compliance-scan"] | json }}
593
+ Security: {{ outputs["security-scan"] | json }}
594
+ ```
595
+
596
+ ### Multi-Turn Conversation
597
+
598
+ Handle agents that ask follow-up questions:
599
+
600
+ ```yaml
601
+ steps:
602
+ interactive-review:
603
+ type: a2a
604
+ agent_card: "https://agent.example.com/.well-known/agent-card.json"
605
+ message: "Analyze {{ pr.title }} for best practices"
606
+ max_turns: 3
607
+ on_input_required: |
608
+ Here is additional context:
609
+ {{ pr.body }}
610
+ transform_js: |
611
+ return {
612
+ issues: (output.issues || []).map(function(i) {
613
+ return {
614
+ file: i.file,
615
+ line: i.line || 0,
616
+ ruleId: 'a2a/' + (i.ruleId || 'issue'),
617
+ message: i.message,
618
+ severity: i.severity || 'warning'
619
+ };
620
+ })
621
+ };
622
+ ```
623
+
624
+ ---
625
+
626
+ ## Security Considerations
627
+
628
+ - **Always configure auth for production** — without it, anyone can submit tasks to your agent
629
+ - **Use `token_env` / `key_env`** — never hardcode tokens in config files
630
+ - **Enable TLS** for public-facing servers
631
+ - **Agent Card is public** — it's served without auth, so don't include sensitive information
632
+ - **Task TTL** — set appropriate retention to limit stored data (`task_ttl: "7d"`)
633
+ - **Token comparison** uses timing-safe comparison to prevent timing attacks
634
+
635
+ ---
636
+
637
+ ## Debugging
638
+
639
+ ```bash
640
+ # Start with debug logging
641
+ visor --a2a --debug --config .visor.yaml
642
+
643
+ # Verify Agent Card is served correctly
644
+ curl http://localhost:9000/.well-known/agent-card.json | jq .
645
+
646
+ # Monitor live task queue
647
+ visor tasks list --watch
648
+
649
+ # Check queue statistics
650
+ visor tasks stats
651
+
652
+ # View specific task
653
+ visor tasks list --state failed # Find failed tasks
654
+ ```
655
+
656
+ With OpenTelemetry enabled, A2A tasks emit spans with:
657
+ - `agent.task.id` — Task ID
658
+ - `agent.task.state` — Current state
659
+ - `agent.task.workflow_id` — Target workflow
660
+
661
+ ---
662
+
663
+ ## Related Documentation
664
+
665
+ - [MCP Provider](./mcp-provider.md) — MCP tool integration (complementary to A2A)
666
+ - [HTTP Integration](./http.md) — HTTP server and webhooks
667
+ - [Workflows](./workflows.md) — Reusable workflow definitions
668
+ - [Configuration](./configuration.md) — Configuration reference
669
+ - [Architecture](./architecture.md) — System architecture overview
670
+ - [Security](./security.md) — Security best practices
671
+ - [Debugging](./debugging.md) — Debugging techniques
672
+ - [RFC: A2A Protocol Support](../rfc/001-a2a-protocol-support.md) — Design document