verybot 0.1.3
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.
Potentially problematic release.
This version of verybot might be problematic. Click here for more details.
- package/dist/brain/agent-registry.d.ts +75 -0
- package/dist/brain/agent-registry.js +124 -0
- package/dist/brain/agent.d.ts +146 -0
- package/dist/brain/agent.js +680 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +33 -0
- package/dist/brain/context.js +77 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +21 -0
- package/dist/brain/loop.js +161 -0
- package/dist/brain/mcp-adapter.d.ts +39 -0
- package/dist/brain/mcp-adapter.js +227 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +10 -0
- package/dist/brain/providers.js +69 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +84 -0
- package/dist/brain/run-tools.d.ts +47 -0
- package/dist/brain/run-tools.js +84 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +35 -0
- package/dist/channels/commands.js +65 -0
- package/dist/channels/discord/channel.d.ts +18 -0
- package/dist/channels/discord/channel.js +154 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +26 -0
- package/dist/channels/slack/channel.js +207 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +21 -0
- package/dist/channels/specs.js +96 -0
- package/dist/channels/telegram/channel.d.ts +18 -0
- package/dist/channels/telegram/channel.js +156 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +23 -0
- package/dist/channels/whatsapp/channel.js +242 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +13 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/manager.d.ts +55 -0
- package/dist/computer/browser/manager.js +496 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +41 -0
- package/dist/config/agent-config.js +14 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +99 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.js +224 -0
- package/dist/control-ui/assets/index-BANXNUyt.js +143 -0
- package/dist/control-ui/assets/index-BSUFrP9R.css +1 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/chat.d.ts +24 -0
- package/dist/gateway/methods/chat.js +19 -0
- package/dist/gateway/methods/config.d.ts +13 -0
- package/dist/gateway/methods/config.js +14 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/prompt-templates.d.ts +23 -0
- package/dist/gateway/methods/prompt-templates.js +82 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +26 -0
- package/dist/gateway/methods/sessions.js +54 -0
- package/dist/gateway/methods/skills.d.ts +35 -0
- package/dist/gateway/methods/skills.js +202 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +21 -0
- package/dist/gateway/methods/tasks.js +46 -0
- package/dist/gateway/methods/teams.d.ts +70 -0
- package/dist/gateway/methods/teams.js +374 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +75 -0
- package/dist/gateway/server.d.ts +4 -0
- package/dist/gateway/server.js +133 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +212 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +43 -0
- package/dist/integrations/registry.js +258 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/extractor.d.ts +6 -0
- package/dist/memory/extractor.js +72 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/store.d.ts +34 -0
- package/dist/memory/store.js +328 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.js +29 -0
- package/dist/prompt-templates/builtins.d.ts +2 -0
- package/dist/prompt-templates/builtins.js +72 -0
- package/dist/prompt-templates/store.d.ts +39 -0
- package/dist/prompt-templates/store.js +174 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/store.d.ts +47 -0
- package/dist/tasks/store.js +193 -0
- package/dist/tasks/types.d.ts +75 -0
- package/dist/tasks/types.js +32 -0
- package/dist/teams/store.d.ts +78 -0
- package/dist/teams/store.js +420 -0
- package/dist/teams/types.d.ts +23 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +16 -0
- package/dist/tools/bash.js +62 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +16 -0
- package/dist/tools/delegate.js +216 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +65 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +29 -0
- package/dist/tools/tasks.js +92 -0
- package/dist/tools/teams.d.ts +7 -0
- package/dist/tools/teams.js +180 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/package.json +90 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ChannelMessage {
|
|
2
|
+
id: number;
|
|
3
|
+
channelId: string;
|
|
4
|
+
sender: string;
|
|
5
|
+
role: "task" | "result" | "error";
|
|
6
|
+
content: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* SQLite-backed ordered message log for inter-agent communication.
|
|
11
|
+
* Each channel is a linear log that any agent (orchestrator or worker) can read/post to.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ChannelStore {
|
|
14
|
+
private db;
|
|
15
|
+
private constructor();
|
|
16
|
+
static create(dbPath: string): Promise<ChannelStore>;
|
|
17
|
+
private createSchema;
|
|
18
|
+
/** Create a new channel, returns its UUID. */
|
|
19
|
+
createChannel(): string;
|
|
20
|
+
/** Post a message to a channel. Returns the message ID. */
|
|
21
|
+
post(channelId: string, sender: string, role: ChannelMessage["role"], content: string): number;
|
|
22
|
+
/** Read all messages in a channel, ordered by ID. */
|
|
23
|
+
read(channelId: string): ChannelMessage[];
|
|
24
|
+
/** Clean up old channel messages older than maxAge ms. */
|
|
25
|
+
cleanup(maxAgeMs: number): number;
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { mkdirSync } from "fs";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import Database from "better-sqlite3";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* SQLite-backed ordered message log for inter-agent communication.
|
|
8
|
+
* Each channel is a linear log that any agent (orchestrator or worker) can read/post to.
|
|
9
|
+
*/
|
|
10
|
+
export class ChannelStore {
|
|
11
|
+
db;
|
|
12
|
+
constructor(db) {
|
|
13
|
+
this.db = db;
|
|
14
|
+
}
|
|
15
|
+
static async create(dbPath) {
|
|
16
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
17
|
+
const db = new Database(dbPath);
|
|
18
|
+
db.pragma("journal_mode = WAL");
|
|
19
|
+
const store = new ChannelStore(db);
|
|
20
|
+
store.createSchema();
|
|
21
|
+
return store;
|
|
22
|
+
}
|
|
23
|
+
createSchema() {
|
|
24
|
+
this.db.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS channel_messages (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
channel_id TEXT NOT NULL,
|
|
28
|
+
sender TEXT NOT NULL,
|
|
29
|
+
role TEXT NOT NULL,
|
|
30
|
+
content TEXT NOT NULL,
|
|
31
|
+
created_at INTEGER NOT NULL
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_channel_messages_channel
|
|
34
|
+
ON channel_messages(channel_id, id);
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
/** Create a new channel, returns its UUID. */
|
|
38
|
+
createChannel() {
|
|
39
|
+
return randomUUID().slice(0, 12);
|
|
40
|
+
}
|
|
41
|
+
/** Post a message to a channel. Returns the message ID. */
|
|
42
|
+
post(channelId, sender, role, content) {
|
|
43
|
+
const info = this.db
|
|
44
|
+
.prepare(`INSERT INTO channel_messages (channel_id, sender, role, content, created_at)
|
|
45
|
+
VALUES (?, ?, ?, ?, ?)`)
|
|
46
|
+
.run(channelId, sender, role, content, Date.now());
|
|
47
|
+
return Number(info.lastInsertRowid);
|
|
48
|
+
}
|
|
49
|
+
/** Read all messages in a channel, ordered by ID. */
|
|
50
|
+
read(channelId) {
|
|
51
|
+
const rows = this.db
|
|
52
|
+
.prepare("SELECT * FROM channel_messages WHERE channel_id = ? ORDER BY id")
|
|
53
|
+
.all(channelId);
|
|
54
|
+
return rows.map(toMessage);
|
|
55
|
+
}
|
|
56
|
+
/** Clean up old channel messages older than maxAge ms. */
|
|
57
|
+
cleanup(maxAgeMs) {
|
|
58
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
59
|
+
const info = this.db
|
|
60
|
+
.prepare("DELETE FROM channel_messages WHERE created_at < ?")
|
|
61
|
+
.run(cutoff);
|
|
62
|
+
return info.changes;
|
|
63
|
+
}
|
|
64
|
+
close() {
|
|
65
|
+
this.db.close();
|
|
66
|
+
logger.info("Channel store closed");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function toMessage(row) {
|
|
70
|
+
return {
|
|
71
|
+
id: row.id,
|
|
72
|
+
channelId: row.channel_id,
|
|
73
|
+
sender: row.sender,
|
|
74
|
+
role: row.role,
|
|
75
|
+
content: row.content,
|
|
76
|
+
createdAt: row.created_at,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type LanguageModel, type ModelMessage } from "ai";
|
|
2
|
+
import type { Session } from "./session.js";
|
|
3
|
+
/** Prefix injected into user messages for automated scheduled task runs. */
|
|
4
|
+
export declare const SCHEDULED_TASK_PREFIX = "[Scheduled Task]";
|
|
5
|
+
/** Prefix for compacted scheduler results in session history. */
|
|
6
|
+
export declare const SCHEDULER_RESULT_PREFIX = "[Scheduler Result |";
|
|
7
|
+
/** Estimate token count from a plain string. */
|
|
8
|
+
export declare function estimateStringTokens(text: string): number;
|
|
9
|
+
/** Estimate token count from message content. */
|
|
10
|
+
export declare function estimateTokens(messages: ModelMessage[]): number;
|
|
11
|
+
/**
|
|
12
|
+
* Compact conversation if estimated tokens exceed the context budget.
|
|
13
|
+
*
|
|
14
|
+
* Budget = contextWindow - systemPromptTokens - outputReserve
|
|
15
|
+
*
|
|
16
|
+
* Returns the (possibly compacted) messages and whether compaction occurred.
|
|
17
|
+
*/
|
|
18
|
+
export declare function compactMessages(model: LanguageModel, messages: ModelMessage[], contextWindow: number, systemPrompt: string): Promise<{
|
|
19
|
+
messages: ModelMessage[];
|
|
20
|
+
compacted: boolean;
|
|
21
|
+
summary?: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Programmatic compaction for scheduler sessions.
|
|
25
|
+
*
|
|
26
|
+
* 1. Collapses each [Scheduled Task] user msg + its assistant reply into a
|
|
27
|
+
* single "[Scheduler Result | ...]" one-liner. Human messages interleaved
|
|
28
|
+
* in the session are left untouched — only messages starting with
|
|
29
|
+
* SCHEDULED_TASK_PREFIX are candidates.
|
|
30
|
+
* 2. Prunes old compacted results, keeping only the most recent MAX_SCHEDULER_RESULTS.
|
|
31
|
+
*
|
|
32
|
+
* No LLM cost — pure string manipulation.
|
|
33
|
+
* Call before appending a new scheduled task so all existing runs become "previous".
|
|
34
|
+
*/
|
|
35
|
+
export declare function compactSchedulerRuns(session: Session): void;
|
|
36
|
+
/** Detect context overflow errors from various providers. */
|
|
37
|
+
export declare function isContextOverflowError(error: unknown): boolean;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { generateText } from "ai";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
const CHARS_PER_TOKEN = 4;
|
|
4
|
+
const SAFETY_MARGIN = 1.2;
|
|
5
|
+
// ── Scheduler compaction constants ──────────────────────────────────────────
|
|
6
|
+
/** Prefix injected into user messages for automated scheduled task runs. */
|
|
7
|
+
export const SCHEDULED_TASK_PREFIX = "[Scheduled Task]";
|
|
8
|
+
/** Prefix for compacted scheduler results in session history. */
|
|
9
|
+
export const SCHEDULER_RESULT_PREFIX = "[Scheduler Result |";
|
|
10
|
+
/** Max characters kept from the assistant response in a compacted line. */
|
|
11
|
+
const SCHEDULER_SUMMARY_MAX_LEN = 200;
|
|
12
|
+
/** Max characters kept from the task prompt in a compacted line. */
|
|
13
|
+
const SCHEDULER_PROMPT_MAX_LEN = 40;
|
|
14
|
+
/** Maximum number of compacted scheduler results retained in the session. */
|
|
15
|
+
const MAX_SCHEDULER_RESULTS = 100;
|
|
16
|
+
/** Keep the most recent N messages intact (not summarized). */
|
|
17
|
+
const KEEP_RECENT = 2;
|
|
18
|
+
/** Reserve tokens for model output (max response length). */
|
|
19
|
+
const OUTPUT_RESERVE = 8_192;
|
|
20
|
+
/** Estimate token count from a plain string. */
|
|
21
|
+
export function estimateStringTokens(text) {
|
|
22
|
+
return Math.ceil(Math.max(0, text.length) / CHARS_PER_TOKEN);
|
|
23
|
+
}
|
|
24
|
+
/** Estimate token count from message content. */
|
|
25
|
+
export function estimateTokens(messages) {
|
|
26
|
+
let chars = 0;
|
|
27
|
+
for (const msg of messages) {
|
|
28
|
+
if (typeof msg.content === "string") {
|
|
29
|
+
chars += msg.content.length;
|
|
30
|
+
}
|
|
31
|
+
else if (Array.isArray(msg.content)) {
|
|
32
|
+
for (const part of msg.content) {
|
|
33
|
+
if ("text" in part && typeof part.text === "string") {
|
|
34
|
+
chars += part.text.length;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return Math.ceil(Math.max(0, chars) / CHARS_PER_TOKEN);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Compact conversation if estimated tokens exceed the context budget.
|
|
43
|
+
*
|
|
44
|
+
* Budget = contextWindow - systemPromptTokens - outputReserve
|
|
45
|
+
*
|
|
46
|
+
* Returns the (possibly compacted) messages and whether compaction occurred.
|
|
47
|
+
*/
|
|
48
|
+
export async function compactMessages(model, messages, contextWindow, systemPrompt) {
|
|
49
|
+
const systemTokens = estimateStringTokens(systemPrompt);
|
|
50
|
+
// Cap output reserve to 10% of context window so small windows aren't swamped
|
|
51
|
+
const outputReserve = Math.min(OUTPUT_RESERVE, Math.floor(contextWindow * 0.1));
|
|
52
|
+
const messageBudget = contextWindow - systemTokens - outputReserve;
|
|
53
|
+
const estimated = estimateTokens(messages);
|
|
54
|
+
if (estimated * SAFETY_MARGIN <= messageBudget) {
|
|
55
|
+
return { messages, compacted: false };
|
|
56
|
+
}
|
|
57
|
+
logger.info(`Compaction triggered: ~${estimated} msg tokens * ${SAFETY_MARGIN} = ~${Math.ceil(estimated * SAFETY_MARGIN)} ` +
|
|
58
|
+
`> budget ${messageBudget} (context ${contextWindow} - system ${systemTokens} - output ${outputReserve})`);
|
|
59
|
+
// Not enough messages for LLM summarization — fall back to dropping oldest
|
|
60
|
+
if (messages.length <= KEEP_RECENT) {
|
|
61
|
+
logger.info("Too few messages for summarization, falling back to truncation");
|
|
62
|
+
return { messages: fallbackTruncate(messages, messageBudget), compacted: true };
|
|
63
|
+
}
|
|
64
|
+
// Dynamically adjust how many recent messages to keep based on budget
|
|
65
|
+
const keepRecent = Math.max(2, Math.min(KEEP_RECENT, messages.length - 2));
|
|
66
|
+
const recent = messages.slice(-keepRecent);
|
|
67
|
+
const old = messages.slice(0, -keepRecent);
|
|
68
|
+
// Extract readable text from old messages (skip tool parts)
|
|
69
|
+
const transcript = buildTranscript(old);
|
|
70
|
+
if (!transcript) {
|
|
71
|
+
logger.info("No text content in old messages, skipping compaction");
|
|
72
|
+
return { messages, compacted: false };
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const summary = await summarize(model, transcript);
|
|
76
|
+
const compacted = [
|
|
77
|
+
{ role: "user", content: `[Prior conversation summary]\n${summary}` },
|
|
78
|
+
...recent,
|
|
79
|
+
];
|
|
80
|
+
const afterTokens = estimateTokens(compacted);
|
|
81
|
+
logger.info(`Compaction complete: ${estimated} -> ${afterTokens} tokens (${messages.length} -> ${compacted.length} messages)`);
|
|
82
|
+
return { messages: compacted, compacted: true, summary };
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger.warn(`Compaction LLM call failed: ${err instanceof Error ? err.message : err}`);
|
|
86
|
+
// Fallback: drop oldest messages until under budget (no summary available)
|
|
87
|
+
return { messages: fallbackTruncate(messages, messageBudget), compacted: true };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Build a readable transcript from messages, skipping tool-use/tool-result parts. */
|
|
91
|
+
function buildTranscript(messages) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
for (const msg of messages) {
|
|
94
|
+
const role = msg.role === "user" ? "User" : "Assistant";
|
|
95
|
+
if (typeof msg.content === "string") {
|
|
96
|
+
lines.push(`${role}: ${msg.content}`);
|
|
97
|
+
}
|
|
98
|
+
else if (Array.isArray(msg.content)) {
|
|
99
|
+
const textParts = [];
|
|
100
|
+
for (const part of msg.content) {
|
|
101
|
+
if ("text" in part && typeof part.text === "string") {
|
|
102
|
+
textParts.push(part.text);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (textParts.length > 0) {
|
|
106
|
+
lines.push(`${role}: ${textParts.join(" ")}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return lines.length > 0 ? lines.join("\n") : null;
|
|
111
|
+
}
|
|
112
|
+
/** Call LLM to summarize the conversation transcript. */
|
|
113
|
+
async function summarize(model, transcript) {
|
|
114
|
+
const { text } = await generateText({
|
|
115
|
+
model,
|
|
116
|
+
system: "You are a conversation summarizer. Produce a concise summary that preserves: " +
|
|
117
|
+
"key facts about the user (name, preferences, location, etc.), " +
|
|
118
|
+
"important decisions or agreements, " +
|
|
119
|
+
"context needed to continue the conversation naturally. " +
|
|
120
|
+
"Use bullet points. Be brief but complete.",
|
|
121
|
+
messages: [
|
|
122
|
+
{
|
|
123
|
+
role: "user",
|
|
124
|
+
content: `Summarize this conversation:\n\n${transcript}`,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
return text;
|
|
129
|
+
}
|
|
130
|
+
/** Fallback: drop oldest messages until estimated tokens fit within budget. Keep at least 2. */
|
|
131
|
+
function fallbackTruncate(messages, messageBudget) {
|
|
132
|
+
let totalTokens = estimateTokens(messages);
|
|
133
|
+
// Walk forward, subtracting each dropped message's tokens until under budget
|
|
134
|
+
for (let i = 0; i < messages.length - 2; i++) {
|
|
135
|
+
if (totalTokens * SAFETY_MARGIN <= messageBudget) {
|
|
136
|
+
return messages.slice(i);
|
|
137
|
+
}
|
|
138
|
+
totalTokens -= estimateTokens([messages[i]]);
|
|
139
|
+
}
|
|
140
|
+
// Nothing fits — return the last 2 messages
|
|
141
|
+
return messages.slice(-2);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Programmatic compaction for scheduler sessions.
|
|
145
|
+
*
|
|
146
|
+
* 1. Collapses each [Scheduled Task] user msg + its assistant reply into a
|
|
147
|
+
* single "[Scheduler Result | ...]" one-liner. Human messages interleaved
|
|
148
|
+
* in the session are left untouched — only messages starting with
|
|
149
|
+
* SCHEDULED_TASK_PREFIX are candidates.
|
|
150
|
+
* 2. Prunes old compacted results, keeping only the most recent MAX_SCHEDULER_RESULTS.
|
|
151
|
+
*
|
|
152
|
+
* No LLM cost — pure string manipulation.
|
|
153
|
+
* Call before appending a new scheduled task so all existing runs become "previous".
|
|
154
|
+
*/
|
|
155
|
+
export function compactSchedulerRuns(session) {
|
|
156
|
+
const messages = session.getMessages();
|
|
157
|
+
let changed = 0;
|
|
158
|
+
// Single backwards pass: compact uncompacted task runs and collect compacted indices.
|
|
159
|
+
// Backwards iteration keeps splice indices stable for the compaction step.
|
|
160
|
+
const compactedIndices = [];
|
|
161
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
162
|
+
const msg = messages[i];
|
|
163
|
+
if (msg.role !== "user" || typeof msg.content !== "string")
|
|
164
|
+
continue;
|
|
165
|
+
// Already compacted — track index for pruning
|
|
166
|
+
if (msg.content.startsWith(SCHEDULER_RESULT_PREFIX)) {
|
|
167
|
+
compactedIndices.push(i);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// Uncompacted scheduled task — compact if followed by assistant reply
|
|
171
|
+
if (!msg.content.startsWith(SCHEDULED_TASK_PREFIX))
|
|
172
|
+
continue;
|
|
173
|
+
const next = messages[i + 1];
|
|
174
|
+
if (!next || next.role !== "assistant")
|
|
175
|
+
continue;
|
|
176
|
+
const prompt = msg.content.slice(SCHEDULED_TASK_PREFIX.length).trim().slice(0, SCHEDULER_PROMPT_MAX_LEN);
|
|
177
|
+
const response = typeof next.content === "string"
|
|
178
|
+
? next.content.slice(0, SCHEDULER_SUMMARY_MAX_LEN)
|
|
179
|
+
: "completed";
|
|
180
|
+
const timestamp = new Date().toISOString().slice(0, 16).replace("T", " ");
|
|
181
|
+
messages.splice(i, 2, { role: "user", content: `${SCHEDULER_RESULT_PREFIX} ${timestamp} | ${prompt}] ${response}` });
|
|
182
|
+
compactedIndices.push(i);
|
|
183
|
+
changed++;
|
|
184
|
+
}
|
|
185
|
+
// compactedIndices is in descending order from the backwards pass — reverse for pruning
|
|
186
|
+
compactedIndices.reverse();
|
|
187
|
+
// Prune oldest compacted results beyond the cap
|
|
188
|
+
const pruneCount = compactedIndices.length - MAX_SCHEDULER_RESULTS;
|
|
189
|
+
if (pruneCount > 0) {
|
|
190
|
+
// Remove oldest (lowest-index) entries; splice backwards to keep indices stable
|
|
191
|
+
for (let i = pruneCount - 1; i >= 0; i--) {
|
|
192
|
+
messages.splice(compactedIndices[i], 1);
|
|
193
|
+
}
|
|
194
|
+
changed += pruneCount;
|
|
195
|
+
logger.info(`[scheduler] Pruned ${pruneCount} old result(s), keeping ${MAX_SCHEDULER_RESULTS}`);
|
|
196
|
+
}
|
|
197
|
+
if (changed > 0) {
|
|
198
|
+
session.replaceMessages(messages);
|
|
199
|
+
logger.info(`[scheduler] Compacted/pruned ${changed} scheduler message(s)`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/** Detect context overflow errors from various providers. */
|
|
203
|
+
export function isContextOverflowError(error) {
|
|
204
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
205
|
+
const lower = msg.toLowerCase();
|
|
206
|
+
return (lower.includes("request_too_large") ||
|
|
207
|
+
lower.includes("context length exceeded") ||
|
|
208
|
+
lower.includes("maximum context length") ||
|
|
209
|
+
lower.includes("prompt is too long") ||
|
|
210
|
+
lower.includes("exceeds model context window") ||
|
|
211
|
+
lower.includes("request exceeds the maximum size") ||
|
|
212
|
+
(lower.includes("request size exceeds") && lower.includes("context window")) ||
|
|
213
|
+
(lower.includes("413") && lower.includes("too large")));
|
|
214
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BashSecurityMode } from "../config.js";
|
|
2
|
+
export interface ContextInput {
|
|
3
|
+
identity: string;
|
|
4
|
+
/** Model identifier shown to the model so it can self-identify (e.g. "claude-3.5-sonnet"). */
|
|
5
|
+
modelId?: string;
|
|
6
|
+
language?: string;
|
|
7
|
+
skillPrompts?: string[];
|
|
8
|
+
skillListing?: string;
|
|
9
|
+
/** Compact listing of all available integrations (always shown). */
|
|
10
|
+
integrationListing?: string;
|
|
11
|
+
/** System prompts for currently active integrations only. */
|
|
12
|
+
activeIntegrationPrompts?: string[];
|
|
13
|
+
hasMemory?: boolean;
|
|
14
|
+
bashMode?: BashSecurityMode;
|
|
15
|
+
bashSafeBins?: string[];
|
|
16
|
+
hasDesktop?: boolean;
|
|
17
|
+
/** Whether schedule tools are available. */
|
|
18
|
+
hasScheduler?: boolean;
|
|
19
|
+
/** Whether this is a scheduled task execution (not a user conversation). */
|
|
20
|
+
scheduledTask?: boolean;
|
|
21
|
+
/** Whether this session is the team's shared scheduler session. */
|
|
22
|
+
schedulerSession?: boolean;
|
|
23
|
+
/** Whether TTS speak tool is available. */
|
|
24
|
+
hasTTS?: boolean;
|
|
25
|
+
/** Channel type for this session (e.g. "telegram", "gateway"). */
|
|
26
|
+
channelType?: string;
|
|
27
|
+
/** Workers the orchestrator can delegate to. */
|
|
28
|
+
delegationWorkers?: Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
identity: string;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export declare function buildSystemPrompt(input: ContextInput): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export function buildSystemPrompt(input) {
|
|
2
|
+
const parts = [];
|
|
3
|
+
// Language directive
|
|
4
|
+
if (input.language === "auto" || !input.language) {
|
|
5
|
+
parts.push(`Reply in the user's language (default: English). Internal reasoning in English.`);
|
|
6
|
+
}
|
|
7
|
+
else if (input.language !== "English") {
|
|
8
|
+
parts.push(`Reply in ${input.language}. Internal reasoning in English.`);
|
|
9
|
+
}
|
|
10
|
+
parts.push(input.identity);
|
|
11
|
+
if (input.modelId) {
|
|
12
|
+
parts.push(`You are powered by the ${input.modelId} model.`);
|
|
13
|
+
}
|
|
14
|
+
parts.push(`Current time: ${new Date().toISOString()}`);
|
|
15
|
+
// Compact tool strategy — only cross-tool orchestration that can't live in individual tool descriptions
|
|
16
|
+
const strategy = [];
|
|
17
|
+
strategy.push(`Web: try web_fetch first → browser tools if blocked/JS-heavy. Never give up without trying browser.`);
|
|
18
|
+
if (input.hasTTS) {
|
|
19
|
+
const isLocal = !input.channelType || input.channelType === "gateway";
|
|
20
|
+
if (isLocal) {
|
|
21
|
+
strategy.push(`Voice: prefer speak for short replies in this local session.`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
strategy.push(`Voice: only use speak if user explicitly asks (remote ${input.channelType} session).`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (input.hasDesktop) {
|
|
28
|
+
strategy.push(`Desktop: screenshot → act → screenshot → verify.`);
|
|
29
|
+
}
|
|
30
|
+
parts.push(`## Tools\n${strategy.map((s) => `- ${s}`).join("\n")}`);
|
|
31
|
+
// Bash — only allowlist mode needs extra context (safe bins list)
|
|
32
|
+
if (input.bashMode === "allowlist") {
|
|
33
|
+
const bins = input.bashSafeBins?.join(", ") ?? "";
|
|
34
|
+
parts.push(`## Bash\nAllowlist mode. Safe commands: ${bins}. If blocked, suggest an allowed alternative.`);
|
|
35
|
+
}
|
|
36
|
+
if (input.skillPrompts?.length) {
|
|
37
|
+
for (const prompt of input.skillPrompts) {
|
|
38
|
+
parts.push(prompt);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (input.skillListing) {
|
|
42
|
+
parts.push(input.skillListing);
|
|
43
|
+
}
|
|
44
|
+
if (input.integrationListing) {
|
|
45
|
+
parts.push(input.integrationListing);
|
|
46
|
+
}
|
|
47
|
+
if (input.activeIntegrationPrompts?.length) {
|
|
48
|
+
for (const prompt of input.activeIntegrationPrompts) {
|
|
49
|
+
parts.push(prompt);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Delegation — keep because it's multi-tool orchestration
|
|
53
|
+
if (input.delegationWorkers?.length) {
|
|
54
|
+
const workerList = input.delegationWorkers
|
|
55
|
+
.map((w) => `- **${w.name}**: ${w.identity}`)
|
|
56
|
+
.join("\n");
|
|
57
|
+
parts.push(`## Delegation
|
|
58
|
+
Workers run in background. Available:
|
|
59
|
+
${workerList}
|
|
60
|
+
Delegate → get channel ID → notified on completion → read_channel for results.
|
|
61
|
+
Workers have no conversation context — include everything they need in the task.`);
|
|
62
|
+
}
|
|
63
|
+
// Scheduler session guidance
|
|
64
|
+
if (input.schedulerSession) {
|
|
65
|
+
parts.push(`## Scheduler Session
|
|
66
|
+
Shared session. [Scheduled Task] = automated. [User via ...] = human. [Scheduler Result | ...] = compacted history.`);
|
|
67
|
+
}
|
|
68
|
+
// Scheduled task execution mode (non-interactive)
|
|
69
|
+
if (input.scheduledTask) {
|
|
70
|
+
parts.push(`## Scheduled Task Mode
|
|
71
|
+
Automated task — execute immediately, do not ask questions.
|
|
72
|
+
Reminders: deliver directly. Data tasks: fetch and present.
|
|
73
|
+
Conditional: reply with [SKIP] + reason if nothing noteworthy.
|
|
74
|
+
Keep replies concise — this is a notification.`);
|
|
75
|
+
}
|
|
76
|
+
return parts.join("\n\n");
|
|
77
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type DelegationStatus = "running" | "completed" | "failed";
|
|
2
|
+
export interface DelegationRecord {
|
|
3
|
+
id: string;
|
|
4
|
+
agentId: string;
|
|
5
|
+
sessionKey: string;
|
|
6
|
+
task: string;
|
|
7
|
+
channelId: string | null;
|
|
8
|
+
status: DelegationStatus;
|
|
9
|
+
result: string | null;
|
|
10
|
+
error: string | null;
|
|
11
|
+
createdAt: number;
|
|
12
|
+
completedAt: number | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* SQLite-backed persistence for delegation tasks.
|
|
16
|
+
* Shares the same DB file as MemoryStore and ScheduleStore.
|
|
17
|
+
*/
|
|
18
|
+
export declare class DelegationStore {
|
|
19
|
+
private db;
|
|
20
|
+
private constructor();
|
|
21
|
+
static create(dbPath: string): Promise<DelegationStore>;
|
|
22
|
+
private createSchema;
|
|
23
|
+
insert(record: Omit<DelegationRecord, "result" | "error" | "completedAt"> & {
|
|
24
|
+
status: "running";
|
|
25
|
+
}): void;
|
|
26
|
+
markCompleted(id: string, result: string): void;
|
|
27
|
+
markFailed(id: string, error: string): void;
|
|
28
|
+
getById(id: string): DelegationRecord | null;
|
|
29
|
+
listBySession(sessionKey: string, status?: DelegationStatus): DelegationRecord[];
|
|
30
|
+
/** Clean up old completed/failed delegations older than maxAge ms. */
|
|
31
|
+
cleanup(maxAgeMs: number): number;
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { mkdirSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* SQLite-backed persistence for delegation tasks.
|
|
7
|
+
* Shares the same DB file as MemoryStore and ScheduleStore.
|
|
8
|
+
*/
|
|
9
|
+
export class DelegationStore {
|
|
10
|
+
db;
|
|
11
|
+
constructor(db) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
}
|
|
14
|
+
static async create(dbPath) {
|
|
15
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
16
|
+
const db = new Database(dbPath);
|
|
17
|
+
db.pragma("journal_mode = WAL");
|
|
18
|
+
const store = new DelegationStore(db);
|
|
19
|
+
store.createSchema();
|
|
20
|
+
return store;
|
|
21
|
+
}
|
|
22
|
+
createSchema() {
|
|
23
|
+
this.db.exec(`
|
|
24
|
+
CREATE TABLE IF NOT EXISTS delegations (
|
|
25
|
+
id TEXT PRIMARY KEY,
|
|
26
|
+
agent_id TEXT NOT NULL,
|
|
27
|
+
session_key TEXT NOT NULL,
|
|
28
|
+
task TEXT NOT NULL,
|
|
29
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
30
|
+
result TEXT,
|
|
31
|
+
error TEXT,
|
|
32
|
+
created_at INTEGER NOT NULL,
|
|
33
|
+
completed_at INTEGER
|
|
34
|
+
);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_delegations_session ON delegations(session_key);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_delegations_status ON delegations(status);
|
|
37
|
+
`);
|
|
38
|
+
// Migration: add channel_id column if missing (graceful for existing DBs)
|
|
39
|
+
try {
|
|
40
|
+
this.db.exec("ALTER TABLE delegations ADD COLUMN channel_id TEXT");
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Column already exists — ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
insert(record) {
|
|
47
|
+
this.db
|
|
48
|
+
.prepare(`INSERT INTO delegations (id, agent_id, session_key, task, channel_id, status, created_at)
|
|
49
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
50
|
+
.run(record.id, record.agentId, record.sessionKey, record.task, record.channelId ?? null, record.status, record.createdAt);
|
|
51
|
+
}
|
|
52
|
+
markCompleted(id, result) {
|
|
53
|
+
this.db
|
|
54
|
+
.prepare(`UPDATE delegations SET status = 'completed', result = ?, completed_at = ? WHERE id = ?`)
|
|
55
|
+
.run(result, Date.now(), id);
|
|
56
|
+
}
|
|
57
|
+
markFailed(id, error) {
|
|
58
|
+
this.db
|
|
59
|
+
.prepare(`UPDATE delegations SET status = 'failed', error = ?, completed_at = ? WHERE id = ?`)
|
|
60
|
+
.run(error, Date.now(), id);
|
|
61
|
+
}
|
|
62
|
+
getById(id) {
|
|
63
|
+
const row = this.db
|
|
64
|
+
.prepare("SELECT * FROM delegations WHERE id = ?")
|
|
65
|
+
.get(id);
|
|
66
|
+
return row ? toRecord(row) : null;
|
|
67
|
+
}
|
|
68
|
+
listBySession(sessionKey, status) {
|
|
69
|
+
if (status) {
|
|
70
|
+
const rows = this.db
|
|
71
|
+
.prepare("SELECT * FROM delegations WHERE session_key = ? AND status = ? ORDER BY created_at DESC")
|
|
72
|
+
.all(sessionKey, status);
|
|
73
|
+
return rows.map(toRecord);
|
|
74
|
+
}
|
|
75
|
+
const rows = this.db
|
|
76
|
+
.prepare("SELECT * FROM delegations WHERE session_key = ? ORDER BY created_at DESC")
|
|
77
|
+
.all(sessionKey);
|
|
78
|
+
return rows.map(toRecord);
|
|
79
|
+
}
|
|
80
|
+
/** Clean up old completed/failed delegations older than maxAge ms. */
|
|
81
|
+
cleanup(maxAgeMs) {
|
|
82
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
83
|
+
const info = this.db
|
|
84
|
+
.prepare("DELETE FROM delegations WHERE status != 'running' AND completed_at <= ?")
|
|
85
|
+
.run(cutoff);
|
|
86
|
+
return info.changes;
|
|
87
|
+
}
|
|
88
|
+
close() {
|
|
89
|
+
this.db.close();
|
|
90
|
+
logger.info("Delegation store closed");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function toRecord(row) {
|
|
94
|
+
return {
|
|
95
|
+
id: row.id,
|
|
96
|
+
agentId: row.agent_id,
|
|
97
|
+
sessionKey: row.session_key,
|
|
98
|
+
task: row.task,
|
|
99
|
+
channelId: row.channel_id ?? null,
|
|
100
|
+
status: row.status,
|
|
101
|
+
result: row.result ?? null,
|
|
102
|
+
error: row.error ?? null,
|
|
103
|
+
createdAt: row.created_at,
|
|
104
|
+
completedAt: row.completed_at ?? null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type LanguageModel, type ModelMessage, type ToolSet } from "ai";
|
|
2
|
+
export interface RunLoopResult {
|
|
3
|
+
/** The final text reply. */
|
|
4
|
+
text: string;
|
|
5
|
+
/** All response messages (assistant + tool) generated during the run. */
|
|
6
|
+
responseMessages: ModelMessage[];
|
|
7
|
+
}
|
|
8
|
+
export declare function runLoop(opts: {
|
|
9
|
+
model: LanguageModel;
|
|
10
|
+
system: string;
|
|
11
|
+
messages: ModelMessage[];
|
|
12
|
+
tools: ToolSet;
|
|
13
|
+
sessionKey: string;
|
|
14
|
+
maxSteps?: number;
|
|
15
|
+
/** Worker agent ID (e.g. "researcher"). Undefined for the orchestrator. */
|
|
16
|
+
agentId?: string;
|
|
17
|
+
/** Suppress all WebSocket broadcasts (used for background workers). */
|
|
18
|
+
silent?: boolean;
|
|
19
|
+
/** Human-readable label for logs (e.g. team name instead of UUID). Falls back to sessionKey. */
|
|
20
|
+
sessionLabel?: string;
|
|
21
|
+
}): Promise<RunLoopResult>;
|