botinabox 1.10.0 → 2.0.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.
package/dist/index.d.ts CHANGED
@@ -1831,13 +1831,15 @@ interface ToolDefinition {
1831
1831
  description: string;
1832
1832
  input_schema: Record<string, unknown>;
1833
1833
  }
1834
- type ToolHandler = (input: Record<string, unknown>, context: {
1834
+ type ToolHandler = (input: Record<string, unknown>, context: ToolContext) => Promise<string>;
1835
+ interface ToolContext {
1835
1836
  taskId: string;
1836
1837
  agentId: string;
1837
1838
  hooks: HookBus;
1839
+ db: DataStore;
1838
1840
  /** Resolve a relative file path to an absolute path (environment-aware). */
1839
1841
  resolveFilePath?: (path: string) => string;
1840
- }) => Promise<string>;
1842
+ }
1841
1843
  interface ExecutionEngineConfig {
1842
1844
  /** Anthropic client instance */
1843
1845
  client: {
@@ -1891,6 +1893,105 @@ declare const readFileTool: {
1891
1893
  handler: ToolHandler;
1892
1894
  };
1893
1895
 
1896
+ /**
1897
+ * Built-in tools: File operations — list, register, send, read.
1898
+ */
1899
+
1900
+ declare const listFilesTool: {
1901
+ definition: ToolDefinition;
1902
+ handler: ToolHandler;
1903
+ };
1904
+ declare const registerFileTool: {
1905
+ definition: ToolDefinition;
1906
+ handler: ToolHandler;
1907
+ };
1908
+
1909
+ declare const dispatchTaskTool: {
1910
+ definition: ToolDefinition;
1911
+ handler: ToolHandler;
1912
+ };
1913
+ declare const cancelTaskTool: {
1914
+ definition: ToolDefinition;
1915
+ handler: ToolHandler;
1916
+ };
1917
+ declare const reassignTaskTool: {
1918
+ definition: ToolDefinition;
1919
+ handler: ToolHandler;
1920
+ };
1921
+
1922
+ /**
1923
+ * Built-in tools: System awareness — task status, agent status, system health.
1924
+ */
1925
+
1926
+ declare const getTaskStatusTool: {
1927
+ definition: ToolDefinition;
1928
+ handler: ToolHandler;
1929
+ };
1930
+ declare const getAgentStatusTool: {
1931
+ definition: ToolDefinition;
1932
+ handler: ToolHandler;
1933
+ };
1934
+ declare const getSystemStatusTool: {
1935
+ definition: ToolDefinition;
1936
+ handler: ToolHandler;
1937
+ };
1938
+ declare const getActiveTasksTool: {
1939
+ definition: ToolDefinition;
1940
+ handler: ToolHandler;
1941
+ };
1942
+
1943
+ /**
1944
+ * Built-in tools: Entity lookup — agents, projects, details.
1945
+ */
1946
+
1947
+ declare const listAgentsTool: {
1948
+ definition: ToolDefinition;
1949
+ handler: ToolHandler;
1950
+ };
1951
+ declare const listProjectsTool: {
1952
+ definition: ToolDefinition;
1953
+ handler: ToolHandler;
1954
+ };
1955
+ declare const getAgentDetailTool: {
1956
+ definition: ToolDefinition;
1957
+ handler: ToolHandler;
1958
+ };
1959
+
1960
+ /**
1961
+ * Built-in tools: Communication — send messages, read conversations, search history.
1962
+ * All messaging is channel-agnostic: slack, email, discord, sms — same tools.
1963
+ */
1964
+
1965
+ declare const sendMessageTool: {
1966
+ definition: ToolDefinition;
1967
+ handler: ToolHandler;
1968
+ };
1969
+ declare const addTaskCommentTool: {
1970
+ definition: ToolDefinition;
1971
+ handler: ToolHandler;
1972
+ };
1973
+ declare const readConversationTool: {
1974
+ definition: ToolDefinition;
1975
+ handler: ToolHandler;
1976
+ };
1977
+ declare const searchConversationTool: {
1978
+ definition: ToolDefinition;
1979
+ handler: ToolHandler;
1980
+ };
1981
+
1982
+ /**
1983
+ * Built-in tools: Entity creation — agents, projects.
1984
+ */
1985
+
1986
+ declare const createAgentTool: {
1987
+ definition: ToolDefinition;
1988
+ handler: ToolHandler;
1989
+ };
1990
+ declare const createProjectTool: {
1991
+ definition: ToolDefinition;
1992
+ handler: ToolHandler;
1993
+ };
1994
+
1894
1995
  interface SecretInput {
1895
1996
  name: string;
1896
1997
  type?: string;
@@ -1979,4 +2080,4 @@ declare function isLoginRequired(stdout: string): boolean;
1979
2080
  /** Rewrite local image paths to prevent CLI auto-embedding as vision content. */
1980
2081
  declare function deactivateLocalImagePaths(prompt: string): string;
1981
2082
 
1982
- export { AGENT_STATUSES, type AgentConfig, type AgentDefinition, type AgentFilter, type AgentRecord, AgentRegistry, type AgentStatus, ApiExecutionAdapter, type ApprovalResponse, type ApprovalStatus, AuditEmitter, type AuditEvent, BackupManager, type BotConfig, BreakerState, type BudgetCheck, type BudgetConfig, BudgetController, CORE_MIGRATIONS, ChannelAdapter, ChannelRegistry, ChannelRegistryError, ChatMessage, ChatResponderConfig, ChatSessionManager, CircuitBreaker, type CircuitBreakerConfig, CliExecutionAdapter, type ColumnValidator, ColumnValidatorImpl, type ConfigLoadError, type ConfigLoadResult, ConnectorConfig, DEFAULTS, DEFAULT_CONFIG, type DataConfig, DataStore, type DefaultLLMCallConfig, DeterministicAdapter, type DeterministicConfig, type DomainEntityContextOptions, type DomainSchemaOptions, DriftGate, EVENTS, type EntityColumnDef, type EntityConfig, type ExecutionAdapter, type ExecutionEngineConfig, type FeedbackEntry, type GateFinding, type GateInput, type GateResult, GateRunner, type GateVerdict, GovernanceGate, HealthStatus, HookBus, InboundMessage, LLMProvider, LearningPipeline, type LearningPipelineConfig, type LoopDetection, LoopDetector, type LoopDetectorConfig, LoopType, MAX_CHAIN_DEPTH, MessagePipeline, type ModelConfig, ModelInfo, ModelRouter, NdjsonLogger, NotificationQueue, type PackageMigration, type PackageUpdate, type ParsedStream, type PermissionPrompt, type PermissionProvider, PermissionRelay, type PermissionRelayConfig, type PlaybookEntry, ProviderRegistry, QAGate, QualityGate, RUN_STATUSES, type RenderConfig, ResolvedModel, type RetryPolicy, type RunContext, RunManager, type RunResult, type RunStatus, type SanitizerOptions, type Schedule, type ScheduleDef, Scheduler, type SchemaError, type SecretInput, type SecretMeta, SecretStore, type SecurityConfig, SessionKey, SessionManager, type SkillEntry, type StepRef, type SystemContextOptions, TASK_STATUSES, type TaskDefinition, TaskQueue, type TaskRecord, type TaskStatus, TokenUsage, type ToolDefinition, type ToolHandler, UpdateChecker, type UpdateConfig, UpdateManager, type UpdateManifest, type UsageSummary, type User, type UserInput, UserRegistry, WakeupQueue, type WorkflowConfigEntry, type WorkflowDefinition$1 as WorkflowDefinition, WorkflowEngine, type WorkflowRunRecord, type WorkflowRunStatus, type WorkflowStep$1 as WorkflowStep, type WorkflowStepConfig, type WorkflowTrigger, _resetConfig, areDependenciesMet, autoUpdate, buildAgentBindings, buildChainOrigin, buildProcessEnv, buildSystemContext, checkAllowlist, checkChainDepth, checkMentionGate, chunkText, classifyUpdate, compareVersions, createConfigRevision, createDefaultLLMCall, deactivateLocalImagePaths, defineCoreEntityContexts, defineCoreTables, defineDomainEntityContexts, defineDomainTables, detectCycle, discoverChannels, discoverProviders, formatText, getConfig, initConfig, interpolate, interpolateEnv, isLoginRequired, isMaxTurns, loadConfig, parseClaudeStream, parseVersion, readFileTool, registerExecutionEngine, runPackageMigrations, sanitize, sendFileTool, topologicalSort, truncateAtWord, validateConfig };
2083
+ export { AGENT_STATUSES, type AgentConfig, type AgentDefinition, type AgentFilter, type AgentRecord, AgentRegistry, type AgentStatus, ApiExecutionAdapter, type ApprovalResponse, type ApprovalStatus, AuditEmitter, type AuditEvent, BackupManager, type BotConfig, BreakerState, type BudgetCheck, type BudgetConfig, BudgetController, CORE_MIGRATIONS, ChannelAdapter, ChannelRegistry, ChannelRegistryError, ChatMessage, ChatResponderConfig, ChatSessionManager, CircuitBreaker, type CircuitBreakerConfig, CliExecutionAdapter, type ColumnValidator, ColumnValidatorImpl, type ConfigLoadError, type ConfigLoadResult, ConnectorConfig, DEFAULTS, DEFAULT_CONFIG, type DataConfig, DataStore, type DefaultLLMCallConfig, DeterministicAdapter, type DeterministicConfig, type DomainEntityContextOptions, type DomainSchemaOptions, DriftGate, EVENTS, type EntityColumnDef, type EntityConfig, type ExecutionAdapter, type ExecutionEngineConfig, type FeedbackEntry, type GateFinding, type GateInput, type GateResult, GateRunner, type GateVerdict, GovernanceGate, HealthStatus, HookBus, InboundMessage, LLMProvider, LearningPipeline, type LearningPipelineConfig, type LoopDetection, LoopDetector, type LoopDetectorConfig, LoopType, MAX_CHAIN_DEPTH, MessagePipeline, type ModelConfig, ModelInfo, ModelRouter, NdjsonLogger, NotificationQueue, type PackageMigration, type PackageUpdate, type ParsedStream, type PermissionPrompt, type PermissionProvider, PermissionRelay, type PermissionRelayConfig, type PlaybookEntry, ProviderRegistry, QAGate, QualityGate, RUN_STATUSES, type RenderConfig, ResolvedModel, type RetryPolicy, type RunContext, RunManager, type RunResult, type RunStatus, type SanitizerOptions, type Schedule, type ScheduleDef, Scheduler, type SchemaError, type SecretInput, type SecretMeta, SecretStore, type SecurityConfig, SessionKey, SessionManager, type SkillEntry, type StepRef, type SystemContextOptions, TASK_STATUSES, type TaskDefinition, TaskQueue, type TaskRecord, type TaskStatus, TokenUsage, type ToolContext, type ToolDefinition, type ToolHandler, UpdateChecker, type UpdateConfig, UpdateManager, type UpdateManifest, type UsageSummary, type User, type UserInput, UserRegistry, WakeupQueue, type WorkflowConfigEntry, type WorkflowDefinition$1 as WorkflowDefinition, WorkflowEngine, type WorkflowRunRecord, type WorkflowRunStatus, type WorkflowStep$1 as WorkflowStep, type WorkflowStepConfig, type WorkflowTrigger, _resetConfig, addTaskCommentTool, areDependenciesMet, autoUpdate, buildAgentBindings, buildChainOrigin, buildProcessEnv, buildSystemContext, cancelTaskTool, checkAllowlist, checkChainDepth, checkMentionGate, chunkText, classifyUpdate, compareVersions, createAgentTool, createConfigRevision, createDefaultLLMCall, createProjectTool, deactivateLocalImagePaths, defineCoreEntityContexts, defineCoreTables, defineDomainEntityContexts, defineDomainTables, detectCycle, discoverChannels, discoverProviders, dispatchTaskTool, formatText, getActiveTasksTool, getAgentDetailTool, getAgentStatusTool, getConfig, getSystemStatusTool, getTaskStatusTool, initConfig, interpolate, interpolateEnv, isLoginRequired, isMaxTurns, listAgentsTool, listFilesTool, listProjectsTool, loadConfig, parseClaudeStream, parseVersion, readConversationTool, readFileTool, reassignTaskTool, registerExecutionEngine, registerFileTool, runPackageMigrations, sanitize, searchConversationTool, sendFileTool, sendMessageTool, topologicalSort, truncateAtWord, validateConfig };
package/dist/index.js CHANGED
@@ -6092,10 +6092,16 @@ async function registerExecutionEngine(opts) {
6092
6092
  const runId = await runs.startRun(assigneeId, taskId, "api");
6093
6093
  const prompt = task.description ?? task.title ?? "";
6094
6094
  try {
6095
+ const toolListing = toolDefs.length > 0 ? `
6096
+ ## Available Tools
6097
+ ${toolDefs.map((t) => `- **${t.name}**: ${t.description}`).join("\n")}
6098
+
6099
+ Use your tools to take action. Do NOT describe what you would do \u2014 call the tool.` : "";
6095
6100
  const systemPrompt = [
6096
6101
  `You are ${agent.name}, an AI agent with role: ${agent.role}.`,
6097
6102
  systemContext ? `
6098
6103
  ${systemContext}` : "",
6104
+ toolListing,
6099
6105
  config.systemPromptSuffix ?? ""
6100
6106
  ].filter(Boolean).join("\n");
6101
6107
  const messages = [{ role: "user", content: prompt }];
@@ -6126,7 +6132,7 @@ ${systemContext}` : "",
6126
6132
  try {
6127
6133
  const result = await handler(
6128
6134
  toolUse.input,
6129
- { taskId, agentId: assigneeId, hooks, resolveFilePath: config.resolveFilePath }
6135
+ { taskId, agentId: assigneeId, hooks, db, resolveFilePath: config.resolveFilePath }
6130
6136
  );
6131
6137
  toolResults.push({ type: "tool_result", tool_use_id: toolUse.id, content: result });
6132
6138
  } catch (err) {
@@ -6251,6 +6257,461 @@ function parseZipEntries(buf) {
6251
6257
  return entries;
6252
6258
  }
6253
6259
 
6260
+ // src/core/orchestrator/tools/file-ops.ts
6261
+ var listFilesTool = {
6262
+ definition: {
6263
+ name: "list_files",
6264
+ description: 'List all files in the system. Shows file names, types, access levels, and paths. Use when the user asks "what files do we have?" or needs to find a document.',
6265
+ input_schema: {
6266
+ type: "object",
6267
+ properties: {
6268
+ search: { type: "string", description: "Optional search term to filter files by name" }
6269
+ }
6270
+ }
6271
+ },
6272
+ handler: async (input, ctx) => {
6273
+ const files = await ctx.db.query("file");
6274
+ let results = files.filter((f) => !f.deleted_at);
6275
+ const search = input.search;
6276
+ if (search) {
6277
+ const lower = search.toLowerCase();
6278
+ results = results.filter((f) => (f.name ?? "").toLowerCase().includes(lower));
6279
+ }
6280
+ if (results.length === 0) return "No files found.";
6281
+ return results.map((f) => `- ${f.name} (${f.access_level ?? "internal"}) | ${f.file_path ?? "no path"}`).join("\n");
6282
+ }
6283
+ };
6284
+ var registerFileTool = {
6285
+ definition: {
6286
+ name: "register_file",
6287
+ description: "Register a new file in the vault for tracking. Use when a new document needs to be tracked by the system.",
6288
+ input_schema: {
6289
+ type: "object",
6290
+ properties: {
6291
+ name: { type: "string", description: "File name/title" },
6292
+ file_path: { type: "string", description: "Path to the file on disk" },
6293
+ mime_type: { type: "string", description: "MIME type (e.g. application/pdf)" },
6294
+ access_level: { type: "string", description: "Access level: internal, confidential, public" }
6295
+ },
6296
+ required: ["name", "file_path"]
6297
+ }
6298
+ },
6299
+ handler: async (input, ctx) => {
6300
+ const orgs = await ctx.db.query("org");
6301
+ const orgId = orgs[0]?.id ?? "";
6302
+ const row = await ctx.db.insert("file", {
6303
+ org_id: orgId,
6304
+ name: input.name,
6305
+ file_path: input.file_path,
6306
+ mime_type: input.mime_type ?? "application/octet-stream",
6307
+ access_level: input.access_level ?? "internal"
6308
+ });
6309
+ return `File "${input.name}" registered with ID ${row.id}.`;
6310
+ }
6311
+ };
6312
+
6313
+ // src/core/orchestrator/tools/task-ops.ts
6314
+ import { randomUUID as randomUUID2 } from "crypto";
6315
+ var dispatchTaskTool = {
6316
+ definition: {
6317
+ name: "dispatch_task",
6318
+ description: "Create a new task and assign it to an agent. The agent will automatically pick it up and execute. Use when work needs to be delegated to a specialist.",
6319
+ input_schema: {
6320
+ type: "object",
6321
+ properties: {
6322
+ title: { type: "string", description: "Short task title" },
6323
+ description: { type: "string", description: "Detailed task description" },
6324
+ agent_slug: { type: "string", description: 'Agent slug to assign to (e.g. "engineer", "qa")' },
6325
+ priority: { type: "number", description: "Priority 1-10 (lower = higher priority). Default: 5" }
6326
+ },
6327
+ required: ["title", "agent_slug"]
6328
+ }
6329
+ },
6330
+ handler: async (input, ctx) => {
6331
+ const agents = await ctx.db.query("agents", { where: { slug: input.agent_slug } });
6332
+ const agent = agents[0];
6333
+ if (!agent) return `Error: agent "${input.agent_slug}" not found.`;
6334
+ const row = await ctx.db.insert("tasks", {
6335
+ id: randomUUID2(),
6336
+ title: input.title,
6337
+ description: input.description ?? input.title,
6338
+ assignee_id: agent.id,
6339
+ priority: input.priority ?? 5,
6340
+ status: "todo"
6341
+ });
6342
+ return `Task "${input.title}" dispatched to ${agent.name} (ID: ${row.id}).`;
6343
+ }
6344
+ };
6345
+ var cancelTaskTool = {
6346
+ definition: {
6347
+ name: "cancel_task",
6348
+ description: "Cancel a task by ID. Use when a task is no longer needed.",
6349
+ input_schema: {
6350
+ type: "object",
6351
+ properties: {
6352
+ task_id: { type: "string", description: "Task ID to cancel" }
6353
+ },
6354
+ required: ["task_id"]
6355
+ }
6356
+ },
6357
+ handler: async (input, ctx) => {
6358
+ const task = await ctx.db.get("tasks", { id: input.task_id });
6359
+ if (!task) return `Error: task ${input.task_id} not found.`;
6360
+ await ctx.db.update("tasks", { id: input.task_id }, { status: "cancelled" });
6361
+ return `Task "${task.title}" cancelled.`;
6362
+ }
6363
+ };
6364
+ var reassignTaskTool = {
6365
+ definition: {
6366
+ name: "reassign_task",
6367
+ description: "Reassign a task to a different agent. Cancels the original and creates a copy for the new agent.",
6368
+ input_schema: {
6369
+ type: "object",
6370
+ properties: {
6371
+ task_id: { type: "string", description: "Task ID to reassign" },
6372
+ new_agent_slug: { type: "string", description: "Agent slug to reassign to" }
6373
+ },
6374
+ required: ["task_id", "new_agent_slug"]
6375
+ }
6376
+ },
6377
+ handler: async (input, ctx) => {
6378
+ const task = await ctx.db.get("tasks", { id: input.task_id });
6379
+ if (!task) return `Error: task ${input.task_id} not found.`;
6380
+ const agents = await ctx.db.query("agents", { where: { slug: input.new_agent_slug } });
6381
+ const newAgent = agents[0];
6382
+ if (!newAgent) return `Error: agent "${input.new_agent_slug}" not found.`;
6383
+ await ctx.db.update("tasks", { id: input.task_id }, { status: "cancelled" });
6384
+ const row = await ctx.db.insert("tasks", {
6385
+ id: randomUUID2(),
6386
+ title: task.title,
6387
+ description: task.description,
6388
+ assignee_id: newAgent.id,
6389
+ priority: task.priority,
6390
+ status: "todo"
6391
+ });
6392
+ return `Task "${task.title}" reassigned from original to ${newAgent.name} (new ID: ${row.id}).`;
6393
+ }
6394
+ };
6395
+
6396
+ // src/core/orchestrator/tools/status.ts
6397
+ var getTaskStatusTool = {
6398
+ definition: {
6399
+ name: "get_task_status",
6400
+ description: "Get the current status, result, and details of a specific task. Use when checking on work progress.",
6401
+ input_schema: {
6402
+ type: "object",
6403
+ properties: {
6404
+ task_id: { type: "string", description: "Task ID to check" }
6405
+ },
6406
+ required: ["task_id"]
6407
+ }
6408
+ },
6409
+ handler: async (input, ctx) => {
6410
+ const task = await ctx.db.get("tasks", { id: input.task_id });
6411
+ if (!task) return `Task ${input.task_id} not found.`;
6412
+ const agent = task.assignee_id ? await ctx.db.get("agents", { id: task.assignee_id }) : null;
6413
+ return [
6414
+ `**${task.title}**`,
6415
+ `Status: ${task.status}`,
6416
+ `Assigned to: ${agent?.name ?? "unassigned"}`,
6417
+ `Priority: ${task.priority}`,
6418
+ task.result ? `Result: ${task.result.slice(0, 500)}` : null
6419
+ ].filter(Boolean).join("\n");
6420
+ }
6421
+ };
6422
+ var getAgentStatusTool = {
6423
+ definition: {
6424
+ name: "get_agent_status",
6425
+ description: "Get an agent's current status, active tasks, and recent run history. Use when checking what an agent is doing.",
6426
+ input_schema: {
6427
+ type: "object",
6428
+ properties: {
6429
+ agent_slug: { type: "string", description: 'Agent slug (e.g. "engineer")' }
6430
+ },
6431
+ required: ["agent_slug"]
6432
+ }
6433
+ },
6434
+ handler: async (input, ctx) => {
6435
+ const agents = await ctx.db.query("agents", { where: { slug: input.agent_slug } });
6436
+ const agent = agents[0];
6437
+ if (!agent) return `Agent "${input.agent_slug}" not found.`;
6438
+ const tasks = await ctx.db.query("tasks", { where: { assignee_id: agent.id, status: "todo" } });
6439
+ const runs = await ctx.db.query("runs", { where: { agent_id: agent.id }, limit: 5 });
6440
+ return [
6441
+ `**${agent.name}** (${agent.role})`,
6442
+ `Status: ${agent.status}`,
6443
+ `Pending tasks: ${tasks.length}`,
6444
+ runs.length > 0 ? `Recent runs: ${runs.map((r) => `${r.status} (${r.exit_code})`).join(", ")}` : "No recent runs"
6445
+ ].join("\n");
6446
+ }
6447
+ };
6448
+ var getSystemStatusTool = {
6449
+ definition: {
6450
+ name: "get_system_status",
6451
+ description: "Get overall system health: active agents, pending tasks, recent activity. Use for a quick system overview.",
6452
+ input_schema: { type: "object", properties: {} }
6453
+ },
6454
+ handler: async (_input, ctx) => {
6455
+ const agents = await ctx.db.query("agents");
6456
+ const todoTasks = await ctx.db.query("tasks", { where: { status: "todo" } });
6457
+ const runningTasks = await ctx.db.query("tasks", { where: { status: "in_progress" } });
6458
+ const recentRuns = await ctx.db.query("runs", { limit: 5 });
6459
+ return [
6460
+ `Agents: ${agents.length} (${agents.filter((a) => a.status === "running").length} running)`,
6461
+ `Pending tasks: ${todoTasks.length}`,
6462
+ `In-progress tasks: ${runningTasks.length}`,
6463
+ `Recent runs: ${recentRuns.length}`
6464
+ ].join("\n");
6465
+ }
6466
+ };
6467
+ var getActiveTasksTool = {
6468
+ definition: {
6469
+ name: "get_active_tasks",
6470
+ description: "List all active (todo/in_progress) tasks across all agents. Use to see what work is pending.",
6471
+ input_schema: { type: "object", properties: {} }
6472
+ },
6473
+ handler: async (_input, ctx) => {
6474
+ const todo = await ctx.db.query("tasks", { where: { status: "todo" } });
6475
+ const inProgress = await ctx.db.query("tasks", { where: { status: "in_progress" } });
6476
+ const all = [...todo, ...inProgress];
6477
+ if (all.length === 0) return "No active tasks.";
6478
+ return all.map((t) => `- [${t.status}] ${t.title} (priority: ${t.priority})`).join("\n");
6479
+ }
6480
+ };
6481
+
6482
+ // src/core/orchestrator/tools/roster.ts
6483
+ var listAgentsTool = {
6484
+ definition: {
6485
+ name: "list_agents",
6486
+ description: 'List all agents with their roles, status, and capabilities. Use when asking "who can do this?" or "what agents do we have?"',
6487
+ input_schema: { type: "object", properties: {} }
6488
+ },
6489
+ handler: async (_input, ctx) => {
6490
+ const agents = await ctx.db.query("agents");
6491
+ if (agents.length === 0) return "No agents registered.";
6492
+ return agents.filter((a) => !a.deleted_at).map((a) => `- **${a.name}** (${a.role}) \u2014 ${a.status}`).join("\n");
6493
+ }
6494
+ };
6495
+ var listProjectsTool = {
6496
+ definition: {
6497
+ name: "list_projects",
6498
+ description: "List all projects with status and description. Use when asking about company projects or work.",
6499
+ input_schema: { type: "object", properties: {} }
6500
+ },
6501
+ handler: async (_input, ctx) => {
6502
+ const projects = await ctx.db.query("project").catch(() => []);
6503
+ if (projects.length === 0) return "No projects found.";
6504
+ return projects.filter((p) => !p.deleted_at).map((p) => `- **${p.name}** (${p.status ?? "unknown"})${p.description ? ` \u2014 ${p.description.slice(0, 80)}` : ""}`).join("\n");
6505
+ }
6506
+ };
6507
+ var getAgentDetailTool = {
6508
+ definition: {
6509
+ name: "get_agent_detail",
6510
+ description: "Get full details about an agent: role, projects, skills, recent messages. Use when you need to know an agent's capabilities or history.",
6511
+ input_schema: {
6512
+ type: "object",
6513
+ properties: {
6514
+ agent_slug: { type: "string", description: "Agent slug" }
6515
+ },
6516
+ required: ["agent_slug"]
6517
+ }
6518
+ },
6519
+ handler: async (input, ctx) => {
6520
+ const agents = await ctx.db.query("agents", { where: { slug: input.agent_slug } });
6521
+ const agent = agents[0];
6522
+ if (!agent) return `Agent "${input.agent_slug}" not found.`;
6523
+ const skills = await ctx.db.query("agent_skills", { where: { agent_id: agent.id } }).catch(() => []);
6524
+ const skillNames = [];
6525
+ for (const link of skills) {
6526
+ const skill = await ctx.db.get("skills", { id: link.skill_id });
6527
+ if (skill) skillNames.push(skill.name);
6528
+ }
6529
+ return [
6530
+ `# ${agent.name}`,
6531
+ `Role: ${agent.role}`,
6532
+ `Status: ${agent.status}`,
6533
+ `Adapter: ${agent.adapter}`,
6534
+ skillNames.length > 0 ? `Skills: ${skillNames.join(", ")}` : "No skills assigned"
6535
+ ].join("\n");
6536
+ }
6537
+ };
6538
+
6539
+ // src/core/orchestrator/tools/messaging.ts
6540
+ var sendMessageTool = {
6541
+ definition: {
6542
+ name: "send_message",
6543
+ description: 'Send a message to a user or channel. Works across any channel: slack, email, discord, sms. For email, set channel="email" and include subject in the message. Use when the user asks to send/email/message someone.',
6544
+ input_schema: {
6545
+ type: "object",
6546
+ properties: {
6547
+ channel: { type: "string", description: 'Channel: "slack", "email", "discord", etc.' },
6548
+ recipient: { type: "string", description: "Recipient: Slack user ID, email address, channel name, etc." },
6549
+ text: { type: "string", description: "Message body" },
6550
+ subject: { type: "string", description: "Subject line (for email channel)" }
6551
+ },
6552
+ required: ["channel", "recipient", "text"]
6553
+ }
6554
+ },
6555
+ handler: async (input, ctx) => {
6556
+ await ctx.hooks.emit("message.send", {
6557
+ channel: input.channel,
6558
+ recipient: input.recipient,
6559
+ text: input.text,
6560
+ subject: input.subject,
6561
+ taskId: ctx.taskId
6562
+ });
6563
+ return `Message sent to ${input.recipient} via ${input.channel}.`;
6564
+ }
6565
+ };
6566
+ var addTaskCommentTool = {
6567
+ definition: {
6568
+ name: "add_task_comment",
6569
+ description: "Add a progress update or comment to a task. This syncs back to the Slack thread where the task was created. Use for mid-task status updates.",
6570
+ input_schema: {
6571
+ type: "object",
6572
+ properties: {
6573
+ task_id: { type: "string", description: "Task ID to comment on" },
6574
+ comment: { type: "string", description: "Progress update text" }
6575
+ },
6576
+ required: ["task_id", "comment"]
6577
+ }
6578
+ },
6579
+ handler: async (input, ctx) => {
6580
+ const task = await ctx.db.get("tasks", { id: input.task_id });
6581
+ if (!task) return `Task ${input.task_id} not found.`;
6582
+ const result = (task.result ?? "") + `
6583
+
6584
+ ---
6585
+ ${input.comment}`;
6586
+ await ctx.db.update("tasks", { id: input.task_id }, { result });
6587
+ const mappings = await ctx.db.query("thread_task_map", { where: { task_id: input.task_id } });
6588
+ if (mappings.length > 0) {
6589
+ await ctx.hooks.emit("response.ready", {
6590
+ text: input.comment,
6591
+ channel: "slack",
6592
+ threadId: mappings[0].thread_ts,
6593
+ taskId: input.task_id
6594
+ });
6595
+ }
6596
+ return `Comment added to task "${task.title}".`;
6597
+ }
6598
+ };
6599
+ var readConversationTool = {
6600
+ definition: {
6601
+ name: "read_conversation",
6602
+ description: "Read recent messages from a channel or conversation. Use when you need context about what was discussed recently.",
6603
+ input_schema: {
6604
+ type: "object",
6605
+ properties: {
6606
+ channel: { type: "string", description: 'Channel name (e.g. "slack")' },
6607
+ limit: { type: "number", description: "Number of recent messages to read. Default: 20" }
6608
+ },
6609
+ required: ["channel"]
6610
+ }
6611
+ },
6612
+ handler: async (input, ctx) => {
6613
+ const limit = input.limit ?? 20;
6614
+ const messages = await ctx.db.query("messages", {
6615
+ where: { channel: input.channel },
6616
+ orderBy: "created_at",
6617
+ limit
6618
+ });
6619
+ if (messages.length === 0) return `No messages found in ${input.channel}.`;
6620
+ return messages.map((m) => {
6621
+ const dir = m.direction === "inbound" ? "\u2192" : "\u2190";
6622
+ const who = m.from_user ?? m.from_agent ?? "unknown";
6623
+ return `${dir} ${who}: ${(m.body ?? "").slice(0, 200)}`;
6624
+ }).join("\n");
6625
+ }
6626
+ };
6627
+ var searchConversationTool = {
6628
+ definition: {
6629
+ name: "search_conversation",
6630
+ description: "Search message history across all channels by keyword. Use when looking for a specific discussion, decision, or topic that was mentioned previously.",
6631
+ input_schema: {
6632
+ type: "object",
6633
+ properties: {
6634
+ query: { type: "string", description: "Search keyword or phrase" },
6635
+ limit: { type: "number", description: "Max results. Default: 10" }
6636
+ },
6637
+ required: ["query"]
6638
+ }
6639
+ },
6640
+ handler: async (input, ctx) => {
6641
+ const query = input.query;
6642
+ const limit = input.limit ?? 10;
6643
+ const messages = await ctx.db.query("messages", {
6644
+ filters: [{ col: "body", op: "like", val: `%${query}%` }],
6645
+ orderBy: "created_at",
6646
+ limit
6647
+ });
6648
+ if (messages.length === 0) return `No messages found matching "${query}".`;
6649
+ return messages.map((m) => {
6650
+ const who = m.from_user ?? m.from_agent ?? "unknown";
6651
+ const ts = m.created_at?.slice(0, 16) ?? "";
6652
+ return `[${ts}] ${who}: ${(m.body ?? "").slice(0, 150)}`;
6653
+ }).join("\n");
6654
+ }
6655
+ };
6656
+
6657
+ // src/core/orchestrator/tools/management.ts
6658
+ var createAgentTool = {
6659
+ definition: {
6660
+ name: "create_agent",
6661
+ description: "Register a new agent in the system with a role and adapter. Use when the team needs a new specialist.",
6662
+ input_schema: {
6663
+ type: "object",
6664
+ properties: {
6665
+ slug: { type: "string", description: "Unique identifier (lowercase, no spaces)" },
6666
+ name: { type: "string", description: "Display name" },
6667
+ role: { type: "string", description: 'Role (e.g. "engineer", "qa", "marketing")' },
6668
+ adapter: { type: "string", description: 'Execution adapter: "api" or "cli". Default: "api"' }
6669
+ },
6670
+ required: ["slug", "name", "role"]
6671
+ }
6672
+ },
6673
+ handler: async (input, ctx) => {
6674
+ const existing = await ctx.db.query("agents", { where: { slug: input.slug } });
6675
+ if (existing.length > 0) return `Agent "${input.slug}" already exists.`;
6676
+ const row = await ctx.db.insert("agents", {
6677
+ slug: input.slug,
6678
+ name: input.name,
6679
+ role: input.role,
6680
+ adapter: input.adapter ?? "api",
6681
+ status: "idle",
6682
+ adapter_config: "{}",
6683
+ heartbeat_config: "{}"
6684
+ });
6685
+ return `Agent "${input.name}" (${input.role}) created with ID ${row.id}.`;
6686
+ }
6687
+ };
6688
+ var createProjectTool = {
6689
+ definition: {
6690
+ name: "create_project",
6691
+ description: "Register a new project with status and description. Use when a new initiative or product needs tracking.",
6692
+ input_schema: {
6693
+ type: "object",
6694
+ properties: {
6695
+ name: { type: "string", description: "Project name" },
6696
+ status: { type: "string", description: "Status: active_build, production, planning, etc." },
6697
+ description: { type: "string", description: "Brief description" }
6698
+ },
6699
+ required: ["name"]
6700
+ }
6701
+ },
6702
+ handler: async (input, ctx) => {
6703
+ const orgs = await ctx.db.query("org");
6704
+ const orgId = orgs[0]?.id ?? "";
6705
+ const row = await ctx.db.insert("project", {
6706
+ org_id: orgId,
6707
+ name: input.name,
6708
+ status: input.status ?? "planning",
6709
+ description: input.description ?? ""
6710
+ });
6711
+ return `Project "${input.name}" created with ID ${row.id}.`;
6712
+ }
6713
+ };
6714
+
6254
6715
  // src/core/orchestrator/user-registry.ts
6255
6716
  import { v4 as uuidv4 } from "uuid";
6256
6717
  var UserRegistry = class {
@@ -6632,20 +7093,24 @@ export {
6632
7093
  WakeupQueue,
6633
7094
  WorkflowEngine,
6634
7095
  _resetConfig,
7096
+ addTaskCommentTool,
6635
7097
  areDependenciesMet,
6636
7098
  autoUpdate,
6637
7099
  buildAgentBindings,
6638
7100
  buildChainOrigin,
6639
7101
  buildProcessEnv,
6640
7102
  buildSystemContext,
7103
+ cancelTaskTool,
6641
7104
  checkAllowlist,
6642
7105
  checkChainDepth,
6643
7106
  checkMentionGate,
6644
7107
  chunkText,
6645
7108
  classifyUpdate,
6646
7109
  compareVersions,
7110
+ createAgentTool,
6647
7111
  createConfigRevision,
6648
7112
  createDefaultLLMCall,
7113
+ createProjectTool,
6649
7114
  deactivateLocalImagePaths,
6650
7115
  defineCoreEntityContexts,
6651
7116
  defineCoreTables,
@@ -6654,21 +7119,35 @@ export {
6654
7119
  detectCycle,
6655
7120
  discoverChannels,
6656
7121
  discoverProviders,
7122
+ dispatchTaskTool,
6657
7123
  formatText,
7124
+ getActiveTasksTool,
7125
+ getAgentDetailTool,
7126
+ getAgentStatusTool,
6658
7127
  getConfig,
7128
+ getSystemStatusTool,
7129
+ getTaskStatusTool,
6659
7130
  initConfig,
6660
7131
  interpolate,
6661
7132
  interpolateEnv,
6662
7133
  isLoginRequired,
6663
7134
  isMaxTurns,
7135
+ listAgentsTool,
7136
+ listFilesTool,
7137
+ listProjectsTool,
6664
7138
  loadConfig,
6665
7139
  parseClaudeStream,
6666
7140
  parseVersion,
7141
+ readConversationTool,
6667
7142
  readFileTool,
7143
+ reassignTaskTool,
6668
7144
  registerExecutionEngine,
7145
+ registerFileTool,
6669
7146
  runPackageMigrations,
6670
7147
  sanitize,
7148
+ searchConversationTool,
6671
7149
  sendFileTool,
7150
+ sendMessageTool,
6672
7151
  topologicalSort,
6673
7152
  truncateAtWord,
6674
7153
  validateConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botinabox",
3
- "version": "1.10.0",
3
+ "version": "2.0.1",
4
4
  "description": "Bot in a Box — framework for building multi-agent bots",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",