ACP client GitHub issue

A Caspian client for the Agent Communication Protocol — REST/HTTP agent-to-agent interop.

vibecode
{"vibecode": {
    "doc": "acp_client",
    "role": "spec for puck.uno/ai/acp/client — a Caspian package that lets user code call out to agents speaking the ACP (Agent Communication Protocol) REST/HTTP standard. V1.0 covers call-and-response only; stateful sessions, multimodal input, async dispatch, streaming, and discovery are deferred.",
    "audience": ["Caspian programmers calling out to ACP-speaking agents",
        "implementers of the puck.uno/ai/acp/client package"],
    "namespace": "puck.uno/ai/acp/client",
    "protocol_external_spec": "https://agentcommunicationprotocol.dev",
    "v1_0_scope": ["call_and_response"],
    "deferred": ["stateful_sessions", "multimodal_input", "async_dispatch", "streaming", "agent_metadata_discovery"],
    "key_concepts": ["caspian_client_for_external_protocol",
        "call_and_response_only_in_v1",
        "acp_is_not_puckai"]
}}

puck.uno/ai/acp/client is a Caspian library that calls out to agents speaking the Agent Communication Protocol — a REST/HTTP standard for AI agent interop, governed by the Linux Foundation, with BeeAI as the reference framework.

ACP is not Puckai. Puckai is Puck's own structured-conversation format (documented separately); ACP is an external generic HTTP envelope for agent calls. The two occupy adjacent space at different abstraction levels and are independent — this package lets Caspian programs participate in the broader ACP ecosystem without committing to Puckai's structure.


V1.0 scope GitHub issue

The first cut covers only the simplest agent-call pattern:

Deferred for now (not in V1.0; will land when there's a real use case driving each):


API GitHub issue

Constructor GitHub issue

$client = %puck['https://puck.uno/ai/acp/client'].new(url: 'https://example.com/acp-agent')

The url: kwarg is the ACP endpoint to talk to. Other constructor kwargs (auth headers, defaults, timeouts) TBD per the ACP spec.

send GitHub issue

$reply = $client.send(input: 'What is the weather?')

Calls the ACP server with the input and returns the agent's reply, typed according to the response's content-type. No wrapper for known types — the return value is the parsed body.

Content-type dispatch GitHub issue

Content-Type Return value
text/* (text/plain, text/html, etc.) string
application/json parsed JSON — hash, array, string, number, boolean, null per the JSON structure
anything else a raw-response object carrying raw bytes + content-type

User code that expects a specific content-type from a specific agent can use the reply directly. Code uncertain about what it'll get can check $reply.class or (for raw-response objects) $reply.content_type and branch.

$reply = $client.send(input: 'hello')              # text/plain → $reply is a string
$reply = $client.send(input: 'give me weather data')   # application/json → $reply is a hash like {temp: 70, ...}
$reply = $client.send(input: 'send the chart')     # image/png → $reply is a raw-response object

Raw response (unknown content-types) GitHub issue

For content-types V1.0 doesn't know how to parse (binary, images, audio, anything not text/* or application/json), send returns a puck.uno/ai/acp/raw_response instance:

$reply = $client.send(input: 'send image')
$reply.body            # raw bytes
$reply.content_type    # the Content-Type header, e.g. 'image/png'

This keeps the V1.0 surface small while not silently mishandling responses outside its parsing scope — user code always gets back something usable, and can hand off raw bytes to whatever knows what to do with them.

puckai GitHub issue

A convenience method for talking to remote agents that speak Puckai. Wraps the Puckai worldlet round-trip — build, send, parse — in a single call.

$reply = $client.puckai(input: 'What is the weather?')
$reply # the final value (e.g. true, a string, a hash)

$reply = $client.puckai(input: 'What is the weather?', audit: true)
$reply # the full returned worldlet

$client.puckai(input: 'What is the weather?', log: true) # side effect: logs the worldlet

$client.puckai(input: 'What is the weather?', bootstrap: true) # cold-start receiver: includes Puckai bootstrap

Recognized kwargs:

All four kwargs compose. $client.puckai(input: '...', bootstrap: true, audit: true, log: true) sends a bootstrap-inclusive worldlet, returns the response worldlet, AND logs it.

Minimal bootstrap pointer is always sent GitHub issue

Even when bootstrap: false (the default), every outgoing Puckai worldlet includes a minimal instructions pointer in its top-level vibecode:

json
"vibecode": {
    "instructions": "This is an Puckai worldlet. Spec: https://puck.uno/ai/puckai/vibecode.json"
}

The instructions field is the most directive name available — a cold receiver reading the vibecode sees "do this" rather than passive metadata. The URL points at a standalone vibecode document (bare hash, no wrapper) the receiver can fetch and parse directly. See Puckai bootstrap § One source, two forms for the full design.

bootstrap: true is the opt-in for the full inline bootstrap content (the multi-key instructions hash from bootstrap.json) when network resolution isn't viable for the receiver.

The pointer merges cleanly with any caller-supplied vibecode (e.g. agent_guidance): both keys coexist at the same vibecode level.

What the call builds under the hood GitHub issue

puckai(input: '...') builds a default-shaped worldlet:

The worldlet is POSTed to the ACP endpoint as application/json. The response is parsed as an Puckai worldlet; the decision body is extracted from the single decision record and returned.

Limits of the convenience method GitHub issue

puckai is deliberately minimal. To configure Puckai parameters beyond inputexpects, confidence_floor, report, decider, multi-issue worldlets, anything in the full Puckai surface — build the worldlet explicitly via the Puckai API and send it through .send directly:

$worldlet = ... # build via Puckai helpers
$response = $client.send(input: $worldlet)   # raw send; response is the returned worldlet

If the remote agent doesn't speak Puckai (the response isn't a parseable Puckai worldlet), the call raises.


Implementation notes GitHub issue


Reading the ACP spec GitHub issue

ACP's authoritative spec lives at agentcommunicationprotocol.dev with the OpenAPI definition on GitHub. Implementation requires pinning:


See also GitHub issue


© 2026 Puck.uno