Appearance
Actor integration
If you author a Matrix actor and want users to converse with it through Chat (or Director's Chat tab), you need to satisfy a small contract. This page is the contract.
The minimum: be promptable
The user-facing path always lands in system.agents. system.agents then needs to recognize your actor as a target. The way that works:
- Your actor declares
static promptable = true(or returnspromptable: truefrom$introspect). - Your actor's
$introspectincludes acognitiveStateblock with at least one ofpurpose,systemPrompt,memoryScope,regime.
That's enough for Chat's UI to enable the input and offer your actor as a target. The actual prompt goes to system.agents, which uses the snapshotTree describing your actor as conversation context.
The recommended: full introspection
A richer integration:
ts
class MyActor extends MatrixActor {
static description = 'What this actor does, in one sentence.';
static promptable = true;
static cognitiveState = {
purpose: 'concise statement of intent',
systemPrompt: 'optional system prompt for cognitive interactions',
memoryScope: 'session' | 'principal' | 'global',
regime: 'explore' | 'execute' | ...,
};
static accepts = {
'my-op': { ... },
// declared ops, with description for each
};
static emits = {
'my-event': { ... },
};
static skills = {
'do-the-thing': {
description: 'Plain-English skill description.',
ops: ['my-op'],
},
};
}Chat's snapshotTree carries mount, tag, accepts, emits, skills, tokens, cognitiveState, and children to system.agents. The richer your introspect, the better the model's answer.
What Chat actually sends
Going to system.agents $prompt (verified against RemoteActorConversationAdapter.prompt):
ts
{
prompt: request.text,
requestId: request.requestId,
traceId?: request.traceId,
activityTo: request.activityTo,
callerSnapshot:request.callerSnapshot,
callerRealm: <transport.addressRoot or daemonRoot>,
callerMount: <chat root, default 'chat'>,
sessionId?: request.sessionId,
principalId?: request.principalId,
}For the surface (Director's Chat tab), the payload also includes callerRoot, callerRuntimeRoot, callerSurfaceMount, and snapshotTree.
Your actor doesn't see the $prompt directly — system.agents does. But your actor will see:
- Tool calls from the model (if the model decides to invoke your declared ops).
$activityframes (relayed viaactivityTo) if Chat askssystem.agentsto send activity to a specific address — your actor doesn't get those by default.
If your actor is NOT promptable
You can still be a Chat target — the user just won't be able to converse with you the same way. Instead, Chat treats you as a "discussion topic": the prompt goes to system.agents about you, and the snapshotTree describes you. The model then answers questions about your declared interface using the snapshot.
This is how Director's "ask about this actor" works for non-promptable mounts.
The alternative path: be the agent yourself
If you want to be the cognitive agent (own sessions, own memory, own inference routing), don't use Chat's system.agents indirection. Mount your actor at a name like my-app.agent and have Chat target it directly.
To do that:
- Implement
$promptand emit$activityframes yourself (or delegate tosystem.inferencedirectly). - Implement
$sessionList,$sessionDelete,session_state.read,session_state.upsert. - Configure Chat with
serviceRefs.conversation.target = '<your-mount>'andmode: 'remote-actor'.
This bypasses system.agents. It's a heavier lift but useful for purpose-built agents (e.g., a SmithersAgent that owns its own session lifecycle).
Cancellation contract
If a user cancels mid-conversation, Chat sends $cancel { requestId }. Your actor (or system.agents) should:
- Stop emitting further
$activityframes for that requestId. - Free any resources tied to the request.
- Optionally emit a
$activityframe withphase: 'cancelled'so Chat knows.
Honoring cancel is a strong recommendation, not a hard requirement — Chat will time out the stream eventually.
See also
Source:
projects/matrix-3/packages/chat/src/app/services/adapters/RemoteActorConversationAdapter.ts:18-51(the prompt payload),chat-component/src/surface/ConversationSurfaceActor.ts:339-435(surface variant with snapshotTree).