@agenticmail/enterprise 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +183 -0
- package/agenticmail-enterprise.db +0 -0
- package/dashboards/README.md +120 -0
- package/dashboards/dotnet/Program.cs +261 -0
- package/dashboards/express/app.js +146 -0
- package/dashboards/go/main.go +513 -0
- package/dashboards/html/index.html +535 -0
- package/dashboards/java/AgenticMailDashboard.java +376 -0
- package/dashboards/php/index.php +414 -0
- package/dashboards/python/app.py +273 -0
- package/dashboards/ruby/app.rb +195 -0
- package/dist/chunk-77IDQJL3.js +7 -0
- package/dist/chunk-7RGCCHIT.js +115 -0
- package/dist/chunk-DXNKR3TG.js +1355 -0
- package/dist/chunk-IQWA44WT.js +970 -0
- package/dist/chunk-LCUZGIDH.js +965 -0
- package/dist/chunk-N2JVTNNJ.js +2553 -0
- package/dist/chunk-O462UJBH.js +363 -0
- package/dist/chunk-PNKVD2UK.js +26 -0
- package/dist/cli.js +218 -0
- package/dist/dashboard/index.html +558 -0
- package/dist/db-adapter-DEWEFNIV.js +7 -0
- package/dist/dynamodb-CCGL2E77.js +426 -0
- package/dist/engine/index.js +1261 -0
- package/dist/index.js +522 -0
- package/dist/mongodb-ODTXIVPV.js +319 -0
- package/dist/mysql-RM3S2FV5.js +521 -0
- package/dist/postgres-LN7A6MGQ.js +518 -0
- package/dist/routes-2JEPIIKC.js +441 -0
- package/dist/routes-74ZLKJKP.js +399 -0
- package/dist/server.js +7 -0
- package/dist/sqlite-3K5YOZ4K.js +439 -0
- package/dist/turso-LDWODSDI.js +442 -0
- package/package.json +49 -0
- package/src/admin/routes.ts +331 -0
- package/src/auth/routes.ts +130 -0
- package/src/cli.ts +260 -0
- package/src/dashboard/index.html +558 -0
- package/src/db/adapter.ts +230 -0
- package/src/db/dynamodb.ts +456 -0
- package/src/db/factory.ts +51 -0
- package/src/db/mongodb.ts +360 -0
- package/src/db/mysql.ts +472 -0
- package/src/db/postgres.ts +479 -0
- package/src/db/sql-schema.ts +123 -0
- package/src/db/sqlite.ts +391 -0
- package/src/db/turso.ts +411 -0
- package/src/deploy/fly.ts +368 -0
- package/src/deploy/managed.ts +213 -0
- package/src/engine/activity.ts +474 -0
- package/src/engine/agent-config.ts +429 -0
- package/src/engine/agenticmail-bridge.ts +296 -0
- package/src/engine/approvals.ts +278 -0
- package/src/engine/db-adapter.ts +682 -0
- package/src/engine/db-schema.ts +335 -0
- package/src/engine/deployer.ts +595 -0
- package/src/engine/index.ts +134 -0
- package/src/engine/knowledge.ts +486 -0
- package/src/engine/lifecycle.ts +635 -0
- package/src/engine/openclaw-hook.ts +371 -0
- package/src/engine/routes.ts +528 -0
- package/src/engine/skills.ts +473 -0
- package/src/engine/tenant.ts +345 -0
- package/src/engine/tool-catalog.ts +189 -0
- package/src/index.ts +64 -0
- package/src/lib/resilience.ts +326 -0
- package/src/middleware/index.ts +286 -0
- package/src/server.ts +310 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Configuration Builder
|
|
3
|
+
*
|
|
4
|
+
* Generates the full OpenClaw workspace config for a deployed agent:
|
|
5
|
+
* SOUL.md, USER.md, AGENTS.md, HEARTBEAT.md, gateway config, etc.
|
|
6
|
+
* Everything needed to spin up a fully configured agent from the admin dashboard.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ─── Types ──────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export interface AgentConfig {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
displayName: string; // Human-facing name
|
|
15
|
+
|
|
16
|
+
// Identity
|
|
17
|
+
identity: {
|
|
18
|
+
personality: string; // SOUL.md content — who the agent IS
|
|
19
|
+
role: string; // e.g. "Customer Support Lead"
|
|
20
|
+
tone: 'formal' | 'casual' | 'professional' | 'friendly' | 'custom';
|
|
21
|
+
customTone?: string;
|
|
22
|
+
language: string; // Primary language (e.g. "en", "es", "fr")
|
|
23
|
+
avatar?: string; // URL or base64
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Model
|
|
27
|
+
model: {
|
|
28
|
+
provider: 'anthropic' | 'openai' | 'google' | 'custom';
|
|
29
|
+
modelId: string; // e.g. "claude-sonnet-4-20250514", "gpt-4o"
|
|
30
|
+
thinkingLevel: 'off' | 'low' | 'medium' | 'high';
|
|
31
|
+
temperature?: number;
|
|
32
|
+
maxTokens?: number;
|
|
33
|
+
fallbackModelId?: string; // Fallback if primary is down
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Communication channels
|
|
37
|
+
channels: {
|
|
38
|
+
enabled: ChannelConfig[];
|
|
39
|
+
primaryChannel: string; // Which channel is default
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Email
|
|
43
|
+
email: {
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
provider: 'relay' | 'domain' | 'none';
|
|
46
|
+
address?: string; // e.g. "support@acme.com"
|
|
47
|
+
relayConfig?: { email: string; appPassword: string; provider: 'gmail' | 'outlook' };
|
|
48
|
+
domainConfig?: { domain: string; cloudflareToken: string };
|
|
49
|
+
signature?: string;
|
|
50
|
+
autoReply?: { enabled: boolean; message: string; afterHours: boolean };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Workspace
|
|
54
|
+
workspace: {
|
|
55
|
+
persistentMemory: boolean; // Keep memory across restarts
|
|
56
|
+
memoryMaxSizeMb: number;
|
|
57
|
+
workingDirectory: string; // Where the agent's files live
|
|
58
|
+
sharedDirectories: string[]; // Directories shared between agents
|
|
59
|
+
gitEnabled: boolean; // Auto-commit workspace changes
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Heartbeat & scheduling
|
|
63
|
+
heartbeat: {
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
intervalMinutes: number;
|
|
66
|
+
checks: string[]; // What to check: ['email', 'calendar', 'tickets']
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Context
|
|
70
|
+
context: {
|
|
71
|
+
userInfo?: string; // USER.md content — info about the user/company
|
|
72
|
+
customInstructions?: string; // Additional AGENTS.md instructions
|
|
73
|
+
knowledgeBase?: string[]; // Paths or URLs to reference docs
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Permission profile
|
|
77
|
+
permissionProfileId: string;
|
|
78
|
+
|
|
79
|
+
// Deployment
|
|
80
|
+
deployment: {
|
|
81
|
+
target: DeploymentTarget;
|
|
82
|
+
config: DeploymentConfig;
|
|
83
|
+
status: DeploymentStatus;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
createdAt: string;
|
|
87
|
+
updatedAt: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ChannelConfig {
|
|
91
|
+
type: 'whatsapp' | 'telegram' | 'discord' | 'slack' | 'email' | 'web' | 'api';
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
config: Record<string, any>; // Channel-specific config (tokens, webhook URLs, etc.)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type DeploymentTarget = 'docker' | 'vps' | 'fly' | 'railway' | 'aws' | 'gcp' | 'azure' | 'local';
|
|
97
|
+
|
|
98
|
+
export interface DeploymentConfig {
|
|
99
|
+
// Docker
|
|
100
|
+
docker?: {
|
|
101
|
+
image: string;
|
|
102
|
+
tag: string;
|
|
103
|
+
ports: number[];
|
|
104
|
+
volumes: string[];
|
|
105
|
+
env: Record<string, string>;
|
|
106
|
+
resources: { cpuLimit: string; memoryLimit: string };
|
|
107
|
+
restart: 'always' | 'unless-stopped' | 'on-failure' | 'no';
|
|
108
|
+
network?: string;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// VPS / bare metal
|
|
112
|
+
vps?: {
|
|
113
|
+
host: string;
|
|
114
|
+
port: number;
|
|
115
|
+
user: string;
|
|
116
|
+
authMethod: 'key' | 'password';
|
|
117
|
+
sshKeyPath?: string;
|
|
118
|
+
installPath: string; // Where to install on the VPS
|
|
119
|
+
systemd: boolean; // Use systemd for process management
|
|
120
|
+
sudo: boolean;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Cloud platforms
|
|
124
|
+
cloud?: {
|
|
125
|
+
provider: 'fly' | 'railway' | 'aws' | 'gcp' | 'azure';
|
|
126
|
+
region: string;
|
|
127
|
+
size: string; // Instance size / machine type
|
|
128
|
+
apiToken: string;
|
|
129
|
+
appName?: string; // Auto-generated if not set
|
|
130
|
+
customDomain?: string;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type DeploymentStatus =
|
|
135
|
+
| 'not-deployed'
|
|
136
|
+
| 'provisioning'
|
|
137
|
+
| 'deploying'
|
|
138
|
+
| 'running'
|
|
139
|
+
| 'stopped'
|
|
140
|
+
| 'error'
|
|
141
|
+
| 'updating';
|
|
142
|
+
|
|
143
|
+
// ─── Config Generator ───────────────────────────────────
|
|
144
|
+
|
|
145
|
+
export class AgentConfigGenerator {
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate the complete workspace files for an agent
|
|
149
|
+
*/
|
|
150
|
+
generateWorkspace(config: AgentConfig): WorkspaceFiles {
|
|
151
|
+
return {
|
|
152
|
+
'SOUL.md': this.generateSoul(config),
|
|
153
|
+
'USER.md': this.generateUser(config),
|
|
154
|
+
'AGENTS.md': this.generateAgents(config),
|
|
155
|
+
'IDENTITY.md': this.generateIdentity(config),
|
|
156
|
+
'HEARTBEAT.md': this.generateHeartbeat(config),
|
|
157
|
+
'TOOLS.md': this.generateTools(config),
|
|
158
|
+
'MEMORY.md': `# MEMORY.md — ${config.displayName}'s Long-Term Memory\n\n_Created ${new Date().toISOString()}_\n`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Generate the OpenClaw gateway config for this agent
|
|
164
|
+
*/
|
|
165
|
+
generateGatewayConfig(config: AgentConfig): GatewayConfig {
|
|
166
|
+
const channels: Record<string, any> = {};
|
|
167
|
+
|
|
168
|
+
for (const ch of config.channels.enabled) {
|
|
169
|
+
if (!ch.enabled) continue;
|
|
170
|
+
channels[ch.type] = ch.config;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
model: `${config.model.provider}/${config.model.modelId}`,
|
|
175
|
+
thinking: config.model.thinkingLevel,
|
|
176
|
+
temperature: config.model.temperature,
|
|
177
|
+
maxTokens: config.model.maxTokens,
|
|
178
|
+
channels,
|
|
179
|
+
heartbeat: config.heartbeat.enabled ? {
|
|
180
|
+
intervalMinutes: config.heartbeat.intervalMinutes,
|
|
181
|
+
} : undefined,
|
|
182
|
+
workspace: config.workspace.workingDirectory,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Generate a docker-compose.yml for this agent
|
|
188
|
+
*/
|
|
189
|
+
generateDockerCompose(config: AgentConfig): string {
|
|
190
|
+
const dc = config.deployment.config.docker;
|
|
191
|
+
if (!dc) throw new Error('No Docker config');
|
|
192
|
+
|
|
193
|
+
const env = { ...dc.env };
|
|
194
|
+
// Inject standard vars
|
|
195
|
+
env['OPENCLAW_MODEL'] = `${config.model.provider}/${config.model.modelId}`;
|
|
196
|
+
env['OPENCLAW_THINKING'] = config.model.thinkingLevel;
|
|
197
|
+
if (config.email.enabled && config.email.address) {
|
|
198
|
+
env['AGENTICMAIL_EMAIL'] = config.email.address;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const envLines = Object.entries(env).map(([k, v]) => ` ${k}: "${v}"`).join('\n');
|
|
202
|
+
const volumes = dc.volumes.map(v => ` - ${v}`).join('\n');
|
|
203
|
+
const ports = dc.ports.map(p => ` - "${p}:${p}"`).join('\n');
|
|
204
|
+
|
|
205
|
+
return `version: "3.8"
|
|
206
|
+
|
|
207
|
+
services:
|
|
208
|
+
${config.name}:
|
|
209
|
+
image: ${dc.image}:${dc.tag}
|
|
210
|
+
container_name: agenticmail-${config.name}
|
|
211
|
+
restart: ${dc.restart}
|
|
212
|
+
ports:
|
|
213
|
+
${ports}
|
|
214
|
+
volumes:
|
|
215
|
+
${volumes}
|
|
216
|
+
environment:
|
|
217
|
+
${envLines}
|
|
218
|
+
deploy:
|
|
219
|
+
resources:
|
|
220
|
+
limits:
|
|
221
|
+
cpus: "${dc.resources.cpuLimit}"
|
|
222
|
+
memory: ${dc.resources.memoryLimit}
|
|
223
|
+
${dc.network ? ` networks:\n - ${dc.network}\n\nnetworks:\n ${dc.network}:\n external: true` : ''}
|
|
224
|
+
`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Generate a systemd service file for VPS deployment
|
|
229
|
+
*/
|
|
230
|
+
generateSystemdUnit(config: AgentConfig): string {
|
|
231
|
+
const vps = config.deployment.config.vps;
|
|
232
|
+
if (!vps) throw new Error('No VPS config');
|
|
233
|
+
|
|
234
|
+
return `[Unit]
|
|
235
|
+
Description=AgenticMail Agent: ${config.displayName}
|
|
236
|
+
After=network.target
|
|
237
|
+
|
|
238
|
+
[Service]
|
|
239
|
+
Type=simple
|
|
240
|
+
User=${vps.user}
|
|
241
|
+
WorkingDirectory=${vps.installPath}
|
|
242
|
+
ExecStart=/usr/bin/env node ${vps.installPath}/node_modules/.bin/openclaw gateway start
|
|
243
|
+
Restart=always
|
|
244
|
+
RestartSec=10
|
|
245
|
+
Environment=NODE_ENV=production
|
|
246
|
+
Environment=OPENCLAW_MODEL=${config.model.provider}/${config.model.modelId}
|
|
247
|
+
Environment=OPENCLAW_THINKING=${config.model.thinkingLevel}
|
|
248
|
+
|
|
249
|
+
# Security hardening
|
|
250
|
+
NoNewPrivileges=true
|
|
251
|
+
ProtectSystem=strict
|
|
252
|
+
ProtectHome=read-only
|
|
253
|
+
ReadWritePaths=${vps.installPath}
|
|
254
|
+
PrivateTmp=true
|
|
255
|
+
|
|
256
|
+
[Install]
|
|
257
|
+
WantedBy=multi-user.target
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Generate a deployment script for VPS
|
|
263
|
+
*/
|
|
264
|
+
generateVPSDeployScript(config: AgentConfig): string {
|
|
265
|
+
const vps = config.deployment.config.vps;
|
|
266
|
+
if (!vps) throw new Error('No VPS config');
|
|
267
|
+
|
|
268
|
+
return `#!/bin/bash
|
|
269
|
+
set -euo pipefail
|
|
270
|
+
|
|
271
|
+
# AgenticMail Agent Deployment Script
|
|
272
|
+
# Agent: ${config.displayName}
|
|
273
|
+
# Target: ${vps.host}
|
|
274
|
+
|
|
275
|
+
echo "🚀 Deploying ${config.displayName} to ${vps.host}..."
|
|
276
|
+
|
|
277
|
+
INSTALL_PATH="${vps.installPath}"
|
|
278
|
+
${vps.sudo ? 'SUDO="sudo"' : 'SUDO=""'}
|
|
279
|
+
|
|
280
|
+
# 1. Install Node.js if needed
|
|
281
|
+
if ! command -v node &> /dev/null; then
|
|
282
|
+
echo "📦 Installing Node.js..."
|
|
283
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | $SUDO bash -
|
|
284
|
+
$SUDO apt-get install -y nodejs
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# 2. Create workspace
|
|
288
|
+
mkdir -p "$INSTALL_PATH/workspace"
|
|
289
|
+
cd "$INSTALL_PATH"
|
|
290
|
+
|
|
291
|
+
# 3. Install OpenClaw + AgenticMail
|
|
292
|
+
echo "📦 Installing packages..."
|
|
293
|
+
npm init -y 2>/dev/null || true
|
|
294
|
+
npm install openclaw agenticmail @agenticmail/core @agenticmail/openclaw
|
|
295
|
+
|
|
296
|
+
# 4. Write workspace files
|
|
297
|
+
echo "📝 Writing agent configuration..."
|
|
298
|
+
${Object.entries(this.generateWorkspace(config)).map(([file, content]) =>
|
|
299
|
+
`cat > "$INSTALL_PATH/workspace/${file}" << 'WORKSPACE_EOF'\n${content}\nWORKSPACE_EOF`
|
|
300
|
+
).join('\n\n')}
|
|
301
|
+
|
|
302
|
+
# 5. Write gateway config
|
|
303
|
+
cat > "$INSTALL_PATH/config.yaml" << 'CONFIG_EOF'
|
|
304
|
+
${JSON.stringify(this.generateGatewayConfig(config), null, 2)}
|
|
305
|
+
CONFIG_EOF
|
|
306
|
+
|
|
307
|
+
# 6. Install systemd service
|
|
308
|
+
echo "⚙️ Installing systemd service..."
|
|
309
|
+
$SUDO tee /etc/systemd/system/agenticmail-${config.name}.service > /dev/null << 'SERVICE_EOF'
|
|
310
|
+
${this.generateSystemdUnit(config)}
|
|
311
|
+
SERVICE_EOF
|
|
312
|
+
|
|
313
|
+
$SUDO systemctl daemon-reload
|
|
314
|
+
$SUDO systemctl enable agenticmail-${config.name}
|
|
315
|
+
$SUDO systemctl start agenticmail-${config.name}
|
|
316
|
+
|
|
317
|
+
echo "✅ ${config.displayName} deployed and running!"
|
|
318
|
+
echo " Status: systemctl status agenticmail-${config.name}"
|
|
319
|
+
echo " Logs: journalctl -u agenticmail-${config.name} -f"
|
|
320
|
+
`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ─── Private Generators ─────────────────────────────
|
|
324
|
+
|
|
325
|
+
private generateSoul(config: AgentConfig): string {
|
|
326
|
+
if (config.identity.personality) return config.identity.personality;
|
|
327
|
+
|
|
328
|
+
const toneMap = {
|
|
329
|
+
formal: 'Be professional and precise. Use proper grammar. Avoid slang or casual language.',
|
|
330
|
+
casual: 'Be relaxed and conversational. Use contractions. Feel free to be informal.',
|
|
331
|
+
professional: 'Be competent and clear. Direct communication without being stiff.',
|
|
332
|
+
friendly: 'Be warm and approachable. Show genuine interest. Use a positive tone.',
|
|
333
|
+
custom: config.identity.customTone || '',
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return `# SOUL.md — Who You Are
|
|
337
|
+
|
|
338
|
+
## Role
|
|
339
|
+
You are **${config.displayName}**, a ${config.identity.role}.
|
|
340
|
+
|
|
341
|
+
## Communication Style
|
|
342
|
+
${toneMap[config.identity.tone]}
|
|
343
|
+
|
|
344
|
+
## Language
|
|
345
|
+
Primary language: ${config.identity.language}
|
|
346
|
+
|
|
347
|
+
## Core Principles
|
|
348
|
+
- Be genuinely helpful, not performatively helpful
|
|
349
|
+
- Be resourceful — try to figure things out before asking
|
|
350
|
+
- Earn trust through competence
|
|
351
|
+
- Keep private information private
|
|
352
|
+
|
|
353
|
+
## Boundaries
|
|
354
|
+
- Never share confidential company information
|
|
355
|
+
- Ask before taking irreversible actions
|
|
356
|
+
- Stay within your assigned role and permissions
|
|
357
|
+
`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private generateUser(config: AgentConfig): string {
|
|
361
|
+
return config.context?.userInfo || `# USER.md — About Your Organization\n\n_Configure this from the admin dashboard._\n`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private generateAgents(config: AgentConfig): string {
|
|
365
|
+
const customInstructions = config.context?.customInstructions || '';
|
|
366
|
+
return `# AGENTS.md — Your Workspace
|
|
367
|
+
|
|
368
|
+
## Every Session
|
|
369
|
+
1. Read SOUL.md — this is who you are
|
|
370
|
+
2. Read USER.md — this is who you're helping
|
|
371
|
+
3. Check memory/ for recent context
|
|
372
|
+
|
|
373
|
+
## Memory
|
|
374
|
+
- Daily notes: memory/YYYY-MM-DD.md
|
|
375
|
+
- Long-term: MEMORY.md
|
|
376
|
+
|
|
377
|
+
## Safety
|
|
378
|
+
- Don't exfiltrate private data
|
|
379
|
+
- Don't run destructive commands without asking
|
|
380
|
+
- When in doubt, ask
|
|
381
|
+
|
|
382
|
+
${customInstructions}
|
|
383
|
+
`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private generateIdentity(config: AgentConfig): string {
|
|
387
|
+
return `# IDENTITY.md
|
|
388
|
+
|
|
389
|
+
- **Name:** ${config.displayName}
|
|
390
|
+
- **Role:** ${config.identity.role}
|
|
391
|
+
- **Tone:** ${config.identity.tone}
|
|
392
|
+
`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private generateHeartbeat(config: AgentConfig): string {
|
|
396
|
+
if (!config.heartbeat.enabled) return '# HEARTBEAT.md\n# Heartbeat disabled\n';
|
|
397
|
+
|
|
398
|
+
const checks = config.heartbeat.checks.map(c => `- Check ${c}`).join('\n');
|
|
399
|
+
return `# HEARTBEAT.md
|
|
400
|
+
|
|
401
|
+
## Periodic Checks
|
|
402
|
+
${checks}
|
|
403
|
+
|
|
404
|
+
## Schedule
|
|
405
|
+
Check every ${config.heartbeat.intervalMinutes} minutes during active hours.
|
|
406
|
+
`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private generateTools(config: AgentConfig): string {
|
|
410
|
+
return `# TOOLS.md — Local Notes
|
|
411
|
+
|
|
412
|
+
_Add environment-specific notes here (camera names, SSH hosts, etc.)_
|
|
413
|
+
`;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ─── Output Types ───────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
export type WorkspaceFiles = Record<string, string>;
|
|
420
|
+
|
|
421
|
+
export interface GatewayConfig {
|
|
422
|
+
model: string;
|
|
423
|
+
thinking: string;
|
|
424
|
+
temperature?: number;
|
|
425
|
+
maxTokens?: number;
|
|
426
|
+
channels: Record<string, any>;
|
|
427
|
+
heartbeat?: { intervalMinutes: number };
|
|
428
|
+
workspace: string;
|
|
429
|
+
}
|