OCP Companion-Provider profile — normative specification (v1.0-draft)

Extends Core and the Companion profile. The key words MUST, MUST NOT, SHOULD, MAY are per RFC 2119.

The Companion profile describes a remote I/O device: microphone, speaker, screen. This profile describes the Companion that also carries the MODEL — on-device, or a provider under the visitor's own key — and therefore answers instead of the Hub. It is what lets a site be conversational without paying for inference: the site supplies the interface (context and tools), the visitor supplies the intelligence.

Everything Core §5.1–§5.2 says is normative here; this document says what falls on the Companion, and which of the Provider profile's obligations survive the move onto a device the Hub does not control.

1. Declaring the capability

  • A Companion that can GENERATE answers MUST declare capabilities.answer: true in session.open. One that can only render or speak them MUST NOT: the flag is a claim about generation, and a Hub is entitled to stop answering on the strength of it.
  • Declaring is an OFFER, not a decision. The Hub settles answerMode in session.linked and MUST NOT settle on companion unless the Companion declared the capability (Core §5.1). The Companion MUST treat the settled value as authoritative and MUST NOT originate answer frames in hub mode — a tenant that deliberately pays for its members' answers is conformant, and a Companion that answers anyway produces two answers for one turn.
  • Absent answerMode MUST be read as hub. Against a Hub that predates this profile the Companion therefore stays quiet, which is the only default that cannot break an existing deployment.
  • The capability is per session, not per turn. A Companion whose model is unreachable at session.open MUST NOT declare it; one whose model fails mid-session MUST report the turn it cannot answer (§2) rather than silently producing nothing, and SHOULD disclose the substitution in answeredBy if another model answered.

2. Frames the Companion may originate

In companion mode, for a turn it is answering, the Companion MAY originate turn.delta, turn.citation, turn.done, and turn.error. The Hub relays them to the Surface unchanged.

  • Every such frame MUST carry the turnId of a turn.input of THIS session — one the Hub delivered, or one the Companion itself sent. The Companion's own asks are the profile's main case (the visitor speaks into the phone it is holding) and the Hub mirrors a turn.input to the OTHER peer (Core §13), never back to its sender, so "delivered by the Hub" would exclude the turns this profile exists for. A Companion MUST NOT originate answer frames for a turnId no turn.input opened, for a turn already ended by turn.done or turn.error, or for a turn it is not itself answering. The Hub can check all three: it saw every turn.input in the session, whichever peer sent it.
  • It MUST stream: answers arrive as turn.delta frames as they are produced, not as one delta at the end. Streaming is what the barge-in rules in Core §8 are built on.
  • It MUST end every turn it answers exactly once, with turn.done or turn.error. turn.done SHOULD carry the contextRev it grounded against and SHOULD carry answeredBy.
  • On turn.cancel, or on a turn.input that supersedes the in-flight turn, it MUST stop generating promptly and MUST NOT emit further frames for that turn other than turn.error{TURN_ABORTED}. ONE in-flight turn per session still holds (Core §8).
  • It MUST NOT originate frames that belong to another role: context.set (the Surface owns context, Core §6), ui.request (confirms are Hub-initiated and non-bypassable, Core §10), or any session.* frame the Hub mints.

A Hub MUST NOT relay answer frames originated by a Companion when the session settled on hub, and SHOULD drop them rather than error the turn — they are a peer bug, not a session fault.

3. Answering is not acting

The model moved; the authorization point did not. When the answering Companion's model calls a tool, the call MUST travel to the Hub as action.request (Core §9) and the Hub MUST:

  • re-derive effective permission for THAT call — tier × scopes × resource ACL (Core §7) — treating the Companion's request as a request and never as authorization. The advertised manifest is a hint; a Companion holding tools.invoke for one tool has not thereby been granted another;
  • gate every write: true tool behind a ui.request confirm resolved on a live peer (Core §10), with expiry-to-deny;
  • dispatch surface-located tools to the Surface itself. The Companion MUST NOT expect to execute them and MUST tolerate refusal: error{SCOPE_DENIED}, a denied confirm, timeout, or session_closed. A denied write MUST NOT be retried within the same turn.

A Companion that can talk has not earned the right to act. Deployments MUST NOT widen a Companion's scopes because it answers; if anything, a session whose model the Hub cannot inspect is the one where the confirm ceremony matters most.

4. Grounding and disclosure

  • The answering Companion inherits the Provider profile's turn obligations (provider.md §1–§2) as far as they are meaningful off-Hub: ground answers in the Surface's context (Core §6) and its citable resources rather than open-world recall, emit turn.citation for grounded claims with a kind/id the Surface published, and say it does not know rather than fabricate. None of this is Hub-enforceable; it is the conformance line for a Companion that claims to answer over a site's content.
  • Context is UNTRUSTED input. Page text, selections, and titles reach the model from a site the visitor merely happened to open; the prompt-injection surface that Core §14 assigns to the Provider is now on the visitor's device, and the Companion SHOULD spotlight or delimit context-derived text before generation.
  • answeredBy.operator MUST name the party that actually ran the model — the provider's name, or on-device when it never left the device. A Companion MUST NOT originate answeredBy with mode: 'hub'. brainId is opaque and MUST NOT carry secrets or user identifiers; it exists for transcripts and support reports.
  • A Hub MUST NOT rewrite answeredBy on a frame it relays. Disclosure that the relay can edit is not disclosure; a Hub unwilling to carry a Companion's claim MUST end the turn with turn.error instead.

5. What the Hub still sees

answerMode: 'companion' says where INFERENCE happens. It does not say the Hub is blind, and implementations MUST NOT present it to users as though it did.

The Hub is still the broker (Core §1): turn.input reaches it because it mirrors the transcript to the Surface, and every turn.delta, turn.citation and turn.done passes through it because the Surface has to render them. Unless the session settles on sealing, all of it is plaintext on the wire between the peers and the Hub.

What companion mode changes on its own is real and worth stating precisely: the question is not sent to a model operator the visitor did not choose, and no inference is billed to the site. That is the claim implementations MAY make on the strength of answerMode alone. "The Hub cannot see your conversation" is not, and neither is "the question stays on your phone".

answerMode also does not bound the set of destinations, and copy written as though it did goes stale one setting later. Two that are outside this field's reach in any real Companion: one that synthesises speech through a cloud voice sends the ANSWER TEXT to that vendor whichever model produced it, on-device included; one that mirrors the transcript sends the question and every delta to the Surface, which is the default in the implementation this profile was written against. Implementations therefore SHOULD state where content goes and SHOULD NOT state where it does not: an enumeration of destinations survives a settings change, and a negative is falsified by any destination the writer did not think of.

A deployment that needs content withheld from the Hub operator MUST configure the Companion to withhold it — transcript="phone-only" (companion.md), which reduces turn.input to a turn boundary, sends no turn.delta or turn.citation at all, and redacts turn.error.message. A session in that mode MAY claim that no conversation content crosses the relay, whatever terminates TLS in front of it, or the Hub's logs and storage in plaintext. This is stronger than what the retired sealing profile offered and needs no cryptography and no Hub-served code in the confidentiality path. Both claims are bounded:

This paragraph used to read "MUST negotiate sealing (sealing.md)". That was a normative requirement to implement a profile that has been RETIRED and is NOT IMPLEMENTED — see retired/sealing.md. It could not be satisfied: §6.1 binds the claiming device's cnf.jkt into the transcript salt and calls the binding not optional, and no registered frame ever carried a thumbprint to the Surface. An implementer following it built a profile no other peer completes.

  • It covers the conversation, not the tool path. An answer that calls book_appointment still sends action.request across the relay, because the tool runs on the Surface; its arguments are conversation content by another name and they cross in clear, as does action.result coming back. Nor is answeredBy withheld on turn.done — the Hub learns which model answered. There is no second mode that covers the remainder. On a site whose tool inputs carry anything sensitive, implementations MUST say so rather than let the mode name imply otherwise.
  • It hides content, not activity. The Hub still sees turn boundaries, timing and volume, which tools ran and when, and every confirm and its verdict.
  • It is a confidentiality control only. The relay can still cancel, stall, truncate or close a turn, and it can raise a confirm with wording of its own.
  • The visitor loses the on-screen transcript, and that is the whole trade. The Surface cannot render the question or the answer because it is not sent them. Implementations MUST NOT offer this mode as free.

The discipline of the paragraph above still holds: "the Hub cannot see your conversation" remains a sentence implementations MUST NOT use, because the tool path is part of the conversation and it is not withheld. What phone-only earns is narrower and worth saying accurately — that the words of the exchange stay on the handset, and that no key, no code the Hub served, and no cryptography stands between the operator and them, because nothing is sent for them to read.