@mutirolabs/openclaw-brain 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,279 @@
1
+ // Long-lived bridge session: owns the subprocess, performs the handshake,
2
+ // dispatches inbound envelopes, and keeps a narrow per-conversation cache for
3
+ // `session.snapshot`. Structured so the OpenClaw plugin runtime can
4
+ // start/stop one session per configured Mutiro account.
5
+ import { attachEnvelopeReader, createBridgeClient, createHostProcess, } from "./bridge-client.js";
6
+ import { buildSyntheticBridgeMessage, cloneMessage, trimRecentMessages, } from "./bridge-messages.js";
7
+ import { DEFAULT_OPTIONAL_CAPABILITIES, MAX_RECENT_MESSAGES, TYPE_URLS, } from "./bridge-protocol.js";
8
+ import { deliverObservedEnvelope } from "./inbound.js";
9
+ import { createMutiroOutbound } from "./outbound.js";
10
+ const consoleLogger = {
11
+ info: (msg) => console.log(`[openclaw-mutiro] ${msg}`),
12
+ warn: (msg) => console.warn(`[openclaw-mutiro] ${msg}`),
13
+ error: (msg) => console.error(`[openclaw-mutiro] ${msg}`),
14
+ };
15
+ export const startBridgeSession = async (options) => {
16
+ const logger = options.logger ?? consoleLogger;
17
+ const host = createHostProcess({
18
+ agentDir: options.agentDir,
19
+ env: options.env,
20
+ logger,
21
+ onExit: options.onHostExit,
22
+ });
23
+ const bridge = createBridgeClient(host);
24
+ const outbound = createMutiroOutbound(bridge);
25
+ const conversations = new Map();
26
+ let agentUsername = "";
27
+ const getConversation = (conversationId) => {
28
+ const existing = conversations.get(conversationId);
29
+ if (existing)
30
+ return existing;
31
+ const state = { recentMessages: [] };
32
+ conversations.set(conversationId, state);
33
+ return state;
34
+ };
35
+ const appendRecent = (conversationId, message) => {
36
+ if (!message || typeof message !== "object")
37
+ return;
38
+ const state = getConversation(conversationId);
39
+ state.recentMessages.push(cloneMessage(message));
40
+ state.recentMessages = trimRecentMessages(state.recentMessages, MAX_RECENT_MESSAGES);
41
+ };
42
+ const initializeBridge = async () => {
43
+ // Standalone bridge mode mirrors the documented handshake:
44
+ // ready → session.initialize → subscription.set → message.observed.
45
+ logger.info("host ready, sending initialization");
46
+ await bridge.request("session.initialize", {
47
+ "@type": TYPE_URLS.bridgeInitializeCommand,
48
+ role: "brain",
49
+ client_name: options.clientName ?? "openclaw-mutiro-bridge",
50
+ client_version: options.clientVersion ?? "1.0.0",
51
+ requested_optional_capabilities: options.requestedOptionalCapabilities ?? DEFAULT_OPTIONAL_CAPABILITIES,
52
+ });
53
+ logger.info("subscribing to event stream");
54
+ await bridge.request("subscription.set", {
55
+ "@type": TYPE_URLS.bridgeSubscriptionSetCommand,
56
+ all: true,
57
+ conversation_ids: [],
58
+ });
59
+ logger.info("handshake complete, listening for messages");
60
+ };
61
+ const handleObservedMessage = async (envelope) => {
62
+ if (envelope.type === "message.observed") {
63
+ // Ack delivery immediately so the host knows we accepted the turn, even
64
+ // though the actual visible reply will happen later via message.send.
65
+ bridge.ack(envelope.request_id, TYPE_URLS.bridgeMessageObservedResult);
66
+ }
67
+ const turn = await deliverObservedEnvelope(envelope, {
68
+ accountId: options.accountId,
69
+ agentUsername,
70
+ deliver: options.deliver,
71
+ });
72
+ if (!turn) {
73
+ if (envelope.conversation_id && envelope.message_id) {
74
+ outbound.endTurn({
75
+ conversationId: envelope.conversation_id,
76
+ replyToMessageId: envelope.message_id,
77
+ });
78
+ }
79
+ return;
80
+ }
81
+ appendRecent(turn.conversationId, envelope.payload?.message);
82
+ };
83
+ const handleTaskRequest = async (envelope) => {
84
+ // ChatBridgeTaskRequest fields (see spec/protobuf/shared/chat_bridge.proto):
85
+ // conversation_id, username, prompt, prompt_data, metadata, timeout_ms.
86
+ // The response MUST carry the agent's reply text inside
87
+ // ChatBridgeTaskResult; unlike message.observed there is no secondary
88
+ // message.send path — the host waits for this command_result.
89
+ const payload = (envelope.payload ?? {});
90
+ const conversationId = payload.conversation_id || envelope.conversation_id || "task-queue";
91
+ const prompt = (payload.prompt ?? "").trim();
92
+ const timeoutMs = typeof payload.timeout_ms === "number"
93
+ ? payload.timeout_ms
94
+ : typeof payload.timeout_ms === "string"
95
+ ? Number.parseInt(payload.timeout_ms, 10) || undefined
96
+ : undefined;
97
+ let resultText = "";
98
+ if (!prompt) {
99
+ logger.warn("task.request arrived without a prompt; returning empty result");
100
+ }
101
+ else if (!options.resolveTaskRequest) {
102
+ logger.warn("task.request received but no resolveTaskRequest is configured");
103
+ }
104
+ else {
105
+ try {
106
+ resultText = await options.resolveTaskRequest({
107
+ conversationId,
108
+ accountId: options.accountId,
109
+ username: payload.username,
110
+ prompt,
111
+ promptData: payload.prompt_data,
112
+ metadata: payload.metadata,
113
+ timeoutMs,
114
+ requestId: envelope.request_id,
115
+ });
116
+ }
117
+ catch (err) {
118
+ logger.error(`task.request resolver failed: ${err instanceof Error ? err.message : String(err)}`);
119
+ resultText = "";
120
+ }
121
+ }
122
+ bridge.send("command_result", {
123
+ "@type": TYPE_URLS.bridgeCommandResult,
124
+ ok: true,
125
+ response: {
126
+ "@type": TYPE_URLS.bridgeTaskResult,
127
+ text: resultText,
128
+ },
129
+ }, {
130
+ request_id: envelope.request_id,
131
+ conversation_id: conversationId,
132
+ });
133
+ };
134
+ const handleSessionSnapshot = async (envelope) => {
135
+ const payload = (envelope.payload ?? {});
136
+ const conversationId = payload.conversation_id || envelope.conversation_id;
137
+ logger.info(`session.snapshot requested: conversation_id=${conversationId ?? ""} call_id=${payload.call_id ?? ""} username=${payload.username ?? ""}`);
138
+ if (!conversationId) {
139
+ bridge.sendError(envelope.request_id, "invalid_request", "session.snapshot conversation_id is required");
140
+ return;
141
+ }
142
+ const cached = conversations.get(conversationId);
143
+ // Ask the plugin runtime to build a rich snapshot. The resolver may
144
+ // return the real agent system prompt, the real session transcript, and
145
+ // channel-owned tool hints so the live voice model starts with the same
146
+ // persona the chat brain has. Any undefined field falls back to cached
147
+ // synthetic state so an offline/missing resolver degrades gracefully.
148
+ let snapshot;
149
+ if (options.resolveLiveSnapshot) {
150
+ try {
151
+ snapshot = await options.resolveLiveSnapshot({
152
+ conversationId,
153
+ accountId: options.accountId,
154
+ callId: payload.call_id,
155
+ username: payload.username,
156
+ });
157
+ }
158
+ catch (err) {
159
+ logger.warn(`session.snapshot resolver failed: ${err instanceof Error ? err.message : String(err)}`);
160
+ snapshot = null;
161
+ }
162
+ }
163
+ const response = {
164
+ "@type": TYPE_URLS.bridgeSessionSnapshotResult,
165
+ recent_messages: snapshot?.recentMessages ?? cached?.recentMessages ?? [],
166
+ metadata: {
167
+ conversation_id: conversationId,
168
+ ...(snapshot?.metadata ?? {}),
169
+ },
170
+ };
171
+ if (snapshot?.systemInstruction && snapshot.systemInstruction.trim()) {
172
+ response.system_instruction = snapshot.systemInstruction;
173
+ }
174
+ if (snapshot?.promptData && Object.keys(snapshot.promptData).length > 0) {
175
+ response.prompt_data = snapshot.promptData;
176
+ }
177
+ if (snapshot?.toolHints && snapshot.toolHints.length > 0) {
178
+ response.tool_hints = snapshot.toolHints.map((hint) => ({
179
+ name: hint.name,
180
+ description: hint.description ?? "",
181
+ metadata: hint.metadata ?? {},
182
+ }));
183
+ }
184
+ bridge.send("command_result", {
185
+ "@type": TYPE_URLS.bridgeCommandResult,
186
+ ok: true,
187
+ response,
188
+ }, {
189
+ request_id: envelope.request_id,
190
+ conversation_id: conversationId,
191
+ });
192
+ };
193
+ const handleSessionObserved = async (envelope) => {
194
+ const payload = (envelope.payload ?? {});
195
+ const conversationId = payload.conversation_id || envelope.conversation_id;
196
+ if (!conversationId) {
197
+ bridge.sendError(envelope.request_id, "invalid_request", "session.observed conversation_id is required");
198
+ return;
199
+ }
200
+ const observedText = (payload.text || "").trim();
201
+ if (observedText) {
202
+ appendRecent(conversationId, buildSyntheticBridgeMessage({
203
+ conversationId,
204
+ senderUsername: "system",
205
+ text: observedText,
206
+ metadata: { source: (payload.source || "").trim() },
207
+ }));
208
+ }
209
+ bridge.ack(envelope.request_id, TYPE_URLS.bridgeSessionObservedResult);
210
+ };
211
+ attachEnvelopeReader(host, async (envelope) => {
212
+ switch (envelope.type) {
213
+ case "ready": {
214
+ const payload = (envelope.payload ?? {});
215
+ agentUsername = payload.agent_username || agentUsername;
216
+ try {
217
+ await initializeBridge();
218
+ }
219
+ catch (err) {
220
+ logger.error(`handshake failed: ${err instanceof Error ? err.message : String(err)}`);
221
+ }
222
+ return;
223
+ }
224
+ case "command_result":
225
+ bridge.resolveResponse(envelope.request_id, envelope.payload);
226
+ return;
227
+ case "error":
228
+ if (!bridge.rejectResponse(envelope.request_id, envelope.error)) {
229
+ logger.error(`host error: ${JSON.stringify(envelope.error)}`);
230
+ }
231
+ return;
232
+ case "message.observed":
233
+ case "event.message":
234
+ await handleObservedMessage(envelope);
235
+ return;
236
+ case "task.request":
237
+ await handleTaskRequest(envelope);
238
+ return;
239
+ case "session.snapshot":
240
+ await handleSessionSnapshot(envelope);
241
+ return;
242
+ case "session.observed":
243
+ await handleSessionObserved(envelope);
244
+ return;
245
+ default:
246
+ if (envelope.request_id) {
247
+ bridge.sendError(envelope.request_id, "unsupported_envelope", `unsupported envelope type ${JSON.stringify(envelope.type)}`, {
248
+ conversation_id: envelope.conversation_id,
249
+ message_id: envelope.message_id,
250
+ reply_to_message_id: envelope.reply_to_message_id,
251
+ });
252
+ }
253
+ }
254
+ }, logger);
255
+ const shutdown = async () => {
256
+ try {
257
+ bridge.send("host.shutdown", { "@type": "type.googleapis.com/mutiro.chatbridge.ChatBridgeShutdownCommand" });
258
+ }
259
+ catch {
260
+ // best-effort
261
+ }
262
+ host.kill("SIGTERM");
263
+ await new Promise((resolve) => {
264
+ if (host.exitCode !== null)
265
+ resolve();
266
+ else
267
+ host.once("exit", () => resolve());
268
+ });
269
+ };
270
+ return {
271
+ accountId: options.accountId,
272
+ host,
273
+ bridge,
274
+ outbound,
275
+ getAgentUsername: () => agentUsername,
276
+ shutdown,
277
+ };
278
+ };
279
+ //# sourceMappingURL=bridge-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-session.js","sourceRoot":"","sources":["../../src/bridge-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ // OpenClaw channel plugin definition. This file is the "hot" import path
2
+ // loaded during gateway startup and plugin discovery, so it stays narrow:
3
+ // manifest metadata plus a lazy handle into the heavier runtime module.
4
+ //
5
+ // The runtime (`channel.runtime.ts`) owns subprocess lifecycle, envelope
6
+ // dispatch, and the per-account bridge session registry.
7
+ import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
8
+ import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
9
+ import { mutiroMessageActions } from "./actions.js";
10
+ import { mutiroAgentTools } from "./agent-tools.js";
11
+ import { mutiroConfigAdapter } from "./config.js";
12
+ import { mutiroSetupAdapter, mutiroSetupWizard } from "./setup-surface.js";
13
+ const loadMutiroChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime.js"), "mutiroChannelRuntime");
14
+ const outbound = {
15
+ deliveryMode: "direct",
16
+ // `sendText` is called whenever OpenClaw wants to push a text reply into a
17
+ // Mutiro conversation. The `accountId` selects which active bridge session
18
+ // to route through; `to` is the Mutiro `conversation_id`; `replyToId` is the
19
+ // message the reply threads under.
20
+ async sendText(ctx) {
21
+ const runtime = await loadMutiroChannelRuntime();
22
+ return runtime.sendMutiroText(ctx);
23
+ },
24
+ // `sendMedia` is the single-shot media path. The channel runtime uses the
25
+ // bridge-local `media.upload` command to stage the file, then attaches it
26
+ // to a `message.send`.
27
+ async sendMedia(ctx) {
28
+ const runtime = await loadMutiroChannelRuntime();
29
+ return runtime.sendMutiroMedia(ctx);
30
+ },
31
+ };
32
+ // Read `channels.mutiro.replyToMode` as an override; otherwise default to
33
+ // `"first"` so the agent's first reply in a turn threads under the inbound
34
+ // message. Mutiro clients render reply-to as a visible quoted pill, so this
35
+ // anchors context nicely in groups without being noisy in DMs. Set
36
+ // `channels.mutiro.replyToMode` to `"off"`, `"all"`, or `"batched"` in the
37
+ // OpenClaw config to override.
38
+ const resolveMutiroReplyToMode = ({ cfg }) => {
39
+ const section = cfg.channels?.mutiro;
40
+ const configured = section?.replyToMode;
41
+ if (configured === "off" || configured === "first" || configured === "all" || configured === "batched") {
42
+ return configured;
43
+ }
44
+ return "first";
45
+ };
46
+ export const mutiroPlugin = createChatChannelPlugin({
47
+ base: {
48
+ id: "mutiro",
49
+ meta: {
50
+ id: "mutiro",
51
+ label: "Mutiro",
52
+ selectionLabel: "Mutiro",
53
+ docsPath: "/channels/mutiro",
54
+ docsLabel: "mutiro",
55
+ blurb: "Official Mutiro Channel for OpenClaw. Point at a Mutiro agent directory to enable.",
56
+ order: 80,
57
+ quickstartAllowFrom: true,
58
+ markdownCapable: true,
59
+ },
60
+ capabilities: {
61
+ chatTypes: ["direct", "group"],
62
+ reactions: true,
63
+ reply: true,
64
+ media: true,
65
+ },
66
+ config: mutiroConfigAdapter,
67
+ agentTools: mutiroAgentTools,
68
+ actions: mutiroMessageActions,
69
+ // Setup surfaces: `setup` is the non-interactive adapter path
70
+ // (`openclaw channels add --channel mutiro [flags]`); `setupWizard` is what
71
+ // runs when the user invokes `openclaw channels add` with no flags and
72
+ // picks `mutiro` from the selection list.
73
+ setup: mutiroSetupAdapter,
74
+ setupWizard: mutiroSetupWizard,
75
+ // Messaging adapter: teaches OpenClaw how to recognize a Mutiro target.
76
+ // Without it, reactions/forwards/cross-channel sends fail with
77
+ // "Unknown target" because the core resolver can't match a
78
+ // `conv_<uuid>` conversation id or a leading-@ username against any
79
+ // directory/id pattern it knows about.
80
+ messaging: {
81
+ normalizeTarget: (raw) => {
82
+ const trimmed = raw.trim();
83
+ if (!trimmed)
84
+ return undefined;
85
+ // Strip a leading @ on usernames so downstream comparisons and the
86
+ // bridge's `to_username` field see the raw handle.
87
+ return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
88
+ },
89
+ targetResolver: {
90
+ hint: "Use a Mutiro conversation id (e.g. conv_<uuid>) or @username.",
91
+ looksLikeId: (raw, normalized) => {
92
+ const value = (normalized ?? raw).trim();
93
+ if (!value)
94
+ return false;
95
+ // conv_<...> = conversation id; bare @handle or a plain alphanumeric
96
+ // username both route to message.send_voice / react / etc.
97
+ return /^conv_/i.test(value) || /^@/.test(raw) || /^[A-Za-z0-9_.-]+$/.test(value);
98
+ },
99
+ resolveTarget: async ({ input, normalized }) => {
100
+ const value = (normalized || input).trim().replace(/^@/, "");
101
+ if (!value)
102
+ return null;
103
+ const isConversation = /^conv_/i.test(value);
104
+ return {
105
+ to: value,
106
+ kind: isConversation ? "group" : "user",
107
+ display: isConversation ? value : `@${value}`,
108
+ source: "normalized",
109
+ };
110
+ },
111
+ },
112
+ },
113
+ // Gateway lifecycle: startAccount spawns the bridge subprocess for this
114
+ // account and wires inbound observed messages into OpenClaw's reply
115
+ // dispatcher. stopAccount tears the subprocess down.
116
+ gateway: {
117
+ async startAccount(ctx) {
118
+ const runtime = await loadMutiroChannelRuntime();
119
+ return runtime.startMutiroAccount(ctx);
120
+ },
121
+ async stopAccount(ctx) {
122
+ const runtime = await loadMutiroChannelRuntime();
123
+ await runtime.stopMutiroAccount(ctx);
124
+ },
125
+ },
126
+ // Status adapter: answers `openclaw channels status mutiro`. The runtime
127
+ // already updates `running` / `connected` / `lastConnectedAt` /
128
+ // `reconnectAttempts` via `ctx.setStatus()` when the bridge subprocess
129
+ // starts, handshakes, exits, or is in backoff. Here we just enrich the
130
+ // snapshot with Mutiro-specific context (agent workspace path, bridge
131
+ // mode, derived health string).
132
+ status: {
133
+ buildAccountSnapshot: ({ account, runtime }) => {
134
+ const base = runtime ?? { accountId: account.accountId };
135
+ const running = base.running ?? false;
136
+ const connected = base.connected ?? false;
137
+ const restartPending = base.restartPending ?? false;
138
+ const healthState = !running
139
+ ? restartPending
140
+ ? "restarting"
141
+ : "stopped"
142
+ : connected
143
+ ? "healthy"
144
+ : "connecting";
145
+ return {
146
+ ...base,
147
+ accountId: account.accountId,
148
+ configured: account.configured,
149
+ enabled: account.enabled,
150
+ mode: "bridge",
151
+ healthState,
152
+ dbPath: account.config.agentDir ?? null,
153
+ };
154
+ },
155
+ },
156
+ },
157
+ // Threading adapter: Mutiro natively supports `reply_to_message_id`, so wire
158
+ // OpenClaw's reply-dispatch into it. `allowExplicitReplyTagsWhenOff` keeps
159
+ // agent-directed reply markers working even when the user has disabled
160
+ // automatic reply-threading.
161
+ threading: {
162
+ resolveReplyToMode: resolveMutiroReplyToMode,
163
+ allowExplicitReplyTagsWhenOff: true,
164
+ },
165
+ outbound,
166
+ });
167
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"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"}