talon-agent 1.0.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/README.md +137 -0
- package/bin/talon.js +5 -0
- package/package.json +86 -0
- package/prompts/base.md +13 -0
- package/prompts/custom.md.example +22 -0
- package/prompts/dream.md +41 -0
- package/prompts/identity.md +45 -0
- package/prompts/teams.md +52 -0
- package/prompts/telegram.md +89 -0
- package/prompts/terminal.md +13 -0
- package/src/__tests__/chat-id.test.ts +91 -0
- package/src/__tests__/chat-settings.test.ts +337 -0
- package/src/__tests__/config.test.ts +546 -0
- package/src/__tests__/cron-store.test.ts +440 -0
- package/src/__tests__/daily-log.test.ts +146 -0
- package/src/__tests__/dispatcher.test.ts +383 -0
- package/src/__tests__/errors.test.ts +240 -0
- package/src/__tests__/fuzz.test.ts +302 -0
- package/src/__tests__/gateway-actions.test.ts +1453 -0
- package/src/__tests__/gateway-context.test.ts +102 -0
- package/src/__tests__/gateway-http.test.ts +245 -0
- package/src/__tests__/handlers.test.ts +351 -0
- package/src/__tests__/history-persistence.test.ts +172 -0
- package/src/__tests__/history.test.ts +659 -0
- package/src/__tests__/integration.test.ts +189 -0
- package/src/__tests__/log.test.ts +110 -0
- package/src/__tests__/media-index.test.ts +277 -0
- package/src/__tests__/plugin.test.ts +317 -0
- package/src/__tests__/prompt-builder.test.ts +71 -0
- package/src/__tests__/sessions.test.ts +594 -0
- package/src/__tests__/teams-frontend.test.ts +239 -0
- package/src/__tests__/telegram.test.ts +177 -0
- package/src/__tests__/terminal-commands.test.ts +367 -0
- package/src/__tests__/terminal-frontend.test.ts +141 -0
- package/src/__tests__/terminal-renderer.test.ts +278 -0
- package/src/__tests__/watchdog.test.ts +287 -0
- package/src/__tests__/workspace.test.ts +184 -0
- package/src/backend/claude-sdk/index.ts +438 -0
- package/src/backend/claude-sdk/tools.ts +605 -0
- package/src/backend/opencode/index.ts +252 -0
- package/src/bootstrap.ts +134 -0
- package/src/cli.ts +611 -0
- package/src/core/cron.ts +148 -0
- package/src/core/dispatcher.ts +126 -0
- package/src/core/dream.ts +295 -0
- package/src/core/errors.ts +206 -0
- package/src/core/gateway-actions.ts +267 -0
- package/src/core/gateway.ts +258 -0
- package/src/core/plugin.ts +432 -0
- package/src/core/prompt-builder.ts +43 -0
- package/src/core/pulse.ts +175 -0
- package/src/core/types.ts +85 -0
- package/src/frontend/teams/actions.ts +101 -0
- package/src/frontend/teams/formatting.ts +220 -0
- package/src/frontend/teams/graph.ts +297 -0
- package/src/frontend/teams/index.ts +308 -0
- package/src/frontend/teams/proxy-fetch.ts +28 -0
- package/src/frontend/teams/tools.ts +177 -0
- package/src/frontend/telegram/actions.ts +437 -0
- package/src/frontend/telegram/admin.ts +178 -0
- package/src/frontend/telegram/callbacks.ts +251 -0
- package/src/frontend/telegram/commands.ts +543 -0
- package/src/frontend/telegram/formatting.ts +101 -0
- package/src/frontend/telegram/handlers.ts +1008 -0
- package/src/frontend/telegram/helpers.ts +105 -0
- package/src/frontend/telegram/index.ts +130 -0
- package/src/frontend/telegram/middleware.ts +177 -0
- package/src/frontend/telegram/userbot.ts +546 -0
- package/src/frontend/terminal/commands.ts +303 -0
- package/src/frontend/terminal/index.ts +282 -0
- package/src/frontend/terminal/input.ts +297 -0
- package/src/frontend/terminal/renderer.ts +248 -0
- package/src/index.ts +144 -0
- package/src/login.ts +89 -0
- package/src/storage/chat-settings.ts +218 -0
- package/src/storage/cron-store.ts +165 -0
- package/src/storage/daily-log.ts +97 -0
- package/src/storage/history.ts +278 -0
- package/src/storage/media-index.ts +116 -0
- package/src/storage/sessions.ts +328 -0
- package/src/util/chat-id.ts +21 -0
- package/src/util/config.ts +244 -0
- package/src/util/log.ts +122 -0
- package/src/util/paths.ts +80 -0
- package/src/util/time.ts +86 -0
- package/src/util/trace.ts +35 -0
- package/src/util/watchdog.ts +108 -0
- package/src/util/workspace.ts +208 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode backend — uses the OpenCode SDK as an alternative to Claude Agent SDK.
|
|
3
|
+
*
|
|
4
|
+
* Implements the same QueryBackend interface so it's a drop-in replacement.
|
|
5
|
+
* Manages an OpenCode server process and routes queries through it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createOpencode, type OpencodeClient } from "@opencode-ai/sdk";
|
|
9
|
+
import type { TalonConfig } from "../../util/config.js";
|
|
10
|
+
import type { QueryParams, QueryResult } from "../../core/types.js";
|
|
11
|
+
import {
|
|
12
|
+
getSession,
|
|
13
|
+
incrementTurns,
|
|
14
|
+
recordUsage,
|
|
15
|
+
setSessionId,
|
|
16
|
+
setSessionName,
|
|
17
|
+
resetSession,
|
|
18
|
+
} from "../../storage/sessions.js";
|
|
19
|
+
import { getChatSettings } from "../../storage/chat-settings.js";
|
|
20
|
+
import { getRecentHistory } from "../../storage/history.js";
|
|
21
|
+
import { classify } from "../../core/errors.js";
|
|
22
|
+
import { log, logError, logWarn } from "../../util/log.js";
|
|
23
|
+
import { traceMessage } from "../../util/trace.js";
|
|
24
|
+
|
|
25
|
+
// ── State ───────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
let config: TalonConfig;
|
|
28
|
+
let client: OpencodeClient | null = null;
|
|
29
|
+
let serverHandle: { url: string; close(): void } | null = null;
|
|
30
|
+
let gatewayPortFn: () => number = () => 19876;
|
|
31
|
+
|
|
32
|
+
export function initOpenCodeAgent(
|
|
33
|
+
cfg: TalonConfig,
|
|
34
|
+
getGatewayPort?: () => number,
|
|
35
|
+
): void {
|
|
36
|
+
config = cfg;
|
|
37
|
+
if (getGatewayPort) gatewayPortFn = getGatewayPort;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Server lifecycle ────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
async function ensureServer(): Promise<OpencodeClient> {
|
|
43
|
+
if (client) return client;
|
|
44
|
+
|
|
45
|
+
log("agent", "Starting OpenCode server...");
|
|
46
|
+
const result = await createOpencode({
|
|
47
|
+
port: 4096,
|
|
48
|
+
timeout: 10_000,
|
|
49
|
+
});
|
|
50
|
+
client = result.client;
|
|
51
|
+
serverHandle = result.server;
|
|
52
|
+
log("agent", `OpenCode server running at ${result.server.url}`);
|
|
53
|
+
|
|
54
|
+
// Register our MCP tools server with OpenCode
|
|
55
|
+
try {
|
|
56
|
+
const toolsPath = new URL("../claude-sdk/tools.ts", import.meta.url)
|
|
57
|
+
.pathname;
|
|
58
|
+
await client.mcp.add({
|
|
59
|
+
body: {
|
|
60
|
+
name: "talon-tools",
|
|
61
|
+
config: {
|
|
62
|
+
type: "local" as const,
|
|
63
|
+
command: ["node", "--import", "tsx", toolsPath],
|
|
64
|
+
environment: {
|
|
65
|
+
TALON_BRIDGE_URL: `http://127.0.0.1:${gatewayPortFn()}`,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
log("agent", "Registered talon-tools MCP server with OpenCode");
|
|
71
|
+
} catch (err) {
|
|
72
|
+
logWarn(
|
|
73
|
+
"agent",
|
|
74
|
+
`MCP registration failed (tools may not be available): ${err instanceof Error ? err.message : err}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return client;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function stopOpenCodeServer(): void {
|
|
82
|
+
if (serverHandle) {
|
|
83
|
+
serverHandle.close();
|
|
84
|
+
serverHandle = null;
|
|
85
|
+
client = null;
|
|
86
|
+
log("agent", "OpenCode server stopped");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Session management ──────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
async function ensureSession(
|
|
93
|
+
oc: OpencodeClient,
|
|
94
|
+
chatId: string,
|
|
95
|
+
): Promise<string> {
|
|
96
|
+
const session = getSession(chatId);
|
|
97
|
+
|
|
98
|
+
if (session.sessionId) {
|
|
99
|
+
// Verify session still exists
|
|
100
|
+
try {
|
|
101
|
+
await oc.session.get({ path: { id: session.sessionId } });
|
|
102
|
+
return session.sessionId;
|
|
103
|
+
} catch {
|
|
104
|
+
logWarn(
|
|
105
|
+
"agent",
|
|
106
|
+
`[${chatId}] Session ${session.sessionId} expired, creating new`,
|
|
107
|
+
);
|
|
108
|
+
resetSession(chatId);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Create new session
|
|
113
|
+
const resp = await oc.session.create({
|
|
114
|
+
body: { title: `Chat ${chatId}` },
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Extract session ID from response
|
|
118
|
+
const data = resp.data as Record<string, unknown> | undefined;
|
|
119
|
+
const newId = (data?.id as string) ?? String(Date.now());
|
|
120
|
+
setSessionId(chatId, newId);
|
|
121
|
+
log("agent", `[${chatId}] Created OpenCode session: ${newId}`);
|
|
122
|
+
return newId;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Main handler ────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
export async function handleMessage(
|
|
128
|
+
params: QueryParams,
|
|
129
|
+
_retried = false,
|
|
130
|
+
): Promise<QueryResult> {
|
|
131
|
+
if (!config) throw new Error("OpenCode agent not initialized");
|
|
132
|
+
|
|
133
|
+
const { chatId, text, senderName, isGroup, onTextBlock } = params;
|
|
134
|
+
const t0 = Date.now();
|
|
135
|
+
|
|
136
|
+
const chatSettings = getChatSettings(chatId);
|
|
137
|
+
const activeModel = chatSettings.model ?? config.model;
|
|
138
|
+
|
|
139
|
+
// Resolve provider and model ID
|
|
140
|
+
const providerID = activeModel.includes("gpt")
|
|
141
|
+
? "openai"
|
|
142
|
+
: activeModel.includes("gemini")
|
|
143
|
+
? "google"
|
|
144
|
+
: "anthropic";
|
|
145
|
+
const modelID = activeModel;
|
|
146
|
+
|
|
147
|
+
const oc = await ensureServer();
|
|
148
|
+
const sessionId = await ensureSession(oc, chatId);
|
|
149
|
+
|
|
150
|
+
// Build prompt with group context
|
|
151
|
+
const msgIdHint = params.messageId ? ` [msg_id:${params.messageId}]` : "";
|
|
152
|
+
let continuityPrefix = "";
|
|
153
|
+
const session = getSession(chatId);
|
|
154
|
+
if (!session.sessionId && session.turns > 0) {
|
|
155
|
+
const recent = getRecentHistory(chatId, 3);
|
|
156
|
+
if (recent.length > 0) {
|
|
157
|
+
const ctx = recent
|
|
158
|
+
.map(
|
|
159
|
+
(m) =>
|
|
160
|
+
`[${new Date(m.timestamp).toISOString().slice(11, 16)}] ${m.senderName}: ${m.text.slice(0, 300)}`,
|
|
161
|
+
)
|
|
162
|
+
.join("\n");
|
|
163
|
+
continuityPrefix = `[Session resumed — recent context:\n${ctx}]\n\n`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const prompt = isGroup
|
|
168
|
+
? `${continuityPrefix}[${senderName}]${msgIdHint}: ${text}`
|
|
169
|
+
: `${continuityPrefix}${text}${msgIdHint}`;
|
|
170
|
+
|
|
171
|
+
log("agent", `[${chatId}] <- (${text.length} chars)`);
|
|
172
|
+
traceMessage(chatId, "in", text, { senderName, isGroup });
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const resp = await oc.session.prompt({
|
|
176
|
+
body: {
|
|
177
|
+
parts: [{ type: "text" as const, text: prompt }],
|
|
178
|
+
model: { providerID, modelID },
|
|
179
|
+
system: config.systemPrompt,
|
|
180
|
+
},
|
|
181
|
+
path: { id: sessionId },
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Extract text from response parts
|
|
185
|
+
const data = resp.data as Record<string, unknown> | undefined;
|
|
186
|
+
const parts = (data?.parts as Array<Record<string, unknown>>) ?? [];
|
|
187
|
+
let responseText = "";
|
|
188
|
+
let toolCalls = 0;
|
|
189
|
+
|
|
190
|
+
for (const part of parts) {
|
|
191
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
192
|
+
if (responseText && onTextBlock) {
|
|
193
|
+
await onTextBlock(responseText);
|
|
194
|
+
}
|
|
195
|
+
responseText = part.text;
|
|
196
|
+
} else if (part.type === "tool") {
|
|
197
|
+
toolCalls++;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const durationMs = Date.now() - t0;
|
|
202
|
+
|
|
203
|
+
// Persist session state
|
|
204
|
+
incrementTurns(chatId);
|
|
205
|
+
recordUsage(chatId, {
|
|
206
|
+
inputTokens: 0, // OpenCode doesn't expose token counts in the same way
|
|
207
|
+
outputTokens: 0,
|
|
208
|
+
cacheRead: 0,
|
|
209
|
+
cacheWrite: 0,
|
|
210
|
+
durationMs,
|
|
211
|
+
model: activeModel,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (session.turns === 0 && text) {
|
|
215
|
+
const cleanText = text
|
|
216
|
+
.replace(/^\[.*?\]\s*/g, "")
|
|
217
|
+
.replace(/\[msg_id:\d+\]\s*/g, "")
|
|
218
|
+
.trim();
|
|
219
|
+
if (cleanText) {
|
|
220
|
+
setSessionName(
|
|
221
|
+
chatId,
|
|
222
|
+
cleanText.length > 30 ? cleanText.slice(0, 30) + "..." : cleanText,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
log(
|
|
228
|
+
"agent",
|
|
229
|
+
`[${chatId}] -> (${durationMs}ms${toolCalls > 0 ? ` tools=${toolCalls}` : ""})`,
|
|
230
|
+
);
|
|
231
|
+
traceMessage(chatId, "out", responseText, { durationMs, toolCalls });
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
text: responseText.trim(),
|
|
235
|
+
durationMs,
|
|
236
|
+
inputTokens: 0,
|
|
237
|
+
outputTokens: 0,
|
|
238
|
+
cacheRead: 0,
|
|
239
|
+
cacheWrite: 0,
|
|
240
|
+
};
|
|
241
|
+
} catch (err) {
|
|
242
|
+
const classified = classify(err);
|
|
243
|
+
// Session expired — reset and retry once
|
|
244
|
+
if (classified.reason === "session_expired" && !_retried) {
|
|
245
|
+
logWarn("agent", `[${chatId}] OpenCode session expired, retrying`);
|
|
246
|
+
resetSession(chatId);
|
|
247
|
+
return handleMessage(params, true);
|
|
248
|
+
}
|
|
249
|
+
logError("agent", `[${chatId}] OpenCode error: ${classified.message}`);
|
|
250
|
+
throw classified;
|
|
251
|
+
}
|
|
252
|
+
}
|
package/src/bootstrap.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared bootstrap logic used by both the main entry point (index.ts)
|
|
3
|
+
* and the CLI chat command (cli.ts).
|
|
4
|
+
*
|
|
5
|
+
* Handles: config loading, env vars, plugin loading, workspace init,
|
|
6
|
+
* storage loading, backend + dispatcher initialization.
|
|
7
|
+
*
|
|
8
|
+
* Frontend creation and lifecycle remain with the callers since they
|
|
9
|
+
* differ (index.ts selects dynamically, cli.ts always uses terminal).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { loadConfig, rebuildSystemPrompt } from "./util/config.js";
|
|
13
|
+
import { initWorkspace } from "./util/workspace.js";
|
|
14
|
+
import { loadSessions } from "./storage/sessions.js";
|
|
15
|
+
import { loadChatSettings } from "./storage/chat-settings.js";
|
|
16
|
+
import { loadCronJobs } from "./storage/cron-store.js";
|
|
17
|
+
import { loadHistory } from "./storage/history.js";
|
|
18
|
+
import { loadMediaIndex } from "./storage/media-index.js";
|
|
19
|
+
import { cleanupOldLogs } from "./storage/daily-log.js";
|
|
20
|
+
import { initDispatcher } from "./core/dispatcher.js";
|
|
21
|
+
import { initPulse, resetPulseTimer } from "./core/pulse.js";
|
|
22
|
+
import { initCron } from "./core/cron.js";
|
|
23
|
+
import { initDream } from "./core/dream.js";
|
|
24
|
+
import { log } from "./util/log.js";
|
|
25
|
+
import type { TalonConfig } from "./util/config.js";
|
|
26
|
+
import type { QueryBackend, ContextManager } from "./core/types.js";
|
|
27
|
+
|
|
28
|
+
// ── Types ────────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export type Frontend = {
|
|
31
|
+
context: ContextManager;
|
|
32
|
+
sendTyping: (chatId: number) => Promise<void>;
|
|
33
|
+
sendMessage: (chatId: number, text: string) => Promise<void>;
|
|
34
|
+
getBridgePort: () => number;
|
|
35
|
+
init: () => Promise<void>;
|
|
36
|
+
start: () => Promise<void>;
|
|
37
|
+
stop: () => Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type BootstrapOptions = {
|
|
41
|
+
/** Override frontend names for plugin loading (e.g. ["terminal"]). */
|
|
42
|
+
frontendNames?: string[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type BootstrapResult = {
|
|
46
|
+
config: TalonConfig;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ── Bootstrap: config, env, plugins, workspace, storage ──────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Load config, set env vars, load plugins, init workspace, load all storage.
|
|
53
|
+
* Returns the loaded config for further use by the caller.
|
|
54
|
+
*/
|
|
55
|
+
export async function bootstrap(
|
|
56
|
+
options: BootstrapOptions = {},
|
|
57
|
+
): Promise<BootstrapResult> {
|
|
58
|
+
const config = loadConfig();
|
|
59
|
+
|
|
60
|
+
// Expose search config as env vars for gateway-actions
|
|
61
|
+
if (config.braveApiKey) process.env.TALON_BRAVE_API_KEY = config.braveApiKey;
|
|
62
|
+
if (config.searxngUrl) process.env.TALON_SEARXNG_URL = config.searxngUrl;
|
|
63
|
+
|
|
64
|
+
// Load plugins (external tool packages)
|
|
65
|
+
if (config.plugins.length > 0) {
|
|
66
|
+
const { loadPlugins, getPluginPromptAdditions } = await import(
|
|
67
|
+
"./core/plugin.js"
|
|
68
|
+
);
|
|
69
|
+
const frontends =
|
|
70
|
+
options.frontendNames ??
|
|
71
|
+
(Array.isArray(config.frontend)
|
|
72
|
+
? config.frontend
|
|
73
|
+
: [config.frontend]);
|
|
74
|
+
await loadPlugins(config.plugins, frontends);
|
|
75
|
+
rebuildSystemPrompt(config, getPluginPromptAdditions());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
initWorkspace(config.workspace);
|
|
79
|
+
loadSessions();
|
|
80
|
+
loadChatSettings();
|
|
81
|
+
loadCronJobs();
|
|
82
|
+
loadHistory();
|
|
83
|
+
loadMediaIndex();
|
|
84
|
+
cleanupOldLogs();
|
|
85
|
+
|
|
86
|
+
return { config };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Backend + dispatcher wiring ──────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create the AI backend and wire the dispatcher.
|
|
93
|
+
* Call this after creating the frontend.
|
|
94
|
+
*/
|
|
95
|
+
export async function initBackendAndDispatcher(
|
|
96
|
+
config: TalonConfig,
|
|
97
|
+
frontend: Frontend,
|
|
98
|
+
): Promise<void> {
|
|
99
|
+
let backend: QueryBackend;
|
|
100
|
+
|
|
101
|
+
if (config.backend === "opencode") {
|
|
102
|
+
const {
|
|
103
|
+
initOpenCodeAgent,
|
|
104
|
+
handleMessage: opencodeHandleMessage,
|
|
105
|
+
} = await import("./backend/opencode/index.js");
|
|
106
|
+
initOpenCodeAgent(config, frontend.getBridgePort);
|
|
107
|
+
backend = { query: (params) => opencodeHandleMessage(params) };
|
|
108
|
+
log("bot", "Backend: OpenCode");
|
|
109
|
+
} else {
|
|
110
|
+
const {
|
|
111
|
+
initAgent: initClaudeAgent,
|
|
112
|
+
handleMessage: claudeHandleMessage,
|
|
113
|
+
} = await import("./backend/claude-sdk/index.js");
|
|
114
|
+
initClaudeAgent(config, frontend.getBridgePort);
|
|
115
|
+
backend = { query: (params) => claudeHandleMessage(params) };
|
|
116
|
+
log("bot", "Backend: Claude SDK");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
initDispatcher({
|
|
120
|
+
backend,
|
|
121
|
+
context: frontend.context,
|
|
122
|
+
sendTyping: frontend.sendTyping,
|
|
123
|
+
onActivity: () => resetPulseTimer(),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
initPulse();
|
|
127
|
+
initCron({ sendMessage: frontend.sendMessage });
|
|
128
|
+
initDream({
|
|
129
|
+
model: config.model,
|
|
130
|
+
dreamModel: config.dreamModel,
|
|
131
|
+
claudeBinary: config.claudeBinary,
|
|
132
|
+
workspace: config.workspace,
|
|
133
|
+
});
|
|
134
|
+
}
|