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.
- package/LICENSE +663 -0
- package/README.md +69 -19
- package/SOUL.md +29 -26
- package/config/mcp.json +126 -66
- package/daemora-ui/README.md +11 -0
- package/package.json +12 -2
- package/skills/api-development.md +35 -0
- package/skills/artifacts-builder/SKILL.md +74 -0
- package/skills/artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/skills/artifacts-builder/scripts/init-artifact.sh +322 -0
- package/skills/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/brand-guidelines.md +73 -0
- package/skills/browser.md +77 -0
- package/skills/changelog-generator.md +104 -0
- package/skills/coding.md +26 -10
- package/skills/content-research-writer.md +538 -0
- package/skills/data-analysis.md +27 -0
- package/skills/debugging.md +33 -0
- package/skills/devops.md +37 -0
- package/skills/document-docx.md +197 -0
- package/skills/document-pdf.md +294 -0
- package/skills/document-pptx.md +484 -0
- package/skills/document-xlsx.md +289 -0
- package/skills/domain-name-brainstormer.md +212 -0
- package/skills/file-organizer.md +433 -0
- package/skills/frontend-design.md +42 -0
- package/skills/image-enhancer.md +99 -0
- package/skills/invoice-organizer.md +446 -0
- package/skills/lead-research-assistant.md +199 -0
- package/skills/mcp-builder/SKILL.md +328 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +916 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +752 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/meeting-insights-analyzer.md +327 -0
- package/skills/orchestration.md +93 -0
- package/skills/raffle-winner-picker.md +159 -0
- package/skills/slack-gif-creator/SKILL.md +646 -0
- package/skills/slack-gif-creator/core/color_palettes.py +302 -0
- package/skills/slack-gif-creator/core/easing.py +230 -0
- package/skills/slack-gif-creator/core/frame_composer.py +469 -0
- package/skills/slack-gif-creator/core/gif_builder.py +246 -0
- package/skills/slack-gif-creator/core/typography.py +357 -0
- package/skills/slack-gif-creator/core/validators.py +264 -0
- package/skills/slack-gif-creator/core/visual_effects.py +494 -0
- package/skills/slack-gif-creator/requirements.txt +4 -0
- package/skills/slack-gif-creator/templates/bounce.py +106 -0
- package/skills/slack-gif-creator/templates/explode.py +331 -0
- package/skills/slack-gif-creator/templates/fade.py +329 -0
- package/skills/slack-gif-creator/templates/flip.py +291 -0
- package/skills/slack-gif-creator/templates/kaleidoscope.py +211 -0
- package/skills/slack-gif-creator/templates/morph.py +329 -0
- package/skills/slack-gif-creator/templates/move.py +293 -0
- package/skills/slack-gif-creator/templates/pulse.py +268 -0
- package/skills/slack-gif-creator/templates/shake.py +127 -0
- package/skills/slack-gif-creator/templates/slide.py +291 -0
- package/skills/slack-gif-creator/templates/spin.py +269 -0
- package/skills/slack-gif-creator/templates/wiggle.py +300 -0
- package/skills/slack-gif-creator/templates/zoom.py +312 -0
- package/skills/system-admin.md +44 -0
- package/skills/tailored-resume-generator.md +345 -0
- package/skills/theme-factory/SKILL.md +59 -0
- package/skills/theme-factory/theme-showcase.pdf +0 -0
- package/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/skills/theme-factory/themes/desert-rose.md +19 -0
- package/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/skills/theme-factory/themes/golden-hour.md +19 -0
- package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/skills/video-downloader.md +99 -0
- package/skills/web-development.md +32 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/src/agents/SubAgentManager.js +134 -16
- package/src/agents/systemPrompt.js +427 -0
- package/src/api/openai-compat.js +212 -0
- package/src/channels/TelegramChannel.js +5 -2
- package/src/channels/index.js +7 -10
- package/src/cli.js +281 -55
- package/src/config/agentProfiles.js +1 -0
- package/src/config/default.js +15 -1
- package/src/config/models.js +314 -78
- package/src/config/permissions.js +12 -0
- package/src/core/AgentLoop.js +70 -50
- package/src/core/Compaction.js +111 -11
- package/src/core/MessageQueue.js +90 -0
- package/src/core/Task.js +13 -0
- package/src/core/TaskQueue.js +1 -1
- package/src/core/TaskRunner.js +81 -6
- package/src/index.js +725 -59
- package/src/mcp/MCPAgentRunner.js +48 -11
- package/src/mcp/MCPManager.js +40 -2
- package/src/models/ModelRouter.js +74 -4
- package/src/safety/DockerSandbox.js +212 -0
- package/src/safety/ExecApproval.js +118 -0
- package/src/scheduler/Heartbeat.js +56 -21
- package/src/services/cleanup.js +106 -0
- package/src/services/sessions.js +39 -1
- package/src/setup/wizard.js +125 -75
- package/src/skills/SkillLoader.js +132 -17
- package/src/storage/TaskStore.js +19 -1
- package/src/tools/browserAutomation.js +615 -104
- package/src/tools/executeCommand.js +19 -1
- package/src/tools/index.js +7 -1
- package/src/tools/manageAgents.js +55 -4
- package/src/tools/replyWithFile.js +62 -0
- package/src/tools/screenCapture.js +12 -1
- package/src/tools/taskManager.js +164 -0
- package/src/tools/useMCP.js +3 -1
- package/src/utils/Embeddings.js +236 -12
- package/src/webhooks/WebhookHandler.js +107 -0
- 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 "
|
|
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 =
|
|
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
|
-
// ──
|
|
233
|
-
const
|
|
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
|
-
|
|
236
|
-
|
|
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:
|
|
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
|
|
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", {
|
|
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) {
|