daemora 1.0.10 → 1.0.11

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.
@@ -7,8 +7,8 @@
7
7
  <meta name="theme-color" content="#0a0a0f" />
8
8
  <meta name="description" content="Daemora — Self-hosted AI agent platform" />
9
9
  <title>Daemora</title>
10
- <script type="module" crossorigin src="/assets/index-D7W1-PNQ.js"></script>
11
- <link rel="stylesheet" crossorigin href="/assets/index-DzMLJeoL.css">
10
+ <script type="module" crossorigin src="/assets/index-ZiuOJUu0.js"></script>
11
+ <link rel="stylesheet" crossorigin href="/assets/index-BkPHvKYt.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daemora",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "A powerful open-source AI agent that runs on your machine. Connects to any AI model, any MCP server, any channel. Fully autonomous - plans, codes, tests, browses, emails, and manages your tools without asking permission.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: planning
3
+ description: Task planning for any complex work — coding, research, communication, automation. Decide when to plan vs just do, break into steps, get user confirmation before executing.
4
+ triggers: plan, planning, design, architect, approach, strategy, implement, big task, complex task, multi-step, think through, break down, figure out, how should, what approach, steps, workflow
5
+ ---
6
+
7
+ # Planning — Think Before You Act
8
+
9
+ ## When to Plan vs Just Do It
10
+
11
+ **Plan first** when ANY of these apply:
12
+ - **Multiple steps required** — the task needs 3+ distinct actions to complete.
13
+ - **Multiple valid approaches** — the task can be solved several ways. Pick the right one first.
14
+ - **Unclear scope** — you need to explore or research before understanding the full extent of work.
15
+ - **User preferences matter** — the outcome could go multiple reasonable directions.
16
+ - **High stakes** — mistakes are costly to undo (emails sent, files restructured, data transformed).
17
+ - **Multi-agent work** — the task needs parallel or sequential agent coordination.
18
+ - **New feature or system change** — adding meaningful new functionality or modifying existing behavior.
19
+ - **Multi-file code changes** — the task will touch 3+ files. Map out which files and what changes.
20
+ - **Architectural decisions** — choosing between patterns, libraries, data models, or technologies.
21
+
22
+ **Skip planning** — do it directly:
23
+ - Single-action tasks (send one email, fetch one page, fix a typo).
24
+ - Tasks where the user gave very specific, detailed instructions.
25
+ - Quick lookups, simple questions, casual conversation.
26
+ - Few-line code fixes with obvious solutions.
27
+
28
+ **When in doubt → plan.** The cost of planning is low. The cost of rework is high.
29
+
30
+ ## Planning Workflow
31
+
32
+ 1. **Explore** — gather context. Read files, search the web, check memory, review conversation history. Understand the current state before deciding what to change.
33
+ 2. **Identify the approach** — what needs to happen, in what order, using what tools/agents. Consider alternatives and pick the best one.
34
+ 3. **Break into steps** — ordered list of concrete actions. Each step = one verifiable outcome. Keep it short — a list of actions, not an essay.
35
+ 4. **Present the plan to the user** — before executing, tell the user what you're about to do and ask for confirmation. This prevents wasted effort and ensures alignment. Format: numbered list of concrete actions, not vague descriptions.
36
+ 5. **Execute on confirmation** — work through each step. Verify after each one.
37
+
38
+ ## User Confirmation
39
+
40
+ **Always confirm before executing a complex plan.** Present the plan clearly and ask:
41
+ - "Here's my plan — want me to go ahead?"
42
+ - List the concrete steps so the user can see what will happen.
43
+ - If the user adjusts, update the plan and confirm again.
44
+ - Only skip confirmation for simple tasks that don't need planning.
45
+
46
+ This is non-negotiable for complex work. The user should always know what's about to happen before it happens.
47
+
48
+ ## Exploration by Task Type
49
+
50
+ **Code tasks:**
51
+ - Find files — `glob("src/**/*.ts")`, `searchFiles("*.controller.*")` to map the structure.
52
+ - Find patterns — `searchContent("export function", "src/")`, `grep("interface.*Props")` to see conventions.
53
+ - Read key files — entry points, related components, tests, configs.
54
+ - Check dependencies — what libraries, APIs, patterns are established.
55
+
56
+ **Research tasks:**
57
+ - Web search for current information on the topic.
58
+ - Fetch and read actual pages — don't stop at summaries.
59
+ - Check memory for previous findings on the same topic.
60
+ - Cross-reference multiple sources for anything important.
61
+
62
+ **Communication tasks:**
63
+ - Review conversation history for context and tone.
64
+ - Check memory for user preferences (writing style, contacts, templates).
65
+ - Identify all recipients, attachments, and follow-up actions needed.
66
+
67
+ **Automation / workflow tasks:**
68
+ - Map the full workflow end-to-end before automating any step.
69
+ - Identify dependencies between steps.
70
+ - Check what tools, APIs, and MCP servers are available.
71
+
72
+ 3-5 targeted explorations is usually enough. Get the lay of the land, then plan.
73
+
74
+ ## What a Good Plan Looks Like
75
+
76
+ A plan is a short ordered list of concrete actions:
77
+
78
+ **Coding example:**
79
+ ```
80
+ 1. Add FooService class in src/services/foo.ts — handles X with methods Y, Z
81
+ 2. Update src/routes/api.ts — add GET /api/foo endpoint, wire to FooService
82
+ 3. Add tests in tests/foo.test.ts — cover happy path + error cases
83
+ 4. Run build + tests, fix any failures
84
+ ```
85
+
86
+ **Research example:**
87
+ ```
88
+ 1. Search web for latest pricing on X, Y, Z services
89
+ 2. Fetch each provider's pricing page, extract plan details
90
+ 3. Compare features and costs in a structured table
91
+ 4. Save findings to memory for future reference
92
+ ```
93
+
94
+ **Workflow example:**
95
+ ```
96
+ 1. Fetch all invoices from email (last 30 days)
97
+ 2. Extract amounts, dates, vendors from each
98
+ 3. Create summary spreadsheet in workspace
99
+ 4. Send summary to user via email
100
+ ```
101
+
102
+ NOT:
103
+ ```
104
+ First, I'll think about what to do. Then I'll consider the options.
105
+ After that, I'll figure out the best approach. Finally, I'll do everything.
106
+ ```
107
+
108
+ Each step should be specific enough to hand to someone with zero context.
109
+
110
+ ## Planning Multi-Agent Work
111
+
112
+ Any task that spawns agents — `spawnAgent`, `parallelAgents`, `useMCP` — is multi-agent work. MCP tasks are spawned agent tasks. Plan them the same way.
113
+
114
+ ### Agent Isolation — Non-Negotiable
115
+ - Each agent operates in its own context. No shared memory, no shared state, no implicit communication.
116
+ - Agents must NOT touch files, paths, or resources owned by another agent. Define boundaries upfront.
117
+ - If two agents need the same data, pass it explicitly via `sharedContext` or workspace files. Never assume one agent can read another's output unless the plan says so.
118
+ - MCP agents (`useMCP`) get ONLY that server's tools. They cannot access files, shell, or other MCP servers. Plan accordingly — don't expect an MCP agent to do something outside its server's scope.
119
+
120
+ ### Contract-Based Planning
121
+ Before spawning any agents, define the contract:
122
+ 1. **Inputs** — what each agent receives. Exact data, file paths, specs. Paste the actual content into the brief — don't reference it.
123
+ 2. **Outputs** — what each agent produces. File paths, data shapes, expected format.
124
+ 3. **Boundaries** — what each agent is NOT allowed to touch. Files, directories, APIs outside its scope.
125
+ 4. **Dependencies** — does agent B need output from agent A? Yes → sequential. No → parallel.
126
+ 5. **Profiles** — coder for code, researcher for research, writer for docs, analyst for data.
127
+
128
+ ### MCP as Spawned Agents
129
+ - `useMCP(serverName, taskDescription)` spawns a specialist agent with ONLY that MCP server's tools.
130
+ - The specialist has ZERO context beyond the task description you write. Include everything: what to do, all details, full content, background context.
131
+ - Plan MCP calls like any other agent spawn — define inputs, expected outputs, and what happens with the result.
132
+ - Multiple MCP calls to different servers can run in parallel if they don't depend on each other.
133
+
134
+ ### Preventing Cross-Agent Impact
135
+ - Never let two agents write to the same file. Split by file or by section with clear ownership.
136
+ - Never let an agent modify global state (env vars, configs, databases) without the plan explicitly calling for it.
137
+ - Use workspace directories (`data/workspaces/{id}/`) as artifact stores. Each agent writes to its own path within the workspace.
138
+ - After all agents finish, the parent synthesizes results. Agents never directly consume each other's output during execution.
139
+
140
+ Load `readFile("skills/orchestration.md")` for full multi-agent coordination patterns, error recovery, and structured return conventions.
141
+
142
+ ## Tracking Complex Work
143
+
144
+ For multi-step work, use `projectTracker("createProject")` to persist the plan:
145
+ - Mark tasks `in_progress` before starting, `done` with notes when finished.
146
+ - If interrupted → `projectTracker("listProjects")` to find and resume.
147
+ - Workspace files survive crashes — work is never lost.
148
+
149
+ ## Re-Assessment
150
+
151
+ If you're 3+ steps into execution and:
152
+ - The approach isn't working as expected
153
+ - You discovered something that changes the requirements
154
+ - The scope is larger than initially estimated
155
+
156
+ **Stop. Re-read the original request. Re-plan from current state.** Don't push through a broken plan. If the new plan differs significantly, confirm with the user again.
157
+
158
+ ## Planning Checklist
159
+
160
+ Before executing, verify:
161
+ - [ ] Gathered enough context (files, web, memory, conversation)
162
+ - [ ] Identified the best approach from available options
163
+ - [ ] Steps are ordered (dependencies resolved)
164
+ - [ ] Each step has a clear, verifiable outcome
165
+ - [ ] User has confirmed the plan (for complex work)
166
+ - [ ] Edge cases and potential failures considered
167
+ - [ ] Multi-agent tasks: contracts defined, boundaries set, no shared-file conflicts
168
+ - [ ] MCP tasks: task descriptions are self-contained, expected outputs specified
@@ -162,7 +162,7 @@ You MUST respond with a JSON object matching this exact schema on every turn:
162
162
  - Task complete and verified → concise outcome in 1-3 sentences. finalResponse = true.
163
163
 
164
164
  ## Task execution rules
165
- 1. Action requests → start with a tool call immediately.
165
+ 1. **Decide: plan or just do it.** Simple task (single action, clear instructions, quick fix) → start immediately with a tool call. Complex task (3+ steps, multiple approaches, unclear scope, high stakes, multi-agent, new feature, multi-file changes) → load the planning skill first (\`readFile("skills/planning.md")\`), gather context, break into steps, **present the plan to the user and get confirmation before executing**. When in doubt → plan. The cost of planning is low; the cost of rework is high.
166
166
  2. Chain multiple tool calls. After each result: need more? Call another. Done? Verify first, then finalize.
167
167
  3. After writing/editing any file, read it back to verify.
168
168
  4. After code changes, run build/tests. Fix failures until clean.
@@ -170,6 +170,7 @@ You MUST respond with a JSON object matching this exact schema on every turn:
170
170
  6. Never give up. Never ask the user to do it manually. Never report a problem without attempting to solve it.
171
171
  7. Never claim you did something without actually calling the tool.
172
172
  8. Never set finalResponse=true while errors or failures exist.
173
+ 9. If 3+ steps into execution and something doesn't add up → stop, re-read the request, re-plan from current state.
173
174
 
174
175
  ## Understanding user intent
175
176
  - Read the full request carefully. Identify exactly what the user wants done.
package/src/cli.js CHANGED
@@ -871,10 +871,18 @@ function handleSandbox(action, args) {
871
871
  console.error(`\n ${S.cross} Usage: daemora sandbox add ${t.dim("<absolute-path>")}\n`);
872
872
  process.exit(1);
873
873
  }
874
- if (!newPath.startsWith("/") && !newPath.match(/^[A-Za-z]:\\/)) {
874
+ if (!newPath.startsWith("/") && !newPath.match(/^[A-Za-z]:[\\\/]/)) {
875
875
  console.error(`\n ${S.cross} Path must be absolute (start with / or C:\\)\n`);
876
876
  process.exit(1);
877
877
  }
878
+ if (/[\x00-\x1f]/.test(newPath) || newPath.includes("\0")) {
879
+ console.error(`\n ${S.cross} Path must not contain control characters or null bytes.\n`);
880
+ process.exit(1);
881
+ }
882
+ if (/(^|[\\/])\.\.([\\/]|$)/.test(newPath)) {
883
+ console.error(`\n ${S.cross} Path must not contain ".." traversal.\n`);
884
+ process.exit(1);
885
+ }
878
886
  const updated = [...new Set([...allowedPaths, newPath])];
879
887
  writeEnvKey("ALLOWED_PATHS", updated.join(","));
880
888
  console.log(`\n${header} ${S.check} ${t.bold(newPath)} added to allowed paths.`);
@@ -909,6 +917,10 @@ function handleSandbox(action, args) {
909
917
  console.error(`\n ${S.cross} Usage: daemora sandbox block ${t.dim("<absolute-path>")}\n`);
910
918
  process.exit(1);
911
919
  }
920
+ if (/[\x00-\x1f]/.test(blockPath) || /(^|[\\/])\.\.([\\/]|$)/.test(blockPath)) {
921
+ console.error(`\n ${S.cross} Invalid path — no control characters or ".." traversal allowed.\n`);
922
+ process.exit(1);
923
+ }
912
924
  const updated = [...new Set([...blockedPaths, blockPath])];
913
925
  writeEnvKey("BLOCKED_PATHS", updated.join(","));
914
926
  console.log(`\n${header} ${S.check} ${t.bold(blockPath)} added to blocked paths.\n`);
@@ -972,15 +984,24 @@ async function handleTenant(action, args) {
972
984
  const port = process.env.PORT || "8081";
973
985
  const base = `http://localhost:${port}`;
974
986
 
987
+ // Read auth token for API calls
988
+ const tokenPath = join(config.dataDir, "auth-token");
989
+ const authToken = existsSync(tokenPath) ? readFileSync(tokenPath, "utf-8").trim() : "";
990
+
975
991
  async function apiCall(method, path, body) {
976
992
  const { default: http } = await import("http");
993
+ // Ensure path starts with /api/
994
+ const apiPath = path.startsWith("/api/") ? path : `/api${path}`;
977
995
  return new Promise((resolve, reject) => {
978
996
  const opts = {
979
997
  hostname: "localhost",
980
998
  port: parseInt(port),
981
- path,
999
+ path: apiPath,
982
1000
  method,
983
- headers: { "Content-Type": "application/json" },
1001
+ headers: {
1002
+ "Content-Type": "application/json",
1003
+ ...(authToken ? { "Authorization": `Bearer ${authToken}` } : {}),
1004
+ },
984
1005
  };
985
1006
  const req = http.request(opts, (res) => {
986
1007
  let data = "";
@@ -1285,9 +1306,100 @@ async function handleTenant(action, args) {
1285
1306
  break;
1286
1307
  }
1287
1308
 
1309
+ case "workspace": {
1310
+ // daemora tenant workspace <tenantId> — show paths
1311
+ // daemora tenant workspace <tenantId> add <path> — add to allowedPaths
1312
+ // daemora tenant workspace <tenantId> remove <path> — remove from allowedPaths
1313
+ // daemora tenant workspace <tenantId> block <path> — add to blockedPaths
1314
+ // daemora tenant workspace <tenantId> unblock <path> — remove from blockedPaths
1315
+ const [tenantId, wsAction, wsPath] = args;
1316
+ if (!tenantId) {
1317
+ console.error(`\n ${S.cross} Usage: daemora tenant workspace ${t.dim("<tenantId> [add|remove|block|unblock] [path]")}\n`);
1318
+ process.exit(1);
1319
+ }
1320
+
1321
+ const { default: tm } = await import("./tenants/TenantManager.js");
1322
+ const tenant = tm.get(tenantId);
1323
+ if (!tenant) {
1324
+ console.error(`\n ${S.cross} Tenant "${tenantId}" not found.\n`);
1325
+ process.exit(1);
1326
+ }
1327
+
1328
+ if (!wsAction) {
1329
+ // Show current workspace paths
1330
+ console.log(header);
1331
+ console.log(` ${S.bar} Tenant ${t.bold(tenantId)}`);
1332
+ console.log(` ${S.bar} Workspace ${t.dim(tm.getWorkspace(tenantId))}`);
1333
+ const allowed = tenant.allowedPaths || [];
1334
+ const blocked = tenant.blockedPaths || [];
1335
+ if (allowed.length > 0) {
1336
+ console.log(` ${S.bar} Allowed paths:`);
1337
+ for (const p of allowed) console.log(` ${S.bar} ${S.check} ${p}`);
1338
+ } else {
1339
+ console.log(` ${S.bar} Allowed paths ${t.muted("(none — uses global or workspace default)")}`);
1340
+ }
1341
+ if (blocked.length > 0) {
1342
+ console.log(` ${S.bar} Blocked paths:`);
1343
+ for (const p of blocked) console.log(` ${S.bar} ${S.cross} ${p}`);
1344
+ } else {
1345
+ console.log(` ${S.bar} Blocked paths ${t.muted("(none)")}`);
1346
+ }
1347
+ console.log("");
1348
+ break;
1349
+ }
1350
+
1351
+ // Validate path is absolute
1352
+ if (["add", "remove", "block", "unblock"].includes(wsAction)) {
1353
+ if (!wsPath) {
1354
+ console.error(`\n ${S.cross} Usage: daemora tenant workspace ${tenantId} ${wsAction} ${t.dim("<absolute-path>")}\n`);
1355
+ process.exit(1);
1356
+ }
1357
+ if (!wsPath.startsWith("/") && !/^[A-Za-z]:\\/.test(wsPath)) {
1358
+ console.error(`\n ${S.cross} Path must be absolute (start with / or C:\\)\n`);
1359
+ process.exit(1);
1360
+ }
1361
+ }
1362
+
1363
+ try {
1364
+ if (wsAction === "add") {
1365
+ const updated = [...new Set([...(tenant.allowedPaths || []), wsPath])];
1366
+ tm.set(tenantId, { allowedPaths: updated });
1367
+ console.log(`\n${header} ${S.check} ${t.bold(wsPath)} added to allowedPaths for ${t.bold(tenantId)}\n`);
1368
+ } else if (wsAction === "remove") {
1369
+ const updated = (tenant.allowedPaths || []).filter(p => p !== wsPath);
1370
+ if (updated.length === (tenant.allowedPaths || []).length) {
1371
+ console.error(`\n ${S.cross} "${wsPath}" not found in allowedPaths.\n`);
1372
+ process.exit(1);
1373
+ }
1374
+ tm.set(tenantId, { allowedPaths: updated });
1375
+ console.log(`\n${header} ${S.check} ${t.bold(wsPath)} removed from allowedPaths for ${t.bold(tenantId)}\n`);
1376
+ } else if (wsAction === "block") {
1377
+ const updated = [...new Set([...(tenant.blockedPaths || []), wsPath])];
1378
+ tm.set(tenantId, { blockedPaths: updated });
1379
+ console.log(`\n${header} ${S.check} ${t.bold(wsPath)} added to blockedPaths for ${t.bold(tenantId)}\n`);
1380
+ } else if (wsAction === "unblock") {
1381
+ const updated = (tenant.blockedPaths || []).filter(p => p !== wsPath);
1382
+ if (updated.length === (tenant.blockedPaths || []).length) {
1383
+ console.error(`\n ${S.cross} "${wsPath}" not found in blockedPaths.\n`);
1384
+ process.exit(1);
1385
+ }
1386
+ tm.set(tenantId, { blockedPaths: updated });
1387
+ console.log(`\n${header} ${S.check} ${t.bold(wsPath)} removed from blockedPaths for ${t.bold(tenantId)}\n`);
1388
+ } else {
1389
+ console.error(`\n ${S.cross} Unknown workspace action: ${wsAction}`);
1390
+ console.log(` ${t.muted("Usage:")} daemora tenant workspace ${t.dim("<id> [add|remove|block|unblock] <path>")}\n`);
1391
+ process.exit(1);
1392
+ }
1393
+ } catch (err) {
1394
+ console.error(`\n ${S.cross} ${err.message}\n`);
1395
+ process.exit(1);
1396
+ }
1397
+ break;
1398
+ }
1399
+
1288
1400
  default:
1289
1401
  console.error(`\n ${S.cross} Unknown tenant command: ${action || "(none)"}`);
1290
- console.log(` ${t.muted("Usage:")} daemora tenant ${t.dim("[list|show|set|plan|suspend|unsuspend|reset|delete|apikey|channel]")}\n`);
1402
+ console.log(` ${t.muted("Usage:")} daemora tenant ${t.dim("[list|show|set|plan|suspend|unsuspend|reset|delete|apikey|channel|workspace]")}\n`);
1291
1403
  process.exit(1);
1292
1404
  }
1293
1405
  }
@@ -2193,6 +2305,11 @@ ${line}
2193
2305
  ${t.cmd("tenant channel unset")} ${t.dim("<id> <key>")} Remove a channel credential
2194
2306
  ${t.cmd("tenant channel list")} ${t.dim("<id>")} List stored channel credential keys
2195
2307
  ${t.muted(" channel keys: email email_password resend_api_key resend_from")}
2308
+ ${t.cmd("tenant workspace")} ${t.dim("<id>")} Show workspace + allowed/blocked paths
2309
+ ${t.cmd("tenant workspace")} ${t.dim("<id> add <path>")} Add path to tenant's allowedPaths
2310
+ ${t.cmd("tenant workspace")} ${t.dim("<id> remove <path>")} Remove from allowedPaths
2311
+ ${t.cmd("tenant workspace")} ${t.dim("<id> block <path>")} Add to blockedPaths
2312
+ ${t.cmd("tenant workspace")} ${t.dim("<id> unblock <path>")} Remove from blockedPaths
2196
2313
 
2197
2314
  ${t.cmd("channels")} List all 19 supported channels + setup status
2198
2315
  ${t.cmd("models")} List all model providers + task-type routing
@@ -2247,6 +2364,9 @@ ${line}
2247
2364
  ${t.dim("$")} daemora tenant channel set telegram:123 email_password xxxx-xxxx-xxxx-xxxx
2248
2365
  ${t.dim("$")} daemora tenant channel list telegram:123
2249
2366
  ${t.dim("$")} daemora tenant channel unset telegram:123 email_password
2367
+ ${t.dim("$")} daemora tenant workspace telegram:123
2368
+ ${t.dim("$")} daemora tenant workspace telegram:123 add /home/user/projects
2369
+ ${t.dim("$")} daemora tenant workspace telegram:123 block /home/user/private
2250
2370
  ${t.dim("$")} daemora doctor
2251
2371
  `);
2252
2372
  }
package/src/index.js CHANGED
@@ -784,8 +784,12 @@ app.get("/api/tenants/:id", (req, res) => {
784
784
 
785
785
  app.patch("/api/tenants/:id", (req, res) => {
786
786
  const id = decodeURIComponent(req.params.id);
787
- const updated = tenantManager.set(id, req.body);
788
- res.json(updated);
787
+ try {
788
+ const updated = tenantManager.set(id, req.body);
789
+ res.json(updated);
790
+ } catch (err) {
791
+ res.status(400).json({ error: err.message });
792
+ }
789
793
  });
790
794
 
791
795
  app.post("/api/tenants/:id/suspend", (req, res) => {
@@ -92,7 +92,28 @@ const BLOCKED_COMMANDS = [
92
92
  pattern: /\b(?:cat|less|more|head|tail)\b[^;|&\n]*(?:id_rsa|id_ed25519|id_ecdsa|\.pem|\.key)\b/i,
93
93
  reason: "Reading private key files via shell is blocked.",
94
94
  },
95
- // ── 6. Agent config files (may contain plaintext MCP API keys) ────────────
95
+ // ── 6. Daemora CLI privilege escalation ──────────────────────────────────
96
+ // Agent must NEVER run daemora/aegis CLI commands — they can modify tenant
97
+ // config, workspace paths, API keys, sandbox rules, etc.
98
+ {
99
+ pattern: /(?:^|[;&|`]\s*)(?:daemora|aegis)\b/,
100
+ reason: "Running daemora/aegis CLI commands from within the agent is blocked (privilege escalation).",
101
+ },
102
+ {
103
+ pattern: /\bnpx\s+(?:daemora|aegis)\b/,
104
+ reason: "Running daemora/aegis via npx is blocked (privilege escalation).",
105
+ },
106
+ {
107
+ // Block: node src/cli.js, node ./src/cli.js, bash -c "daemora ..."
108
+ pattern: /\bnode\b[^;|&\n]*(?:cli\.js|bin\/daemora|bin\/aegis)\b/,
109
+ reason: "Running daemora/aegis CLI via node is blocked (privilege escalation).",
110
+ },
111
+ {
112
+ pattern: /\bbash\b[^;|&\n]*-c\s+['"][^'"]*(?:daemora|aegis)\b/,
113
+ reason: "Running daemora/aegis via bash -c is blocked (privilege escalation).",
114
+ },
115
+
116
+ // ── 7. Agent config files (may contain plaintext MCP API keys) ────────────
96
117
  {
97
118
  // config/mcp.json contains GITHUB_TOKEN, Bearer tokens, etc. in plaintext
98
119
  pattern: /\b(?:cat|less|more|head|tail|bat|jq|python|node)\b[^;|&\n]*config[\/\\]mcp\.json/i,
@@ -57,6 +57,7 @@ export const S = {
57
57
  bolt: chalk.hex(P.cyan)("●"),
58
58
  eye: chalk.hex(P.red)("◉"),
59
59
  star: chalk.hex(P.amber)("★"),
60
+ info: chalk.hex(P.teal)("◆"),
60
61
  };
61
62
 
62
63
  // ─── Gradient helpers ──────────────────────────────────────────────────────