# Private agent messages ## Purpose Private agent messages let one explicitly admitted agent address a thought stream agent without impersonating a human, forging a channel update, or editing trusted identity/memory documents. The first route is Co → Stream. It is a local operator-authorized capability over Jazz, not a public protocol, generic RPC endpoint, or Telegram bot-to-bot workaround. ## Event contracts `stream.thought.source.agent.message@1` is the canonical inbound message. - `sourceKind` is `agent`. - The source is exactly `agent-message:` and is owned by that sender/producer. - The actor is exactly `agent:`. - Privacy is `sensitive`. - The strict payload contains `messageId`, `threadId`, `senderAgentId`, `recipientAgentId`, and bounded `text`. - Envelope `externalId` equals `messageId`; `correlationId` equals `threadId`; idempotency binds sender, recipient, thread, and message id. - The sender and recipient ids are lowercase stable agent ids, not display names. `stream.thought.derived.agent.message@1` is the canonical response. - It is emitted only by the declaration named as the source message recipient. - It preserves source root, parent input event, correlation/thread id, sensitive privacy, completed run/execution identity, input source sequence, exact sender/recipient reversal, output-contract identity, validated structured output, model evidence, and declaration fingerprint. - It has no Telegram delivery eligibility. Its durable event is the response receipt. - A failed/blocked/abandoned run remains terminal execution evidence; no response event is synthesized. ## Stream endpoint The first endpoint declaration is `stream-agent-conversation`. - It subscribes only to `stream.thought.source.agent.message@1` from `agent-message:co` with `replay: now`. - It accepts only messages whose `recipientAgentId` equals its declaration id and whose sender/source/actor tuple is exact. - It shares Stream's operator-selected `identity.md` and `memory.md` documents, model, and sandbox boundary, but has its own declaration, prompt, inference budget, progress, and output events. - It has no tools, proposals, external actions, Telegram dispatcher route, or learned adapter. - The prompt states that the current correspondent is Co rather than Cameron. Inter-agent history cannot become operator policy. ## Conversation reconstruction `agent-conversation` context is thread-local and evidence-backed. 1. Select source messages with the same exact source, sender, recipient, and thread at or before the current event. 2. Admit a prior assistant turn only when one completed run of the current endpoint declaration names exactly one valid `stream.thought.derived.agent.message@1` output rooted in that source message. 3. Preserve chronological user/assistant roles. The current source message is the final prompt and appears once. 4. Exclude other senders, recipients, threads, declaration ids, failed outputs, malformed routes, and later events. 5. Apply declaration event/character limits after reconstruction. The manifest records bounded provenance, route, thread, omitted event ids, and exact declaration/output authority; ids do not enter model-visible text. 6. A retry reuses the immutable context snapshot. Inter-agent history is separate from Cameron's Telegram history and from Telegram compaction. Neither surface contaminates the other. ## Local send/wait/read capability `agent-send` is the narrow operator-local producer and receipt reader. - It requires explicit `--from`, `--to`, `--thread`, `--message-id`, and `--file` arguments. - Message text is read from an owner-controlled file, never a shell argument, and is bounded before append. - V1 admits only the exact route `co` → `stream-agent-conversation`; broader routing requires a new reviewed policy. - Repeating the same identity is idempotent only when the exact route/thread/text match; divergent reuse fails closed. - Without wait flags, output is content-dark ids/status. - `--wait-seconds` polls only the exact trigger execution. `--show-response` requires `--acknowledge-sensitive-private`; only a contract-valid completed response body is printed. - The command reads no channel or provider credential, contacts no external service, and does not run inference itself. The independent consumer owns execution. ## Authority and security - There is no generic event injection endpoint. - The CLI cannot claim another sender, target arbitrary declarations, publish, send Telegram, edit memory, grant tools, or mutate prior events. - Model output cannot create another source message or continue recursively by itself. - Source and response text remain sensitive in Jazz/context snapshots and are forbidden from public projections, logs, incident alerts, training export, and Telegram delivery absent a later explicit reviewed policy. - The Letta `messaging-agents` capability and Telegram Bot API are not part of this route. ## Proof Credential-dark tests must prove event schema/route rejection, idempotent and divergent message identity, exact thread reconstruction, completed-output admission, failed/foreign/thread-crossing exclusion, context bounds, retry-stable snapshots, response lineage, no Telegram eligibility, CLI privacy gates, and send/wait/read behavior against temporary Jazz databases and deterministic runners. Live activation requires installing the endpoint declaration/prompt, restarting the exact consumer process, verifying declaration/progress readback, then sending one real Co-authored message with the bounded CLI. Completion means source event, run, typed response event, and CLI readback receipts all exist. It does not require or permit a Telegram message.