@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 @@
1
+ {"version":3,"file":"token-provider.js","sourceRoot":"","sources":["../../src/messaging/token-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,OAAO,EACL,4BAA4B,GAG7B,MAAM,uBAAuB,CAAC;AAI/B,sEAAsE;AACtE,kEAAkE;AAClE,uDAAuD;AACvD,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CACtC,kCAAkC,EAClC,KAAK,CACN,CAAC;AAUF;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAgB;IAEhB,OAAO;QACL,eAAe,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACxE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YACd,MAAM,OAAO,GACX,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,EAAE;gBAC9B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;oBACZ,oBAAoB;oBACpB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;iBAChC,CAAC;gBACJ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACtC,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC;gBACrC,GAAG,EAAE,OAAO;gBACZ,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;YACH,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACvE,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kCAAkC,CAChD,IAAyC;IAEzC,OAAO,4BAA4B,CAAC;QAClC,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,QAAQ,EAAE,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC;QAClD,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;AACL,CAAC"}
@@ -4,25 +4,31 @@
4
4
  * Bridges the device-agent's `AgentHost` to the BeeOS platform via the
5
5
  * Agent Control Protocol (ACP) over a WebSocket transport.
6
6
  *
7
- * Responsibilities:
8
- * - Construct `SessionManager` (subscribes to host events) in
9
- * fixed-sid single-session mode, with a JSONL-backed store
10
- * persisting transcripts to
11
- * `${BEEOS_HOME ?? ~/.beeos}/device-agent/sessions/<sid>.jsonl`.
12
- * - Wire ACP handlers (`session/*`, `device/*`, `models/list`, `skills/list`).
13
- * - Open the Bridge WebSocket, bind the router, fan `session/update`
14
- * notifications back through the transport.
7
+ * Responsibilities (v0.9.0):
8
+ * - Open the Bridge WebSocket transport.
9
+ * - Build the per-transport `TransportSessionNotifier` and BIND it
10
+ * to the operator's fixed sid via
11
+ * `ctx.sessionManager.bindNotifier(fixedSessionId, notifier)`. The
12
+ * SessionManager itself, JsonlMessageStore, JsonlSessionSweeper,
13
+ * and PluginCommandRegistry are all owned by `bootstrap.ts` —
14
+ * plugin pulls them via `PluginContext` so MessageBusPlugin can
15
+ * share the SAME instance (the in-flight gate / Layer 3 device-
16
+ * physical-serial constraint depends on a single SessionManager
17
+ * per process).
18
+ * - Wire ACP handlers (`session/*`, `device/*`, `models/list`,
19
+ * `skills/list`) against `ctx.sessionManager`.
20
+ * - Bind the router and fan `session/update` notifications back
21
+ * through the transport for the fixed sid.
15
22
  *
16
- * Single-session sid derivation:
17
- *
18
- * The fixed sid is `device:<first16HexOfSha256(publicKey)>` — a
19
- * deterministic per-device identifier that survives reconnects and
20
- * binary upgrades. Standalone mode (no keypair) falls back to
21
- * `device:standalone` so local CLI testing still gets a stable sid.
23
+ * Single-session sid derivation lives in `bootstrap.ts`
24
+ * (`deriveFixedSessionId`) — the format is
25
+ * `device:<first16HexOfSha256(publicKey)>` (or `device:standalone`),
26
+ * unchanged from v0.8.0.
22
27
  *
23
28
  * Disabled when `--standalone` (no remote control plane) or `--no-acp`
24
29
  * is passed; in that mode only the http-debug plugin is useful.
25
30
  */
31
+ import { deriveFixedSessionId } from "../../app/fixed-session-id.js";
26
32
  import type { IMcpRuntime } from "../../mcp-client/runtime.js";
27
33
  import type { IBridgeTransport } from "../../ports/transport.js";
28
34
  import type { AgentPlugin, PluginContext } from "../../ports/plugin.js";
@@ -39,33 +45,24 @@ export interface AcpPluginOptions {
39
45
  publicKey: Uint8Array;
40
46
  privateKey: Uint8Array;
41
47
  };
42
- /**
43
- * Agent Gateway base URL. Required for the `/screenshot` plugin
44
- * command to upload frames via presign+confirm. When unset, the
45
- * command is NOT registered (the device-agent gracefully degrades —
46
- * `skills/list` returns an empty list and `/screenshot` falls
47
- * through to the AgentRunner).
48
- */
49
- agentGatewayUrl?: string;
50
48
  /** Optional pre-built transport (used by tests for mock injection). */
51
49
  transportOverride?: IBridgeTransport;
52
- /**
53
- * Override the sessions directory. Tests pin this to a tmp dir; in
54
- * production the default `${BEEOS_HOME ?? ~/.beeos}/device-agent/sessions`
55
- * resolves via `getDeviceAgentSessionsDir()`.
56
- */
57
- sessionsDirOverride?: string;
58
50
  }
59
51
  export declare class AcpPlugin implements AgentPlugin {
60
52
  private readonly opts;
61
53
  readonly name = "acp";
62
54
  private ctx?;
63
55
  private transport;
64
- private sessions;
65
- private store;
56
+ private boundSid;
66
57
  private unbind;
67
58
  constructor(opts: AcpPluginOptions);
68
59
  init(ctx: PluginContext): Promise<void>;
69
60
  start(): Promise<void>;
61
+ /**
62
+ * Visible for tests — exposes the sid this plugin binds to so test
63
+ * harnesses can construct a SessionManager with the matching
64
+ * `fixedSessionId` without re-deriving the value.
65
+ */
66
+ static deriveFixedSessionId: typeof deriveFixedSessionId;
70
67
  stop(): Promise<void>;
71
68
  }
@@ -4,34 +4,32 @@
4
4
  * Bridges the device-agent's `AgentHost` to the BeeOS platform via the
5
5
  * Agent Control Protocol (ACP) over a WebSocket transport.
6
6
  *
7
- * Responsibilities:
8
- * - Construct `SessionManager` (subscribes to host events) in
9
- * fixed-sid single-session mode, with a JSONL-backed store
10
- * persisting transcripts to
11
- * `${BEEOS_HOME ?? ~/.beeos}/device-agent/sessions/<sid>.jsonl`.
12
- * - Wire ACP handlers (`session/*`, `device/*`, `models/list`, `skills/list`).
13
- * - Open the Bridge WebSocket, bind the router, fan `session/update`
14
- * notifications back through the transport.
7
+ * Responsibilities (v0.9.0):
8
+ * - Open the Bridge WebSocket transport.
9
+ * - Build the per-transport `TransportSessionNotifier` and BIND it
10
+ * to the operator's fixed sid via
11
+ * `ctx.sessionManager.bindNotifier(fixedSessionId, notifier)`. The
12
+ * SessionManager itself, JsonlMessageStore, JsonlSessionSweeper,
13
+ * and PluginCommandRegistry are all owned by `bootstrap.ts` —
14
+ * plugin pulls them via `PluginContext` so MessageBusPlugin can
15
+ * share the SAME instance (the in-flight gate / Layer 3 device-
16
+ * physical-serial constraint depends on a single SessionManager
17
+ * per process).
18
+ * - Wire ACP handlers (`session/*`, `device/*`, `models/list`,
19
+ * `skills/list`) against `ctx.sessionManager`.
20
+ * - Bind the router and fan `session/update` notifications back
21
+ * through the transport for the fixed sid.
15
22
  *
16
- * Single-session sid derivation:
17
- *
18
- * The fixed sid is `device:<first16HexOfSha256(publicKey)>` — a
19
- * deterministic per-device identifier that survives reconnects and
20
- * binary upgrades. Standalone mode (no keypair) falls back to
21
- * `device:standalone` so local CLI testing still gets a stable sid.
23
+ * Single-session sid derivation lives in `bootstrap.ts`
24
+ * (`deriveFixedSessionId`) — the format is
25
+ * `device:<first16HexOfSha256(publicKey)>` (or `device:standalone`),
26
+ * unchanged from v0.8.0.
22
27
  *
23
28
  * Disabled when `--standalone` (no remote control plane) or `--no-acp`
24
29
  * is passed; in that mode only the http-debug plugin is useful.
25
30
  */
26
- import { fingerprint as sha256Fingerprint } from "@beeos-ai/bridge-client";
27
31
  import { BridgeWsTransport } from "../../adapters/transport-bridge-ws.js";
28
- import { AgentGatewayUserFileUploader } from "../../adapters/user-file-uploader.js";
29
- import { JsonlMessageStore } from "../../app/jsonl-store.js";
30
- import { PluginCommandRegistry } from "../../app/plugin-commands/registry.js";
31
- import { ScreenshotCommandHandler } from "../../app/plugin-commands/screenshot-command.js";
32
- import { ScreenshotService } from "../../app/screenshot-service.js";
33
- import { SessionManager } from "../../app/session-manager.js";
34
- import { getDeviceAgentSessionsDir } from "../../config/paths.js";
32
+ import { deriveFixedSessionId } from "../../app/fixed-session-id.js";
35
33
  import { AcpDeviceHandlers } from "../../inbound/acp/handlers/device.js";
36
34
  import { AcpSessionHandlers } from "../../inbound/acp/handlers/session.js";
37
35
  import { AcpSkillsHandlers } from "../../inbound/acp/handlers/skills.js";
@@ -42,15 +40,14 @@ export class AcpPlugin {
42
40
  name = "acp";
43
41
  ctx;
44
42
  transport = null;
45
- sessions = null;
46
- store = null;
43
+ boundSid = null;
47
44
  unbind = null;
48
45
  constructor(opts) {
49
46
  this.opts = opts;
50
47
  }
51
48
  async init(ctx) {
52
49
  this.ctx = ctx;
53
- const { config, log, host } = ctx;
50
+ const { config, log, sessionManager } = ctx;
54
51
  if (config.disableAcp) {
55
52
  log.info("acp: disabled by --no-acp / disableAcp config");
56
53
  return;
@@ -69,93 +66,18 @@ export class AcpPlugin {
69
66
  transport.on("connected", () => log.info("acp: bridge connected"));
70
67
  transport.on("disconnected", (reason) => log.warn({ reason }, "acp: bridge disconnected"));
71
68
  transport.on("error", (err) => log.error({ err: err.message }, "acp: bridge transport error"));
69
+ // Bind the operator's fixed sid to the Bridge WS transport. MS
70
+ // sessions get their own per-channel notifiers via
71
+ // `MsChannelNotifier` (bound by MessageBusPlugin), so the two
72
+ // surfaces never publish onto each other's channels.
72
73
  const fixedSessionId = deriveFixedSessionId(this.opts.keypair.publicKey);
73
- const sessionsDir = this.opts.sessionsDirOverride ?? getDeviceAgentSessionsDir();
74
- const messageStore = await JsonlMessageStore.open({
75
- sessionsDir,
76
- preloadSids: [fixedSessionId],
77
- logger: { warn: (...args) => log.warn(...args) },
78
- });
79
- log.info({ fixedSessionId, sessionsDir }, "acp: jsonl store ready");
80
74
  const notifier = new TransportSessionNotifier(transport);
81
- // Build the plugin-command registry. Only commands whose dependencies
82
- // are satisfied get registered — this keeps `skills/list` honest in
83
- // degraded modes (e.g. local dev without an agent gateway URL still
84
- // boots, just without `/screenshot`).
85
- const pluginCommands = new PluginCommandRegistry();
86
- if (this.opts.agentGatewayUrl) {
87
- const uploader = new AgentGatewayUserFileUploader({
88
- agentGatewayUrl: this.opts.agentGatewayUrl,
89
- identity: {
90
- publicKey: this.opts.keypair.publicKey,
91
- privateKey: this.opts.keypair.privateKey,
92
- },
93
- });
94
- // ScreenshotService takes the live MCP runtime — bind to a single
95
- // resolved instance per plugin lifetime; on autospawn restart the
96
- // outer mcpProvider issues a fresh runtime and the next call will
97
- // observe it transparently (the service captures the runtime at
98
- // each `captureAndUpload` call, NOT at construction).
99
- const lazyMcp = {
100
- screenshot: () => this.opts.mcpProvider().screenshot(),
101
- uiDump: (o) => this.opts.mcpProvider().uiDump(o),
102
- act: (a) => this.opts.mcpProvider().act(a),
103
- };
104
- const screenshotService = new ScreenshotService(lazyMcp, uploader);
105
- pluginCommands.register(new ScreenshotCommandHandler(screenshotService));
106
- log.info({ commands: pluginCommands.list().map((c) => c.name) }, "acp: plugin commands registered");
107
- }
108
- else {
109
- log.info("acp: agentGatewayUrl unset — skipping `/screenshot` and other upload-dependent plugin commands");
110
- }
111
- const sessions = new SessionManager({
112
- host,
113
- messageStore,
114
- notifier,
115
- fixedSessionId,
116
- pluginCommands,
117
- // `Config.sessionTimeoutMs` is the single source of truth for idle GC.
118
- // 0 disables the threshold check (effectively keeps sessions forever
119
- // until `maxSessions` LRU eviction kicks in) — it's surfaced in the
120
- // schema so kiosk deployments can pin a deterministic value.
121
- sessionTtlMs: config.sessionTimeoutMs > 0 ? config.sessionTimeoutMs : undefined,
122
- onSetModel: async (provider, model) => {
123
- try {
124
- await host.agent.setModel(provider, model);
125
- log.info({ provider, model }, "acp: agent model switched");
126
- }
127
- catch (e) {
128
- log.warn({ err: e.message, provider, model }, "acp: setModel failed");
129
- }
130
- },
131
- // Surface plugin command lifecycle into the agent's pino log so
132
- // failures are auditable. Without this hook the only signal a failed
133
- // `/screenshot` produces is a `system_message_chunk` notice, which
134
- // some clients render and some swallow — leaving operators staring
135
- // at silent JSONL.
136
- onPluginCommandEvent: (ev) => {
137
- if (ev.kind === "begin") {
138
- log.info({ command: ev.name, sessionId: ev.sessionId, turnId: ev.turnId, args: ev.args }, "acp: plugin command begin");
139
- }
140
- else if (ev.kind === "end") {
141
- log.info({ command: ev.name, sessionId: ev.sessionId, turnId: ev.turnId, durationMs: ev.durationMs }, "acp: plugin command end");
142
- }
143
- else {
144
- log.warn({
145
- command: ev.name,
146
- sessionId: ev.sessionId,
147
- turnId: ev.turnId,
148
- durationMs: ev.durationMs,
149
- err: ev.error.message,
150
- stack: ev.error.stack,
151
- }, "acp: plugin command failed");
152
- }
153
- },
154
- });
75
+ sessionManager.bindNotifier(fixedSessionId, notifier);
76
+ this.boundSid = fixedSessionId;
155
77
  const router = new AcpRouter({
156
78
  device: new AcpDeviceHandlers(this.opts.mcpProvider),
157
- session: new AcpSessionHandlers(sessions),
158
- skills: new AcpSkillsHandlers(pluginCommands),
79
+ session: new AcpSessionHandlers(sessionManager),
80
+ skills: new AcpSkillsHandlers(sessionManager.pluginCommandRegistry),
159
81
  log: ({ method, error }) => {
160
82
  if (error)
161
83
  log.warn({ method, err: error.message }, "acp handler error");
@@ -164,8 +86,6 @@ export class AcpPlugin {
164
86
  },
165
87
  });
166
88
  this.transport = transport;
167
- this.sessions = sessions;
168
- this.store = messageStore;
169
89
  this.unbind = bindRouterToTransport(router, transport);
170
90
  }
171
91
  async start() {
@@ -173,45 +93,32 @@ export class AcpPlugin {
173
93
  return;
174
94
  await this.transport.connect();
175
95
  }
96
+ /**
97
+ * Visible for tests — exposes the sid this plugin binds to so test
98
+ * harnesses can construct a SessionManager with the matching
99
+ * `fixedSessionId` without re-deriving the value.
100
+ */
101
+ static deriveFixedSessionId = deriveFixedSessionId;
176
102
  async stop() {
177
- if (this.sessions) {
178
- this.sessions.detach();
179
- this.sessions = null;
180
- }
181
- if (this.unbind) {
103
+ if (this.boundSid && this.ctx) {
182
104
  try {
183
- await this.unbind();
105
+ this.ctx.sessionManager.unbindNotifier(this.boundSid);
184
106
  }
185
107
  catch {
186
108
  /* ignore */
187
109
  }
188
- this.unbind = null;
110
+ this.boundSid = null;
189
111
  }
190
- if (this.store) {
112
+ if (this.unbind) {
191
113
  try {
192
- await this.store.close();
114
+ await this.unbind();
193
115
  }
194
- catch (e) {
195
- // Best-effort flush — log but don't block shutdown on a stale stream.
196
- this.ctx?.log.warn({ err: e.message }, "acp: jsonl store close failed");
116
+ catch {
117
+ /* ignore */
197
118
  }
198
- this.store = null;
119
+ this.unbind = null;
199
120
  }
200
121
  this.transport = null;
201
122
  }
202
123
  }
203
- /**
204
- * Compute the per-device fixed sid.
205
- *
206
- * Format: `device:<first16HexOfSha256(publicKey)>`. 16 hex chars give
207
- * 64 bits of entropy — far more than enough to avoid collisions
208
- * across the BeeOS device fleet, while keeping the sid short enough
209
- * to use as a filename / log correlation tag without truncation.
210
- */
211
- function deriveFixedSessionId(publicKey) {
212
- if (!publicKey || publicKey.length === 0)
213
- return "device:standalone";
214
- const hex = sha256Fingerprint(publicKey);
215
- return `device:${hex.slice(0, 16)}`;
216
- }
217
124
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/acp/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/acp/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Busy-reply path — emits a terminal `agent_reply` with `error: agent_busy`
3
+ * back to the caller's IM channel when a second `chat_message` arrives
4
+ * while a turn is already in flight.
5
+ *
6
+ * Mirrors the SessionManager's "one turn per session" contract that the
7
+ * ACP handlers enforce — concurrent prompts on the same session are
8
+ * rejected, not queued. The MS path keeps the same semantics so callers
9
+ * (L0 / A2A / MCP / OpenAPI) get a deterministic refusal instead of a
10
+ * `/wait` timeout.
11
+ *
12
+ * Type/payload alignment: uses the same `agent_reply` type that the
13
+ * outbound listener emits, with a `{ error }` body that matches
14
+ * `backend/pkg/chatinvoke/invoker.go::extractReplyFinal` (which reads
15
+ * `payload.error` first, falling back to `payload.text`).
16
+ */
17
+ import type { MessageSDK } from "@beeos-ai/message-sdk";
18
+ export interface BusyReplyRequest {
19
+ channelId: string;
20
+ inboundMessageId: string;
21
+ /**
22
+ * Session key derived from the rejected envelope (via
23
+ * `extractChatPrompt(envelope).sessionKey`). Always set so the busy
24
+ * reply payload has the same `{ session_key, error }` shape as a
25
+ * regular `agent_reply`.
26
+ */
27
+ sessionKey: string;
28
+ }
29
+ export interface BusyReplyHandler {
30
+ replyBusy(req: BusyReplyRequest): Promise<void>;
31
+ }
32
+ export interface BusyReplyHandlerOptions {
33
+ sdk: MessageSDK;
34
+ }
35
+ export declare class MessageSdkBusyReplyHandler implements BusyReplyHandler {
36
+ private readonly opts;
37
+ constructor(opts: BusyReplyHandlerOptions);
38
+ replyBusy(req: BusyReplyRequest): Promise<void>;
39
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Busy-reply path — emits a terminal `agent_reply` with `error: agent_busy`
3
+ * back to the caller's IM channel when a second `chat_message` arrives
4
+ * while a turn is already in flight.
5
+ *
6
+ * Mirrors the SessionManager's "one turn per session" contract that the
7
+ * ACP handlers enforce — concurrent prompts on the same session are
8
+ * rejected, not queued. The MS path keeps the same semantics so callers
9
+ * (L0 / A2A / MCP / OpenAPI) get a deterministic refusal instead of a
10
+ * `/wait` timeout.
11
+ *
12
+ * Type/payload alignment: uses the same `agent_reply` type that the
13
+ * outbound listener emits, with a `{ error }` body that matches
14
+ * `backend/pkg/chatinvoke/invoker.go::extractReplyFinal` (which reads
15
+ * `payload.error` first, falling back to `payload.text`).
16
+ */
17
+ export class MessageSdkBusyReplyHandler {
18
+ opts;
19
+ constructor(opts) {
20
+ this.opts = opts;
21
+ }
22
+ async replyBusy(req) {
23
+ await this.opts.sdk.sendToChannel(req.channelId, "agent_reply", {
24
+ session_key: req.sessionKey,
25
+ error: "agent_busy",
26
+ text: "device-agent is processing another request — please retry once it completes.",
27
+ }, { inReplyTo: req.inboundMessageId });
28
+ }
29
+ }
30
+ //# sourceMappingURL=busy-reply.js.map
@@ -0,0 +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,aAAa,CAC/B,GAAG,CAAC,SAAS,EACb,aAAa,EACb;YACE,WAAW,EAAE,GAAG,CAAC,UAAU;YAC3B,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE,8EAA8E;SACrF,EACD,EAAE,SAAS,EAAE,GAAG,CAAC,gBAAgB,EAAE,CACpC,CAAC;IACJ,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"}
@@ -0,0 +1,71 @@
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 { type MessageEnvelope, type MessageSDK } from "@beeos-ai/message-sdk";
42
+ import type { SessionManager } from "../../app/session-manager.js";
43
+ import type { Logger } from "../../logger/index.js";
44
+ import type { BusyReplyHandler } from "./busy-reply.js";
45
+ import type { MsInflightTracker } from "./ms-inflight-tracker.js";
46
+ export interface MessageBusInboundOptions {
47
+ sessionManager: SessionManager;
48
+ sdk: MessageSDK;
49
+ busyReply: BusyReplyHandler;
50
+ tracker: MsInflightTracker;
51
+ logger: Logger;
52
+ /**
53
+ * Local agent identity surfaced to `extractChatPrompt`. Used only for
54
+ * sessionKey prefixing; safe to leave empty in standalone-style
55
+ * tests.
56
+ */
57
+ localAgentId?: string;
58
+ /**
59
+ * Source tag passed to `extractChatPrompt`. Identifies which Centrifugo
60
+ * envelope type triggered the call — `chat_message` is the primary L0
61
+ * path, `agent_request` is the legacy A2A SendTask shim.
62
+ */
63
+ source: "chat_message" | "agent_request";
64
+ }
65
+ /**
66
+ * Build the per-envelope handler. The returned function is what gets
67
+ * registered with `sdk.onMessage(type, handler)` — keeping the factory
68
+ * shape lets unit tests instantiate the closure with mock collaborators
69
+ * and feed envelopes synchronously.
70
+ */
71
+ export declare function buildMessageBusInboundHandler(opts: MessageBusInboundOptions): (envelope: MessageEnvelope) => Promise<void>;