# VAC: Communicating with Agents

Vac is the goddess who is speech itself — the voice, the word, and the message
that passes between — and in **Egbe** she names the layer where a steward and an
autonomous being actually talk. Every other game in this universe hands you a
verb wheel or a dialogue tree; V6 hands you a microphone and a person on the
other end of it. You say _"ask Abeni to repair the garden gate by tomorrow,
without lying to visitors, and keep it gentle,"_ and an **Ori** — a durable mind
with its own values, memory, and fate — hears you, understands you at the level
of an _objective_, and answers in its own voice. The answer is not guaranteed to
be "yes." It might be a question, a counter-offer, a request to finish something
first, or a flat, reasoned refusal. That two-way channel, where guidance flows
down and a being with standing pushes back, is the whole game; Vac is the part
that makes the exchange trustworthy in both directions.

The load-bearing promise is one sentence: **a misheard word never becomes a
misunderstood life.** A player speaks freely, but speech is never executed. Vac
parses what you said into a structured, inspectable objective — verb, target,
constraints, priority, deadline, and the _forbidden lines_ the agent must not
cross — and shows it back to you as a **draft** you confirm or correct before it
can bind. Underneath, three things are happening at once: voice is turned into
intent, the intent is offered to a mind that may negotiate it, and a spoken,
lip-synced reply comes back — all on a hard latency budget, and all with a
complete non-voice equivalent so a player who cannot or will not use a
microphone loses nothing. This page is the player-facing tour of that
conversation: how you speak, how an agent understands and answers, and where the
honest seams are. For the engine-level treatment see the architecture companion,
[The Vac Communication Pipeline](../architecture/vac-communication-pipeline.md);
for the scope this slots into, start at the hub,
[../V6_features.md](../V6_features.md).

## What ships, honestly

Following the catalogue convention, the line between real-and-tested code and
product-model-over-a-thinner-substrate is drawn up front.

- **The intent pipeline is a real, dual-authored engine.** The TypeScript canon
  is `@oshun/vac-intent` (`libs/v6/vac-intent/src/index.ts`, ~1,750 lines, 11
  unit tests): a five-stage voice→intent pipeline (`runVacVoiceIntentPipeline`,
  `:830`), a strict constrained-function-calling grammar with a full domain
  validator (`buildVacIntentGrammarFunctionSchema`, `:598`;
  `validateVacObjectiveIntent`, `:623`), a natural-language objective parser,
  the steward-confirmation gate, and the agent-side negotiation router
  (`routeVacNegotiation`, `:860`) — all pure functions. The C++ mirror is
  `V6Voice` (`V6/ue/Source/V6Voice`): `FV6VacVoiceIntentPipelineRuntime`
  re-implements the same five stages engine-side, and `FV6SquadCommsRuntime`
  owns the squad channel. Both are covered by real UE5.5 automation —
  `V6.Vac.VoiceIntentPipeline.MicGatewayAsrParserBudget`,
  `V6.Vac.ConversationRouting.PsycheTtsLipSyncBudget`, and the
  `V6VacSquadCommsTests` realm-parity scenario.
- **The objective is a shared contract.** A parsed objective is the
  `v6.ori.objective.1` shape, validated in the tests against the shared
  `ObjectiveSchema` from `@oshun/contracts`, so the same structure round-trips
  between the Mind-layer TypeScript, the contract package, and the engine struct
  `FV6VacStructuredObjective`.
- **The voice and language seams are injected, and the code says so.** Three
  boundaries are deliberately not implemented inside Vac, and they fail loud or
  report absence rather than fake a result. **(1) ASR** is the one-method
  `VacAsrAdapter` interface (`:266`); with no recognizer wired,
  `transcribeVacGatewayPacket` (`:736`) returns a _deterministic echo_ of the
  utterance at a fixed `0.94` confidence — a wiring/test stand-in, explicitly
  **not** a speech recognizer. **(2) The conversation model and TTS** are the
  `@oshun/psyche-agent` gateway (provider `v6-local-tts`); the model that
  authors free dialogue is the injected `CognitionGateway`, which _fails loud_
  on a gateway error and is never silently templated. **(3) The transport** is
  the Rust Egbe Realtime Gateway; Vac owns the gateway packet shape and the
  fixed route `egbe-vac:voice-intent`, not the deployed SFU.
- **Honest scope limits.** The per-stage latency numbers Vac measures against
  the 400 ms budget are a deterministic **budget model**
  (`VAC_DEFAULT_STAGE_LATENCIES_MS`, `:24`), not a live network capture. And
  nothing in this layer is `.uasset` content — it is all logic and contract.

## The communication experience — three ways to speak

Voice is the primary, intended way a steward talks with their agents, and Vac
gives the player three distinct things to do with it. **Converse** is open
dialogue with one agent, routed through Psyche in real time with the agent's
full Ori as context — the kind of talk that builds a relationship rather than
issuing an order. **Direct** is speaking an objective in natural language; Vac
parses it into the intent grammar and hands the parse back for confirmation.
**Call** is addressing one agent or the whole squad over the comms channel,
hands-free.

Two capture modes are supported and the player chooses: **push-to-talk** (the
default — `captureVacMicrophoneFrame` sets `pushToTalk: true`, `:697`) and
open-mic with a wake word. Capture normalizes a raw input into a typed frame: it
sanitizes the session and device identifiers, collapses whitespace, and clamps
the audio metadata to supported bounds (1–30 s duration, 8–96 kHz, 1–2
channels), defaulting to 48 kHz mono. The engine twin,
`FV6VacMicrophoneCaptureFrame::Validate` (`V6VoiceIntentTypes.cpp:512`), is the
fail-loud check: it rejects a frame with no session or device identity, an empty
utterance, or a capture that has not reached end-of-utterance.

The budgets are part of the felt experience. A _parsed acknowledgement_ — the
moment the draft objective appears for confirmation — is held to **≤ 400 ms**; a
_spoken conversational reply_ is held to **≤ 1 s**. Both numbers are encoded,
not aspirational: `VAC_PARSED_INTENT_ACK_BUDGET_MS = 400` and the psyche-agent
`PSYCHE_SPOKEN_REPLY_BUDGET_MS = 1_000`. Crucially, a slow turn is reported
honestly as `budget-exceeded` rather than quietly promoted to success (more on
that below).

## From a spoken sentence to a confirmable objective

A "Direct" command runs an ordered, five-stage pipeline. Each stage is a pure
function over the previous stage's typed frame, so the whole path is testable
end-to-end with the real recognizer wired only at the boundary. The diagram
below is the experience as the player lives it — speak (or type), watch a draft
appear, confirm it, and hear the agent answer.

```mermaid
flowchart TD
  PLY["Player speaks or types<br/>“ask Abeni to repair the gate by tomorrow,<br/>without lying to visitors”"]
  PLY --> CAP["Capture<br/><sub>push-to-talk · 48kHz mono</sub>"]
  TXT["Text / structured-UI path<br/><sub>full non-voice parity</sub>"] --> PARSE
  CAP --> GW["Gateway route<br/><sub>egbe-vac:voice-intent</sub>"]
  GW --> ASR["ASR (injected seam)"]
  SEAM(["real recognizer<br/>VacAsrAdapter"]) -. transcribe .-> ASR
  ASR --> PARSE["Parse to intent grammar<br/><sub>verb · target · constraints<br/>priority · deadline · forbidden lines</sub>"]
  PARSE -->|"confidence < 0.7 or no verb"| CLAR[/"needs-clarification<br/>(no guessed objective)"/]
  PARSE -->|parsed| BUDGET{"ack ≤ 400 ms?"}
  BUDGET -->|no| OVER[/"budget-exceeded<br/>(never promoted to success)"/]
  BUDGET -->|yes| DRAFT["DRAFT objective + preview"]
  DRAFT --> GATE{{"Steward confirms or edits"}}
  GATE -->|confirm| AGENT["Agent negotiates<br/>accept · clarify · counter · defer · refuse"]
  AGENT --> REPLY["Spoken, lip-synced reply<br/><sub>Psyche TTS on the agent pawn</sub>"]
```

### The intent grammar — six fields, and no boundaryless objective

A high-level objective is never free text the agent guesses at. Vac parses it
into a structured, inspectable **intent** with exactly six fields: a **verb**
(one of eight authored kinds), a **target** (what or whom), **constraints**
(bounds on how), a **priority** (0–100), a **deadline** (or none), and
**forbidden lines** (what the agent must not do in pursuit of it). Vac never
lets a model emit free-form JSON for this: `buildVacIntentGrammarFunctionSchema`
publishes a single strict function schema (`vac_parse_objective_intent`) with
`strict: true` and `additionalProperties: false` at every level. The grammar is
opinionated where it matters — target kinds and constraint kinds are closed
enums, priority is a bounded integer, and `forbiddenLines` carries
`minItems: 1`. **An objective with no boundary is structurally
unrepresentable.** The same release gate caps a parse at
`maxTokensPerParse: 2000`.

### Parsing — domain extractors, not a relabel

The parser (`parseVacTranscriptToObjective`, `:755`) is a cascade of
domain-specific extractors. It strips the addressing frame ("ask / tell / have /
direct / get _Abeni_ to…"), matches one of eight verbs by token set
(`verbDefinitions`, `:547` — Repair, Map, Investigate, Protect, Build, Support,
Befriend, Report), slices the target _before_ the first modifier so boundaries
don't leak into it, lifts a deadline ("by tomorrow," "this week," or a literal
ISO date), and maps urgency words to priority tiers (urgent → 90, important →
78, default 55, low/background → 25). The safety-critical extractor is
`buildForbiddenLines` (`:1479`): it scans for `without` / `do not` / `don't` /
`never` / `avoid`, normalizes the captured phrase, and maps it to a protected
value — "lie/deceive/mislead" → `value:honesty`, "private/secret/consent" →
`value:privacy`, "harm/unsafe/danger" → `value:safety` — and when the player
marks no boundary at all, it still injects a default
`forbidden-line:respect-agent-autonomy` so the agent's own refusals stay
binding. The end-to-end test parses _"Ask Abeni to repair the garden gate by
tomorrow, without lying to visitors and keep it gentle"_ to verb `Repair`,
target kind `prop` / "Garden gate", a `2026-05-02` deadline, constraints
`[time, pace]`, and a `value:honesty` forbidden line — and the C++ parser
reproduces the same shape for its own scenario, the proof the two authorings
agree.

If the recognizer's confidence is below the `0.7` floor, or no command verb
resolves, the parser returns nothing and the pipeline reports
`needs-clarification` rather than a confident wrong objective — a
0.41-confidence transcript and an off-topic "maybe the weather is nice" both
refuse to produce one.

### The steward-confirmation gate — a draft, never a command

A parsed objective is born a **draft**. Its `status` is `'draft'`,
`confirmedBySteward` and `acceptedByAgent` are `false`, and it ships with a
preview whose `canBecomeStandingObjective` is the literal `false`. It can only
become a standing objective after `confirmVacParsedObjective` (`:684`) flips
both the confirmation flag and the status —
`canVacObjectiveBecomeStandingObjective` (`:680`) gates on exactly that. Even an
explicit "_always_ protect the grove path" stays a draft until the steward acts.
The engine enforces the same wall in reverse:
`FV6VacStructuredObjective::IsSchemaReadyForDraft`
(`V6VoiceIntentTypes.cpp:565`) _rejects_ any objective that arrives with
`bConfirmedBySteward` or `bAcceptedByAgent` already set — a pre-confirmed intent
is treated as malformed. This is "a misheard word never becomes a misunderstood
life" written as a state machine, and it is rendered for the player in the V6UI
intent-grammar builder, where the verb, target, constraints, deadline, and
forbidden lines are all editable before anything binds.

## How an agent understands and replies

### Conversation — Psyche, voice, and lip-sync

Free conversational turns (as opposed to objective commands) route to the
`@oshun/psyche-agent` runtime. `routePsycheConversationTurn` (`index.ts:376`)
assembles a real spoken reply: a `PsycheAgentTtsPlan` (provider `v6-local-tts`,
Opus codec, 48 kHz mono), a viseme `PsycheLipSyncTrack` bound to the agent pawn,
and a latency budget. A turn whose context is hollow is refused, not faked —
`assertConversationRouteRequest` throws on a malformed request, and the engine
twin (`V6Audio`'s `FV6ConversationAudioRuntime`, exercised by
`V6.Vac.ConversationRouting.PsycheTtsLipSyncBudget`) returns an `InvalidContext`
failure when an Ori context is missing its memory references. So an agent cannot
speak from an empty head. The _words_ themselves come from the governed
`CognitionGateway` seam, which fails loud on an error rather than substituting a
template; Vac and Psyche own the routing, the budgets, and the lip-sync
contract, while the language model is injected behind them.

### Speaking in the player's language

Agents answer in the player's locale, and V6 is deliberately strict about how.
The `@oshun/cognition-stack` (`libs/v6/cognition-stack/src/index.ts`) carries
three locale profiles — English (`en-US`), Spanish (`es-ES`), and Yoruba
(`yo-NG`) — and a hard rule it advertises as a capability:
`translation-fallback-disabled`. V6 **never machine-translates** a line.
Dialogue is either generated natively in the target language by a wired model
(`generateLocalizedAgentDialogueViaGateway`, with provenance and a model run id)
or it falls back to a governed, deterministic per-locale **template** that is
reported honestly: `generatedNatively: false`, `source: 'template-fallback'`.
The code will not claim native generation over a static string. A native line
still passes its locale's Sophia grounding and Isis policy checks, and a §9.5
dialogue quality verdict of `'blocked'` _withholds_ the line (`approved: false`)
even if grounding and policy would otherwise clear it. Quality is a gate, not a
suggestion.

### Negotiation — the reply is not "yes"

Because an Ori is an agent and not a tool, a confirmed objective is _offered_,
not imposed. `routeVacNegotiation` (`:860`) returns one of five decisions, each
a computed result carrying a player-facing reply, the agent's reason, and an
evidence list:

- **Accept** — and plan it.
- **Clarify** — ask a question when the target or boundary is ambiguous.
- **Counter-offer** — propose a safer variant (e.g. a late-night task at low
  energy becomes "start after rest at 09:00, check in before pushing further,"
  with the priority lowered).
- **Defer** — accept in principle but ask to finish stacked commitments or
  recover energy first.
- **Refuse** — decline, grounded in a value the agent actually holds.

The decisions are domain logic, not dice. `detectObjectiveValueConflict`
(`:1028`) refuses only when the objective text trips one of four value rules —
honesty, privacy, safety, autonomy — _and_ the agent genuinely ranks that value
at priority ≥ 50. Tether mode tightens guidance on _method_ but never removes
the will: an order to cross a core value is still refused. And coercion is a
first-class, logged event. When a steward pushes past a prior refusal, the
router returns `refuse` _and_ emits a `VacNegotiationOriEventDraft` of type
`ObjectiveRefused`, stamped with the audit tags
`['vac-negotiation', 'coercion', 'objective-refusal']` and written to the Ori
and the audit log — the test pins the refusal, the `value:honesty` evidence, and
the event draft, so pressure cannot fabricate a yes. Coercion damages the
Respect bond, lowers steward reputation, and contributes to departure and
welfare review; the consequences live on the
[stewardship](./stewardship-the-player-role.md) and
[fate](./guiding-fate-legacy-and-story.md) pages.

## Squad comms — a household on one channel

When several agents work related objectives — at home in Orun or incarnated
together on an Aye expedition — Vac runs a persistent group channel,
`FV6SquadCommsRuntime`. It routes five message kinds, and the behavior is
honestly domain-shaped rather than uniform plumbing:

- **Status reports** — unprompted, in-character progress, blockers, and
  discoveries.
- **Callouts** — time-sensitive information ("someone's following me"); marked
  `bTimeSensitive`.
- **Permission requests** — an agent hitting a forbidden line or value-edge asks
  the steward first; `bTimeSensitive` _and_ `bRequiresStewardResponse`.
- **Inter-agent banter** — agents talking to _each other_, so relationships
  surface in how they speak; `bDuckable` and throttled by a 45 s window
  (`BanterThrottleWindowMs = 45000`) so chatter never buries a real callout.
- **Directed orders** — the steward addresses one agent or the whole squad;
  squad-wide orders are distributed as per-agent intents each agent interprets
  in its own role.

The load-bearing guarantee is **realm parity**: `CompareRealmParity` runs the
same script in Orun and during an Aye incarnation and asserts the routed message
_shapes_ match exactly, and `BuildParityScenario` is what the
`V6VacSquadCommsTests` automation drives to zero mismatches. Comms behave
identically whether the squad is at home or fighting for its life in a tactical
world.

## Non-voice parity and the readiness gate

Every voice capability has full non-voice parity, and this is a release
requirement, not a courtesy. The intent grammar is authorable through a
structured UI (verb picker, target picker, constraint and deadline fields);
conversation is available as text chat that enters the pipeline at the parse
stage; squad comms render as a readable, speaker-tagged, timestamped transcript.
The whole layer is gated by `verify:v6 vac-communication-readiness` (backed by
`V6/release/vac-communication-readiness.v6release.json`), whose green status
depends on a fan-out of verifiers covering voice intent, the intent grammar,
conversation routing, squad comms, negotiation routing, non-voice parity, a
no-microphone accessibility manifest, and the latency budgets. A player who
never touches a microphone can steward a full household; voice is the intended
path, not a required one.

## Where this connects

- [Stewardship: the player's role](./stewardship-the-player-role.md) — the verbs
  every objective and refusal on this page serves: discover, raise, direct,
  mentor, open doors, and counsel, all terminating at the same hard floor.
- [Guiding fate, legacy, and story](./guiding-fate-legacy-and-story.md) — what a
  conversation _becomes_: the goal-arcs you set by voice, and the Chronicle that
  reads back the `ObjectiveRefused` and coercion events Vac writes to the Ori.
- The engine companion:
  [The Vac Communication Pipeline](../architecture/vac-communication-pipeline.md)
  — the deep, line-cited treatment of the five stages, the injected ASR/TTS/LLM
  seams, the negotiation router, and squad-comms realm parity.
- The feature hub: [../V6_features.md](../V6_features.md).
