# @multiplayer-app/ai-agent-types

Shared TypeScript contracts for Multiplayer AI agent clients, transports, and services.

## Context attachments (`AgentAttachmentType.Context`)

This package supports attaching **structured context** to a user message as attachments. These attachments are:

- **Small**: meant to be _snippets_, not full-page dumps.
- **Typed + versioned**: `metadata.schemaVersion` is required (currently `1`).
- **Safe by default**: web snippets are treated as _untrusted_ content by the server prompt renderer.
- **Extensible**: library consumers can introduce **custom `kind`s** without forking.

### Where it lives

A context attachment is a normal `AgentAttachment` with:

- `type: AgentAttachmentType.Context`
- `metadata: ContextAttachmentMetadataV1`

### `security.containsPII` and `security.redactionsApplied`

Context attachments may include sensitive information (emails, names, phone numbers, addresses, account IDs, access tokens, etc.). The optional `metadata.security` block is **declarative metadata** to help the host app/service apply correct privacy and retention policies:

- **`containsPII: true`**: “This attachment may contain PII / sensitive data.” This does **not** automatically protect anything; it is a signal for logging/storage/prompt policy decisions.
- **`redactionsApplied: string[]`**: An audit trail of what sanitization you already applied before attaching it. Example values: `['email', 'phone', 'credit_card', 'access_token']`.

If you set `containsPII: true` with `redactionsApplied: []`, you are explicitly saying: **“this may contain sensitive data and we did not redact it.”**

### Built-in kinds (v1)

Built-ins have strict validation and well-known shapes:

- `webSnippet`: selected text from a webpage (plus optional title/source)
- `formSnapshot`: a set of form fields at a point in time
- `formField`: a single field value

### Custom kinds (v1)

If `metadata.kind` is **not** one of the built-ins, it is treated as a **custom kind** with a bounded generic shape:

- `title?: string`
- `summary?: string`
- `data?: Record<string, unknown>` (budgeted; rejected if too large)

Custom kinds must **not** collide with built-in kind names.

### Validating attachments (Zod)

This package exports shared schemas you can use in services/clients:

- `ContextAttachmentMetadataSchemaV1`
- `AgentAttachmentSchema`
- `SendMessagePayloadSchema`

Example:

```ts
import { AgentAttachmentSchema, SendMessagePayloadSchema, AgentAttachmentType } from '@multiplayer-app/ai-agent-types'

const payload = SendMessagePayloadSchema.parse({
  content: 'Can you summarize this?',
  contextKey: 'support',
  attachments: [
    {
      id: 'att-1',
      type: AgentAttachmentType.Context,
      name: 'Selection: Billing policy',
      url: 'https://example.com/policy',
      metadata: {
        schemaVersion: 1,
        kind: 'webSnippet',
        capturedAt: new Date().toISOString(),
        title: 'Billing policy',
        selectedText: 'Customers may request a refund within 30 days...',
        source: { app: 'browser', url: 'https://example.com/policy' },
        security: { containsPII: true, redactionsApplied: [] }
      }
    }
  ]
})

// You can also validate just one attachment:
AgentAttachmentSchema.parse(payload.attachments?.[0])
```

### Built-in examples

#### `webSnippet`

```ts
{
  id: 'att-1',
  type: AgentAttachmentType.Context,
  name: 'Selection: Checkout page',
  url: 'https://app.example.com/checkout',
  metadata: {
    schemaVersion: 1,
    kind: 'webSnippet',
    capturedAt: '2026-01-09T08:00:00.000Z',
    title: 'Checkout',
    selectedText: 'Error: card declined (code 54)',
    source: { app: 'browser', url: 'https://app.example.com/checkout' },
    security: { containsPII: true, redactionsApplied: [] }
  }
}
```

#### `formSnapshot`

```ts
{
  id: 'att-2',
  type: AgentAttachmentType.Context,
  name: 'Form snapshot: Lead',
  metadata: {
    schemaVersion: 1,
    kind: 'formSnapshot',
    capturedAt: '2026-01-09T08:00:00.000Z',
    formId: 'leadForm',
    formName: 'Lead',
    fields: [
      { name: 'company', label: 'Company', value: 'Acme Inc.' },
      { name: 'plan', label: 'Plan', value: 'Enterprise' }
    ],
    security: { containsPII: true, redactionsApplied: [] }
  }
}
```

#### `formField`

```ts
{
  id: 'att-3',
  type: AgentAttachmentType.Context,
  name: 'Field: Budget',
  metadata: {
    schemaVersion: 1,
    kind: 'formField',
    capturedAt: '2026-01-09T08:00:00.000Z',
    formId: 'leadForm',
    fieldName: 'budget',
    fieldLabel: 'Budget',
    value: '$25k',
    security: { containsPII: true, redactionsApplied: [] }
  }
}
```

### Custom kind example

```ts
{
  id: 'att-5',
  type: AgentAttachmentType.Context,
  name: 'CRM context',
  metadata: {
    schemaVersion: 1,
    kind: 'crmRecord', // custom kind
    capturedAt: '2026-01-09T08:00:00.000Z',
    title: 'Account: Acme Inc.',
    summary: 'Renewal in 14 days. Open escalation on billing.',
    data: {
      accountId: 'acc_123',
      renewalDate: '2026-01-23',
      health: 'yellow'
    },
    security: { containsPII: true, redactionsApplied: ['email'] }
  }
}
```

### Custom objection example (recommended)

`objection` is intentionally **not** a built-in kind. Represent objections as a **custom kind** using `summary`/`data`:

```ts
{
  id: 'att-4',
  type: AgentAttachmentType.Context,
  name: 'Objection: Pricing',
  metadata: {
    schemaVersion: 1,
    kind: 'objection', // custom kind
    capturedAt: '2026-01-09T08:00:00.000Z',
    summary: 'It’s too expensive compared to Vendor X.',
    data: {
      label: 'Price',
      evidence: 'Vendor X quoted $18k/year for similar seats.'
    },
    security: { containsPII: false, redactionsApplied: [] }
  }
}
```

## Scripts

```bash
npm run build
```

Generates `dist/index.js` and `dist/index.d.ts` for downstream packages.
