@beeos-ai/device-agent 0.7.2 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/app/fixed-session-id.d.ts +19 -0
  2. package/dist/app/fixed-session-id.js +26 -0
  3. package/dist/app/fixed-session-id.js.map +1 -0
  4. package/dist/app/jsonl-session-sweeper.d.ts +90 -0
  5. package/dist/app/jsonl-session-sweeper.js +162 -0
  6. package/dist/app/jsonl-session-sweeper.js.map +1 -0
  7. package/dist/app/jsonl-store.d.ts +10 -0
  8. package/dist/app/jsonl-store.js +10 -4
  9. package/dist/app/jsonl-store.js.map +1 -1
  10. package/dist/app/session-manager.d.ts +135 -1
  11. package/dist/app/session-manager.js +195 -12
  12. package/dist/app/session-manager.js.map +1 -1
  13. package/dist/bootstrap.js +157 -2
  14. package/dist/bootstrap.js.map +1 -1
  15. package/dist/config/loader.d.ts +4 -0
  16. package/dist/config/loader.js +20 -2
  17. package/dist/config/loader.js.map +1 -1
  18. package/dist/config/schema.d.ts +7 -0
  19. package/dist/config/schema.js +82 -0
  20. package/dist/config/schema.js.map +1 -1
  21. package/dist/fleet/domain/device.d.ts +17 -0
  22. package/dist/fleet/domain/device.js +1 -0
  23. package/dist/fleet/domain/device.js.map +1 -1
  24. package/dist/fleet/domain/fsm.d.ts +30 -1
  25. package/dist/fleet/domain/fsm.js +100 -4
  26. package/dist/fleet/domain/fsm.js.map +1 -1
  27. package/dist/fleet/supervisor.js +38 -18
  28. package/dist/fleet/supervisor.js.map +1 -1
  29. package/dist/inbound/cli/main.js +12 -0
  30. package/dist/inbound/cli/main.js.map +1 -1
  31. package/dist/messaging/token-provider.d.ts +43 -0
  32. package/dist/messaging/token-provider.js +65 -0
  33. package/dist/messaging/token-provider.js.map +1 -0
  34. package/dist/plugins/acp/index.d.ts +27 -30
  35. package/dist/plugins/acp/index.js +44 -137
  36. package/dist/plugins/acp/index.js.map +1 -1
  37. package/dist/plugins/message-bus/busy-reply.d.ts +39 -0
  38. package/dist/plugins/message-bus/busy-reply.js +30 -0
  39. package/dist/plugins/message-bus/busy-reply.js.map +1 -0
  40. package/dist/plugins/message-bus/cancel.d.ts +48 -0
  41. package/dist/plugins/message-bus/cancel.js +66 -0
  42. package/dist/plugins/message-bus/cancel.js.map +1 -0
  43. package/dist/plugins/message-bus/inbound.d.ts +71 -0
  44. package/dist/plugins/message-bus/inbound.js +180 -0
  45. package/dist/plugins/message-bus/inbound.js.map +1 -0
  46. package/dist/plugins/message-bus/index.d.ts +146 -0
  47. package/dist/plugins/message-bus/index.js +382 -0
  48. package/dist/plugins/message-bus/index.js.map +1 -0
  49. package/dist/plugins/message-bus/ms-channel-notifier.d.ts +101 -0
  50. package/dist/plugins/message-bus/ms-channel-notifier.js +181 -0
  51. package/dist/plugins/message-bus/ms-channel-notifier.js.map +1 -0
  52. package/dist/plugins/message-bus/ms-inflight-tracker.d.ts +36 -0
  53. package/dist/plugins/message-bus/ms-inflight-tracker.js +42 -0
  54. package/dist/plugins/message-bus/ms-inflight-tracker.js.map +1 -0
  55. package/dist/ports/plugin.d.ts +12 -0
  56. package/package.json +2 -1
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Inbound — `chat_message` / `agent_request` envelope handler.
3
+ *
4
+ * v0.9.0 — wires `MessageSDK.onMessage(...)` directly to
5
+ * `SessionManager.promptForSession(sid, prompt)`. Per-channel session
6
+ * isolation:
7
+ *
8
+ * 1. Extract `{ message, channelId, messageId, contextId, sessionKey,
9
+ * files }` from the payload via `extractChatPrompt` (returns null
10
+ * on malformed envelopes — silently dropped).
11
+ * 2. Reject if no `channelId` or no `messageId` — replies must round
12
+ * trip through `ch:{channelId}` with `in_reply_to=messageId`, so
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 = ""`.
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
+ */
41
+ import { extractChatPrompt, } from "@beeos-ai/message-sdk";
42
+ import { MsChannelNotifier } from "./ms-channel-notifier.js";
43
+ /**
44
+ * Build the per-envelope handler. The returned function is what gets
45
+ * registered with `sdk.onMessage(type, handler)` — keeping the factory
46
+ * shape lets unit tests instantiate the closure with mock collaborators
47
+ * and feed envelopes synchronously.
48
+ */
49
+ export function buildMessageBusInboundHandler(opts) {
50
+ return async (envelope) => {
51
+ const prompt = extractChatPrompt(envelope, {
52
+ source: opts.source,
53
+ localAgentId: opts.localAgentId ?? "",
54
+ });
55
+ if (!prompt) {
56
+ opts.logger.warn({ type: envelope.type, message_id: envelope.message_id }, "message-bus: dropped envelope (no prompt could be extracted)");
57
+ return;
58
+ }
59
+ if (!prompt.channelId || !prompt.messageId) {
60
+ opts.logger.warn({
61
+ type: envelope.type,
62
+ hasChannelId: !!prompt.channelId,
63
+ hasMessageId: !!prompt.messageId,
64
+ }, "message-bus: dropped envelope (missing channelId or messageId — cannot correlate reply)");
65
+ return;
66
+ }
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()) {
72
+ opts.logger.warn({
73
+ incomingMessageId: prompt.messageId,
74
+ incomingChannelId: prompt.channelId,
75
+ }, "message-bus: agent busy — sending agent_busy reply");
76
+ await opts.busyReply.replyBusy({
77
+ channelId: prompt.channelId,
78
+ inboundMessageId: prompt.messageId,
79
+ sessionKey: prompt.sessionKey,
80
+ });
81
+ return;
82
+ }
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,
91
+ channelId: prompt.channelId,
92
+ inboundMessageId: prompt.messageId,
93
+ sessionKey: prompt.sessionKey,
94
+ logger: opts.logger,
95
+ });
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);
103
+ opts.logger.info({
104
+ sid,
105
+ channelId: prompt.channelId,
106
+ inboundMessageId: prompt.messageId,
107
+ contextId: prompt.contextId,
108
+ sessionKey: prompt.sessionKey,
109
+ files: prompt.files.length,
110
+ }, "message-bus: turn started");
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.
141
+ try {
142
+ await opts.sdk.sendToChannel(prompt.channelId, "agent_reply", {
143
+ session_key: prompt.sessionKey,
144
+ error: message,
145
+ text: notifier.currentText,
146
+ }, { inReplyTo: prompt.messageId });
147
+ }
148
+ catch (publishErr) {
149
+ opts.logger.error({
150
+ err: publishErr instanceof Error
151
+ ? publishErr.message
152
+ : String(publishErr),
153
+ channelId: prompt.channelId,
154
+ }, "message-bus: failed to publish fallback agent_reply");
155
+ }
156
+ }
157
+ }
158
+ finally {
159
+ opts.sessionManager.unbindNotifier(sid);
160
+ opts.tracker.clear(prompt.messageId);
161
+ }
162
+ };
163
+ }
164
+ /**
165
+ * Append a markdown-style `[attachments]` block listing file URLs to
166
+ * the prompt text. KISS: today the device-agent agent-core only takes
167
+ * a single string prompt, so the only way to surface attachments is
168
+ * inline. Empty `files` returns the message unchanged.
169
+ *
170
+ * Deeper integration (downloading via the resource-resolution layer,
171
+ * uploading to the device sandbox) is intentionally out of scope —
172
+ * see plan §E.
173
+ */
174
+ function appendAttachments(message, files) {
175
+ if (!files || files.length === 0)
176
+ return message;
177
+ const list = files.map((u) => `- ${u}`).join("\n");
178
+ return `${message}\n\n[attachments]\n${list}`;
179
+ }
180
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
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, type TokenProvider } from "@beeos-ai/message-sdk";
32
+ import type { Logger } from "../../logger/index.js";
33
+ import type { AgentPlugin, PluginContext } from "../../ports/plugin.js";
34
+ /**
35
+ * Backoff schedule for token / connect failures (milliseconds). Each
36
+ * entry is the delay BEFORE the next retry. The last entry caps the
37
+ * delay; once we hit the cap we keep retrying forever at that interval.
38
+ *
39
+ * Tunable via `MessageBusPluginOptions.retrySchedule` — tests pin a
40
+ * tighter schedule (e.g. `[5, 5, 5]`) to keep `vi.useFakeTimers()`
41
+ * runs cheap.
42
+ */
43
+ export declare const DEFAULT_RETRY_SCHEDULE_MS: readonly number[];
44
+ export interface MessageBusPluginOptions {
45
+ /**
46
+ * Ed25519 keypair used to sign `POST /api/v1/messaging/token`. When
47
+ * absent the plugin self-disables.
48
+ */
49
+ keypair?: {
50
+ publicKey: Uint8Array;
51
+ privateKey: Uint8Array;
52
+ };
53
+ /**
54
+ * Override the SDK constructor — used by tests to inject a fake
55
+ * MessageSDK whose `connect` / `onMessage` / `sendToChannel` are
56
+ * spies. Production code leaves this `undefined`.
57
+ */
58
+ sdkFactory?: (logger: Logger) => MessageSDK;
59
+ /**
60
+ * Override the token provider builder — used by tests to skip the
61
+ * real Agent Gateway round-trip.
62
+ */
63
+ tokenProviderFactory?: (opts: {
64
+ agentGatewayUrl: string;
65
+ keypair: {
66
+ publicKey: Uint8Array;
67
+ privateKey: Uint8Array;
68
+ };
69
+ }) => TokenProvider;
70
+ /**
71
+ * Local agent identity surfaced into `extractChatPrompt` for
72
+ * sessionKey prefixing. Defaults to the empty string — fine for the
73
+ * single-session-multiplex topology.
74
+ */
75
+ localAgentId?: string;
76
+ /**
77
+ * Override the retry backoff schedule (ms). Defaults to
78
+ * `DEFAULT_RETRY_SCHEDULE_MS`. Tests pass small numbers; production
79
+ * leaves it `undefined`.
80
+ */
81
+ retrySchedule?: readonly number[];
82
+ /**
83
+ * Test seam — `setTimeout` / `clearTimeout` shim. Defaults to
84
+ * the global `setTimeout` family. Tests inject a deterministic
85
+ * scheduler so they don't need real timers.
86
+ */
87
+ scheduler?: {
88
+ setTimeout: (cb: () => void, ms: number) => unknown;
89
+ clearTimeout: (handle: unknown) => void;
90
+ };
91
+ }
92
+ export declare class MessageBusPlugin implements AgentPlugin {
93
+ private readonly opts;
94
+ readonly name = "message-bus";
95
+ private ctx?;
96
+ private sdk;
97
+ private tracker;
98
+ private resolvedPrincipalId;
99
+ private stopped;
100
+ private retryHandle;
101
+ private retryResolve;
102
+ private connectAttempt;
103
+ private legacyDeprecationLogged;
104
+ private connectSettled;
105
+ private resolveConnectSettled;
106
+ constructor(opts?: MessageBusPluginOptions);
107
+ init(ctx: PluginContext): Promise<void>;
108
+ start(): Promise<void>;
109
+ stop(): Promise<void>;
110
+ 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
+ whenSettled(): Promise<void>;
117
+ private runConnectLoop;
118
+ /**
119
+ * Single connect attempt. Returns `true` on success (sdk connected,
120
+ * handlers + listener installed) and `false` on any failure (caller
121
+ * should schedule a retry).
122
+ *
123
+ * Each attempt builds a FRESH `MessageSDK` instance — re-using one
124
+ * across reconnects would carry stale handlers and confuse the
125
+ * centrifuge-js internal state. The SDK has its own internal
126
+ * reconnect once `.connect()` succeeds; this loop is for the OPENING
127
+ * handshake only.
128
+ */
129
+ private tryConnectOnce;
130
+ 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
+ private logAttemptFailure;
137
+ 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
+ private sleep;
144
+ private cancelPendingRetry;
145
+ private scheduler;
146
+ }