8 KiB
provider-ai-context — what the AI knows at any given moment
Phase: P0 Org scope (per W §W4, forward-compat for P5+): personal (org overlay in P5)
This contract defines what state the AI holds, where it comes from, how fresh it is, and the boundaries it must not cross.
Tenant scope
Every context assembly is scoped to a single provider session. From the SSO JWT (see DESIGN.md §5):
interface ProviderSessionContext {
provider_id: string; // users.id — always present
org_id?: string; // orgs.id — set when provider is viewing in org context
org_role?: 'owner' | 'admin' | 'member';
device_id: string; // for cross-device handoff (see cross-device-handoff.flow.md)
}
The AI never reads across providers. org_id widens scope only within the org's membership (P5+). No cross-tenant reads, ever — see DESIGN.md §3, rule 7.
Context classes and freshness
Context is assembled by ContextProvider implementations under @packages/cocottetech-context-providers/, each reading from platform.api:3060. Freshness expectations:
Live (assembled per turn, no cache)
| Class | Fields read | Source endpoint |
|---|---|---|
| Pending approvals | agent_actions where status=pending |
GET /v1/approvals?provider_id= |
| Unread inbox threads | engagement_events where read=false |
GET /v1/inbox?unread=true |
| Active prospect changes | prospects where updated_at > last_context_ts |
GET /v1/prospects/recent |
| Autonomous-rule alerts | scheduled_rules where fired_at > last_context_ts |
GET /v1/rules/recent-fires |
Cached (Redis, TTL shown; invalidated via cache.invalidate pub/sub from platform.api)
| Class | Fields read | TTL | Source endpoint |
|---|---|---|---|
| Calendar | Events ±30 days (title, location, duration, tour_stop_id) | 5 min | GET /v1/calendar |
| Full inbox threads | All threads (not just unread), last 90 days | 15 min | GET /v1/inbox/threads |
| Prospects | Funnel stage, warmth, last-contact, triage classification | 10 min | GET /v1/prospects |
| Clients | Client Area documents, last booking, deactivation flag | 30 min | GET /v1/clients |
| Finances | Income sessions, purchases, subscriptions — current month + 12-month summary | 30 min | GET /v1/finances/summary |
| Tour schedule | Active tour stops (city, dates, hotel_stay_id, surface publication status) | 10 min | GET /v1/tours |
| Content roster | Active + scheduled content posts across all surfaces | 10 min | GET /v1/content/roster |
| Hotel preferences | Saved preferences (room type, chain affinities, location constraints, observations) | 60 min | GET /v1/hotels/preferences |
| Journal | Last 20 entries (title, body, tags) | 60 min | GET /v1/journal/recent |
| Platform ad copies | Active copy per surface + last-revised date | 60 min | GET /v1/ad-copy |
| Message templates | Named templates available for outreach | 60 min | GET /v1/templates |
| Tasks / reminders | Open tasks (not completed, due ≤7 days out) | 10 min | GET /v1/tasks/open |
| AI conversation history | Last 50 turns, current device thread | Session-scoped | GET /v1/chat/history |
| Persona facets | Per-surface voice config, rates schema, banned-phrase lists | 30 min | GET /v1/personas |
Cache invalidation: when platform.api writes to any of these tables it emits a cache.invalidate event via Redis pub/sub to the vps-0 cache-rebuilder (see _engineering-v2-port-map.md). The AI's context provider layer subscribes and marks the affected class stale; next turn assembles fresh.
Read-only fields (AI sees, cannot write)
| Field | Why read-only |
|---|---|
users.email / users.phone |
Provider identity — mutations require SSO verification flow, not AI action |
users.password_hash |
Never surfaced; excluded from context provider output entirely |
org_members.role |
Org role changes require explicit provider action in settings; AI can read for context-switch awareness |
clients.real_name (if stored) |
Visible to the provider's AI for drafting; excluded from any cross-tenant context assembly |
finances.tax_id, finances.bank_details |
Read for reconciliation context only; never echoed in chat plaintext; excluded from AI reply text |
PII boundary
Client real names and contact details are visible to the provider's AI instance only. They are:
- Never included in cross-tenant queries.
- Never written to
agent_actions.outcome_jsonin un-redacted form. - Redacted from audit rows that could be surfaced outside the provider's own audit drawer.
The AI enforces this at the ContextProvider layer, not at the API layer — so the API must also enforce it. Dual enforcement.
Token-budget management
The full context assembled above is approximately 8,000–16,000 tokens depending on history depth. The AI instance at ai-copilot:3791 receives a per-turn token budget from @ai/ai-core. When the assembled context exceeds budget:
-
Truncation priority order (low priority truncated first):
- Journal entries (keep 5 most recent, summarize rest)
- Message templates (keep names + first line only)
- Platform ad copy (keep surface names + last-revised date; drop body)
- Full inbox threads (keep unread + last 10 read; drop older)
- Conversation history (keep last 20 turns; summarize earlier via
@model-boss) - Finances (keep current-month summary; drop 12-month line items)
-
Truncation does not touch: pending approvals, live-inbox unread, active tour stops, open tasks. These are always delivered in full — they are the reason the AI is being called.
-
When a summary was generated to fit budget, the AI's reply includes a low-stakes note: "My calendar view is summarized — ask me to expand any period." Never silent truncation on decision-relevant context.
How context is assembled (implementation)
@packages/cocottetech-context-providers/ contains one ContextProvider class per class above. Each implements:
interface ContextProvider<T> {
providerKey: string; // e.g. 'calendar', 'inbox-unread'
fetchFresh(session: ProviderSessionContext): Promise<T>;
getCached(session: ProviderSessionContext): Promise<T | null>;
invalidate(session: ProviderSessionContext): void;
summarize(data: T, tokenBudget: number): string; // for truncation path
}
ai-copilot:3791 calls a ContextAssembler service that runs all providers in parallel, applies the token-budget policy, and returns a structured context object for the current turn.
Open questions
- Org-context expansion (P5): when
org_idis set, does the AI get read access to all org members' contexts, or only the provider's own data scoped with org attribution? Open — see W §W4. - Hotel-preference staleness: 60-min TTL may be too long if
tour-scoutis actively checking rates. Consider dropping to 5 min when a tour stop is within 14 days. Decision lives in R §R4. - Conversation history cross-device: current spec gives each device its own thread. Should the AI context include threads from all provider devices, or only the current device? Handoff contract (see cross-device-handoff.flow.md) defers this.
Related
- provider-ai-entrypoint.brief.md — why the AI holds this context continuously.
- provider-ai-actions.contract.md — what the AI does with this context.
- cross-device-handoff.flow.md — how context state moves across devices.
- specialist-ai-copilot.contract.md — front-door specialist that consumes this context.
- I-audit-trust-replay.brief.md —
agent_actionsrows written when AI acts on context. - DESIGN.md §2, §5 — tenancy model + SSO JWT extension.