clementine-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.
Files changed (190) hide show
  1. package/.env.example +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +795 -0
  4. package/dist/agent/agent-manager.d.ts +69 -0
  5. package/dist/agent/agent-manager.js +441 -0
  6. package/dist/agent/assistant.d.ts +225 -0
  7. package/dist/agent/assistant.js +3888 -0
  8. package/dist/agent/auto-update.d.ts +32 -0
  9. package/dist/agent/auto-update.js +186 -0
  10. package/dist/agent/daily-planner.d.ts +24 -0
  11. package/dist/agent/daily-planner.js +379 -0
  12. package/dist/agent/execution-advisor.d.ts +10 -0
  13. package/dist/agent/execution-advisor.js +272 -0
  14. package/dist/agent/hooks.d.ts +45 -0
  15. package/dist/agent/hooks.js +564 -0
  16. package/dist/agent/insight-engine.d.ts +66 -0
  17. package/dist/agent/insight-engine.js +225 -0
  18. package/dist/agent/intent-classifier.d.ts +48 -0
  19. package/dist/agent/intent-classifier.js +214 -0
  20. package/dist/agent/link-extractor.d.ts +19 -0
  21. package/dist/agent/link-extractor.js +90 -0
  22. package/dist/agent/mcp-bridge.d.ts +62 -0
  23. package/dist/agent/mcp-bridge.js +435 -0
  24. package/dist/agent/metacognition.d.ts +66 -0
  25. package/dist/agent/metacognition.js +221 -0
  26. package/dist/agent/orchestrator.d.ts +81 -0
  27. package/dist/agent/orchestrator.js +790 -0
  28. package/dist/agent/profiles.d.ts +22 -0
  29. package/dist/agent/profiles.js +91 -0
  30. package/dist/agent/prompt-cache.d.ts +24 -0
  31. package/dist/agent/prompt-cache.js +68 -0
  32. package/dist/agent/prompt-evolver.d.ts +28 -0
  33. package/dist/agent/prompt-evolver.js +279 -0
  34. package/dist/agent/role-scaffolds.d.ts +28 -0
  35. package/dist/agent/role-scaffolds.js +433 -0
  36. package/dist/agent/safe-restart.d.ts +41 -0
  37. package/dist/agent/safe-restart.js +150 -0
  38. package/dist/agent/self-improve.d.ts +66 -0
  39. package/dist/agent/self-improve.js +1706 -0
  40. package/dist/agent/session-event-log.d.ts +114 -0
  41. package/dist/agent/session-event-log.js +233 -0
  42. package/dist/agent/skill-extractor.d.ts +72 -0
  43. package/dist/agent/skill-extractor.js +435 -0
  44. package/dist/agent/source-mods.d.ts +61 -0
  45. package/dist/agent/source-mods.js +230 -0
  46. package/dist/agent/source-preflight.d.ts +25 -0
  47. package/dist/agent/source-preflight.js +100 -0
  48. package/dist/agent/stall-guard.d.ts +62 -0
  49. package/dist/agent/stall-guard.js +109 -0
  50. package/dist/agent/strategic-planner.d.ts +60 -0
  51. package/dist/agent/strategic-planner.js +352 -0
  52. package/dist/agent/team-bus.d.ts +89 -0
  53. package/dist/agent/team-bus.js +556 -0
  54. package/dist/agent/team-router.d.ts +26 -0
  55. package/dist/agent/team-router.js +37 -0
  56. package/dist/agent/tool-loop-detector.d.ts +59 -0
  57. package/dist/agent/tool-loop-detector.js +242 -0
  58. package/dist/agent/workflow-runner.d.ts +36 -0
  59. package/dist/agent/workflow-runner.js +317 -0
  60. package/dist/agent/workflow-variables.d.ts +16 -0
  61. package/dist/agent/workflow-variables.js +62 -0
  62. package/dist/channels/discord-agent-bot.d.ts +101 -0
  63. package/dist/channels/discord-agent-bot.js +881 -0
  64. package/dist/channels/discord-bot-manager.d.ts +80 -0
  65. package/dist/channels/discord-bot-manager.js +262 -0
  66. package/dist/channels/discord-utils.d.ts +51 -0
  67. package/dist/channels/discord-utils.js +293 -0
  68. package/dist/channels/discord.d.ts +12 -0
  69. package/dist/channels/discord.js +1832 -0
  70. package/dist/channels/slack-agent-bot.d.ts +73 -0
  71. package/dist/channels/slack-agent-bot.js +320 -0
  72. package/dist/channels/slack-bot-manager.d.ts +66 -0
  73. package/dist/channels/slack-bot-manager.js +236 -0
  74. package/dist/channels/slack-utils.d.ts +39 -0
  75. package/dist/channels/slack-utils.js +189 -0
  76. package/dist/channels/slack.d.ts +11 -0
  77. package/dist/channels/slack.js +196 -0
  78. package/dist/channels/telegram.d.ts +10 -0
  79. package/dist/channels/telegram.js +235 -0
  80. package/dist/channels/webhook.d.ts +9 -0
  81. package/dist/channels/webhook.js +78 -0
  82. package/dist/channels/whatsapp.d.ts +11 -0
  83. package/dist/channels/whatsapp.js +181 -0
  84. package/dist/cli/chat.d.ts +14 -0
  85. package/dist/cli/chat.js +220 -0
  86. package/dist/cli/cron.d.ts +17 -0
  87. package/dist/cli/cron.js +552 -0
  88. package/dist/cli/dashboard.d.ts +15 -0
  89. package/dist/cli/dashboard.js +17677 -0
  90. package/dist/cli/index.d.ts +3 -0
  91. package/dist/cli/index.js +2474 -0
  92. package/dist/cli/routes/delegations.d.ts +19 -0
  93. package/dist/cli/routes/delegations.js +154 -0
  94. package/dist/cli/routes/digest.d.ts +17 -0
  95. package/dist/cli/routes/digest.js +375 -0
  96. package/dist/cli/routes/goals.d.ts +14 -0
  97. package/dist/cli/routes/goals.js +258 -0
  98. package/dist/cli/routes/workflows.d.ts +18 -0
  99. package/dist/cli/routes/workflows.js +97 -0
  100. package/dist/cli/setup.d.ts +8 -0
  101. package/dist/cli/setup.js +619 -0
  102. package/dist/cli/tunnel.d.ts +35 -0
  103. package/dist/cli/tunnel.js +141 -0
  104. package/dist/config.d.ts +145 -0
  105. package/dist/config.js +278 -0
  106. package/dist/events/bus.d.ts +43 -0
  107. package/dist/events/bus.js +136 -0
  108. package/dist/gateway/cron-scheduler.d.ts +166 -0
  109. package/dist/gateway/cron-scheduler.js +1767 -0
  110. package/dist/gateway/delivery-queue.d.ts +30 -0
  111. package/dist/gateway/delivery-queue.js +110 -0
  112. package/dist/gateway/heartbeat-scheduler.d.ts +99 -0
  113. package/dist/gateway/heartbeat-scheduler.js +1298 -0
  114. package/dist/gateway/heartbeat.d.ts +3 -0
  115. package/dist/gateway/heartbeat.js +3 -0
  116. package/dist/gateway/lanes.d.ts +24 -0
  117. package/dist/gateway/lanes.js +76 -0
  118. package/dist/gateway/notifications.d.ts +29 -0
  119. package/dist/gateway/notifications.js +75 -0
  120. package/dist/gateway/router.d.ts +210 -0
  121. package/dist/gateway/router.js +1330 -0
  122. package/dist/index.d.ts +12 -0
  123. package/dist/index.js +1015 -0
  124. package/dist/memory/chunker.d.ts +28 -0
  125. package/dist/memory/chunker.js +226 -0
  126. package/dist/memory/consolidation.d.ts +44 -0
  127. package/dist/memory/consolidation.js +171 -0
  128. package/dist/memory/context-assembler.d.ts +50 -0
  129. package/dist/memory/context-assembler.js +149 -0
  130. package/dist/memory/embeddings.d.ts +38 -0
  131. package/dist/memory/embeddings.js +180 -0
  132. package/dist/memory/graph-store.d.ts +66 -0
  133. package/dist/memory/graph-store.js +613 -0
  134. package/dist/memory/mmr.d.ts +21 -0
  135. package/dist/memory/mmr.js +75 -0
  136. package/dist/memory/search.d.ts +26 -0
  137. package/dist/memory/search.js +67 -0
  138. package/dist/memory/store.d.ts +530 -0
  139. package/dist/memory/store.js +2022 -0
  140. package/dist/security/integrity.d.ts +24 -0
  141. package/dist/security/integrity.js +58 -0
  142. package/dist/security/patterns.d.ts +34 -0
  143. package/dist/security/patterns.js +110 -0
  144. package/dist/security/scanner.d.ts +32 -0
  145. package/dist/security/scanner.js +263 -0
  146. package/dist/tools/admin-tools.d.ts +12 -0
  147. package/dist/tools/admin-tools.js +1278 -0
  148. package/dist/tools/external-tools.d.ts +11 -0
  149. package/dist/tools/external-tools.js +1327 -0
  150. package/dist/tools/goal-tools.d.ts +9 -0
  151. package/dist/tools/goal-tools.js +159 -0
  152. package/dist/tools/mcp-server.d.ts +13 -0
  153. package/dist/tools/mcp-server.js +141 -0
  154. package/dist/tools/memory-tools.d.ts +10 -0
  155. package/dist/tools/memory-tools.js +568 -0
  156. package/dist/tools/session-tools.d.ts +6 -0
  157. package/dist/tools/session-tools.js +146 -0
  158. package/dist/tools/shared.d.ts +216 -0
  159. package/dist/tools/shared.js +340 -0
  160. package/dist/tools/team-tools.d.ts +6 -0
  161. package/dist/tools/team-tools.js +447 -0
  162. package/dist/tools/tool-meta.d.ts +34 -0
  163. package/dist/tools/tool-meta.js +133 -0
  164. package/dist/tools/vault-tools.d.ts +8 -0
  165. package/dist/tools/vault-tools.js +457 -0
  166. package/dist/types.d.ts +716 -0
  167. package/dist/types.js +16 -0
  168. package/dist/vault-migrations/0001-add-execution-framework.d.ts +10 -0
  169. package/dist/vault-migrations/0001-add-execution-framework.js +47 -0
  170. package/dist/vault-migrations/0002-add-agentic-communication.d.ts +12 -0
  171. package/dist/vault-migrations/0002-add-agentic-communication.js +79 -0
  172. package/dist/vault-migrations/0003-update-execution-pipeline-narration.d.ts +11 -0
  173. package/dist/vault-migrations/0003-update-execution-pipeline-narration.js +73 -0
  174. package/dist/vault-migrations/helpers.d.ts +14 -0
  175. package/dist/vault-migrations/helpers.js +44 -0
  176. package/dist/vault-migrations/runner.d.ts +14 -0
  177. package/dist/vault-migrations/runner.js +139 -0
  178. package/dist/vault-migrations/types.d.ts +42 -0
  179. package/dist/vault-migrations/types.js +9 -0
  180. package/install.sh +320 -0
  181. package/package.json +84 -0
  182. package/scripts/postinstall.js +125 -0
  183. package/vault/00-System/AGENTS.md +66 -0
  184. package/vault/00-System/CRON.md +71 -0
  185. package/vault/00-System/HEARTBEAT.md +58 -0
  186. package/vault/00-System/MEMORY.md +16 -0
  187. package/vault/00-System/SOUL.md +96 -0
  188. package/vault/05-Tasks/TASKS.md +19 -0
  189. package/vault/06-Templates/_Daily-Template.md +28 -0
  190. package/vault/06-Templates/_People-Template.md +22 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Clementine TypeScript — Agent manager (scoped multi-agent system).
3
+ *
4
+ * Loads agent profiles from two sources:
5
+ * 1. vault/00-System/agents/{slug}/agent.md — new agent directory format
6
+ * 2. vault/00-System/profiles/*.md — legacy profile files
7
+ *
8
+ * Same slug in agents/ wins over profiles/ (agents/ is the primary source).
9
+ * Uses the same 60s TTL cache as ProfileManager.
10
+ *
11
+ * Provides CRUD operations for creating/updating/deleting agents.
12
+ */
13
+ import type { AgentProfile, AgentStatus, SendPolicy } from '../types.js';
14
+ export interface AgentCreateConfig {
15
+ name: string;
16
+ description: string;
17
+ personality?: string;
18
+ tier?: number;
19
+ model?: string;
20
+ avatar?: string;
21
+ channelName?: string | string[];
22
+ teamChat?: boolean;
23
+ respondToAll?: boolean;
24
+ canMessage?: string[];
25
+ allowedTools?: string[];
26
+ allowedUsers?: string[];
27
+ project?: string;
28
+ projects?: string[];
29
+ discordToken?: string;
30
+ discordChannelId?: string;
31
+ slackBotToken?: string;
32
+ slackAppToken?: string;
33
+ slackChannelId?: string;
34
+ sendPolicy?: SendPolicy;
35
+ role?: string;
36
+ status?: AgentStatus;
37
+ budgetMonthlyCents?: number;
38
+ }
39
+ export declare class AgentManager {
40
+ private agentsDir;
41
+ private legacyManager;
42
+ private cache;
43
+ private cacheTime;
44
+ constructor(agentsDir: string, legacyProfilesDir: string);
45
+ private refreshIfStale;
46
+ private loadAgentFile;
47
+ get(slug: string): AgentProfile | null;
48
+ listAll(): AgentProfile[];
49
+ /** Get the agent's directory path (only for agents/ dir agents, not legacy). */
50
+ getAgentDir(slug: string): string | null;
51
+ /** Check if an agent has its own CRON.md. */
52
+ hasOwnCron(slug: string): boolean;
53
+ /** Check if an agent has its own workflows directory. */
54
+ hasOwnWorkflows(slug: string): boolean;
55
+ /** Get the path to an agent's CRON.md (or null). */
56
+ getCronPath(slug: string): string | null;
57
+ /** Get the path to an agent's workflows directory (or null). */
58
+ getWorkflowsDir(slug: string): string | null;
59
+ createAgent(config: AgentCreateConfig): AgentProfile;
60
+ updateAgent(slug: string, changes: Partial<AgentCreateConfig>): AgentProfile;
61
+ /** Quick status update without touching other config. */
62
+ setStatus(slug: string, status: AgentStatus): void;
63
+ /** Check if an agent is runnable (active status). */
64
+ isRunnable(slug: string): boolean;
65
+ deleteAgent(slug: string): void;
66
+ /** Force cache refresh (used after external modifications). */
67
+ invalidateCache(): void;
68
+ }
69
+ //# sourceMappingURL=agent-manager.d.ts.map
@@ -0,0 +1,441 @@
1
+ /**
2
+ * Clementine TypeScript — Agent manager (scoped multi-agent system).
3
+ *
4
+ * Loads agent profiles from two sources:
5
+ * 1. vault/00-System/agents/{slug}/agent.md — new agent directory format
6
+ * 2. vault/00-System/profiles/*.md — legacy profile files
7
+ *
8
+ * Same slug in agents/ wins over profiles/ (agents/ is the primary source).
9
+ * Uses the same 60s TTL cache as ProfileManager.
10
+ *
11
+ * Provides CRUD operations for creating/updating/deleting agents.
12
+ */
13
+ import { execSync } from 'node:child_process';
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+ import matter from 'gray-matter';
17
+ import { ProfileManager } from './profiles.js';
18
+ import { getScaffoldForRole } from './role-scaffolds.js';
19
+ // ── Keychain helpers for agent secrets ────────────────────────────────
20
+ function storeAgentSecret(slug, key, value) {
21
+ execSync(`security add-generic-password -U -s "clementine" -a "AGENT_${slug.toUpperCase()}_${key}" -w "${value}"`, { stdio: 'pipe', timeout: 3000 });
22
+ }
23
+ function getAgentSecret(slug, key) {
24
+ try {
25
+ return execSync(`security find-generic-password -s "clementine" -a "AGENT_${slug.toUpperCase()}_${key}" -w`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
26
+ }
27
+ catch {
28
+ return '';
29
+ }
30
+ }
31
+ function deleteAgentSecret(slug, key) {
32
+ try {
33
+ execSync(`security delete-generic-password -s "clementine" -a "AGENT_${slug.toUpperCase()}_${key}"`, { stdio: 'pipe', timeout: 3000 });
34
+ }
35
+ catch { /* not found — ok */ }
36
+ }
37
+ const CACHE_TTL_MS = 60_000;
38
+ export class AgentManager {
39
+ agentsDir;
40
+ legacyManager;
41
+ cache = new Map();
42
+ cacheTime = 0;
43
+ constructor(agentsDir, legacyProfilesDir) {
44
+ this.agentsDir = agentsDir;
45
+ this.legacyManager = new ProfileManager(legacyProfilesDir);
46
+ }
47
+ refreshIfStale() {
48
+ const now = Date.now();
49
+ if (now - this.cacheTime < CACHE_TTL_MS && this.cache.size > 0) {
50
+ return;
51
+ }
52
+ const profiles = new Map();
53
+ // 1. Load from agents/{slug}/agent.md (primary)
54
+ if (fs.existsSync(this.agentsDir)) {
55
+ try {
56
+ const dirs = fs.readdirSync(this.agentsDir, { withFileTypes: true })
57
+ .filter(d => d.isDirectory() && !d.name.startsWith('_'))
58
+ .map(d => d.name)
59
+ .sort();
60
+ for (const slug of dirs) {
61
+ const agentFile = path.join(this.agentsDir, slug, 'agent.md');
62
+ if (!fs.existsSync(agentFile))
63
+ continue;
64
+ try {
65
+ const profile = this.loadAgentFile(agentFile, slug);
66
+ profiles.set(slug, profile);
67
+ }
68
+ catch {
69
+ // Skip malformed agent files
70
+ }
71
+ }
72
+ }
73
+ catch {
74
+ // agents dir not readable
75
+ }
76
+ }
77
+ // 2. Load legacy profiles (only for slugs not already loaded)
78
+ for (const legacy of this.legacyManager.listAll()) {
79
+ if (!profiles.has(legacy.slug)) {
80
+ profiles.set(legacy.slug, legacy);
81
+ }
82
+ }
83
+ this.cache = profiles;
84
+ this.cacheTime = now;
85
+ }
86
+ loadAgentFile(filePath, slug) {
87
+ const raw = fs.readFileSync(filePath, 'utf-8');
88
+ const { data: meta, content } = matter(raw);
89
+ // Cap tier at 2 — agents can never grant Tier 3
90
+ const tier = Math.min(Number(meta.tier ?? 1), 2);
91
+ // Parse team-specific frontmatter
92
+ let team;
93
+ const channelName = Array.isArray(meta.channelName)
94
+ ? meta.channelName.map(String).filter(Boolean)
95
+ : meta.channelName ? String(meta.channelName) : undefined;
96
+ const canMessage = Array.isArray(meta.canMessage)
97
+ ? meta.canMessage.map(String).filter(Boolean)
98
+ : [];
99
+ const allowedTools = Array.isArray(meta.allowedTools)
100
+ ? meta.allowedTools.map(String).filter(Boolean)
101
+ : undefined;
102
+ const allowedUsers = Array.isArray(meta.allowedUsers)
103
+ ? meta.allowedUsers.map(String).filter(Boolean)
104
+ : typeof meta.allowedUsers === 'string'
105
+ ? meta.allowedUsers.split(',').map((s) => s.trim()).filter(Boolean)
106
+ : undefined;
107
+ if (channelName && (typeof channelName === 'string' || channelName.length > 0)) {
108
+ const teamChat = meta.teamChat === true || meta.teamChat === 'true';
109
+ const respondToAll = meta.respondToAll === true || meta.respondToAll === 'true';
110
+ team = { channelName, channels: [], canMessage, allowedTools, allowedUsers, teamChat, respondToAll: respondToAll || undefined };
111
+ }
112
+ // Resolve Discord token — migrate plaintext to Keychain if needed
113
+ let discordToken;
114
+ if (meta.discordToken) {
115
+ const raw = String(meta.discordToken);
116
+ if (raw === 'keychain') {
117
+ discordToken = getAgentSecret(slug, 'DISCORD_TOKEN') || undefined;
118
+ }
119
+ else {
120
+ // Plaintext token in frontmatter — migrate to Keychain
121
+ discordToken = raw;
122
+ try {
123
+ storeAgentSecret(slug, 'DISCORD_TOKEN', raw);
124
+ meta.discordToken = 'keychain';
125
+ const updated = matter.stringify(content, meta);
126
+ fs.writeFileSync(filePath, updated);
127
+ }
128
+ catch { /* migration failed — continue with plaintext */ }
129
+ }
130
+ }
131
+ // Resolve Slack tokens — same keychain migration pattern as Discord
132
+ let slackBotToken;
133
+ if (meta.slackBotToken) {
134
+ const rawSlack = String(meta.slackBotToken);
135
+ if (rawSlack === 'keychain') {
136
+ slackBotToken = getAgentSecret(slug, 'SLACK_BOT_TOKEN') || undefined;
137
+ }
138
+ else {
139
+ slackBotToken = rawSlack;
140
+ try {
141
+ storeAgentSecret(slug, 'SLACK_BOT_TOKEN', rawSlack);
142
+ meta.slackBotToken = 'keychain';
143
+ const updated = matter.stringify(content, meta);
144
+ fs.writeFileSync(filePath, updated);
145
+ }
146
+ catch { /* migration failed — continue with plaintext */ }
147
+ }
148
+ }
149
+ let slackAppToken;
150
+ if (meta.slackAppToken) {
151
+ const rawApp = String(meta.slackAppToken);
152
+ if (rawApp === 'keychain') {
153
+ slackAppToken = getAgentSecret(slug, 'SLACK_APP_TOKEN') || undefined;
154
+ }
155
+ else {
156
+ slackAppToken = rawApp;
157
+ try {
158
+ storeAgentSecret(slug, 'SLACK_APP_TOKEN', rawApp);
159
+ meta.slackAppToken = 'keychain';
160
+ const updated = matter.stringify(content, meta);
161
+ fs.writeFileSync(filePath, updated);
162
+ }
163
+ catch { /* migration failed — continue with plaintext */ }
164
+ }
165
+ }
166
+ // Parse sendPolicy from frontmatter
167
+ let sendPolicy;
168
+ if (meta.sendPolicy && typeof meta.sendPolicy === 'object') {
169
+ const sp = meta.sendPolicy;
170
+ const requiresApproval = ['none', 'first-in-sequence', 'all'].includes(sp.requiresApproval)
171
+ ? sp.requiresApproval
172
+ : 'all';
173
+ sendPolicy = {
174
+ maxDailyEmails: Number(sp.maxDailyEmails ?? 50),
175
+ requiresApproval,
176
+ businessHoursOnly: sp.businessHoursOnly === true || sp.businessHoursOnly === 'true',
177
+ };
178
+ if (Array.isArray(sp.allowedTemplates) && sp.allowedTemplates.length > 0) {
179
+ sendPolicy.allowedTemplates = sp.allowedTemplates.map(String);
180
+ }
181
+ }
182
+ return {
183
+ slug,
184
+ name: String(meta.name ?? slug.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())),
185
+ tier,
186
+ description: String(meta.description ?? ''),
187
+ systemPromptBody: content.trim(),
188
+ model: meta.model ? String(meta.model) : undefined,
189
+ avatar: meta.avatar ? String(meta.avatar) : undefined,
190
+ team,
191
+ project: meta.project ? String(meta.project) : undefined,
192
+ projects: Array.isArray(meta.projects) ? meta.projects.map(String) : undefined,
193
+ agentDir: path.dirname(filePath),
194
+ discordToken,
195
+ discordChannelId: meta.discordChannelId ? String(meta.discordChannelId) : undefined,
196
+ slackBotToken,
197
+ slackAppToken,
198
+ slackChannelId: meta.slackChannelId ? String(meta.slackChannelId) : undefined,
199
+ sendPolicy,
200
+ allowedMcpServers: Array.isArray(meta.allowedMcpServers)
201
+ ? meta.allowedMcpServers.map(String).filter(Boolean)
202
+ : undefined,
203
+ status: (['active', 'paused', 'error', 'terminated'].includes(meta.status) ? meta.status : 'active'),
204
+ budgetMonthlyCents: meta.budgetMonthlyCents ? Number(meta.budgetMonthlyCents) : undefined,
205
+ strictMemoryIsolation: meta.strictMemoryIsolation === false ? false : true, // default true for all agents
206
+ };
207
+ }
208
+ // ── ProfileManager-compatible interface ───────────────────────────
209
+ get(slug) {
210
+ this.refreshIfStale();
211
+ return this.cache.get(slug) ?? null;
212
+ }
213
+ listAll() {
214
+ this.refreshIfStale();
215
+ return [...this.cache.values()];
216
+ }
217
+ // ── Agent directory helpers ───────────────────────────────────────
218
+ /** Get the agent's directory path (only for agents/ dir agents, not legacy). */
219
+ getAgentDir(slug) {
220
+ const dir = path.join(this.agentsDir, slug);
221
+ return fs.existsSync(path.join(dir, 'agent.md')) ? dir : null;
222
+ }
223
+ /** Check if an agent has its own CRON.md. */
224
+ hasOwnCron(slug) {
225
+ const dir = this.getAgentDir(slug);
226
+ return dir !== null && fs.existsSync(path.join(dir, 'CRON.md'));
227
+ }
228
+ /** Check if an agent has its own workflows directory. */
229
+ hasOwnWorkflows(slug) {
230
+ const dir = this.getAgentDir(slug);
231
+ return dir !== null && fs.existsSync(path.join(dir, 'workflows'));
232
+ }
233
+ /** Get the path to an agent's CRON.md (or null). */
234
+ getCronPath(slug) {
235
+ const dir = this.getAgentDir(slug);
236
+ if (!dir)
237
+ return null;
238
+ const cronPath = path.join(dir, 'CRON.md');
239
+ return fs.existsSync(cronPath) ? cronPath : null;
240
+ }
241
+ /** Get the path to an agent's workflows directory (or null). */
242
+ getWorkflowsDir(slug) {
243
+ const dir = this.getAgentDir(slug);
244
+ if (!dir)
245
+ return null;
246
+ const wfDir = path.join(dir, 'workflows');
247
+ return fs.existsSync(wfDir) ? wfDir : null;
248
+ }
249
+ // ── CRUD ──────────────────────────────────────────────────────────
250
+ createAgent(config) {
251
+ const slug = config.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
252
+ const agentDir = path.join(this.agentsDir, slug);
253
+ if (fs.existsSync(path.join(agentDir, 'agent.md'))) {
254
+ throw new Error(`Agent '${slug}' already exists.`);
255
+ }
256
+ // Ensure directories exist
257
+ fs.mkdirSync(agentDir, { recursive: true });
258
+ // Build frontmatter
259
+ const frontmatter = {
260
+ name: config.name,
261
+ description: config.description,
262
+ tier: Math.min(config.tier ?? 2, 2),
263
+ strictMemoryIsolation: true, // new agents get hard isolation by default
264
+ };
265
+ if (config.model)
266
+ frontmatter.model = config.model;
267
+ if (config.avatar)
268
+ frontmatter.avatar = config.avatar;
269
+ if (config.channelName)
270
+ frontmatter.channelName = config.channelName;
271
+ if (config.teamChat)
272
+ frontmatter.teamChat = config.teamChat;
273
+ if (config.respondToAll)
274
+ frontmatter.respondToAll = config.respondToAll;
275
+ if (config.canMessage?.length)
276
+ frontmatter.canMessage = config.canMessage;
277
+ if (config.allowedTools?.length)
278
+ frontmatter.allowedTools = config.allowedTools;
279
+ if (config.allowedUsers?.length)
280
+ frontmatter.allowedUsers = config.allowedUsers;
281
+ if (config.project)
282
+ frontmatter.project = config.project;
283
+ if (config.discordToken) {
284
+ storeAgentSecret(slug, 'DISCORD_TOKEN', config.discordToken);
285
+ frontmatter.discordToken = 'keychain';
286
+ }
287
+ if (config.discordChannelId)
288
+ frontmatter.discordChannelId = config.discordChannelId;
289
+ if (config.slackBotToken) {
290
+ storeAgentSecret(slug, 'SLACK_BOT_TOKEN', config.slackBotToken);
291
+ frontmatter.slackBotToken = 'keychain';
292
+ }
293
+ if (config.slackAppToken) {
294
+ storeAgentSecret(slug, 'SLACK_APP_TOKEN', config.slackAppToken);
295
+ frontmatter.slackAppToken = 'keychain';
296
+ }
297
+ if (config.slackChannelId)
298
+ frontmatter.slackChannelId = config.slackChannelId;
299
+ if (config.sendPolicy)
300
+ frontmatter.sendPolicy = config.sendPolicy;
301
+ if (config.status)
302
+ frontmatter.status = config.status;
303
+ if (config.budgetMonthlyCents)
304
+ frontmatter.budgetMonthlyCents = config.budgetMonthlyCents;
305
+ const body = config.personality || `You are ${config.name}. ${config.description}`;
306
+ const content = matter.stringify(body, frontmatter);
307
+ fs.writeFileSync(path.join(agentDir, 'agent.md'), content);
308
+ // Scaffold role-specific working directory (CRON.md, playbook, sequences)
309
+ if (config.role) {
310
+ const scaffolder = getScaffoldForRole(config.role);
311
+ if (scaffolder) {
312
+ const scaffold = scaffolder(config.name, slug);
313
+ // Write CRON.md — the autonomous job definitions
314
+ if (scaffold.cronMd) {
315
+ fs.writeFileSync(path.join(agentDir, 'CRON.md'), scaffold.cronMd);
316
+ }
317
+ // Write playbook — ICP, email rules, escalation criteria
318
+ if (scaffold.playbook) {
319
+ fs.writeFileSync(path.join(agentDir, 'PLAYBOOK.md'), scaffold.playbook);
320
+ }
321
+ // Write sequence definitions
322
+ if (scaffold.sequences) {
323
+ fs.writeFileSync(path.join(agentDir, 'SEQUENCES.md'), scaffold.sequences);
324
+ }
325
+ }
326
+ }
327
+ // Invalidate cache
328
+ this.cacheTime = 0;
329
+ return this.get(slug);
330
+ }
331
+ updateAgent(slug, changes) {
332
+ const agentDir = path.join(this.agentsDir, slug);
333
+ const agentFile = path.join(agentDir, 'agent.md');
334
+ if (!fs.existsSync(agentFile)) {
335
+ throw new Error(`Agent '${slug}' not found in agents directory.`);
336
+ }
337
+ const raw = fs.readFileSync(agentFile, 'utf-8');
338
+ const { data: meta, content: body } = matter(raw);
339
+ // Merge changes into frontmatter
340
+ if (changes.name !== undefined)
341
+ meta.name = changes.name;
342
+ if (changes.description !== undefined)
343
+ meta.description = changes.description;
344
+ if (changes.tier !== undefined)
345
+ meta.tier = Math.min(changes.tier, 2);
346
+ if (changes.model !== undefined)
347
+ meta.model = changes.model;
348
+ if (changes.avatar !== undefined)
349
+ meta.avatar = changes.avatar;
350
+ if (changes.channelName !== undefined)
351
+ meta.channelName = changes.channelName;
352
+ if (changes.teamChat !== undefined)
353
+ meta.teamChat = changes.teamChat;
354
+ if (changes.respondToAll !== undefined)
355
+ meta.respondToAll = changes.respondToAll;
356
+ if (changes.canMessage !== undefined)
357
+ meta.canMessage = changes.canMessage;
358
+ if (changes.allowedTools !== undefined)
359
+ meta.allowedTools = changes.allowedTools;
360
+ if (changes.allowedUsers !== undefined)
361
+ meta.allowedUsers = changes.allowedUsers;
362
+ if (changes.project !== undefined)
363
+ meta.project = changes.project;
364
+ if (changes.discordToken !== undefined) {
365
+ if (changes.discordToken) {
366
+ storeAgentSecret(slug, 'DISCORD_TOKEN', changes.discordToken);
367
+ meta.discordToken = 'keychain';
368
+ }
369
+ else {
370
+ deleteAgentSecret(slug, 'DISCORD_TOKEN');
371
+ meta.discordToken = undefined;
372
+ }
373
+ }
374
+ if (changes.discordChannelId !== undefined)
375
+ meta.discordChannelId = changes.discordChannelId || undefined;
376
+ if (changes.slackBotToken !== undefined) {
377
+ if (changes.slackBotToken) {
378
+ storeAgentSecret(slug, 'SLACK_BOT_TOKEN', changes.slackBotToken);
379
+ meta.slackBotToken = 'keychain';
380
+ }
381
+ else {
382
+ deleteAgentSecret(slug, 'SLACK_BOT_TOKEN');
383
+ meta.slackBotToken = undefined;
384
+ }
385
+ }
386
+ if (changes.slackAppToken !== undefined) {
387
+ if (changes.slackAppToken) {
388
+ storeAgentSecret(slug, 'SLACK_APP_TOKEN', changes.slackAppToken);
389
+ meta.slackAppToken = 'keychain';
390
+ }
391
+ else {
392
+ deleteAgentSecret(slug, 'SLACK_APP_TOKEN');
393
+ meta.slackAppToken = undefined;
394
+ }
395
+ }
396
+ if (changes.slackChannelId !== undefined)
397
+ meta.slackChannelId = changes.slackChannelId || undefined;
398
+ if (changes.sendPolicy !== undefined)
399
+ meta.sendPolicy = changes.sendPolicy || undefined;
400
+ if (changes.status !== undefined)
401
+ meta.status = changes.status;
402
+ if (changes.budgetMonthlyCents !== undefined)
403
+ meta.budgetMonthlyCents = changes.budgetMonthlyCents || undefined;
404
+ const newBody = changes.personality ?? body;
405
+ const updated = matter.stringify(newBody, meta);
406
+ fs.writeFileSync(agentFile, updated);
407
+ // Invalidate cache
408
+ this.cacheTime = 0;
409
+ return this.get(slug);
410
+ }
411
+ /** Quick status update without touching other config. */
412
+ setStatus(slug, status) {
413
+ this.updateAgent(slug, { status });
414
+ }
415
+ /** Check if an agent is runnable (active status). */
416
+ isRunnable(slug) {
417
+ const agent = this.get(slug);
418
+ if (!agent)
419
+ return false;
420
+ return !agent.status || agent.status === 'active';
421
+ }
422
+ deleteAgent(slug) {
423
+ const agentDir = path.join(this.agentsDir, slug);
424
+ if (!fs.existsSync(agentDir)) {
425
+ throw new Error(`Agent '${slug}' not found.`);
426
+ }
427
+ // Clean up Keychain secrets
428
+ deleteAgentSecret(slug, 'DISCORD_TOKEN');
429
+ deleteAgentSecret(slug, 'SLACK_BOT_TOKEN');
430
+ deleteAgentSecret(slug, 'SLACK_APP_TOKEN');
431
+ // Remove directory recursively
432
+ fs.rmSync(agentDir, { recursive: true, force: true });
433
+ // Invalidate cache
434
+ this.cacheTime = 0;
435
+ }
436
+ /** Force cache refresh (used after external modifications). */
437
+ invalidateCache() {
438
+ this.cacheTime = 0;
439
+ }
440
+ }
441
+ //# sourceMappingURL=agent-manager.js.map