botinabox 0.3.0 → 0.4.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.
- package/dist/index.d.ts +113 -1
- package/dist/index.js +92 -9
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -466,6 +466,8 @@ declare class ChannelRegistry {
|
|
|
466
466
|
* Run health checks on all registered adapters.
|
|
467
467
|
*/
|
|
468
468
|
healthCheck(): Promise<Record<string, HealthStatus>>;
|
|
469
|
+
/** Check if an adapter is registered. */
|
|
470
|
+
has(id: string): boolean;
|
|
469
471
|
/** Get an adapter by ID. */
|
|
470
472
|
get(id: string): ChannelAdapter | undefined;
|
|
471
473
|
/** List all registered adapters. */
|
|
@@ -550,6 +552,7 @@ type EntitySource = {
|
|
|
550
552
|
remoteKey: string;
|
|
551
553
|
remoteTable: string;
|
|
552
554
|
filters?: Filter[];
|
|
555
|
+
softDelete?: boolean;
|
|
553
556
|
orderBy?: string;
|
|
554
557
|
limit?: number;
|
|
555
558
|
} | {
|
|
@@ -986,6 +989,69 @@ declare const CORE_MIGRATIONS: Array<{
|
|
|
986
989
|
*/
|
|
987
990
|
declare function defineCoreEntityContexts(db: DataStore): void;
|
|
988
991
|
|
|
992
|
+
/**
|
|
993
|
+
* Options for domain table generation.
|
|
994
|
+
* Enable/disable optional tables based on your app's needs.
|
|
995
|
+
*/
|
|
996
|
+
interface DomainSchemaOptions {
|
|
997
|
+
/** Include client + invoice tables (default: true) */
|
|
998
|
+
clients?: boolean;
|
|
999
|
+
/** Include repository table (default: true) */
|
|
1000
|
+
repositories?: boolean;
|
|
1001
|
+
/** Include file table (default: true) */
|
|
1002
|
+
files?: boolean;
|
|
1003
|
+
/** Include channel table (default: true) */
|
|
1004
|
+
channels?: boolean;
|
|
1005
|
+
/** Include rule table + junction tables (default: true) */
|
|
1006
|
+
rules?: boolean;
|
|
1007
|
+
/** Include event audit log (default: true) */
|
|
1008
|
+
events?: boolean;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Define standard domain tables that most multi-agent apps need.
|
|
1012
|
+
* Call after defineCoreTables() and before db.init().
|
|
1013
|
+
*
|
|
1014
|
+
* Provides: org, project, + optional client, invoice, repository,
|
|
1015
|
+
* file, channel, rule, event tables with appropriate junction tables.
|
|
1016
|
+
*
|
|
1017
|
+
* @example
|
|
1018
|
+
* ```ts
|
|
1019
|
+
* defineCoreTables(db);
|
|
1020
|
+
* defineDomainTables(db); // all tables
|
|
1021
|
+
* defineDomainTables(db, { clients: false }); // skip client/invoice
|
|
1022
|
+
* ```
|
|
1023
|
+
*/
|
|
1024
|
+
declare function defineDomainTables(db: DataStore, options?: DomainSchemaOptions): void;
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Options for domain entity context generation.
|
|
1028
|
+
* Match the options used in defineDomainTables().
|
|
1029
|
+
*/
|
|
1030
|
+
interface DomainEntityContextOptions {
|
|
1031
|
+
clients?: boolean;
|
|
1032
|
+
repositories?: boolean;
|
|
1033
|
+
files?: boolean;
|
|
1034
|
+
channels?: boolean;
|
|
1035
|
+
rules?: boolean;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Define entity context rendering for standard domain tables.
|
|
1039
|
+
* Call after defineDomainTables() and defineCoreTables().
|
|
1040
|
+
*
|
|
1041
|
+
* Renders per-entity directories with context files for:
|
|
1042
|
+
* org, project, + optional client, file, channel entities.
|
|
1043
|
+
* Also adds PROJECTS.md, RULES.md, SKILLS.md, REPOS.md to agent context.
|
|
1044
|
+
*
|
|
1045
|
+
* @example
|
|
1046
|
+
* ```ts
|
|
1047
|
+
* defineCoreTables(db);
|
|
1048
|
+
* defineDomainTables(db);
|
|
1049
|
+
* defineCoreEntityContexts(db); // agents, users, skills
|
|
1050
|
+
* defineDomainEntityContexts(db); // org, project, client, file, channel
|
|
1051
|
+
* ```
|
|
1052
|
+
*/
|
|
1053
|
+
declare function defineDomainEntityContexts(db: DataStore, options?: DomainEntityContextOptions): void;
|
|
1054
|
+
|
|
989
1055
|
interface SanitizerOptions {
|
|
990
1056
|
fieldLengthLimits?: Record<string, number>;
|
|
991
1057
|
truncateSuffix?: string;
|
|
@@ -1047,6 +1113,20 @@ declare class AuditEmitter {
|
|
|
1047
1113
|
emit(event: AuditEvent): void;
|
|
1048
1114
|
}
|
|
1049
1115
|
|
|
1116
|
+
/**
|
|
1117
|
+
* Build a clean environment for spawned subprocesses.
|
|
1118
|
+
* Strips secrets and passes only safe system variables.
|
|
1119
|
+
* Used by the CLI execution adapter when spawning agent processes.
|
|
1120
|
+
*/
|
|
1121
|
+
/**
|
|
1122
|
+
* Build a filtered environment for subprocess execution.
|
|
1123
|
+
* Only passes explicitly allowed variables — all secrets are stripped.
|
|
1124
|
+
*
|
|
1125
|
+
* @param allowedKeys - Additional keys to allow beyond the defaults
|
|
1126
|
+
* @param inject - Extra key-value pairs to inject into the env
|
|
1127
|
+
*/
|
|
1128
|
+
declare function buildProcessEnv(allowedKeys?: string[], inject?: Record<string, string>): Record<string, string>;
|
|
1129
|
+
|
|
1050
1130
|
interface PackageUpdate {
|
|
1051
1131
|
name: string;
|
|
1052
1132
|
installedVersion: string;
|
|
@@ -1373,4 +1453,36 @@ declare class SecretStore {
|
|
|
1373
1453
|
private _toMeta;
|
|
1374
1454
|
}
|
|
1375
1455
|
|
|
1376
|
-
|
|
1456
|
+
/**
|
|
1457
|
+
* Parse Claude CLI NDJSON (stream-json) output into structured results.
|
|
1458
|
+
* Used by the CLI execution adapter to extract session info, costs,
|
|
1459
|
+
* token usage, and text output from Claude CLI subprocess output.
|
|
1460
|
+
*/
|
|
1461
|
+
interface ParsedStream {
|
|
1462
|
+
sessionId: string | null;
|
|
1463
|
+
model: string | null;
|
|
1464
|
+
costUsd: number | null;
|
|
1465
|
+
usage: UsageSummary | null;
|
|
1466
|
+
summary: string;
|
|
1467
|
+
isError: boolean;
|
|
1468
|
+
errorMessage: string | null;
|
|
1469
|
+
stopReason: string | null;
|
|
1470
|
+
}
|
|
1471
|
+
interface UsageSummary {
|
|
1472
|
+
inputTokens: number;
|
|
1473
|
+
cachedInputTokens: number;
|
|
1474
|
+
outputTokens: number;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Parse Claude CLI NDJSON output into a structured result.
|
|
1478
|
+
* Handles init, assistant, and result event types.
|
|
1479
|
+
*/
|
|
1480
|
+
declare function parseClaudeStream(stdout: string): ParsedStream;
|
|
1481
|
+
/** Check if the run stopped due to max turns. */
|
|
1482
|
+
declare function isMaxTurns(parsed: ParsedStream): boolean;
|
|
1483
|
+
/** Check if Claude CLI requires login. */
|
|
1484
|
+
declare function isLoginRequired(stdout: string): boolean;
|
|
1485
|
+
/** Rewrite local image paths to prevent CLI auto-embedding as vision content. */
|
|
1486
|
+
declare function deactivateLocalImagePaths(prompt: string): string;
|
|
1487
|
+
|
|
1488
|
+
export { AGENT_STATUSES, type AgentConfig, type AgentDefinition, type AgentFilter, type AgentRecord, AgentRegistry, type AgentStatus, ApiExecutionAdapter, AuditEmitter, type AuditEvent, BackupManager, type BotConfig, type BudgetCheck, type BudgetConfig, BudgetController, CORE_MIGRATIONS, ChannelAdapter, ChannelRegistry, ChannelRegistryError, ChatMessage, ChatSessionManager, CliExecutionAdapter, type ColumnValidator, ColumnValidatorImpl, type ConfigLoadError, type ConfigLoadResult, DEFAULTS, DEFAULT_CONFIG, type DataConfig, DataStore, DataStoreError, type DomainEntityContextOptions, type DomainSchemaOptions, EVENTS, type EntityColumnDef, type EntityConfig, type EntityContextDef, type EntityFileSpec, type EntitySource, type ExecutionAdapter, type Filter, HealthStatus, HeartbeatScheduler, HookBus, type HookHandler, type HookOptions, type HookRegistration, InboundMessage, LLMProvider, MAX_CHAIN_DEPTH, MessagePipeline, type ModelConfig, ModelInfo, ModelRouter, NdjsonLogger, NotificationQueue, type PackageMigration, type PackageUpdate, type ParsedStream, type PkLookup, ProviderRegistry, type QueryOptions, RUN_STATUSES, type RelationDef, type RenderConfig, ResolvedModel, type RetryPolicy, type Row, type RunContext, RunManager, type RunResult, type RunStatus, type SanitizerOptions, type SchemaError, type SecretInput, type SecretMeta, SecretStore, type SecurityConfig, type SeedItem, SessionKey, SessionManager, type SqliteAdapter, type StepRef, TASK_STATUSES, type TableDefinition, type TableInfoRow, type TaskDefinition, TaskQueue, type TaskRecord, type TaskStatus, TokenUsage, type Unsubscribe, 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, buildAgentBindings, buildChainOrigin, buildProcessEnv, checkAllowlist, checkChainDepth, checkMentionGate, chunkText, classifyUpdate, compareVersions, createConfigRevision, deactivateLocalImagePaths, defineCoreEntityContexts, defineCoreTables, defineDomainEntityContexts, defineDomainTables, detectCycle, discoverChannels, discoverProviders, formatText, getConfig, initConfig, interpolate, interpolateEnv, isLoginRequired, isMaxTurns, loadConfig, parseClaudeStream, parseVersion, runPackageMigrations, sanitize, topologicalSort, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -585,6 +585,10 @@ var ChannelRegistry = class {
|
|
|
585
585
|
}
|
|
586
586
|
return results;
|
|
587
587
|
}
|
|
588
|
+
/** Check if an adapter is registered. */
|
|
589
|
+
has(id) {
|
|
590
|
+
return this.adapters.has(id);
|
|
591
|
+
}
|
|
588
592
|
/** Get an adapter by ID. */
|
|
589
593
|
get(id) {
|
|
590
594
|
return this.adapters.get(id)?.adapter;
|
|
@@ -830,14 +834,14 @@ var SessionManager = class {
|
|
|
830
834
|
async save(agentId, channelId, peerId, params) {
|
|
831
835
|
const existing = await this._find(agentId, channelId, peerId);
|
|
832
836
|
if (existing) {
|
|
833
|
-
await this.db.update("
|
|
837
|
+
await this.db.update("sessions", { id: existing["id"] }, {
|
|
834
838
|
context: JSON.stringify(params),
|
|
835
839
|
last_message_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
836
840
|
message_count: (existing["message_count"] ?? 0) + 1
|
|
837
841
|
});
|
|
838
842
|
return existing["id"];
|
|
839
843
|
} else {
|
|
840
|
-
const row = await this.db.insert("
|
|
844
|
+
const row = await this.db.insert("sessions", {
|
|
841
845
|
agent_id: agentId,
|
|
842
846
|
channel: channelId,
|
|
843
847
|
peer_id: peerId,
|
|
@@ -857,7 +861,7 @@ var SessionManager = class {
|
|
|
857
861
|
async clear(agentId, channelId, peerId) {
|
|
858
862
|
const session = await this._find(agentId, channelId, peerId);
|
|
859
863
|
if (session) {
|
|
860
|
-
await this.db.delete("
|
|
864
|
+
await this.db.delete("sessions", { id: session["id"] });
|
|
861
865
|
}
|
|
862
866
|
}
|
|
863
867
|
async shouldClear(session, opts) {
|
|
@@ -876,7 +880,7 @@ var SessionManager = class {
|
|
|
876
880
|
return false;
|
|
877
881
|
}
|
|
878
882
|
async _find(agentId, channelId, peerId) {
|
|
879
|
-
const rows = await this.db.query("
|
|
883
|
+
const rows = await this.db.query("sessions", {
|
|
880
884
|
where: { agent_id: agentId, channel: channelId, peer_id: peerId }
|
|
881
885
|
});
|
|
882
886
|
return rows[0] ?? void 0;
|
|
@@ -923,6 +927,9 @@ var NotificationQueue = class {
|
|
|
923
927
|
* Returns the notification ID.
|
|
924
928
|
*/
|
|
925
929
|
async enqueue(channel, recipient, payload) {
|
|
930
|
+
if (!this.channelRegistry.has(channel)) {
|
|
931
|
+
throw new Error(`No registered adapter for channel: ${channel}`);
|
|
932
|
+
}
|
|
926
933
|
const row = await this.db.insert("notifications", {
|
|
927
934
|
channel,
|
|
928
935
|
recipient_id: recipient,
|
|
@@ -1369,20 +1376,41 @@ function defineCoreTables(db) {
|
|
|
1369
1376
|
]
|
|
1370
1377
|
});
|
|
1371
1378
|
db.define("messages", {
|
|
1379
|
+
columns: {
|
|
1380
|
+
id: "TEXT PRIMARY KEY",
|
|
1381
|
+
channel: "TEXT NOT NULL DEFAULT 'slack'",
|
|
1382
|
+
direction: "TEXT NOT NULL DEFAULT 'inbound'",
|
|
1383
|
+
from_user: "TEXT",
|
|
1384
|
+
from_agent: "TEXT",
|
|
1385
|
+
agent_id: "TEXT",
|
|
1386
|
+
user_id: "TEXT",
|
|
1387
|
+
body: "TEXT NOT NULL",
|
|
1388
|
+
thread_id: "TEXT",
|
|
1389
|
+
task_id: "TEXT",
|
|
1390
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1391
|
+
deleted_at: "TEXT"
|
|
1392
|
+
},
|
|
1393
|
+
tableConstraints: [
|
|
1394
|
+
"CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at)",
|
|
1395
|
+
"CREATE INDEX IF NOT EXISTS idx_messages_thread ON messages(thread_id)",
|
|
1396
|
+
"CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(agent_id)"
|
|
1397
|
+
]
|
|
1398
|
+
});
|
|
1399
|
+
db.define("sessions", {
|
|
1372
1400
|
columns: {
|
|
1373
1401
|
id: "TEXT PRIMARY KEY",
|
|
1374
1402
|
agent_id: "TEXT NOT NULL",
|
|
1375
1403
|
channel: "TEXT NOT NULL",
|
|
1376
1404
|
peer_id: "TEXT NOT NULL",
|
|
1377
1405
|
user_id: "TEXT",
|
|
1378
|
-
last_message_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1379
|
-
message_count: "INTEGER NOT NULL DEFAULT 0",
|
|
1380
1406
|
context: "TEXT NOT NULL DEFAULT '{}'",
|
|
1407
|
+
message_count: "INTEGER NOT NULL DEFAULT 0",
|
|
1408
|
+
last_message_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1381
1409
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1382
1410
|
expires_at: "TEXT"
|
|
1383
1411
|
},
|
|
1384
1412
|
tableConstraints: [
|
|
1385
|
-
"CREATE UNIQUE INDEX IF NOT EXISTS
|
|
1413
|
+
"CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_agent_channel_peer ON sessions(agent_id, channel, peer_id)"
|
|
1386
1414
|
]
|
|
1387
1415
|
});
|
|
1388
1416
|
db.define("skills", {
|
|
@@ -1671,6 +1699,55 @@ ${s.definition}` : null,
|
|
|
1671
1699
|
}
|
|
1672
1700
|
}
|
|
1673
1701
|
});
|
|
1702
|
+
db.defineEntityContext("messages", {
|
|
1703
|
+
table: "messages",
|
|
1704
|
+
directory: "messages",
|
|
1705
|
+
slugColumn: "id",
|
|
1706
|
+
indexFile: "messages/MESSAGES.md",
|
|
1707
|
+
indexRender: (rows) => {
|
|
1708
|
+
const active = rows.filter((r) => r.deleted_at == null);
|
|
1709
|
+
if (!active.length) return "# Messages\n\nNo messages.\n";
|
|
1710
|
+
const recent = active.slice(-100);
|
|
1711
|
+
const lines = recent.map((r) => {
|
|
1712
|
+
const dir = r.direction === "outbound" ? "\u2192" : "\u2190";
|
|
1713
|
+
const who = r.from_agent ?? r.from_user ?? "unknown";
|
|
1714
|
+
const time = (r.created_at ?? "").slice(0, 16);
|
|
1715
|
+
const preview = (r.body ?? "").slice(0, 80);
|
|
1716
|
+
return `- ${dir} **${who}** (${time}): ${preview}`;
|
|
1717
|
+
});
|
|
1718
|
+
return `# Messages
|
|
1719
|
+
|
|
1720
|
+
Last ${lines.length} messages:
|
|
1721
|
+
|
|
1722
|
+
${lines.join("\n")}
|
|
1723
|
+
`;
|
|
1724
|
+
},
|
|
1725
|
+
files: {
|
|
1726
|
+
"MESSAGE.md": {
|
|
1727
|
+
source: { type: "self" },
|
|
1728
|
+
render: (rows) => {
|
|
1729
|
+
const m = rows[0];
|
|
1730
|
+
if (!m) return "";
|
|
1731
|
+
return [
|
|
1732
|
+
"# Message",
|
|
1733
|
+
"",
|
|
1734
|
+
`**Direction:** ${m.direction}`,
|
|
1735
|
+
m.from_user ? `**From User:** ${m.from_user}` : null,
|
|
1736
|
+
m.from_agent ? `**From Agent:** ${m.from_agent}` : null,
|
|
1737
|
+
`**Channel:** ${m.channel}`,
|
|
1738
|
+
m.thread_id ? `**Thread:** ${m.thread_id}` : null,
|
|
1739
|
+
m.task_id ? `**Task:** ${m.task_id}` : null,
|
|
1740
|
+
`**Time:** ${m.created_at}`,
|
|
1741
|
+
"",
|
|
1742
|
+
"---",
|
|
1743
|
+
"",
|
|
1744
|
+
m.body,
|
|
1745
|
+
""
|
|
1746
|
+
].filter(Boolean).join("\n");
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1674
1751
|
}
|
|
1675
1752
|
|
|
1676
1753
|
// src/core/data/domain-schema.ts
|
|
@@ -2697,8 +2774,9 @@ var TaskQueue = class {
|
|
|
2697
2774
|
)
|
|
2698
2775
|
)
|
|
2699
2776
|
});
|
|
2700
|
-
|
|
2701
|
-
|
|
2777
|
+
const taskId = row["id"];
|
|
2778
|
+
void this.hooks.emit("task.created", { taskId, title: task.title });
|
|
2779
|
+
return taskId;
|
|
2702
2780
|
}
|
|
2703
2781
|
async update(id, changes) {
|
|
2704
2782
|
await this.db.update("tasks", { id }, {
|
|
@@ -2835,6 +2913,11 @@ var RunManager = class {
|
|
|
2835
2913
|
}
|
|
2836
2914
|
}
|
|
2837
2915
|
} else {
|
|
2916
|
+
await this.db.update("tasks", { id: taskId }, {
|
|
2917
|
+
status: "done",
|
|
2918
|
+
result: result.output,
|
|
2919
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2920
|
+
});
|
|
2838
2921
|
const task = await this.db.get("tasks", { id: taskId });
|
|
2839
2922
|
if (task && task["followup_agent_id"]) {
|
|
2840
2923
|
const chainDepth = (task["chain_depth"] ?? 0) + 1;
|