feat(13-06): content:request/content:push channel on the existing bridge
- aiui-protocol.ts: AIUIContentRequest (kind + optional scope, no RPC method or params) and ArchyContentPush (adapted bundle + permitted flag). - contextBroker.ts: handleContentRequest gates on the media/files permission categories (either grants access), resolves scope to content.list-mine / content.browse-peer (fanned out across every known federation peer) / content.owned-list, and routes results through archyContentAdapter's adaptContentItems before crossing the iframe boundary. contentRequestSeq is a monotonic guard: a stale RPC response that resolves after a newer content:request has started is discarded rather than posted (AIUI-03 concurrency edge). - contextBroker.test.ts: permission-denied, own-scope, and stale/ out-of-order coverage. Fixed a pre-existing latent flake risk in this file — perms.toggle() is not idempotent across tests because the permissions store persists to localStorage, which vi.clearAllMocks() does not reset; switched the new tests to perms.enableAll(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f7691fd1bb
commit
ce7a2b2cd4
@@ -5,6 +5,8 @@
|
||||
* Archy acts as a context broker — AIUI never directly accesses node data.
|
||||
*/
|
||||
|
||||
import type { ArchyContentBundle } from '@/composables/archyContentAdapter'
|
||||
|
||||
/** Data categories that AIUI can request access to */
|
||||
export type AIContextCategory =
|
||||
| 'apps'
|
||||
@@ -57,12 +59,27 @@ export interface AIUIChatRequest {
|
||||
text: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A content-grid request from AIUI's embedded-mode client. Carries only a
|
||||
* `kind` discriminator and an optional `scope` — the iframe never names an
|
||||
* RPC method or params (T-13-34); the broker decides the call. A single
|
||||
* generic channel (not one per content type) so 13-11's music-library wave
|
||||
* can extend `kind` without touching this file again.
|
||||
*/
|
||||
export interface AIUIContentRequest {
|
||||
type: 'content:request'
|
||||
id: string
|
||||
kind: 'films' | 'songs' | 'podcasts' | 'all'
|
||||
scope?: 'own' | 'peers' | 'owned'
|
||||
}
|
||||
|
||||
export type AIUIRequest =
|
||||
| AIUIContextRequest
|
||||
| AIUIActionRequest
|
||||
| AIUIReadyMessage
|
||||
| AIUIThemeRequest
|
||||
| AIUIChatRequest
|
||||
| AIUIContentRequest
|
||||
|
||||
// ─── Archy → AIUI (Responses) ──────────────────────────────────────────────
|
||||
|
||||
@@ -104,12 +121,29 @@ export interface ArchyChatResponse {
|
||||
error?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The node's answer to a `content:request` — adapted grid records
|
||||
* (`archyContentAdapter.ts`'s `adaptContentItems` output) for whichever
|
||||
* buckets the RPC scope produced. `permitted: false` means neither the
|
||||
* `media` nor the `files` category is granted (content:request is treated
|
||||
* as permitted if either is enabled — see `handleContentRequest`); the
|
||||
* three arrays are empty in that case, never omitted, so AIUI can render an
|
||||
* empty-state instead of hanging on an unresolved request.
|
||||
*/
|
||||
export type ArchyContentPush = {
|
||||
type: 'content:push'
|
||||
id: string
|
||||
kind: string
|
||||
permitted: boolean
|
||||
} & Partial<ArchyContentBundle>
|
||||
|
||||
export type ArchyResponse =
|
||||
| ArchyContextResponse
|
||||
| ArchyActionResponse
|
||||
| ArchyThemeResponse
|
||||
| ArchyPermissionsUpdate
|
||||
| ArchyChatResponse
|
||||
| ArchyContentPush
|
||||
|
||||
// ─── All messages ───────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user