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: trueinsession.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
answerModeinsession.linkedand MUST NOT settle oncompanionunless the Companion declared the capability (Core §5.1). The Companion MUST treat the settled value as authoritative and MUST NOT originate answer frames inhubmode — a tenant that deliberately pays for its members' answers is conformant, and a Companion that answers anyway produces two answers for one turn. - Absent
answerModeMUST be read ashub. 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.openMUST 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 inansweredByif 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
turnIdof aturn.inputof 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 aturn.inputto 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 aturnIdnoturn.inputopened, for a turn already ended byturn.doneorturn.error, or for a turn it is not itself answering. The Hub can check all three: it saw everyturn.inputin the session, whichever peer sent it. - It MUST stream: answers arrive as
turn.deltaframes 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.doneorturn.error.turn.doneSHOULD carry thecontextRevit grounded against and SHOULD carryansweredBy. - On
turn.cancel, or on aturn.inputthat supersedes the in-flight turn, it MUST stop generating promptly and MUST NOT emit further frames for that turn other thanturn.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 anysession.*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.invokefor one tool has not thereby been granted another; - gate every
write: truetool behind aui.requestconfirm 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, orsession_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'scontext(Core §6) and its citable resources rather than open-world recall, emitturn.citationfor grounded claims with akind/idthe 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.operatorMUST name the party that actually ran the model — the provider's name, oron-devicewhen it never left the device. A Companion MUST NOT originateansweredBywithmode: 'hub'.brainIdis opaque and MUST NOT carry secrets or user identifiers; it exists for transcripts and support reports.- A Hub MUST NOT rewrite
answeredByon 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 withturn.errorinstead.
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 — seeretired/sealing.md. It could not be satisfied: §6.1 binds the claiming device'scnf.jktinto 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_appointmentstill sendsaction.requestacross the relay, because the tool runs on the Surface; its arguments are conversation content by another name and they cross in clear, as doesaction.resultcoming back. Nor isansweredBywithheld onturn.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.