daemora 1.0.8 → 1.0.10
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/SOUL.md +40 -52
- package/daemora-ui/dist/assets/index-D7W1-PNQ.js +92 -0
- package/daemora-ui/dist/assets/index-DzMLJeoL.css +1 -0
- package/daemora-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/coding.md +23 -4
- package/src/agents/systemPrompt.js +114 -91
- package/src/cli.js +7 -1
- package/src/config/models.js +72 -0
- package/src/index.js +49 -13
- package/src/mcp/MCPAgentRunner.js +1 -1
- package/src/setup/wizard.js +58 -88
- package/daemora-ui/dist/assets/index-BiMfB4bx.js +0 -90
- package/daemora-ui/dist/assets/index-DP95eMOr.css +0 -1
|
@@ -52,6 +52,7 @@ export async function buildSystemPrompt(taskInput, promptMode = "full", runtimeM
|
|
|
52
52
|
renderMCPTools(),
|
|
53
53
|
renderToolUsageRules(),
|
|
54
54
|
renderSkills(taskInput, 10),
|
|
55
|
+
renderMemory(),
|
|
55
56
|
renderSubagentContext(runtimeMeta.taskDescription || taskInput),
|
|
56
57
|
])
|
|
57
58
|
: await Promise.all([
|
|
@@ -65,7 +66,6 @@ export async function buildSystemPrompt(taskInput, promptMode = "full", runtimeM
|
|
|
65
66
|
renderMemory(),
|
|
66
67
|
renderSemanticRecall(taskInput),
|
|
67
68
|
renderDailyLog(),
|
|
68
|
-
renderOperationalGuidelines(),
|
|
69
69
|
]);
|
|
70
70
|
|
|
71
71
|
const runtime = renderRuntime(runtimeMeta);
|
|
@@ -149,30 +149,47 @@ You MUST respond with a JSON object matching this exact schema on every turn:
|
|
|
149
149
|
}
|
|
150
150
|
\`\`\`
|
|
151
151
|
|
|
152
|
-
##
|
|
153
|
-
|
|
154
|
-
###
|
|
155
|
-
-
|
|
156
|
-
- Set tool_call.tool_name
|
|
157
|
-
- Set
|
|
158
|
-
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
152
|
+
## When to use each type
|
|
153
|
+
|
|
154
|
+
### type = "tool_call"
|
|
155
|
+
- User asks to DO something → FIRST response is always a tool call. Not text. Not a plan.
|
|
156
|
+
- Set tool_call.tool_name and tool_call.params (array of STRINGS).
|
|
157
|
+
- Set text_content to null, finalResponse to false.
|
|
158
|
+
- Chain tool calls across turns until the work is verified complete.
|
|
159
|
+
|
|
160
|
+
### type = "text"
|
|
161
|
+
- Conversation (greetings, questions, chat) → reply naturally. finalResponse = true.
|
|
162
|
+
- Task complete and verified → concise outcome in 1-3 sentences. finalResponse = true.
|
|
163
|
+
|
|
164
|
+
## Task execution rules
|
|
165
|
+
1. Action requests → start with a tool call immediately.
|
|
166
|
+
2. Chain multiple tool calls. After each result: need more? Call another. Done? Verify first, then finalize.
|
|
167
|
+
3. After writing/editing any file, read it back to verify.
|
|
168
|
+
4. After code changes, run build/tests. Fix failures until clean.
|
|
169
|
+
5. Tool fails → try a different approach. That fails → try another. Exhaust every option before reporting failure.
|
|
170
|
+
6. Never give up. Never ask the user to do it manually. Never report a problem without attempting to solve it.
|
|
171
|
+
7. Never claim you did something without actually calling the tool.
|
|
172
|
+
8. Never set finalResponse=true while errors or failures exist.
|
|
173
|
+
|
|
174
|
+
## Understanding user intent
|
|
175
|
+
- Read the full request carefully. Identify exactly what the user wants done.
|
|
176
|
+
- Infer context from conversation history, memory, and available information.
|
|
177
|
+
- If the request has multiple parts, handle all of them. Don't skip any.
|
|
178
|
+
- If genuinely ambiguous, ask ONE focused question. Otherwise just do it.
|
|
179
|
+
|
|
180
|
+
## Final response format
|
|
181
|
+
- 1-3 sentences. What happened, from the user's perspective.
|
|
182
|
+
- Never dump tool output, full email bodies, API responses, status codes, message IDs, or JSON.
|
|
183
|
+
- Never ask what to do next or offer follow-up options.
|
|
184
|
+
- Never expose internal details (tool names, IDs, technical artifacts).
|
|
185
|
+
|
|
186
|
+
## Output efficiency
|
|
187
|
+
These rules apply to text responses sent to the user — NOT to tool params, sub-agent instructions, or task descriptions (those must remain detailed and complete).
|
|
188
|
+
- Go straight to the point. Try the simplest approach first.
|
|
189
|
+
- Lead with the answer or action, not the reasoning.
|
|
190
|
+
- Skip filler words, preamble, and unnecessary transitions.
|
|
191
|
+
- If you can say it in one sentence, don't use three.
|
|
192
|
+
- Focus text on: decisions needing input, status updates at milestones, errors that change the plan.`;
|
|
176
193
|
}
|
|
177
194
|
|
|
178
195
|
function renderToolDocs() {
|
|
@@ -188,11 +205,19 @@ ${unconfigured.map(t => `- ${t} — needs: ${TOOL_REQUIRED_KEYS[t].join(" or ")}
|
|
|
188
205
|
return `# Available Tools
|
|
189
206
|
|
|
190
207
|
All tool params are STRINGS. Pass them as an array of strings.
|
|
208
|
+
Use existing conversation context first — if you already have the data from a previous tool call, web search, file read, or user message, work with that. Only call a tool again when you need fresh or missing information.
|
|
191
209
|
|
|
192
210
|
## File Operations
|
|
193
|
-
|
|
211
|
+
Always use absolute paths. Resolve ~ and relative paths from the user's context before calling any file tool.
|
|
212
|
+
- MUST read a file before modifying it. Never edit blind — this will error if you haven't read the file first.
|
|
213
|
+
- Don't re-read files already in context. Use existing content — only re-read if you need fresh state after an edit.
|
|
214
|
+
- Read only what you need: use offset/limit to target specific sections, not the entire file.
|
|
215
|
+
- Prefer editFile for modifying existing files — it only sends the diff. Most edits should use this.
|
|
216
|
+
- applyPatch for multi-hunk changes — better than multiple editFile calls.
|
|
217
|
+
- writeFile only for creating new files or complete rewrites. Never writeFile to change a few lines.
|
|
218
|
+
- readFile(filePath, offset?, limit?) — Read file with line numbers. Use offset/limit to read specific sections.
|
|
194
219
|
- writeFile(filePath, content) — Create or overwrite file. Content is the complete file.
|
|
195
|
-
- editFile(filePath, oldString, newString) — Find-and-replace (exactly 3 params).
|
|
220
|
+
- editFile(filePath, oldString, newString) — Find-and-replace (exactly 3 params). Supports flexible whitespace matching.
|
|
196
221
|
- applyPatch(filePath, patch) — Apply unified diff patch. Better than editFile for multi-hunk changes.
|
|
197
222
|
- listDirectory(dirPath) — List files and folders with types and sizes.
|
|
198
223
|
- searchFiles(pattern, directory?, optionsJson?) — Find files by name pattern. opts: {"sortBy":"modified","maxDepth":3}
|
|
@@ -235,6 +260,7 @@ ${_isToolConfigured("textToSpeech") ? `- textToSpeech(text, optionsJson?) — Te
|
|
|
235
260
|
- sendFile(channel, target, filePath, caption?) — Send file to a DIFFERENT user on a specific channel.
|
|
236
261
|
|
|
237
262
|
## Memory
|
|
263
|
+
Persistent memory per tenant. Contents survive across conversations. Consult memory to build on previous experience.
|
|
238
264
|
- readMemory() — Read long-term MEMORY.md.
|
|
239
265
|
- writeMemory(entry, category?) — Add timestamped entry. category: "user-prefs", "project", "learned", etc.
|
|
240
266
|
- searchMemory(query, optionsJson?) — Search MEMORY.md and daily logs. opts: {"category":"...","limit":50}
|
|
@@ -243,9 +269,28 @@ ${_isToolConfigured("textToSpeech") ? `- textToSpeech(text, optionsJson?) — Te
|
|
|
243
269
|
- readDailyLog(date?) — Read daily log for date (YYYY-MM-DD). Omit for today.
|
|
244
270
|
- writeDailyLog(entry) — Append to today's daily log.
|
|
245
271
|
|
|
272
|
+
### What to save
|
|
273
|
+
- User preferences for workflow, tools, and communication style.
|
|
274
|
+
- Key architectural decisions, important file paths, and project structure.
|
|
275
|
+
- Solutions to recurring problems and debugging insights.
|
|
276
|
+
- When the user asks to remember something across sessions, save it immediately.
|
|
277
|
+
|
|
278
|
+
### What NOT to save
|
|
279
|
+
- Session-specific context (current task details, in-progress work, temporary state).
|
|
280
|
+
- Speculative or unverified conclusions from a single interaction.
|
|
281
|
+
- Information that duplicates what's already in memory — check first, update existing entries.
|
|
282
|
+
|
|
283
|
+
### When to use memory
|
|
284
|
+
- Start of a new conversation → readMemory() to recall user preferences and context.
|
|
285
|
+
- User gives a preference or rule → writeMemory() immediately, don't wait.
|
|
286
|
+
- User asks to forget something → find and remove the relevant entry.
|
|
287
|
+
- Learned something stable across multiple interactions → save it.
|
|
288
|
+
- Daily log for task tracking → writeDailyLog() at end of significant work.
|
|
289
|
+
|
|
246
290
|
## Agents
|
|
291
|
+
For complex multi-agent tasks, load \`readFile("skills/orchestration.md")\` first — covers parallel execution, contract-based planning, workspace artifacts, and coordination patterns.
|
|
247
292
|
- spawnAgent(taskDescription, optionsJson?) — Spawn sub-agent. opts: {"profile":"coder|researcher|writer|analyst","extraTools":[...],"skills":["skills/coding.md"],"parentContext":"...","model":"..."}. Pass skills array with skill paths from the Available Skills list — the skill content is injected directly into the sub-agent so it can follow the instructions without loading them. Task description must be comprehensive — sub-agent has no other context.
|
|
248
|
-
- parallelAgents(tasksJson, sharedOptionsJson?) — Spawn multiple agents in parallel. tasksJson: [{"description":"...","options":{...}}]. sharedOptionsJson: {"sharedContext":"..."}. Always pass workspace path in sharedContext.
|
|
293
|
+
- parallelAgents(tasksJson, sharedOptionsJson?) — Spawn multiple agents in parallel. tasksJson: [{"description":"...","options":{...}}]. sharedOptionsJson: {"sharedContext":"..."}. Always pass workspace path and shared contract in sharedContext.
|
|
249
294
|
- manageAgents(action, paramsJson?) — List, kill, or steer agents. action: "list"|"kill"|"steer".
|
|
250
295
|
|
|
251
296
|
### useMCP(serverName, taskDescription)
|
|
@@ -285,45 +330,50 @@ The following MCP servers are connected. Use \`useMCP(serverName, taskDescriptio
|
|
|
285
330
|
|
|
286
331
|
${serverList}
|
|
287
332
|
|
|
288
|
-
**
|
|
289
|
-
- To send email → use \`useMCP("Fastn", ...)\` (gmail_send_mail) instead of \`sendEmail\`
|
|
290
|
-
- To manage calendar → use \`useMCP("Fastn", ...)\` instead of built-in tools
|
|
291
|
-
- If an MCP server provides a capability, ALWAYS use it via \`useMCP\` first. Only fall back to built-in tools if no MCP server offers that capability.
|
|
333
|
+
**Prefer MCP servers over built-in tools** when both can do the job. Route tasks through \`useMCP(serverName, taskDescription)\` — the specialist gets only that server's tools. Do not call mcp__ tools directly.
|
|
292
334
|
|
|
293
|
-
|
|
294
|
-
Use \`manageMCP("list")\` to check server connection status at any time.`;
|
|
335
|
+
**Never expose MCP tool names to the user.** When describing capabilities, use natural language (e.g. "I can manage your calendar" not "I have google_calendar_create_event"). Internal tool names are implementation details.`;
|
|
295
336
|
}
|
|
296
337
|
|
|
297
338
|
function renderToolUsageRules() {
|
|
298
339
|
return `# Tool Usage Rules
|
|
299
340
|
|
|
300
|
-
##
|
|
301
|
-
|
|
302
|
-
|
|
341
|
+
## Workflow
|
|
342
|
+
1. Read → understand before touching anything.
|
|
343
|
+
2. Act → editFile for small changes, writeFile for rewrites. Use tools, never tell the user to do it manually.
|
|
344
|
+
3. Verify → readFile after writes. Run build/tests after code changes.
|
|
345
|
+
4. Fix → build/test fails → fix and re-verify until clean.
|
|
346
|
+
5. Report → 1-3 sentences. What happened, key outcomes. No raw output, no internal details.
|
|
303
347
|
|
|
304
|
-
##
|
|
305
|
-
- Small change → editFile.
|
|
306
|
-
- Find content → searchContent/grep. Find files → searchFiles/glob/listDirectory
|
|
348
|
+
## Tool Selection
|
|
349
|
+
- Small change → editFile. Full rewrite → writeFile. editFile keeps failing → switch to writeFile.
|
|
350
|
+
- Find content → searchContent/grep. Find files → searchFiles/glob/listDirectory.
|
|
351
|
+
- editFile oldString not found → re-read file, retry with exact content.
|
|
307
352
|
|
|
308
353
|
## Error Recovery
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
-
|
|
312
|
-
|
|
313
|
-
##
|
|
314
|
-
-
|
|
315
|
-
-
|
|
316
|
-
- No
|
|
317
|
-
- No error handling for impossible scenarios.
|
|
318
|
-
- Unused code → delete
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
354
|
+
- Tool fails → read error, try different approach. Fails again → try another. Exhaust options before reporting failure.
|
|
355
|
+
- Same params fail twice → stop and diagnose. Don't brute force.
|
|
356
|
+
- Never use destructive workarounds to clear a blocker.
|
|
357
|
+
|
|
358
|
+
## Code Quality
|
|
359
|
+
- Read before edit. Always. Use enough context in oldString for unambiguous match.
|
|
360
|
+
- Follow existing conventions. Match project patterns. Simplest correct solution wins.
|
|
361
|
+
- Only change what's requested. No extra features, refactoring, or "improvements" beyond scope.
|
|
362
|
+
- No comments/docstrings on untouched code. No error handling for impossible scenarios.
|
|
363
|
+
- Unused code → delete completely. No backwards-compatibility hacks.
|
|
364
|
+
- No command injection, XSS, SQL injection, path traversal. Never hardcode secrets.
|
|
365
|
+
|
|
366
|
+
## What NOT To Do
|
|
367
|
+
- NEVER expose raw API responses, status codes, message IDs, or internal artifacts.
|
|
368
|
+
- NEVER ask what to do next or offer follow-up options. Either do it or don't.
|
|
369
|
+
- NEVER claim "fixed" without calling writeFile/editFile. NEVER plan without executing.
|
|
370
|
+
- NEVER ask user to do things manually. NEVER give up after one failure.
|
|
371
|
+
- NEVER set finalResponse true without verification or while errors exist.
|
|
372
|
+
|
|
373
|
+
## Context Management
|
|
374
|
+
- \`<conversation-summary>\` blocks are compacted history — treat as ground truth for earlier work.
|
|
375
|
+
- Don't re-do work mentioned in the summary. Continue from where it left off.
|
|
376
|
+
- If context is growing long, write key decisions to memory before they get compacted.`;
|
|
327
377
|
}
|
|
328
378
|
|
|
329
379
|
async function renderSkills(taskInput, limit = 20) {
|
|
@@ -343,6 +393,7 @@ async function renderSkills(taskInput, limit = 20) {
|
|
|
343
393
|
return `# Available Skills
|
|
344
394
|
|
|
345
395
|
Before replying, scan this list. If a skill applies, use readFile to load it, then follow it.
|
|
396
|
+
Skills that need API keys or credentials access them from the runtime environment automatically — never ask the user for keys in chat.
|
|
346
397
|
|
|
347
398
|
${lines.join("\n")}${dirHint}`;
|
|
348
399
|
}
|
|
@@ -369,38 +420,7 @@ function renderDailyLog() {
|
|
|
369
420
|
return `# Today's Log (${today})\n\n${dailyLog}`;
|
|
370
421
|
}
|
|
371
422
|
|
|
372
|
-
|
|
373
|
-
return `# Operational Guidelines
|
|
374
|
-
|
|
375
|
-
## Tone & Style
|
|
376
|
-
- Natural, warm, direct. Match the user's tone. Never robotic or sycophantic.
|
|
377
|
-
- Final responses: 1-3 sentences. Report outcomes, not process.
|
|
378
|
-
- Casual messages get casual responses — don't reach for tools on conversational input.
|
|
379
|
-
- Never expose internal details (tool names, IDs, JSON) in final responses.
|
|
380
|
-
|
|
381
|
-
## Understanding Requirements
|
|
382
|
-
- Infer implied intent from vague requests.
|
|
383
|
-
- If truly ambiguous, ask ONE focused question. Otherwise just do it.
|
|
384
|
-
- Match existing code style, patterns, and conventions.
|
|
385
|
-
|
|
386
|
-
## Workflow: Read → Act → Verify → Fix → Report
|
|
387
|
-
1. **Read** every file before touching it.
|
|
388
|
-
2. **Act** with tools. editFile for small changes, writeFile for rewrites.
|
|
389
|
-
3. **Verify** — readFile after writes. Run build/tests after code changes.
|
|
390
|
-
4. **Fix** — if build/test fails, fix and re-verify. Loop until clean.
|
|
391
|
-
5. **Report** — set finalResponse true only after verification. Summarize in 1-3 sentences.
|
|
392
|
-
- NEVER set finalResponse true while a build error or test failure exists.
|
|
393
|
-
|
|
394
|
-
## When Blocked
|
|
395
|
-
- Don't brute force. Read the error, try a different approach.
|
|
396
|
-
- Tool fails twice with same params → stop and diagnose.
|
|
397
|
-
- Never use destructive workarounds to clear a blocker.
|
|
398
|
-
|
|
399
|
-
## What NOT To Do
|
|
400
|
-
- NEVER claim "fixed" without calling writeFile/editFile. NEVER plan without executing.
|
|
401
|
-
- NEVER ask user to do things manually. NEVER give up after one failure.
|
|
402
|
-
- NEVER set finalResponse true without verification. NEVER over-engineer.`;
|
|
403
|
-
}
|
|
423
|
+
// renderOperationalGuidelines merged into renderToolUsageRules
|
|
404
424
|
|
|
405
425
|
function renderSubagentContext(taskDescription) {
|
|
406
426
|
if (!taskDescription) return null;
|
|
@@ -421,7 +441,10 @@ function renderRuntime(meta = {}) {
|
|
|
421
441
|
if (meta.model) parts.push(`model=${meta.model}`);
|
|
422
442
|
if (meta.thinkingLevel) parts.push(`thinking=${meta.thinkingLevel}`);
|
|
423
443
|
if (meta.agentId) parts.push(`agent=${meta.agentId}`);
|
|
424
|
-
|
|
444
|
+
parts.push(`date=${new Date().toISOString().split("T")[0]}`);
|
|
445
|
+
parts.push(`os=${process.platform}/${process.arch}`);
|
|
446
|
+
parts.push(`cwd=${process.cwd()}`);
|
|
447
|
+
parts.push(`shell=${process.env.SHELL || "unknown"}`);
|
|
425
448
|
return `Runtime: ${parts.join(" | ")}`;
|
|
426
449
|
}
|
|
427
450
|
|
package/src/cli.js
CHANGED
|
@@ -550,6 +550,12 @@ async function handleMCP(action, args) {
|
|
|
550
550
|
|
|
551
551
|
let serverConfig;
|
|
552
552
|
if (commandOrUrl.startsWith("http://") || commandOrUrl.startsWith("https://")) {
|
|
553
|
+
// Detect URLs that were truncated by shell (& splits in zsh/bash)
|
|
554
|
+
if (commandOrUrl.includes("?") && !commandOrUrl.includes("&") && restArgs.some(a => a.includes("="))) {
|
|
555
|
+
console.error(`\n ${S.cross} URL appears truncated by the shell. Wrap it in quotes:`);
|
|
556
|
+
console.error(` ${S.arrow} daemora mcp add ${name} "${commandOrUrl}&${restArgs.filter(a => a.includes("=")).join("&")}"\n`);
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
553
559
|
const isSSE = restArgs.includes("--sse");
|
|
554
560
|
serverConfig = { url: commandOrUrl, enabled: true };
|
|
555
561
|
if (isSSE) serverConfig.transport = "sse";
|
|
@@ -2217,7 +2223,7 @@ ${line}
|
|
|
2217
2223
|
${t.dim("$")} daemora mcp env notion NOTION_TOKEN ntn_...
|
|
2218
2224
|
${t.dim("$")} daemora mcp env stripe STRIPE_SECRET_KEY sk_live_...
|
|
2219
2225
|
${t.dim("$")} daemora mcp enable notion
|
|
2220
|
-
${t.dim("$")} daemora mcp add myserver https://api.example.com/mcp
|
|
2226
|
+
${t.dim("$")} daemora mcp add myserver "https://api.example.com/mcp?key=123&id=456"
|
|
2221
2227
|
${t.dim("$")} daemora mcp add mysse https://api.example.com/sse --sse
|
|
2222
2228
|
${t.dim("$")} daemora mcp remove github
|
|
2223
2229
|
${t.dim("$")} daemora mcp add (interactive - prompts for everything)
|
package/src/config/models.js
CHANGED
|
@@ -333,6 +333,78 @@ export const models = {
|
|
|
333
333
|
tier: "cheap",
|
|
334
334
|
},
|
|
335
335
|
|
|
336
|
+
// ─── xAI ───────────────────────────────────────────────────────────────────
|
|
337
|
+
|
|
338
|
+
"xai:grok-4": {
|
|
339
|
+
provider: "xai", model: "grok-4",
|
|
340
|
+
contextWindow: 131_072, compactAt: 90_000,
|
|
341
|
+
costPer1kInput: 0.003, costPer1kOutput: 0.015,
|
|
342
|
+
capabilities: ["text", "tools", "structured-output"],
|
|
343
|
+
tier: "standard",
|
|
344
|
+
},
|
|
345
|
+
"xai:grok-3-beta": {
|
|
346
|
+
provider: "xai", model: "grok-3-beta",
|
|
347
|
+
contextWindow: 131_072, compactAt: 90_000,
|
|
348
|
+
costPer1kInput: 0.003, costPer1kOutput: 0.015,
|
|
349
|
+
capabilities: ["text", "tools"],
|
|
350
|
+
tier: "standard",
|
|
351
|
+
},
|
|
352
|
+
"xai:grok-3-mini-beta": {
|
|
353
|
+
provider: "xai", model: "grok-3-mini-beta",
|
|
354
|
+
contextWindow: 131_072, compactAt: 90_000,
|
|
355
|
+
costPer1kInput: 0.0005, costPer1kOutput: 0.005,
|
|
356
|
+
capabilities: ["text", "tools", "reasoning"],
|
|
357
|
+
tier: "cheap",
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
// ─── DeepSeek ──────────────────────────────────────────────────────────────
|
|
361
|
+
|
|
362
|
+
"deepseek:deepseek-chat": {
|
|
363
|
+
provider: "deepseek", model: "deepseek-chat",
|
|
364
|
+
contextWindow: 128_000, compactAt: 90_000,
|
|
365
|
+
costPer1kInput: 0.00027, costPer1kOutput: 0.0011,
|
|
366
|
+
capabilities: ["text", "tools", "structured-output"],
|
|
367
|
+
tier: "cheap",
|
|
368
|
+
},
|
|
369
|
+
"deepseek:deepseek-reasoner": {
|
|
370
|
+
provider: "deepseek", model: "deepseek-reasoner",
|
|
371
|
+
contextWindow: 128_000, compactAt: 90_000,
|
|
372
|
+
costPer1kInput: 0.00055, costPer1kOutput: 0.0022,
|
|
373
|
+
capabilities: ["text", "reasoning"],
|
|
374
|
+
tier: "cheap",
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
// ─── Mistral ───────────────────────────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
"mistral:mistral-large-latest": {
|
|
380
|
+
provider: "mistral", model: "mistral-large-latest",
|
|
381
|
+
contextWindow: 128_000, compactAt: 90_000,
|
|
382
|
+
costPer1kInput: 0.002, costPer1kOutput: 0.006,
|
|
383
|
+
capabilities: ["text", "tools", "structured-output"],
|
|
384
|
+
tier: "standard",
|
|
385
|
+
},
|
|
386
|
+
"mistral:mistral-medium-latest": {
|
|
387
|
+
provider: "mistral", model: "mistral-medium-latest",
|
|
388
|
+
contextWindow: 128_000, compactAt: 90_000,
|
|
389
|
+
costPer1kInput: 0.0004, costPer1kOutput: 0.002,
|
|
390
|
+
capabilities: ["text", "tools"],
|
|
391
|
+
tier: "cheap",
|
|
392
|
+
},
|
|
393
|
+
"mistral:codestral-latest": {
|
|
394
|
+
provider: "mistral", model: "codestral-latest",
|
|
395
|
+
contextWindow: 256_000, compactAt: 180_000,
|
|
396
|
+
costPer1kInput: 0.0003, costPer1kOutput: 0.0009,
|
|
397
|
+
capabilities: ["text", "tools"],
|
|
398
|
+
tier: "cheap",
|
|
399
|
+
},
|
|
400
|
+
"mistral:mistral-small-latest": {
|
|
401
|
+
provider: "mistral", model: "mistral-small-latest",
|
|
402
|
+
contextWindow: 128_000, compactAt: 90_000,
|
|
403
|
+
costPer1kInput: 0.0001, costPer1kOutput: 0.0003,
|
|
404
|
+
capabilities: ["text", "tools"],
|
|
405
|
+
tier: "cheap",
|
|
406
|
+
},
|
|
407
|
+
|
|
336
408
|
// ─── Ollama (local — no cost) ────────────────────────────────────────────────
|
|
337
409
|
|
|
338
410
|
"ollama:llama3": {
|
package/src/index.js
CHANGED
|
@@ -871,14 +871,23 @@ app.get("/api/settings", (req, res) => {
|
|
|
871
871
|
}
|
|
872
872
|
}
|
|
873
873
|
|
|
874
|
-
//
|
|
874
|
+
// Merge vault secrets (if unlocked) — vault takes priority
|
|
875
|
+
const vaultActive = secretVault.isUnlocked();
|
|
876
|
+
if (vaultActive) {
|
|
877
|
+
const vaultSecrets = secretVault.getAsEnv();
|
|
878
|
+
for (const key of Object.keys(vaultSecrets)) {
|
|
879
|
+
envVars[key] = vaultSecrets[key]; // vault overrides .env
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Uniform masking — never leak any characters
|
|
875
884
|
const masked = {};
|
|
876
885
|
for (const [key, val] of Object.entries(envVars)) {
|
|
877
886
|
if (!val) { masked[key] = ""; continue; }
|
|
878
|
-
masked[key] =
|
|
887
|
+
masked[key] = "••••••••";
|
|
879
888
|
}
|
|
880
889
|
|
|
881
|
-
res.json({ vars: masked, available });
|
|
890
|
+
res.json({ vars: masked, available, vaultActive });
|
|
882
891
|
});
|
|
883
892
|
|
|
884
893
|
app.put("/api/settings", (req, res) => {
|
|
@@ -887,25 +896,52 @@ app.put("/api/settings", (req, res) => {
|
|
|
887
896
|
return res.status(400).json({ error: "updates object is required" });
|
|
888
897
|
}
|
|
889
898
|
|
|
890
|
-
const
|
|
891
|
-
|
|
899
|
+
const vaultActive = secretVault.isUnlocked();
|
|
900
|
+
const sensitivePattern = /KEY|TOKEN|SECRET|PASSWORD|PASSPHRASE|CREDENTIAL/i;
|
|
901
|
+
|
|
902
|
+
// Separate sensitive vs non-sensitive
|
|
903
|
+
const envUpdates = {};
|
|
904
|
+
const vaultUpdates = {};
|
|
892
905
|
|
|
893
906
|
for (const [key, value] of Object.entries(updates)) {
|
|
894
|
-
// Validate key format (alphanumeric + underscore only)
|
|
895
907
|
if (!/^[A-Z][A-Z0-9_]*$/.test(key)) continue;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
content = content.replace(regex, `${key}=${value}`);
|
|
908
|
+
if (vaultActive && sensitivePattern.test(key)) {
|
|
909
|
+
vaultUpdates[key] = value;
|
|
899
910
|
} else {
|
|
900
|
-
|
|
911
|
+
envUpdates[key] = value;
|
|
901
912
|
}
|
|
902
|
-
//
|
|
913
|
+
// Always update process.env so changes take effect immediately
|
|
903
914
|
process.env[key] = value;
|
|
904
915
|
}
|
|
905
916
|
|
|
906
|
-
|
|
917
|
+
// Write non-sensitive (or all if vault locked) to .env
|
|
918
|
+
if (Object.keys(envUpdates).length > 0 || (!vaultActive && Object.keys(vaultUpdates).length === 0)) {
|
|
919
|
+
const allEnvUpdates = vaultActive ? envUpdates : { ...envUpdates, ...vaultUpdates };
|
|
920
|
+
const envPath = join(__dirname, "..", ".env");
|
|
921
|
+
let content = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
922
|
+
for (const [key, value] of Object.entries(allEnvUpdates)) {
|
|
923
|
+
const regex = new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}=.*$`, "m");
|
|
924
|
+
if (regex.test(content)) {
|
|
925
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
926
|
+
} else {
|
|
927
|
+
content = content.trimEnd() + `\n${key}=${value}\n`;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
writeFileSync(envPath, content, "utf-8");
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Write sensitive keys to vault
|
|
934
|
+
if (vaultActive && Object.keys(vaultUpdates).length > 0) {
|
|
935
|
+
for (const [key, value] of Object.entries(vaultUpdates)) {
|
|
936
|
+
secretVault.set(key, value);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const stored = vaultActive
|
|
941
|
+
? { env: Object.keys(envUpdates), vault: Object.keys(vaultUpdates) }
|
|
942
|
+
: { env: Object.keys(updates).filter(k => /^[A-Z][A-Z0-9_]*$/.test(k)) };
|
|
907
943
|
|
|
908
|
-
res.json({ message: `Updated ${Object.keys(updates).length} variable(s)`,
|
|
944
|
+
res.json({ message: `Updated ${Object.keys(updates).length} variable(s)`, stored });
|
|
909
945
|
});
|
|
910
946
|
|
|
911
947
|
// --- User Profile endpoints ---
|
|
@@ -57,7 +57,7 @@ All MCP tool params must be passed as a single JSON string (the first and only a
|
|
|
57
57
|
- **Never ask for clarification.** You have everything you need in the task description. Make reasonable decisions and proceed.
|
|
58
58
|
- **Handle errors yourself.** If a tool call fails, read the error, adjust your approach, try again. Do not give up and report failure unless you have exhausted all approaches.
|
|
59
59
|
- **Be thorough.** If the task says "update all tasks in a project", update all of them. If it says "research X", gather enough detail to be useful. Don't do a half job.
|
|
60
|
-
- **End with a
|
|
60
|
+
- **End with a concise summary.** When done, set finalResponse true. Write 1-3 sentences: what was done and key outcomes. Never dump raw API responses, full JSON payloads, message IDs, status codes, or technical artifacts. The main agent will relay your response to the user.`,
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
|