@beeos-ai/device-agent 0.7.2 → 0.8.0
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.
- package/dist/agent-runtime/skills/sqlite-store.d.ts +60 -0
- package/dist/agent-runtime/skills/sqlite-store.js +234 -0
- package/dist/agent-runtime/skills/sqlite-store.js.map +1 -0
- package/dist/bootstrap.js +11 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.js +18 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +5 -0
- package/dist/config/schema.js +42 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/inbound/cli/main.js +12 -0
- package/dist/inbound/cli/main.js.map +1 -1
- package/dist/messaging/token-provider.d.ts +43 -0
- package/dist/messaging/token-provider.js +65 -0
- package/dist/messaging/token-provider.js.map +1 -0
- package/dist/plugins/message-bus/busy-reply.d.ts +39 -0
- package/dist/plugins/message-bus/busy-reply.js +30 -0
- package/dist/plugins/message-bus/busy-reply.js.map +1 -0
- package/dist/plugins/message-bus/inbound.d.ts +55 -0
- package/dist/plugins/message-bus/inbound.js +123 -0
- package/dist/plugins/message-bus/inbound.js.map +1 -0
- package/dist/plugins/message-bus/index.d.ts +147 -0
- package/dist/plugins/message-bus/index.js +392 -0
- package/dist/plugins/message-bus/index.js.map +1 -0
- package/dist/plugins/message-bus/outbound.d.ts +43 -0
- package/dist/plugins/message-bus/outbound.js +149 -0
- package/dist/plugins/message-bus/outbound.js.map +1 -0
- package/dist/plugins/message-bus/turn-context.d.ts +57 -0
- package/dist/plugins/message-bus/turn-context.js +53 -0
- package/dist/plugins/message-bus/turn-context.js.map +1 -0
- package/dist/test-lan-autoglm.d.ts +7 -0
- package/dist/test-lan-autoglm.js +46 -0
- package/dist/test-lan-autoglm.js.map +1 -0
- package/dist/test-mem-breakdown.d.ts +1 -0
- package/dist/test-mem-breakdown.js +103 -0
- package/dist/test-mem-breakdown.js.map +1 -0
- package/dist/test-mem.d.ts +1 -0
- package/dist/test-mem.js +60 -0
- package/dist/test-mem.js.map +1 -0
- package/dist/test-mobile-use.d.ts +1 -0
- package/dist/test-mobile-use.js +18 -0
- package/dist/test-mobile-use.js.map +1 -0
- package/dist/test-screenshot-loop.d.ts +1 -0
- package/dist/test-screenshot-loop.js +43 -0
- package/dist/test-screenshot-loop.js.map +1 -0
- package/dist/test-screenshot.d.ts +1 -0
- package/dist/test-screenshot.js +10 -0
- package/dist/test-screenshot.js.map +1 -0
- package/dist/test-ui-xml-dump.d.ts +15 -0
- package/dist/test-ui-xml-dump.js +49 -0
- package/dist/test-ui-xml-dump.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin — message-bus.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes the device-agent to the BeeOS Message Service so it can
|
|
5
|
+
* receive `chat_message` envelopes from L0 / A2A / MCP / OpenAPI
|
|
6
|
+
* invocations and reply over `ch:{channelId}` with `agent_reply` /
|
|
7
|
+
* `agent_reply_delta` (in_reply_to = original message_id).
|
|
8
|
+
*
|
|
9
|
+
* Disabled by default — opt in by setting `BEEOS_MESSAGE_BUS_ENABLED=true`
|
|
10
|
+
* (or `--message-bus`) AND providing both `--agent-gateway-url` and
|
|
11
|
+
* an Ed25519 identity. Without those the plugin logs a one-line warning
|
|
12
|
+
* during `start()` and stays disabled — the rest of the agent (Bridge
|
|
13
|
+
* ACP, HTTP debug) still runs.
|
|
14
|
+
*
|
|
15
|
+
* Lifecycle:
|
|
16
|
+
* - `init` — read config, decide enabled / disabled.
|
|
17
|
+
* - `start` — kick off the connect loop. Returns IMMEDIATELY (does
|
|
18
|
+
* not block bootstrap on the network). The first connect attempt
|
|
19
|
+
* and any subsequent retries run in the background.
|
|
20
|
+
* - `stop` — cancel any pending retry, disconnect SDK, unsubscribe
|
|
21
|
+
* outbound listener, drop in-flight register.
|
|
22
|
+
*
|
|
23
|
+
* Failure mode: if `tokenProvider()` rejects (e.g. the device instance
|
|
24
|
+
* has not been provisioned with `agentPublicKey` yet), the plugin
|
|
25
|
+
* schedules a retry with exponential backoff (30s → 60s → 120s → 240s,
|
|
26
|
+
* capped at 300s) and keeps trying forever until either `connect`
|
|
27
|
+
* succeeds or `stop()` cancels the loop. This mirrors beeos-claw's
|
|
28
|
+
* `localServer.onceAgentMappingReady(...)` deferred-connect pattern —
|
|
29
|
+
* see `[index.ts:484-498](agents/beeos-claw/src/index.ts)`.
|
|
30
|
+
*/
|
|
31
|
+
import { MessageSDK } from "@beeos-ai/message-sdk";
|
|
32
|
+
import { createDeviceMessagingTokenProvider } from "../../messaging/token-provider.js";
|
|
33
|
+
import { MessageSdkBusyReplyHandler } from "./busy-reply.js";
|
|
34
|
+
import { buildMessageBusInboundHandler } from "./inbound.js";
|
|
35
|
+
import { installMessageBusOutbound } from "./outbound.js";
|
|
36
|
+
import { TurnContextRegister } from "./turn-context.js";
|
|
37
|
+
/**
|
|
38
|
+
* Backoff schedule for token / connect failures (milliseconds). Each
|
|
39
|
+
* entry is the delay BEFORE the next retry. The last entry caps the
|
|
40
|
+
* delay; once we hit the cap we keep retrying forever at that interval.
|
|
41
|
+
*
|
|
42
|
+
* Tunable via `MessageBusPluginOptions.retrySchedule` — tests pin a
|
|
43
|
+
* tighter schedule (e.g. `[5, 5, 5]`) to keep `vi.useFakeTimers()`
|
|
44
|
+
* runs cheap.
|
|
45
|
+
*/
|
|
46
|
+
export const DEFAULT_RETRY_SCHEDULE_MS = Object.freeze([
|
|
47
|
+
30_000,
|
|
48
|
+
60_000,
|
|
49
|
+
120_000,
|
|
50
|
+
240_000,
|
|
51
|
+
300_000,
|
|
52
|
+
]);
|
|
53
|
+
/** Attempt indices (1-based) where a `warn` log is preferred over `debug`. */
|
|
54
|
+
const WARN_ATTEMPT_INDICES = new Set([1, 3, 5, 10]);
|
|
55
|
+
export class MessageBusPlugin {
|
|
56
|
+
opts;
|
|
57
|
+
name = "message-bus";
|
|
58
|
+
ctx;
|
|
59
|
+
sdk = null;
|
|
60
|
+
register = null;
|
|
61
|
+
unsubscribeOutbound = null;
|
|
62
|
+
resolvedPrincipalId = "";
|
|
63
|
+
// Connect-loop state.
|
|
64
|
+
stopped = false;
|
|
65
|
+
retryHandle = null;
|
|
66
|
+
// When the loop is sleeping in `sleep(ms)`, this resolver lets
|
|
67
|
+
// `stop()` (or any other test-level cancel) wake it up early with
|
|
68
|
+
// `cancelled = true`. Always cleared back to `null` once the resolver
|
|
69
|
+
// fires.
|
|
70
|
+
retryResolve = null;
|
|
71
|
+
connectAttempt = 0;
|
|
72
|
+
legacyDeprecationLogged = false;
|
|
73
|
+
// Resolves once the plugin has either successfully connected OR is
|
|
74
|
+
// permanently disabled. Tests await this to avoid racing the
|
|
75
|
+
// background loop. Production callers ignore it.
|
|
76
|
+
connectSettled = Promise.resolve();
|
|
77
|
+
resolveConnectSettled = () => undefined;
|
|
78
|
+
constructor(opts = {}) {
|
|
79
|
+
this.opts = opts;
|
|
80
|
+
}
|
|
81
|
+
async init(ctx) {
|
|
82
|
+
this.ctx = ctx;
|
|
83
|
+
if (!ctx.config.messageBus?.enabled) {
|
|
84
|
+
ctx.log.info("message-bus: disabled (set BEEOS_MESSAGE_BUS_ENABLED=true or --message-bus to enable)");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!this.opts.keypair) {
|
|
88
|
+
ctx.log.warn("message-bus: enabled but no Ed25519 identity available — stays disabled");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!ctx.config.agentGatewayUrl) {
|
|
92
|
+
ctx.log.warn("message-bus: enabled but no agentGatewayUrl configured — stays disabled");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async start() {
|
|
97
|
+
const ctx = this.ctx;
|
|
98
|
+
if (!ctx)
|
|
99
|
+
return;
|
|
100
|
+
if (!ctx.config.messageBus?.enabled)
|
|
101
|
+
return;
|
|
102
|
+
if (!this.opts.keypair)
|
|
103
|
+
return;
|
|
104
|
+
if (!ctx.config.agentGatewayUrl)
|
|
105
|
+
return;
|
|
106
|
+
this.connectSettled = new Promise((resolve) => {
|
|
107
|
+
this.resolveConnectSettled = resolve;
|
|
108
|
+
});
|
|
109
|
+
// Kick off the loop. Do NOT await — the bootstrap should not block
|
|
110
|
+
// on a remote handshake. The loop reschedules itself via setTimeout
|
|
111
|
+
// until either `tryConnectOnce` returns true or `stop()` is called.
|
|
112
|
+
void this.runConnectLoop();
|
|
113
|
+
}
|
|
114
|
+
async stop() {
|
|
115
|
+
this.stopped = true;
|
|
116
|
+
this.cancelPendingRetry();
|
|
117
|
+
// Resolve any outstanding waiter so test code that awaits
|
|
118
|
+
// `whenSettled()` does not hang past stop().
|
|
119
|
+
this.resolveConnectSettled();
|
|
120
|
+
if (this.unsubscribeOutbound) {
|
|
121
|
+
try {
|
|
122
|
+
this.unsubscribeOutbound();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
/* ignore */
|
|
126
|
+
}
|
|
127
|
+
this.unsubscribeOutbound = null;
|
|
128
|
+
}
|
|
129
|
+
if (this.sdk) {
|
|
130
|
+
try {
|
|
131
|
+
await this.sdk.disconnect();
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
this.ctx?.log.warn({ err: err instanceof Error ? err.message : String(err) }, "message-bus: disconnect failed");
|
|
135
|
+
}
|
|
136
|
+
this.sdk = null;
|
|
137
|
+
}
|
|
138
|
+
if (this.register) {
|
|
139
|
+
this.register.reset();
|
|
140
|
+
this.register = null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Test seam — exposes the resolved principal so integration tests can
|
|
144
|
+
// assert what the plugin actually subscribed to without poking at
|
|
145
|
+
// private state.
|
|
146
|
+
get principalId() {
|
|
147
|
+
return this.resolvedPrincipalId;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Test helper — resolves once `start()`'s connect loop has either
|
|
151
|
+
* successfully connected or `stop()` was called. Production code
|
|
152
|
+
* never needs this.
|
|
153
|
+
*/
|
|
154
|
+
whenSettled() {
|
|
155
|
+
return this.connectSettled;
|
|
156
|
+
}
|
|
157
|
+
// ── Internals ──────────────────────────────────────────────────────────
|
|
158
|
+
async runConnectLoop() {
|
|
159
|
+
const ctx = this.ctx;
|
|
160
|
+
const log = ctx.log;
|
|
161
|
+
while (!this.stopped) {
|
|
162
|
+
this.connectAttempt += 1;
|
|
163
|
+
const ok = await this.tryConnectOnce(this.connectAttempt);
|
|
164
|
+
if (ok) {
|
|
165
|
+
this.resolveConnectSettled();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (this.stopped) {
|
|
169
|
+
this.resolveConnectSettled();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const delayMs = this.nextDelayMs(this.connectAttempt);
|
|
173
|
+
this.logRetryScheduled(this.connectAttempt, delayMs, log);
|
|
174
|
+
const cancelled = await this.sleep(delayMs);
|
|
175
|
+
if (cancelled) {
|
|
176
|
+
this.resolveConnectSettled();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.resolveConnectSettled();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Single connect attempt. Returns `true` on success (sdk connected,
|
|
184
|
+
* handlers + listener installed) and `false` on any failure (caller
|
|
185
|
+
* should schedule a retry).
|
|
186
|
+
*
|
|
187
|
+
* Each attempt builds a FRESH `MessageSDK` instance — re-using one
|
|
188
|
+
* across reconnects would carry stale handlers and confuse the
|
|
189
|
+
* centrifuge-js internal state. The SDK has its own internal
|
|
190
|
+
* reconnect once `.connect()` succeeds; this loop is for the OPENING
|
|
191
|
+
* handshake only.
|
|
192
|
+
*/
|
|
193
|
+
async tryConnectOnce(attempt) {
|
|
194
|
+
const ctx = this.ctx;
|
|
195
|
+
const log = ctx.log;
|
|
196
|
+
const keypair = this.opts.keypair;
|
|
197
|
+
const agentGatewayUrl = ctx.config.agentGatewayUrl;
|
|
198
|
+
const tokenProvider = this.opts.tokenProviderFactory?.({ agentGatewayUrl, keypair }) ??
|
|
199
|
+
createDeviceMessagingTokenProvider({ agentGatewayUrl, keypair });
|
|
200
|
+
const sdk = this.opts.sdkFactory?.(log) ??
|
|
201
|
+
new MessageSDK({
|
|
202
|
+
// No serviceUrl — agents talk to MS only via Agent Gateway
|
|
203
|
+
// proxy + Centrifugo (P6 boundary).
|
|
204
|
+
centrifugoUrl: ctx.config.messageBus.centrifugoUrl || undefined,
|
|
205
|
+
}, wrapLoggerForSdk(log));
|
|
206
|
+
let tok;
|
|
207
|
+
try {
|
|
208
|
+
tok = await tokenProvider(ctx.config.messageBus.principalIdOverride ?? "");
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
this.logAttemptFailure(attempt, "messaging-token fetch failed", err, log);
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
const principalId = tok.principal_id;
|
|
215
|
+
if (!principalId) {
|
|
216
|
+
this.logAttemptFailure(attempt, "gateway returned empty principal_id", null, log);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
const register = new TurnContextRegister();
|
|
220
|
+
const busyReply = new MessageSdkBusyReplyHandler({ sdk });
|
|
221
|
+
const chatHandler = buildMessageBusInboundHandler({
|
|
222
|
+
host: ctx.host,
|
|
223
|
+
register,
|
|
224
|
+
busyReply,
|
|
225
|
+
logger: log,
|
|
226
|
+
localAgentId: this.opts.localAgentId,
|
|
227
|
+
source: "chat_message",
|
|
228
|
+
});
|
|
229
|
+
const legacyHandler = buildMessageBusInboundHandler({
|
|
230
|
+
host: ctx.host,
|
|
231
|
+
register,
|
|
232
|
+
busyReply,
|
|
233
|
+
logger: log,
|
|
234
|
+
localAgentId: this.opts.localAgentId,
|
|
235
|
+
source: "agent_request",
|
|
236
|
+
});
|
|
237
|
+
sdk.onMessage("chat_message", async (env) => chatHandler(env));
|
|
238
|
+
// Gap F — fire a one-shot deprecation warn the FIRST time a legacy
|
|
239
|
+
// `agent_request` envelope arrives. Mirrors beeos-claw
|
|
240
|
+
// [`index.ts:551-557`](agents/beeos-claw/src/index.ts).
|
|
241
|
+
sdk.onMessage("agent_request", async (env) => {
|
|
242
|
+
if (!this.legacyDeprecationLogged) {
|
|
243
|
+
this.legacyDeprecationLogged = true;
|
|
244
|
+
log.warn({}, "message-bus: received legacy 'agent_request' envelope — this path is deprecated; please migrate upstream callers to 'chat_message' (L0 AgentInvocationService)");
|
|
245
|
+
}
|
|
246
|
+
await legacyHandler(env);
|
|
247
|
+
});
|
|
248
|
+
const unsubscribe = installMessageBusOutbound({
|
|
249
|
+
host: ctx.host,
|
|
250
|
+
sdk,
|
|
251
|
+
register,
|
|
252
|
+
logger: log,
|
|
253
|
+
});
|
|
254
|
+
sdk.onceConnected(() => {
|
|
255
|
+
log.info({
|
|
256
|
+
principalId,
|
|
257
|
+
centrifugoUrl: tok.centrifugo_url,
|
|
258
|
+
channels: tok.channels,
|
|
259
|
+
attempt,
|
|
260
|
+
}, "message-bus: connected");
|
|
261
|
+
});
|
|
262
|
+
try {
|
|
263
|
+
await sdk.connect(principalId, {
|
|
264
|
+
centrifugoUrl: tok.centrifugo_url,
|
|
265
|
+
tokenProvider,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
// Roll back the listener so we don't double-subscribe on the
|
|
270
|
+
// next retry, and disconnect the half-opened SDK.
|
|
271
|
+
try {
|
|
272
|
+
unsubscribe();
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
/* ignore */
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
await sdk.disconnect();
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
/* ignore */
|
|
282
|
+
}
|
|
283
|
+
this.logAttemptFailure(attempt, "Centrifugo connect failed", err, log);
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
this.sdk = sdk;
|
|
287
|
+
this.register = register;
|
|
288
|
+
this.unsubscribeOutbound = unsubscribe;
|
|
289
|
+
this.resolvedPrincipalId = principalId;
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
nextDelayMs(attempt) {
|
|
293
|
+
const schedule = this.opts.retrySchedule ?? DEFAULT_RETRY_SCHEDULE_MS;
|
|
294
|
+
const idx = Math.min(attempt - 1, schedule.length - 1);
|
|
295
|
+
return schedule[idx];
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Log an attempt failure with attenuated severity — first attempt
|
|
299
|
+
* + a few well-spaced retries get a `warn`, the rest are `debug` to
|
|
300
|
+
* avoid log spam when an instance has never been provisioned.
|
|
301
|
+
*/
|
|
302
|
+
logAttemptFailure(attempt, summary, err, log) {
|
|
303
|
+
const errStr = err == null
|
|
304
|
+
? null
|
|
305
|
+
: err instanceof Error
|
|
306
|
+
? err.message
|
|
307
|
+
: String(err);
|
|
308
|
+
const fields = { attempt };
|
|
309
|
+
if (errStr)
|
|
310
|
+
fields.err = errStr;
|
|
311
|
+
if (WARN_ATTEMPT_INDICES.has(attempt)) {
|
|
312
|
+
log.warn(fields, `message-bus: ${summary} — will retry`);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
log.debug(fields, `message-bus: ${summary} — will retry`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
logRetryScheduled(attempt, delayMs, log) {
|
|
319
|
+
if (WARN_ATTEMPT_INDICES.has(attempt)) {
|
|
320
|
+
log.warn({ attempt, retryInMs: delayMs }, "message-bus: scheduled connect retry");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Sleep `ms` milliseconds. Returns `true` when stop() cancels the
|
|
325
|
+
* timer mid-flight (so the caller breaks out of its loop), or
|
|
326
|
+
* `false` once the timer fires normally.
|
|
327
|
+
*/
|
|
328
|
+
sleep(ms) {
|
|
329
|
+
const sched = this.scheduler();
|
|
330
|
+
return new Promise((resolve) => {
|
|
331
|
+
this.retryResolve = resolve;
|
|
332
|
+
this.retryHandle = sched.setTimeout(() => {
|
|
333
|
+
this.retryHandle = null;
|
|
334
|
+
this.retryResolve = null;
|
|
335
|
+
resolve(false);
|
|
336
|
+
}, ms);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
cancelPendingRetry() {
|
|
340
|
+
if (this.retryHandle == null)
|
|
341
|
+
return;
|
|
342
|
+
const sched = this.scheduler();
|
|
343
|
+
try {
|
|
344
|
+
sched.clearTimeout(this.retryHandle);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
/* ignore */
|
|
348
|
+
}
|
|
349
|
+
this.retryHandle = null;
|
|
350
|
+
const resolve = this.retryResolve;
|
|
351
|
+
this.retryResolve = null;
|
|
352
|
+
if (resolve)
|
|
353
|
+
resolve(true);
|
|
354
|
+
}
|
|
355
|
+
scheduler() {
|
|
356
|
+
return (this.opts.scheduler ?? {
|
|
357
|
+
setTimeout: (cb, t) => setTimeout(cb, t),
|
|
358
|
+
clearTimeout: (h) => clearTimeout(h),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Adapt the device-agent pino logger to the MessageSDKLogger shape the
|
|
364
|
+
* shared SDK expects. The SDK calls `info(msg)` / `warn(msg, fields?)`
|
|
365
|
+
* with positional args; pino's signature is `(obj, msg)` so we coerce
|
|
366
|
+
* to a stringly form that prints sanely either way.
|
|
367
|
+
*/
|
|
368
|
+
function wrapLoggerForSdk(log) {
|
|
369
|
+
return {
|
|
370
|
+
info: (...args) => log.info(formatSdkArgs(args)),
|
|
371
|
+
warn: (...args) => log.warn(formatSdkArgs(args)),
|
|
372
|
+
error: (...args) => log.error(formatSdkArgs(args)),
|
|
373
|
+
debug: (...args) => log.debug(formatSdkArgs(args)),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function formatSdkArgs(args) {
|
|
377
|
+
return args
|
|
378
|
+
.map((a) => {
|
|
379
|
+
if (typeof a === "string")
|
|
380
|
+
return a;
|
|
381
|
+
if (a instanceof Error)
|
|
382
|
+
return a.message;
|
|
383
|
+
try {
|
|
384
|
+
return JSON.stringify(a);
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
return String(a);
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
.join(" ");
|
|
391
|
+
}
|
|
392
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound — bridge AgentHost events back to the MS IM channel.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to `AgentHost.on(listener)` ONCE at plugin start, then
|
|
5
|
+
* publishes Centrifugo `ch:{channelId}` envelopes whose shape matches
|
|
6
|
+
* the contract the rest of the BeeOS platform expects:
|
|
7
|
+
*
|
|
8
|
+
* - `agent_reply_delta` — streaming chunks. Carries an inner
|
|
9
|
+
* `update.sessionUpdate` field that callers (A2A / OpenAPI) demux
|
|
10
|
+
* on. Only `agent_message_chunk` is treated as user-visible
|
|
11
|
+
* reply text by `backend/pkg/convmsg/extract.go::extractNestedDelta`;
|
|
12
|
+
* `agent_thought_chunk` is intentionally filtered out so reasoning
|
|
13
|
+
* never leaks into status text.
|
|
14
|
+
*
|
|
15
|
+
* - `agent_reply` — terminal reply on `task:end`. `payload.text` for
|
|
16
|
+
* success, `payload.error` for failure. Matches
|
|
17
|
+
* `chatinvoke/invoker.go::extractReplyFinal`.
|
|
18
|
+
*
|
|
19
|
+
* Each envelope carries `in_reply_to` set to the original request's
|
|
20
|
+
* `message_id`, which is what the L0 `/channels/{id}/wait` endpoint
|
|
21
|
+
* matches on.
|
|
22
|
+
*
|
|
23
|
+
* The listener filters by taskId via the in-flight register — events
|
|
24
|
+
* for tasks NOT submitted through the message-bus inbound (e.g. ACP
|
|
25
|
+
* `session/prompt` traffic) are ignored, so the two surfaces never
|
|
26
|
+
* collide.
|
|
27
|
+
*/
|
|
28
|
+
import type { MessageSDK } from "@beeos-ai/message-sdk";
|
|
29
|
+
import type { AgentHost } from "../../ports/agent-host.js";
|
|
30
|
+
import type { Logger } from "../../logger/index.js";
|
|
31
|
+
import type { TurnContextRegister } from "./turn-context.js";
|
|
32
|
+
export interface MessageBusOutboundOptions {
|
|
33
|
+
host: AgentHost;
|
|
34
|
+
sdk: MessageSDK;
|
|
35
|
+
register: TurnContextRegister;
|
|
36
|
+
logger: Logger;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Install the outbound listener on the host. Returns an `unsubscribe`
|
|
40
|
+
* function — call it from the plugin's `stop()` so we don't keep
|
|
41
|
+
* publishing after the SDK has been disconnected.
|
|
42
|
+
*/
|
|
43
|
+
export declare function installMessageBusOutbound(opts: MessageBusOutboundOptions): () => void;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound — bridge AgentHost events back to the MS IM channel.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to `AgentHost.on(listener)` ONCE at plugin start, then
|
|
5
|
+
* publishes Centrifugo `ch:{channelId}` envelopes whose shape matches
|
|
6
|
+
* the contract the rest of the BeeOS platform expects:
|
|
7
|
+
*
|
|
8
|
+
* - `agent_reply_delta` — streaming chunks. Carries an inner
|
|
9
|
+
* `update.sessionUpdate` field that callers (A2A / OpenAPI) demux
|
|
10
|
+
* on. Only `agent_message_chunk` is treated as user-visible
|
|
11
|
+
* reply text by `backend/pkg/convmsg/extract.go::extractNestedDelta`;
|
|
12
|
+
* `agent_thought_chunk` is intentionally filtered out so reasoning
|
|
13
|
+
* never leaks into status text.
|
|
14
|
+
*
|
|
15
|
+
* - `agent_reply` — terminal reply on `task:end`. `payload.text` for
|
|
16
|
+
* success, `payload.error` for failure. Matches
|
|
17
|
+
* `chatinvoke/invoker.go::extractReplyFinal`.
|
|
18
|
+
*
|
|
19
|
+
* Each envelope carries `in_reply_to` set to the original request's
|
|
20
|
+
* `message_id`, which is what the L0 `/channels/{id}/wait` endpoint
|
|
21
|
+
* matches on.
|
|
22
|
+
*
|
|
23
|
+
* The listener filters by taskId via the in-flight register — events
|
|
24
|
+
* for tasks NOT submitted through the message-bus inbound (e.g. ACP
|
|
25
|
+
* `session/prompt` traffic) are ignored, so the two surfaces never
|
|
26
|
+
* collide.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Install the outbound listener on the host. Returns an `unsubscribe`
|
|
30
|
+
* function — call it from the plugin's `stop()` so we don't keep
|
|
31
|
+
* publishing after the SDK has been disconnected.
|
|
32
|
+
*/
|
|
33
|
+
export function installMessageBusOutbound(opts) {
|
|
34
|
+
const listener = (event) => {
|
|
35
|
+
void handleEvent(event, opts);
|
|
36
|
+
};
|
|
37
|
+
return opts.host.on(listener);
|
|
38
|
+
}
|
|
39
|
+
async function handleEvent(event, opts) {
|
|
40
|
+
const ctx = opts.register.getByTaskId(event.taskId);
|
|
41
|
+
if (!ctx)
|
|
42
|
+
return; // Not an MS-originated turn — ignored.
|
|
43
|
+
try {
|
|
44
|
+
switch (event.type) {
|
|
45
|
+
case "task:start":
|
|
46
|
+
// Intentionally not emitted. beeos-claw does not publish an
|
|
47
|
+
// `agent_status` delta on prompt start either — see
|
|
48
|
+
// [acp-server.ts onStreamDelta](agents/beeos-claw/src/acp-server.ts);
|
|
49
|
+
// the first wire publish is the first real LLM delta. The
|
|
50
|
+
// server-side `extractNestedDelta` only honours
|
|
51
|
+
// `agent_message_chunk` anyway, so synthesising an extra
|
|
52
|
+
// `agent_status` envelope would be pure noise. KISS — skip.
|
|
53
|
+
return;
|
|
54
|
+
case "task:text":
|
|
55
|
+
await sendDelta(opts, ctx, "agent_message_chunk", event.text);
|
|
56
|
+
return;
|
|
57
|
+
case "task:reasoning":
|
|
58
|
+
await sendDelta(opts, ctx, "agent_thought_chunk", event.text);
|
|
59
|
+
return;
|
|
60
|
+
case "task:notice":
|
|
61
|
+
// Notices (warnings / non-fatal errors) surface as user-visible
|
|
62
|
+
// chunks so the operator sees something. Same `agent_message_chunk`
|
|
63
|
+
// sessionUpdate so they get included in the final concatenated
|
|
64
|
+
// text by extractNestedDelta. Payload is the canonical
|
|
65
|
+
// `{ session_key, update }` shape — no extra fields, matching
|
|
66
|
+
// beeos-claw's onStreamDelta output exactly.
|
|
67
|
+
await sendDelta(opts, ctx, "agent_message_chunk", event.text);
|
|
68
|
+
return;
|
|
69
|
+
case "task:tool":
|
|
70
|
+
case "task:step":
|
|
71
|
+
// Tool calls and step transitions are debug-grade today —
|
|
72
|
+
// streaming them would dilute the agent_message_chunk text.
|
|
73
|
+
// Skip silently; callers that want richer telemetry should
|
|
74
|
+
// subscribe to the personal channel directly.
|
|
75
|
+
return;
|
|
76
|
+
case "task:end": {
|
|
77
|
+
const ok = !event.error;
|
|
78
|
+
const finalText = (event.result?.finalMessage ??
|
|
79
|
+
event.result?.text ??
|
|
80
|
+
"") || "";
|
|
81
|
+
// Wire shape mirrors beeos-claw `[acp-server.ts:498/:514]`
|
|
82
|
+
// (agents/beeos-claw/src/acp-server.ts):
|
|
83
|
+
// success → { session_key, text }
|
|
84
|
+
// failure → { session_key, error } (text optional)
|
|
85
|
+
const payload = ok
|
|
86
|
+
? { session_key: ctx.sessionKey, text: finalText }
|
|
87
|
+
: {
|
|
88
|
+
session_key: ctx.sessionKey,
|
|
89
|
+
error: event.error ?? "unknown_error",
|
|
90
|
+
text: finalText,
|
|
91
|
+
};
|
|
92
|
+
await opts.sdk.sendToChannel(ctx.channelId, "agent_reply", payload, { inReplyTo: ctx.inboundMessageId });
|
|
93
|
+
opts.logger.info({
|
|
94
|
+
taskId: event.taskId,
|
|
95
|
+
channelId: ctx.channelId,
|
|
96
|
+
ok,
|
|
97
|
+
durationMs: event.completedAt - ctx.startedAt,
|
|
98
|
+
}, "message-bus: turn finished");
|
|
99
|
+
opts.register.clear(event.taskId);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// Never throw out of the AgentHost listener — that would crash the
|
|
106
|
+
// entire event fan-out. Log and continue; the next event for the
|
|
107
|
+
// same task will retry the publish path.
|
|
108
|
+
opts.logger.error({
|
|
109
|
+
err: err instanceof Error ? err.message : String(err),
|
|
110
|
+
eventType: event.type,
|
|
111
|
+
taskId: event.taskId,
|
|
112
|
+
channelId: ctx.channelId,
|
|
113
|
+
}, "message-bus: outbound publish failed");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Publish a single `agent_reply_delta` envelope.
|
|
118
|
+
*
|
|
119
|
+
* Wire shape locked-in to match beeos-claw
|
|
120
|
+
* [`acp-server.ts onStreamDelta` (acp-server.ts:438-450)](agents/beeos-claw/src/acp-server.ts):
|
|
121
|
+
*
|
|
122
|
+
* ```json
|
|
123
|
+
* {
|
|
124
|
+
* "session_key": "agent:{id}:invoke:ctx-...",
|
|
125
|
+
* "update": {
|
|
126
|
+
* "sessionUpdate": "agent_message_chunk" | "agent_thought_chunk",
|
|
127
|
+
* "content": { "type": "text", "text": "..." }
|
|
128
|
+
* }
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* Server-side `backend/pkg/convmsg/extract.go::extractNestedDelta`
|
|
133
|
+
* filters on `update.sessionUpdate === "agent_message_chunk"` for
|
|
134
|
+
* user-visible reply text — `agent_thought_chunk` is dropped so
|
|
135
|
+
* reasoning never leaks into status text. The `session_key` field is
|
|
136
|
+
* forward-compatible: no current backend consumer reads it (verified
|
|
137
|
+
* via `rg session_key backend/`), but emitting it preserves byte-for-
|
|
138
|
+
* byte parity with beeos-claw for downstream observers.
|
|
139
|
+
*/
|
|
140
|
+
async function sendDelta(opts, ctx, sessionUpdate, text) {
|
|
141
|
+
await opts.sdk.sendToChannel(ctx.channelId, "agent_reply_delta", {
|
|
142
|
+
session_key: ctx.sessionKey,
|
|
143
|
+
update: {
|
|
144
|
+
sessionUpdate,
|
|
145
|
+
content: { type: "text", text },
|
|
146
|
+
},
|
|
147
|
+
}, { inReplyTo: ctx.inboundMessageId });
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=outbound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/outbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-flight Message-Service turn register.
|
|
3
|
+
*
|
|
4
|
+
* Holds the channel + reply correlator for the SINGLE turn that's
|
|
5
|
+
* currently executing on the host. Single-entry by design — device-agent
|
|
6
|
+
* runs one ACP session and the SessionManager serialises turns inside
|
|
7
|
+
* that session, so a second concurrent `chat_message` would be a logic
|
|
8
|
+
* error and is rejected via `busy-reply.ts`.
|
|
9
|
+
*
|
|
10
|
+
* The taskId is allocated by `host.submitTask(...)` and used to filter
|
|
11
|
+
* the `host.on(listener)` event stream so we never publish another
|
|
12
|
+
* turn's deltas to the wrong MS channel.
|
|
13
|
+
*/
|
|
14
|
+
export interface MessagingTurnContext {
|
|
15
|
+
/** Allocated by `host.submitTask` — used to filter AgentEvent stream. */
|
|
16
|
+
taskId: string;
|
|
17
|
+
/** IM channel the reply is published on (`ch:{channelId}`). */
|
|
18
|
+
channelId: string;
|
|
19
|
+
/** Request `message_id` echoed back as `in_reply_to` on every reply. */
|
|
20
|
+
inboundMessageId: string;
|
|
21
|
+
/**
|
|
22
|
+
* Stable session anchor (e.g. `agent:{id}:invoke:ctx-{contextId}` or
|
|
23
|
+
* `agent:{id}:invoke:ch-{channelId}`) derived by `extractChatPrompt`.
|
|
24
|
+
* Embedded as `payload.session_key` on every outbound publish so the
|
|
25
|
+
* wire format matches beeos-claw `[acp-server.ts:443]`
|
|
26
|
+
* (agents/beeos-claw/src/acp-server.ts) — downstream consumers
|
|
27
|
+
* (dashboards, A2A SSE projection) use it to group chunks belonging
|
|
28
|
+
* to the same logical conversation turn.
|
|
29
|
+
*/
|
|
30
|
+
sessionKey: string;
|
|
31
|
+
/** Optional context-id, surfaced for logging only. */
|
|
32
|
+
contextId?: string;
|
|
33
|
+
/** Wall-clock timestamp the turn was claimed (ms). For diagnostics. */
|
|
34
|
+
startedAt: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Single-entry in-flight register.
|
|
38
|
+
*
|
|
39
|
+
* Methods are intentionally synchronous — they run on the WS publication
|
|
40
|
+
* callback (low latency) and on AgentHost listener invocations.
|
|
41
|
+
*/
|
|
42
|
+
export declare class TurnContextRegister {
|
|
43
|
+
private current;
|
|
44
|
+
isBusy(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Claim the register for a new turn. Returns the entry on success or
|
|
47
|
+
* `null` when a previous turn is still in flight (caller MUST then
|
|
48
|
+
* use the busy-reply path).
|
|
49
|
+
*/
|
|
50
|
+
claim(entry: Omit<MessagingTurnContext, "startedAt">): MessagingTurnContext | null;
|
|
51
|
+
current_(): MessagingTurnContext | null;
|
|
52
|
+
getByTaskId(taskId: string): MessagingTurnContext | null;
|
|
53
|
+
/** Idempotent — clearing twice is a no-op. */
|
|
54
|
+
clear(taskId: string): void;
|
|
55
|
+
/** Force-clear (used during plugin stop / disconnect). */
|
|
56
|
+
reset(): void;
|
|
57
|
+
}
|