Metadata-Version: 2.4
Name: hcp
Version: 0.1.3
Summary: The Human-AI Collaboration Protocol (HCP)
Author: Yang Yu, Yijia Shao
Requires-Python: >=3.9
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: pydantic (>=2.1.0)
Requires-Dist: pytest (>=8.4.2)
Description-Content-Type: text/markdown

# Human-AI Collaboration Protocol (HCP)

**The HTTP between humans and AI.**

[![PyPI version](https://img.shields.io/pypi/v/hcp?color=brightgreen)](https://pypi.org/project/hcp/)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![Machine-checked spec (Lean 4)](https://img.shields.io/badge/Spec-Machine--checked_in_Lean_4-purple.svg)](https://github.com/yu-yang-i/HCP/blob/main/hcp_proof/HCP_Proof.lean)
[![Protocol 1.0](https://img.shields.io/badge/protocol-1.0_frozen-informational.svg)](docs/schema.md)

![HCP Architecture Diagram](https://raw.githubusercontent.com/yu-yang-i/HCP/main/assets/arc.jpg)

HCP is a wire protocol for structured interaction between an AI agent and a person. The agent describes the *shape* of the data it needs or wants to show — a range, a choice, a tagged blob, or a recursive record — and any client renders that shape however its substrate allows: a slider on the web, a dial in XR, an ASCII bar in a terminal. The person can always answer in plain text instead, and every exchange is logged with the agent's reasoning attached.

The conformance contract that makes this work is machine-checked in Lean 4: any client that meets it is, from the agent's side, indistinguishable from any other. See [`docs/math.md`](docs/math.md) for exactly what is and is not proven.

---

## 1. The Problem: Asymmetric Collaboration

Today's human-AI collaboration is asymmetric by construction: the human asks or commands, the AI responds. This worked while agents were limited. As they become capable, it becomes a ceiling.

The bottleneck is twofold. **Architecturally**, half of the collaboration action space is missing by design. **Communicatively**, everything is forced through a low-bandwidth, ambiguous channel: unstructured chat.

### A. Input ambiguity
When an agent needs a specific parameter — a confidence integer, a boolean confirmation — it has to ask in prose.

* **Agent:** "Please set the confidence level (0–100)."
* **User:** "Just make it safe."
* **Result:** the agent parses, guesses, or hallucinates.

### B. The missing reverse gear
Capable agents need the other direction: to **delegate** ("I need domain expertise on X"), to **request decisions** ("Speed or accuracy?"), to **suggest** ("Three approaches — try them in this order"). Today all of that goes through chat, which is ambiguous (the agent cannot control how the answer is formatted) and stateless (no way to say whether the ask is blocking or background, or to track whether it was resolved).

## 2. The Solution: Symmetric Data Topology

**HCP does not define UI.** It defines the *shape* of data. The agent specifies the container; the client decides how to render it.

Input and output are the same object — a **DataShape** — and a `Direction` says whether the agent is reading (`REQUEST`) or writing (`OFFER`). The same shape that collects a value can display one.

### The four shapes

| Shape | What it is | Web (example) | XR (example) |
| :--- | :--- | :--- | :--- |
| **Discrete** | Selection from a finite set | Dropdown / Radio / Badge | Floating orbs |
| **Continuous** | Magnitude within a range | Slider / Gauge | Dial / Throttle |
| **Symbolic** | Opaque payload, tagged by media type | Text field / File upload | Dictation / Hologram |
| **Composite** | Recursive record of shapes | Form / Dashboard | Control panel |

Because HCP defines data rather than widgets, the agent is substrate-independent. The same Python that requests a "vector field" can surface as a file uploader in a browser, a holographic cube in a VR engine, or an input on hardware that does not exist yet — provided the client meets HCP's two conformance obligations, *soundness* (nothing invalid reaches the agent) and *completeness* (no valid answer is unreachable). That guarantee is a theorem, not a hope; §7 says what it covers.

**The agent is the director; the client is the renderer.**

## 3. How HCP relates to prior work

HCP is not a replacement for MCP. MCP connects agents to *tools*; HCP connects agents to *people* — and HCP ships as an MCP server, so the bridge between the two is one line of configuration.

Within human-facing protocols, HCP sits in the schema-only lineage — XForms, JSON-Schema forms, MCP elicitation: "send a schema, render it how you like" — rather than the component-catalog lineage (Adaptive Cards, A2UI). What it adds is the half that lineage never wrote down: a definition of what rendering a schema *correctly* means, a proof that meeting it is sufficient, the reverse direction, the agent's reasoning travelling with the request, and the outcome logged as a first-class record.

### Why not just A2UI?

Unlike A2UI, HCP does not draw streamed UI inside an app's design system. It defines data shapes, and four kinds of robustness follow from that choice:

* **Type safety.** A2UI validates the UI *structure* against a catalog schema; the person's *answer* travels back as an app-defined event, outside the protocol. HCP defines what a valid answer is for every shape and checks it on both sides — soundness and completeness, machine-checked in Lean 4. Nothing invalid reaches the agent.
* **Cross-device.** A2UI adapts the agent to the device: the renderer advertises its catalog, the agent generates against it, and an unresolved component is an error, not a fallback. HCP adapts the device to the agent: four shapes, no catalog, and a primitive fallback that is proven to conform — so a terminal, a headset, or a client that does not exist yet needs nothing written for it.
* **Future-proof.** UI vocabularies churn — `Card`, `Row`, `Modal` are this decade's screen language — and every widget toolkit that named *how to show* has been replaced. The categories of an *answer* have not: finite set, ordered continuum, opaque content, product of these. XForms (2003), MCP elicitation (2025), and A2UI's own input components all land on them. HCP standardizes those and nothing else.
* **Rich UI without a catalog.** A2UI's expressiveness ends at its catalog, and it forbids code over the wire by design. In HCP an agent can ship a renderer *as content*: a `SYMBOLIC` `text/html` leaf beside the typed leaves it renders. The same message is a live, interactive 3D scene on a browser or headset and three numeric prompts on a terminal — and the interaction telemetry comes back as an ordinary result. The host validates what the renderer returns and runs it with an opaque origin ([cookbook, Pattern 6](docs/cookbook.md)). Where A2UI's ceiling is the catalog, HCP's ceiling is the client.

They compose: an A2UI renderer is a natural HCP client (CONTINUOUS → Slider, DISCRETE → ChoicePicker, COMPOSITE → a Column of fields, an A2UI `action` → an HCP result). HCP says what is being asked and what counts as an answer; on a screen, A2UI says how it looks — or the agent does.

| | Between | Defines | Rendered by | If the person wants to say something else | Correctness contract |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **MCP elicitation** | Agent (server) → person, via the client | A flat JSON-Schema form | Client's choice | Accept / decline / cancel; no text in the result | — |
| **A2UI** (Google, v0.9 / 1.0 RC) | Agent → UI, with data sync and function calls back | A component tree from the client's own catalog, streamed | Catalog components (Flutter, React, Lit, Angular) | App-defined | — |
| **AG-UI** (CopilotKit) | Agent ↔ frontend | An event stream (state, messages, UI events) | App's components | Chat alongside | — |
| **Adaptive Cards** (Microsoft) | App → host | Card components with declared fallback | Host renderer | App-defined | — |
| **HCP** | Agent ↔ person, both directions | Four recursive data shapes; no widgets | Any conforming substrate | Reject always carries the person's text | Soundness + completeness, machine-checked |

The full comparison, with citations, is in the paper. This table is only meant to answer the first question a reader has.

## 4. Installation

```bash
pip install hcp
```

## 5. Usage: The Python SDK

HCP is a **mixin**. You do not rewrite your agent; you inherit `HCPMixin` and it can speak shapes instead of only text. The mixin keeps one piece of state — the registry of outstanding interactions — so that every answer is checked against the shape that was actually sent.

### A. Architectural flexibility

HCP is an interface protocol, not an execution pipeline. The SDK translates agent intent into the wire protocol; how the intent is generated is up to you.

* **Pattern A — Single-pass.** Inject `HCP_PORTAL_PROMPT` into the primary agent's system prompt; it emits HCP JSON natively. *Low-latency copilots, strong foundation models.*
* **Pattern B — Lazy-loading.** The primary agent calls a lightweight tool that triggers a secondary, isolated LLM call to generate the interaction. *Complex reasoning agents with tight token budgets.*
* **Pattern C — Native fine-tuning.** A model fine-tuned on the Co-Op Memory emits HCP JSON without a prompt.
* **Pattern D — Tool-call adapter.** `HCP_Request` / `HCP_Offer` as tools: declare them (`hcp_tools(format=...)`, any runtime format) or *serve* them — `hcp.mcp_server.HCPMCPServer` makes HCP an MCP server, so any MCP-capable agent adopts it by adding one server. Same validation and Co-Op Memory as Pattern A, no prompt. *Recommended whenever tool calling is available.*

### B. Inherit the mixin

```python
from hcp import HCPMixin, HCP_PORTAL_PROMPT

class BaseAgent(HCPMixin):
    # ... agent reasoning logic ...
```

### C. The forward pass (intent → protocol)

Pass the LLM's raw output to `hcp_forward()`. It strips markdown fences, validates the schema, inflates the objects, registers the interaction, and logs the proposal.

```python
    async def step(self, user_input):
        raw_llm_output = await self.llm.generate(user_input)   # Pattern A, B, or C
        try:
            hcp_payload = self.hcp_forward(raw_llm_output)     # str -> validated JSON-RPC message
            self.send_message(content=None, metadata={"hcp": hcp_payload})
        except ValueError as e:
            print(f"HCP Error: {e}")                           # hallucinated schema, min > max, ... -> fall back to text
```

Only well-formed shapes leave the agent: a paradoxical range (`min > max`), a default outside its own shape, or a malformed composite field raises instead of being forwarded. When your *code* — not the LLM — decides the interaction, call the typed helpers directly (`hcp_request_continuous`, `hcp_offer_symbolic`, …); both paths share one wrapper, so the wire format, registry, and log are identical.

### D. Pattern B: lazy-loading

Add a trigger to the main agent's system prompt:

```text
- Enable_HCP(context_summary, detailed_intention, detailed_next_step): Use this ONLY if you need to interact with the User.
```

Then run the micro-inference:

```python
    async def execute_hcp(self, context_summary, detailed_intention, detailed_next_step):
        user_msg = (f"Context: {context_summary}\n"
                    f"Intention: {detailed_intention}\n"
                    f"Next Step: {detailed_next_step}\n")
        raw_llm_output = await self.llm.generate(HCP_PORTAL_PROMPT, user_msg)
        try:
            hcp_payload = self.hcp_forward(raw_llm_output)
            self.send_message(content=None, metadata={"hcp": hcp_payload})
        except ValueError as e:
            print(f"HCP Error: {e}")
```

### E. The feedback loop (protocol → Co-Op Memory)

When the client answers, `hcp_resolve` enforces the agent's half of the contract and logs the outcome.

```python
    def on_incoming_message(self, message):
        metadata = getattr(message, "metadata", {}) or {}

        if "hcp_response" in metadata:
            raw_payload = metadata["hcp_response"]              # a result, or an error response
        else:
            # Free text is a USER-INITIATED atom: fresh refId; it rejects every
            # outstanding blocking request, and the SDK records which ones.
            raw_payload = self.hcp_wrap_chat(message.content, message_id=message.id)

        data, used_proposal = self.hcp_resolve(raw_payload)

        if used_proposal is None:
            return                                              # system outcome (unrenderable / expired / cancelled): logged, nothing to consume
        if used_proposal:
            self.memory.add(f"[HCP] User accepted: {data}")     # validated against the requested shape
        else:
            ...                                                 # the person's text: handle it as chat
```

What `hcp_resolve` guarantees:

* **Soundness.** An accepted value is checked against the shape that was sent, using `hcp.validity.is_value_valid` — a line-by-line transliteration of the Lean predicate. A value that does not fit never reaches your context. The agent does not trust the client.
* **The escape hatch.** A rejection must carry the person's text.
* **SYMBOLIC narrowing.** SYMBOLIC values are type-*tagged*, not type-checked. Register a codec per media type — `hcp_register_codec("application/json", is_valid_json)` — and it runs inside `hcp_resolve`.
* **Lifecycle.** Every message with an `id` is resolved exactly once — by a result (the person's verdict) or an error (`UNRENDERABLE`, `EXPIRED`, `CANCELLED`, never counted as a rejection). `hcp_cancel(refId)` withdraws a stale offer. Offers that expect nothing back (`expects_result=False`) are notifications and are not logged.

The wire protocol is specified in [`docs/schema.md`](docs/schema.md); the SDK reference is [`docs/SDK.md`](docs/SDK.md).

### F. The Co-Op Memory hook

Override the hook to persist the log.

```python
    def hcp_on_log(self, log_entry):
        """
        Invoked at emission (actor "Agent"), on every result (actor "User"),
        and on every system outcome (actor "System").

        {
            "timestamp": "iso-date...",
            "actor": "Agent" | "User" | "System",
            "payload": <the wire message verbatim>,
            "outcome": "UNRENDERABLE" | "EXPIRED" | "CANCELLED"   # system outcomes only
        }
        Join Agent and User entries on refId; a user-initiated text lists the
        requests it rejected under payload.result.context.resolves.
        """
        MyDatabase.save(log_entry)
```

## 6. Why HCP

### A. Elastic fallback

Shapes are recursive, so complex structures — a physics configuration, a medical intake, a robot pose — are composites of simpler shapes, and a client that cannot render the whole can always render the parts.

**The "3D gizmo" problem.** An agent requests a 3D vector (X, Y, Z) to position a robotic arm.

1. **A high-fidelity client** (Apple Vision Pro) recognises the group and renders a holographic manipulation gizmo.
2. **A low-fidelity client** (a text terminal) has no gizmo. In a component-catalog protocol, that is a render failure.
3. **An HCP client** decomposes the composite until it reaches shapes it can render:

   * Web: three sliders.
   * XR: three floating dials.
   * Terminal: three ASCII sliders driven by arrow keys.

     `X-Axis: [<-- ███░░░░░░░ -->] 30%`
     `Y-Axis: [<-- ██████░░░░ -->] 60%`
     `Z-Axis: [<-- ████░░░░░░ -->] 40%`

The agent gets its (X, Y, Z) on every substrate, and type safety survives the fallback: even in the terminal the person cannot type "hello" into a `0.0–1.0` range. The fallback is principled, not best-effort — the primitive one-widget-per-shape substrate is itself proven to conform.

### B. Co-Op Memory

Cursor's Tab learns from whether you press Tab or keep typing: *act + outcome*. HCP records more. Every proposal carries the agent's **rationale**; every rejection carries the person's **text**. Each record is a complete tuple — reason, act, outcome, and (on rejection) the correction:

* A **veto**: "wipe `/var/logs`" → "No. Those logs are needed for legal compliance."
* A **style correction**: a functional refactor offered → the person types a class instead.
* An **abstraction mismatch**: "how many retries?" → "I don't know the number; pick one that's safe."

`usedProposal: false` alone would conflate all three. The text tells them apart, so a capable model can derive labels from the record alone — no separate labeling pass. The derivation is still inference, not ground truth; [`docs/log-example.md`](docs/log-example.md) walks through what the records do and do not tell you.

## 7. Formal specification

The conformance contract and its consequences are stated and machine-checked in [`hcp_proof/HCP_Proof.lean`](hcp_proof/HCP_Proof.lean) (Lean 4.20, core only, no `sorry`; the transfer theorems depend on no axioms at all).

**Proven:** the validity relation for all four shapes; that soundness + completeness imply the agent cannot distinguish one conforming client from another (`substrate_indistinguishable`); that a primitive substrate conforms, so a universal fallback exists; that two natural-looking clients — a trapping dropdown, a lenient text box — *fail* the contract, so the obligations bite; and that none of it depends on the numeric representation (`Float`, `Int`, `Nat` are all instances).

**Not proven:** that any particular real client conforms (each client's obligation, discharged by the conformance suite in `tests/`); anything about the *content* of SYMBOLIC payloads (only the tag); anything about rendering, layout, or timing.

[`docs/math.md`](docs/math.md) explains the development section by section.

## 8. Advanced scenarios

Because HCP decouples parameters from rendering, it can drive a physics engine or an XR simulation without the agent knowing graphics programming. The [cookbook](docs/cookbook.md) has five patterns — Tab-style ghost text, a fluid simulation, an AR assembly guide, a SYMBOLIC-free 6-DOF robot arm, and Designer-style option galleries. One excerpt:

### The robotic arm (kinetic alignment)

In spatial computing, "acceptance" is not a click. The person's hand has to match the agent's proposed motion in space and time.

* **Scenario:** AR glasses showing a mechanic how to install a component.
* **Mechanism:** an `OFFER` — a volumetric ghost hand waiting to be overlapped.
* **Acceptance:** the person's tracked hand synchronises with the ghost's skeletal pose.

```python
class IndustrialGuide(HCPMixin):

    def show_assembly_step(self):
        frames = [...]  # 2 s of skeletal keyframes: position, wrist quaternion, grip
        return self.hcp_offer_symbolic(
            rationale="Guide user to insert Servo Motor A with specific torque.",
            label="Sync with Ghost Hand",
            mimeType="application/x-skeletal-pose-stream+json",
            value={"target_object": "servo_motor_a", "animation_clip": frames, "tolerance_threshold": 0.95},
        )
```

* **Alignment:** the person shadows the ghost hand; joint distance stays near zero → `usedProposal: true`.
* **Deviation:** the person rotates their wrist 90° and reaches for a screwdriver → `usedProposal: false`, value `{ "detected_tool": "screwdriver", "hand_pose": [...] }`. The agent proposed a push; the person corrected it to a screw. **HCP captures the physical correction of the workflow.**

## 9. Repository

```
hcp/          the package            docs/         intro · concepts · architecture · schema · cookbook · log-example · math · SDK
tests/        conformance suite      hcp_proof/    HCP_Proof.lean
```

```bash
python -m pytest tests -q          # 56 tests: the Lean case matrix plus every SDK enforcement path
lean hcp_proof/HCP_Proof.lean      # Lean 4.20; no output means every theorem checked
```

If you build a client, `tests/test_validity.py` is your conformance oracle: everything your client returns as accepted must satisfy `is_value_valid`, and everything `is_value_valid` accepts must be reachable in your UI.

Protocol version **1.0** is frozen. Within 1.x, receivers ignore unknown fields and new `hcp/*` methods; nothing existing changes. The versioning rules are in [`docs/schema.md`](docs/schema.md) §3.

## 10. So What?

* **Researchers** get a high-fidelity record of human-AI interaction — reason, act, outcome, correction — in one format.
* **Developers** get online-learning signal from real interactions, and a contract that makes the agent's guarantees independent of the frontend.
* **Founders** deploy the same agent into any conforming client — CLI, Blender, spatial computing, a brain-computer interface — without touching the backend.

**HCP is not designed just for this era.**

The agent you write today runs unchanged on any conforming client — including the ones that do not exist yet.
