botinabox 2.4.2 → 2.5.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 +2 -2
  4. package/dist/channels/slack/index.d.ts +1 -1
  5. package/dist/{chat-pipeline-BWrtVqEP.d.ts → chat-pipeline-DuNX5WoL.d.ts} +3 -0
  6. package/dist/cli.js +0 -0
  7. package/dist/index.d.ts +11 -2
  8. package/dist/index.js +64 -27
  9. package/package.json +100 -99
  10. package/dist/channels/discord/adapter.d.ts +0 -32
  11. package/dist/channels/discord/adapter.js +0 -70
  12. package/dist/channels/discord/inbound.d.ts +0 -25
  13. package/dist/channels/discord/inbound.js +0 -24
  14. package/dist/channels/discord/models.d.ts +0 -8
  15. package/dist/channels/discord/models.js +0 -5
  16. package/dist/channels/discord/outbound.d.ts +0 -14
  17. package/dist/channels/discord/outbound.js +0 -38
  18. package/dist/channels/slack/adapter.d.ts +0 -33
  19. package/dist/channels/slack/adapter.js +0 -74
  20. package/dist/channels/slack/inbound.d.ts +0 -59
  21. package/dist/channels/slack/inbound.js +0 -96
  22. package/dist/channels/slack/models.d.ts +0 -9
  23. package/dist/channels/slack/models.js +0 -5
  24. package/dist/channels/slack/outbound.d.ts +0 -12
  25. package/dist/channels/slack/outbound.js +0 -18
  26. package/dist/channels/slack/transcribe.d.ts +0 -41
  27. package/dist/channels/slack/transcribe.js +0 -106
  28. package/dist/channels/webhook/adapter.d.ts +0 -23
  29. package/dist/channels/webhook/adapter.js +0 -86
  30. package/dist/channels/webhook/hmac.d.ts +0 -13
  31. package/dist/channels/webhook/hmac.js +0 -26
  32. package/dist/channels/webhook/models.d.ts +0 -9
  33. package/dist/channels/webhook/models.js +0 -5
  34. package/dist/channels/webhook/server.d.ts +0 -20
  35. package/dist/channels/webhook/server.js +0 -91
  36. package/dist/chat-pipeline-C-XlLGNl.d.ts +0 -648
  37. package/dist/chat-pipeline-CR1KF6eX.d.ts +0 -652
  38. package/dist/chat-pipeline-DisuC8SB.d.ts +0 -643
  39. package/dist/chunk-2LGXQPEA.js +0 -41
  40. package/dist/chunk-3X3YKI4T.js +0 -357
  41. package/dist/chunk-D47AIFOD.js +0 -351
  42. package/dist/chunk-DSNJKNEW.js +0 -328
  43. package/dist/chunk-GS2JFL6I.js +0 -144
  44. package/dist/chunk-J6S6QMUY.js +0 -144
  45. package/dist/chunk-QLA6YOFN.js +0 -22
  46. package/dist/chunk-UACT2WXX.js +0 -381
  47. package/dist/cli/templates/config.yml.d.ts +0 -7
  48. package/dist/cli/templates/config.yml.js +0 -61
  49. package/dist/cli/templates/env.d.ts +0 -1
  50. package/dist/cli/templates/env.js +0 -30
  51. package/dist/cli/templates/index.ts.d.ts +0 -2
  52. package/dist/cli/templates/index.ts.js +0 -30
  53. package/dist/cli/templates/package.json.d.ts +0 -5
  54. package/dist/cli/templates/package.json.js +0 -28
  55. package/dist/connector-DDahQw-2.d.ts +0 -63
  56. package/dist/connectors/google/calendar-connector.d.ts +0 -40
  57. package/dist/connectors/google/calendar-connector.js +0 -243
  58. package/dist/connectors/google/gmail-connector.d.ts +0 -42
  59. package/dist/connectors/google/gmail-connector.js +0 -345
  60. package/dist/connectors/google/oauth.d.ts +0 -48
  61. package/dist/connectors/google/oauth.js +0 -112
  62. package/dist/connectors/google/types.d.ts +0 -78
  63. package/dist/connectors/google/types.js +0 -2
  64. package/dist/core/chat/auto-discovery.d.ts +0 -16
  65. package/dist/core/chat/auto-discovery.js +0 -54
  66. package/dist/core/chat/channel-registry.d.ts +0 -45
  67. package/dist/core/chat/channel-registry.js +0 -96
  68. package/dist/core/chat/chat-pipeline.d.ts +0 -113
  69. package/dist/core/chat/chat-pipeline.js +0 -395
  70. package/dist/core/chat/chat-responder.d.ts +0 -90
  71. package/dist/core/chat/chat-responder.js +0 -185
  72. package/dist/core/chat/formatter.d.ts +0 -11
  73. package/dist/core/chat/formatter.js +0 -60
  74. package/dist/core/chat/index.d.ts +0 -24
  75. package/dist/core/chat/index.js +0 -18
  76. package/dist/core/chat/message-interpreter.d.ts +0 -91
  77. package/dist/core/chat/message-interpreter.js +0 -166
  78. package/dist/core/chat/message-store.d.ts +0 -66
  79. package/dist/core/chat/message-store.js +0 -131
  80. package/dist/core/chat/notification-queue.d.ts +0 -34
  81. package/dist/core/chat/notification-queue.js +0 -111
  82. package/dist/core/chat/pipeline.d.ts +0 -38
  83. package/dist/core/chat/pipeline.js +0 -89
  84. package/dist/core/chat/policies.d.ts +0 -16
  85. package/dist/core/chat/policies.js +0 -25
  86. package/dist/core/chat/routing.d.ts +0 -17
  87. package/dist/core/chat/routing.js +0 -36
  88. package/dist/core/chat/session-key.d.ts +0 -30
  89. package/dist/core/chat/session-key.js +0 -65
  90. package/dist/core/chat/session-manager.d.ts +0 -17
  91. package/dist/core/chat/session-manager.js +0 -23
  92. package/dist/core/chat/text-chunker.d.ts +0 -9
  93. package/dist/core/chat/text-chunker.js +0 -48
  94. package/dist/core/chat/triage-router.d.ts +0 -75
  95. package/dist/core/chat/triage-router.js +0 -142
  96. package/dist/core/chat/types.d.ts +0 -5
  97. package/dist/core/chat/types.js +0 -5
  98. package/dist/core/config/defaults.d.ts +0 -2
  99. package/dist/core/config/defaults.js +0 -38
  100. package/dist/core/config/index.d.ts +0 -6
  101. package/dist/core/config/index.js +0 -4
  102. package/dist/core/config/interpolate.d.ts +0 -5
  103. package/dist/core/config/interpolate.js +0 -27
  104. package/dist/core/config/loader.d.ts +0 -24
  105. package/dist/core/config/loader.js +0 -59
  106. package/dist/core/config/schema.d.ts +0 -5
  107. package/dist/core/config/schema.js +0 -119
  108. package/dist/core/data/core-entity-contexts.d.ts +0 -14
  109. package/dist/core/data/core-entity-contexts.js +0 -197
  110. package/dist/core/data/core-migrations.d.ts +0 -5
  111. package/dist/core/data/core-migrations.js +0 -45
  112. package/dist/core/data/core-schema.d.ts +0 -6
  113. package/dist/core/data/core-schema.js +0 -454
  114. package/dist/core/data/data-store.d.ts +0 -67
  115. package/dist/core/data/data-store.js +0 -218
  116. package/dist/core/data/domain-entity-contexts.d.ts +0 -29
  117. package/dist/core/data/domain-entity-contexts.js +0 -321
  118. package/dist/core/data/domain-schema.d.ts +0 -36
  119. package/dist/core/data/domain-schema.js +0 -323
  120. package/dist/core/data/index.d.ts +0 -7
  121. package/dist/core/data/index.js +0 -7
  122. package/dist/core/data/types.d.ts +0 -111
  123. package/dist/core/data/types.js +0 -1
  124. package/dist/core/hooks/hook-bus.d.ts +0 -18
  125. package/dist/core/hooks/hook-bus.js +0 -120
  126. package/dist/core/hooks/index.d.ts +0 -2
  127. package/dist/core/hooks/index.js +0 -1
  128. package/dist/core/hooks/types.d.ts +0 -19
  129. package/dist/core/hooks/types.js +0 -1
  130. package/dist/core/index.d.ts +0 -4
  131. package/dist/core/index.js +0 -4
  132. package/dist/core/llm/auto-discovery.d.ts +0 -11
  133. package/dist/core/llm/auto-discovery.js +0 -49
  134. package/dist/core/llm/cost-tracker.d.ts +0 -6
  135. package/dist/core/llm/cost-tracker.js +0 -38
  136. package/dist/core/llm/index.d.ts +0 -4
  137. package/dist/core/llm/index.js +0 -3
  138. package/dist/core/llm/model-router.d.ts +0 -25
  139. package/dist/core/llm/model-router.js +0 -49
  140. package/dist/core/llm/provider-registry.d.ts +0 -9
  141. package/dist/core/llm/provider-registry.js +0 -25
  142. package/dist/core/llm/types.d.ts +0 -2
  143. package/dist/core/llm/types.js +0 -2
  144. package/dist/core/orchestrator/adapters/api-adapter.d.ts +0 -34
  145. package/dist/core/orchestrator/adapters/api-adapter.js +0 -88
  146. package/dist/core/orchestrator/adapters/cli-adapter.d.ts +0 -22
  147. package/dist/core/orchestrator/adapters/cli-adapter.js +0 -69
  148. package/dist/core/orchestrator/adapters/deterministic-adapter.d.ts +0 -35
  149. package/dist/core/orchestrator/adapters/deterministic-adapter.js +0 -75
  150. package/dist/core/orchestrator/adapters/env-whitelist.d.ts +0 -4
  151. package/dist/core/orchestrator/adapters/env-whitelist.js +0 -27
  152. package/dist/core/orchestrator/adapters/output-extractor.d.ts +0 -11
  153. package/dist/core/orchestrator/adapters/output-extractor.js +0 -59
  154. package/dist/core/orchestrator/adapters/process-manager.d.ts +0 -15
  155. package/dist/core/orchestrator/adapters/process-manager.js +0 -26
  156. package/dist/core/orchestrator/adapters/tool-loop.d.ts +0 -22
  157. package/dist/core/orchestrator/adapters/tool-loop.js +0 -66
  158. package/dist/core/orchestrator/agent-registry.d.ts +0 -31
  159. package/dist/core/orchestrator/agent-registry.js +0 -135
  160. package/dist/core/orchestrator/budget-controller.d.ts +0 -19
  161. package/dist/core/orchestrator/budget-controller.js +0 -73
  162. package/dist/core/orchestrator/chain-guard.d.ts +0 -14
  163. package/dist/core/orchestrator/chain-guard.js +0 -23
  164. package/dist/core/orchestrator/circuit-breaker.d.ts +0 -65
  165. package/dist/core/orchestrator/circuit-breaker.js +0 -159
  166. package/dist/core/orchestrator/claude-stream-parser.d.ts +0 -31
  167. package/dist/core/orchestrator/claude-stream-parser.js +0 -99
  168. package/dist/core/orchestrator/config-revisions.d.ts +0 -6
  169. package/dist/core/orchestrator/config-revisions.js +0 -17
  170. package/dist/core/orchestrator/dependency-resolver.d.ts +0 -20
  171. package/dist/core/orchestrator/dependency-resolver.js +0 -78
  172. package/dist/core/orchestrator/governance-gate.d.ts +0 -110
  173. package/dist/core/orchestrator/governance-gate.js +0 -170
  174. package/dist/core/orchestrator/learning-pipeline.d.ts +0 -109
  175. package/dist/core/orchestrator/learning-pipeline.js +0 -249
  176. package/dist/core/orchestrator/loop-detector.d.ts +0 -51
  177. package/dist/core/orchestrator/loop-detector.js +0 -133
  178. package/dist/core/orchestrator/ndjson-logger.d.ts +0 -6
  179. package/dist/core/orchestrator/ndjson-logger.js +0 -18
  180. package/dist/core/orchestrator/permission-relay.d.ts +0 -72
  181. package/dist/core/orchestrator/permission-relay.js +0 -164
  182. package/dist/core/orchestrator/run-manager.d.ts +0 -31
  183. package/dist/core/orchestrator/run-manager.js +0 -178
  184. package/dist/core/orchestrator/scheduler.d.ts +0 -70
  185. package/dist/core/orchestrator/scheduler.js +0 -198
  186. package/dist/core/orchestrator/secret-store.d.ts +0 -57
  187. package/dist/core/orchestrator/secret-store.js +0 -171
  188. package/dist/core/orchestrator/session-manager.d.ts +0 -13
  189. package/dist/core/orchestrator/session-manager.js +0 -66
  190. package/dist/core/orchestrator/task-queue.d.ts +0 -34
  191. package/dist/core/orchestrator/task-queue.js +0 -83
  192. package/dist/core/orchestrator/template-interpolate.d.ts +0 -5
  193. package/dist/core/orchestrator/template-interpolate.js +0 -18
  194. package/dist/core/orchestrator/user-registry.d.ts +0 -47
  195. package/dist/core/orchestrator/user-registry.js +0 -76
  196. package/dist/core/orchestrator/wakeup-queue.d.ts +0 -9
  197. package/dist/core/orchestrator/wakeup-queue.js +0 -45
  198. package/dist/core/orchestrator/workflow-engine.d.ts +0 -47
  199. package/dist/core/orchestrator/workflow-engine.js +0 -204
  200. package/dist/core/security/audit.d.ts +0 -20
  201. package/dist/core/security/audit.js +0 -33
  202. package/dist/core/security/column-validator.d.ts +0 -20
  203. package/dist/core/security/column-validator.js +0 -37
  204. package/dist/core/security/index.d.ts +0 -5
  205. package/dist/core/security/index.js +0 -5
  206. package/dist/core/security/process-env.d.ts +0 -13
  207. package/dist/core/security/process-env.js +0 -49
  208. package/dist/core/security/sanitizer.d.ts +0 -11
  209. package/dist/core/security/sanitizer.js +0 -39
  210. package/dist/core/security/types.d.ts +0 -11
  211. package/dist/core/security/types.js +0 -1
  212. package/dist/core/update/auto-update.d.ts +0 -21
  213. package/dist/core/update/auto-update.js +0 -102
  214. package/dist/core/update/backup-manager.d.ts +0 -7
  215. package/dist/core/update/backup-manager.js +0 -24
  216. package/dist/core/update/index.d.ts +0 -8
  217. package/dist/core/update/index.js +0 -6
  218. package/dist/core/update/migration-hooks.d.ts +0 -11
  219. package/dist/core/update/migration-hooks.js +0 -10
  220. package/dist/core/update/types.d.ts +0 -11
  221. package/dist/core/update/types.js +0 -1
  222. package/dist/core/update/update-checker.d.ts +0 -11
  223. package/dist/core/update/update-checker.js +0 -63
  224. package/dist/core/update/update-manager.d.ts +0 -25
  225. package/dist/core/update/update-manager.js +0 -101
  226. package/dist/core/update/version-utils.d.ts +0 -6
  227. package/dist/core/update/version-utils.js +0 -34
  228. package/dist/gmail-connector-2FVYTQJH.js +0 -6
  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,49 +0,0 @@
1
- import { readdir, readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- /**
4
- * Scans nodeModulesPath/@botinabox/{pkg}/package.json for each package in the scope.
5
- * If pkg.botinabox?.type === 'provider', dynamically imports the package
6
- * and returns the discovered LLMProvider instances.
7
- *
8
- * The optional importer parameter allows injection for testing.
9
- */
10
- export async function discoverProviders(nodeModulesPath, importer = (name) => import(name)) {
11
- const scopeDir = join(nodeModulesPath, "@botinabox");
12
- let entries;
13
- try {
14
- entries = await readdir(scopeDir);
15
- }
16
- catch {
17
- // No @botinabox scope directory — gracefully return empty
18
- return [];
19
- }
20
- const providers = [];
21
- for (const entry of entries) {
22
- const pkgPath = join(scopeDir, entry, "package.json");
23
- let pkg;
24
- try {
25
- const raw = await readFile(pkgPath, "utf8");
26
- pkg = JSON.parse(raw);
27
- }
28
- catch {
29
- continue;
30
- }
31
- const botinabox = pkg.botinabox;
32
- if (botinabox?.type !== "provider") {
33
- continue;
34
- }
35
- const packageName = pkg.name;
36
- if (!packageName) {
37
- continue;
38
- }
39
- try {
40
- const mod = await importer(packageName);
41
- const provider = "default" in mod && mod.default ? mod.default : mod;
42
- providers.push(provider);
43
- }
44
- catch {
45
- // Failed to import — skip silently
46
- }
47
- }
48
- return providers;
49
- }
@@ -1,6 +0,0 @@
1
- import type { HookBus } from "../hooks/hook-bus.js";
2
- import type { DataStore } from "../data/data-store.js";
3
- import type { ModelInfo } from "./types.js";
4
- export declare function setupCostTracker(hooks: HookBus, db: DataStore, opts?: {
5
- modelCatalog?: ModelInfo[];
6
- }): void;
@@ -1,38 +0,0 @@
1
- function calculateCostCents(usage, model) {
2
- if (!model?.inputCostPerMToken)
3
- return 0;
4
- const inputCost = (usage.inputTokens / 1_000_000) * model.inputCostPerMToken;
5
- const outputCost = (usage.outputTokens / 1_000_000) * (model.outputCostPerMToken ?? 0);
6
- return Math.round((inputCost + outputCost) * 100);
7
- }
8
- export function setupCostTracker(hooks, db, opts) {
9
- const catalog = opts?.modelCatalog ?? [];
10
- hooks.register("run.completed", async (payload) => {
11
- const ctx = payload;
12
- if (!ctx.usage)
13
- return;
14
- const modelId = ctx.usage.model ?? ctx.model;
15
- const providerId = ctx.usage.provider ?? ctx.provider;
16
- if (!modelId || !providerId)
17
- return;
18
- const modelInfo = catalog.find((m) => m.id === modelId);
19
- const costCents = calculateCostCents(ctx.usage, modelInfo);
20
- await db.insert("cost_events", {
21
- agent_id: ctx.agentId ?? null,
22
- run_id: ctx.runId ?? null,
23
- provider: providerId,
24
- model: modelId,
25
- input_tokens: ctx.usage.inputTokens,
26
- output_tokens: ctx.usage.outputTokens,
27
- cost_cents: costCents,
28
- });
29
- if (ctx.agentId) {
30
- const agent = await db.get("agents", ctx.agentId);
31
- if (agent) {
32
- await db.update("agents", ctx.agentId, {
33
- spent_monthly_cents: (agent.spent_monthly_cents ?? 0) + costCents,
34
- });
35
- }
36
- }
37
- }, { priority: 20 });
38
- }
@@ -1,4 +0,0 @@
1
- export { ProviderRegistry } from "./provider-registry.js";
2
- export { ModelRouter } from "./model-router.js";
3
- export { discoverProviders } from "./auto-discovery.js";
4
- export type { LLMProvider, ModelInfo, ResolvedModel, ChatParams, ChatResult, ChatMessage, ContentBlock, TokenUsage, ToolDefinition, ToolUse, } from "./types.js";
@@ -1,3 +0,0 @@
1
- export { ProviderRegistry } from "./provider-registry.js";
2
- export { ModelRouter } from "./model-router.js";
3
- export { discoverProviders } from "./auto-discovery.js";
@@ -1,25 +0,0 @@
1
- import type { ModelConfig } from "../../shared/index.js";
2
- import type { ModelInfo, ResolvedModel } from "./types.js";
3
- import type { ProviderRegistry } from "./provider-registry.js";
4
- export declare class ModelRouter {
5
- private readonly registry;
6
- private readonly config;
7
- constructor(registry: ProviderRegistry, config: ModelConfig);
8
- /**
9
- * Resolve a model ID or alias to a ResolvedModel.
10
- * 1. Look up alias in config.aliases (or use as-is)
11
- * 2. Search all registered providers for a model with that id
12
- */
13
- resolve(modelIdOrAlias: string): ResolvedModel | undefined;
14
- /**
15
- * Try primary model, then each in config.fallbackChain.
16
- * Throws if none found.
17
- */
18
- resolveWithFallback(modelIdOrAlias: string): ResolvedModel;
19
- /**
20
- * Use config.routing[purpose] ?? config.default, then resolveWithFallback.
21
- */
22
- resolveForPurpose(purpose: string): ResolvedModel;
23
- /** Returns all models from all registered providers. */
24
- listAvailable(): ModelInfo[];
25
- }
@@ -1,49 +0,0 @@
1
- export class ModelRouter {
2
- registry;
3
- config;
4
- constructor(registry, config) {
5
- this.registry = registry;
6
- this.config = config;
7
- }
8
- /**
9
- * Resolve a model ID or alias to a ResolvedModel.
10
- * 1. Look up alias in config.aliases (or use as-is)
11
- * 2. Search all registered providers for a model with that id
12
- */
13
- resolve(modelIdOrAlias) {
14
- const modelId = this.config.aliases[modelIdOrAlias] ?? modelIdOrAlias;
15
- for (const provider of this.registry.list()) {
16
- const found = provider.models.find((m) => m.id === modelId);
17
- if (found) {
18
- return { provider: provider.id, model: found.id };
19
- }
20
- }
21
- return undefined;
22
- }
23
- /**
24
- * Try primary model, then each in config.fallbackChain.
25
- * Throws if none found.
26
- */
27
- resolveWithFallback(modelIdOrAlias) {
28
- const primary = this.resolve(modelIdOrAlias);
29
- if (primary)
30
- return primary;
31
- for (const fallback of this.config.fallbackChain) {
32
- const resolved = this.resolve(fallback);
33
- if (resolved)
34
- return resolved;
35
- }
36
- throw new Error(`No available model found for "${modelIdOrAlias}" and fallback chain [${this.config.fallbackChain.join(", ")}]`);
37
- }
38
- /**
39
- * Use config.routing[purpose] ?? config.default, then resolveWithFallback.
40
- */
41
- resolveForPurpose(purpose) {
42
- const modelIdOrAlias = this.config.routing[purpose] ?? this.config.default;
43
- return this.resolveWithFallback(modelIdOrAlias);
44
- }
45
- /** Returns all models from all registered providers. */
46
- listAvailable() {
47
- return this.registry.listModels();
48
- }
49
- }
@@ -1,9 +0,0 @@
1
- import type { LLMProvider, ModelInfo } from "./types.js";
2
- export declare class ProviderRegistry {
3
- private providers;
4
- register(provider: LLMProvider): void;
5
- unregister(id: string): void;
6
- get(id: string): LLMProvider | undefined;
7
- list(): LLMProvider[];
8
- listModels(): ModelInfo[];
9
- }
@@ -1,25 +0,0 @@
1
- export class ProviderRegistry {
2
- providers = new Map();
3
- register(provider) {
4
- if (this.providers.has(provider.id)) {
5
- throw new Error(`Provider already registered: ${provider.id}`);
6
- }
7
- this.providers.set(provider.id, provider);
8
- }
9
- unregister(id) {
10
- this.providers.delete(id);
11
- }
12
- get(id) {
13
- return this.providers.get(id);
14
- }
15
- list() {
16
- return Array.from(this.providers.values());
17
- }
18
- listModels() {
19
- const models = [];
20
- for (const provider of this.providers.values()) {
21
- models.push(...provider.models);
22
- }
23
- return models;
24
- }
25
- }
@@ -1,2 +0,0 @@
1
- /** LLM module types — re-exports from @botinabox/shared */
2
- export type { LLMProvider, ModelInfo, ResolvedModel, ChatParams, ChatResult, ChatMessage, ContentBlock, TokenUsage, ToolDefinition, ToolUse, } from "../../shared/index.js";
@@ -1,2 +0,0 @@
1
- /** LLM module types — re-exports from @botinabox/shared */
2
- export {};
@@ -1,34 +0,0 @@
1
- import type { ModelRouter } from '../../llm/model-router.js';
2
- import type { ChatMessage, TokenUsage } from "../../../shared/index.js";
3
- export declare class ApiExecutionAdapter {
4
- private modelRouter;
5
- readonly type = "api";
6
- constructor(modelRouter: ModelRouter);
7
- execute(ctx: {
8
- agent: {
9
- id: string;
10
- model?: string;
11
- adapter_config?: string;
12
- };
13
- task: {
14
- description?: string;
15
- context?: string;
16
- };
17
- sessionParams?: {
18
- history?: ChatMessage[];
19
- };
20
- contextFiles?: string[];
21
- abortSignal?: AbortSignal;
22
- onLog?: (stream: 'stdout' | 'stderr', chunk: string) => void;
23
- }): Promise<{
24
- output: string;
25
- exitCode: number;
26
- usage: TokenUsage & {
27
- provider: string;
28
- model: string;
29
- };
30
- sessionParams: {
31
- history: ChatMessage[];
32
- };
33
- }>;
34
- }
@@ -1,88 +0,0 @@
1
- import { readFileSync, existsSync } from 'node:fs';
2
- import { toolLoop } from './tool-loop.js';
3
- export class ApiExecutionAdapter {
4
- modelRouter;
5
- type = 'api';
6
- constructor(modelRouter) {
7
- this.modelRouter = modelRouter;
8
- }
9
- async execute(ctx) {
10
- const modelId = ctx.agent.model ?? 'default';
11
- const resolved = this.modelRouter.resolve(modelId) ?? this.modelRouter.resolveForPurpose('default');
12
- const { provider, model } = resolved;
13
- // Get the LLM provider
14
- const registry = this.modelRouter.registry;
15
- const providerImpl = registry.list().find((p) => p.id === provider);
16
- if (!providerImpl) {
17
- throw new Error(`Provider not found: ${provider}`);
18
- }
19
- // Build system prompt
20
- let systemPrompt = '';
21
- // Load context files
22
- if (ctx.contextFiles && ctx.contextFiles.length > 0) {
23
- const fileContents = [];
24
- for (const filePath of ctx.contextFiles) {
25
- if (existsSync(filePath)) {
26
- const content = readFileSync(filePath, 'utf8');
27
- fileContents.push(`<file path="${filePath}">\n${content}\n</file>`);
28
- }
29
- }
30
- if (fileContents.length > 0) {
31
- systemPrompt = fileContents.join('\n\n');
32
- }
33
- }
34
- // Build messages from history + task
35
- const messages = [
36
- ...(ctx.sessionParams?.history ?? []),
37
- ];
38
- const taskContent = [
39
- ctx.task.description,
40
- ctx.task.context,
41
- ].filter(Boolean).join('\n\n');
42
- if (taskContent) {
43
- messages.push({ role: 'user', content: taskContent });
44
- }
45
- let outputText = '';
46
- let totalUsage = { inputTokens: 0, outputTokens: 0 };
47
- const history = [...messages];
48
- try {
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- const callLLM = (params) => providerImpl.chat(params);
51
- for await (const event of toolLoop({
52
- model,
53
- messages,
54
- systemPrompt: systemPrompt || undefined,
55
- maxIterations: 20,
56
- signal: ctx.abortSignal,
57
- }, callLLM)) {
58
- if (event.type === 'text') {
59
- outputText += event.content;
60
- ctx.onLog?.('stdout', event.content);
61
- }
62
- else if (event.type === 'done') {
63
- totalUsage = {
64
- inputTokens: (totalUsage.inputTokens ?? 0) + (event.result.usage.inputTokens ?? 0),
65
- outputTokens: (totalUsage.outputTokens ?? 0) + (event.result.usage.outputTokens ?? 0),
66
- };
67
- }
68
- }
69
- history.push({ role: 'assistant', content: outputText });
70
- return {
71
- output: outputText,
72
- exitCode: 0,
73
- usage: { ...totalUsage, provider, model },
74
- sessionParams: { history },
75
- };
76
- }
77
- catch (err) {
78
- const message = err instanceof Error ? err.message : String(err);
79
- ctx.onLog?.('stderr', message);
80
- return {
81
- output: message,
82
- exitCode: 1,
83
- usage: { inputTokens: 0, outputTokens: 0, provider, model },
84
- sessionParams: { history },
85
- };
86
- }
87
- }
88
- }
@@ -1,22 +0,0 @@
1
- export declare class CliExecutionAdapter {
2
- readonly type = "cli";
3
- execute(ctx: {
4
- agent: {
5
- id: string;
6
- cwd?: string;
7
- adapter_config?: string;
8
- skip_permissions?: boolean;
9
- };
10
- task: {
11
- title: string;
12
- description?: string;
13
- context?: string;
14
- };
15
- logPath?: string;
16
- onLog?: (stream: string, chunk: string) => void;
17
- abortSignal?: AbortSignal;
18
- }): Promise<{
19
- output: string;
20
- exitCode: number;
21
- }>;
22
- }
@@ -1,69 +0,0 @@
1
- import { createWriteStream } from 'node:fs';
2
- import { spawnProcess, killProcessGroup } from './process-manager.js';
3
- import { extractOutput } from './output-extractor.js';
4
- export class CliExecutionAdapter {
5
- type = 'cli';
6
- async execute(ctx) {
7
- const cwd = ctx.agent.cwd ?? process.cwd();
8
- let config = {};
9
- if (ctx.agent.adapter_config) {
10
- try {
11
- config = JSON.parse(ctx.agent.adapter_config);
12
- }
13
- catch {
14
- // Ignore invalid config
15
- }
16
- }
17
- const skipPermissions = ctx.agent.skip_permissions ?? config['skip_permissions'] ?? false;
18
- const args = [];
19
- if (skipPermissions) {
20
- args.push('--dangerously-skip-permissions');
21
- }
22
- // Build prompt from task
23
- const prompt = [
24
- ctx.task.title,
25
- ctx.task.description,
26
- ctx.task.context,
27
- ]
28
- .filter(Boolean)
29
- .join('\n\n');
30
- // Append prompt as final argument (--print mode)
31
- args.push('--print', prompt);
32
- const child = spawnProcess('claude', args, { cwd });
33
- const stdoutChunks = [];
34
- let logStream = null;
35
- if (ctx.logPath) {
36
- logStream = createWriteStream(ctx.logPath, { flags: 'a' });
37
- }
38
- // Set up abort handler
39
- const abortHandler = () => {
40
- if (child.pid != null) {
41
- killProcessGroup(child.pid);
42
- }
43
- };
44
- ctx.abortSignal?.addEventListener('abort', abortHandler);
45
- child.stdout?.on('data', (chunk) => {
46
- stdoutChunks.push(chunk);
47
- const str = chunk.toString('utf8');
48
- ctx.onLog?.('stdout', str);
49
- logStream?.write(chunk);
50
- });
51
- child.stderr?.on('data', (chunk) => {
52
- const str = chunk.toString('utf8');
53
- ctx.onLog?.('stderr', str);
54
- });
55
- const exitCode = await new Promise((resolve) => {
56
- child.on('close', (code) => {
57
- resolve(code ?? 1);
58
- });
59
- child.on('error', () => {
60
- resolve(1);
61
- });
62
- });
63
- ctx.abortSignal?.removeEventListener('abort', abortHandler);
64
- logStream?.end();
65
- const rawOutput = Buffer.concat(stdoutChunks).toString('utf8');
66
- const output = extractOutput(rawOutput);
67
- return { output, exitCode };
68
- }
69
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * DeterministicAdapter — executes scripts without LLM calls.
3
- * Story 6.1
4
- *
5
- * For tasks that don't require reasoning: routing, validation,
6
- * data fetching, file transforms. Runs a user-specified command
7
- * (Python, Node, bash) as a subprocess with task context on stdin.
8
- */
9
- export interface DeterministicConfig {
10
- command: string;
11
- args?: string[];
12
- env?: Record<string, string>;
13
- timeoutMs?: number;
14
- inputMode?: 'stdin' | 'arg';
15
- }
16
- export declare class DeterministicAdapter {
17
- readonly type = "deterministic";
18
- execute(ctx: {
19
- agent: {
20
- id: string;
21
- cwd?: string;
22
- adapter_config?: string;
23
- };
24
- task: {
25
- title: string;
26
- description?: string;
27
- context?: string;
28
- };
29
- abortSignal?: AbortSignal;
30
- onLog?: (stream: string, chunk: string) => void;
31
- }): Promise<{
32
- output: string;
33
- exitCode: number;
34
- }>;
35
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * DeterministicAdapter — executes scripts without LLM calls.
3
- * Story 6.1
4
- *
5
- * For tasks that don't require reasoning: routing, validation,
6
- * data fetching, file transforms. Runs a user-specified command
7
- * (Python, Node, bash) as a subprocess with task context on stdin.
8
- */
9
- import { spawnProcess, killProcessGroup } from './process-manager.js';
10
- const DEFAULT_TIMEOUT_MS = 30_000;
11
- export class DeterministicAdapter {
12
- type = 'deterministic';
13
- async execute(ctx) {
14
- const cwd = ctx.agent.cwd ?? process.cwd();
15
- let config = { command: 'echo' };
16
- if (ctx.agent.adapter_config) {
17
- try {
18
- config = JSON.parse(ctx.agent.adapter_config);
19
- }
20
- catch {
21
- throw new Error('Invalid adapter_config for deterministic adapter');
22
- }
23
- }
24
- if (!config.command) {
25
- throw new Error('Deterministic adapter requires a command');
26
- }
27
- const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
28
- // Build task payload as JSON for the script
29
- const payload = JSON.stringify({
30
- taskId: ctx.task.title,
31
- description: ctx.task.description ?? '',
32
- context: ctx.task.context ?? '',
33
- });
34
- // Build args — append payload as final arg in 'arg' mode
35
- const args = [...(config.args ?? [])];
36
- if (config.inputMode === 'arg') {
37
- args.push(payload);
38
- }
39
- const child = spawnProcess(config.command, args, {
40
- cwd,
41
- extraEnv: config.env,
42
- });
43
- // Write payload to stdin in default mode
44
- if (config.inputMode !== 'arg' && child.stdin) {
45
- child.stdin.write(payload);
46
- child.stdin.end();
47
- }
48
- const stdoutChunks = [];
49
- child.stdout?.on('data', (chunk) => {
50
- stdoutChunks.push(chunk);
51
- ctx.onLog?.('stdout', chunk.toString('utf8'));
52
- });
53
- child.stderr?.on('data', (chunk) => {
54
- ctx.onLog?.('stderr', chunk.toString('utf8'));
55
- });
56
- // Set up abort + timeout
57
- const abortHandler = () => {
58
- if (child.pid != null)
59
- killProcessGroup(child.pid);
60
- };
61
- ctx.abortSignal?.addEventListener('abort', abortHandler);
62
- const timeout = setTimeout(() => {
63
- if (child.pid != null)
64
- killProcessGroup(child.pid);
65
- }, timeoutMs);
66
- const exitCode = await new Promise((resolve) => {
67
- child.on('close', (code) => resolve(code ?? 1));
68
- child.on('error', () => resolve(1));
69
- });
70
- clearTimeout(timeout);
71
- ctx.abortSignal?.removeEventListener('abort', abortHandler);
72
- const output = Buffer.concat(stdoutChunks).toString('utf8');
73
- return { output, exitCode };
74
- }
75
- }
@@ -1,4 +0,0 @@
1
- /**
2
- * Filter the process environment to only allowed keys, then merge in extras.
3
- */
4
- export declare function filterEnv(allowed?: string[], extra?: Record<string, string>): Record<string, string>;
@@ -1,27 +0,0 @@
1
- const DEFAULT_ALLOWED_ENV = [
2
- 'PATH',
3
- 'HOME',
4
- 'SHELL',
5
- 'LANG',
6
- 'USER',
7
- 'TERM',
8
- 'NODE_PATH',
9
- 'TMPDIR',
10
- ];
11
- /**
12
- * Filter the process environment to only allowed keys, then merge in extras.
13
- */
14
- export function filterEnv(allowed, extra) {
15
- const allowedKeys = allowed ?? DEFAULT_ALLOWED_ENV;
16
- const result = {};
17
- for (const key of allowedKeys) {
18
- const val = process.env[key];
19
- if (val !== undefined) {
20
- result[key] = val;
21
- }
22
- }
23
- if (extra) {
24
- Object.assign(result, extra);
25
- }
26
- return result;
27
- }
@@ -1,11 +0,0 @@
1
- export declare const MAX_OUTPUT_BYTES: number;
2
- /**
3
- * Extract final output from NDJSON log content.
4
- * Finds last line with {type:'result'} or {role:'assistant'}.
5
- * Caps at 4MB.
6
- */
7
- export declare function extractOutput(ndjsonContent: string): string;
8
- /**
9
- * Read file and extract output.
10
- */
11
- export declare function extractOutputFromFile(logPath: string): string;
@@ -1,59 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- export const MAX_OUTPUT_BYTES = 4 * 1024 * 1024; // 4MB
3
- /**
4
- * Extract final output from NDJSON log content.
5
- * Finds last line with {type:'result'} or {role:'assistant'}.
6
- * Caps at 4MB.
7
- */
8
- export function extractOutput(ndjsonContent) {
9
- const lines = ndjsonContent.split('\n').filter((l) => l.trim().length > 0);
10
- let lastOutput;
11
- for (const line of lines) {
12
- try {
13
- const parsed = JSON.parse(line);
14
- if (parsed['type'] === 'result') {
15
- const content = parsed['result'] ?? parsed['content'] ?? parsed['output'];
16
- if (typeof content === 'string') {
17
- lastOutput = content;
18
- }
19
- }
20
- else if (parsed['role'] === 'assistant') {
21
- const content = parsed['content'];
22
- if (typeof content === 'string') {
23
- lastOutput = content;
24
- }
25
- }
26
- else if (parsed['type'] === 'assistant') {
27
- const message = parsed['message'];
28
- if (message && Array.isArray(message['content'])) {
29
- for (const block of message['content']) {
30
- if (typeof block === 'object' &&
31
- block !== null &&
32
- block['type'] === 'text') {
33
- lastOutput = block['text'];
34
- }
35
- }
36
- }
37
- }
38
- }
39
- catch {
40
- // Skip non-JSON lines
41
- }
42
- }
43
- if (!lastOutput) {
44
- // Fall back to raw content
45
- lastOutput = ndjsonContent;
46
- }
47
- // Cap at 4MB
48
- if (Buffer.byteLength(lastOutput, 'utf8') > MAX_OUTPUT_BYTES) {
49
- return Buffer.from(lastOutput, 'utf8').subarray(0, MAX_OUTPUT_BYTES).toString('utf8');
50
- }
51
- return lastOutput;
52
- }
53
- /**
54
- * Read file and extract output.
55
- */
56
- export function extractOutputFromFile(logPath) {
57
- const content = readFileSync(logPath, 'utf8');
58
- return extractOutput(content);
59
- }