@beeos-ai/device-agent 0.8.0 → 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 (77) 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 +146 -2
  14. package/dist/bootstrap.js.map +1 -1
  15. package/dist/config/loader.js +2 -0
  16. package/dist/config/loader.js.map +1 -1
  17. package/dist/config/schema.d.ts +2 -0
  18. package/dist/config/schema.js +41 -1
  19. package/dist/config/schema.js.map +1 -1
  20. package/dist/fleet/domain/device.d.ts +17 -0
  21. package/dist/fleet/domain/device.js +1 -0
  22. package/dist/fleet/domain/device.js.map +1 -1
  23. package/dist/fleet/domain/fsm.d.ts +30 -1
  24. package/dist/fleet/domain/fsm.js +100 -4
  25. package/dist/fleet/domain/fsm.js.map +1 -1
  26. package/dist/fleet/supervisor.js +38 -18
  27. package/dist/fleet/supervisor.js.map +1 -1
  28. package/dist/plugins/acp/index.d.ts +27 -30
  29. package/dist/plugins/acp/index.js +44 -137
  30. package/dist/plugins/acp/index.js.map +1 -1
  31. package/dist/plugins/message-bus/cancel.d.ts +48 -0
  32. package/dist/plugins/message-bus/cancel.js +66 -0
  33. package/dist/plugins/message-bus/cancel.js.map +1 -0
  34. package/dist/plugins/message-bus/inbound.d.ts +38 -22
  35. package/dist/plugins/message-bus/inbound.js +107 -50
  36. package/dist/plugins/message-bus/inbound.js.map +1 -1
  37. package/dist/plugins/message-bus/index.d.ts +1 -2
  38. package/dist/plugins/message-bus/index.js +25 -35
  39. package/dist/plugins/message-bus/index.js.map +1 -1
  40. package/dist/plugins/message-bus/ms-channel-notifier.d.ts +101 -0
  41. package/dist/plugins/message-bus/ms-channel-notifier.js +181 -0
  42. package/dist/plugins/message-bus/ms-channel-notifier.js.map +1 -0
  43. package/dist/plugins/message-bus/ms-inflight-tracker.d.ts +36 -0
  44. package/dist/plugins/message-bus/ms-inflight-tracker.js +42 -0
  45. package/dist/plugins/message-bus/ms-inflight-tracker.js.map +1 -0
  46. package/dist/ports/plugin.d.ts +12 -0
  47. package/package.json +1 -1
  48. package/dist/agent-runtime/skills/sqlite-store.d.ts +0 -60
  49. package/dist/agent-runtime/skills/sqlite-store.js +0 -234
  50. package/dist/agent-runtime/skills/sqlite-store.js.map +0 -1
  51. package/dist/plugins/message-bus/outbound.d.ts +0 -43
  52. package/dist/plugins/message-bus/outbound.js +0 -149
  53. package/dist/plugins/message-bus/outbound.js.map +0 -1
  54. package/dist/plugins/message-bus/turn-context.d.ts +0 -57
  55. package/dist/plugins/message-bus/turn-context.js +0 -53
  56. package/dist/plugins/message-bus/turn-context.js.map +0 -1
  57. package/dist/test-lan-autoglm.d.ts +0 -7
  58. package/dist/test-lan-autoglm.js +0 -46
  59. package/dist/test-lan-autoglm.js.map +0 -1
  60. package/dist/test-mem-breakdown.d.ts +0 -1
  61. package/dist/test-mem-breakdown.js +0 -103
  62. package/dist/test-mem-breakdown.js.map +0 -1
  63. package/dist/test-mem.d.ts +0 -1
  64. package/dist/test-mem.js +0 -60
  65. package/dist/test-mem.js.map +0 -1
  66. package/dist/test-mobile-use.d.ts +0 -1
  67. package/dist/test-mobile-use.js +0 -18
  68. package/dist/test-mobile-use.js.map +0 -1
  69. package/dist/test-screenshot-loop.d.ts +0 -1
  70. package/dist/test-screenshot-loop.js +0 -43
  71. package/dist/test-screenshot-loop.js.map +0 -1
  72. package/dist/test-screenshot.d.ts +0 -1
  73. package/dist/test-screenshot.js +0 -10
  74. package/dist/test-screenshot.js.map +0 -1
  75. package/dist/test-ui-xml-dump.d.ts +0 -15
  76. package/dist/test-ui-xml-dump.js +0 -49
  77. package/dist/test-ui-xml-dump.js.map +0 -1
@@ -0,0 +1,101 @@
1
+ /**
2
+ * MsChannelNotifier — translate per-session ACP `session/update`
3
+ * envelopes into BeeOS Message Service wire envelopes for ONE
4
+ * Centrifugo channel (`ch:{channelId}`).
5
+ *
6
+ * Implements `SessionUpdateNotifier` so it can be `bindNotifier`'d
7
+ * to a specific sid in `SessionManager`. The MessageBus inbound
8
+ * handler creates one of these per `chat_message` envelope, binds
9
+ * it for the duration of the turn, then unbinds it when the await
10
+ * for `promptForSession()` returns.
11
+ *
12
+ * Wire mapping (locked in to match beeos-claw `acp-server.ts onStreamDelta`):
13
+ *
14
+ * - `agent_message_chunk` → `agent_reply_delta` envelope with
15
+ * `update.sessionUpdate=agent_message_chunk`. The text is also
16
+ * accumulated into `accumulatedText` so the terminal `agent_reply`
17
+ * can carry the full reply as `payload.text`.
18
+ *
19
+ * - `agent_thought_chunk` → `agent_reply_delta` envelope with
20
+ * `update.sessionUpdate=agent_thought_chunk`. Reasoning is NEVER
21
+ * accumulated — `backend/pkg/convmsg/extract.go::extractNestedDelta`
22
+ * intentionally drops thought chunks so they don't pollute reply
23
+ * text, and we mirror that here.
24
+ *
25
+ * - `stream_complete` → terminal `agent_reply` envelope. Payload
26
+ * shape mirrors beeos-claw:
27
+ * success → { session_key, text }
28
+ * cancelled → { session_key, error: "cancelled", text? }
29
+ * failure → { session_key, error, text? }
30
+ *
31
+ * - All other `sessionUpdate` types (`agent_status`, `tool_call`,
32
+ * `tool_call_update`, `history_turn`, `system_message_chunk`,
33
+ * `user_message_chunk`) are intentionally SKIPPED. The old
34
+ * outbound listener also skipped them; mirroring keeps the wire
35
+ * format byte-for-byte identical for downstream consumers.
36
+ *
37
+ * **Only the terminal `agent_reply`** carries `in_reply_to = inboundMessageId`
38
+ * so the L0 `/channels/{id}/wait` endpoint can match the reply on the
39
+ * original `message_id`. Streaming deltas are fan-out only and MUST NOT
40
+ * carry `in_reply_to` — otherwise MS `/wait` (which only checks
41
+ * `in_reply_to`, not message type) would short-circuit on the first
42
+ * delta and the caller would misreport "completed". Mirrors beeos-claw
43
+ * `acp-server.ts:73-85`.
44
+ *
45
+ * The notifier never throws out of `send()` — that runs on the
46
+ * SessionManager event hot path and a synchronous throw would skip
47
+ * subsequent handlers (e.g. status:idle). All publish failures are
48
+ * logged and swallowed. The next event for the same turn will retry
49
+ * any subsequent publishes via the same path.
50
+ */
51
+ import type { MessageSDK } from "@beeos-ai/message-sdk";
52
+ import type { SessionUpdateNotifier } from "../../app/session-manager.js";
53
+ import type { Logger } from "../../logger/index.js";
54
+ export interface MsChannelNotifierOptions {
55
+ sdk: MessageSDK;
56
+ /** IM channel the reply is published on (`ch:{channelId}`). */
57
+ channelId: string;
58
+ /**
59
+ * Original `chat_message.message_id`. Echoed as `in_reply_to` on
60
+ * every published envelope so L0 `/wait` can match.
61
+ */
62
+ inboundMessageId: string;
63
+ /**
64
+ * Stable session anchor (`agent:{id}:invoke:ctx-{contextId}` or
65
+ * `agent:{id}:invoke:ch-{channelId}`) derived by `extractChatPrompt`.
66
+ * Embedded as `payload.session_key` for forward-compat parity with
67
+ * beeos-claw downstream observers.
68
+ */
69
+ sessionKey: string;
70
+ logger: Logger;
71
+ }
72
+ export declare class MsChannelNotifier implements SessionUpdateNotifier {
73
+ private readonly opts;
74
+ /** Cumulative `agent_message_chunk` text — populates terminal `agent_reply.text`. */
75
+ private accumulatedText;
76
+ private finalized;
77
+ constructor(opts: MsChannelNotifierOptions);
78
+ /**
79
+ * Called by SessionManager for every `session/update` envelope on
80
+ * this notifier's bound sid. Fire-and-forget: the SessionManager
81
+ * event hot path is synchronous, so we kick off the publish in the
82
+ * background and never block.
83
+ */
84
+ send(_sessionId: string, update: Record<string, unknown>): void;
85
+ /**
86
+ * Cumulative reply text emitted so far. Exposed for diagnostics +
87
+ * tests; not part of any wire contract.
88
+ */
89
+ get currentText(): string;
90
+ /** True once the terminal `agent_reply` has been published. */
91
+ get isFinalized(): boolean;
92
+ private handle;
93
+ private publishDelta;
94
+ /**
95
+ * Emit the terminal `agent_reply`. Idempotent — guarded by
96
+ * `finalized` so a duplicate `stream_complete` (extremely rare,
97
+ * but possible if the host event handler ever fires twice) cannot
98
+ * publish two terminal envelopes.
99
+ */
100
+ private publishFinal;
101
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * MsChannelNotifier — translate per-session ACP `session/update`
3
+ * envelopes into BeeOS Message Service wire envelopes for ONE
4
+ * Centrifugo channel (`ch:{channelId}`).
5
+ *
6
+ * Implements `SessionUpdateNotifier` so it can be `bindNotifier`'d
7
+ * to a specific sid in `SessionManager`. The MessageBus inbound
8
+ * handler creates one of these per `chat_message` envelope, binds
9
+ * it for the duration of the turn, then unbinds it when the await
10
+ * for `promptForSession()` returns.
11
+ *
12
+ * Wire mapping (locked in to match beeos-claw `acp-server.ts onStreamDelta`):
13
+ *
14
+ * - `agent_message_chunk` → `agent_reply_delta` envelope with
15
+ * `update.sessionUpdate=agent_message_chunk`. The text is also
16
+ * accumulated into `accumulatedText` so the terminal `agent_reply`
17
+ * can carry the full reply as `payload.text`.
18
+ *
19
+ * - `agent_thought_chunk` → `agent_reply_delta` envelope with
20
+ * `update.sessionUpdate=agent_thought_chunk`. Reasoning is NEVER
21
+ * accumulated — `backend/pkg/convmsg/extract.go::extractNestedDelta`
22
+ * intentionally drops thought chunks so they don't pollute reply
23
+ * text, and we mirror that here.
24
+ *
25
+ * - `stream_complete` → terminal `agent_reply` envelope. Payload
26
+ * shape mirrors beeos-claw:
27
+ * success → { session_key, text }
28
+ * cancelled → { session_key, error: "cancelled", text? }
29
+ * failure → { session_key, error, text? }
30
+ *
31
+ * - All other `sessionUpdate` types (`agent_status`, `tool_call`,
32
+ * `tool_call_update`, `history_turn`, `system_message_chunk`,
33
+ * `user_message_chunk`) are intentionally SKIPPED. The old
34
+ * outbound listener also skipped them; mirroring keeps the wire
35
+ * format byte-for-byte identical for downstream consumers.
36
+ *
37
+ * **Only the terminal `agent_reply`** carries `in_reply_to = inboundMessageId`
38
+ * so the L0 `/channels/{id}/wait` endpoint can match the reply on the
39
+ * original `message_id`. Streaming deltas are fan-out only and MUST NOT
40
+ * carry `in_reply_to` — otherwise MS `/wait` (which only checks
41
+ * `in_reply_to`, not message type) would short-circuit on the first
42
+ * delta and the caller would misreport "completed". Mirrors beeos-claw
43
+ * `acp-server.ts:73-85`.
44
+ *
45
+ * The notifier never throws out of `send()` — that runs on the
46
+ * SessionManager event hot path and a synchronous throw would skip
47
+ * subsequent handlers (e.g. status:idle). All publish failures are
48
+ * logged and swallowed. The next event for the same turn will retry
49
+ * any subsequent publishes via the same path.
50
+ */
51
+ export class MsChannelNotifier {
52
+ opts;
53
+ /** Cumulative `agent_message_chunk` text — populates terminal `agent_reply.text`. */
54
+ accumulatedText = "";
55
+ finalized = false;
56
+ constructor(opts) {
57
+ this.opts = opts;
58
+ }
59
+ /**
60
+ * Called by SessionManager for every `session/update` envelope on
61
+ * this notifier's bound sid. Fire-and-forget: the SessionManager
62
+ * event hot path is synchronous, so we kick off the publish in the
63
+ * background and never block.
64
+ */
65
+ send(_sessionId, update) {
66
+ void this.handle(update);
67
+ }
68
+ /**
69
+ * Cumulative reply text emitted so far. Exposed for diagnostics +
70
+ * tests; not part of any wire contract.
71
+ */
72
+ get currentText() {
73
+ return this.accumulatedText;
74
+ }
75
+ /** True once the terminal `agent_reply` has been published. */
76
+ get isFinalized() {
77
+ return this.finalized;
78
+ }
79
+ async handle(update) {
80
+ const sessionUpdate = pickSessionUpdate(update);
81
+ try {
82
+ switch (sessionUpdate) {
83
+ case "agent_message_chunk": {
84
+ const text = pickContentText(update.content);
85
+ if (!text)
86
+ return;
87
+ this.accumulatedText += text;
88
+ await this.publishDelta("agent_message_chunk", text);
89
+ return;
90
+ }
91
+ case "agent_thought_chunk": {
92
+ const text = pickContentText(update.content);
93
+ if (!text)
94
+ return;
95
+ await this.publishDelta("agent_thought_chunk", text);
96
+ return;
97
+ }
98
+ case "stream_complete": {
99
+ const stopReason = pickString(update.stopReason) || "end_turn";
100
+ const errorText = pickString(update.error);
101
+ await this.publishFinal(stopReason, errorText);
102
+ return;
103
+ }
104
+ default:
105
+ // agent_status / tool_call / tool_call_update / history_turn
106
+ // / system_message_chunk / user_message_chunk → skip.
107
+ return;
108
+ }
109
+ }
110
+ catch (err) {
111
+ this.opts.logger.error({
112
+ err: err instanceof Error ? err.message : String(err),
113
+ channelId: this.opts.channelId,
114
+ sessionUpdate,
115
+ }, "ms-channel-notifier: publish failed");
116
+ }
117
+ }
118
+ async publishDelta(sessionUpdate, text) {
119
+ // Streaming deltas are fan-out only — they MUST NOT carry
120
+ // `in_reply_to`, otherwise MS `/wait` (which only checks
121
+ // `in_reply_to`, not message type) would short-circuit on the
122
+ // first delta and the caller would think the turn finished.
123
+ // Mirrors beeos-claw `acp-server.ts:73-85`.
124
+ await this.opts.sdk.sendToChannel(this.opts.channelId, "agent_reply_delta", {
125
+ session_key: this.opts.sessionKey,
126
+ update: {
127
+ sessionUpdate,
128
+ content: { type: "text", text },
129
+ },
130
+ });
131
+ }
132
+ /**
133
+ * Emit the terminal `agent_reply`. Idempotent — guarded by
134
+ * `finalized` so a duplicate `stream_complete` (extremely rare,
135
+ * but possible if the host event handler ever fires twice) cannot
136
+ * publish two terminal envelopes.
137
+ */
138
+ async publishFinal(stopReason, errorText) {
139
+ if (this.finalized)
140
+ return;
141
+ this.finalized = true;
142
+ let payload;
143
+ if (stopReason === "cancelled") {
144
+ payload = {
145
+ session_key: this.opts.sessionKey,
146
+ error: "cancelled",
147
+ text: this.accumulatedText,
148
+ };
149
+ }
150
+ else if (errorText) {
151
+ payload = {
152
+ session_key: this.opts.sessionKey,
153
+ error: errorText,
154
+ text: this.accumulatedText,
155
+ };
156
+ }
157
+ else {
158
+ payload = {
159
+ session_key: this.opts.sessionKey,
160
+ text: this.accumulatedText,
161
+ };
162
+ }
163
+ await this.opts.sdk.sendToChannel(this.opts.channelId, "agent_reply", payload, { inReplyTo: this.opts.inboundMessageId });
164
+ }
165
+ }
166
+ function pickSessionUpdate(update) {
167
+ const v = update.sessionUpdate;
168
+ return typeof v === "string" ? v : "";
169
+ }
170
+ function pickContentText(content) {
171
+ if (!content || typeof content !== "object")
172
+ return "";
173
+ const c = content;
174
+ if (typeof c.text === "string")
175
+ return c.text;
176
+ return "";
177
+ }
178
+ function pickString(v) {
179
+ return typeof v === "string" ? v : "";
180
+ }
181
+ //# sourceMappingURL=ms-channel-notifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ms-channel-notifier.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/ms-channel-notifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tiny single-entry tracker for the IN-FLIGHT MS-originated turn.
3
+ *
4
+ * v0.9.0 — replaces the v0.8.0 `TurnContextRegister`, which carried
5
+ * far more state because the outbound listener needed to look up the
6
+ * channelId / sessionKey / inboundMessageId per AgentEvent. With
7
+ * `MsChannelNotifier` owning the wire mapping per-sid, the tracker
8
+ * only needs to answer ONE question: "given a `chat_cancel.target_
9
+ * message_id`, which sid (if any) is currently running?".
10
+ *
11
+ * The tracker is single-entry by design — `SessionManager.currentTask`
12
+ * already enforces "at most one in-flight turn" at the SessionManager
13
+ * level, so the plugin layer can't legitimately have two MS turns
14
+ * active at once.
15
+ */
16
+ export interface MsInflightEntry {
17
+ /** Original inbound `chat_message.message_id`. */
18
+ messageId: string;
19
+ /** Centrifugo channel `ch:{channelId}` the reply will be published on. */
20
+ channelId: string;
21
+ /** Per-channel sid bound on `SessionManager` (== `extractChatPrompt.sessionKey`). */
22
+ sid: string;
23
+ /** Wall-clock timestamp the turn was claimed (ms). For diagnostics. */
24
+ startedAt: number;
25
+ }
26
+ export declare class MsInflightTracker {
27
+ private current;
28
+ set(entry: Omit<MsInflightEntry, "startedAt">): MsInflightEntry;
29
+ /** Idempotent — clearing twice or with a wrong id is a no-op. */
30
+ clear(messageId: string): void;
31
+ /** Force-clear (used during plugin stop / disconnect). */
32
+ reset(): void;
33
+ findByInboundMessageId(messageId: string): MsInflightEntry | null;
34
+ /** Visible for tests / logs. */
35
+ peek(): MsInflightEntry | null;
36
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Tiny single-entry tracker for the IN-FLIGHT MS-originated turn.
3
+ *
4
+ * v0.9.0 — replaces the v0.8.0 `TurnContextRegister`, which carried
5
+ * far more state because the outbound listener needed to look up the
6
+ * channelId / sessionKey / inboundMessageId per AgentEvent. With
7
+ * `MsChannelNotifier` owning the wire mapping per-sid, the tracker
8
+ * only needs to answer ONE question: "given a `chat_cancel.target_
9
+ * message_id`, which sid (if any) is currently running?".
10
+ *
11
+ * The tracker is single-entry by design — `SessionManager.currentTask`
12
+ * already enforces "at most one in-flight turn" at the SessionManager
13
+ * level, so the plugin layer can't legitimately have two MS turns
14
+ * active at once.
15
+ */
16
+ export class MsInflightTracker {
17
+ current = null;
18
+ set(entry) {
19
+ this.current = { ...entry, startedAt: Date.now() };
20
+ return this.current;
21
+ }
22
+ /** Idempotent — clearing twice or with a wrong id is a no-op. */
23
+ clear(messageId) {
24
+ if (this.current && this.current.messageId === messageId) {
25
+ this.current = null;
26
+ }
27
+ }
28
+ /** Force-clear (used during plugin stop / disconnect). */
29
+ reset() {
30
+ this.current = null;
31
+ }
32
+ findByInboundMessageId(messageId) {
33
+ return this.current && this.current.messageId === messageId
34
+ ? this.current
35
+ : null;
36
+ }
37
+ /** Visible for tests / logs. */
38
+ peek() {
39
+ return this.current;
40
+ }
41
+ }
42
+ //# sourceMappingURL=ms-inflight-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ms-inflight-tracker.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/ms-inflight-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH,MAAM,OAAO,iBAAiB;IACpB,OAAO,GAA2B,IAAI,CAAC;IAE/C,GAAG,CAAC,KAAyC;QAC3C,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,SAAiB;QACrB,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACzD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,sBAAsB,CAAC,SAAiB;QACtC,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS;YACzD,CAAC,CAAC,IAAI,CAAC,OAAO;YACd,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAED,gCAAgC;IAChC,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF"}
@@ -33,11 +33,23 @@
33
33
  */
34
34
  import type { Logger } from "pino";
35
35
  import type { Config } from "../config/schema.js";
36
+ import type { SessionManager } from "../app/session-manager.js";
36
37
  import type { AgentHost } from "./agent-host.js";
37
38
  export interface PluginContext {
38
39
  host: AgentHost;
39
40
  config: Config;
40
41
  log: Logger;
42
+ /**
43
+ * v0.9.0 — bootstrap-owned `SessionManager` shared across plugins.
44
+ *
45
+ * AcpPlugin uses it via the operator's fixed sid; MessageBusPlugin
46
+ * uses `findOrCreateSession(sessionKey)` + `bindNotifier` to route
47
+ * per-channel turns. Plugins MUST NOT construct their own
48
+ * SessionManager — single instance per process is the invariant
49
+ * that lets the in-flight gate (Layer 3) protect the physical
50
+ * device.
51
+ */
52
+ sessionManager: SessionManager;
41
53
  }
42
54
  export interface AgentPlugin {
43
55
  /** Unique, stable plugin name (used for logging / disable filters). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beeos-ai/device-agent",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "BeeOS device-agent — Android/desktop AI control agent (TypeScript) speaking ACP over Bridge WebSocket. Connects to a sibling device-mcp-server process for device control.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,60 +0,0 @@
1
- /**
2
- * agent-runtime/skills/sqlite-store — SQLite-backed
3
- * {@link SkillCacheStore}.
4
- *
5
- * Phase 3.1 of the [device-agent reflector upgrade plan](../../../../../../.cursor/plans/device-agent-reflector-skills_d5599350.plan.md).
6
- *
7
- * Schema (kept verbatim with the plan):
8
- * ```sql
9
- * CREATE TABLE skills (
10
- * id TEXT PRIMARY KEY,
11
- * app_package TEXT NOT NULL,
12
- * task_summary TEXT NOT NULL,
13
- * task_embedding BLOB,
14
- * action_sequence_json TEXT NOT NULL,
15
- * success_count INTEGER NOT NULL DEFAULT 1,
16
- * fail_count INTEGER NOT NULL DEFAULT 0,
17
- * last_used_at INTEGER NOT NULL,
18
- * created_at INTEGER NOT NULL
19
- * );
20
- * CREATE INDEX idx_skills_app ON skills(app_package);
21
- * ```
22
- *
23
- * Default DB path: `~/.beeos/device-agent/skills.sqlite`. Containerised
24
- * deployments mount `/var/lib/beeos/device-agent` and override
25
- * `SKILL_CACHE_PATH` accordingly.
26
- *
27
- * Search is implemented as a full scan over the matching app rows —
28
- * the cache is intentionally small (<10k rows / device), so a brute
29
- * cosine pass is cheaper than maintaining an ANN index. If profile
30
- * data shows that's wrong we can plug in `sqlite-vss` / `pgvector`
31
- * later behind the same {@link SkillCacheStore} interface.
32
- */
33
- import type { Database as DB } from "better-sqlite3";
34
- import type { NewSkillInput, Skill, SkillCacheStore, SkillMatch } from "./types.js";
35
- export interface SQLiteSkillCacheStoreOptions {
36
- /** Override the default DB path. Use ":memory:" for ephemeral tests. */
37
- path?: string;
38
- /** Pre-built better-sqlite3 Database (used by tests). */
39
- db?: DB;
40
- /** Skip auto-mkdir on the parent dir (when path is :memory:). */
41
- noMkdir?: boolean;
42
- }
43
- export declare function defaultSkillCachePath(): string;
44
- export declare class SQLiteSkillCacheStore implements SkillCacheStore {
45
- private readonly db;
46
- private readonly stmts;
47
- private closed;
48
- constructor(opts?: SQLiteSkillCacheStoreOptions);
49
- insert(input: NewSkillInput): Skill;
50
- get(id: string): Skill | undefined;
51
- listByApp(appPackage: string, limit?: number): Skill[];
52
- listAll(limit?: number): Skill[];
53
- search(appPackage: string, query: Float32Array, k: number): SkillMatch[];
54
- recordSuccess(id: string, now?: number): void;
55
- recordFailure(id: string, now?: number): void;
56
- delete(id: string): boolean;
57
- clear(): void;
58
- close(): void;
59
- private applyMigrations;
60
- }