@beeos-ai/device-agent 0.8.0 → 0.9.3
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/app/fixed-session-id.d.ts +19 -0
- package/dist/app/fixed-session-id.js +26 -0
- package/dist/app/fixed-session-id.js.map +1 -0
- package/dist/app/jsonl-session-sweeper.d.ts +90 -0
- package/dist/app/jsonl-session-sweeper.js +162 -0
- package/dist/app/jsonl-session-sweeper.js.map +1 -0
- package/dist/app/jsonl-store.d.ts +10 -0
- package/dist/app/jsonl-store.js +10 -4
- package/dist/app/jsonl-store.js.map +1 -1
- package/dist/app/session-manager.d.ts +135 -1
- package/dist/app/session-manager.js +195 -12
- package/dist/app/session-manager.js.map +1 -1
- package/dist/bootstrap.js +146 -2
- package/dist/bootstrap.js.map +1 -1
- package/dist/config/loader.js +2 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +2 -0
- package/dist/config/schema.js +41 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/fleet/domain/device.d.ts +17 -0
- package/dist/fleet/domain/device.js +1 -0
- package/dist/fleet/domain/device.js.map +1 -1
- package/dist/fleet/domain/fsm.d.ts +30 -1
- package/dist/fleet/domain/fsm.js +100 -4
- package/dist/fleet/domain/fsm.js.map +1 -1
- package/dist/fleet/supervisor.js +38 -18
- package/dist/fleet/supervisor.js.map +1 -1
- package/dist/plugins/acp/index.d.ts +27 -30
- package/dist/plugins/acp/index.js +44 -137
- package/dist/plugins/acp/index.js.map +1 -1
- package/dist/plugins/message-bus/busy-reply.js +13 -5
- package/dist/plugins/message-bus/busy-reply.js.map +1 -1
- package/dist/plugins/message-bus/cancel.d.ts +48 -0
- package/dist/plugins/message-bus/cancel.js +66 -0
- package/dist/plugins/message-bus/cancel.js.map +1 -0
- package/dist/plugins/message-bus/inbound.d.ts +38 -22
- package/dist/plugins/message-bus/inbound.js +113 -50
- package/dist/plugins/message-bus/inbound.js.map +1 -1
- package/dist/plugins/message-bus/index.d.ts +1 -2
- package/dist/plugins/message-bus/index.js +39 -37
- package/dist/plugins/message-bus/index.js.map +1 -1
- package/dist/plugins/message-bus/ms-channel-notifier.d.ts +113 -0
- package/dist/plugins/message-bus/ms-channel-notifier.js +206 -0
- package/dist/plugins/message-bus/ms-channel-notifier.js.map +1 -0
- package/dist/plugins/message-bus/ms-inflight-tracker.d.ts +36 -0
- package/dist/plugins/message-bus/ms-inflight-tracker.js +42 -0
- package/dist/plugins/message-bus/ms-inflight-tracker.js.map +1 -0
- package/dist/ports/plugin.d.ts +12 -0
- package/package.json +2 -2
- package/dist/agent-runtime/skills/sqlite-store.d.ts +0 -60
- package/dist/agent-runtime/skills/sqlite-store.js +0 -234
- package/dist/agent-runtime/skills/sqlite-store.js.map +0 -1
- package/dist/plugins/message-bus/outbound.d.ts +0 -43
- package/dist/plugins/message-bus/outbound.js +0 -149
- package/dist/plugins/message-bus/outbound.js.map +0 -1
- package/dist/plugins/message-bus/turn-context.d.ts +0 -57
- package/dist/plugins/message-bus/turn-context.js +0 -53
- package/dist/plugins/message-bus/turn-context.js.map +0 -1
- package/dist/test-lan-autoglm.d.ts +0 -7
- package/dist/test-lan-autoglm.js +0 -46
- package/dist/test-lan-autoglm.js.map +0 -1
- package/dist/test-mem-breakdown.d.ts +0 -1
- package/dist/test-mem-breakdown.js +0 -103
- package/dist/test-mem-breakdown.js.map +0 -1
- package/dist/test-mem.d.ts +0 -1
- package/dist/test-mem.js +0 -60
- package/dist/test-mem.js.map +0 -1
- package/dist/test-mobile-use.d.ts +0 -1
- package/dist/test-mobile-use.js +0 -18
- package/dist/test-mobile-use.js.map +0 -1
- package/dist/test-screenshot-loop.d.ts +0 -1
- package/dist/test-screenshot-loop.js +0 -43
- package/dist/test-screenshot-loop.js.map +0 -1
- package/dist/test-screenshot.d.ts +0 -1
- package/dist/test-screenshot.js +0 -10
- package/dist/test-screenshot.js.map +0 -1
- package/dist/test-ui-xml-dump.d.ts +0 -15
- package/dist/test-ui-xml-dump.js +0 -49
- package/dist/test-ui-xml-dump.js.map +0 -1
|
@@ -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,0BAA0B;IACR;IAA7B,YAA6B,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAE9D,KAAK,CAAC,SAAS,CAAC,GAAqB;QACnC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"busy-reply.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/busy-reply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAwBH,MAAM,OAAO,0BAA0B;IACR;IAA7B,YAA6B,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAE9D,KAAK,CAAC,SAAS,CAAC,GAAqB;QACnC,oEAAoE;QACpE,mEAAmE;QACnE,mEAAmE;QACnE,2DAA2D;QAC3D,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE;YACpD,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE;gBACP,WAAW,EAAE,GAAG,CAAC,UAAU;gBAC3B,KAAK,EAAE,YAAY;gBACnB,IAAI,EAAE,8EAA8E;aACrF;YACD,SAAS,EAAE,GAAG,CAAC,gBAAgB;SAChC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound — `chat_cancel` envelope handler.
|
|
3
|
+
*
|
|
4
|
+
* The BeeOS platform pushes a protocol-agnostic `chat_cancel` envelope
|
|
5
|
+
* onto the agent's personal Centrifugo channel when an in-flight
|
|
6
|
+
* `chat_message` turn must be aborted. Triggers include:
|
|
7
|
+
*
|
|
8
|
+
* - A2A Service `tasks/cancel` JSON-RPC (caller-initiated)
|
|
9
|
+
* - A2A Service `InvokeTask` ctx-cancel fallback (caller HTTP
|
|
10
|
+
* disconnect / deadline)
|
|
11
|
+
* - A2A Service timeout sweeper (task exceeded `timeout_ms`)
|
|
12
|
+
*
|
|
13
|
+
* The wire payload (see `backend/pkg/convmsg/extract.go`
|
|
14
|
+
* `MessageTypeChatCancel`) carries IM primitives only:
|
|
15
|
+
*
|
|
16
|
+
* {
|
|
17
|
+
* "target_message_id": "<message_id of the original chat_message>",
|
|
18
|
+
* "reason": "canceled" | "caller_disconnected" | "timeout"
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* Agents MUST NOT decode protocol headers (no `task_id`, no
|
|
22
|
+
* `a2a_task_cancel`) — that contract is enforced here by intentionally
|
|
23
|
+
* reading nothing besides `target_message_id` and `reason`.
|
|
24
|
+
*
|
|
25
|
+
* v0.9.0 lookup path: query the tiny `MsInflightTracker` for the
|
|
26
|
+
* matching sid, then call `sessionManager.cancel()`. The SessionManager
|
|
27
|
+
* itself owns the AbortController and only allows ONE in-flight turn
|
|
28
|
+
* globally — so resolving by inboundMessageId is unambiguous (either
|
|
29
|
+
* the current turn is THIS turn, or the cancel is for a stale /
|
|
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
|
+
*/
|
|
38
|
+
import type { MessageEnvelope } from "@beeos-ai/message-sdk";
|
|
39
|
+
import type { SessionManager } from "../../app/session-manager.js";
|
|
40
|
+
import type { Logger } from "../../logger/index.js";
|
|
41
|
+
import type { MsInflightTracker } from "./ms-inflight-tracker.js";
|
|
42
|
+
export declare const MESSAGE_TYPE_CHAT_CANCEL = "chat_cancel";
|
|
43
|
+
export interface MessageBusCancelHandlerOptions {
|
|
44
|
+
sessionManager: SessionManager;
|
|
45
|
+
tracker: MsInflightTracker;
|
|
46
|
+
logger: Logger;
|
|
47
|
+
}
|
|
48
|
+
export declare function buildMessageBusCancelHandler(opts: MessageBusCancelHandlerOptions): (envelope: MessageEnvelope) => Promise<void>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound — `chat_cancel` envelope handler.
|
|
3
|
+
*
|
|
4
|
+
* The BeeOS platform pushes a protocol-agnostic `chat_cancel` envelope
|
|
5
|
+
* onto the agent's personal Centrifugo channel when an in-flight
|
|
6
|
+
* `chat_message` turn must be aborted. Triggers include:
|
|
7
|
+
*
|
|
8
|
+
* - A2A Service `tasks/cancel` JSON-RPC (caller-initiated)
|
|
9
|
+
* - A2A Service `InvokeTask` ctx-cancel fallback (caller HTTP
|
|
10
|
+
* disconnect / deadline)
|
|
11
|
+
* - A2A Service timeout sweeper (task exceeded `timeout_ms`)
|
|
12
|
+
*
|
|
13
|
+
* The wire payload (see `backend/pkg/convmsg/extract.go`
|
|
14
|
+
* `MessageTypeChatCancel`) carries IM primitives only:
|
|
15
|
+
*
|
|
16
|
+
* {
|
|
17
|
+
* "target_message_id": "<message_id of the original chat_message>",
|
|
18
|
+
* "reason": "canceled" | "caller_disconnected" | "timeout"
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* Agents MUST NOT decode protocol headers (no `task_id`, no
|
|
22
|
+
* `a2a_task_cancel`) — that contract is enforced here by intentionally
|
|
23
|
+
* reading nothing besides `target_message_id` and `reason`.
|
|
24
|
+
*
|
|
25
|
+
* v0.9.0 lookup path: query the tiny `MsInflightTracker` for the
|
|
26
|
+
* matching sid, then call `sessionManager.cancel()`. The SessionManager
|
|
27
|
+
* itself owns the AbortController and only allows ONE in-flight turn
|
|
28
|
+
* globally — so resolving by inboundMessageId is unambiguous (either
|
|
29
|
+
* the current turn is THIS turn, or the cancel is for a stale /
|
|
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
|
+
*/
|
|
38
|
+
export const MESSAGE_TYPE_CHAT_CANCEL = "chat_cancel";
|
|
39
|
+
export function buildMessageBusCancelHandler(opts) {
|
|
40
|
+
return async (envelope) => {
|
|
41
|
+
const payload = (envelope.payload ?? {});
|
|
42
|
+
const targetMessageId = pickString(payload.target_message_id);
|
|
43
|
+
if (!targetMessageId) {
|
|
44
|
+
opts.logger.debug({ type: envelope.type, message_id: envelope.message_id }, "message-bus: chat_cancel dropped (no target_message_id)");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const reason = pickString(payload.reason) || "unknown";
|
|
48
|
+
const entry = opts.tracker.findByInboundMessageId(targetMessageId);
|
|
49
|
+
if (!entry) {
|
|
50
|
+
opts.logger.debug({ targetMessageId, reason }, "message-bus: chat_cancel for unknown / already-finished turn — no-op");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const cancelled = await opts.sessionManager.cancel();
|
|
54
|
+
opts.logger.info({
|
|
55
|
+
sid: entry.sid,
|
|
56
|
+
channelId: entry.channelId,
|
|
57
|
+
targetMessageId,
|
|
58
|
+
reason,
|
|
59
|
+
cancelled,
|
|
60
|
+
}, "message-bus: chat_cancel received — aborting in-flight turn");
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function pickString(v) {
|
|
64
|
+
return typeof v === "string" && v.length > 0 ? v : "";
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=cancel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancel.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/cancel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AASH,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAQtD,MAAM,UAAU,4BAA4B,CAC1C,IAAoC;IAEpC,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAA4B,CAAC;QACpE,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,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,EACxD,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,37 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Inbound — `chat_message` / `agent_request` envelope handler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* v0.9.0 — wires `MessageSDK.onMessage(...)` directly to
|
|
5
|
+
* `SessionManager.promptForSession(sid, prompt)`. Per-channel session
|
|
6
|
+
* isolation:
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* payload (returns null on malformed envelopes — silently dropped).
|
|
8
|
+
* 1. Extract `{ message, channelId, messageId, contextId, sessionKey,
|
|
9
|
+
* files }` from the payload via `extractChatPrompt` (returns null
|
|
10
|
+
* on malformed envelopes — silently dropped).
|
|
12
11
|
* 2. Reject if no `channelId` or no `messageId` — replies must round
|
|
13
12
|
* trip through `ch:{channelId}` with `in_reply_to=messageId`, so
|
|
14
|
-
* neither is optional
|
|
15
|
-
* 3.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* neither is optional.
|
|
14
|
+
* 3. Layer 3 in-flight gate: if `sessionManager.isTurnInFlight()`
|
|
15
|
+
* returns true, send a busy-reply on the inbound channel and
|
|
16
|
+
* return. The agent serialises every prompt through a single
|
|
17
|
+
* physical device, so concurrent requests are rejected (not
|
|
18
|
+
* queued) — matches v0.8.0 contract.
|
|
19
|
+
* 4. Resolve the per-channel sid from `sessionKey` (the SDK already
|
|
20
|
+
* computed `agent:{id}:invoke:ctx-{contextId}` /
|
|
21
|
+
* `:ch-{channelId}`). Bind a fresh `MsChannelNotifier` to that
|
|
22
|
+
* sid for the duration of the turn.
|
|
23
|
+
* 5. Build the prompt (text + a minimal `[attachments]` block when
|
|
24
|
+
* `files.length > 0`) and call
|
|
25
|
+
* `sessionManager.promptForSession(sid, promptText)`.
|
|
26
|
+
* 6. The notifier translates streaming session/update envelopes into
|
|
27
|
+
* MS wire envelopes (`agent_reply_delta`, terminal `agent_reply`)
|
|
28
|
+
* automatically — no separate outbound listener needed.
|
|
29
|
+
* 7. ALWAYS unbind the notifier on completion (success / error /
|
|
30
|
+
* cancellation) so the next turn on the same channel gets a fresh
|
|
31
|
+
* notifier with `accumulatedText = ""`.
|
|
20
32
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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.
|
|
25
40
|
*/
|
|
26
|
-
import { type MessageEnvelope } from "@beeos-ai/message-sdk";
|
|
27
|
-
import type {
|
|
41
|
+
import { type MessageEnvelope, type MessageSDK } from "@beeos-ai/message-sdk";
|
|
42
|
+
import type { SessionManager } from "../../app/session-manager.js";
|
|
28
43
|
import type { Logger } from "../../logger/index.js";
|
|
29
|
-
import type { TurnContextRegister } from "./turn-context.js";
|
|
30
44
|
import type { BusyReplyHandler } from "./busy-reply.js";
|
|
45
|
+
import type { MsInflightTracker } from "./ms-inflight-tracker.js";
|
|
31
46
|
export interface MessageBusInboundOptions {
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
sessionManager: SessionManager;
|
|
48
|
+
sdk: MessageSDK;
|
|
34
49
|
busyReply: BusyReplyHandler;
|
|
50
|
+
tracker: MsInflightTracker;
|
|
35
51
|
logger: Logger;
|
|
36
52
|
/**
|
|
37
53
|
* Local agent identity surfaced to `extractChatPrompt`. Used only for
|
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Inbound — `chat_message` / `agent_request` envelope handler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* v0.9.0 — wires `MessageSDK.onMessage(...)` directly to
|
|
5
|
+
* `SessionManager.promptForSession(sid, prompt)`. Per-channel session
|
|
6
|
+
* isolation:
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* payload (returns null on malformed envelopes — silently dropped).
|
|
8
|
+
* 1. Extract `{ message, channelId, messageId, contextId, sessionKey,
|
|
9
|
+
* files }` from the payload via `extractChatPrompt` (returns null
|
|
10
|
+
* on malformed envelopes — silently dropped).
|
|
12
11
|
* 2. Reject if no `channelId` or no `messageId` — replies must round
|
|
13
12
|
* trip through `ch:{channelId}` with `in_reply_to=messageId`, so
|
|
14
|
-
* neither is optional
|
|
15
|
-
* 3.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* neither is optional.
|
|
14
|
+
* 3. Layer 3 in-flight gate: if `sessionManager.isTurnInFlight()`
|
|
15
|
+
* returns true, send a busy-reply on the inbound channel and
|
|
16
|
+
* return. The agent serialises every prompt through a single
|
|
17
|
+
* physical device, so concurrent requests are rejected (not
|
|
18
|
+
* queued) — matches v0.8.0 contract.
|
|
19
|
+
* 4. Resolve the per-channel sid from `sessionKey` (the SDK already
|
|
20
|
+
* computed `agent:{id}:invoke:ctx-{contextId}` /
|
|
21
|
+
* `:ch-{channelId}`). Bind a fresh `MsChannelNotifier` to that
|
|
22
|
+
* sid for the duration of the turn.
|
|
23
|
+
* 5. Build the prompt (text + a minimal `[attachments]` block when
|
|
24
|
+
* `files.length > 0`) and call
|
|
25
|
+
* `sessionManager.promptForSession(sid, promptText)`.
|
|
26
|
+
* 6. The notifier translates streaming session/update envelopes into
|
|
27
|
+
* MS wire envelopes (`agent_reply_delta`, terminal `agent_reply`)
|
|
28
|
+
* automatically — no separate outbound listener needed.
|
|
29
|
+
* 7. ALWAYS unbind the notifier on completion (success / error /
|
|
30
|
+
* cancellation) so the next turn on the same channel gets a fresh
|
|
31
|
+
* notifier with `accumulatedText = ""`.
|
|
20
32
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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.
|
|
25
40
|
*/
|
|
26
41
|
import { extractChatPrompt, } from "@beeos-ai/message-sdk";
|
|
42
|
+
import { MsChannelNotifier } from "./ms-channel-notifier.js";
|
|
27
43
|
/**
|
|
28
44
|
* Build the per-envelope handler. The returned function is what gets
|
|
29
45
|
* registered with `sdk.onMessage(type, handler)` — keeping the factory
|
|
@@ -48,11 +64,14 @@ export function buildMessageBusInboundHandler(opts) {
|
|
|
48
64
|
}, "message-bus: dropped envelope (missing channelId or messageId — cannot correlate reply)");
|
|
49
65
|
return;
|
|
50
66
|
}
|
|
51
|
-
|
|
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
|
+
if (opts.sessionManager.isTurnInFlight()) {
|
|
52
72
|
opts.logger.warn({
|
|
53
73
|
incomingMessageId: prompt.messageId,
|
|
54
74
|
incomingChannelId: prompt.channelId,
|
|
55
|
-
activeTurn: opts.register.current_()?.taskId,
|
|
56
75
|
}, "message-bus: agent busy — sending agent_busy reply");
|
|
57
76
|
await opts.busyReply.replyBusy({
|
|
58
77
|
channelId: prompt.channelId,
|
|
@@ -61,47 +80,91 @@ export function buildMessageBusInboundHandler(opts) {
|
|
|
61
80
|
});
|
|
62
81
|
return;
|
|
63
82
|
}
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
const claimed = opts.register.claim({
|
|
74
|
-
taskId: submitted.taskId,
|
|
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
|
+
const sid = prompt.sessionKey;
|
|
89
|
+
const notifier = new MsChannelNotifier({
|
|
90
|
+
sdk: opts.sdk,
|
|
75
91
|
channelId: prompt.channelId,
|
|
76
92
|
inboundMessageId: prompt.messageId,
|
|
77
93
|
sessionKey: prompt.sessionKey,
|
|
78
|
-
|
|
94
|
+
logger: opts.logger,
|
|
79
95
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
channelId: prompt.channelId,
|
|
88
|
-
inboundMessageId: prompt.messageId,
|
|
89
|
-
sessionKey: prompt.sessionKey,
|
|
90
|
-
});
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
96
|
+
opts.sessionManager.bindNotifier(sid, notifier);
|
|
97
|
+
opts.tracker.set({
|
|
98
|
+
messageId: prompt.messageId,
|
|
99
|
+
channelId: prompt.channelId,
|
|
100
|
+
sid,
|
|
101
|
+
});
|
|
102
|
+
const promptText = appendAttachments(prompt.message, prompt.files);
|
|
93
103
|
opts.logger.info({
|
|
94
|
-
|
|
104
|
+
sid,
|
|
95
105
|
channelId: prompt.channelId,
|
|
96
106
|
inboundMessageId: prompt.messageId,
|
|
97
107
|
contextId: prompt.contextId,
|
|
98
108
|
sessionKey: prompt.sessionKey,
|
|
109
|
+
files: prompt.files.length,
|
|
99
110
|
}, "message-bus: turn started");
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
try {
|
|
112
|
+
const result = await opts.sessionManager.promptForSession(sid, promptText);
|
|
113
|
+
opts.logger.info({
|
|
114
|
+
sid,
|
|
115
|
+
channelId: prompt.channelId,
|
|
116
|
+
inboundMessageId: prompt.messageId,
|
|
117
|
+
stopReason: result.stopReason,
|
|
118
|
+
}, "message-bus: turn finished");
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
122
|
+
opts.logger.warn({
|
|
123
|
+
sid,
|
|
124
|
+
channelId: prompt.channelId,
|
|
125
|
+
inboundMessageId: prompt.messageId,
|
|
126
|
+
err: message,
|
|
127
|
+
}, "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
|
+
if (message === "agent_busy") {
|
|
132
|
+
await opts.busyReply.replyBusy({
|
|
133
|
+
channelId: prompt.channelId,
|
|
134
|
+
inboundMessageId: prompt.messageId,
|
|
135
|
+
sessionKey: prompt.sessionKey,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
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
|
+
try {
|
|
144
|
+
await opts.sdk.sendChannelMessage(prompt.channelId, {
|
|
145
|
+
type: "agent_reply",
|
|
146
|
+
payload: {
|
|
147
|
+
session_key: prompt.sessionKey,
|
|
148
|
+
error: message,
|
|
149
|
+
text: notifier.currentText,
|
|
150
|
+
},
|
|
151
|
+
inReplyTo: prompt.messageId,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (publishErr) {
|
|
155
|
+
opts.logger.error({
|
|
156
|
+
err: publishErr instanceof Error
|
|
157
|
+
? publishErr.message
|
|
158
|
+
: String(publishErr),
|
|
159
|
+
channelId: prompt.channelId,
|
|
160
|
+
}, "message-bus: failed to publish fallback agent_reply");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
opts.sessionManager.unbindNotifier(sid);
|
|
166
|
+
opts.tracker.clear(prompt.messageId);
|
|
167
|
+
}
|
|
105
168
|
};
|
|
106
169
|
}
|
|
107
170
|
/**
|
|
@@ -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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"}
|
|
@@ -94,8 +94,7 @@ export declare class MessageBusPlugin implements AgentPlugin {
|
|
|
94
94
|
readonly name = "message-bus";
|
|
95
95
|
private ctx?;
|
|
96
96
|
private sdk;
|
|
97
|
-
private
|
|
98
|
-
private unsubscribeOutbound;
|
|
97
|
+
private tracker;
|
|
99
98
|
private resolvedPrincipalId;
|
|
100
99
|
private stopped;
|
|
101
100
|
private retryHandle;
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
import { MessageSDK } from "@beeos-ai/message-sdk";
|
|
32
32
|
import { createDeviceMessagingTokenProvider } from "../../messaging/token-provider.js";
|
|
33
33
|
import { MessageSdkBusyReplyHandler } from "./busy-reply.js";
|
|
34
|
+
import { buildMessageBusCancelHandler, MESSAGE_TYPE_CHAT_CANCEL, } from "./cancel.js";
|
|
34
35
|
import { buildMessageBusInboundHandler } from "./inbound.js";
|
|
35
|
-
import {
|
|
36
|
-
import { TurnContextRegister } from "./turn-context.js";
|
|
36
|
+
import { MsInflightTracker } from "./ms-inflight-tracker.js";
|
|
37
37
|
/**
|
|
38
38
|
* Backoff schedule for token / connect failures (milliseconds). Each
|
|
39
39
|
* entry is the delay BEFORE the next retry. The last entry caps the
|
|
@@ -57,8 +57,7 @@ export class MessageBusPlugin {
|
|
|
57
57
|
name = "message-bus";
|
|
58
58
|
ctx;
|
|
59
59
|
sdk = null;
|
|
60
|
-
|
|
61
|
-
unsubscribeOutbound = null;
|
|
60
|
+
tracker = null;
|
|
62
61
|
resolvedPrincipalId = "";
|
|
63
62
|
// Connect-loop state.
|
|
64
63
|
stopped = false;
|
|
@@ -117,15 +116,6 @@ export class MessageBusPlugin {
|
|
|
117
116
|
// Resolve any outstanding waiter so test code that awaits
|
|
118
117
|
// `whenSettled()` does not hang past stop().
|
|
119
118
|
this.resolveConnectSettled();
|
|
120
|
-
if (this.unsubscribeOutbound) {
|
|
121
|
-
try {
|
|
122
|
-
this.unsubscribeOutbound();
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
/* ignore */
|
|
126
|
-
}
|
|
127
|
-
this.unsubscribeOutbound = null;
|
|
128
|
-
}
|
|
129
119
|
if (this.sdk) {
|
|
130
120
|
try {
|
|
131
121
|
await this.sdk.disconnect();
|
|
@@ -135,9 +125,9 @@ export class MessageBusPlugin {
|
|
|
135
125
|
}
|
|
136
126
|
this.sdk = null;
|
|
137
127
|
}
|
|
138
|
-
if (this.
|
|
139
|
-
this.
|
|
140
|
-
this.
|
|
128
|
+
if (this.tracker) {
|
|
129
|
+
this.tracker.reset();
|
|
130
|
+
this.tracker = null;
|
|
141
131
|
}
|
|
142
132
|
}
|
|
143
133
|
// Test seam — exposes the resolved principal so integration tests can
|
|
@@ -199,8 +189,12 @@ export class MessageBusPlugin {
|
|
|
199
189
|
createDeviceMessagingTokenProvider({ agentGatewayUrl, keypair });
|
|
200
190
|
const sdk = this.opts.sdkFactory?.(log) ??
|
|
201
191
|
new MessageSDK({
|
|
202
|
-
//
|
|
203
|
-
//
|
|
192
|
+
// serviceUrl is discovered post-token (tok.service_url) and
|
|
193
|
+
// late-bound via setServiceUrl below. Once bound, the SDK
|
|
194
|
+
// forwards the Centrifugo token as Authorization: Bearer
|
|
195
|
+
// for principal-level REST auth — agent never sees the MS
|
|
196
|
+
// X-API-Key. This is the durable publish path used by
|
|
197
|
+
// sendChannelMessage for terminal agent_reply.
|
|
204
198
|
centrifugoUrl: ctx.config.messageBus.centrifugoUrl || undefined,
|
|
205
199
|
}, wrapLoggerForSdk(log));
|
|
206
200
|
let tok;
|
|
@@ -216,20 +210,31 @@ export class MessageBusPlugin {
|
|
|
216
210
|
this.logAttemptFailure(attempt, "gateway returned empty principal_id", null, log);
|
|
217
211
|
return false;
|
|
218
212
|
}
|
|
219
|
-
|
|
213
|
+
// Late-bind MS REST URL discovered from the token response so
|
|
214
|
+
// sdk.sendChannelMessage (used by MsChannelNotifier.publishFinal
|
|
215
|
+
// for durable terminal replies) has somewhere to POST. When
|
|
216
|
+
// service_url is empty (legacy gateway not exposing it yet), the
|
|
217
|
+
// SDK keeps `serviceUrl: undefined` and any sendChannelMessage call
|
|
218
|
+
// throws — that's the explicit failure mode preferred over silently
|
|
219
|
+
// falling back to lossy Centrifugo direct publish.
|
|
220
|
+
sdk.setServiceUrl(tok.service_url);
|
|
221
|
+
const tracker = new MsInflightTracker();
|
|
220
222
|
const busyReply = new MessageSdkBusyReplyHandler({ sdk });
|
|
223
|
+
const sessionManager = ctx.sessionManager;
|
|
221
224
|
const chatHandler = buildMessageBusInboundHandler({
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
sessionManager,
|
|
226
|
+
sdk,
|
|
224
227
|
busyReply,
|
|
228
|
+
tracker,
|
|
225
229
|
logger: log,
|
|
226
230
|
localAgentId: this.opts.localAgentId,
|
|
227
231
|
source: "chat_message",
|
|
228
232
|
});
|
|
229
233
|
const legacyHandler = buildMessageBusInboundHandler({
|
|
230
|
-
|
|
231
|
-
|
|
234
|
+
sessionManager,
|
|
235
|
+
sdk,
|
|
232
236
|
busyReply,
|
|
237
|
+
tracker,
|
|
233
238
|
logger: log,
|
|
234
239
|
localAgentId: this.opts.localAgentId,
|
|
235
240
|
source: "agent_request",
|
|
@@ -245,12 +250,17 @@ export class MessageBusPlugin {
|
|
|
245
250
|
}
|
|
246
251
|
await legacyHandler(env);
|
|
247
252
|
});
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
253
|
+
// Cooperative cancel: the platform pushes `chat_cancel` envelopes
|
|
254
|
+
// when the caller hangs up / explicitly cancels / times out. We
|
|
255
|
+
// address the in-flight turn IM-natively by `target_message_id`
|
|
256
|
+
// (== inbound chat_message.message_id) so the agent stays free of
|
|
257
|
+
// A2A / MCP / OpenAPI protocol knowledge.
|
|
258
|
+
const cancelHandler = buildMessageBusCancelHandler({
|
|
259
|
+
sessionManager,
|
|
260
|
+
tracker,
|
|
252
261
|
logger: log,
|
|
253
262
|
});
|
|
263
|
+
sdk.onMessage(MESSAGE_TYPE_CHAT_CANCEL, async (env) => cancelHandler(env));
|
|
254
264
|
sdk.onceConnected(() => {
|
|
255
265
|
log.info({
|
|
256
266
|
principalId,
|
|
@@ -266,14 +276,7 @@ export class MessageBusPlugin {
|
|
|
266
276
|
});
|
|
267
277
|
}
|
|
268
278
|
catch (err) {
|
|
269
|
-
//
|
|
270
|
-
// next retry, and disconnect the half-opened SDK.
|
|
271
|
-
try {
|
|
272
|
-
unsubscribe();
|
|
273
|
-
}
|
|
274
|
-
catch {
|
|
275
|
-
/* ignore */
|
|
276
|
-
}
|
|
279
|
+
// Disconnect the half-opened SDK so the next retry starts clean.
|
|
277
280
|
try {
|
|
278
281
|
await sdk.disconnect();
|
|
279
282
|
}
|
|
@@ -284,8 +287,7 @@ export class MessageBusPlugin {
|
|
|
284
287
|
return false;
|
|
285
288
|
}
|
|
286
289
|
this.sdk = sdk;
|
|
287
|
-
this.
|
|
288
|
-
this.unsubscribeOutbound = unsubscribe;
|
|
290
|
+
this.tracker = tracker;
|
|
289
291
|
this.resolvedPrincipalId = principalId;
|
|
290
292
|
return true;
|
|
291
293
|
}
|