botinabox 0.6.0 → 1.1.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/channels/slack/index.d.ts +27 -1
- package/dist/channels/slack/index.js +4 -2
- package/dist/chunk-2LGXQPEA.js +41 -0
- package/dist/inbound-CGIXRXGC.js +8 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.js +135 -2
- package/package.json +2 -2
|
@@ -39,8 +39,23 @@ declare function createSlackAdapter(client?: BoltClient): SlackAdapter;
|
|
|
39
39
|
* Story 4.5
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
+
interface SlackFile {
|
|
43
|
+
id?: string;
|
|
44
|
+
filetype?: string;
|
|
45
|
+
subtype?: string;
|
|
46
|
+
url_private?: string;
|
|
47
|
+
preview?: string;
|
|
48
|
+
transcription?: {
|
|
49
|
+
status?: string;
|
|
50
|
+
preview?: {
|
|
51
|
+
content?: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
42
56
|
interface SlackEvent {
|
|
43
57
|
type: string;
|
|
58
|
+
subtype?: string;
|
|
44
59
|
client_msg_id?: string;
|
|
45
60
|
ts?: string;
|
|
46
61
|
event_ts?: string;
|
|
@@ -48,10 +63,21 @@ interface SlackEvent {
|
|
|
48
63
|
user?: string;
|
|
49
64
|
text?: string;
|
|
50
65
|
thread_ts?: string;
|
|
66
|
+
files?: SlackFile[];
|
|
51
67
|
[key: string]: unknown;
|
|
52
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Extract the text content from a voice message file.
|
|
71
|
+
* Prefers the transcription preview; falls back to the file preview text.
|
|
72
|
+
* Returns null if the file is not a voice message or has no transcript.
|
|
73
|
+
*/
|
|
74
|
+
declare function extractVoiceTranscript(file: SlackFile): string | null;
|
|
53
75
|
/**
|
|
54
76
|
* Parse a Slack event into an InboundMessage.
|
|
77
|
+
*
|
|
78
|
+
* Handles standard text messages and voice messages (file_share subtype
|
|
79
|
+
* with audio files). Voice message transcripts are extracted and prefixed
|
|
80
|
+
* with `[Voice message]`.
|
|
55
81
|
*/
|
|
56
82
|
declare function parseSlackEvent(event: SlackEvent): InboundMessage;
|
|
57
83
|
|
|
@@ -78,4 +104,4 @@ interface SlackConfig {
|
|
|
78
104
|
signingSecret?: string;
|
|
79
105
|
}
|
|
80
106
|
|
|
81
|
-
export { type BoltClient, SlackAdapter, type SlackConfig, type SlackEvent, createSlackAdapter as default, formatForSlack, parseSlackEvent };
|
|
107
|
+
export { type BoltClient, SlackAdapter, type SlackConfig, type SlackEvent, type SlackFile, createSlackAdapter as default, extractVoiceTranscript, formatForSlack, parseSlackEvent };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
extractVoiceTranscript,
|
|
2
3
|
parseSlackEvent
|
|
3
|
-
} from "../../chunk-
|
|
4
|
+
} from "../../chunk-2LGXQPEA.js";
|
|
4
5
|
|
|
5
6
|
// src/channels/slack/outbound.ts
|
|
6
7
|
function formatForSlack(text) {
|
|
@@ -63,7 +64,7 @@ var SlackAdapter = class {
|
|
|
63
64
|
/** Simulate receiving an inbound message (for testing/webhooks). */
|
|
64
65
|
async receive(event) {
|
|
65
66
|
if (this.onMessage) {
|
|
66
|
-
const { parseSlackEvent: parseSlackEvent2 } = await import("../../inbound-
|
|
67
|
+
const { parseSlackEvent: parseSlackEvent2 } = await import("../../inbound-CGIXRXGC.js");
|
|
67
68
|
const msg = parseSlackEvent2(event);
|
|
68
69
|
await this.onMessage(msg);
|
|
69
70
|
}
|
|
@@ -75,6 +76,7 @@ function createSlackAdapter(client) {
|
|
|
75
76
|
export {
|
|
76
77
|
SlackAdapter,
|
|
77
78
|
createSlackAdapter as default,
|
|
79
|
+
extractVoiceTranscript,
|
|
78
80
|
formatForSlack,
|
|
79
81
|
parseSlackEvent
|
|
80
82
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/channels/slack/inbound.ts
|
|
2
|
+
var AUDIO_TYPES = /* @__PURE__ */ new Set(["aac", "mp4", "m4a", "ogg", "webm", "mp3", "wav"]);
|
|
3
|
+
function extractVoiceTranscript(file) {
|
|
4
|
+
const isAudio = file.subtype === "slack_audio" || AUDIO_TYPES.has(file.filetype ?? "");
|
|
5
|
+
if (!isAudio) return null;
|
|
6
|
+
const transcript = file.transcription?.preview?.content ?? (typeof file.preview === "string" ? file.preview : null);
|
|
7
|
+
return transcript ?? null;
|
|
8
|
+
}
|
|
9
|
+
function parseSlackEvent(event) {
|
|
10
|
+
const id = event.client_msg_id ?? event.ts ?? event.event_ts ?? `slack-${Date.now()}`;
|
|
11
|
+
const channel = event.channel ?? "unknown";
|
|
12
|
+
const from = event.user ?? "unknown";
|
|
13
|
+
const threadId = event.thread_ts !== void 0 ? event.thread_ts : void 0;
|
|
14
|
+
const receivedAt = event.ts ? new Date(parseFloat(event.ts) * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
15
|
+
let body = event.text ?? "";
|
|
16
|
+
if (event.subtype === "file_share" && event.files?.length) {
|
|
17
|
+
for (const file of event.files) {
|
|
18
|
+
const transcript = extractVoiceTranscript(file);
|
|
19
|
+
if (transcript) {
|
|
20
|
+
body = body ? `${body}
|
|
21
|
+
|
|
22
|
+
[Voice message] ${transcript}` : `[Voice message] ${transcript}`;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
id,
|
|
29
|
+
channel,
|
|
30
|
+
from,
|
|
31
|
+
body,
|
|
32
|
+
threadId,
|
|
33
|
+
receivedAt,
|
|
34
|
+
raw: event
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
extractVoiceTranscript,
|
|
40
|
+
parseSlackEvent
|
|
41
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -324,6 +324,13 @@ declare const AGENT_STATUSES: readonly ["idle", "running", "paused", "terminated
|
|
|
324
324
|
/** Run status values */
|
|
325
325
|
declare const RUN_STATUSES: readonly ["queued", "running", "succeeded", "failed", "cancelled"];
|
|
326
326
|
|
|
327
|
+
/** Shared utility functions. */
|
|
328
|
+
/**
|
|
329
|
+
* Truncate text at a word boundary, appending "..." if truncated.
|
|
330
|
+
* Returns the original text if it's shorter than maxLen.
|
|
331
|
+
*/
|
|
332
|
+
declare function truncateAtWord(text: string, maxLen: number): string;
|
|
333
|
+
|
|
327
334
|
type HookHandler = (context: Record<string, unknown>) => Promise<void> | void;
|
|
328
335
|
type Unsubscribe = () => void;
|
|
329
336
|
interface HookOptions {
|
|
@@ -1012,6 +1019,8 @@ interface DomainSchemaOptions {
|
|
|
1012
1019
|
rules?: boolean;
|
|
1013
1020
|
/** Include event audit log (default: true) */
|
|
1014
1021
|
events?: boolean;
|
|
1022
|
+
/** Include cross-domain junction tables (default: true) */
|
|
1023
|
+
junctions?: boolean;
|
|
1015
1024
|
}
|
|
1016
1025
|
/**
|
|
1017
1026
|
* Define standard domain tables that most multi-agent apps need.
|
|
@@ -1514,6 +1523,15 @@ declare class SecretStore {
|
|
|
1514
1523
|
list(): Promise<SecretMeta[]>;
|
|
1515
1524
|
rotate(name: string, newValue: string, environment?: string): Promise<void>;
|
|
1516
1525
|
delete(name: string, environment?: string): Promise<void>;
|
|
1526
|
+
/**
|
|
1527
|
+
* Load a sync cursor by key. Returns undefined if not found.
|
|
1528
|
+
* Cursors are stored as secrets with type='sync_cursor'.
|
|
1529
|
+
*/
|
|
1530
|
+
loadCursor(key: string): Promise<string | undefined>;
|
|
1531
|
+
/**
|
|
1532
|
+
* Persist a sync cursor by key. Creates or updates the secret.
|
|
1533
|
+
*/
|
|
1534
|
+
saveCursor(key: string, value: string): Promise<void>;
|
|
1517
1535
|
private _toMeta;
|
|
1518
1536
|
}
|
|
1519
1537
|
|
|
@@ -1549,4 +1567,4 @@ declare function isLoginRequired(stdout: string): boolean;
|
|
|
1549
1567
|
/** Rewrite local image paths to prevent CLI auto-embedding as vision content. */
|
|
1550
1568
|
declare function deactivateLocalImagePaths(prompt: string): string;
|
|
1551
1569
|
|
|
1552
|
-
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, ConnectorConfig, 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, 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 Schedule, type ScheduleDef, Scheduler, 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 };
|
|
1570
|
+
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, ConnectorConfig, 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, 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 Schedule, type ScheduleDef, Scheduler, 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, truncateAtWord, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -75,6 +75,15 @@ var RUN_STATUSES = [
|
|
|
75
75
|
"cancelled"
|
|
76
76
|
];
|
|
77
77
|
|
|
78
|
+
// src/shared/utils.ts
|
|
79
|
+
function truncateAtWord(text, maxLen) {
|
|
80
|
+
if (text.length <= maxLen) return text;
|
|
81
|
+
const truncated = text.slice(0, maxLen);
|
|
82
|
+
const lastSpace = truncated.lastIndexOf(" ");
|
|
83
|
+
const cutPoint = lastSpace > maxLen * 0.5 ? lastSpace : maxLen;
|
|
84
|
+
return truncated.slice(0, cutPoint) + "...";
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
// src/core/hooks/hook-bus.ts
|
|
79
88
|
var HookBus = class {
|
|
80
89
|
registrations = /* @__PURE__ */ new Map();
|
|
@@ -1763,7 +1772,7 @@ ${s.definition}` : null,
|
|
|
1763
1772
|
const dir = r.direction === "outbound" ? "\u2192" : "\u2190";
|
|
1764
1773
|
const who = r.from_agent ?? r.from_user ?? "unknown";
|
|
1765
1774
|
const time = (r.created_at ?? "").slice(0, 16);
|
|
1766
|
-
const preview = (r.body ?? ""
|
|
1775
|
+
const preview = truncateAtWord(r.body ?? "", 80);
|
|
1767
1776
|
return `- ${dir} **${who}** (${time}): ${preview}`;
|
|
1768
1777
|
});
|
|
1769
1778
|
return `# Messages
|
|
@@ -1810,6 +1819,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
1810
1819
|
channels: true,
|
|
1811
1820
|
rules: true,
|
|
1812
1821
|
events: true,
|
|
1822
|
+
junctions: true,
|
|
1813
1823
|
...options
|
|
1814
1824
|
};
|
|
1815
1825
|
db.define("org", {
|
|
@@ -1837,6 +1847,8 @@ function defineDomainTables(db, options = {}) {
|
|
|
1837
1847
|
deploy_target: "TEXT",
|
|
1838
1848
|
production_url: "TEXT",
|
|
1839
1849
|
branch_strategy: "TEXT",
|
|
1850
|
+
repo_path: "TEXT",
|
|
1851
|
+
codename: "TEXT",
|
|
1840
1852
|
notes: "TEXT",
|
|
1841
1853
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1842
1854
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
@@ -1877,6 +1889,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
1877
1889
|
contact_name: "TEXT",
|
|
1878
1890
|
contact_email: "TEXT",
|
|
1879
1891
|
phone: "TEXT",
|
|
1892
|
+
address: "TEXT",
|
|
1880
1893
|
status: "TEXT NOT NULL DEFAULT 'active'",
|
|
1881
1894
|
notes: "TEXT",
|
|
1882
1895
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
@@ -1954,6 +1967,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
1954
1967
|
project_id: "TEXT",
|
|
1955
1968
|
access_level: "TEXT NOT NULL DEFAULT 'org'",
|
|
1956
1969
|
description: "TEXT",
|
|
1970
|
+
tags: "TEXT",
|
|
1957
1971
|
notes: "TEXT",
|
|
1958
1972
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1959
1973
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
@@ -1987,6 +2001,8 @@ function defineDomainTables(db, options = {}) {
|
|
|
1987
2001
|
scope: "TEXT NOT NULL DEFAULT 'org'",
|
|
1988
2002
|
category: "TEXT NOT NULL DEFAULT 'process'",
|
|
1989
2003
|
priority: "INTEGER NOT NULL DEFAULT 50",
|
|
2004
|
+
rationale: "TEXT",
|
|
2005
|
+
enforcement: "TEXT DEFAULT 'advisory'",
|
|
1990
2006
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1991
2007
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1992
2008
|
deleted_at: "TEXT"
|
|
@@ -2030,6 +2046,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
2030
2046
|
actor_user_id: "TEXT",
|
|
2031
2047
|
project_id: "TEXT",
|
|
2032
2048
|
channel_id: "TEXT",
|
|
2049
|
+
source: "TEXT",
|
|
2033
2050
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
2034
2051
|
deleted_at: "TEXT"
|
|
2035
2052
|
},
|
|
@@ -2039,6 +2056,58 @@ function defineDomainTables(db, options = {}) {
|
|
|
2039
2056
|
]
|
|
2040
2057
|
});
|
|
2041
2058
|
}
|
|
2059
|
+
if (opts.junctions) {
|
|
2060
|
+
db.define("secret_client", {
|
|
2061
|
+
columns: {
|
|
2062
|
+
secret_id: "TEXT NOT NULL",
|
|
2063
|
+
client_id: "TEXT NOT NULL",
|
|
2064
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2065
|
+
},
|
|
2066
|
+
primaryKey: ["secret_id", "client_id"]
|
|
2067
|
+
});
|
|
2068
|
+
db.define("secret_user", {
|
|
2069
|
+
columns: {
|
|
2070
|
+
secret_id: "TEXT NOT NULL",
|
|
2071
|
+
user_id: "TEXT NOT NULL",
|
|
2072
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2073
|
+
},
|
|
2074
|
+
primaryKey: ["secret_id", "user_id"]
|
|
2075
|
+
});
|
|
2076
|
+
db.define("secret_repository", {
|
|
2077
|
+
columns: {
|
|
2078
|
+
secret_id: "TEXT NOT NULL",
|
|
2079
|
+
repository_id: "TEXT NOT NULL",
|
|
2080
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2081
|
+
},
|
|
2082
|
+
primaryKey: ["secret_id", "repository_id"]
|
|
2083
|
+
});
|
|
2084
|
+
db.define("file_agent", {
|
|
2085
|
+
columns: {
|
|
2086
|
+
file_id: "TEXT NOT NULL",
|
|
2087
|
+
agent_id: "TEXT NOT NULL",
|
|
2088
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2089
|
+
},
|
|
2090
|
+
primaryKey: ["file_id", "agent_id"]
|
|
2091
|
+
});
|
|
2092
|
+
db.define("user_channel", {
|
|
2093
|
+
columns: {
|
|
2094
|
+
user_id: "TEXT NOT NULL",
|
|
2095
|
+
channel_id: "TEXT NOT NULL",
|
|
2096
|
+
role: "TEXT",
|
|
2097
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2098
|
+
},
|
|
2099
|
+
primaryKey: ["user_id", "channel_id"]
|
|
2100
|
+
});
|
|
2101
|
+
db.define("user_project", {
|
|
2102
|
+
columns: {
|
|
2103
|
+
user_id: "TEXT NOT NULL",
|
|
2104
|
+
project_id: "TEXT NOT NULL",
|
|
2105
|
+
role: "TEXT",
|
|
2106
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2107
|
+
},
|
|
2108
|
+
primaryKey: ["user_id", "project_id"]
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2042
2111
|
}
|
|
2043
2112
|
|
|
2044
2113
|
// src/core/data/domain-entity-contexts.ts
|
|
@@ -2151,7 +2220,32 @@ ${lines.join("\n\n")}
|
|
|
2151
2220
|
},
|
|
2152
2221
|
omitIfEmpty: true
|
|
2153
2222
|
}
|
|
2154
|
-
} : {}
|
|
2223
|
+
} : {},
|
|
2224
|
+
"MESSAGES.md": {
|
|
2225
|
+
source: {
|
|
2226
|
+
type: "hasMany",
|
|
2227
|
+
table: "messages",
|
|
2228
|
+
foreignKey: "project_id",
|
|
2229
|
+
orderBy: "created_at",
|
|
2230
|
+
limit: 100
|
|
2231
|
+
},
|
|
2232
|
+
render: (rows) => {
|
|
2233
|
+
if (!rows.length) return "# Messages\n\nNo messages.\n";
|
|
2234
|
+
const lines = rows.map((r) => {
|
|
2235
|
+
const dir = r.direction === "inbound" ? "\u2192" : "\u2190";
|
|
2236
|
+
const ts = (r.created_at ?? "").slice(0, 16);
|
|
2237
|
+
const agent = r.from_agent ? ` [${r.from_agent}]` : "";
|
|
2238
|
+
const body = r.body ?? "";
|
|
2239
|
+
const preview = truncateAtWord(body, 150);
|
|
2240
|
+
return `- ${dir} **${ts}**${agent} ${preview}`;
|
|
2241
|
+
});
|
|
2242
|
+
return `# Messages
|
|
2243
|
+
|
|
2244
|
+
${lines.join("\n")}
|
|
2245
|
+
`;
|
|
2246
|
+
},
|
|
2247
|
+
omitIfEmpty: false
|
|
2248
|
+
}
|
|
2155
2249
|
}
|
|
2156
2250
|
});
|
|
2157
2251
|
if (opts.clients) {
|
|
@@ -4039,6 +4133,44 @@ var SecretStore = class {
|
|
|
4039
4133
|
});
|
|
4040
4134
|
await this.hooks.emit("secret.deleted", { name, environment });
|
|
4041
4135
|
}
|
|
4136
|
+
// ── Cursor persistence helpers ──────────────────────────────────
|
|
4137
|
+
/**
|
|
4138
|
+
* Load a sync cursor by key. Returns undefined if not found.
|
|
4139
|
+
* Cursors are stored as secrets with type='sync_cursor'.
|
|
4140
|
+
*/
|
|
4141
|
+
async loadCursor(key) {
|
|
4142
|
+
const name = `sync-cursor:${key}`;
|
|
4143
|
+
const rows = await this.db.query("secrets", {
|
|
4144
|
+
where: { name },
|
|
4145
|
+
filters: [{ col: "deleted_at", op: "isNull" }],
|
|
4146
|
+
limit: 1
|
|
4147
|
+
});
|
|
4148
|
+
return rows[0]?.value ?? void 0;
|
|
4149
|
+
}
|
|
4150
|
+
/**
|
|
4151
|
+
* Persist a sync cursor by key. Creates or updates the secret.
|
|
4152
|
+
*/
|
|
4153
|
+
async saveCursor(key, value) {
|
|
4154
|
+
const name = `sync-cursor:${key}`;
|
|
4155
|
+
const rows = await this.db.query("secrets", {
|
|
4156
|
+
where: { name },
|
|
4157
|
+
filters: [{ col: "deleted_at", op: "isNull" }],
|
|
4158
|
+
limit: 1
|
|
4159
|
+
});
|
|
4160
|
+
if (rows.length > 0) {
|
|
4161
|
+
await this.db.update("secrets", rows[0].id, {
|
|
4162
|
+
value,
|
|
4163
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4164
|
+
});
|
|
4165
|
+
} else {
|
|
4166
|
+
await this.db.insert("secrets", {
|
|
4167
|
+
id: uuidv42(),
|
|
4168
|
+
name,
|
|
4169
|
+
type: "sync_cursor",
|
|
4170
|
+
value
|
|
4171
|
+
});
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4042
4174
|
_toMeta(row) {
|
|
4043
4175
|
return {
|
|
4044
4176
|
id: row.id,
|
|
@@ -4213,5 +4345,6 @@ export {
|
|
|
4213
4345
|
runPackageMigrations,
|
|
4214
4346
|
sanitize,
|
|
4215
4347
|
topologicalSort,
|
|
4348
|
+
truncateAtWord,
|
|
4216
4349
|
validateConfig
|
|
4217
4350
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botinabox",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Bot in a Box — framework for building multi-agent bots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@types/uuid": "^10.0.0",
|
|
60
60
|
"ajv": "^8.17.1",
|
|
61
61
|
"cron-parser": "^4.9.0",
|
|
62
|
-
"latticesql": "^0.
|
|
62
|
+
"latticesql": "^1.0.0",
|
|
63
63
|
"uuid": "^13.0.0",
|
|
64
64
|
"yaml": "^2.7.0"
|
|
65
65
|
},
|