Skip to content

@memberjunction/conversations-runtime

Framework-agnostic runtime layer for MemberJunction conversational AI experiences.

The pure-TypeScript orchestration layer that sits beneath every chat surface in MJ — overlay, embedded panel, full-page Chat workspace, and any future custom UX. Zero UX dependencies, client + server consumable.

┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ ANGULAR APPS │ │ NON-ANGULAR JS APPS │
│ (MJ Explorer, Angular widgets) │ │ (React, Vue, Node workers, CLI) │
└──────────────────────────────────────┘ └──────────────────────────────────────┘
│ │
▼ │
@memberjunction/ng-conversations │
(Angular widget wrapper) │
│ │
└──────────────────┬─────────────────────────┘
@memberjunction/conversations-runtime ★ this package
@memberjunction/core-entities (ConversationEngine — data layer)
Sub-componentPurpose
MentionsParse @-mentions out of message text (JSON + legacy formats). Pure string logic.
BridgeCoordinate active-conversation state between the corner overlay and the full-page workspace.
DefaultAgentResolve which agent handles a conversation turn via Application-Settings-driven chain (explicit → app-scoped → global → code-const Sage fallback).
ToolsThe shared ClientToolRegistry from @memberjunction/ai-agent-client — register tools the agent can invoke on the client.
AgentRunnerOrchestrates processMessage — resolves the target agent, filters candidates, dispatches via AgentClientSession.
StreamingRoutes per-message progress + completion events from the server’s PubSub channel to consumer callbacks. Also routes streamed final-response content: chunks tagged kind:'final-response' are accumulated per message (deltas → full text so far) and dispatched via MessageProgressUpdate.streaming; unmarked stream chunks are dropped.
SessionsObservability over the AI Agent Sessions/Channels infrastructure from PR #2787. Hosts register an ISessionsAdapter at bootstrap; the runtime re-broadcasts session lifecycle events as 'session-started' | 'session-channel' | 'session-ended'.

The runtime needs UI affordances (toasts, active-task indicators, session lifecycle observability) but cannot import any framework. Hosts implement small interfaces and register them at bootstrap:

InterfaceWhat the runtime callsDefault (no host wiring)
INotificationAdapterNotify(level, message, ttlMs?)ConsoleNotificationAdapter (console.log/warn/error)
IActiveTaskTrackerRemoveByAgentRunId(agentRunId)NoOpActiveTaskTracker
ISessionsAdapter(observable) SessionLifecycle$NoOpSessionsAdapter (EMPTY observable)

Registration:

ConversationsRuntime.Instance.UseNotificationAdapter({ Notify: (...) => {} });
ConversationsRuntime.Instance.UseActiveTaskTracker({ RemoveByAgentRunId: (...) => {} });
ConversationsRuntime.Instance.UseSessionsAdapter(myAdapter);

In @memberjunction/ng-conversations, ConversationsRuntimeBootstrap registers all three automatically on first DI injection.

ConversationsRuntime is decorated with @RegisterForStartup({ deferred: true, deferredDelay: 5000, severity: 'warn' }). 5 seconds after app boot, the MJ startup manager fires HandleStartup(), which calls Config(false) — pre-loading dependent engines in the background. Non-blocking; failures log as warnings.

import { ConversationsRuntime } from '@memberjunction/conversations-runtime';
// At app boot — lazy, idempotent, no penalty if called per entry point
await ConversationsRuntime.Instance.Config(false, contextUser);
// Parse a mention out of user text
const mentions = ConversationsRuntime.Instance.Mentions.parseMentions(
'@Sage help me',
AIEngineBase.Instance.Agents
);
// Resolve the default agent for the current application
const agent = await ConversationsRuntime.Instance.DefaultAgent.resolve({
applicationId: currentAppId,
});
// Register a client tool the agent can invoke
ConversationsRuntime.Instance.Tools.Register({
Name: 'NavigateToRecord',
Description: 'Open an entity record in the UI',
ParameterSchema: { type: 'object', properties: { EntityName: { type: 'string' } } },
Handler: async (params) => {
// ... your navigation code ...
return { Success: true };
},
});

Every API accepts an optional provider?: IMetadataProvider parameter and falls back to Metadata.Provider when omitted. Apps connecting to multiple MJ servers in parallel should pass an explicit provider to scope the runtime to that server.