@beeos-ai/device-agent 0.9.5 → 0.9.8
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/config/schema.js +14 -6
- package/dist/config/schema.js.map +1 -1
- package/dist/inbound/cli/main.js +2 -2
- package/dist/inbound/cli/run-command.js +13 -1
- package/dist/inbound/cli/run-command.js.map +1 -1
- package/dist/messaging/token-provider.d.ts +15 -14
- package/dist/messaging/token-provider.js +16 -16
- package/dist/messaging/token-provider.js.map +1 -1
- package/dist/model/adapters/guiowl.js +3 -2
- package/dist/model/adapters/guiowl.js.map +1 -1
- package/dist/model/adapters/maiui.js +80 -53
- package/dist/model/adapters/maiui.js.map +1 -1
- package/dist/model/adapters/qwenvl.js +147 -51
- package/dist/model/adapters/qwenvl.js.map +1 -1
- package/dist/model/adapters/tool-call.js +13 -3
- package/dist/model/adapters/tool-call.js.map +1 -1
- package/dist/model/adapters/uitars.js +80 -84
- package/dist/model/adapters/uitars.js.map +1 -1
- package/dist/plugins/message-bus/busy-reply.d.ts +5 -5
- package/dist/plugins/message-bus/busy-reply.js +30 -9
- package/dist/plugins/message-bus/busy-reply.js.map +1 -1
- package/dist/plugins/message-bus/cancel.d.ts +5 -11
- package/dist/plugins/message-bus/cancel.js +8 -14
- package/dist/plugins/message-bus/cancel.js.map +1 -1
- package/dist/plugins/message-bus/inbound.d.ts +18 -25
- package/dist/plugins/message-bus/inbound.js +23 -55
- package/dist/plugins/message-bus/inbound.js.map +1 -1
- package/dist/plugins/message-bus/index.d.ts +25 -44
- package/dist/plugins/message-bus/index.js +81 -111
- package/dist/plugins/message-bus/index.js.map +1 -1
- package/dist/plugins/message-bus/ms-channel-notifier.d.ts +24 -37
- package/dist/plugins/message-bus/ms-channel-notifier.js +39 -59
- package/dist/plugins/message-bus/ms-channel-notifier.js.map +1 -1
- package/dist/resources/parsers.js +17 -0
- package/dist/resources/parsers.js.map +1 -1
- package/package.json +2 -2
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
* `/wait` timeout.
|
|
11
11
|
*
|
|
12
12
|
* Type/payload alignment: uses the same `agent_reply` type that the
|
|
13
|
-
*
|
|
13
|
+
* MsChannelNotifier emits, with a `{ error }` body that matches
|
|
14
14
|
* `backend/pkg/chatinvoke/invoker.go::extractReplyFinal` (which reads
|
|
15
15
|
* `payload.error` first, falling back to `payload.text`).
|
|
16
16
|
*/
|
|
17
|
-
import type {
|
|
17
|
+
import type { MessageClient } from "@beeos-ai/message-sdk";
|
|
18
18
|
export interface BusyReplyRequest {
|
|
19
19
|
channelId: string;
|
|
20
20
|
inboundMessageId: string;
|
|
21
21
|
/**
|
|
22
22
|
* Session key derived from the rejected envelope (via
|
|
23
|
-
* `extractChatPrompt(
|
|
23
|
+
* `extractChatPrompt(msg).sessionKey`). Always set so the busy
|
|
24
24
|
* reply payload has the same `{ session_key, error }` shape as a
|
|
25
25
|
* regular `agent_reply`.
|
|
26
26
|
*/
|
|
@@ -30,9 +30,9 @@ export interface BusyReplyHandler {
|
|
|
30
30
|
replyBusy(req: BusyReplyRequest): Promise<void>;
|
|
31
31
|
}
|
|
32
32
|
export interface BusyReplyHandlerOptions {
|
|
33
|
-
|
|
33
|
+
client: MessageClient;
|
|
34
34
|
}
|
|
35
|
-
export declare class
|
|
35
|
+
export declare class MessageClientBusyReplyHandler implements BusyReplyHandler {
|
|
36
36
|
private readonly opts;
|
|
37
37
|
constructor(opts: BusyReplyHandlerOptions);
|
|
38
38
|
replyBusy(req: BusyReplyRequest): Promise<void>;
|
|
@@ -10,28 +10,49 @@
|
|
|
10
10
|
* `/wait` timeout.
|
|
11
11
|
*
|
|
12
12
|
* Type/payload alignment: uses the same `agent_reply` type that the
|
|
13
|
-
*
|
|
13
|
+
* MsChannelNotifier emits, with a `{ error }` body that matches
|
|
14
14
|
* `backend/pkg/chatinvoke/invoker.go::extractReplyFinal` (which reads
|
|
15
15
|
* `payload.error` first, falling back to `payload.text`).
|
|
16
16
|
*/
|
|
17
|
-
export class
|
|
17
|
+
export class MessageClientBusyReplyHandler {
|
|
18
18
|
opts;
|
|
19
19
|
constructor(opts) {
|
|
20
20
|
this.opts = opts;
|
|
21
21
|
}
|
|
22
22
|
async replyBusy(req) {
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
23
|
+
// Persistent v2 send — MS writes to channel_messages, fans out via
|
|
24
|
+
// Centrifugo. Caller's /wait long-poll has reconnect race windows;
|
|
25
|
+
// persistence is required so the busy reply isn't lost.
|
|
26
|
+
//
|
|
27
|
+
// Wire contract (PR7 — see agents/device-agent/docs/adr/006-agent-busy-contract.md):
|
|
28
|
+
// - `error` (string) — legacy field surfaced as
|
|
29
|
+
// TaskState.Error by chatinvoke's
|
|
30
|
+
// msgderive.extractError. Stable.
|
|
31
|
+
// - `error_code` (string) — structured machine-readable code.
|
|
32
|
+
// Callers prefer this over fragile
|
|
33
|
+
// `error.includes("agent_busy")`
|
|
34
|
+
// string matching. Stable; future
|
|
35
|
+
// busy variants will add new codes
|
|
36
|
+
// while keeping `error_code` as the
|
|
37
|
+
// switch-on field.
|
|
38
|
+
// - `text` (string) — human-readable explanation.
|
|
39
|
+
//
|
|
40
|
+
// `replyTo` is set so MS's single_shot logic
|
|
41
|
+
// (channel.SingleShot && reply_to != "") auto-closes the channel
|
|
42
|
+
// with reason="completed". Callers therefore observe
|
|
43
|
+
// TaskState.Status === TaskFailed (msgderive extracts the
|
|
44
|
+
// embedded `error` field), not TaskSucceeded, even though
|
|
45
|
+
// close_reason is technically "completed".
|
|
46
|
+
await this.opts.client.messages.send({
|
|
47
|
+
conversationId: req.channelId,
|
|
28
48
|
type: "agent_reply",
|
|
29
|
-
|
|
49
|
+
replyTo: req.inboundMessageId,
|
|
50
|
+
content: {
|
|
30
51
|
session_key: req.sessionKey,
|
|
31
52
|
error: "agent_busy",
|
|
53
|
+
error_code: "agent_busy",
|
|
32
54
|
text: "device-agent is processing another request — please retry once it completes.",
|
|
33
55
|
},
|
|
34
|
-
inReplyTo: req.inboundMessageId,
|
|
35
56
|
});
|
|
36
57
|
}
|
|
37
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"busy-reply.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/busy-reply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAwBH,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"busy-reply.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/busy-reply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAwBH,MAAM,OAAO,6BAA6B;IACX;IAA7B,YAA6B,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAE9D,KAAK,CAAC,SAAS,CAAC,GAAqB;QACnC,mEAAmE;QACnE,mEAAmE;QACnE,wDAAwD;QACxD,EAAE;QACF,qFAAqF;QACrF,wDAAwD;QACxD,+DAA+D;QAC/D,+DAA+D;QAC/D,iEAAiE;QACjE,gEAAgE;QAChE,8DAA8D;QAC9D,+DAA+D;QAC/D,gEAAgE;QAChE,iEAAiE;QACjE,gDAAgD;QAChD,2DAA2D;QAC3D,EAAE;QACF,6CAA6C;QAC7C,iEAAiE;QACjE,qDAAqD;QACrD,0DAA0D;QAC1D,0DAA0D;QAC1D,2CAA2C;QAC3C,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YACnC,cAAc,EAAE,GAAG,CAAC,SAAS;YAC7B,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,GAAG,CAAC,gBAAgB;YAC7B,OAAO,EAAE;gBACP,WAAW,EAAE,GAAG,CAAC,UAAU;gBAC3B,KAAK,EAAE,YAAY;gBACnB,UAAU,EAAE,YAAY;gBACxB,IAAI,EAAE,8EAA8E;aACrF;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inbound — `chat_cancel`
|
|
2
|
+
* Inbound — `chat_cancel` message handler.
|
|
3
3
|
*
|
|
4
|
-
* The BeeOS platform pushes a protocol-agnostic `chat_cancel`
|
|
4
|
+
* The BeeOS platform pushes a protocol-agnostic `chat_cancel` message
|
|
5
5
|
* onto the agent's personal Centrifugo channel when an in-flight
|
|
6
6
|
* `chat_message` turn must be aborted. Triggers include:
|
|
7
7
|
*
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* disconnect / deadline)
|
|
11
11
|
* - A2A Service timeout sweeper (task exceeded `timeout_ms`)
|
|
12
12
|
*
|
|
13
|
-
* The wire
|
|
13
|
+
* The wire content (see `backend/pkg/convmsg/extract.go`
|
|
14
14
|
* `MessageTypeChatCancel`) carries IM primitives only:
|
|
15
15
|
*
|
|
16
16
|
* {
|
|
@@ -28,14 +28,8 @@
|
|
|
28
28
|
* globally — so resolving by inboundMessageId is unambiguous (either
|
|
29
29
|
* the current turn is THIS turn, or the cancel is for a stale /
|
|
30
30
|
* already-completed turn we should silently drop).
|
|
31
|
-
*
|
|
32
|
-
* No-ops:
|
|
33
|
-
* - missing / non-string `target_message_id` → drop, debug log
|
|
34
|
-
* - tracker has no entry for that message_id → drop, debug log
|
|
35
|
-
* (the turn already finished, or this is a stray cancel for a
|
|
36
|
-
* different agent on a shared principal — both safe to ignore)
|
|
37
31
|
*/
|
|
38
|
-
import type {
|
|
32
|
+
import type { Message } from "@beeos-ai/message-sdk";
|
|
39
33
|
import type { SessionManager } from "../../app/session-manager.js";
|
|
40
34
|
import type { Logger } from "../../logger/index.js";
|
|
41
35
|
import type { MsInflightTracker } from "./ms-inflight-tracker.js";
|
|
@@ -45,4 +39,4 @@ export interface MessageBusCancelHandlerOptions {
|
|
|
45
39
|
tracker: MsInflightTracker;
|
|
46
40
|
logger: Logger;
|
|
47
41
|
}
|
|
48
|
-
export declare function buildMessageBusCancelHandler(opts: MessageBusCancelHandlerOptions): (
|
|
42
|
+
export declare function buildMessageBusCancelHandler(opts: MessageBusCancelHandlerOptions): (msg: Message) => Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inbound — `chat_cancel`
|
|
2
|
+
* Inbound — `chat_cancel` message handler.
|
|
3
3
|
*
|
|
4
|
-
* The BeeOS platform pushes a protocol-agnostic `chat_cancel`
|
|
4
|
+
* The BeeOS platform pushes a protocol-agnostic `chat_cancel` message
|
|
5
5
|
* onto the agent's personal Centrifugo channel when an in-flight
|
|
6
6
|
* `chat_message` turn must be aborted. Triggers include:
|
|
7
7
|
*
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* disconnect / deadline)
|
|
11
11
|
* - A2A Service timeout sweeper (task exceeded `timeout_ms`)
|
|
12
12
|
*
|
|
13
|
-
* The wire
|
|
13
|
+
* The wire content (see `backend/pkg/convmsg/extract.go`
|
|
14
14
|
* `MessageTypeChatCancel`) carries IM primitives only:
|
|
15
15
|
*
|
|
16
16
|
* {
|
|
@@ -28,23 +28,17 @@
|
|
|
28
28
|
* globally — so resolving by inboundMessageId is unambiguous (either
|
|
29
29
|
* the current turn is THIS turn, or the cancel is for a stale /
|
|
30
30
|
* already-completed turn we should silently drop).
|
|
31
|
-
*
|
|
32
|
-
* No-ops:
|
|
33
|
-
* - missing / non-string `target_message_id` → drop, debug log
|
|
34
|
-
* - tracker has no entry for that message_id → drop, debug log
|
|
35
|
-
* (the turn already finished, or this is a stray cancel for a
|
|
36
|
-
* different agent on a shared principal — both safe to ignore)
|
|
37
31
|
*/
|
|
38
32
|
export const MESSAGE_TYPE_CHAT_CANCEL = "chat_cancel";
|
|
39
33
|
export function buildMessageBusCancelHandler(opts) {
|
|
40
|
-
return async (
|
|
41
|
-
const
|
|
42
|
-
const targetMessageId = pickString(
|
|
34
|
+
return async (msg) => {
|
|
35
|
+
const content = (msg.content ?? {});
|
|
36
|
+
const targetMessageId = pickString(content.target_message_id);
|
|
43
37
|
if (!targetMessageId) {
|
|
44
|
-
opts.logger.debug({ type:
|
|
38
|
+
opts.logger.debug({ type: msg.type, message_id: msg.id }, "message-bus: chat_cancel dropped (no target_message_id)");
|
|
45
39
|
return;
|
|
46
40
|
}
|
|
47
|
-
const reason = pickString(
|
|
41
|
+
const reason = pickString(content.reason) || "unknown";
|
|
48
42
|
const entry = opts.tracker.findByInboundMessageId(targetMessageId);
|
|
49
43
|
if (!entry) {
|
|
50
44
|
opts.logger.debug({ targetMessageId, reason }, "message-bus: chat_cancel for unknown / already-finished turn — no-op");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancel.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/cancel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cancel.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/cancel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AASH,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAQtD,MAAM,UAAU,4BAA4B,CAC1C,IAAoC;IAEpC,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAA4B,CAAC;QAC/D,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC9D,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,EAAE,EACtC,yDAAyD,CAC1D,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;QAEvD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,eAAe,CAAC,CAAC;QACnE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,eAAe,EAAE,MAAM,EAAE,EAC3B,sEAAsE,CACvE,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,IAAI,CACd;YACE,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,eAAe;YACf,MAAM;YACN,SAAS;SACV,EACD,6DAA6D,CAC9D,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAU;IAC5B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACxD,CAAC"}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inbound — `chat_message` / `agent_request`
|
|
2
|
+
* Inbound — `chat_message` / `agent_request` handler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Wires `client.on("message", ...)` to
|
|
5
5
|
* `SessionManager.promptForSession(sid, prompt)`. Per-channel session
|
|
6
6
|
* isolation:
|
|
7
7
|
*
|
|
8
8
|
* 1. Extract `{ message, channelId, messageId, contextId, sessionKey,
|
|
9
|
-
* files }` from the
|
|
10
|
-
* on malformed
|
|
9
|
+
* files }` from the v2 `Message` via `extractChatPrompt`
|
|
10
|
+
* (returns null on malformed messages — silently dropped).
|
|
11
11
|
* 2. Reject if no `channelId` or no `messageId` — replies must round
|
|
12
|
-
* trip through `ch:{
|
|
12
|
+
* trip through `ch:{conversationId}` with `replyTo=messageId`, so
|
|
13
13
|
* neither is optional.
|
|
14
14
|
* 3. Layer 3 in-flight gate: if `sessionManager.isTurnInFlight()`
|
|
15
15
|
* returns true, send a busy-reply on the inbound channel and
|
|
16
16
|
* return. The agent serialises every prompt through a single
|
|
17
17
|
* physical device, so concurrent requests are rejected (not
|
|
18
|
-
* queued)
|
|
18
|
+
* queued).
|
|
19
19
|
* 4. Resolve the per-channel sid from `sessionKey` (the SDK already
|
|
20
20
|
* computed `agent:{id}:invoke:ctx-{contextId}` /
|
|
21
21
|
* `:ch-{channelId}`). Bind a fresh `MsChannelNotifier` to that
|
|
@@ -24,28 +24,20 @@
|
|
|
24
24
|
* `files.length > 0`) and call
|
|
25
25
|
* `sessionManager.promptForSession(sid, promptText)`.
|
|
26
26
|
* 6. The notifier translates streaming session/update envelopes into
|
|
27
|
-
* MS
|
|
27
|
+
* MS messages (`agent_reply_delta`, terminal `agent_reply`)
|
|
28
28
|
* automatically — no separate outbound listener needed.
|
|
29
29
|
* 7. ALWAYS unbind the notifier on completion (success / error /
|
|
30
30
|
* cancellation) so the next turn on the same channel gets a fresh
|
|
31
31
|
* notifier with `accumulatedText = ""`.
|
|
32
|
-
*
|
|
33
|
-
* Defensive paths:
|
|
34
|
-
* - If `promptForSession` rejects with `"agent_busy"` (extremely
|
|
35
|
-
* rare race after the explicit `isTurnInFlight` check), fall
|
|
36
|
-
* through to the busy-reply path so the caller's `/wait` doesn't
|
|
37
|
-
* hang.
|
|
38
|
-
* - For any other thrown error, publish a terminal `agent_reply`
|
|
39
|
-
* with `error` set so callers see a definite outcome.
|
|
40
32
|
*/
|
|
41
|
-
import {
|
|
33
|
+
import type { Message, MessageClient } from "@beeos-ai/message-sdk";
|
|
42
34
|
import type { SessionManager } from "../../app/session-manager.js";
|
|
43
35
|
import type { Logger } from "../../logger/index.js";
|
|
44
36
|
import type { BusyReplyHandler } from "./busy-reply.js";
|
|
45
37
|
import type { MsInflightTracker } from "./ms-inflight-tracker.js";
|
|
46
38
|
export interface MessageBusInboundOptions {
|
|
47
39
|
sessionManager: SessionManager;
|
|
48
|
-
|
|
40
|
+
client: MessageClient;
|
|
49
41
|
busyReply: BusyReplyHandler;
|
|
50
42
|
tracker: MsInflightTracker;
|
|
51
43
|
logger: Logger;
|
|
@@ -56,16 +48,17 @@ export interface MessageBusInboundOptions {
|
|
|
56
48
|
*/
|
|
57
49
|
localAgentId?: string;
|
|
58
50
|
/**
|
|
59
|
-
* Source tag passed to `extractChatPrompt`. Identifies which
|
|
60
|
-
*
|
|
61
|
-
*
|
|
51
|
+
* Source tag passed to `extractChatPrompt`. Identifies which message
|
|
52
|
+
* type triggered the call — `chat_message` is the primary L0 path,
|
|
53
|
+
* `agent_request` is the legacy A2A SendTask shim.
|
|
62
54
|
*/
|
|
63
55
|
source: "chat_message" | "agent_request";
|
|
64
56
|
}
|
|
65
57
|
/**
|
|
66
|
-
* Build the per-
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* and feed
|
|
58
|
+
* Build the per-message handler. The returned function is what gets
|
|
59
|
+
* dispatched from the `client.on("message", ...)` listener after a
|
|
60
|
+
* type-filter check — keeping the factory shape lets unit tests
|
|
61
|
+
* instantiate the closure with mock collaborators and feed messages
|
|
62
|
+
* synchronously.
|
|
70
63
|
*/
|
|
71
|
-
export declare function buildMessageBusInboundHandler(opts: MessageBusInboundOptions): (
|
|
64
|
+
export declare function buildMessageBusInboundHandler(opts: MessageBusInboundOptions): (msg: Message) => Promise<void>;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inbound — `chat_message` / `agent_request`
|
|
2
|
+
* Inbound — `chat_message` / `agent_request` handler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Wires `client.on("message", ...)` to
|
|
5
5
|
* `SessionManager.promptForSession(sid, prompt)`. Per-channel session
|
|
6
6
|
* isolation:
|
|
7
7
|
*
|
|
8
8
|
* 1. Extract `{ message, channelId, messageId, contextId, sessionKey,
|
|
9
|
-
* files }` from the
|
|
10
|
-
* on malformed
|
|
9
|
+
* files }` from the v2 `Message` via `extractChatPrompt`
|
|
10
|
+
* (returns null on malformed messages — silently dropped).
|
|
11
11
|
* 2. Reject if no `channelId` or no `messageId` — replies must round
|
|
12
|
-
* trip through `ch:{
|
|
12
|
+
* trip through `ch:{conversationId}` with `replyTo=messageId`, so
|
|
13
13
|
* neither is optional.
|
|
14
14
|
* 3. Layer 3 in-flight gate: if `sessionManager.isTurnInFlight()`
|
|
15
15
|
* returns true, send a busy-reply on the inbound channel and
|
|
16
16
|
* return. The agent serialises every prompt through a single
|
|
17
17
|
* physical device, so concurrent requests are rejected (not
|
|
18
|
-
* queued)
|
|
18
|
+
* queued).
|
|
19
19
|
* 4. Resolve the per-channel sid from `sessionKey` (the SDK already
|
|
20
20
|
* computed `agent:{id}:invoke:ctx-{contextId}` /
|
|
21
21
|
* `:ch-{channelId}`). Bind a fresh `MsChannelNotifier` to that
|
|
@@ -24,50 +24,39 @@
|
|
|
24
24
|
* `files.length > 0`) and call
|
|
25
25
|
* `sessionManager.promptForSession(sid, promptText)`.
|
|
26
26
|
* 6. The notifier translates streaming session/update envelopes into
|
|
27
|
-
* MS
|
|
27
|
+
* MS messages (`agent_reply_delta`, terminal `agent_reply`)
|
|
28
28
|
* automatically — no separate outbound listener needed.
|
|
29
29
|
* 7. ALWAYS unbind the notifier on completion (success / error /
|
|
30
30
|
* cancellation) so the next turn on the same channel gets a fresh
|
|
31
31
|
* notifier with `accumulatedText = ""`.
|
|
32
|
-
*
|
|
33
|
-
* Defensive paths:
|
|
34
|
-
* - If `promptForSession` rejects with `"agent_busy"` (extremely
|
|
35
|
-
* rare race after the explicit `isTurnInFlight` check), fall
|
|
36
|
-
* through to the busy-reply path so the caller's `/wait` doesn't
|
|
37
|
-
* hang.
|
|
38
|
-
* - For any other thrown error, publish a terminal `agent_reply`
|
|
39
|
-
* with `error` set so callers see a definite outcome.
|
|
40
32
|
*/
|
|
41
|
-
import { extractChatPrompt
|
|
33
|
+
import { extractChatPrompt } from "@beeos-ai/message-sdk/chat-envelope";
|
|
42
34
|
import { MsChannelNotifier } from "./ms-channel-notifier.js";
|
|
43
35
|
/**
|
|
44
|
-
* Build the per-
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* and feed
|
|
36
|
+
* Build the per-message handler. The returned function is what gets
|
|
37
|
+
* dispatched from the `client.on("message", ...)` listener after a
|
|
38
|
+
* type-filter check — keeping the factory shape lets unit tests
|
|
39
|
+
* instantiate the closure with mock collaborators and feed messages
|
|
40
|
+
* synchronously.
|
|
48
41
|
*/
|
|
49
42
|
export function buildMessageBusInboundHandler(opts) {
|
|
50
|
-
return async (
|
|
51
|
-
const prompt = extractChatPrompt(
|
|
43
|
+
return async (msg) => {
|
|
44
|
+
const prompt = extractChatPrompt(msg, {
|
|
52
45
|
source: opts.source,
|
|
53
46
|
localAgentId: opts.localAgentId ?? "",
|
|
54
47
|
});
|
|
55
48
|
if (!prompt) {
|
|
56
|
-
opts.logger.warn({ type:
|
|
49
|
+
opts.logger.warn({ type: msg.type, message_id: msg.id }, "message-bus: dropped message (no prompt could be extracted)");
|
|
57
50
|
return;
|
|
58
51
|
}
|
|
59
52
|
if (!prompt.channelId || !prompt.messageId) {
|
|
60
53
|
opts.logger.warn({
|
|
61
|
-
type:
|
|
54
|
+
type: msg.type,
|
|
62
55
|
hasChannelId: !!prompt.channelId,
|
|
63
56
|
hasMessageId: !!prompt.messageId,
|
|
64
|
-
}, "message-bus: dropped
|
|
57
|
+
}, "message-bus: dropped message (missing channelId or messageId — cannot correlate reply)");
|
|
65
58
|
return;
|
|
66
59
|
}
|
|
67
|
-
// Layer 3 — physical-device-serial gate. The agent has ONE device
|
|
68
|
-
// and ONE in-flight task slot. Concurrent invocations get an
|
|
69
|
-
// `agent_busy` reply so callers can retry instead of hanging on
|
|
70
|
-
// /wait.
|
|
71
60
|
if (opts.sessionManager.isTurnInFlight()) {
|
|
72
61
|
opts.logger.warn({
|
|
73
62
|
incomingMessageId: prompt.messageId,
|
|
@@ -80,14 +69,9 @@ export function buildMessageBusInboundHandler(opts) {
|
|
|
80
69
|
});
|
|
81
70
|
return;
|
|
82
71
|
}
|
|
83
|
-
// Per-channel session anchor. `sessionKey` is the canonical id —
|
|
84
|
-
// multiple chat_message envelopes carrying the same `context_id`
|
|
85
|
-
// (or `channel_id` fallback) flow into the same sid, so the
|
|
86
|
-
// history-injection preamble in `_executePrompt` can stitch them
|
|
87
|
-
// into a continuous conversation.
|
|
88
72
|
const sid = prompt.sessionKey;
|
|
89
73
|
const notifier = new MsChannelNotifier({
|
|
90
|
-
|
|
74
|
+
client: opts.client,
|
|
91
75
|
channelId: prompt.channelId,
|
|
92
76
|
inboundMessageId: prompt.messageId,
|
|
93
77
|
sessionKey: prompt.sessionKey,
|
|
@@ -125,9 +109,6 @@ export function buildMessageBusInboundHandler(opts) {
|
|
|
125
109
|
inboundMessageId: prompt.messageId,
|
|
126
110
|
err: message,
|
|
127
111
|
}, "message-bus: promptForSession rejected — emitting fallback reply");
|
|
128
|
-
// If the notifier already published the terminal `agent_reply`
|
|
129
|
-
// (normal stream_complete path) the second publish is harmless
|
|
130
|
-
// because the notifier guards on `finalized`.
|
|
131
112
|
if (message === "agent_busy") {
|
|
132
113
|
await opts.busyReply.replyBusy({
|
|
133
114
|
channelId: prompt.channelId,
|
|
@@ -136,19 +117,16 @@ export function buildMessageBusInboundHandler(opts) {
|
|
|
136
117
|
});
|
|
137
118
|
}
|
|
138
119
|
else if (!notifier.isFinalized) {
|
|
139
|
-
// Surface the error as a terminal agent_reply so callers see
|
|
140
|
-
// a definite outcome instead of a /wait timeout. Use durable
|
|
141
|
-
// sendChannelMessage (same path as MsChannelNotifier.publishFinal)
|
|
142
|
-
// so caller's /wait can match via DB even if it's mid-reconnect.
|
|
143
120
|
try {
|
|
144
|
-
await opts.
|
|
121
|
+
await opts.client.messages.send({
|
|
122
|
+
conversationId: prompt.channelId,
|
|
145
123
|
type: "agent_reply",
|
|
146
|
-
|
|
124
|
+
replyTo: prompt.messageId,
|
|
125
|
+
content: {
|
|
147
126
|
session_key: prompt.sessionKey,
|
|
148
127
|
error: message,
|
|
149
128
|
text: notifier.currentText,
|
|
150
129
|
},
|
|
151
|
-
inReplyTo: prompt.messageId,
|
|
152
130
|
});
|
|
153
131
|
}
|
|
154
132
|
catch (publishErr) {
|
|
@@ -167,16 +145,6 @@ export function buildMessageBusInboundHandler(opts) {
|
|
|
167
145
|
}
|
|
168
146
|
};
|
|
169
147
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Append a markdown-style `[attachments]` block listing file URLs to
|
|
172
|
-
* the prompt text. KISS: today the device-agent agent-core only takes
|
|
173
|
-
* a single string prompt, so the only way to surface attachments is
|
|
174
|
-
* inline. Empty `files` returns the message unchanged.
|
|
175
|
-
*
|
|
176
|
-
* Deeper integration (downloading via the resource-resolution layer,
|
|
177
|
-
* uploading to the device sandbox) is intentionally out of scope —
|
|
178
|
-
* see plan §E.
|
|
179
|
-
*/
|
|
180
148
|
function appendAttachments(message, files) {
|
|
181
149
|
if (!files || files.length === 0)
|
|
182
150
|
return message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/inbound.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/inbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,33 +2,32 @@
|
|
|
2
2
|
* Plugin — message-bus.
|
|
3
3
|
*
|
|
4
4
|
* Subscribes the device-agent to the BeeOS Message Service so it can
|
|
5
|
-
* receive `chat_message`
|
|
6
|
-
* invocations and reply over `ch:{
|
|
7
|
-
* `agent_reply_delta` (
|
|
5
|
+
* receive `chat_message` messages from L0 / A2A / MCP / OpenAPI
|
|
6
|
+
* invocations and reply over `ch:{conversationId}` with `agent_reply` /
|
|
7
|
+
* `agent_reply_delta` (replyTo = original message_id).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Enabled by default since v0.9.7. Opt out with
|
|
10
|
+
* `BEEOS_MESSAGE_BUS_ENABLED=false` or `--no-message-bus`. Even when
|
|
11
|
+
* enabled, the plugin requires BOTH `--agent-gateway-url` and an
|
|
12
|
+
* Ed25519 identity to actually connect; missing either, it logs a
|
|
13
|
+
* one-line warning during `start()` and stays dormant — the rest of
|
|
14
|
+
* the agent (Bridge ACP, HTTP debug) still runs.
|
|
14
15
|
*
|
|
15
16
|
* Lifecycle:
|
|
16
17
|
* - `init` — read config, decide enabled / disabled.
|
|
17
18
|
* - `start` — kick off the connect loop. Returns IMMEDIATELY (does
|
|
18
19
|
* not block bootstrap on the network). The first connect attempt
|
|
19
20
|
* and any subsequent retries run in the background.
|
|
20
|
-
* - `stop` — cancel any pending retry, disconnect
|
|
21
|
-
*
|
|
21
|
+
* - `stop` — cancel any pending retry, disconnect client, drop
|
|
22
|
+
* in-flight register.
|
|
22
23
|
*
|
|
23
24
|
* Failure mode: if `tokenProvider()` rejects (e.g. the device instance
|
|
24
25
|
* has not been provisioned with `agentPublicKey` yet), the plugin
|
|
25
26
|
* schedules a retry with exponential backoff (30s → 60s → 120s → 240s,
|
|
26
27
|
* capped at 300s) and keeps trying forever until either `connect`
|
|
27
|
-
* succeeds or `stop()` cancels the loop.
|
|
28
|
-
* `localServer.onceAgentMappingReady(...)` deferred-connect pattern —
|
|
29
|
-
* see `[index.ts:484-498](agents/beeos-claw/src/index.ts)`.
|
|
28
|
+
* succeeds or `stop()` cancels the loop.
|
|
30
29
|
*/
|
|
31
|
-
import {
|
|
30
|
+
import { MessageClient, type TokenProvider } from "@beeos-ai/message-sdk";
|
|
32
31
|
import type { Logger } from "../../logger/index.js";
|
|
33
32
|
import type { AgentPlugin, PluginContext } from "../../ports/plugin.js";
|
|
34
33
|
/**
|
|
@@ -51,11 +50,11 @@ export interface MessageBusPluginOptions {
|
|
|
51
50
|
privateKey: Uint8Array;
|
|
52
51
|
};
|
|
53
52
|
/**
|
|
54
|
-
* Override the
|
|
55
|
-
*
|
|
56
|
-
*
|
|
53
|
+
* Override the client constructor — used by tests to inject a fake
|
|
54
|
+
* MessageClient whose `connect` / `on` / `messages.send` are spies.
|
|
55
|
+
* Production code leaves this `undefined`.
|
|
57
56
|
*/
|
|
58
|
-
|
|
57
|
+
clientFactory?: (logger: Logger) => MessageClient;
|
|
59
58
|
/**
|
|
60
59
|
* Override the token provider builder — used by tests to skip the
|
|
61
60
|
* real Agent Gateway round-trip.
|
|
@@ -81,8 +80,7 @@ export interface MessageBusPluginOptions {
|
|
|
81
80
|
retrySchedule?: readonly number[];
|
|
82
81
|
/**
|
|
83
82
|
* Test seam — `setTimeout` / `clearTimeout` shim. Defaults to
|
|
84
|
-
* the global `setTimeout` family.
|
|
85
|
-
* scheduler so they don't need real timers.
|
|
83
|
+
* the global `setTimeout` family.
|
|
86
84
|
*/
|
|
87
85
|
scheduler?: {
|
|
88
86
|
setTimeout: (cb: () => void, ms: number) => unknown;
|
|
@@ -93,7 +91,7 @@ export declare class MessageBusPlugin implements AgentPlugin {
|
|
|
93
91
|
private readonly opts;
|
|
94
92
|
readonly name = "message-bus";
|
|
95
93
|
private ctx?;
|
|
96
|
-
private
|
|
94
|
+
private client;
|
|
97
95
|
private tracker;
|
|
98
96
|
private resolvedPrincipalId;
|
|
99
97
|
private stopped;
|
|
@@ -108,38 +106,21 @@ export declare class MessageBusPlugin implements AgentPlugin {
|
|
|
108
106
|
start(): Promise<void>;
|
|
109
107
|
stop(): Promise<void>;
|
|
110
108
|
get principalId(): string;
|
|
111
|
-
/**
|
|
112
|
-
* Test helper — resolves once `start()`'s connect loop has either
|
|
113
|
-
* successfully connected or `stop()` was called. Production code
|
|
114
|
-
* never needs this.
|
|
115
|
-
*/
|
|
116
109
|
whenSettled(): Promise<void>;
|
|
117
110
|
private runConnectLoop;
|
|
118
111
|
/**
|
|
119
|
-
* Single connect attempt. Returns `true` on success
|
|
120
|
-
*
|
|
121
|
-
* should schedule a retry).
|
|
112
|
+
* Single connect attempt. Returns `true` on success, `false` on any
|
|
113
|
+
* failure (caller should schedule a retry).
|
|
122
114
|
*
|
|
123
|
-
* Each attempt builds a FRESH `
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* handshake only.
|
|
115
|
+
* Each attempt builds a FRESH `MessageClient` — re-using one across
|
|
116
|
+
* reconnects would carry stale handlers and confuse centrifuge-js
|
|
117
|
+
* internal state. The client has its own internal reconnect once
|
|
118
|
+
* `.connect()` succeeds; this loop is for the OPENING handshake only.
|
|
128
119
|
*/
|
|
129
120
|
private tryConnectOnce;
|
|
130
121
|
private nextDelayMs;
|
|
131
|
-
/**
|
|
132
|
-
* Log an attempt failure with attenuated severity — first attempt
|
|
133
|
-
* + a few well-spaced retries get a `warn`, the rest are `debug` to
|
|
134
|
-
* avoid log spam when an instance has never been provisioned.
|
|
135
|
-
*/
|
|
136
122
|
private logAttemptFailure;
|
|
137
123
|
private logRetryScheduled;
|
|
138
|
-
/**
|
|
139
|
-
* Sleep `ms` milliseconds. Returns `true` when stop() cancels the
|
|
140
|
-
* timer mid-flight (so the caller breaks out of its loop), or
|
|
141
|
-
* `false` once the timer fires normally.
|
|
142
|
-
*/
|
|
143
124
|
private sleep;
|
|
144
125
|
private cancelPendingRetry;
|
|
145
126
|
private scheduler;
|