Technical specification for the Iris AI Assistant platform. Documents the shared type system, the runnable API surface, the conversation/memory/agent library clusters, persistence, events, configuration, and integration contracts as they exist in
libs/iris/**andapps/iris/**.Grounding rule: every entity, field, enum value, endpoint, and configuration key below is traceable to source. Items not yet implemented are labelled
(planned). Where the prior draft asserted detail that does not exist in code, it has been removed.
This document is the authoritative source for what is actually implemented in
the Iris codebase, as opposed to what is planned. It was written directly
against the source files in libs/iris/types/src/,
libs/iris/conversation-core/src/, libs/iris/conversation-orchestration/src/,
libs/iris/agents/src/, libs/iris/core/src/config/,
libs/iris/database/prisma/schema.prisma, apps/iris/api/src/,
libs/shared/event-bus/src/, and the libs/iris/integrations/ packages.
Package counts come from find libs/iris -name package.json (258) and
find apps/iris -name package.json (11).
Several things asserted in earlier drafts were removed because they could not be
traced to code: Kafka topics, the iris_* PostgreSQL tables, Qdrant
collections, Redis cache-key tables, the WebSocket event catalog,
Free/Pro/Enterprise plan-limit tiers, the model-routing decision matrix, and
several environment variables. Every section that describes target-state
infrastructure that is not yet wired is explicitly labelled as planned.
Table of Contents#
- Scope and Implementation Status
- Shared Type System (
@iris/types) - Conversation Domain Objects
- Memory Domain Objects
- Agent, Task, and Action Domain Objects
- Model and Provider Domain Objects
- Tool Domain Objects
- User, Session, and Preference Objects
- Event Type System
- Conversation Engine (
@iris/conversation-core) - HTTP / GraphQL API Surface (
@iris/api) - Persistence
- Configuration (
@iris/config,@iris/core) - Library Clusters
- Cross-Domain Integration Libraries
- Rollback-Safe In-Match Commentary Contract (V2 Consumer)
- V2 Accessibility Bridge Contract (V2 Consumer)
- V2 Mobile Companion App (V2 Consumer)
- Acceptance Criteria
1. Scope and Implementation Status#
Iris is partially implemented. The codebase is a large package tree:
libs/iris/**— 258package.jsonfiles (Nx projects), organised into functional clusters (conversation, memory, knowledge, agents, code, privacy, platform, multimodal, emotional, accessibility, analytics, integrations, and foundation libraries).apps/iris/**— 11 applicationpackage.jsonfiles:@iris/api,@iris/dashboard,@iris/desktop,@iris/developer-portal,@iris/marketplace,@iris/mobile,@iris/wearable-shared,@iris/browser-extension,@iris/pwa,@iris/widget,@iris/xr-shared.- There is no
services/iris/directory.
The most fully built-out, verifiable pieces — and the focus of the concrete sections of this document — are:
@iris/types— the shared domain type system (8 source modules with tests).@iris/api— a runnable Hono REST + GraphQL service with conversation, memory, agent, and knowledge routes.@iris/conversation-core— the dialogue engine: conversation manager, turn manager, context window, dialogue state machine.@iris/conversation-orchestration— multi-model orchestration plus the V2 match-commentary contract.@iris/agents— a mobile-safe built-in agent catalog.@iris/core/@iris/config— foundation utilities and configuration.libs/iris/database— a Prisma schema (3 models).
Many cluster leaf packages exist as Nx projects with their own src/; this
document describes the ones whose contracts are load-bearing and verifiable. It
does not claim production deployment, Kubernetes topology, or external
infrastructure that is not present in the repository.
2. Shared Type System (@iris/types)#
libs/iris/types/src/ is the canonical domain vocabulary. All other Iris
packages import their core types from here rather than defining them locally,
which ensures that a ConversationId in the conversation engine is the same
type as a ConversationId in the memory system. There are eight source modules,
each with a co-located *.test.ts:
| Module | Defines |
|---|---|
conversation.ts |
Conversations, messages, turns, threads, content blocks |
memory.ts |
The four-tier memory model |
agent.ts |
Agents, tasks, actions, executions, plans, multi-agent |
model.ts |
Providers, models, requests, responses, streaming, costs |
tool.ts |
Tool definitions, calls, executions, registry interfaces |
user.ts |
Users, profiles, preferences, sessions, auth, quotas |
events.ts |
Domain events, event bus / event store interfaces |
index.ts |
Re-exports |
Two conventions are used throughout the type system:
- Branded ID types — every entity has its own opaque ID type (e.g.
ConversationId,MessageId,AgentId) defined asstring & { readonly [Brand]: ... }. This prevents accidentally passing aUserIdwhere aConversationIdis expected. Acreate*Id()factory produces IDs in the form${prefix}_${base36-timestamp}-${random}(e.g.conv_…,msg_…,agt_…). The full list:ConversationId,MessageId,ThreadId,TurnId,MemoryId,MemoryBlockId,MemoryChunkId,AgentId,TaskId,ActionId,ExecutionId,ToolId,ToolCallId,ToolExecutionId,UserId,SessionId,ProfileId,EventId,CorrelationId. - Zod schemas — most string-union types ship a paired
…Schema(z.enum([...])) for runtime validation, so the same enum definition works for both TypeScript type checking and API request validation.
3. Conversation Domain Objects#
The conversation module defines the full lifecycle of a dialogue: from the
top-level Conversation container through Thread (a branch of the
conversation) down to individual Message objects and the ContentBlock[]
array that carries the actual content. Every field is readonly on the core
types, enforcing immutable records at the type level.
3.1 Conversation#
Conversation (all fields readonly):
| Field | Type | Meaning |
|---|---|---|
id |
ConversationId |
Branded conversation ID |
title |
string? |
Optional title |
description |
string? |
Optional description |
ownerId |
string |
Owning user |
status |
ConversationStatus |
Lifecycle status |
config |
ConversationConfig |
Model, mode, sampling, tooling config |
participants |
ParticipantState[] |
Participants with per-participant counts |
threads |
Thread[] |
Branch threads |
activeThreadId |
ThreadId? |
Currently active thread |
stats |
ConversationStats |
Aggregate counters |
createdAt |
Date |
Creation time |
updatedAt |
Date |
Last update |
lastMessageAt |
Date? |
Time of last message |
metadata |
Record<string,…>? |
Arbitrary metadata |
Related inputs: ConversationInput (create), ConversationUpdate (patch).
ConversationConfig — mode (ConversationMode), model (string),
provider?, systemPrompt?, temperature?, maxTokens?, topP?, topK?,
stopSequences?, tools? (string IDs), contextWindow?,
summarizationEnabled?, streamingEnabled?.
ConversationStats — messageCount, turnCount, threadCount,
participantCount, totalTokens, inputTokens, outputTokens,
averageResponseMs?, topics?.
ConversationContext — assembled context for a model call: systemPrompt?,
messages, tools?, summary?, contextWindow, totalTokens, truncated.
3.2 Message#
Message: id (MessageId), conversationId, threadId?,
parentMessageId?, role (ParticipantRole), participantId, content
(ContentBlock[]), status (MessageStatus), createdAt, updatedAt?,
metadata? (MessageMetadata).
MessageMetadata: model?, provider?, stopReason? (FinishReason),
tokenUsage? (TokenUsage), latencyMs?, cached?, plus open keys.
TokenUsage (conversation module): inputTokens, outputTokens,
totalTokens, cacheReadTokens?, cacheWriteTokens?.
MessageDelta carries streaming updates with type of content_block_start,
content_block_delta, content_block_stop, or message_delta.
3.3 Content Blocks#
ContentType union: text, image, audio, video, file, code,
tool_use, tool_result. ContentBlock is the discriminated union of
TextContent, ImageContent, AudioContent, VideoContent, FileContent,
CodeContent, ToolUseContent, ToolResultContent. TextContent carries
optional TextAnnotation[] (bold | italic | code | link | mention | highlight
with startIndex/endIndex). Type guards: isTextContent, isImageContent,
isToolUseContent, isToolResultContent; helpers extractText, textContent,
normalizeContent.
3.4 Turn and Thread#
Turn: id (TurnId), conversationId, index, userMessage,
assistantMessage?, toolUses? (ToolExchange[]), startedAt,
completedAt?, durationMs?, tokenUsage?, metadata?. ToolExchange pairs
a ToolUseContent with a ToolResultContent. TurnSummary is a compressed
turn record (userIntent, assistantAction, outcome?, topics?,
tokenCount).
Thread: id (ThreadId), conversationId, parentThreadId?,
branchFromMessageId?, name?, description?, status (ThreadStatus),
messageCount, createdAt, updatedAt, resolvedAt?, metadata?.
3.5 Conversation Enums#
The following enums define the status vocabulary for conversation, message, and thread lifecycle management. They are the values that appear in status fields throughout the REST API, GraphQL schema, and event payloads.
| Enum | Values |
|---|---|
ParticipantRole |
user, assistant, system, tool, function |
ContentType |
text, image, audio, video, file, code, tool_use, tool_result |
MessageStatus |
pending, streaming, completed, failed, cancelled, filtered |
FinishReason |
stop, length, tool_use, content_filter, error, cancelled |
ThreadStatus |
active, resolved, archived, deleted |
ConversationStatus |
active, paused, completed, archived, deleted |
ConversationMode |
chat, completion, assistant, agent |
3.6 Conversation Operations and Events#
Operation request/response types: SendMessageRequest/SendMessageResponse,
RegenerateMessageRequest, BranchConversationRequest.
ConversationEventType (the type-module union): conversation.created,
conversation.updated, conversation.deleted, message.created,
message.updated, message.deleted, message.streaming, turn.started,
turn.completed, thread.created, thread.resolved, participant.joined,
participant.left. Event objects: ConversationEvent, MessageEvent,
TurnEvent, ThreadEvent, union AnyConversationEvent.
4. Memory Domain Objects#
memory.ts documents itself as a MemGPT-inspired tiered memory model. The
type module is the authoritative definition of the four-tier hierarchy; it is
referenced by all 22 packages in libs/iris/memory/ and by the /api/v1/memory
routes in @iris/api.
4.1 Memory Tiers and Classification#
MemoryTier defines four tiers: core, working, archival, episodic.
Each tier has a distinct timescale and entry type, described in the subsections
below.
The four-tier model is authoritative. A
semantictier and along_termtier (named in an earlier draft) are not in the enum. "Semantic" retrieval is a property of archival memory (vector embeddings), not a separate tier; episodic memory covers long-term event history.
MemoryType — 13 values: persona, goal, preference, fact, event,
conversation, entity, relationship, skill, instruction, context,
summary, reflection.
MemoryImportance — critical, high, medium, low, trivial, with
MEMORY_IMPORTANCE_VALUES mapping them to 100 / 75 / 50 / 25 / 10.
CoreMemorySection — persona, user, system, goals.
4.2 Tier Entry Types#
Core memory — CoreMemoryBlock (id, section, content, maxTokens,
currentTokens, timestamps); CoreMemoryConfig groups the persona/user/system/
goals blocks under a totalTokenBudget; CoreMemoryUpdate operations are
replace | append | prepend | insert.
Working memory — WorkingMemoryEntry (id, type, content, relevance,
recency, accessCount, timestamps, expiresAt?). WorkingMemoryConfig
(maxEntries, maxTokens, decayRate, relevanceThreshold, ttlMs?).
WorkingMemoryState bundles entries + config.
Archival memory — ArchivalMemoryEntry (id, type, content,
summary?, importance, embedding? (number[]), embeddingModel?,
topics?, entities? (EntityReference[]), source? (MemorySource),
timestamps, accessCount). ArchivalSearchQuery / ArchivalSearchResult
(matchType: semantic | keyword | hybrid). ArchivalMemoryConfig:
embeddingModel, embeddingDimensions, indexType (flat | hnsw | ivf),
similarityMetric (cosine | euclidean | dot_product), autoSummarize,
extractEntities.
Episodic memory — EpisodicMemoryEntry (id, eventType, timestamp,
content, participants?, location?, emotion? (EmotionState),
outcome?, relatedMemories?, conversationId?, turnId?).
EpisodicEventType — 12 values: conversation_start, conversation_end,
user_message, assistant_response, tool_use, error, milestone,
decision, learning, preference_change, goal_update, context_switch.
EmotionState: valence (-1..1), arousal (0..1), dominance?, label?.
4.3 Generic Memory, Chunks, Operations#
MemoryBlock is the tier-agnostic record (tier, type, content,
importance, tokenCount, embedding?, references?, access counters).
MemoryChunk and MemoryContext assemble tier content into an LLM context
window under a token budget. MemoryContextConfig carries coreBudgetRatio /
workingBudgetRatio / archivalBudgetRatio / episodicBudgetRatio.
MemoryOperation — create, read, update, delete, search, summarize,
merge, split, archive, restore. MemorySummary records a summarization
with a compressionRatio.
4.4 Default Configurations#
These constants represent the out-of-box behavior when no explicit configuration
is provided. The DEFAULT_MEMORY_CONTEXT_CONFIG budget ratios (core 15%,
working 40%, archival 30%, episodic 15%) determine how the LLM context window is
partitioned between tiers on each turn.
| Constant | Notable values |
|---|---|
DEFAULT_WORKING_MEMORY_CONFIG |
maxEntries 100, maxTokens 8000, decayRate 0.95, relevanceThreshold 0.3, ttlMs 3600000 |
DEFAULT_ARCHIVAL_MEMORY_CONFIG |
embeddingModel 'text-embedding-3-small', embeddingDimensions 1536, indexType 'hnsw', similarityMetric 'cosine' |
DEFAULT_MEMORY_CONTEXT_CONFIG |
tokenBudget 32000; budget ratios core 0.15 / working 0.4 / archival 0.3 / episodic 0.15 |
5. Agent, Task, and Action Domain Objects#
The agent module defines the runtime objects for autonomous task execution. An
Agent holds configuration (what capabilities it has, which model it uses, what
tools it can invoke) and a live AgentState (what it is currently doing, how
many turns it has taken, any errors). A Task represents a unit of work
assigned to an agent, and Action represents a single step within that task.
The nesting is: one Agent → many Tasks → each Task has many Actions.
5.1 Agent#
AgentConfig: id (AgentId), name, description?, role (AgentRole),
model, provider?, systemPrompt, capabilities (AgentCapability[]),
tools?, maxTurns?, maxTokensPerTurn?, temperature?, topP?,
stopSequences?, metadata?. AgentState adds runtime status, current task/
action, counters, errors (AgentError[]), metrics (AgentMetrics). Agent
= config + state + timestamps.
DEFAULT_AGENT_CONFIG — role: 'assistant',
capabilities: ['conversation','tool_use'], maxTurns: 20, temperature: 0.7.
5.2 Task and Action#
Task: id (TaskId), name, agentId, parentTaskId?, status,
priority, input (TaskInput), output? (TaskOutput), actions
(ActionRecord[]), dependencies?, blockedBy?, timestamps, timeoutMs?,
retryCount, maxRetries. TaskInput type is
text | structured | multimodal; TaskConstraints caps tokens/actions/duration
and allows tool allow/forbid lists. TaskOutput may carry TaskArtifact[].
Action: id (ActionId), taskId, agentId, type (ActionType), name,
input, status, result? (ActionResult), timing, retryCount.
ActionResult may include ActionSideEffect[] (file_created,
file_modified, file_deleted, memory_updated, state_changed,
external_call, each with a reversible flag).
DEFAULT_TASK_CONSTRAINTS — maxTokens: 100000, maxActions: 50,
maxDurationMs: 300000.
5.3 Execution, Delegation, Planning, Multi-Agent#
- Execution —
ExecutionContext(token budget, deadline, variables, tools, capabilities),ExecutionResult,ExecutionTrace/ExecutionTraceEntry(entrytype:thought | action | observation | error | decision). - Delegation —
DelegationRequest/DelegationResultfor agent-to-agent task handoff. - Planning —
ExecutionPlan(steps,dependencies,confidence,alternatives?),PlanStep,PlanDependency(requires | enables | blocks). - Multi-agent —
AgentTeam(coordinatorId,routingStrategy:round_robin | capability_based | load_balanced | custom),AgentMessage(request | response | notification | broadcast).
5.4 Agent Enums#
The following enums define the vocabulary for agent roles, lifecycle states,
declared capabilities, and task/action status. Note that the runnable
@iris/api agent route validates a different, route-local set of these values
(see §11.3) — the API layer is not yet fully aligned to the types defined here.
| Enum | Values |
|---|---|
AgentRole |
assistant, specialist, coordinator, executor, analyst, reviewer, custom |
AgentStatus |
initializing, ready, busy, waiting, paused, error, terminated |
AgentCapability |
conversation, tool_use, code_execution, file_access, web_access, memory_access, delegation, planning, reflection, learning |
TaskStatus |
pending, queued, running, paused, completed, failed, cancelled, timeout |
TaskPriority |
critical, high, normal, low, background (TASK_PRIORITY_VALUES 100/75/50/25/10) |
ActionType |
tool_call, message, delegate, memory_operation, code_execution, file_operation, web_request, wait, decision, custom |
ActionStatus |
pending, executing, completed, failed, cancelled, skipped |
Note: the runnable
@iris/apiagent route validates a different, route-local set of capability/status enums (see §11.3) — the API layer is not yet aligned to@iris/types.
5.5 Built-In Agent Catalog (@iris/agents)#
libs/iris/agents/src/index.ts exports a static catalog used by V2 companion
surfaces. IRIS_BUILT_IN_AGENT_IDS — 9 IDs: general, codeassist,
researcher, writer, analyst, support, creative, operations,
supervisor. Each IrisBuiltInAgentDescriptor carries label, specialty,
summary, surfaces (web | mobile | desktop | service | game-companion),
capabilities (IrisAgentCapability), defaultToolCategories
(IrisAgentToolCategory), maxAutonomousSteps, requiresHumanReview, and
implementationPackages. Exported functions: listIrisBuiltInAgents,
getIrisBuiltInAgent, selectIrisAgentForIntent (intent-ordered selection over
IrisAgentIntent), resolveIrisAgentsLaunchReadiness.
The descriptors delegate to implementation packages — @iris/agents-core,
@iris/archetypes, @iris/multi-agent, @iris/workflows, and (for
supervisor) @iris/agent-marketplace.
6. Model and Provider Domain Objects#
The model module defines the typed layer between Iris and AI providers. It gives
every provider a uniform ModelDefinition shape so the model router
(@iris/model-routing) can compare providers on price, context window, and
capability without hardcoding provider-specific logic into the routing decision.
The WELL_KNOWN_MODELS registry is the authoritative list of supported models
with real pricing and context metadata.
6.1 Providers and Models#
ModelProvider — anthropic, openai, google, cohere, mistral, groq,
together, aws-bedrock, azure-openai, local, custom. ProviderStatus —
available, degraded, unavailable, unknown. ProviderConfig and
ProviderHealth (latencyMs?, quotaRemaining?).
ModelDefinition: id, provider, name, displayName?, tier
(ModelTier), capabilities (ModelCapability[]), contextWindow,
maxOutputTokens, inputPricePerMToken, outputPricePerMToken,
cachePricePerMToken?, embeddingDimensions?, dates, isDefault?.
ModelTier — flagship, standard, fast, economy, embedding.
ModelCapability — chat, completion, embedding, vision, audio,
video, function_calling, tool_use, streaming, json_mode,
system_prompt, multi_turn, caching.
6.2 Model Registry (WELL_KNOWN_MODELS)#
The registry ships eight models with real pricing and context-window metadata.
claude-sonnet-4 is the default (isDefault: true). MODEL_ALIASES maps short
names like claude, gpt4, and gemini to their full model IDs so callers can
use convenient names. The helpers resolveModelAlias, getModelDefinition,
modelHasCapability, estimateCost, findModels, and getDefaultModel are
the primary API for querying this registry.
Eight registered models with real pricing/context metadata:
| Key | Provider | Tier | Context | Default |
|---|---|---|---|---|
claude-opus-4-5 |
anthropic | flagship | 200K | no |
claude-sonnet-4 |
anthropic | standard | 200K | yes |
claude-haiku-3-5 |
anthropic | fast | 200K | no |
gpt-4o |
openai | flagship | 128K | no |
gpt-4o-mini |
openai | fast | 128K | no |
gemini-2-0-flash |
fast | 1M | no | |
text-embedding-3-small |
openai | embedding | 8191 | no |
text-embedding-3-large |
openai | embedding | 8191 | no |
MODEL_ALIASES maps short names (claude, claude-opus, gpt4, gemini,
embed-small, …) to model IDs. Helpers: resolveModelAlias,
getModelDefinition, modelHasCapability, estimateCost, findModels,
getDefaultModel.
6.3 Requests, Responses, Streaming, Cost#
ChatCompletionRequest (model, messages, tools?, toolChoice?,
responseFormat?, sampling params, stream?), EmbeddingRequest,
ChatCompletionResponse, EmbeddingResponse. StopReason — end_turn,
stop_sequence, max_tokens, tool_use, content_filter, error.
StreamEventType — message_start, content_block_start,
content_block_delta, content_block_stop, message_delta, message_stop,
error. Cost types: CostEstimate, UsageTracking (per-model and per-provider
breakdowns). Caching: CacheControl (ephemeral | persistent),
CachedContentBlock.
7. Tool Domain Objects#
The tool module defines the contract for everything an agent can invoke. A
ToolDefinition declares what a tool does, what inputs it accepts
(JSON-Schema-shaped), what risk level it carries, and what rate limits apply. A
ToolSpecification is the trimmed version passed directly to an LLM (name,
description, input_schema), containing only what the model needs to decide
whether to call the tool. The registry and executor interfaces (IToolRegistry,
IToolExecutor, IToolValidator) are the extension points for registering new
tools.
ToolDefinition: id (ToolId), name, displayName?, description,
category (ToolCategory), version, status (ToolStatus), riskLevel
(ToolRiskLevel), inputSchema (ToolInputSchema, JSON-Schema-shaped),
outputSchema?, examples?, permissions?, rateLimit? (ToolRateLimit),
timeout?, retryConfig? (ToolRetryConfig), tags?. ToolSpecification is
the trimmed shape passed to an LLM (name, description, input_schema).
| Enum | Values |
|---|---|
ToolCategory |
code, file, web, data, communication, search, analysis, generation, memory, utility, custom |
ToolRiskLevel |
safe, low, medium, high, critical |
ToolStatus |
enabled, disabled, deprecated, experimental |
ToolExecutionStatus |
pending, validating, executing, completed, failed, timeout, cancelled, rate_limited |
FileOperation |
read, write, append, delete, list, copy, move |
WebOperation |
fetch, search, scrape, screenshot |
CodeOperation |
execute, analyze, format, lint, test |
Execution types: ToolExecutionContext, ToolExecutionResult,
ToolExecutionError (type:
validation | execution | timeout | permission | rate_limit | unknown),
ToolResult. Registry/executor interfaces: IToolRegistry, IToolExecutor,
IToolValidator. Defaults: DEFAULT_TOOL_RETRY_CONFIG (maxRetries 3,
initialDelayMs 1000, maxDelayMs 30000, backoffMultiplier 2),
DEFAULT_TOOL_RATE_LIMIT (maxCallsPerMinute 60, maxCallsPerHour 1000,
maxConcurrent 10), DEFAULT_TOOL_TIMEOUT = 30000.
8. User, Session, and Preference Objects#
The user module defines the core identity, authentication, and preference
objects. User is the minimal identity record; UserProfile adds rich profile
data. UserPreferences groups five preference namespaces (GeneralPreferences,
AssistantPreferences, NotificationPreferences, PrivacyPreferences,
AccessibilityPreferences) under a single object, each with a DEFAULT_*
constant and a createDefaultPreferences(userId) factory. IrisJwtClaims
defines the token payload for JWT-authenticated sessions.
User: id (UserId), email, username?, displayName, name fields,
avatarUrl?, status (UserStatus), role (UserRole), accountType
(AccountType), organizationId?, teamIds?, permissions?, timestamps.
UserProfile adds bio/location/social/expertise plus language, timezone,
locale.
| Enum | Values |
|---|---|
UserStatus |
active, inactive, pending, suspended, deleted |
UserRole |
user, premium, admin, system |
AccountType |
individual, team, enterprise |
ThemePreference |
light, dark, system |
VoiceStyle |
natural, professional, friendly, concise |
ResponseLength |
brief, moderate, detailed, comprehensive |
SessionStatus |
active, idle, expired, revoked |
DeviceType |
desktop, mobile, tablet, unknown |
AuthMethod |
password, oauth, sso, api_key, magic_link |
OAuthProvider |
google, github, microsoft, apple, facebook |
UsagePeriod |
hour, day, week, month |
UserPreferences groups GeneralPreferences, AssistantPreferences,
NotificationPreferences, PrivacyPreferences, AccessibilityPreferences.
AssistantPreferences includes defaultModel?, voiceStyle, responseLength,
showThinking, streamResponses, rememberContext, contextRetentionDays,
preferredTools?, disabledTools?. Each preference group ships a DEFAULT_*
constant and createDefaultPreferences(userId) assembles them.
Authentication: AuthContext, IrisJwtClaims (sub, userId, sessionId,
email, roles, permissions?, iat/exp/iss/aud), LoginRequest,
LoginResponse. Quotas: UserQuota (conversation/message/token/apiCall/storage
limits + used counters), UsageStats. Guard isQuotaExceeded compares used vs.
limit across all five quota dimensions.
Quota limits are fields on
UserQuotarecords; they are not hard-coded plan tiers anywhere inlibs/iris. There is no per-plan limits table in code; an earlier draft's "Free/Pro/Enterprise" table has been dropped.
9. Event Type System#
The event module defines the type-level contracts for how Iris components notify each other about state changes. It uses a CloudEvents-inspired envelope that carries routing metadata (category, aggregate, correlation ID, trace IDs) separately from the event payload. This lets consumers filter and route events based on the envelope alone, without deserializing the payload.
Note that this module defines TypeScript interfaces, not a running message
broker. The shared runtime transport is the @oshun/event-bus (see §12.3), and
no libs/iris package currently publishes events onto it. The types here are
the agreed contract for when publishing is implemented.
events.ts defines a CloudEvents-style domain-event model. DomainEvent<T>:
id (EventId), type (string), category (EventCategory), aggregateId,
aggregateType, payload, metadata (EventMetadata), occurredAt,
sequenceNumber?. EventMetadata carries correlationId, causationId?,
source, version, timestamp, optional userId/sessionId/agentId/
traceId/spanId/tags/context.
EventCategory — conversation, message, agent, task, memory, user,
session, system, tool, model, error. EventSeverity — debug,
info, warning, error, critical.
The IrisEventType union is composed from per-category unions. Representative
members (string literals defined in code):
| Category | Event types (literal type strings) |
|---|---|
| Conversation | conversation.created/updated/deleted/archived/restored |
| Message | message.created/updated/deleted, message.streaming.started/delta/completed/error |
| Agent | agent.created/updated/deleted/started/stopped/paused/resumed/error |
| Task | task.created/started/completed/failed/cancelled/paused/resumed/progress |
| Memory | memory.created/updated/deleted/accessed/summarized/compacted/promoted/demoted |
| Tool | tool.registered/unregistered/called/completed/failed/timeout |
| Model | model.request.started/completed/failed/rate_limited, model.stream.started/chunk/completed |
| User | user.created/updated/deleted/activated/deactivated, user.preferences.updated |
| Session | session.created/refreshed/expired/revoked |
| System | system.started/stopped, system.health.changed, system.config.updated, system.maintenance.started/completed |
| Error | error.occurred/recovered/escalated |
Typed payloads exist for the principal events (ConversationCreatedPayload,
MessageCreatedPayload, TaskCompletedPayload, MemoryCreatedPayload,
ToolCalledPayload, ModelRequestCompletedPayload, …).
Interfaces IEventBus (publish, publishBatch, subscribe, unsubscribe,
getSubscriptionCount) and IEventStore (append, getEvents,
getEventsByType, getLatestEvent) define the bus/store contracts, plus
event-replay types. Factories: createEventMetadata, createDomainEvent,
createEventId, createCorrelationId.
These are type-level contracts in
@iris/types. They are not the same as the shared@oshun/event-busruntime (see §12.3); nolibs/irispackage currently publishes domain events onto a broker, and there are no Kafka topics — the prior draft's Kafka topic table was unfounded.
10. Conversation Engine (@iris/conversation-core)#
@iris/conversation-core is one of the most fully built-out packages in the
iris codebase. It implements the core dialogue runtime: a state machine that
tracks every conversation from idle through generating to completed, a
context window manager that assembles and truncates the message history to fit
within token budgets, and a conversation manager that handles persistence,
branching, incognito mode, search indexing, snapshots, and export.
libs/iris/conversation-core/src/ is the dialogue engine. Public exports
(index.ts) include createConversationManager, createContextWindow,
createDialogueStateMachine, plus turn and history managers.
10.1 Dialogue State Machine#
The dialogue state machine is the heart of the conversation engine. It enforces
that a conversation can only move between states through defined transitions —
for example, you cannot jump from idle directly to streaming without going
through processing and generating. This makes it impossible for a bug to
leave a conversation stuck in an intermediate state silently.
DialogueStateType defines 11 states: idle, awaiting_input, processing,
generating, streaming, tool_executing, awaiting_tool_result,
completed, error, paused, terminated.
DialogueEvent — discriminated union driving transitions: USER_MESSAGE,
SYSTEM_MESSAGE, START_GENERATION, START_STREAMING, STREAM_CHUNK,
STREAM_COMPLETE, GENERATION_COMPLETE, TOOL_CALL, TOOL_RESULT, ERROR,
PAUSE, RESUME, TERMINATE, RESET.
A static STATE_TRANSITIONS table (dialogue-state.ts) maps each state to its
allowed (event → next state) pairs. Representative edges:
idle --USER_MESSAGE--> processing;
processing --START_GENERATION--> generating;
generating --TOOL_CALL--> tool_executing;
streaming --STREAM_COMPLETE--> completed; any non-terminal state
--ERROR--> error and --TERMINATE--> terminated. StateTransitionResult
records success, fromState, toState, event, and an error string on a
rejected transition. DialogueStateData tracks previousState,
transitionCount, pendingToolCalls, and streamingState.
10.2 Context Window#
ContextWindowConfig — maxTokens, systemPromptReserved, responseReserved,
enableSummarization, summarizationThreshold (0–1 ratio),
maxMessagesBeforeSummarize, minMessagesToKeep, enableTruncation,
truncationStrategy (oldest_first | least_important | smart),
charsPerToken. ContextWindowState and ContextAddResult report token
accounting and whether summarization/truncation occurred.
ContextOptimizationOptions / OptimizedContextResult describe budget-aware
message selection with per-role weights and optional focus terms.
10.3 Conversation Manager and Sessions#
ConversationManagerConfig controls persistence, auto-save interval, in-memory
cap, branching, search, export, and a default incognito policy.
ConversationSession bundles a MutableConversation, DialogueStateData,
ContextWindowState, the active/all turns (TurnState), and session metadata.
CreateConversationOptions accepts
incognito?: boolean | ConversationIncognitoEnableOptions.
Incognito mode — ConversationIncognitoPolicy toggles disablePersistence,
disableSearchIndexing, disableExport, disableSnapshots,
disableBookmarks, disableTagging, scrubMessageMetadata.
ConversationIncognitoMode records the active mode (enabledAt, reason?,
expiresAt?, policy).
TurnManagerConfig caps concurrent tool calls, tool-execution timeout, retries,
and toggles turn summaries. TurnState tracks activeToolCalls /
completedToolCalls maps and a token accumulator.
10.4 History, Snapshots, Export#
ConversationSnapshot captures dialogue/context/stats state for rollback, with
reason of auto | manual | before_tool | before_branch | error_recovery.
RollbackResult reports removed messages/turns. ConversationStorage is the
storage-adapter interface (save/load conversations, messages, turns, snapshots).
ConversationExportPayload / ConversationImportResult define portable export
bundles (format: object | json).
10.5 Conversation Engine Events#
ConversationEngineEvent is the engine-internal event union (distinct from the
@iris/types events.ts model). Members include
conversation.created/ updated/deleted,
conversation.incognito_enabled/disabled,
message.created/updated/streaming/completed, turn.started/completed/failed,
tool.called/completed, state.changed, context.summarized/truncated,
snapshot.created, rollback.completed, thread.created/switched, error.
Other conversation-core source files: context-window.ts,
conversation-manager.ts, conversation-history.ts, turn-manager.ts,
streaming-response-generator.ts, conversation-search-index.ts,
conversation-bookmarks.ts, conversation-tags.ts,
conversation-highlights.ts, conversation-sharing.ts,
conversation-analytics-tracker.ts, in-memory-conversation-storage.ts.
11. HTTP / GraphQL API Surface (@iris/api)#
@iris/api is the only runnable service in the iris codebase today. It is a
single Hono process that hosts all route groups — conversation, memory, agent,
knowledge, GraphQL, health, and docs — under one process on port 3100 (default).
Authentication is not yet implemented; every route resolves the user via a
getUserId() placeholder returning 'user-001'. Rate limiting is applied
per-route using preset tiers (readonly, standard, heavy, streaming,
search).
apps/iris/api/ is a runnable Hono service. src/index.ts wires global
middleware (request-id, distributed tracing, secureHeaders, CORS, request
logging, response timing), mounts the route groups, and serves on PORT
(default 3100). Logging and tracing are initialised from env via
@iris/core/logging and @iris/core/tracing.
Mount points:
| Base path | Router |
|---|---|
/health |
healthRoutes |
/api/v1/conversations |
conversationRoutes |
/api/v1/memory |
memoryRoutes |
/api/v1/agents |
agentRoutes |
/api/v1/knowledge |
knowledgeRoutes |
/graphql |
GraphQL handler (playground enabled) |
/docs |
docsRoutes (OpenAPI + code samples) |
/ |
Service info JSON |
All routes apply a per-route rateLimiter preset (readonly, standard,
heavy, streaming, search). Request bodies are validated with
@hono/zod-validator. Authentication is not yet implemented — every route
resolves the user via a getUserId() placeholder returning 'user-001'.
11.1 Health Endpoints#
| Method | Path | Description |
|---|---|---|
| GET | /health |
Basic liveness JSON (status, service, version) |
| GET | /health/live |
Liveness probe ({ status: 'alive' }) |
| GET | /health/ready |
Readiness probe — real Postgres + Redis health round-trips via @oshun/database; 503 if a subsystem is unhealthy |
| GET | /health/detailed |
Runtime + process memory metrics |
11.2 Conversation Endpoints (/api/v1/conversations)#
| Method | Path | Description |
|---|---|---|
| GET | / |
List (paginated; status, search, sortBy, sortOrder query params) |
| POST | / |
Create (CreateConversationSchema) |
| GET | /:id |
Get by ID |
| PATCH | /:id |
Update title / systemPrompt / settings / metadata |
| DELETE | /:id |
Delete |
| POST | /:id/archive |
Archive |
| POST | /:id/branch/:messageId |
Branch from a message |
| GET | /stats/summary |
Per-user conversation stats |
| POST | /:id/messages |
Send a message, returns user + assistant message |
| GET | /:id/messages |
List messages (limit, before, after) |
| POST | /:id/regenerate |
Regenerate last assistant response |
| POST | /:id/stream |
Stream a response via Server-Sent Events |
CreateConversationSchema: title?, systemPrompt?, model (default
claude-3-5-sonnet-20241022), settings? (temperature default 0.7 [0–2],
maxTokens default 4096 [1–100000], topP?, presencePenalty?,
frequencyPenalty?), metadata?. SendMessageSchema: content (1–100000
chars), role (user | system, default user), optional contentBlocks
(type ∈ text|image|audio|code|tool_use|tool_result), metadata?,
parentMessageId?.
11.3 Agent Endpoints (/api/v1/agents)#
Agent CRUD plus enable/disable/stats, per-agent tool management, and tasks:
| Method | Path | Description |
|---|---|---|
| GET / POST | / |
List / create agents |
| GET / PATCH / DELETE | /:id |
Get / update / delete an agent |
| POST | /:id/enable, /:id/disable |
Enable / disable an agent |
| GET | /:id/stats |
Agent statistics |
| GET / POST | /:id/tools |
List / add agent tools |
| DELETE / PATCH | /:id/tools/:toolName |
Remove / enable-disable a tool |
| GET / POST | /tasks |
List / create tasks |
| GET | /tasks/:taskId |
Get a task |
| POST | /tasks/:taskId/{run,pause,resume,cancel} |
Task lifecycle |
| GET | /:id/tasks |
List tasks for one agent |
Route-local validation enums (note: not the @iris/types enums): agent
status ∈ idle, running, paused, error, completed, disabled; capabilities ∈
conversation, code_generation, code_execution, web_search, file_operations, tool_use, memory_access, delegation, planning, reasoning;
task priority ∈ critical, high, medium, low; task status ∈
pending, queued, running, paused, completed, failed, cancelled.
11.4 Memory Endpoints (/api/v1/memory)#
Tier-segmented routes plus generic memory CRUD:
| Method | Path | Description |
|---|---|---|
| GET | /core |
All core memory blocks |
| GET / PUT | /core/:section |
Get / replace a core block |
| POST | /core/:section/append |
Append to a core block |
| GET | /working/:sessionId |
Working memory for a session |
| POST | /working |
Add a working-memory entry |
| DELETE | /working/:sessionId |
Clear a session's working memory |
| GET | /archival/search |
Search archival memory |
| GET / POST | /archival |
List / add archival entries |
| GET / POST | /episodic |
List / record episodic events |
| POST | / |
Create a memory of any tier |
| GET / PATCH / DELETE | /:id |
Get / update / delete a memory |
| POST | /search |
Cross-tier semantic search |
| GET | /stats/summary |
Memory statistics |
| POST | /cleanup |
Delete expired memories |
CreateMemorySchema validates tier ∈ core, working, archival, episodic;
type against the 13-value MemoryType enum; importance against
critical, high, medium, low, trivial. Note the route's core-section validation
accepts persona, human, goals, instructions, custom — the runnable API uses
human where @iris/types CoreMemorySection uses user; the API layer is
not yet fully aligned to the type module.
11.5 Knowledge Endpoints (/api/v1/knowledge)#
Knowledge bases, documents, chunks, and search/RAG:
| Method | Path | Description |
|---|---|---|
| GET / POST | / |
List / create knowledge bases |
| GET / PATCH / DELETE | /:id |
KB get / update / delete |
| GET / POST | /:id/documents |
List / add documents |
| GET / PATCH / DELETE | /:id/documents/:documentId |
Document get / update / delete |
| GET | /:id/documents/:documentId/chunks |
List a document's chunks |
| POST | /search |
Search across knowledge bases |
| GET | /search |
Quick GET search (q, kb) |
| POST | /rag |
Build RAG context for a query |
| GET | /:id/stats |
KB statistics |
Document type ∈ text, markdown, pdf, html, code, json, csv, spreadsheet;
document status ∈ pending, processing, indexed, failed, archived; document
source.type ∈ upload, url, api, integration. KB settings allow chunkSize
(100–2000), chunkOverlap (0–200), embeddingModel, maxDocuments,
maxChunksPerDocument, autoReindex. RAG: maxChunks (1–20), maxTokens
(100–16000), minScore (0–1).
11.6 GraphQL API (/graphql)#
src/graphql/schema.ts defines an SDL schema served with a playground.
- Enums include
ConversationStatus,MessageRole,AttachmentType(FILE, IMAGE, AUDIO, VIDEO, CODE),ToolCallStatus,TaskStatus,TaskStepType(THINKING, TOOL_CALL, TOOL_RESULT, RESPONSE),TaskStepStatus,MemoryTier(CORE, WORKING, ARCHIVAL, EPISODIC),MemoryType, and others. - Query fields —
conversation/conversations,message/messages,agent/agents,task/tasks,coreMemory/coreMemoryBlock, and the knowledge queries; list queries use Relay-style connections. - Mutation fields — conversation, message, agent, task, memory, and
knowledge mutations (e.g.
createConversation,sendMessage,regenerateMessage,createAgent,runTask,updateCoreMemory,addToArchival,recordEpisodicEvent,createKnowledgeBase,addDocument,reindexKnowledgeBase). - Subscription fields —
messageStream,conversationUpdated,agentUpdated,taskUpdated.
Supporting GraphQL files: resolvers.ts, data-loaders.ts,
service-data-sources.ts, types.ts.
12. Persistence#
Understanding how Iris stores data is critical for anyone working on the
service. There are two distinct storage mechanisms that must not be confused:
(1) the Prisma schema in libs/iris/database, which defines the PostgreSQL
tables for governance data (consent, continuity, memory scope); and (2) the
DurableJsonStateStore used by @iris/api services, which stores conversation
and agent state in in-memory Maps backed by a local JSON file. The in-memory
store is not a database — it is a development-stage durability mechanism that
will be replaced with PostgreSQL persistence as the service matures.
12.1 Prisma Schema (libs/iris/database)#
libs/iris/database/prisma/schema.prisma targets PostgreSQL via the
IRIS_DATABASE_URL env var and generates a Prisma client to
../src/generated/client. It defines exactly three models:
ConsentRecord (consent_records) — a consent ledger entry. Fields: id
(UUID), slug (unique), title, summary, description?, primaryDomain,
domains[], subject (JSON), category, status, target (JSON), version,
legalBasis/collection/verification (JSON), permissions/evidence (JSON
arrays), the request/decision/grant/deny/ withdraw/revoke timestamps,
expiresAt?, renewalRequired, renewalRequestedAt?, supersededById?,
history (JSON), metadata (JSON), createdAt/updatedAt. Indexed on
(primaryDomain,status), (status,requestedAt), expiresAt, supersededById.
ContinuityState (continuity_states) — per-user cross-domain continuity
snapshot. Fields: id, userId (UUID), summary, primaryDomain,
domains[], recentDomains[], status, momentum, focusTheme? (JSON),
currentIntent?, streakDays, activeJourneyCount, reminderCount,
lastActivityAt?, lastComputedAt, journeys/routeSnapshots/reminders/
checkpoints (JSON arrays), assistantState? (JSON), timestamps. Indexed on
userId, (primaryDomain,status), (status,lastComputedAt), lastActivityAt.
MemoryScope (memory_scopes) — a named, governed memory boundary. Fields:
id, slug (unique), title, summary, description?, primaryDomain,
domains[], status, kind, mode, owner (JSON), priority, isDefault,
boundary/retention/consent/sharing/ userControls/governance (JSON),
runtimeMappings (JSON array), linkedPersonaIds[], tags[], metadata
(JSON), timestamps. Indexed on slug, (primaryDomain,status),
(status,priority), isDefault.
The Prisma schema does not define
iris_conversations,iris_messages,iris_memories,iris_knowledge_documents, oriris_knowledge_chunks. Those tables, and the Qdrant/Redis cache-key tables described in an earlier draft, do not exist in code and have been removed.
12.2 API Service Storage#
The @iris/api services (conversation.service.ts, memory.service.ts,
agent.service.ts, knowledge.service.ts) hold all state in in-memory
Maps keyed by branded IDs (e.g.
conversations: Map<ConversationId, StoredConversation>, with
userConversations, agentTasks, documentChunks, etc. as secondary indexes).
Durability is provided by DurableJsonStateStore<T>
(src/services/durable-json-state.ts): an atomic JSON-file snapshot store that
writes to a ${pid}.${timestamp}.tmp file and renameSyncs it into place,
loading on startup. It is file-backed JSON state, not a database. The
/health/ready probe is the only place the API touches PostgreSQL/Redis, and
only to check connectivity through @oshun/database.
12.3 Event Transport#
The shared @oshun/event-bus library (libs/shared/event-bus) is
Redis-backed, not Kafka. event-bus.ts opens two ioredis connections
(publisher + subscriber): publish() serialises an envelope into a TTL-bounded
Redis key and fans it out over Redis pub/sub; a Redis HASH tracks acked
(eventId, subscriptionId | group) pairs; nack(delay) and delayed publish use
a durable Redis sorted set; dead letters live in a Redis list + hash. Consumer
groups race via SET NX claim keys.
No libs/iris package currently publishes onto this bus, and there is no Kafka
broker anywhere in the iris tree.
13. Configuration (@iris/config, @iris/core)#
All Iris service configuration is managed through a Zod-validated schema tree
defined in libs/iris/core/src/config/schemas.ts. This means configuration
errors are caught at startup rather than at runtime, and every config key has an
explicit type. The loader (loader.ts) reads from environment variables,
populating the schema with default values where variables are absent.
13.1 Configuration Schema#
libs/iris/core/src/config/schemas.ts defines the Zod-validated configuration
tree (irisConfigSchema → IrisConfig):
service—name(defaultiris),version,environment(development | staging | production | test),instanceId?.logging—level(trace | debug | info | warn | error | fatal | silent),pretty?,redactPaths?,includeTimestamp,enableTracing.context—defaultTimeoutMs(30000),maxContextDepth(10),propagateHeaders.providers— per-provider config objects foranthropic,openai,google,cohere,mistral,local;defaultProvider(defaultanthropic).agents— array ofagentConfigSchemaentries.memory—enabled,provider(in-memory | redis | postgres | vector-db, defaultin-memory),maxSize(1000),ttlMs?, optionalembeddingsblock.conversation—maxTurns(100),maxTokensPerMessage?,contextWindow(128000),persistMessages(defaultfalse), optionalsummarizationblock.tools— array oftoolConfigSchemaentries.
Provider defaults: Anthropic claude-3-5-sonnet-20241022, OpenAI gpt-4-turbo,
Google gemini-1.5-pro / us-central1, Cohere command-r-plus, Mistral
mistral-large-latest; all HTTP providers default to a 120000 ms timeout and 3
retries.
13.2 Environment Variables#
libs/iris/core/src/config/loader.ts reads configuration from the environment
(it accepts a custom env object, defaulting to process.env). The table below
lists every variable that the loader actually reads — as verified against
source. Variables listed in earlier documentation drafts that are not read in
code (e.g. IRIS_QDRANT_URL, IRIS_KAFKA_BROKERS, IRIS_JWT_SECRET) have been
removed from this table.
Keys actually read:
| Variable | Purpose |
|---|---|
NODE_ENV |
Environment selection |
SERVICE_NAME, SERVICE_VERSION, INSTANCE_ID |
Service identity |
LOG_LEVEL, LOG_PRETTY, LOG_TIMESTAMP, LOG_REDACT_PATHS |
Logging |
IRIS_ENABLE_TRACING |
Enable tracing in logging config |
IRIS_DEFAULT_TIMEOUT_MS, IRIS_MAX_CONTEXT_DEPTH, IRIS_PROPAGATE_HEADERS |
Context |
IRIS_DEFAULT_PROVIDER |
Default model provider |
ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, ANTHROPIC_MAX_TOKENS, ANTHROPIC_TEMPERATURE, ANTHROPIC_TIMEOUT, ANTHROPIC_MAX_RETRIES |
Anthropic |
OPENAI_API_KEY, OPENAI_ORGANIZATION, OPENAI_BASE_URL, OPENAI_MODEL, OPENAI_MAX_TOKENS, OPENAI_TEMPERATURE, OPENAI_TIMEOUT, OPENAI_MAX_RETRIES |
OpenAI |
GOOGLE_AI_API_KEY, GOOGLE_AI_PROJECT, GOOGLE_AI_LOCATION, GOOGLE_AI_MODEL, GOOGLE_AI_MAX_TOKENS, GOOGLE_AI_TEMPERATURE, GOOGLE_AI_TIMEOUT |
Google AI |
IRIS_MEMORY_ENABLED, IRIS_MEMORY_PROVIDER, IRIS_MEMORY_MAX_SIZE, IRIS_MEMORY_TTL_MS |
Memory |
IRIS_EMBEDDINGS_ENABLED, IRIS_EMBEDDINGS_PROVIDER, IRIS_EMBEDDINGS_MODEL, IRIS_EMBEDDINGS_DIMENSIONS |
Embeddings |
IRIS_CONVERSATION_MAX_TURNS, IRIS_CONVERSATION_MAX_TOKENS_PER_MESSAGE, IRIS_CONVERSATION_CONTEXT_WINDOW, IRIS_CONVERSATION_PERSIST |
Conversation |
IRIS_SUMMARIZATION_ENABLED, IRIS_SUMMARIZATION_THRESHOLD, IRIS_SUMMARIZATION_MODEL |
Summarization |
The @iris/api service additionally reads PORT (default 3100),
CORS_ORIGINS, OTEL_SERVICE_NAME / IRIS_SERVICE_NAME, NODE_ENV, and the
POSTGRES_* / REDIS_* / DB_* variables consumed by @oshun/database for
its health probes.
Cohere and Mistral have config schemas but no dedicated
*_API_KEYloader branch inloader.ts. There is noIRIS_QDRANT_URL,IRIS_MINIO_*,IRIS_KAFKA_BROKERS,IRIS_ENCRYPTION_KEY, orIRIS_JWT_SECRETconsumed in code — the prior draft's environment table listed variables that are not read anywhere.
13.3 Error Codes#
libs/iris/core/src/errors/codes.ts defines a numeric error-code taxonomy (~92
codes) grouped by range: general (1000–1099), validation (1100–1199), auth
(1200–1299), resource (1300–1399), context (1400–1499), conversation
(1500–1599), agent (1600–1699), model/provider (1700–1799), memory (1800–1899),
configuration (1900–1999), external service (2000–2099). It also maps codes to
HTTP statuses. errors/base.ts defines the base error classes used across
@iris/core.
@iris/core also provides request context, structured logging (Pino-based),
and OpenTelemetry-style tracing (correlation, decorators, profiler,
tracer).
14. Library Clusters#
libs/iris/** is organised into clusters, each covering a distinct functional
area. The package counts below are derived from package.json files actually on
disk — not from directory listings — so they are authoritative. Some package
names do not match their directory path; where this is true, the package.json
name field is the correct identifier, not the path.
14.1 Conversation Cluster#
There are 31 @iris/conversation*-named packages. They fall into two
groups:
- Top-level
conversation-*packages (15):@iris/conversation-core,conversation-context,conversation-intent,conversation-state,conversation-response,conversation-rag,conversation-style,conversation-format,conversation-citations,conversation-uncertainty,conversation-orchestration, and the four provider adapters (conversation-providers-anthropic,-google,-local,-openai). libs/iris/conversation/subdirectory packages (16): the umbrella@iris/conversationplusconversation-benchmarking,conversation-branching,conversation-costs,conversation-export,conversation-finetuning,conversation-prompts,conversation-search,conversation-summarization,conversation-templates,conversation-tokens, and the six reasoning packages underconversation/reasoning/(@iris/code-reasoning,@iris/consistency,@iris/math-reasoning,@iris/metacognition,@iris/scientific-reasoning,@iris/structured-reasoning).
Earlier docs described "10 conversation-_ libs" — the real count is far higher, and the two-group layout (sibling
conversation-_directories vs. children ofconversation/) is itself a notable structural fact.
14.2 Memory Cluster#
libs/iris/memory/ contains 22 package.json files: @iris/memory-core,
memory-short-term, memory-long-term, memory-episodic, memory-semantic,
memory-working-equivalent state lives under memory-transitions /
memory-consolidation, plus memory-retrieval, memory-persistence,
memory-analytics, memory-debugging, memory-migration, memory-sharing,
memory-tools, memory-visualization, memory-writing, and a
memory/personalization/ sub-tree (@iris/personalization,
personalization-feedback, -inference, -segments, -testing) and
@iris/privacy (memory/privacy).
The earlier "7 memory-* libs" figure is wrong; the directory holds 22 packages. Note also
@iris/memory-workingdoes not exist as a package — working-memory behaviour is split acrossmemory-transitions,memory-consolidation, and the tier types in@iris/types.
14.3 Other Clusters#
| Cluster | Representative packages |
|---|---|
knowledge/ (~23) |
@iris/knowledge (core), knowledge-chunking, knowledge-embeddings, knowledge-retrieval, knowledge-rag (+ rag/ sub-tree: agentic-rag, rag-multimodal, rag-evaluation, rag-debugging, rag-adaptive-chunking), knowledge-graph, knowledge-graphrag, knowledge-factcheck, knowledge-grounding, knowledge-realtime, knowledge-curation, knowledge-enterprise, knowledge-personal, knowledge-versioning, knowledge-export, knowledge-query, knowledge-freshness, knowledge-types |
agents/ |
@iris/agents (catalog), agents-core, @iris/archetypes, @iris/multi-agent (+ multi-agent-memory), @iris/agent-personalities, @iris/agent-marketplace, @iris/agent-specialization, @iris/workflows, the agents/proactive/ packages (ambient, anticipation, automation, reminders), the agents/tools/ registry (api-tools, code-tools, database-tools, filesystem-tools, web-tools, function-calling, tool-auth, tool-composition, tool-marketplace, tool-monitoring, tools-registry, tool-versioning, learning-tools), and the agents/computer-use/ packages (@iris/computer-use, @iris/browser-automation, @iris/desktop-automation, @iris/sandbox, @iris/action-safety, @iris/screenshot-vision, @oshun/iris-computer-use-native, computer-use-recording, -recovery, -templates, -accessibility) |
code/ |
@iris/code-generation, code-review, code-explanation, code-understanding, code-architecture, code-codebase, code-dependencies, code-semantic, code-search, code-quality, code-metrics, code-consistency, code-languages, code-git, code-agentic, code-cli, code-turbo-mode, code-memory-persistence, code-repository-intelligence, and the code/ide/ packages (code-ide, -actions, -completions, -hover, -inline) |
multimodal/ |
@iris/voice (multimodal/voice/recognition) and the voice/ family (voice-synthesis, voice-conversation, voice-commands, voice-auth, voice-effects, voice-journaling, voice-pronunciation), @iris/multimodal/vision and the vision/ family (vision-understanding, vision-documents, vision-diagrams, vision-generation, vision-screenshare, vision-search, vision-video, vision-memory), @iris/iot, @iris/spatial, @iris/bci |
voice/ (top-level) |
@iris/voice-providers, @iris/voice-empathic-synthesis, @iris/voice-ultra-low-latency |
emotional/ |
emotional-recognition, emotional-response, emotional-rapport, emotional-social, emotional-ethics, emotional-tracking, emotional-wellbeing, emotional-voice-analysis, emotional-multimodal-fusion |
privacy/ |
privacy (umbrella, at memory/privacy) plus the privacy/ tree: privacy-encryption, privacy-local, privacy-anonymization, privacy-minimization, privacy-audit, privacy-access, privacy-secrets, privacy-sovereignty, privacy-communication, privacy-dashboard, privacy-private-cloud, privacy-third-party-consent, privacy-tiered-compute, and the privacy/safety/ and privacy/security/ sub-trees |
platform/ |
platform-admin, platform-billing, platform-gateway, platform-ratelimit, platform-reporting, platform-codegen, platform-sla / platform-sla-management, platform-webhooks, @iris/streaming, @iris/plugins, the platform/customization/ packages (@iris/agent-builder, @iris/whitelabel), and the platform/sdk-* packages |
accessibility/ |
accessibility (umbrella), accessibility-visual (+ -display, -alternatives), accessibility-motor, accessibility-hearing, accessibility-cognitive, accessibility-braille, accessibility-i18n, accessibility-voice-ui, accessibility-adaptive, accessibility-testing |
analytics/ |
analytics (umbrella), analytics-realtime, analytics-cohort, analytics-funnel, analytics-ab |
personalization/ (top-level) |
personalization-benchmarking, personalization-state-aware, personalization-user-model-persistence |
bci/ (top-level) |
bci-apple-hid, bci-intent-prediction-api, bci-privacy-framework |
testing/ |
testing (umbrella), testing-chaos, testing-load, testing-synthetic, testing-visual |
| Foundation | @iris/core, @iris/types, @iris/config, @iris/embeddings, @iris/model-routing, @iris/ensemble, @iris/failover, @iris/mcp, @iris/a2a, @iris/reasoning-thinking, @iris/concordia-assistant, @iris/sdk (sdk/typescript), libs/iris/database (Prisma schema), presence (presence-sync, presence-integration) |
Some package names do not carry an
@iris/prefix or do not match their directory path (e.g.@iris/knowledgelives atknowledge/core,@iris/voicelives atmultimodal/voice/recognition,@oshun/iris-computer-use-nativelives underagents/computer-use/native/ts,@iris/privacylives atmemory/privacy). Treat thepackage.jsonnamefield as authoritative.
15. Cross-Domain Integration Libraries#
libs/iris/integrations/ contains six packages, one per sister Oshun domain
— they are cross-Oshun-domain bridges, not third-party SaaS connectors. Each
package encapsulates the typed API surface that Iris exposes to a specific
consuming domain, so that domain can depend on a stable contract without
importing from @iris/api internals directly. The @iris/integrations-psyche
package is the most developed example: it exports ./avatar, ./emotion,
./conferencing, and ./agents sub-paths, with only eventemitter3 and zod
as runtime dependencies.
| Package | Bridges Iris to | Stated purpose |
|---|---|---|
@iris/integrations-hathor |
Hathor | Worldbuilding integration |
@iris/integrations-maya |
Maya | Metaverse-engine integration |
@iris/integrations-nyx |
Nyx | Astronomy integration |
@iris/integrations-psyche |
Psyche | AI avatar representation, emotion expression, conferencing presence |
@iris/integrations-sophia |
Sophia | Research & knowledge integration |
@iris/integrations-yemaya |
Yemaya | Creative Studio integration |
There is no top-level @iris/integrations package, and no Slack / Microsoft
Teams / Notion / Jira / GitHub / Confluence connector exists anywhere in
libs/iris. The @iris/integrations-psyche agent classes exported from
./agents include avatar-presenter-agent, conference-participant-agent,
emotion-controller-agent, and persona-manager-agent.
16. Rollback-Safe In-Match Commentary Contract (V2 Consumer)#
The V2 fighting-game project (V2/) consumes Iris for AI play-by-play
commentary during live matches. This section documents a specialized Iris API
contract that is entirely distinct from the standard conversation API.
The fundamental engineering challenge is that V2's combat simulation uses rollback networking for lag compensation: when a desync is detected, the engine rolls back game state and re-simulates from the divergence point. Any LLM call made from inside the rollback bubble would be non-deterministic on the re-sim, corrupting replay files and breaking reconciliation between peers. Iris therefore exposes an in-match commentary contract with hard guarantees that LLM calls originate only from outside the rollback boundary and that commentary outputs never feed game state.
Implementation: createMatchCommentaryStream is implemented in
libs/iris/conversation-orchestration/src/match-commentary.ts and re-exported
from that package's index.ts (verified on disk, with a co-located
match-commentary.spec.ts). It is consumed by V2 through
@v2/iris-commentary-orchestration plus the V2AICommentary Unreal plugin.
The example below shows the complete call signature. Key parameters:
determinismMode: 'off-rollback-only' is the hard contract flag;
latencyBudgetMs sets the end-to-end deadline;
fallbackMode: 'pre-recorded-bank' activates the pre-recorded fallback when the
budget is exceeded; rightsContext carries the NIL/likeness metadata for rights
gates.
import { createMatchCommentaryStream } from '@iris/conversation-orchestration';
const stream = createMatchCommentaryStream({
matchId: 'match_abc123',
commentators: [
'calliope-v2-aria-voss-play-by-play',
'calliope-v2-marcus-knox-color',
], // @calliope/persona-live ids
ruleset: 'wwe', // cross-ref @shakti/fighting-ruleset-bridge
determinismMode: 'off-rollback-only', // hard contract: never feeds simulation
latencyBudgetMs: 800, // end-to-end cap from KO trigger to playable audio
fallbackMode: 'pre-recorded-bank', // when latency exceeded
rightsContext: { fighters: ['ryu', 'ken'], venue: 'metro-city-arena' },
});
stream.on('cue', (cue) => {
// cue.kind: 'pre-match' | 'mid-match' | 'ko' | 'finisher' | 'reversal' | 'comeback' | 'post-match'
});
16.1 Determinism Guarantees#
The fundamental safety property is that commentary outputs are
gameplay-inert: they are audio + subtitle only and never feed simulation state.
Every emitted cue is an audio/subtitle artifact (an audio voice handle plus
a subtitle string) — there is no field a cue can write that the rollback
engine reads back. Because outputs don't influence the deterministic combat sim,
the call itself can be asynchronous and non-deterministic without breaking
rollback.
- No call from inside the rollback bubble. Commentary calls originate from
V2's
V2/services/adapter layer, never fromV2Combat,V2Gameplay,V2Input, orV2Netcode. Iris refuses calls taggedfrom-rollback-frame=true. - No output mutates simulation state. Commentary tokens are audio / subtitle outputs only; they never feed game state, AI Director hints, balance counters, or any input the rollback engine consumes.
- Replay reproducibility via record-and-replay cache. The LLM call is
non-deterministic, so a seed alone cannot regenerate the same line. The cache
stores the specific output of the live call, keyed by
(matchId, cueId, cueTriggerFrame, branchId)— a record-and-replay pattern. Each fresh replay-takeover from a mid-match save state mints a newbranchId; re-playing the same takeover branch hits the cache. - Per-peer cue distribution in 1v1 rollback matches. Each peer's
V2/services/adapter independently calls Iris; commentary is local-audio output only, so divergence between peers is acceptable (Invariant #2). Tournament broadcasts and spectator surfaces receive one canonical stream from the broadcaster's adapter (or the host peer's, absent a broadcaster). - Latency budget with deterministic fallback. KO-trigger commentary has an
800 ms end-to-end budget
(
IRIS_MATCH_COMMENTARY_DEFAULT_LATENCY_BUDGET_MS = 800): ~420 ms Iris LLM round trip, ~240 ms persona TTS render, ~100 ms Psyche lipsync/expression render, ~40 ms safety margin. On budget overrun the contract emits alatency-budget-exceededcue from the pre-recorded bank. Unlike the live record-and-replay cache (Invariant #3), the fallback line is fully deterministic so every peer and every replay selects the identical line: it is chosen byselectFallbackLine(buildIrisMatchCommentaryFallbackKey( …))from a key whose shape isIRIS_MATCH_COMMENTARY_FALLBACK_KEY_SHAPE = '(cueKind,ruleset,commentatorId,seed)'. TheIrisMatchCommentaryFallbackKeycarries exactly those four fields —cueKind,ruleset,commentatorId,seed— andstringifyIrisMatchCommentaryFallbackKeyproduces the stable(cueKind,ruleset,commentatorId,seed)digest used to index the bank, so the pre-recorded fallback never desyncs peers or replays.
16.2 Cue Taxonomy#
Each cue type has its own latency budget that reflects how time-sensitive the
associated game event is. Pre-match cues have generous 5-second budgets because
they can be pre-baked during the loading screen. KO and reversal cues have tight
600–800 ms budgets because they must play while the game moment is still fresh.
The finisher cue gets 1,200 ms because fatality/critical-art sequences have
their own animation time that absorbs the extra latency.
| Cue | Trigger | Latency budget |
|---|---|---|
pre-match.intro |
Match-load fade-in | 5,000 ms (pre-bake OK) |
pre-match.tale |
Mid-load tale about the rivalry / venue | 5,000 ms (pre-bake OK) |
round.start |
Round begin | 800 ms |
combo.notable |
Player lands a notable combo | 800 ms |
reversal |
Counter-hit / parry / Drive Impact / Soul Charge / Reversal | 600 ms |
near-ko |
Defender HP < 10% | 800 ms |
ko |
KO event | 600 ms |
finisher |
Fatality / Critical Art / Rage Art / Critical Edge / Blazin' | 1,200 ms |
comeback |
Player overcomes a 50%+ HP deficit | 800 ms |
pre-match.tournament |
Tournament context line | 5,000 ms (pre-bake OK) |
post-match.recap |
Match-end recap pre-replay | 1,500 ms |
16.3 Per-Commentator Persona Binding and Gates#
Each commentator is not a generic TTS voice — it is a named AI artist with a distinct persona, vocal style, and rights profile. This section documents the rendering pipeline from LLM output to final audio, and the safety gates that run before any cue is emitted.
Each commentator binds to a @calliope/persona-live AI artist. The pipeline is:
V2 cue trigger → @iris/conversation-orchestration (LLM call, off-rollback) →
@calliope/persona-live persona transform → @iris/voice TTS →
@psyche/avatar-lipsync + @psyche/avatar-expressions → @euterpe/master mix
ducking → V2 V2Audio MetaSounds graph.
Each cue passes rights + safety gates before emission — NIL/likeness clearance, originality checking, harm checking, regional tone filtering, and EU AI Act conformity. Several gate packages are planned, not yet on disk; specific package assignments are tracked in the V2 dependency backlog. Failed gates fall through to the pre-recorded bank with a logged reason code.
Cross-references to
@themis/*,@kuanyin/*,@nous/safety,@calliope/persona-live,@v2/*, etc. describe planned cross-domain contracts owned by other domains and the V2 sister-monorepo; their existence and exact names are governed by those domains' own docs and backlogs, not this file.
16.4 V2 Bridge Surfaces (planned / cross-domain)#
The following V2-facing surfaces are described in the V2 architecture and dependency docs and are owned jointly with the V2 sister monorepo:
@iris/concordia-assistant(libs/iris/concordia-assistant, on disk) — Iris source of truth for appellant / arbiter dialogue scaffolding in Concordia-style dispute flows; consumed by V2 through@v2/concordia-substrate.@iris/accessibilityaccessibility-bridge profile — consumed by V2 through@v2/iris-accessibility.@iris/voicereal-time translation routing — consumed by V2 through@v2/iris-realtime-translationfor spectator chat, commentary localization, broadcast overlays, and companion second-screen feeds; presentation-only and off rollback.
These bridge contracts depend on packages owned by other domains; treat their naming and status as governed by the V2 and partner-domain docs.
17. V2 Accessibility Bridge Contract (V2 Consumer)#
The V2 fighting-game project consumes Iris for player accessibility across its
Unreal main client and its companion surfaces. This V2 Accessibility Bridge
Contract is owned by @iris/accessibility
(libs/iris/accessibility/src/index.ts) and adapted into V2 by
@v2/iris-accessibility
(apps/v2/iris-accessibility/src/iris-accessibility.ts). Like the commentary
contract in §16, every output is presentation / certification only: the surface
sets mayInfluenceRollback: false and cannot change deterministic
match-simulation state.
@iris/accessibility exports buildIrisV2AccessibilityBridge, the bridge id
IRIS_V2_ACCESSIBILITY_BRIDGE_ID, and the capability vocabulary
(caption-streaming, dyslexia-typography, cognitive-load-reduction,
screen-reader-bridge, native-ue-accessibility-hooks). From a
IrisV2AccessibilityProfileInput it derives:
- Caption streaming. Iris does not yet own a live caption engine end to end,
so the bridge names
@psyche/caption-streamingas its real-time caption provider (thecaptionProviderPackageNamefield). V2 reuses the same Psyche engine that backs@v2/psyche-caption-streaming(see Psyche §19.2), keeping a single caption pipeline behind both the spectator/HUD path and the accessibility path. - Dyslexia-friendly typography.
IrisV2DyslexiaTypographyTokensships a dyslexia-safe font stack withletterSpacingEm: 0, generous word spacing and line height, a minimum body font size, and a measure cap — and assertspreservesButtonLabelFitso the typography change never clips fight-UI labels. - Cognitive assistive UI.
IrisV2CognitiveLoadUiPolicydrives a reduced-cognitive-load mode (simplified language, persistent tutorial hints, step-by-step prompts, reduced-distraction chrome, reduced motion) while guaranteeinghidesCriticalActions: false— the cognitive assistive UI never removes a control the player needs to fight. - Screen-reader bridge.
IrisV2ScreenReaderBridgePolicydefines the screen-reader bridge: a live-region policy ofpolite-status-assertive-critical, mandatory descriptions for every focusable widget, and suppression of dramatic color-only text so platform screen readers receive clean semantics.
@v2/iris-accessibility wraps that bridge through
buildV2IrisAccessibilitySurface and surfaces explicit V2 control flags
(controlsCaptionStreaming, controlsDyslexiaCognitiveAssistiveUi,
controlsScreenReaderBridge, companionAndMainClientBridge,
nativeUeAccessibilityHooks). It composes the Psyche caption engine via
createAccessibilityCaptionEngine, emits the
v2.accessibility.caption-stream.ready topic, and gates release on
accessibility certification (certGateBlocksRelease: true). The rollback policy
string is off-rollback-accessibility-ui-only, and the surface carries
mayInfluenceRollback: false. The contract is validated by
V2/ue/Tools/check-v2-iris-accessibility.py.
18. V2 Mobile Companion App (V2 Consumer)#
V2's second-screen companion is implemented as the React Native surface
apps/oshun/mobile/v2/ (companionAppModel.ts, V2CompanionApp.tsx,
V2CompanionLaunchTile.tsx, route app/v2.tsx). Its agent layer is the
mobile-safe built-in catalog @iris/agents (see §5.5), which the companion
imports to resolve frame-data and appeal-help agents.
buildV2CompanionModel assembles the companion from the shared Oshun client
packages (@oshun/shell-core, @oshun/auth-client,
@oshun/concordia-integration, @oshun/trust-safety) plus @sophia/client for
local research and @iris/agents for the agent catalog. It calls
resolveIrisAgentsLaunchReadiness to confirm the agent package is wired and
selectIrisAgentForIntent to pick the frame-data-query agent for the "Open
frame-data codex" action. The companion is purely a client surface; it never
touches the rollback simulation. The binding is validated by
V2/ue/Tools/check-v2-mobile-companion.py, whose Iris-facing checks require
that apps/oshun/mobile/v2/ and @iris/agents are both named in this
specification.
19. Acceptance Criteria#
These criteria define the minimum bar for a change to be considered complete and correct for the Iris domain. They are written to catch the most common failure modes: type misalignment, hardcoded config, storage claims that outrun implementation, and documentation that drifts from code.
A change to the Iris domain satisfies this specification when:
- Type fidelity — new or changed domain objects are defined in
@iris/typeswith branded IDs and (for string unions) a paired Zod…Schema; co-located*.test.tsfiles cover the new types. - Enum discipline — enum values added in code are reflected here; the
four-tier
MemoryTiermodel is preserved unless the enum itself changes. - API contract — new
@iris/apiroutes apply arateLimiterpreset, validate input with@hono/zod-validator, and surface errors through theerrorHandler; the GraphQL schema stays consistent with the REST routes. - State-machine soundness — changes to
conversation-corekeep theDialogueStateType/DialogueEventtransition table total (every non-terminal state handlesERRORandTERMINATE). - Persistence honesty — claims about storage match reality: the Prisma
schema has three models; the API services use in-memory
Maps withDurableJsonStateStoreJSON-file durability. - Event transport honesty — event-transport descriptions remain
Redis-backed (
@oshun/event-bus); no Kafka claims. - Configuration accuracy — new configuration keys are added to
core/config/schemas.tsand read inloader.ts, and documented in §13.2 with their real variable names. - No fabrication — every entity, endpoint, event, and count in the docs is
traceable to source; planned-but-absent items are labelled
(planned).