feat(iios): generic opaque metadata + manual ticket assign (glue prereqs)
Generic kernel primitives so app backends (e.g. a CRM support glue) can link
their domain to interactions WITHOUT the kernel learning any app vocabulary:
- thread: accept an opaque `metadata` bag on create (merged with membership),
echo it in listThreads, and filter listThreads by `?metadata[key]=value`
(equality on the opaque bag — the kernel never interprets the keys).
- support: accept opaque `metadata` on createTicket/escalate (thread bag
inherited); add SupportService.assignTo — a policy-gated manual assignment
of a ticket to a target actor (POST /v1/support/tickets/:id/assignee).
- SDK @insignia/iios-kernel-client 0.1.2: createThread(metadata),
listThreads({metadata}) filter, createTicket(metadata), escalate(metadata),
assignTicket; ThreadSummary/Ticket expose the opaque bag.
Generic-safety gate holds: no crm/customer/lead in kernel code (opaque values
only). Tests: +2 (metadata create/filter, ticket metadata + assignTo); 31 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,10 +23,14 @@ export class ThreadsController {
|
||||
private readonly session: SessionVerifier,
|
||||
) {}
|
||||
|
||||
/** Generic "my threads" — every thread the caller participates in (last message + unread). */
|
||||
/**
|
||||
* Generic "my threads" — every thread the caller participates in (last message + unread).
|
||||
* `?metadata[key]=value` narrows to threads whose opaque attribute bag matches (equality on each key).
|
||||
*/
|
||||
@Get()
|
||||
async listThreads(@Headers('authorization') auth?: string) {
|
||||
return this.messages.listThreads(this.principal(auth));
|
||||
async listThreads(@Headers('authorization') auth?: string, @Query('metadata') metadata?: Record<string, string>) {
|
||||
const metaFilter = metadata && typeof metadata === 'object' ? metadata : undefined;
|
||||
return this.messages.listThreads(this.principal(auth), metaFilter ? { metadata: metaFilter } : undefined);
|
||||
}
|
||||
|
||||
/** Generic "my annotated messages" (e.g. ?type=save for a personal bookmarks list). */
|
||||
@@ -35,11 +39,11 @@ export class ThreadsController {
|
||||
return this.messages.listMyAnnotated(this.principal(auth), type);
|
||||
}
|
||||
|
||||
/** Create a thread; `membership`/`creatorRole`/`subject` are opaque, app-supplied attributes the kernel stores but never interprets. */
|
||||
/** Create a thread; `membership`/`creatorRole`/`subject`/`metadata` are opaque, app-supplied attributes the kernel stores but never interprets. */
|
||||
@Post()
|
||||
@HttpCode(201)
|
||||
async createThread(@Body() body: { membership?: string; creatorRole?: string; subject?: string }, @Headers('authorization') auth?: string) {
|
||||
return this.messages.openThread(null, this.principal(auth), { membership: body?.membership, creatorRole: body?.creatorRole, subject: body?.subject });
|
||||
async createThread(@Body() body: { membership?: string; creatorRole?: string; subject?: string; metadata?: Record<string, unknown> }, @Headers('authorization') auth?: string) {
|
||||
return this.messages.openThread(null, this.principal(auth), { membership: body?.membership, creatorRole: body?.creatorRole, subject: body?.subject, metadata: body?.metadata });
|
||||
}
|
||||
|
||||
/** Governed membership: add a user (by userId) to a thread — policy enforces DM cap / roles. */
|
||||
|
||||
Reference in New Issue
Block a user