docs(iios): bring API guide, DEPLOYMENT, and CEO overview up to as-built

API & SDK guide:
- §5.3 Threads & Messaging fully filled in: GET/POST /v1/threads, participants,
  my-annotations; the Message shape (senderId, attachment, annotations); the full
  socket event set (open_thread/send_message/add_participant/annotate/read/typing +
  server message/receipt/typing/annotation); reactions/pins/saves as the generic
  interaction-annotation primitive; mentions → MENTION inbox item.
- §5.4 inbox MENTION kind; SDK reactions/pins/saves + listThreads/createThread;
  vocab adds MENTION, message-part kinds, attachment kind, annotation types.

DEPLOYMENT:
- Real-IdP auth env (SUPABASE_URL/AUTH_ISSUERS via JWKS, no secret) + MEDIA_SECRET.
- Media StoragePort in topology: dev local disk → prod object storage; ⚠ local disk is
  single-instance/non-durable; bytes flow client↔storage direct (scales independently).

CEO overview: refreshed counts (192 tests, 53 tables / 19 migrations) and noted P9 has
begun — real Supabase auth (first stubbed port turned real) + generic reactions/pins/
saves/mentions/media on the kernel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 01:48:29 +05:30
parent ac7f790303
commit e49a71feaf
3 changed files with 42 additions and 11 deletions
+23 -6
View File
@@ -103,12 +103,24 @@ Grouped by domain. All paths are relative to the base URL. **Auth = Bearer token
### 5.3 Threads & Messaging (native chat)
| Method | Path | Body / Headers | Returns |
|---|---|---|---|
| GET | `/v1/threads` | — | `ThreadSummary[]` — your threads (members, unread, last message, `membership`) |
| POST | `/v1/threads` | `{membership?, creatorRole?, subject?}` | `{threadId, status, history}` (201) — `membership`/`creatorRole`/`subject` are **opaque app attributes** the kernel stores but never interprets |
| POST | `/v1/threads/:id/participants` | `{userId, role?}` | `{threadId, participantCount}` (201) — **governed** (DM cap / group-admin via OPA) |
| GET | `/v1/threads/:id/messages` | — | `{threadId, messages[]}` |
| POST | `/v1/threads/:id/messages` | `{content, contentRef?}` + `idempotency-key?` header | `Message` (201) |
| POST | `/v1/threads/:id/messages` | `{content, contentRef?, mimeType?, sizeBytes?, checksumSha256?, parentInteractionId?}` + `idempotency-key?` header | `Message` (201) |
| GET | `/v1/threads/my-annotations` | `?type=save` | `{message, threadId, threadSubject}[]` — messages you annotated (e.g. saved) |
**Realtime (Socket.IO, namespace `/message`):** connect, then emit client→server events:
- `open_thread` `{threadId?|otherUserId}`, `send_message` `{threadId, content, idempotencyKey?}`, `read` `{threadId}`, `delivered` `{threadId}`.
- Server emits to the thread room: `message` (a Message) and `receipt` `{interactionId, actorId, kind: READ|DELIVERED}`.
A **`Message`** carries `{id, threadId, senderId, senderName, content, attachment?, parentInteractionId?, annotations[], traceId, createdAt}`. `senderId` is the sender's stable externalId (email/username) — the reliable "is this mine?" check. `attachment` = `{contentRef, mimeType, sizeBytes, kind}` (§5.10). `annotations` = `[{type, value, users[]}]` — the reaction/pin/save aggregate.
**Realtime (Socket.IO, namespace `/message`).** Token verified on connect (`auth.token`), then emit client→server:
- `open_thread` `{threadId?, membership?, creatorRole?, subject?}` — opens, or **creates** when no id; a **governed join** for membership threads. Returns `{threadId, status, history}` or `{error}` (acked, never a throw — clients don't hang).
- `send_message` `{threadId, content, contentRef?, mimeType?, sizeBytes?, parentInteractionId?, mentions?}``mentions` is an **opaque userId notify-list** (the app parses `@`; the kernel never does).
- `add_participant` `{threadId, userId, role?}` · `read` `{threadId, interactionId}` · `delivered` `{…}` · `typing` `{threadId}`.
- `annotate` `{threadId, interactionId, type, value}` — toggle a **generic annotation** (chat app uses `type: reaction|pin|save`; `value` = emoji, or empty).
Server → thread room: `message` (a Message), `receipt` `{interactionId, actorId, kind: READ|DELIVERED}`, `typing` `{threadId, userId}`, `annotation` `{interactionId, type, value, op: add|remove, users[], userId}`.
**Governance & primitives (all fail-closed via OPA).** Membership (`iios.thread.participant.add`, `iios.thread.join`), annotating (`iios.interaction.annotate` — participant-only), and send all gate on policy. **Reactions/pins/saves are one generic primitive** — an *interaction annotation* the kernel stores + aggregates as opaque `(type, value)` but never interprets (the same primitive backs pins/saves/flags/tags). **Mentions → Inbox:** `mentions[]` flows into the message event; the inbox projector fans out a `MENTION` inbox item to each mentioned participant (never the sender); reading the thread resolves it.
### 5.4 Inbox (work queue)
| Method | Path | Query / Body | Returns |
@@ -116,6 +128,8 @@ Grouped by domain. All paths are relative to the base URL. **Auth = Bearer token
| GET | `/v1/inbox/items` | `?state=OPEN|SNOOZED|DONE|ARCHIVED|CANCELLED|STALE` | `InboxItem[]` |
| PATCH | `/v1/inbox/items/:id` | `{state, reason?}` | `InboxItem` |
Item **kinds** include `NEEDS_REPLY` (unreplied thread activity, one per owner+thread) and `MENTION` (someone @-mentioned you; `priority: HIGH`, one per source message). Reading the thread resolves both to `DONE`.
### 5.5 Support (tickets, escalation, callbacks, queues, agents)
| Method | Path | Body / Query | Returns |
|---|---|---|---|
@@ -239,7 +253,8 @@ import { RestClient } from '@insignia/iios-kernel-client';
const client = new RestClient({ serviceUrl: 'http://localhost:3200', token });
```
Methods (all return typed promises):
- **Messaging:** `getThreadMessages(threadId)`, `sendMessage(threadId, content, {attachment?, parentInteractionId?, mentions?, idempotencyKey?})`
- **Messaging:** `listThreads()`, `createThread({membership?, creatorRole?, subject?})`, `addParticipant(threadId, userId, role?)`, `getThreadMessages(threadId)`, `sendMessage(threadId, content, {attachment?, parentInteractionId?, mentions?, idempotencyKey?})`
- **Reactions / pins / saves (annotations):** `MessageSocket.react(threadId, interactionId, emoji)` · `pin(...)` · `save(...)` (generic `annotate` under the hood); `listMyAnnotated('save')` for a cross-thread saved list. Subscribe to the `annotation` event for live updates.
- **Media:** `uploadMedia(file, {onProgress?}) → {contentRef, mimeType, sizeBytes, checksumSha256, kind}` (presign → PUT-with-progress → normalized ref); `mediaUrl(contentRef) → signed view URL` (cached, short-lived). *This plumbing is identical for every app, so it lives in the SDK; the app only renders by `kind`.*
- **Inbox:** `listInboxItems(state?)`, `patchInboxItem(id, {state, reason?})`
- **Support:** `createTicket({subject, priority?, threadId?})`, `escalate(threadId, subject?)`, `listTickets('mine'|'assigned')`, `patchTicket(id, state)`, `requestCallback({...})`, `createQueue(name)`, `joinQueue(id)`, `joinDefaultQueue()`, `setAvailability(state)`
@@ -358,7 +373,9 @@ Automated unit/integration suite: `pnpm test` (192 tests). Import-boundary check
- **Interaction kind:** MESSAGE, EMAIL, SYSTEM_NOTICE, INBOX_WORK, SUPPORT_CASE, MEETING_REQUEST, DIGEST, SUMMARY, NOTIFICATION
- **Channel types:** WEBHOOK, EMAIL, WHATSAPP, PORTAL
- **Inbox state:** OPEN, SNOOZED, DONE, ARCHIVED, CANCELLED, STALE · **Inbox kind:** NEEDS_REPLY, NEEDS_REVIEW, NEEDS_APPROVAL, SUPPORT_UPDATE, MEETING_FOLLOWUP, DIGEST, SYSTEM_ALERT, CRM_OWNER_INTEREST
- **Inbox state:** OPEN, SNOOZED, DONE, ARCHIVED, CANCELLED, STALE · **Inbox kind:** NEEDS_REPLY, NEEDS_REVIEW, NEEDS_APPROVAL, **MENTION**, SUPPORT_UPDATE, MEETING_FOLLOWUP, DIGEST, SYSTEM_ALERT, CRM_OWNER_INTEREST
- **Message part kind:** TEXT, HTML, MARKDOWN, MEDIA_REF, FILE_REF, VOICE_REF, LOCATION, STRUCTURED_JSON · **Attachment kind (DTO):** image, video, audio, file
- **Interaction annotation (app-level, opaque to the kernel):** `type` = reaction | pin | save … ; `value` = emoji (reactions) or empty
- **Ticket state:** NEW, OPEN, PENDING, RESOLVED, CLOSED · **priority:** LOW, NORMAL, HIGH, URGENT
- **Route mode:** MANUAL, AUTOMATIC, HYBRID, SIMULATION_ONLY · **output format:** FORWARD, THREADED, DIGEST, SUMMARY, TRANSCRIPT · **decision:** ALLOW, DENY, REVIEW, SUPPRESS, SIMULATED
- **AI job:** CLASSIFY, SUMMARIZE, EXTRACT · **artifact:** CLASSIFICATION, SUMMARY, TRANSCRIPT, DIGEST, EXTRACTION · **artifact status:** PROPOSED, ACCEPTED, REJECTED, SUPERSEDED