A2A Agent Mode
cliq can operate as an A2A (Agent-to-Agent) agent. When enabled, other AI systems — planners, orchestrators, mesh routers — can discover what cliq can do, send it a requirement in natural language, and receive structured results when the pipeline completes. cliq assembles the right team, orchestrates the full pipeline, and returns the outcome. No human in the loop (unless a HUG gate is involved).
This turns cliq into a callable capability in a larger agent ecosystem.
Enabling A2A
Section titled “Enabling A2A”A2A is off by default. Pass --a2a when starting the server:
cliq server start --a2aThis does three things:
- Mounts the agent card at
GET /.well-known/agent-card.json— a standard discovery endpoint that describes cliq’s capabilities - Mounts the JSON-RPC endpoint at
POST /a2a/jsonrpc— where calling agents send tasks - Joins configured meshes — registers with any mesh networks marked
active: truein settings
Without --a2a, none of these exist. The server runs as a plain local API.
To join a specific mesh:
cliq server start --a2a --mesh prodcliq server start --a2a --mesh prod --mesh stagingThe --mesh flag implies --a2a. You can repeat it to join multiple meshes.
Configuration
Section titled “Configuration”Add a server.a2a section to ~/.cliqrc/settings.json:
{ "server": { "port": 4100, "a2a": { "agent_type": "cliq_pipeline", "instance_id": "cliq-macbook-01", "workspace_root": "/tmp/cliq-workspaces", "meshes": [ { "name": "prod", "type": "savant", "active": true, "url": "https://savant.example.com", "client_id": "cliq-agent", "client_secret": "your-secret-here" } ] } }}| Field | Type | Description |
|---|---|---|
server.port | number | HTTP port for the cliq server. Default: 4100. |
server.a2a.agent_type | string | Logical agent type for mesh routing. Shared across all your cliq instances. |
server.a2a.instance_id | string | Unique identifier for this cliq instance. Must be globally unique. |
server.a2a.workspace_root | string | Directory where temporary task workspaces are created. Cleaned up after each task. |
server.a2a.meshes | array | Mesh networks to join. See below. |
server.a2a.advertise | string[] | Which teams to include in the agent card. Default: ["@cliq"]. See Advertising Teams. |
server.a2a.enable_builder | boolean | Expose builder capabilities (build_team, improve_role, validate_team). Default: false. |
Mesh configuration
Section titled “Mesh configuration”| Field | Type | Description |
|---|---|---|
name | string | Human-readable name (used with --mesh flag) |
type | string | Mesh protocol. Currently "savant" is supported. |
active | boolean | Join this mesh when --a2a is used without --mesh |
url | string | Mesh server URL |
client_id | string | Authentication client ID |
client_secret | string | Authentication client secret |
The Agent Card
Section titled “The Agent Card”When A2A is enabled, GET /.well-known/agent-card.json returns a standard A2A agent card. Calling agents use this to discover what cliq can do before sending tasks.
The card is built dynamically from your installed teams. Each team’s top-level metadata — description, use_when, not_for, tags, and inputs — is used to build a skill entry in the agent card.
The skill description is composed from the team’s description, use_when/not_for hints, and inputs. For example, given a team like:
name: "@local/feature-pipeline"description: "Feature development with git lifecycle for JS/TS projects"tags: [code, git, tdd]
use_when: - The task requires code changes delivered as a pull request
inputs: - name: target_branch description: "Branch name for the feature"the agent card would include a skill description like:
Feature development with git lifecycle for JS/TS projects. Use when: The task requires code changes delivered as a pull request. Required parameters (provide as a JSON data part): target_branch — Branch name for the feature.
See Build from Scratch — A2A Metadata for a walkthrough.
Advertising teams
Section titled “Advertising teams”The advertise setting controls which teams appear in the agent card. Teams should include description, use_when, and not_for fields for effective A2A discovery.
| Pattern | Matches |
|---|---|
"@" | All teams in all scopes |
"@cliq" | All @cliq teams |
"@local" | All @local teams |
"@cliq/hello-world" | A specific team |
Prefix with ! to exclude:
| Pattern | Excludes |
|---|---|
"!@local" | All @local teams |
"!@cliq/hello-world" | A specific team |
Evaluation: union all include patterns, subtract all exclude patterns. If no include patterns are present, all teams are included (as if "@" were specified).
{ "advertise": ["@cliq"] }{ "advertise": ["@local", "@cliq/feature-dev-js"] }{ "advertise": ["@cliq", "!@cliq/hello-world"] }{ "advertise": [] }System capabilities (builder) are controlled separately via enable_builder, not advertise.
Invoking a Pipeline
Section titled “Invoking a Pipeline”A calling agent sends a task via JSON-RPC to POST /a2a/jsonrpc. The message follows a convention of text + data parts:
- Message text — the requirement spec / task description. This is what the team works on. It becomes the
requirement.mdthat drives the pipeline. - Data part (JSON) — contains
team, optionalrepos, and teaminputs. Only theteamfield is required;inputsis only needed when the team declares inputs.
The agent card’s skill description tells callers what to send based on the team’s inputs fields.
Task vs. inputs
Section titled “Task vs. inputs”These are separate concepts:
- Task (message text) — the requirement the team works on. Sent as the message text in the JSON-RPC request.
- Inputs (data part) — template parameters that configure the workflow (e.g. branch names, folder IDs). Declared in
inputsand referenced via$(inputs.key).
When no message text is provided, the team runs in nospec mode — it executes autonomously with a generic task.
Team without inputs
Section titled “Team without inputs”{ "jsonrpc": "2.0", "method": "message/send", "id": "req-1", "params": { "message": { "role": "user", "parts": [ { "kind": "text", "text": "Write a blog post about the future of AI coordination" }, { "kind": "data", "data": { "team": "@local/blog-pipeline", "repos": "https://github.com/org/content.git" } } ] } }}Team with inputs
Section titled “Team with inputs”When the team declares inputs (e.g. target_branch), pass them in the data part:
{ "jsonrpc": "2.0", "method": "message/send", "id": "req-2", "params": { "message": { "role": "user", "parts": [ { "kind": "text", "text": "Build a login page with email/password auth" }, { "kind": "data", "data": { "team": "@cliq/feature-dev-js", "repos": "https://github.com/org/webapp.git", "inputs": { "target_branch": "feature/login" } } } ] } }}If required inputs are missing, the executor responds with an error listing the missing parameters.
Data part fields
Section titled “Data part fields”| Field | Required | Description |
|---|---|---|
team | Yes | Team to invoke (@scope/name) |
repos | No | Comma-separated git repo URLs to clone into the workspace |
inputs | No | JSON object of team input values (keys must match declared input names) |
cliq resolves the team, creates an isolated workspace under workspace_root, runs cliq init → cliq assemble → cliq req → cliq run, and returns the result.
Task Lifecycle
Section titled “Task Lifecycle”Every A2A task moves through these states:
- Submitted — task received, workspace being created
- Working — pipeline running
- Completed — pipeline finished successfully
- Failed — pipeline encountered an error or escalated
- Cancelled — cancelled by request
Each task gets an isolated workspace. The workspace is cleaned up after the task reaches a terminal state.
Polling and cancellation
Section titled “Polling and cancellation”Use standard A2A methods — no special capabilities needed:
tasks/get— current state of any task, including phase progress and artifactstasks/cancel— cancels a running pipeline; the orchestrator terminates agents and exits cleanly
Execution backends
Section titled “Execution backends”Pipelines dispatched via A2A can run on three backends, selected automatically from settings:
| Backend | Setting | Description |
|---|---|---|
| Local | default | Runs directly on the host |
| Docker | docker.enabled: true | Each task runs in an isolated container with the workspace bind-mounted |
| Kubernetes | kubernetes.enabled: true | Tasks run as K8s Jobs with shared PVC storage (requires PostgreSQL) |
Kubernetes takes highest precedence, then Docker, then local. See Docker Isolation and Kubernetes Deployment.
Real-time Streaming
Section titled “Real-time Streaming”Use message/sendSubscribe instead of message/send to receive Server-Sent Events (SSE) as the pipeline runs:
data: {"kind":"status-update","status":{"state":"working","message":{"parts":[{"kind":"text","text":"Pipeline started — team: blog-pipeline"}]}}}
data: {"kind":"status-update","status":{"state":"working","message":{"parts":[{"kind":"text","text":"Running: researcher"}]}}}
data: {"kind":"status-update","status":{"state":"working","message":{"parts":[{"kind":"text","text":"Phase completed: researcher"},{"kind":"data","data":{"event":"on_phase_complete","phase":"researcher","phase_type":"standard"}}]}}}
data: {"kind":"status-update","status":{"state":"completed","message":{"parts":[{"kind":"text","text":"Pipeline completed"}]}}}
data: {"kind":"artifact-update","artifact":{"name":"pipeline_result","parts":[{"kind":"data","data":{"status":"completed","phases":[...]}}]}}Every update has a human-readable TextPart. Most also include a machine-readable DataPart with structured context so calling agents can programmatically track progress.
Event types
Section titled “Event types”| Event | When | DataPart fields |
|---|---|---|
| Pipeline started | Task accepted | — |
| Workspace initialized | After cliq init | event, instance_id, team, req_key |
| Phase started | Phase begins | event, phase, phase_type, instance_id |
| Phase output | Periodic agent output flush | event, phase, offset, lines |
| Phase completed | Phase finishes | event, phase, phase_type, outcome |
| Gate verdict | Gate command completes | event, gate, iteration, max_iterations, commands, verdict |
| Pipeline escalated | Human intervention needed | event, phase, reason |
| Pipeline completed | All phases finished | — (terminal) |
| Pipeline cancelled | Cancelled by request | event, reason |
Phase output streaming
Section titled “Phase output streaming”When server.workflow_streaming is enabled in settings, the orchestrator flushes each agent’s terminal output as phase_output events. Output is formatted into:
lines— human-readable text (assistant messages, tool calls as→ Read path, completions as✓ N lines)events(optional) — raw structured JSON objects for programmatic analysis
The offset field tracks byte position for gap detection.
Preflight events
Section titled “Preflight events”Before the pipeline starts, the orchestrator verifies required tools. These appear as structured events:
{"type":"cliq:preflight_started","tools":["git","node"]}{"type":"cliq:tool_check","tool":"git","available":true}{"type":"cliq:tool_check","tool":"node","available":true}{"type":"cliq:preflight_completed","passed":2,"total":2,"verdict":"PASS"}If preflight fails, the pipeline does not start.
Command events
Section titled “Command events”During gate and exec phases, the orchestrator emits command results:
{"type":"cliq:command_started","command":"output-exists","phase":"quality-gate"}{"type":"cliq:command_completed","command":"output-exists","pass":true,"duration_ms":50,"exit_code":0}{"type":"cliq:commands_summary","phase":"quality-gate","passed":4,"total":4,"verdict":"PASS"}The verdict field reflects the outcome: PASS (all passed), CONTINUE (some failed, iterations remain), or ESCALATE (failed on final iteration).
Final artifact
Section titled “Final artifact”When the pipeline reaches a terminal state, cliq publishes a TaskArtifactUpdateEvent containing a post-mortem summary:
| Field | Always present | Description |
|---|---|---|
status | Yes | completed, failed, or cancelled |
instance_id | Yes | cliq instance ID |
phases | When phases ran | Phase-by-phase status array |
gate_verdicts | When gates ran | All gate verdicts with command results |
orchestrator_log | When log exists | Last 10KB of orchestrator.log |
escalation | On failure | Phase and reason |
How events flow internally
Section titled “How events flow internally”The orchestrator runs in tmux and writes lifecycle events to .cliq/signals/_a2a_events as JSON Lines. The server process polls this file and relays events to the calling agent over the SSE stream. This is automatic for all A2A-initiated pipelines.
Push Notifications
Section titled “Push Notifications”For fully asynchronous operation, calling agents can submit a task and receive updates via HTTP callbacks instead of holding an SSE connection open:
{ "jsonrpc": "2.0", "method": "message/send", "id": "req-1", "params": { "message": { "role": "user", "parts": [ { "kind": "text", "text": "Write a blog post about teamwork" }, { "kind": "data", "data": { "team": "@local/blog-pipeline" } } ] }, "configuration": { "pushNotificationConfig": { "url": "https://caller.example.com/a2a/notify", "token": "my-callback-token" } } }}The server acknowledges immediately with submitted state, then pushes status updates to the callback URL as the pipeline progresses. Push configs survive server restarts (persisted in SQLite). The agent card advertises pushNotifications: true.
Mesh Integration
Section titled “Mesh Integration”When using a Savant mesh, the lifecycle is:
- Authenticate — exchange
client_id/client_secretfor a JWT viaPOST /auth/token - Register —
POST /agents/registerwith the agent card URL and full agent card object - Receive tasks — the mesh routes tasks to
POST /a2a/jsonrpcbased on capability matching - Deregister —
POST /agents/deregisteron graceful shutdown
The agent card includes enough detail for the mesh router to intelligently match tasks to cliq — team descriptions, use_when / not_for hints, tags, and skill definitions.
System Capabilities
Section titled “System Capabilities”Beyond team pipelines, cliq can expose operational capabilities when enable_builder: true:
build_team
Section titled “build_team”Generate a complete team from a natural language description. Returns roles, workflow DAG, gate commands, and A2A metadata.
| Parameter | Required | Description |
|---|---|---|
intent | Yes | Natural language description of the desired team |
auto_install | No | If true, install to @local |
improve_role
Section titled “improve_role”Improve a single role prompt using AI analysis.
| Parameter | Required | Description |
|---|---|---|
role_name | Yes | Role to improve |
role_content | Yes | Current markdown content |
team_name | Yes | Owning team name |
instruction | No | Specific improvement direction |
validate_team
Section titled “validate_team”Validate a team definition for structural correctness (no LLM needed).
| Parameter | Required | Description |
|---|---|---|
team_json | Yes | JSON-encoded team definition |
Response: { valid, errors, warnings }
Persistence and Recovery
Section titled “Persistence and Recovery”All task state is persisted in ~/.cliqrc/cliq.db (SQLite, WAL mode). This provides:
- Crash recovery — if the server crashes, in-flight tasks are marked as failed and workspaces are cleaned up on restart
- Task retention — terminal tasks remain queryable for 7 days, pruned on server shutdown
- Push notification durability — callback configs survive restarts
For multi-replica deployments, switch to PostgreSQL via database.url in settings.
Troubleshooting
Section titled “Troubleshooting”| Issue | Fix |
|---|---|
| ”No A2A configuration found” | Add a server.a2a section to ~/.cliqrc/settings.json |
| ”Failed to register with mesh” | Check mesh URL, credentials, and network connectivity |
| Agent card not served | Ensure --a2a was passed to cliq server start |
| Task stuck in “working” | Check workspace for orchestrator logs. Cancel and retry. |
| Tasks lost after crash | Tasks are in SQLite. On restart, in-flight tasks are marked failed. Query tasks/get. |
| Push notifications not received | Ensure callback URL is reachable. Check server logs for delivery errors. |