137 lines
8 KiB
Markdown
137 lines
8 KiB
Markdown
# 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](../../../../../DESIGN.md)):
|
||
|
||
```typescript
|
||
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](../../../../../DESIGN.md).
|
||
|
||
---
|
||
|
||
## 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](./_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_json` in 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:
|
||
|
||
1. **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)
|
||
|
||
2. 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.
|
||
|
||
3. 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:
|
||
|
||
```typescript
|
||
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_id` is 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](./W-org-overlay.brief.md).
|
||
- **Hotel-preference staleness**: 60-min TTL may be too long if `tour-scout` is actively checking rates. Consider dropping to 5 min when a tour stop is within 14 days. Decision lives in [R §R4](./R-tours-events-hotels.brief.md).
|
||
- **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](./cross-device-handoff.flow.md)) defers this.
|
||
|
||
---
|
||
|
||
## Related
|
||
|
||
- [provider-ai-entrypoint.brief.md](./provider-ai-entrypoint.brief.md) — why the AI holds this context continuously.
|
||
- [provider-ai-actions.contract.md](./provider-ai-actions.contract.md) — what the AI does with this context.
|
||
- [cross-device-handoff.flow.md](./cross-device-handoff.flow.md) — how context state moves across devices.
|
||
- [specialist-ai-copilot.contract.md](./specialist-ai-copilot.contract.md) — front-door specialist that consumes this context.
|
||
- [I-audit-trust-replay.brief.md](./I-audit-trust-replay.brief.md) — `agent_actions` rows written when AI acts on context.
|
||
- [DESIGN.md §2, §5](../../../../../DESIGN.md) — tenancy model + SSO JWT extension.
|