botinabox 2.5.2 → 2.6.0

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 (277) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +190 -190
  3. package/bin/botinabox.mjs +1 -1
  4. package/dist/{chunk-UACT2WXX.js → chunk-7AGWGYZC.js} +43 -5
  5. package/dist/cli.js +1 -1
  6. package/dist/connectors/google/index.d.ts +10 -1
  7. package/dist/connectors/google/index.js +1 -1
  8. package/dist/{gmail-connector-2FVYTQJH.js → gmail-connector-VP5FF56J.js} +2 -1
  9. package/dist/index.d.ts +64 -1
  10. package/dist/index.js +44 -10
  11. package/package.json +100 -100
  12. package/dist/channels/discord/adapter.d.ts +0 -32
  13. package/dist/channels/discord/adapter.js +0 -70
  14. package/dist/channels/discord/inbound.d.ts +0 -25
  15. package/dist/channels/discord/inbound.js +0 -24
  16. package/dist/channels/discord/models.d.ts +0 -8
  17. package/dist/channels/discord/models.js +0 -5
  18. package/dist/channels/discord/outbound.d.ts +0 -14
  19. package/dist/channels/discord/outbound.js +0 -38
  20. package/dist/channels/slack/adapter.d.ts +0 -33
  21. package/dist/channels/slack/adapter.js +0 -74
  22. package/dist/channels/slack/inbound.d.ts +0 -59
  23. package/dist/channels/slack/inbound.js +0 -96
  24. package/dist/channels/slack/models.d.ts +0 -9
  25. package/dist/channels/slack/models.js +0 -5
  26. package/dist/channels/slack/outbound.d.ts +0 -12
  27. package/dist/channels/slack/outbound.js +0 -18
  28. package/dist/channels/slack/transcribe.d.ts +0 -41
  29. package/dist/channels/slack/transcribe.js +0 -106
  30. package/dist/channels/webhook/adapter.d.ts +0 -23
  31. package/dist/channels/webhook/adapter.js +0 -86
  32. package/dist/channels/webhook/hmac.d.ts +0 -13
  33. package/dist/channels/webhook/hmac.js +0 -26
  34. package/dist/channels/webhook/models.d.ts +0 -9
  35. package/dist/channels/webhook/models.js +0 -5
  36. package/dist/channels/webhook/server.d.ts +0 -20
  37. package/dist/channels/webhook/server.js +0 -91
  38. package/dist/chat-pipeline-C-XlLGNl.d.ts +0 -648
  39. package/dist/chat-pipeline-CR1KF6eX.d.ts +0 -652
  40. package/dist/chat-pipeline-DisuC8SB.d.ts +0 -643
  41. package/dist/chunk-2LGXQPEA.js +0 -41
  42. package/dist/chunk-3X3YKI4T.js +0 -357
  43. package/dist/chunk-D47AIFOD.js +0 -351
  44. package/dist/chunk-DSNJKNEW.js +0 -328
  45. package/dist/chunk-GS2JFL6I.js +0 -144
  46. package/dist/chunk-J6S6QMUY.js +0 -144
  47. package/dist/chunk-QLA6YOFN.js +0 -22
  48. package/dist/cli/templates/config.yml.d.ts +0 -7
  49. package/dist/cli/templates/config.yml.js +0 -61
  50. package/dist/cli/templates/env.d.ts +0 -1
  51. package/dist/cli/templates/env.js +0 -30
  52. package/dist/cli/templates/index.ts.d.ts +0 -2
  53. package/dist/cli/templates/index.ts.js +0 -30
  54. package/dist/cli/templates/package.json.d.ts +0 -5
  55. package/dist/cli/templates/package.json.js +0 -28
  56. package/dist/connector-DDahQw-2.d.ts +0 -63
  57. package/dist/connectors/google/calendar-connector.d.ts +0 -40
  58. package/dist/connectors/google/calendar-connector.js +0 -243
  59. package/dist/connectors/google/gmail-connector.d.ts +0 -42
  60. package/dist/connectors/google/gmail-connector.js +0 -345
  61. package/dist/connectors/google/oauth.d.ts +0 -48
  62. package/dist/connectors/google/oauth.js +0 -112
  63. package/dist/connectors/google/types.d.ts +0 -78
  64. package/dist/connectors/google/types.js +0 -2
  65. package/dist/core/chat/auto-discovery.d.ts +0 -16
  66. package/dist/core/chat/auto-discovery.js +0 -54
  67. package/dist/core/chat/channel-registry.d.ts +0 -45
  68. package/dist/core/chat/channel-registry.js +0 -96
  69. package/dist/core/chat/chat-pipeline.d.ts +0 -113
  70. package/dist/core/chat/chat-pipeline.js +0 -395
  71. package/dist/core/chat/chat-responder.d.ts +0 -90
  72. package/dist/core/chat/chat-responder.js +0 -185
  73. package/dist/core/chat/formatter.d.ts +0 -11
  74. package/dist/core/chat/formatter.js +0 -60
  75. package/dist/core/chat/index.d.ts +0 -24
  76. package/dist/core/chat/index.js +0 -18
  77. package/dist/core/chat/message-interpreter.d.ts +0 -91
  78. package/dist/core/chat/message-interpreter.js +0 -166
  79. package/dist/core/chat/message-store.d.ts +0 -66
  80. package/dist/core/chat/message-store.js +0 -131
  81. package/dist/core/chat/notification-queue.d.ts +0 -34
  82. package/dist/core/chat/notification-queue.js +0 -111
  83. package/dist/core/chat/pipeline.d.ts +0 -38
  84. package/dist/core/chat/pipeline.js +0 -89
  85. package/dist/core/chat/policies.d.ts +0 -16
  86. package/dist/core/chat/policies.js +0 -25
  87. package/dist/core/chat/routing.d.ts +0 -17
  88. package/dist/core/chat/routing.js +0 -36
  89. package/dist/core/chat/session-key.d.ts +0 -30
  90. package/dist/core/chat/session-key.js +0 -65
  91. package/dist/core/chat/session-manager.d.ts +0 -17
  92. package/dist/core/chat/session-manager.js +0 -23
  93. package/dist/core/chat/text-chunker.d.ts +0 -9
  94. package/dist/core/chat/text-chunker.js +0 -48
  95. package/dist/core/chat/triage-router.d.ts +0 -75
  96. package/dist/core/chat/triage-router.js +0 -142
  97. package/dist/core/chat/types.d.ts +0 -5
  98. package/dist/core/chat/types.js +0 -5
  99. package/dist/core/config/defaults.d.ts +0 -2
  100. package/dist/core/config/defaults.js +0 -38
  101. package/dist/core/config/index.d.ts +0 -6
  102. package/dist/core/config/index.js +0 -4
  103. package/dist/core/config/interpolate.d.ts +0 -5
  104. package/dist/core/config/interpolate.js +0 -27
  105. package/dist/core/config/loader.d.ts +0 -24
  106. package/dist/core/config/loader.js +0 -59
  107. package/dist/core/config/schema.d.ts +0 -5
  108. package/dist/core/config/schema.js +0 -119
  109. package/dist/core/data/core-entity-contexts.d.ts +0 -14
  110. package/dist/core/data/core-entity-contexts.js +0 -197
  111. package/dist/core/data/core-migrations.d.ts +0 -5
  112. package/dist/core/data/core-migrations.js +0 -45
  113. package/dist/core/data/core-schema.d.ts +0 -6
  114. package/dist/core/data/core-schema.js +0 -454
  115. package/dist/core/data/data-store.d.ts +0 -67
  116. package/dist/core/data/data-store.js +0 -218
  117. package/dist/core/data/domain-entity-contexts.d.ts +0 -29
  118. package/dist/core/data/domain-entity-contexts.js +0 -321
  119. package/dist/core/data/domain-schema.d.ts +0 -36
  120. package/dist/core/data/domain-schema.js +0 -323
  121. package/dist/core/data/index.d.ts +0 -7
  122. package/dist/core/data/index.js +0 -7
  123. package/dist/core/data/types.d.ts +0 -111
  124. package/dist/core/data/types.js +0 -1
  125. package/dist/core/hooks/hook-bus.d.ts +0 -18
  126. package/dist/core/hooks/hook-bus.js +0 -120
  127. package/dist/core/hooks/index.d.ts +0 -2
  128. package/dist/core/hooks/index.js +0 -1
  129. package/dist/core/hooks/types.d.ts +0 -19
  130. package/dist/core/hooks/types.js +0 -1
  131. package/dist/core/index.d.ts +0 -4
  132. package/dist/core/index.js +0 -4
  133. package/dist/core/llm/auto-discovery.d.ts +0 -11
  134. package/dist/core/llm/auto-discovery.js +0 -49
  135. package/dist/core/llm/cost-tracker.d.ts +0 -6
  136. package/dist/core/llm/cost-tracker.js +0 -38
  137. package/dist/core/llm/index.d.ts +0 -4
  138. package/dist/core/llm/index.js +0 -3
  139. package/dist/core/llm/model-router.d.ts +0 -25
  140. package/dist/core/llm/model-router.js +0 -49
  141. package/dist/core/llm/provider-registry.d.ts +0 -9
  142. package/dist/core/llm/provider-registry.js +0 -25
  143. package/dist/core/llm/types.d.ts +0 -2
  144. package/dist/core/llm/types.js +0 -2
  145. package/dist/core/orchestrator/adapters/api-adapter.d.ts +0 -34
  146. package/dist/core/orchestrator/adapters/api-adapter.js +0 -88
  147. package/dist/core/orchestrator/adapters/cli-adapter.d.ts +0 -22
  148. package/dist/core/orchestrator/adapters/cli-adapter.js +0 -69
  149. package/dist/core/orchestrator/adapters/deterministic-adapter.d.ts +0 -35
  150. package/dist/core/orchestrator/adapters/deterministic-adapter.js +0 -75
  151. package/dist/core/orchestrator/adapters/env-whitelist.d.ts +0 -4
  152. package/dist/core/orchestrator/adapters/env-whitelist.js +0 -27
  153. package/dist/core/orchestrator/adapters/output-extractor.d.ts +0 -11
  154. package/dist/core/orchestrator/adapters/output-extractor.js +0 -59
  155. package/dist/core/orchestrator/adapters/process-manager.d.ts +0 -15
  156. package/dist/core/orchestrator/adapters/process-manager.js +0 -26
  157. package/dist/core/orchestrator/adapters/tool-loop.d.ts +0 -22
  158. package/dist/core/orchestrator/adapters/tool-loop.js +0 -66
  159. package/dist/core/orchestrator/agent-registry.d.ts +0 -31
  160. package/dist/core/orchestrator/agent-registry.js +0 -135
  161. package/dist/core/orchestrator/budget-controller.d.ts +0 -19
  162. package/dist/core/orchestrator/budget-controller.js +0 -73
  163. package/dist/core/orchestrator/chain-guard.d.ts +0 -14
  164. package/dist/core/orchestrator/chain-guard.js +0 -23
  165. package/dist/core/orchestrator/circuit-breaker.d.ts +0 -65
  166. package/dist/core/orchestrator/circuit-breaker.js +0 -159
  167. package/dist/core/orchestrator/claude-stream-parser.d.ts +0 -31
  168. package/dist/core/orchestrator/claude-stream-parser.js +0 -99
  169. package/dist/core/orchestrator/config-revisions.d.ts +0 -6
  170. package/dist/core/orchestrator/config-revisions.js +0 -17
  171. package/dist/core/orchestrator/dependency-resolver.d.ts +0 -20
  172. package/dist/core/orchestrator/dependency-resolver.js +0 -78
  173. package/dist/core/orchestrator/governance-gate.d.ts +0 -110
  174. package/dist/core/orchestrator/governance-gate.js +0 -170
  175. package/dist/core/orchestrator/learning-pipeline.d.ts +0 -109
  176. package/dist/core/orchestrator/learning-pipeline.js +0 -249
  177. package/dist/core/orchestrator/loop-detector.d.ts +0 -51
  178. package/dist/core/orchestrator/loop-detector.js +0 -133
  179. package/dist/core/orchestrator/ndjson-logger.d.ts +0 -6
  180. package/dist/core/orchestrator/ndjson-logger.js +0 -18
  181. package/dist/core/orchestrator/permission-relay.d.ts +0 -72
  182. package/dist/core/orchestrator/permission-relay.js +0 -164
  183. package/dist/core/orchestrator/run-manager.d.ts +0 -31
  184. package/dist/core/orchestrator/run-manager.js +0 -178
  185. package/dist/core/orchestrator/scheduler.d.ts +0 -70
  186. package/dist/core/orchestrator/scheduler.js +0 -198
  187. package/dist/core/orchestrator/secret-store.d.ts +0 -57
  188. package/dist/core/orchestrator/secret-store.js +0 -171
  189. package/dist/core/orchestrator/session-manager.d.ts +0 -13
  190. package/dist/core/orchestrator/session-manager.js +0 -66
  191. package/dist/core/orchestrator/task-queue.d.ts +0 -34
  192. package/dist/core/orchestrator/task-queue.js +0 -83
  193. package/dist/core/orchestrator/template-interpolate.d.ts +0 -5
  194. package/dist/core/orchestrator/template-interpolate.js +0 -18
  195. package/dist/core/orchestrator/user-registry.d.ts +0 -47
  196. package/dist/core/orchestrator/user-registry.js +0 -76
  197. package/dist/core/orchestrator/wakeup-queue.d.ts +0 -9
  198. package/dist/core/orchestrator/wakeup-queue.js +0 -45
  199. package/dist/core/orchestrator/workflow-engine.d.ts +0 -47
  200. package/dist/core/orchestrator/workflow-engine.js +0 -204
  201. package/dist/core/security/audit.d.ts +0 -20
  202. package/dist/core/security/audit.js +0 -33
  203. package/dist/core/security/column-validator.d.ts +0 -20
  204. package/dist/core/security/column-validator.js +0 -37
  205. package/dist/core/security/index.d.ts +0 -5
  206. package/dist/core/security/index.js +0 -5
  207. package/dist/core/security/process-env.d.ts +0 -13
  208. package/dist/core/security/process-env.js +0 -49
  209. package/dist/core/security/sanitizer.d.ts +0 -11
  210. package/dist/core/security/sanitizer.js +0 -39
  211. package/dist/core/security/types.d.ts +0 -11
  212. package/dist/core/security/types.js +0 -1
  213. package/dist/core/update/auto-update.d.ts +0 -21
  214. package/dist/core/update/auto-update.js +0 -102
  215. package/dist/core/update/backup-manager.d.ts +0 -7
  216. package/dist/core/update/backup-manager.js +0 -24
  217. package/dist/core/update/index.d.ts +0 -8
  218. package/dist/core/update/index.js +0 -6
  219. package/dist/core/update/migration-hooks.d.ts +0 -11
  220. package/dist/core/update/migration-hooks.js +0 -10
  221. package/dist/core/update/types.d.ts +0 -11
  222. package/dist/core/update/types.js +0 -1
  223. package/dist/core/update/update-checker.d.ts +0 -11
  224. package/dist/core/update/update-checker.js +0 -63
  225. package/dist/core/update/update-manager.d.ts +0 -25
  226. package/dist/core/update/update-manager.js +0 -101
  227. package/dist/core/update/version-utils.d.ts +0 -6
  228. package/dist/core/update/version-utils.js +0 -34
  229. package/dist/gmail-connector-MNUBRNFM.js +0 -6
  230. package/dist/gmail-connector-PS2VLGNE.js +0 -6
  231. package/dist/gmail-connector-ULSMN6X2.js +0 -6
  232. package/dist/gmail-connector-URRFX6A3.js +0 -6
  233. package/dist/inbound-AFBUPSPG.js +0 -10
  234. package/dist/inbound-AFOHYNUY.js +0 -6
  235. package/dist/inbound-CGIXRXGC.js +0 -8
  236. package/dist/inbound-MCOLRH6U.js +0 -10
  237. package/dist/inbound-SNEMBLGA.js +0 -6
  238. package/dist/inbound-ZJHAYVMF.js +0 -10
  239. package/dist/provider-qqJYv9nv.d.ts +0 -75
  240. package/dist/providers/anthropic/models.d.ts +0 -2
  241. package/dist/providers/anthropic/models.js +0 -29
  242. package/dist/providers/anthropic/provider.d.ts +0 -13
  243. package/dist/providers/anthropic/provider.js +0 -119
  244. package/dist/providers/anthropic/tool-converter.d.ts +0 -10
  245. package/dist/providers/anthropic/tool-converter.js +0 -7
  246. package/dist/providers/ollama/provider.d.ts +0 -17
  247. package/dist/providers/ollama/provider.js +0 -185
  248. package/dist/providers/openai/models.d.ts +0 -2
  249. package/dist/providers/openai/models.js +0 -29
  250. package/dist/providers/openai/provider.d.ts +0 -13
  251. package/dist/providers/openai/provider.js +0 -163
  252. package/dist/providers/openai/tool-converter.d.ts +0 -10
  253. package/dist/providers/openai/tool-converter.js +0 -10
  254. package/dist/shared/constants.d.ts +0 -50
  255. package/dist/shared/constants.js +0 -64
  256. package/dist/shared/index.d.ts +0 -14
  257. package/dist/shared/index.js +0 -14
  258. package/dist/shared/types/agent.d.ts +0 -36
  259. package/dist/shared/types/agent.js +0 -2
  260. package/dist/shared/types/channel.d.ts +0 -70
  261. package/dist/shared/types/channel.js +0 -2
  262. package/dist/shared/types/config.d.ts +0 -111
  263. package/dist/shared/types/config.js +0 -2
  264. package/dist/shared/types/connector.d.ts +0 -77
  265. package/dist/shared/types/connector.js +0 -2
  266. package/dist/shared/types/execution.d.ts +0 -29
  267. package/dist/shared/types/execution.js +0 -2
  268. package/dist/shared/types/provider.d.ts +0 -73
  269. package/dist/shared/types/provider.js +0 -2
  270. package/dist/shared/types/task.d.ts +0 -47
  271. package/dist/shared/types/task.js +0 -2
  272. package/dist/shared/types/workflow.d.ts +0 -39
  273. package/dist/shared/types/workflow.js +0 -2
  274. package/dist/shared/utils.d.ts +0 -6
  275. package/dist/shared/utils.js +0 -13
  276. package/dist/update-check.d.ts +0 -5
  277. package/dist/update-check.js +0 -56
@@ -1,23 +0,0 @@
1
- /**
2
- * ChatSessionManager — wraps orchestrator SessionManager with SessionKey-based lookup.
3
- * Story 4.3
4
- */
5
- import { SessionManager } from "../orchestrator/session-manager.js";
6
- export class ChatSessionManager {
7
- inner;
8
- constructor(db) {
9
- this.inner = new SessionManager(db);
10
- }
11
- async save(key, params) {
12
- return this.inner.save(key.agentId, key.channel, key.scope, params);
13
- }
14
- async load(key) {
15
- return this.inner.load(key.agentId, key.channel, key.scope);
16
- }
17
- async clear(key) {
18
- return this.inner.clear(key.agentId, key.channel, key.scope);
19
- }
20
- async shouldClear(session, opts) {
21
- return this.inner.shouldClear(session, opts);
22
- }
23
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Text chunker — splits long text into chunks at natural boundaries.
3
- * Story 4.4
4
- */
5
- /**
6
- * Split text into chunks of at most maxLen characters.
7
- * Splits at paragraph boundaries first, then sentence, then word, then hard-cuts.
8
- */
9
- export declare function chunkText(text: string, maxLen: number): string[];
@@ -1,48 +0,0 @@
1
- /**
2
- * Text chunker — splits long text into chunks at natural boundaries.
3
- * Story 4.4
4
- */
5
- /**
6
- * Split text into chunks of at most maxLen characters.
7
- * Splits at paragraph boundaries first, then sentence, then word, then hard-cuts.
8
- */
9
- export function chunkText(text, maxLen) {
10
- if (maxLen <= 0)
11
- throw new Error("maxLen must be > 0");
12
- if (text.length <= maxLen)
13
- return [text];
14
- const chunks = [];
15
- function splitInto(segment) {
16
- if (segment.length <= maxLen) {
17
- if (segment.length > 0)
18
- chunks.push(segment);
19
- return;
20
- }
21
- // Try paragraph split (\n\n)
22
- const paraIdx = segment.lastIndexOf("\n\n", maxLen);
23
- if (paraIdx > 0) {
24
- chunks.push(segment.slice(0, paraIdx));
25
- splitInto(segment.slice(paraIdx + 2).trimStart());
26
- return;
27
- }
28
- // Try sentence split (". ")
29
- const sentIdx = segment.lastIndexOf(". ", maxLen);
30
- if (sentIdx > 0) {
31
- chunks.push(segment.slice(0, sentIdx + 1));
32
- splitInto(segment.slice(sentIdx + 2).trimStart());
33
- return;
34
- }
35
- // Try word split (" ")
36
- const wordIdx = segment.lastIndexOf(" ", maxLen);
37
- if (wordIdx > 0) {
38
- chunks.push(segment.slice(0, wordIdx));
39
- splitInto(segment.slice(wordIdx + 1));
40
- return;
41
- }
42
- // Hard cut
43
- chunks.push(segment.slice(0, maxLen));
44
- splitInto(segment.slice(maxLen));
45
- }
46
- splitInto(text);
47
- return chunks;
48
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * TriageRouter — content-based routing with deterministic-first resolution.
3
- * Story 6.3
4
- *
5
- * Replaces the simple channel→agent binding with intelligent routing:
6
- * 1. Keyword/regex rules evaluated first (deterministic, ~4ms)
7
- * 2. LLM classification only for ambiguous messages (async, ~2-4s)
8
- * 3. Ownership chain logged for every routing decision
9
- *
10
- * Key constraint: specialists return to triage, never to another specialist.
11
- */
12
- import type { DataStore } from '../data/data-store.js';
13
- import type { HookBus } from '../hooks/hook-bus.js';
14
- import type { InboundMessage } from './types.js';
15
- export interface RoutingRule {
16
- /** Target agent slug */
17
- agentSlug: string;
18
- /** Keywords that trigger this rule (case-insensitive) */
19
- keywords?: string[];
20
- /** Regex patterns that trigger this rule */
21
- patterns?: string[];
22
- /** Priority — lower number wins ties. Default: 50 */
23
- priority?: number;
24
- }
25
- export interface RoutingDecision {
26
- timestamp: string;
27
- source: string;
28
- target: string;
29
- reason: string;
30
- method: 'deterministic' | 'llm';
31
- messageId?: string;
32
- channel?: string;
33
- }
34
- export interface TriageRouterConfig {
35
- /** Static routing rules evaluated deterministically */
36
- rules: RoutingRule[];
37
- /** Fallback agent if no rule matches and LLM is unavailable */
38
- fallbackAgent?: string;
39
- /** Whether to use LLM for ambiguous messages. Default: true */
40
- llmFallback?: boolean;
41
- /** Log decisions to the database. Default: true */
42
- persist?: boolean;
43
- }
44
- export declare class TriageRouter {
45
- private db;
46
- private hooks;
47
- private readonly rules;
48
- private readonly fallbackAgent?;
49
- private readonly llmFallback;
50
- private readonly persist;
51
- private readonly compiledRules;
52
- constructor(db: DataStore, hooks: HookBus, config: TriageRouterConfig);
53
- /**
54
- * Route an inbound message to the best agent.
55
- * Returns the agent slug and the routing decision.
56
- */
57
- route(msg: InboundMessage): Promise<{
58
- agentSlug: string | undefined;
59
- decision: RoutingDecision;
60
- }>;
61
- /**
62
- * Query the ownership chain for a given message or channel.
63
- */
64
- getDecisionHistory(filter?: {
65
- channel?: string;
66
- limit?: number;
67
- }): Promise<RoutingDecision[]>;
68
- /**
69
- * LLM classification — emits a hook for external LLM integration.
70
- * Returns agent slug + reason, or undefined if LLM is unavailable.
71
- */
72
- private classifyWithLLM;
73
- private buildDecision;
74
- private logDecision;
75
- }
@@ -1,142 +0,0 @@
1
- /**
2
- * TriageRouter — content-based routing with deterministic-first resolution.
3
- * Story 6.3
4
- *
5
- * Replaces the simple channel→agent binding with intelligent routing:
6
- * 1. Keyword/regex rules evaluated first (deterministic, ~4ms)
7
- * 2. LLM classification only for ambiguous messages (async, ~2-4s)
8
- * 3. Ownership chain logged for every routing decision
9
- *
10
- * Key constraint: specialists return to triage, never to another specialist.
11
- */
12
- export class TriageRouter {
13
- db;
14
- hooks;
15
- rules;
16
- fallbackAgent;
17
- llmFallback;
18
- persist;
19
- compiledRules;
20
- constructor(db, hooks, config) {
21
- this.db = db;
22
- this.hooks = hooks;
23
- this.rules = config.rules;
24
- this.fallbackAgent = config.fallbackAgent;
25
- this.llmFallback = config.llmFallback ?? true;
26
- this.persist = config.persist ?? true;
27
- // Pre-compile patterns for fast matching
28
- this.compiledRules = this.rules
29
- .sort((a, b) => (a.priority ?? 50) - (b.priority ?? 50))
30
- .map((rule) => ({
31
- rule,
32
- regexes: (rule.patterns ?? []).map((p) => new RegExp(p, 'i')),
33
- keywordSet: new Set((rule.keywords ?? []).map((k) => k.toLowerCase())),
34
- }));
35
- }
36
- /**
37
- * Route an inbound message to the best agent.
38
- * Returns the agent slug and the routing decision.
39
- */
40
- async route(msg) {
41
- const body = msg.body.toLowerCase();
42
- const words = new Set(body.split(/\s+/));
43
- // Phase 1: Deterministic — keyword + regex matching
44
- for (const { rule, regexes, keywordSet } of this.compiledRules) {
45
- // Keyword match
46
- for (const keyword of keywordSet) {
47
- if (words.has(keyword) || body.includes(keyword)) {
48
- const decision = this.buildDecision(rule.agentSlug, `keyword: '${keyword}'`, 'deterministic', msg);
49
- await this.logDecision(decision);
50
- return { agentSlug: rule.agentSlug, decision };
51
- }
52
- }
53
- // Regex match
54
- for (const regex of regexes) {
55
- if (regex.test(msg.body)) {
56
- const decision = this.buildDecision(rule.agentSlug, `pattern: ${regex.source}`, 'deterministic', msg);
57
- await this.logDecision(decision);
58
- return { agentSlug: rule.agentSlug, decision };
59
- }
60
- }
61
- }
62
- // Phase 2: LLM classification (if enabled)
63
- if (this.llmFallback) {
64
- const agentSlugs = this.rules.map((r) => r.agentSlug);
65
- const classified = await this.classifyWithLLM(msg, agentSlugs);
66
- if (classified) {
67
- const decision = this.buildDecision(classified.agentSlug, `llm: ${classified.reason}`, 'llm', msg);
68
- await this.logDecision(decision);
69
- return { agentSlug: classified.agentSlug, decision };
70
- }
71
- }
72
- // Phase 3: Fallback
73
- const decision = this.buildDecision(this.fallbackAgent, 'fallback: no rule matched', 'deterministic', msg);
74
- await this.logDecision(decision);
75
- return { agentSlug: this.fallbackAgent, decision };
76
- }
77
- /**
78
- * Query the ownership chain for a given message or channel.
79
- */
80
- async getDecisionHistory(filter) {
81
- const rows = await this.db.query('activity_log', {
82
- where: { event_type: 'triage_decision' },
83
- });
84
- let decisions = rows.map((r) => {
85
- try {
86
- return JSON.parse(r['payload']);
87
- }
88
- catch {
89
- return null;
90
- }
91
- }).filter((d) => d !== null);
92
- if (filter?.channel) {
93
- decisions = decisions.filter((d) => d.channel === filter.channel);
94
- }
95
- // Sort by timestamp descending
96
- decisions.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
97
- if (filter?.limit) {
98
- decisions = decisions.slice(0, filter.limit);
99
- }
100
- return decisions;
101
- }
102
- /**
103
- * LLM classification — emits a hook for external LLM integration.
104
- * Returns agent slug + reason, or undefined if LLM is unavailable.
105
- */
106
- async classifyWithLLM(msg, agentSlugs) {
107
- // Emit classification request — listeners provide the result
108
- const result = {};
109
- await this.hooks.emit('triage.classify', {
110
- message: msg,
111
- candidates: agentSlugs,
112
- respond: (slug, reason) => {
113
- result.agentSlug = slug;
114
- result.reason = reason;
115
- },
116
- });
117
- if (result.agentSlug && result.reason) {
118
- return { agentSlug: result.agentSlug, reason: result.reason };
119
- }
120
- return undefined;
121
- }
122
- buildDecision(target, reason, method, msg) {
123
- return {
124
- timestamp: new Date().toISOString(),
125
- source: 'triage',
126
- target: target ?? 'none',
127
- reason,
128
- method,
129
- messageId: msg.id,
130
- channel: msg.channel,
131
- };
132
- }
133
- async logDecision(decision) {
134
- if (!this.persist)
135
- return;
136
- await this.db.insert('activity_log', {
137
- event_type: 'triage_decision',
138
- payload: JSON.stringify(decision),
139
- });
140
- await this.hooks.emit('triage.routed', { decision });
141
- }
142
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Chat types — re-exports from @botinabox/shared channel types.
3
- * Story 4.1
4
- */
5
- export type { ChatType, FormattingMode, ChannelCapabilities, ChannelMeta, InboundMessage, Attachment, OutboundPayload, SendResult, HealthStatus, ChannelConfig, ChannelAdapter, } from "../../shared/index.js";
@@ -1,5 +0,0 @@
1
- /**
2
- * Chat types — re-exports from @botinabox/shared channel types.
3
- * Story 4.1
4
- */
5
- export {};
@@ -1,2 +0,0 @@
1
- import type { BotConfig } from "../../shared/index.js";
2
- export declare const DEFAULT_CONFIG: BotConfig;
@@ -1,38 +0,0 @@
1
- export const DEFAULT_CONFIG = {
2
- data: {
3
- path: "./data/bot.db",
4
- walMode: true,
5
- },
6
- channels: {},
7
- agents: [],
8
- providers: {},
9
- models: {
10
- aliases: {
11
- fast: "claude-haiku-4-5",
12
- smart: "claude-opus-4-6",
13
- balanced: "claude-sonnet-4-6",
14
- },
15
- default: "smart",
16
- routing: {
17
- conversation: "fast",
18
- task_execution: "smart",
19
- classification: "fast",
20
- },
21
- fallbackChain: [],
22
- },
23
- entities: {},
24
- security: {
25
- fieldLengthLimits: { default: 65535 },
26
- },
27
- render: {
28
- outputDir: "./context",
29
- watchIntervalMs: 30_000,
30
- },
31
- updates: {
32
- policy: "auto-compatible",
33
- checkIntervalMs: 86_400_000,
34
- },
35
- budget: {
36
- warnPercent: 80,
37
- },
38
- };
@@ -1,6 +0,0 @@
1
- export { loadConfig, getConfig, initConfig, _resetConfig } from "./loader.js";
2
- export type { ConfigLoadError, ConfigLoadResult } from "./loader.js";
3
- export { interpolateEnv } from "./interpolate.js";
4
- export { validateConfig } from "./schema.js";
5
- export type { SchemaError } from "./schema.js";
6
- export { DEFAULT_CONFIG } from "./defaults.js";
@@ -1,4 +0,0 @@
1
- export { loadConfig, getConfig, initConfig, _resetConfig } from "./loader.js";
2
- export { interpolateEnv } from "./interpolate.js";
3
- export { validateConfig } from "./schema.js";
4
- export { DEFAULT_CONFIG } from "./defaults.js";
@@ -1,5 +0,0 @@
1
- /**
2
- * Interpolate ${ENV_VAR} references in YAML string values.
3
- * Recursively walks objects and arrays.
4
- */
5
- export declare function interpolateEnv(value: unknown, env?: Record<string, string | undefined>): unknown;
@@ -1,27 +0,0 @@
1
- /**
2
- * Interpolate ${ENV_VAR} references in YAML string values.
3
- * Recursively walks objects and arrays.
4
- */
5
- export function interpolateEnv(value, env = process.env) {
6
- if (typeof value === "string") {
7
- return value.replace(/\$\{([^}]+)\}/g, (_, name) => {
8
- const envVal = env[name];
9
- if (envVal === undefined) {
10
- // Leave the placeholder if env var is not set (don't silently swallow)
11
- return `\${${name}}`;
12
- }
13
- return envVal;
14
- });
15
- }
16
- if (Array.isArray(value)) {
17
- return value.map(item => interpolateEnv(item, env));
18
- }
19
- if (value !== null && typeof value === "object") {
20
- const result = {};
21
- for (const [k, v] of Object.entries(value)) {
22
- result[k] = interpolateEnv(v, env);
23
- }
24
- return result;
25
- }
26
- return value;
27
- }
@@ -1,24 +0,0 @@
1
- import type { BotConfig } from "../../shared/index.js";
2
- export interface ConfigLoadError {
3
- field: string;
4
- message: string;
5
- }
6
- export interface ConfigLoadResult {
7
- config: BotConfig;
8
- errors: ConfigLoadError[];
9
- }
10
- /**
11
- * Load and merge config from file, with env var interpolation and runtime overrides.
12
- * Merge order: defaults < config file < runtime overrides
13
- */
14
- export declare function loadConfig(opts?: {
15
- configPath?: string;
16
- overrides?: Partial<BotConfig>;
17
- env?: Record<string, string | undefined>;
18
- }): ConfigLoadResult;
19
- /** Get the loaded config singleton. Call loadConfig() first. */
20
- export declare function getConfig(): BotConfig;
21
- /** Initialize the config singleton. Returns errors if any. */
22
- export declare function initConfig(opts?: Parameters<typeof loadConfig>[0]): ConfigLoadError[];
23
- /** Reset the singleton (for testing) */
24
- export declare function _resetConfig(): void;
@@ -1,59 +0,0 @@
1
- import { readFileSync, existsSync } from "node:fs";
2
- import { parse as parseYaml } from "yaml";
3
- import { DEFAULT_CONFIG } from "./defaults.js";
4
- import { interpolateEnv } from "./interpolate.js";
5
- function deepMerge(base, override) {
6
- if (override === undefined || override === null)
7
- return base;
8
- if (base === undefined || base === null)
9
- return override;
10
- if (typeof base !== "object" || typeof override !== "object")
11
- return override;
12
- if (Array.isArray(override))
13
- return override;
14
- const result = { ...base };
15
- for (const [k, v] of Object.entries(override)) {
16
- if (v !== undefined) {
17
- result[k] = deepMerge(result[k], v);
18
- }
19
- }
20
- return result;
21
- }
22
- /**
23
- * Load and merge config from file, with env var interpolation and runtime overrides.
24
- * Merge order: defaults < config file < runtime overrides
25
- */
26
- export function loadConfig(opts) {
27
- const configPath = opts?.configPath ?? "botinabox.config.yml";
28
- const errors = [];
29
- let fileConfig = {};
30
- if (existsSync(configPath)) {
31
- try {
32
- const raw = readFileSync(configPath, "utf-8");
33
- const parsed = parseYaml(raw);
34
- fileConfig = interpolateEnv(parsed, opts?.env ?? process.env);
35
- }
36
- catch (err) {
37
- errors.push({ field: "configPath", message: `Failed to parse ${configPath}: ${String(err)}` });
38
- }
39
- }
40
- const merged = deepMerge(deepMerge(DEFAULT_CONFIG, fileConfig), opts?.overrides ?? {});
41
- return { config: Object.freeze(merged), errors };
42
- }
43
- let _config = null;
44
- /** Get the loaded config singleton. Call loadConfig() first. */
45
- export function getConfig() {
46
- if (!_config)
47
- throw new Error("Config not loaded — call loadConfig() first");
48
- return _config;
49
- }
50
- /** Initialize the config singleton. Returns errors if any. */
51
- export function initConfig(opts) {
52
- const { config, errors } = loadConfig(opts);
53
- _config = config;
54
- return errors;
55
- }
56
- /** Reset the singleton (for testing) */
57
- export function _resetConfig() {
58
- _config = null;
59
- }
@@ -1,5 +0,0 @@
1
- export interface SchemaError {
2
- path: string;
3
- message: string;
4
- }
5
- export declare function validateConfig(config: unknown): SchemaError[];
@@ -1,119 +0,0 @@
1
- import Ajv from "ajv";
2
- const ajv = new Ajv({ allErrors: true, coerceTypes: false });
3
- const BOT_CONFIG_SCHEMA = {
4
- type: "object",
5
- additionalProperties: true,
6
- properties: {
7
- data: {
8
- type: "object",
9
- required: ["path", "walMode"],
10
- properties: {
11
- path: { type: "string", minLength: 1 },
12
- walMode: { type: "boolean" },
13
- backupDir: { type: "string" },
14
- },
15
- },
16
- models: {
17
- type: "object",
18
- required: ["aliases", "default", "routing", "fallbackChain"],
19
- properties: {
20
- aliases: { type: "object", additionalProperties: { type: "string" } },
21
- default: { type: "string", minLength: 1 },
22
- routing: { type: "object", additionalProperties: { type: "string" } },
23
- fallbackChain: { type: "array", items: { type: "string" } },
24
- costLimit: {
25
- type: "object",
26
- properties: {
27
- perRunCents: { type: "number", minimum: 0 },
28
- },
29
- },
30
- },
31
- },
32
- security: {
33
- type: "object",
34
- properties: {
35
- fieldLengthLimits: { type: "object", additionalProperties: { type: "number" } },
36
- allowedFilePrefixes: { type: "array", items: { type: "string" } },
37
- },
38
- },
39
- render: {
40
- type: "object",
41
- required: ["outputDir", "watchIntervalMs"],
42
- properties: {
43
- outputDir: { type: "string", minLength: 1 },
44
- watchIntervalMs: { type: "number", minimum: 1000 },
45
- },
46
- },
47
- updates: {
48
- type: "object",
49
- required: ["policy", "checkIntervalMs"],
50
- properties: {
51
- policy: { type: "string", enum: ["auto-all", "auto-compatible", "auto-patch", "notify", "manual"] },
52
- checkIntervalMs: { type: "number", minimum: 60_000 },
53
- },
54
- },
55
- budget: {
56
- type: "object",
57
- required: ["warnPercent"],
58
- properties: {
59
- warnPercent: { type: "number", minimum: 1, maximum: 100 },
60
- globalMonthlyCents: { type: "number", minimum: 0 },
61
- },
62
- },
63
- agents: {
64
- type: "array",
65
- items: {
66
- type: "object",
67
- required: ["slug", "name", "adapter"],
68
- properties: {
69
- slug: { type: "string", minLength: 1, pattern: "^[a-z0-9-]+$" },
70
- name: { type: "string", minLength: 1 },
71
- adapter: { type: "string", minLength: 1 },
72
- model: { type: "string" },
73
- workdir: { type: "string" },
74
- maxConcurrentRuns: { type: "number", minimum: 1 },
75
- budgetMonthlyCents: { type: "number", minimum: 0 },
76
- canCreateAgents: { type: "boolean" },
77
- skipPermissions: { type: "boolean" },
78
- },
79
- },
80
- },
81
- channels: {
82
- type: "object",
83
- additionalProperties: {
84
- type: "object",
85
- required: ["enabled"],
86
- properties: {
87
- enabled: { type: "boolean" },
88
- },
89
- },
90
- },
91
- providers: {
92
- type: "object",
93
- additionalProperties: {
94
- type: "object",
95
- required: ["enabled"],
96
- properties: {
97
- enabled: { type: "boolean" },
98
- },
99
- },
100
- },
101
- },
102
- };
103
- let _validate = null;
104
- function getValidator() {
105
- if (!_validate) {
106
- _validate = ajv.compile(BOT_CONFIG_SCHEMA);
107
- }
108
- return _validate;
109
- }
110
- export function validateConfig(config) {
111
- const validate = getValidator();
112
- const valid = validate(config);
113
- if (valid)
114
- return [];
115
- return (validate.errors ?? []).map(err => ({
116
- path: err.instancePath || "/",
117
- message: err.message ?? "invalid",
118
- }));
119
- }
@@ -1,14 +0,0 @@
1
- import type { DataStore } from "./data-store.js";
2
- /**
3
- * Define default entity context rendering for botinabox core tables.
4
- * Call after defineCoreTables() and before or after init().
5
- *
6
- * Renders:
7
- * - agents/ — per-agent context (AGENT.md, PROJECTS.md if agent_project exists)
8
- * - users/ — per-user context (USER.md) — protected
9
- * - skills/ — per-skill context (SKILL.md)
10
- *
11
- * Apps can override by calling db.defineEntityContext() with the same table name
12
- * BEFORE calling defineCoreEntityContexts().
13
- */
14
- export declare function defineCoreEntityContexts(db: DataStore): void;