daemora 1.0.4 → 1.0.6

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 (123) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +69 -19
  3. package/SOUL.md +29 -26
  4. package/config/mcp.json +126 -66
  5. package/daemora-ui/README.md +11 -0
  6. package/package.json +12 -2
  7. package/skills/api-development.md +35 -0
  8. package/skills/artifacts-builder/SKILL.md +74 -0
  9. package/skills/artifacts-builder/scripts/bundle-artifact.sh +54 -0
  10. package/skills/artifacts-builder/scripts/init-artifact.sh +322 -0
  11. package/skills/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  12. package/skills/brand-guidelines.md +73 -0
  13. package/skills/browser.md +77 -0
  14. package/skills/changelog-generator.md +104 -0
  15. package/skills/coding.md +26 -10
  16. package/skills/content-research-writer.md +538 -0
  17. package/skills/data-analysis.md +27 -0
  18. package/skills/debugging.md +33 -0
  19. package/skills/devops.md +37 -0
  20. package/skills/document-docx.md +197 -0
  21. package/skills/document-pdf.md +294 -0
  22. package/skills/document-pptx.md +484 -0
  23. package/skills/document-xlsx.md +289 -0
  24. package/skills/domain-name-brainstormer.md +212 -0
  25. package/skills/file-organizer.md +433 -0
  26. package/skills/frontend-design.md +42 -0
  27. package/skills/image-enhancer.md +99 -0
  28. package/skills/invoice-organizer.md +446 -0
  29. package/skills/lead-research-assistant.md +199 -0
  30. package/skills/mcp-builder/SKILL.md +328 -0
  31. package/skills/mcp-builder/reference/evaluation.md +602 -0
  32. package/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
  33. package/skills/mcp-builder/reference/node_mcp_server.md +916 -0
  34. package/skills/mcp-builder/reference/python_mcp_server.md +752 -0
  35. package/skills/mcp-builder/scripts/connections.py +151 -0
  36. package/skills/mcp-builder/scripts/evaluation.py +373 -0
  37. package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  38. package/skills/mcp-builder/scripts/requirements.txt +2 -0
  39. package/skills/meeting-insights-analyzer.md +327 -0
  40. package/skills/orchestration.md +93 -0
  41. package/skills/raffle-winner-picker.md +159 -0
  42. package/skills/slack-gif-creator/SKILL.md +646 -0
  43. package/skills/slack-gif-creator/core/color_palettes.py +302 -0
  44. package/skills/slack-gif-creator/core/easing.py +230 -0
  45. package/skills/slack-gif-creator/core/frame_composer.py +469 -0
  46. package/skills/slack-gif-creator/core/gif_builder.py +246 -0
  47. package/skills/slack-gif-creator/core/typography.py +357 -0
  48. package/skills/slack-gif-creator/core/validators.py +264 -0
  49. package/skills/slack-gif-creator/core/visual_effects.py +494 -0
  50. package/skills/slack-gif-creator/requirements.txt +4 -0
  51. package/skills/slack-gif-creator/templates/bounce.py +106 -0
  52. package/skills/slack-gif-creator/templates/explode.py +331 -0
  53. package/skills/slack-gif-creator/templates/fade.py +329 -0
  54. package/skills/slack-gif-creator/templates/flip.py +291 -0
  55. package/skills/slack-gif-creator/templates/kaleidoscope.py +211 -0
  56. package/skills/slack-gif-creator/templates/morph.py +329 -0
  57. package/skills/slack-gif-creator/templates/move.py +293 -0
  58. package/skills/slack-gif-creator/templates/pulse.py +268 -0
  59. package/skills/slack-gif-creator/templates/shake.py +127 -0
  60. package/skills/slack-gif-creator/templates/slide.py +291 -0
  61. package/skills/slack-gif-creator/templates/spin.py +269 -0
  62. package/skills/slack-gif-creator/templates/wiggle.py +300 -0
  63. package/skills/slack-gif-creator/templates/zoom.py +312 -0
  64. package/skills/system-admin.md +44 -0
  65. package/skills/tailored-resume-generator.md +345 -0
  66. package/skills/theme-factory/SKILL.md +59 -0
  67. package/skills/theme-factory/theme-showcase.pdf +0 -0
  68. package/skills/theme-factory/themes/arctic-frost.md +19 -0
  69. package/skills/theme-factory/themes/botanical-garden.md +19 -0
  70. package/skills/theme-factory/themes/desert-rose.md +19 -0
  71. package/skills/theme-factory/themes/forest-canopy.md +19 -0
  72. package/skills/theme-factory/themes/golden-hour.md +19 -0
  73. package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  74. package/skills/theme-factory/themes/modern-minimalist.md +19 -0
  75. package/skills/theme-factory/themes/ocean-depths.md +19 -0
  76. package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  77. package/skills/theme-factory/themes/tech-innovation.md +19 -0
  78. package/skills/video-downloader.md +99 -0
  79. package/skills/web-development.md +32 -0
  80. package/skills/webapp-testing/SKILL.md +96 -0
  81. package/skills/webapp-testing/examples/console_logging.py +35 -0
  82. package/skills/webapp-testing/examples/element_discovery.py +40 -0
  83. package/skills/webapp-testing/examples/static_html_automation.py +33 -0
  84. package/skills/webapp-testing/scripts/with_server.py +106 -0
  85. package/src/agents/SubAgentManager.js +134 -16
  86. package/src/agents/systemPrompt.js +427 -0
  87. package/src/api/openai-compat.js +212 -0
  88. package/src/channels/TelegramChannel.js +5 -2
  89. package/src/channels/index.js +7 -10
  90. package/src/cli.js +281 -55
  91. package/src/config/agentProfiles.js +1 -0
  92. package/src/config/default.js +15 -1
  93. package/src/config/models.js +314 -78
  94. package/src/config/permissions.js +12 -0
  95. package/src/core/AgentLoop.js +70 -50
  96. package/src/core/Compaction.js +111 -11
  97. package/src/core/MessageQueue.js +90 -0
  98. package/src/core/Task.js +13 -0
  99. package/src/core/TaskQueue.js +1 -1
  100. package/src/core/TaskRunner.js +81 -6
  101. package/src/index.js +725 -59
  102. package/src/mcp/MCPAgentRunner.js +48 -11
  103. package/src/mcp/MCPManager.js +40 -2
  104. package/src/models/ModelRouter.js +74 -4
  105. package/src/safety/DockerSandbox.js +212 -0
  106. package/src/safety/ExecApproval.js +118 -0
  107. package/src/scheduler/Heartbeat.js +56 -21
  108. package/src/services/cleanup.js +106 -0
  109. package/src/services/sessions.js +39 -1
  110. package/src/setup/wizard.js +125 -75
  111. package/src/skills/SkillLoader.js +132 -17
  112. package/src/storage/TaskStore.js +19 -1
  113. package/src/tools/browserAutomation.js +615 -104
  114. package/src/tools/executeCommand.js +19 -1
  115. package/src/tools/index.js +7 -1
  116. package/src/tools/manageAgents.js +55 -4
  117. package/src/tools/replyWithFile.js +62 -0
  118. package/src/tools/screenCapture.js +12 -1
  119. package/src/tools/taskManager.js +164 -0
  120. package/src/tools/useMCP.js +3 -1
  121. package/src/utils/Embeddings.js +236 -12
  122. package/src/webhooks/WebhookHandler.js +107 -0
  123. package/src/systemPrompt.js +0 -528
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Start one or more servers, wait for them to be ready, run a command, then clean up.
4
+
5
+ Usage:
6
+ # Single server
7
+ python scripts/with_server.py --server "npm run dev" --port 5173 -- python automation.py
8
+ python scripts/with_server.py --server "npm start" --port 3000 -- python test.py
9
+
10
+ # Multiple servers
11
+ python scripts/with_server.py \
12
+ --server "cd backend && python server.py" --port 3000 \
13
+ --server "cd frontend && npm run dev" --port 5173 \
14
+ -- python test.py
15
+ """
16
+
17
+ import subprocess
18
+ import socket
19
+ import time
20
+ import sys
21
+ import argparse
22
+
23
+ def is_server_ready(port, timeout=30):
24
+ """Wait for server to be ready by polling the port."""
25
+ start_time = time.time()
26
+ while time.time() - start_time < timeout:
27
+ try:
28
+ with socket.create_connection(('localhost', port), timeout=1):
29
+ return True
30
+ except (socket.error, ConnectionRefusedError):
31
+ time.sleep(0.5)
32
+ return False
33
+
34
+
35
+ def main():
36
+ parser = argparse.ArgumentParser(description='Run command with one or more servers')
37
+ parser.add_argument('--server', action='append', dest='servers', required=True, help='Server command (can be repeated)')
38
+ parser.add_argument('--port', action='append', dest='ports', type=int, required=True, help='Port for each server (must match --server count)')
39
+ parser.add_argument('--timeout', type=int, default=30, help='Timeout in seconds per server (default: 30)')
40
+ parser.add_argument('command', nargs=argparse.REMAINDER, help='Command to run after server(s) ready')
41
+
42
+ args = parser.parse_args()
43
+
44
+ # Remove the '--' separator if present
45
+ if args.command and args.command[0] == '--':
46
+ args.command = args.command[1:]
47
+
48
+ if not args.command:
49
+ print("Error: No command specified to run")
50
+ sys.exit(1)
51
+
52
+ # Parse server configurations
53
+ if len(args.servers) != len(args.ports):
54
+ print("Error: Number of --server and --port arguments must match")
55
+ sys.exit(1)
56
+
57
+ servers = []
58
+ for cmd, port in zip(args.servers, args.ports):
59
+ servers.append({'cmd': cmd, 'port': port})
60
+
61
+ server_processes = []
62
+
63
+ try:
64
+ # Start all servers
65
+ for i, server in enumerate(servers):
66
+ print(f"Starting server {i+1}/{len(servers)}: {server['cmd']}")
67
+
68
+ # Use shell=True to support commands with cd and &&
69
+ process = subprocess.Popen(
70
+ server['cmd'],
71
+ shell=True,
72
+ stdout=subprocess.PIPE,
73
+ stderr=subprocess.PIPE
74
+ )
75
+ server_processes.append(process)
76
+
77
+ # Wait for this server to be ready
78
+ print(f"Waiting for server on port {server['port']}...")
79
+ if not is_server_ready(server['port'], timeout=args.timeout):
80
+ raise RuntimeError(f"Server failed to start on port {server['port']} within {args.timeout}s")
81
+
82
+ print(f"Server ready on port {server['port']}")
83
+
84
+ print(f"\nAll {len(servers)} server(s) ready")
85
+
86
+ # Run the command
87
+ print(f"Running: {' '.join(args.command)}\n")
88
+ result = subprocess.run(args.command)
89
+ sys.exit(result.returncode)
90
+
91
+ finally:
92
+ # Clean up all servers
93
+ print(f"\nStopping {len(server_processes)} server(s)...")
94
+ for i, process in enumerate(server_processes):
95
+ try:
96
+ process.terminate()
97
+ process.wait(timeout=5)
98
+ except subprocess.TimeoutExpired:
99
+ process.kill()
100
+ process.wait()
101
+ print(f"Server {i+1} stopped")
102
+ print("All servers stopped")
103
+
104
+
105
+ if __name__ == '__main__':
106
+ main()
@@ -1,11 +1,14 @@
1
1
  import { runAgentLoop } from "../core/AgentLoop.js";
2
- import { buildSystemPrompt } from "../systemPrompt.js";
2
+ import { buildSystemPrompt } from "./systemPrompt.js";
3
3
  import { toolFunctions } from "../tools/index.js";
4
4
  import { agentProfiles, defaultSubAgentTools } from "../config/agentProfiles.js";
5
+ import { config } from "../config/default.js";
5
6
  import eventBus from "../core/EventBus.js";
6
7
  import { v4 as uuidv4 } from "uuid";
7
8
  import tenantContext from "../tenants/TenantContext.js";
8
9
  import { resolveModelForProfile } from "../models/ModelRouter.js";
10
+ import { createSession, getSession, setMessages } from "../services/sessions.js";
11
+ import skillLoader from "../skills/SkillLoader.js";
9
12
 
10
13
  /**
11
14
  * Sub-Agent Manager - spawns, tracks, kills, and steers sub-agents.
@@ -27,6 +30,27 @@ import { resolveModelForProfile } from "../models/ModelRouter.js";
27
30
 
28
31
  const MAX_CONCURRENT_SUB_AGENTS = 7;
29
32
 
33
+ /**
34
+ * Resolve sub-agent model. Priority:
35
+ * 1. Profile-specific route (tenant modelRoutes[profile] or CODE_MODEL/etc env)
36
+ * 2. SUB_AGENT_MODEL (tenant-level or env-level)
37
+ * 3. Parent agent's model (inherit from main agent)
38
+ * 4. DEFAULT_MODEL
39
+ */
40
+ function _resolveSubAgentModel(profile, tenantConfig, parentModel) {
41
+ const _profileEnvMap = { coder: "CODE_MODEL", researcher: "RESEARCH_MODEL", writer: "WRITER_MODEL", analyst: "ANALYST_MODEL" };
42
+ // 1. Profile-specific
43
+ if (profile && tenantConfig.modelRoutes?.[profile]) return tenantConfig.modelRoutes[profile];
44
+ if (profile && process.env[_profileEnvMap[profile]]) return process.env[_profileEnvMap[profile]];
45
+ // 2. Sub-agent model (tenant > env)
46
+ if (tenantConfig.subAgentModel) return tenantConfig.subAgentModel;
47
+ if (process.env.SUB_AGENT_MODEL) return process.env.SUB_AGENT_MODEL;
48
+ // 3. Parent's model — sub-agents default to the same model as the main agent
49
+ if (parentModel) return parentModel;
50
+ // 4. Global default
51
+ return config.defaultModel;
52
+ }
53
+
30
54
  /** Map<agentId, { taskDescription, startedAt, parentTaskId, abortController, steerQueue }> */
31
55
  const activeSubAgents = new Map();
32
56
 
@@ -81,6 +105,7 @@ eventBus.on("supervisor:kill", ({ taskId }) => {
81
105
  * @param {number} [options.depth] Recursion depth (managed internally)
82
106
  * @param {string} [options.parentTaskId] Parent task ID for kill propagation
83
107
  * @param {string} [options.parentContext] Summary/context from parent agent
108
+ * @param {string[]} [options.skills] Skill paths to inject (e.g. ["skills/coding.md", "skills/brand-guidelines.md"])
84
109
  * @param {string} [options.approvalMode] Inherited approval mode
85
110
  * @param {object} [options.channelMeta] Inherited channel meta for approvals
86
111
  * @returns {Promise<string>} Sub-agent's final response
@@ -94,12 +119,15 @@ export async function spawnSubAgent(taskDescription, options = {}) {
94
119
  toolOverride = null, // exact tool functions - specialist agents only (e.g. MCP)
95
120
  systemPromptOverride = null, // replace system prompt - specialist agents only
96
121
  maxCost = 0.10,
97
- timeout = 120_000,
122
+ timeout = 300_000,
98
123
  depth = 0,
99
124
  parentTaskId = null,
100
125
  parentContext = null,
126
+ skills = null, // explicit skill paths to inject (e.g. ["skills/coding.md"])
101
127
  approvalMode = "auto",
102
128
  channelMeta = null,
129
+ historyMessages = [], // previous session messages to prepend (persistent sub-agent sessions)
130
+ returnFullResult = false, // return {text, messages, cost} instead of just text
103
131
  } = options;
104
132
 
105
133
  const maxDepth = 3;
@@ -114,17 +142,18 @@ export async function spawnSubAgent(taskDescription, options = {}) {
114
142
  const agentId = uuidv4().slice(0, 8);
115
143
  const taskId = `subagent-${agentId}`;
116
144
 
145
+ // ── Model resolution ────────────────────────────────────────────────────
146
+ // Priority: explicit > profile route > SUB_AGENT_MODEL > parent's model > DEFAULT_MODEL
147
+ const store = tenantContext.getStore();
148
+ const parentModel = store?.resolvedModel || null;
149
+ const resolvedModel = model
150
+ || _resolveSubAgentModel(profile, store?.resolvedConfig || {}, parentModel);
151
+
117
152
  const profileLabel = profile ? ` [${profile}]` : "";
118
153
  const modelLabel = resolvedModel ? ` ${C.dim}(${resolvedModel})${C.reset}` : "";
119
154
  _agentLog(C.cyan + C.bold, "🤖 SPAWN", agentId, depth,
120
155
  `${C.cyan}${C.bold}${profileLabel}${C.reset}${modelLabel} "${taskDescription.slice(0, 80)}${taskDescription.length > 80 ? "…" : ""}"`);
121
156
 
122
- // ── Model resolution - priority: explicit > profile routing > parent > global default ───────
123
- const store = tenantContext.getStore();
124
- const resolvedModel = model
125
- || resolveModelForProfile(profile, store?.resolvedConfig || {}, null)
126
- || store?.resolvedModel
127
- || config.defaultModel;
128
157
 
129
158
  const apiKeys = store?.apiKeys || {};
130
159
 
@@ -229,14 +258,77 @@ export async function spawnSubAgent(taskDescription, options = {}) {
229
258
  taskDescription: taskDescription.slice(0, 100),
230
259
  });
231
260
 
232
- // ── Build initial messages (optionally include parent context) ────────────
233
- const initialMessages = [];
261
+ // ── Auto session load for regular sub-agents (not MCP they manage their own) ──
262
+ const mainSessionId = store?.sessionId || null;
263
+ const shouldManageSession = !toolOverride && historyMessages.length === 0 && mainSessionId;
264
+ let subSessionId = null;
265
+
266
+ if (shouldManageSession) {
267
+ const sessionKey = profile || "general";
268
+ subSessionId = `${mainSessionId}--${sessionKey}`;
269
+ const subSession = getSession(subSessionId);
270
+ if (subSession && subSession.messages.length > 0) {
271
+ historyMessages = subSession.messages.map(m => ({ role: m.role, content: m.content }));
272
+ console.log(`[SubAgent:${agentId}] Loaded ${historyMessages.length} history messages from "${subSessionId}"`);
273
+ }
274
+ }
275
+
276
+ // ── Skill injection ─────────────────────────────────────────────────────
277
+ // Priority: 1) Explicit skills passed by parent 2) Semantic embedding search
278
+ // Both produce full skill content so the sub-agent doesn't waste a readFile turn.
279
+ let skillContext = "";
280
+ try {
281
+ const injectedSkills = [];
282
+
283
+ // 1. Explicit skills — parent agent passed skill paths/names directly
284
+ if (skills && skills.length > 0) {
285
+ for (const ref of skills) {
286
+ const skill = skillLoader.getSkill(ref);
287
+ if (skill) {
288
+ injectedSkills.push(skill);
289
+ } else {
290
+ console.log(`[SubAgent:${agentId}] Skill not found: "${ref}"`);
291
+ }
292
+ }
293
+ }
294
+
295
+ // 2. Semantic embedding search — find relevant skills the parent didn't explicitly pass
296
+ if (injectedSkills.length === 0 && taskDescription) {
297
+ const semanticResult = await skillLoader.getSkillPromptsAsync(taskDescription);
298
+ if (semanticResult) {
299
+ // getSkillPromptsAsync returns formatted string with --- Skill: name --- blocks
300
+ skillContext = semanticResult;
301
+ }
302
+ }
303
+
304
+ // Format explicitly-passed skills
305
+ if (injectedSkills.length > 0) {
306
+ skillContext = injectedSkills.map(s =>
307
+ `\n--- Skill: ${s.name} ---\n${s.content}\n--- End Skill ---`
308
+ ).join("\n");
309
+ }
310
+
311
+ if (injectedSkills.length > 0) {
312
+ console.log(`[SubAgent:${agentId}] Injected ${injectedSkills.length} skill(s) (explicit): ${injectedSkills.map(s => s.name).join(", ")}`);
313
+ } else if (skillContext) {
314
+ console.log(`[SubAgent:${agentId}] Injected skills (semantic embedding match)`);
315
+ }
316
+ } catch (e) {
317
+ // Non-blocking — skills are optional
318
+ console.log(`[SubAgent:${agentId}] Skill injection failed (non-blocking): ${e.message}`);
319
+ }
234
320
 
235
- if (parentContext) {
236
- // Give the sub-agent a quick summary of what the parent knows
321
+ // ── Build initial messages (include history + parent context + skills) ──
322
+ const initialMessages = [...historyMessages];
323
+
324
+ const contextParts = [];
325
+ if (parentContext) contextParts.push(`[Context from parent agent]:\n${parentContext}`);
326
+ if (skillContext) contextParts.push(`[Matched Skills — follow these instructions precisely]:\n${skillContext}`);
327
+
328
+ if (contextParts.length > 0) {
237
329
  initialMessages.push({
238
330
  role: "user",
239
- content: `[Context from parent agent]:\n${parentContext}\n\n[Your task]:\n${taskDescription}`,
331
+ content: `${contextParts.join("\n\n")}\n\n[Your task]:\n${taskDescription}`,
240
332
  });
241
333
  } else {
242
334
  initialMessages.push({ role: "user", content: taskDescription });
@@ -249,7 +341,11 @@ export async function spawnSubAgent(taskDescription, options = {}) {
249
341
  const result = await Promise.race([
250
342
  runAgentLoop({
251
343
  messages: initialMessages,
252
- systemPrompt: systemPromptOverride || await buildSystemPrompt(taskDescription),
344
+ systemPrompt: systemPromptOverride || await buildSystemPrompt(taskDescription, "minimal", {
345
+ model: resolvedModel,
346
+ agentId,
347
+ taskDescription,
348
+ }),
253
349
  tools: agentTools,
254
350
  modelId: resolvedModel,
255
351
  taskId,
@@ -268,10 +364,32 @@ export async function spawnSubAgent(taskDescription, options = {}) {
268
364
  ]);
269
365
 
270
366
  const elapsed = ((Date.now() - startedAt) / 1000).toFixed(1);
271
- const costStr = result.cost ? ` $${result.cost.toFixed(4)}` : "";
367
+ const costVal = typeof result.cost === "number" ? result.cost : result.cost?.estimatedCost;
368
+ const costStr = costVal ? ` $${costVal.toFixed(4)}` : "";
272
369
  _agentLog(C.green + C.bold, "✅ DONE ", agentId, depth,
273
370
  `${C.green}${C.bold}completed in ${elapsed}s${costStr}${C.reset}`);
274
- eventBus.emitEvent("agent:finished", { agentId, taskId, parentTaskId, cost: result.cost });
371
+ eventBus.emitEvent("agent:finished", {
372
+ agentId, taskId, parentTaskId, cost: result.cost,
373
+ toolCalls: (result.toolCalls || []).map(tc => ({ tool: tc.tool, duration: tc.duration })),
374
+ resultPreview: (result.text || "").slice(0, 200),
375
+ model: resolvedModel,
376
+ role: profile || "general",
377
+ });
378
+
379
+ // ── Auto session save for regular sub-agents ──────────────────────────
380
+ if (subSessionId && result.messages) {
381
+ let subSession = getSession(subSessionId);
382
+ if (!subSession) subSession = createSession(subSessionId);
383
+ const capped = result.messages.length > 100
384
+ ? result.messages.slice(-100)
385
+ : result.messages;
386
+ setMessages(subSessionId, capped);
387
+ console.log(`[SubAgent:${agentId}] Saved ${capped.length} messages to "${subSessionId}"`);
388
+ }
389
+
390
+ if (returnFullResult) {
391
+ return { text: result.text, messages: result.messages, cost: result.cost };
392
+ }
275
393
  return result.text;
276
394
 
277
395
  } catch (error) {