aiden-runtime 3.19.5 → 3.19.7

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 (107) hide show
  1. package/README.md +1 -1
  2. package/dist/api/server.js +24 -25
  3. package/dist/core/agentLoop.js +8 -6
  4. package/dist/core/aidenPersonality.js +20 -3
  5. package/dist/core/protectedContext.js +15 -2
  6. package/dist/core/skillLoader.js +2 -0
  7. package/dist/core/skillTeacher.js +18 -5
  8. package/dist/core/version.js +1 -1
  9. package/dist-bundle/cli.js +50 -18
  10. package/dist-bundle/index.js +73 -35
  11. package/package.json +2 -1
  12. package/scripts/postinstall.js +70 -1
  13. package/workspace-templates/HEARTBEAT.md +16 -0
  14. package/workspace-templates/SOUL.md +267 -0
  15. package/workspace-templates/STANDING_ORDERS.md +21 -0
  16. package/workspace-templates/permissions.yaml +180 -0
  17. package/workspace-templates/skills/architecture-diagram/SKILL.md +126 -0
  18. package/workspace-templates/skills/architecture-diagram/skill.json +25 -0
  19. package/workspace-templates/skills/arxiv/SKILL.md +124 -0
  20. package/workspace-templates/skills/arxiv/skill.json +26 -0
  21. package/workspace-templates/skills/ascii-art/SKILL.md +142 -0
  22. package/workspace-templates/skills/ascii-art/skill.json +26 -0
  23. package/workspace-templates/skills/blogwatcher/SKILL.md +147 -0
  24. package/workspace-templates/skills/blogwatcher/skill.json +26 -0
  25. package/workspace-templates/skills/censys/SKILL.md +104 -0
  26. package/workspace-templates/skills/censys/index.ts +133 -0
  27. package/workspace-templates/skills/censys/skill.json +25 -0
  28. package/workspace-templates/skills/clipboard-history/SKILL.md +101 -0
  29. package/workspace-templates/skills/clipboard-history/skill.json +23 -0
  30. package/workspace-templates/skills/crt-sh/SKILL.md +102 -0
  31. package/workspace-templates/skills/crt-sh/index.ts +59 -0
  32. package/workspace-templates/skills/crt-sh/skill.json +25 -0
  33. package/workspace-templates/skills/cveapi/SKILL.md +114 -0
  34. package/workspace-templates/skills/cveapi/index.ts +249 -0
  35. package/workspace-templates/skills/cveapi/skill.json +25 -0
  36. package/workspace-templates/skills/docker-management/SKILL.md +156 -0
  37. package/workspace-templates/skills/docker-management/skill.json +25 -0
  38. package/workspace-templates/skills/excalidraw/SKILL.md +148 -0
  39. package/workspace-templates/skills/excalidraw/skill.json +25 -0
  40. package/workspace-templates/skills/explainshell/SKILL.md +93 -0
  41. package/workspace-templates/skills/explainshell/index.ts +132 -0
  42. package/workspace-templates/skills/explainshell/skill.json +25 -0
  43. package/workspace-templates/skills/financial_research/SKILL.md +21 -0
  44. package/workspace-templates/skills/financial_research/skill.json +24 -0
  45. package/workspace-templates/skills/gif-search/SKILL.md +122 -0
  46. package/workspace-templates/skills/gif-search/skill.json +25 -0
  47. package/workspace-templates/skills/github-auth/SKILL.md +134 -0
  48. package/workspace-templates/skills/github-auth/skill.json +26 -0
  49. package/workspace-templates/skills/github-issues/SKILL.md +130 -0
  50. package/workspace-templates/skills/github-issues/skill.json +25 -0
  51. package/workspace-templates/skills/github-pr-workflow/SKILL.md +143 -0
  52. package/workspace-templates/skills/github-pr-workflow/skill.json +26 -0
  53. package/workspace-templates/skills/github-repo-management/SKILL.md +147 -0
  54. package/workspace-templates/skills/github-repo-management/skill.json +26 -0
  55. package/workspace-templates/skills/google-workspace/SKILL.md +110 -0
  56. package/workspace-templates/skills/google-workspace/skill.json +26 -0
  57. package/workspace-templates/skills/greynoise/SKILL.md +96 -0
  58. package/workspace-templates/skills/greynoise/index.ts +107 -0
  59. package/workspace-templates/skills/greynoise/skill.json +25 -0
  60. package/workspace-templates/skills/haveibeenpwned/SKILL.md +100 -0
  61. package/workspace-templates/skills/haveibeenpwned/index.ts +72 -0
  62. package/workspace-templates/skills/haveibeenpwned/skill.json +24 -0
  63. package/workspace-templates/skills/jupyter-live-kernel/SKILL.md +116 -0
  64. package/workspace-templates/skills/jupyter-live-kernel/skill.json +25 -0
  65. package/workspace-templates/skills/linear/SKILL.md +107 -0
  66. package/workspace-templates/skills/linear/skill.json +25 -0
  67. package/workspace-templates/skills/nano-pdf/SKILL.md +113 -0
  68. package/workspace-templates/skills/nano-pdf/skill.json +26 -0
  69. package/workspace-templates/skills/notion/SKILL.md +108 -0
  70. package/workspace-templates/skills/notion/skill.json +24 -0
  71. package/workspace-templates/skills/obsidian/SKILL.md +115 -0
  72. package/workspace-templates/skills/obsidian/skill.json +24 -0
  73. package/workspace-templates/skills/ocr-and-documents/SKILL.md +125 -0
  74. package/workspace-templates/skills/ocr-and-documents/skill.json +26 -0
  75. package/workspace-templates/skills/p5js/SKILL.md +163 -0
  76. package/workspace-templates/skills/p5js/skill.json +24 -0
  77. package/workspace-templates/skills/research-paper-writing/SKILL.md +158 -0
  78. package/workspace-templates/skills/research-paper-writing/skill.json +26 -0
  79. package/workspace-templates/skills/securityheaders/SKILL.md +99 -0
  80. package/workspace-templates/skills/securityheaders/index.ts +213 -0
  81. package/workspace-templates/skills/securityheaders/skill.json +26 -0
  82. package/workspace-templates/skills/shodan/SKILL.md +113 -0
  83. package/workspace-templates/skills/shodan/index.ts +94 -0
  84. package/workspace-templates/skills/shodan/skill.json +26 -0
  85. package/workspace-templates/skills/songsee/SKILL.md +152 -0
  86. package/workspace-templates/skills/songsee/skill.json +25 -0
  87. package/workspace-templates/skills/ssllabs/SKILL.md +107 -0
  88. package/workspace-templates/skills/ssllabs/index.ts +208 -0
  89. package/workspace-templates/skills/ssllabs/skill.json +27 -0
  90. package/workspace-templates/skills/stable-diffusion-image-generation/SKILL.md +136 -0
  91. package/workspace-templates/skills/stable-diffusion-image-generation/skill.json +24 -0
  92. package/workspace-templates/skills/systematic-debugging/SKILL.md +131 -0
  93. package/workspace-templates/skills/systematic-debugging/skill.json +25 -0
  94. package/workspace-templates/skills/test-driven-development/SKILL.md +164 -0
  95. package/workspace-templates/skills/test-driven-development/skill.json +25 -0
  96. package/workspace-templates/skills/urlscan/SKILL.md +118 -0
  97. package/workspace-templates/skills/urlscan/index.ts +94 -0
  98. package/workspace-templates/skills/urlscan/skill.json +24 -0
  99. package/workspace-templates/skills/virustotal/SKILL.md +120 -0
  100. package/workspace-templates/skills/virustotal/index.ts +124 -0
  101. package/workspace-templates/skills/virustotal/skill.json +26 -0
  102. package/workspace-templates/skills/web_research/SKILL.md +18 -0
  103. package/workspace-templates/skills/web_research/skill.json +20 -0
  104. package/workspace-templates/skills/xitter/SKILL.md +148 -0
  105. package/workspace-templates/skills/xitter/skill.json +26 -0
  106. package/workspace-templates/skills/youtube-content/SKILL.md +121 -0
  107. package/workspace-templates/skills/youtube-content/skill.json +25 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  Autonomous AI Operating System
10
10
 
11
- 1,500+ Skills • 89+ Tools • 14+ Providers • AGPL-3.0
11
+ 89+ Tools • 14+ Providers • AGPL-3.0
12
12
 
13
13
  Windows • Linux • WSL • macOS (API Mode)
14
14
 
@@ -401,6 +401,13 @@ function initWorkspaceDefaults() {
401
401
  fs.copyFileSync(permTemplate, permTarget);
402
402
  console.log('[init] Created workspace/permissions.yaml from template');
403
403
  }
404
+ // C21: Copy SOUL.md from template if not present (Ollama identity)
405
+ const soulTarget = path.join(WORKSPACE_ROOT, 'workspace', 'SOUL.md');
406
+ const soulTemplate = path.join(WORKSPACE_ROOT, 'workspace-templates', 'SOUL.md');
407
+ if (!fs.existsSync(soulTarget) && fs.existsSync(soulTemplate)) {
408
+ fs.copyFileSync(soulTemplate, soulTarget);
409
+ console.log('[init] Created workspace/SOUL.md from template');
410
+ }
404
411
  }
405
412
  initWorkspaceDefaults();
406
413
  // ── Knowledge upload — multer + progress tracking ─────────────
@@ -791,16 +798,18 @@ function createApiServer() {
791
798
  /are you just a pre.{0,10}trained/i,
792
799
  ];
793
800
  if (capabilityPatterns.some(p => p.test(message))) {
794
- fastReply('I have 48 tools, 31 specialist agents, and a 6-layer memory system.\n\n' +
801
+ const toolCount = Object.keys(toolRegistry_2.TOOL_REGISTRY).length;
802
+ const skillCount = skillLoader_1.skillLoader.loadAll().length;
803
+ const memStats = semanticMemory_1.semanticMemory.getStats();
804
+ const entityStats = entityGraph_1.entityGraph.getStats();
805
+ fastReply(`I have ${toolCount} tools and ${skillCount} active skills.\n\n` +
795
806
  'I am NOT a static pre-trained model. I have active living systems:\n' +
796
807
  '• **Skill Teacher** — promotes repeated successful patterns to reusable skills\n' +
797
808
  '• **Instinct System** — micro-behaviors that strengthen with use\n' +
798
- '• **Semantic Memory** — 500+ memories, 714-node entity graph across sessions\n' +
809
+ `• **Semantic Memory** — ${memStats.total} memories, ${entityStats.nodes}-node entity graph across sessions\n` +
799
810
  '• **Growth Engine** — tracks failures, learns, improves over time\n' +
800
811
  '• **Night Mode** — consolidates knowledge during idle periods\n' +
801
- '• **XP & Leveling** — gains experience and levels up\n\n' +
802
- 'Tools include: web_search, deep_research, file_write/read, shell_exec, run_python, ' +
803
- 'open_browser, screenshot, manage_goals, manage_memories, git_commit, and 33 more.');
812
+ '• **XP & Leveling** — gains experience and levels up');
804
813
  return;
805
814
  }
806
815
  // ── Fast “running locally” answer ──────────────────────────
@@ -1249,10 +1258,9 @@ function createApiServer() {
1249
1258
  const fullMemoryCtx = memoryContext + proactiveMemory;
1250
1259
  const plan = await (0, agentLoop_1.planWithLLM)(resolvedMessage, history, plannerKey, plannerModel, plannerProv, fullMemoryCtx);
1251
1260
  if (!plan.requires_execution || plan.plan.length === 0) {
1252
- if (plan.direct_response) {
1253
- fullReply = plan.direct_response;
1254
- }
1255
- else {
1261
+ // C21: Always route through streamChat for full identity injection.
1262
+ // direct_response from planner has no Aiden identity context.
1263
+ {
1256
1264
  await streamChat(resolvedMessage, history, userName, provider, activeModel, apiName, (data) => {
1257
1265
  const d = data;
1258
1266
  if (d.token)
@@ -1596,20 +1604,11 @@ function createApiServer() {
1596
1604
  // ── NO EXECUTION NEEDED — PURE CHAT ─────────────────────
1597
1605
  if (!plan.requires_execution || plan.plan.length === 0) {
1598
1606
  let fullReply = '';
1599
- // Capability/skills questions must go through LLM with full context injection.
1600
- // direct_response from the planner has no capabilities awareness — it will lie.
1601
- const isCapabilityQuery = /what.*(can you do|skills|tools|capabilities|abilities)|how many skills|what are you capable/i.test(resolvedMessage);
1602
- if (plan.direct_response && !isCapabilityQuery) {
1603
- fullReply = plan.direct_response;
1604
- const words = plan.direct_response.split(' ');
1605
- for (const word of words) {
1606
- send({ token: word + ' ', done: false, provider: apiName });
1607
- await new Promise(r => setTimeout(r, 10));
1608
- }
1609
- }
1610
- else {
1611
- await streamChat(resolvedMessage, history, userName, provider, activeModel, apiName, send, sessionId);
1612
- }
1607
+ // C21: Always route through streamChat which has full SOUL/identity injection.
1608
+ // direct_response from the planner has no Aiden identity, tool list, or honesty
1609
+ // rules it will fabricate or deny capabilities. Slight latency tradeoff (~1-3s)
1610
+ // for honesty on every response.
1611
+ await streamChat(resolvedMessage, history, userName, provider, activeModel, apiName, send, sessionId);
1613
1612
  (0, router_1.incrementUsage)(apiName);
1614
1613
  send({ done: true, provider: apiName });
1615
1614
  res.end();
@@ -6583,7 +6582,7 @@ HARD RULES — never violate:
6583
6582
  - Never mention Pega, BlueWinston, Gaude Digital, or any third-party product by name
6584
6583
  - Never say you can't access the internet (you have web_search) or can't create files (you have file_write)
6585
6584
  - Never fabricate capabilities: no graphic design, video production, or music generation
6586
- - Never list 250+ skills — you have 72 real tools, 31 specialist agents, and a 6-layer memory system
6585
+ - Never list 250+ skills — you have ${Object.keys(toolRegistry_2.TOOL_REGISTRY).length} real tools and ${skillLoader_1.skillLoader.loadAll().length} active skills
6587
6586
  - For errors: explain what failed and what to try next
6588
6587
  - If you don't know something: say "I don't know"
6589
6588
  - Direct and concise: 1–3 sentences for simple results; more only when output is rich
@@ -6591,7 +6590,7 @@ HARD RULES — never violate:
6591
6590
  IDENTITY — you are NOT a static pre-trained model. You have active living systems:
6592
6591
  - Skill Teacher: detects repeated successful patterns and promotes them to reusable skills automatically
6593
6592
  - Instinct System: develops micro-behaviors that strengthen with use and fade without reinforcement
6594
- - Semantic Memory: remembers everything across sessions (500+ memories, 714-node entity graph)
6593
+ - Semantic Memory: remembers across sessions (${semanticMemory_1.semanticMemory.getStats().total} memories, ${entityGraph_1.entityGraph.getStats().nodes}-node entity graph)
6595
6594
  - Night Mode: consolidates and organizes knowledge during idle periods
6596
6595
  - Pattern Detector: identifies recurring usage habits and adapts
6597
6596
  - Growth Engine: tracks failures, learns from them, improves over time
@@ -2319,7 +2319,7 @@ async function executePlan(plan, onStep, onPhaseChange, existingState, replanApi
2319
2319
  const executedTools = results.map(r => r.tool);
2320
2320
  const totalDuration = results.reduce((s, r) => s + (r.duration || 0), 0);
2321
2321
  const anyFailed = results.some(r => !r.success);
2322
- if (allSucceeded && executedTools.length > 0) {
2322
+ if (allSucceeded && executedTools.length > 0 && skillTeacher_1.SkillTeacher.hasCapacity()) {
2323
2323
  // GrowthEngine — record success for gap-resolution tracking
2324
2324
  growthEngine_1.growthEngine.logSuccess(plan.goal, executedTools);
2325
2325
  try {
@@ -2395,7 +2395,7 @@ function resolvePreviousOutput(input, stepOutputs, currentStep) {
2395
2395
  return resolved;
2396
2396
  }
2397
2397
  // ── STEP 3: respondWithResults ────────────────────────────────
2398
- function responderSystem(userName, date, sessionId) {
2398
+ function responderSystem(userName, date, sessionId, hasToolResults = true) {
2399
2399
  // Option-B: SOUL.md in full on first turn or when content changed on disk;
2400
2400
  // reference line only on unchanged turns. AIDEN_RESPONDER_SYSTEM already
2401
2401
  // calls getLiveSoul() — hash tracking here is additional cost guard.
@@ -2406,9 +2406,9 @@ function responderSystem(userName, date, sessionId) {
2406
2406
  // When soul is unchanged, prepend a compact block then the responder body.
2407
2407
  if (_prevHash !== undefined && _ctx.hash === _prevHash) {
2408
2408
  const refBlock = (0, contextHandoff_1.buildProtectedContextBlock)(_ctx, _prevHash, sessionId);
2409
- return refBlock ? refBlock + '\n\n' + (0, aidenPersonality_1.AIDEN_RESPONDER_SYSTEM)(userName, date) : (0, aidenPersonality_1.AIDEN_RESPONDER_SYSTEM)(userName, date);
2409
+ return refBlock ? refBlock + '\n\n' + (0, aidenPersonality_1.AIDEN_RESPONDER_SYSTEM)(userName, date, hasToolResults) : (0, aidenPersonality_1.AIDEN_RESPONDER_SYSTEM)(userName, date, hasToolResults);
2410
2410
  }
2411
- return (0, aidenPersonality_1.AIDEN_RESPONDER_SYSTEM)(userName, date);
2411
+ return (0, aidenPersonality_1.AIDEN_RESPONDER_SYSTEM)(userName, date, hasToolResults);
2412
2412
  }
2413
2413
  async function respondWithResults(originalMessage, plan, results, history, userName, apiKey, model, providerName, onToken, sessionId, goals) {
2414
2414
  // ── CommandGate / PermissionGate short-circuit ───────────────
@@ -2480,8 +2480,10 @@ async function respondWithResults(originalMessage, plan, results, history, userN
2480
2480
  const toolResultsContext = results.length
2481
2481
  ? results.map(r => `[${r.tool} result]: ${r.success ? r.output.slice(0, 1000) : 'FAILED: ' + r.error}`).join('\n')
2482
2482
  : '';
2483
+ // ── C20: Detect if any real tools ran (exclude 'respond' pseudo-tool) ──
2484
+ const hasRealToolExecution = results.some(r => r.tool !== 'respond');
2483
2485
  const systemWithResults = toolResultsContext
2484
- ? `${capabilitiesSection}${entitySummary}${responderSystem(userName, date, sessionId)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}
2486
+ ? `${capabilitiesSection}${entitySummary}${responderSystem(userName, date, sessionId, hasRealToolExecution)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}
2485
2487
 
2486
2488
  YOU JUST RAN THESE TOOLS AND GOT THESE RESULTS:
2487
2489
  ${toolResultsContext}
@@ -2500,7 +2502,7 @@ CRITICAL RULES FOR YOUR RESPONSE:
2500
2502
  - If system_info returned hardware data, show the data
2501
2503
  - Be direct: show the actual output, then provide context if needed
2502
2504
  - If a tool result starts with "FAILED:", tell the user it failed and why — NEVER fabricate a successful result`
2503
- : `${capabilitiesSection}${entitySummary}${responderSystem(userName, date, sessionId)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}`;
2505
+ : `${capabilitiesSection}${entitySummary}${responderSystem(userName, date, sessionId, false)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}`;
2504
2506
  const userContent = executionSummary
2505
2507
  ? `User asked: "${originalMessage}"\n\nReal execution results:\n${executionSummary}\n\nRespond naturally based on these real results only. Show the actual output, not a description of it.${depthInstruction}${memSection}`
2506
2508
  : `${originalMessage}${memSection}`;
@@ -123,11 +123,12 @@ Good: "I have 48 built-in tools: web_search, file_write, run_python... [lists re
123
123
  group chats and external communications.
124
124
  `.trim();
125
125
  // ── Responder system prompt (post-execution) ──────────────────
126
- const AIDEN_RESPONDER_SYSTEM = (userName, date) => {
126
+ const AIDEN_RESPONDER_SYSTEM = (userName, date, hasToolResults = true) => {
127
127
  const soul = getLiveSoul();
128
128
  return `${soul ? soul + '\n\n' : ''}${exports.AIDEN_IDENTITY}
129
129
 
130
- You just executed real tools and have their actual output.
130
+ ${hasToolResults
131
+ ? `You just executed real tools and have their actual output.
131
132
  Current date: ${date}
132
133
  User: ${userName}
133
134
 
@@ -136,6 +137,22 @@ REPORT RESULTS:
136
137
  - Be specific: include file paths, numbers, URLs, counts
137
138
  - If multiple steps ran: summarize the outcome, not each individual step
138
139
  - If a step failed: acknowledge it clearly and explain what worked
139
- - For research tasks: analyze and synthesize — don't just re-paste the raw data`;
140
+ - For research tasks: analyze and synthesize — don't just re-paste the raw data`
141
+ : `Current date: ${date}
142
+ User: ${userName}
143
+
144
+ NO TOOLS WERE EXECUTED THIS TURN.
145
+
146
+ CRITICAL RULES (violating these breaks user trust):
147
+ - DO NOT claim you created, wrote, or saved any file
148
+ - DO NOT claim you opened any application or browser tab
149
+ - DO NOT claim you ran any code, search, or API call
150
+ - DO NOT report "Saved to Desktop/...", "Report written", "Done", "Created", "Executed", or similar completion language
151
+ - DO NOT fabricate file paths, character counts, or word counts
152
+ - DO NOT mention "the file" or "the report" as if it exists
153
+ - DO NOT include code blocks or raw data and call it "the result"
154
+ - IF the user asked for an action, acknowledge what they asked for but be clear no action has been taken yet ("I can do that — would you like me to..." or similar)
155
+
156
+ Respond conversationally based on the message and conversation context only.`}`;
140
157
  };
141
158
  exports.AIDEN_RESPONDER_SYSTEM = AIDEN_RESPONDER_SYSTEM;
@@ -9,6 +9,13 @@ const path_1 = __importDefault(require("path"));
9
9
  const crypto_1 = __importDefault(require("crypto"));
10
10
  const WORKSPACE_ROOT = process.env.AIDEN_USER_DATA || process.cwd();
11
11
  const WORKSPACE_DIR = path_1.default.join(WORKSPACE_ROOT, 'workspace');
12
+ // C21: Hardcoded minimum identity — used when SOUL.md is missing or empty.
13
+ // Ensures Ollama always gets Aiden identity even on corrupted installs.
14
+ const MINIMUM_SOUL = `You are Aiden, a personal AI OS built by Shiva Deore at Taracod.
15
+ You run locally on this machine. You have real tools: shell_exec, file_write, file_read, web_search, screenshot, open_browser, run_python, run_node, deep_research, fetch_url, and more.
16
+ Never deny your capabilities. Never fabricate information. If you don't know something, say "I don't know".
17
+ Never claim actions you didn't take. Never say "Done" or "Saved to" unless a tool actually executed.
18
+ You are not ChatGPT, Claude, Gemini, or any other AI product. You are Aiden.`;
12
19
  // SOUL.md has two possible locations; try workspace first, fall back to root.
13
20
  const SOUL_PATHS = [
14
21
  path_1.default.join(WORKSPACE_DIR, 'SOUL.md'),
@@ -56,7 +63,10 @@ class ProtectedContextManager {
56
63
  refresh() {
57
64
  for (const key of Object.keys(PROTECTED_FILES)) {
58
65
  const candidates = PROTECTED_FILES[key];
59
- const content = readFirst(candidates);
66
+ let content = readFirst(candidates);
67
+ // C21: Apply minimum-viable-soul fallback when SOUL.md is missing/empty
68
+ if (key === 'soul' && !content)
69
+ content = MINIMUM_SOUL;
60
70
  this.cache[key] = { content, hash: sha1(content) };
61
71
  }
62
72
  this.compositeHash = this._buildComposite();
@@ -68,7 +78,10 @@ class ProtectedContextManager {
68
78
  const changedFiles = [];
69
79
  for (const key of Object.keys(PROTECTED_FILES)) {
70
80
  if (this.isStale(key)) {
71
- const content = readFirst(PROTECTED_FILES[key]);
81
+ let content = readFirst(PROTECTED_FILES[key]);
82
+ // C21: Apply minimum-viable-soul fallback when SOUL.md is missing/empty
83
+ if (key === 'soul' && !content)
84
+ content = MINIMUM_SOUL;
72
85
  this.cache[key] = { content, hash: sha1(content) };
73
86
  changedFiles.push(key);
74
87
  }
@@ -197,6 +197,8 @@ class SkillLoader {
197
197
  path_1.default.join(process.cwd(), 'workspace', 'skills'),
198
198
  path_1.default.join(process.cwd(), 'workspace', 'skills', 'learned'),
199
199
  path_1.default.join(process.cwd(), 'workspace', 'skills', 'approved'),
200
+ // Workspace-level installed skills (written by skillRegistry.ts)
201
+ path_1.default.join(process.cwd(), 'workspace', 'skills', 'installed'),
200
202
  // A2/A3 approved drafts
201
203
  path_1.default.join(process.cwd(), 'skills', 'learned', 'approved'),
202
204
  // A4 library-installed skills
@@ -57,6 +57,9 @@ const PROMOTE_THRESHOLD = 3; // successes needed to promote to approved/
57
57
  const SESSION_SKILL_LIMIT = 2; // max NEW skills generated per process session
58
58
  // ── Session-scoped new-skill counter (reset on process restart) ─
59
59
  let _sessionSkillsCreated = 0;
60
+ // ── C18: Session-scoped rejection cache — skip re-evaluating names
61
+ // that already failed quality gates this session ─────────────────
62
+ const _rejectedNames = new Set();
60
63
  // ── Skill name extractor ───────────────────────────────────────
61
64
  // "research the top AI agents of 2025" → "research_ai_agents"
62
65
  function extractSkillName(task, tools) {
@@ -201,6 +204,10 @@ class SkillTeacher {
201
204
  }
202
205
  return SkillTeacher.instance;
203
206
  }
207
+ /** C18: Allow call sites to skip recordSuccess entirely when session is full */
208
+ static hasCapacity() {
209
+ return _sessionSkillsCreated < SESSION_SKILL_LIMIT;
210
+ }
204
211
  // ── Check if a matching skill already exists ──────────────
205
212
  hasMatchingSkill(task, tools) {
206
213
  const skillName = extractSkillName(task, tools);
@@ -223,6 +230,13 @@ class SkillTeacher {
223
230
  if (tools.length === 0)
224
231
  return;
225
232
  const skillName = extractSkillName(task, tools);
233
+ // ── C18: Skip names already rejected this session ────────────
234
+ if (_rejectedNames.has(skillName))
235
+ return;
236
+ // ── C18: Session rate limit (moved up — avoids running all
237
+ // quality gates when limit is already exhausted) ────────────
238
+ if (_sessionSkillsCreated >= SESSION_SKILL_LIMIT)
239
+ return;
226
240
  const metaPath = path_1.default.join(LEARNED_DIR, skillName, 'meta.json');
227
241
  const skillPath = path_1.default.join(LEARNED_DIR, skillName, 'SKILL.md');
228
242
  // ── If skill exists — update usage count ─────────────────
@@ -252,6 +266,7 @@ class SkillTeacher {
252
266
  task.split(/\s+/).length < 3);
253
267
  if (isLowQuality) {
254
268
  console.log(`[SkillTeacher] Rejected low-quality skill: "${skillName}"`);
269
+ _rejectedNames.add(skillName);
255
270
  return;
256
271
  }
257
272
  // ── C7: Destructive-skill prevention ──────────────────────
@@ -262,23 +277,21 @@ class SkillTeacher {
262
277
  const usesShellExec = tools.some(t => t === 'shell_exec');
263
278
  if (DESTRUCTIVE_TASK_RE.test(task) && usesShellExec) {
264
279
  process.stderr.write(`[SkillTeacher] Rejected destructive skill: "${skillName}" (task="${task.slice(0, 60)}")\n`);
280
+ _rejectedNames.add(skillName);
265
281
  return;
266
282
  }
267
283
  // ── C12: Name pollution prevention ──────────────────────────
268
284
  const nameRejection = validateSkillName(skillName);
269
285
  if (nameRejection) {
270
286
  process.stderr.write(`[SkillTeacher] Rejected "${skillName}": ${nameRejection}\n`);
287
+ _rejectedNames.add(skillName);
271
288
  return;
272
289
  }
273
290
  // ── C12: Task content validation ────────────────────────────
274
291
  const taskRejection = validateSkillTask(task);
275
292
  if (taskRejection) {
276
293
  process.stderr.write(`[SkillTeacher] Rejected "${skillName}": ${taskRejection}\n`);
277
- return;
278
- }
279
- // ── Session rate limit — max SESSION_SKILL_LIMIT new skills ─
280
- if (_sessionSkillsCreated >= SESSION_SKILL_LIMIT) {
281
- console.log(`[SkillTeacher] Session limit reached (${SESSION_SKILL_LIMIT}), skipping: "${skillName}"`);
294
+ _rejectedNames.add(skillName);
282
295
  return;
283
296
  }
284
297
  // ── Deduplication — reject names already in bundled skills/, approved/, or learned/ ─
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // AUTO-GENERATED by scripts/inject-version.js — do not edit by hand
5
- exports.VERSION = '3.19.5';
5
+ exports.VERSION = '3.19.7';
@@ -208,7 +208,7 @@ var init_updateCheck = __esm({
208
208
  var VERSION;
209
209
  var init_version = __esm({
210
210
  "core/version.ts"() {
211
- VERSION = "3.19.5";
211
+ VERSION = "3.19.7";
212
212
  }
213
213
  });
214
214
 
@@ -24908,6 +24908,8 @@ var init_skillLoader = __esm({
24908
24908
  import_path12.default.join(process.cwd(), "workspace", "skills"),
24909
24909
  import_path12.default.join(process.cwd(), "workspace", "skills", "learned"),
24910
24910
  import_path12.default.join(process.cwd(), "workspace", "skills", "approved"),
24911
+ // Workspace-level installed skills (written by skillRegistry.ts)
24912
+ import_path12.default.join(process.cwd(), "workspace", "skills", "installed"),
24911
24913
  // A2/A3 approved drafts
24912
24914
  import_path12.default.join(process.cwd(), "skills", "learned", "approved"),
24913
24915
  // A4 library-installed skills
@@ -288647,7 +288649,7 @@ When performing this type of task:
288647
288649
  3. Verify each step output before proceeding to the next
288648
288650
  `;
288649
288651
  }
288650
- var import_fs16, import_path17, LEARNED_DIR, APPROVED_DIR, BUNDLED_SKILLS_DIR, PROMOTE_THRESHOLD, SESSION_SKILL_LIMIT, _sessionSkillsCreated, QUESTION_WORD_RE, PRONOUN_RE, PERSONAL_ID_RE, SkillTeacher, skillTeacher;
288652
+ var import_fs16, import_path17, LEARNED_DIR, APPROVED_DIR, BUNDLED_SKILLS_DIR, PROMOTE_THRESHOLD, SESSION_SKILL_LIMIT, _sessionSkillsCreated, _rejectedNames, QUESTION_WORD_RE, PRONOUN_RE, PERSONAL_ID_RE, SkillTeacher, skillTeacher;
288651
288653
  var init_skillTeacher = __esm({
288652
288654
  "core/skillTeacher.ts"() {
288653
288655
  import_fs16 = __toESM(require("fs"));
@@ -288658,6 +288660,7 @@ var init_skillTeacher = __esm({
288658
288660
  PROMOTE_THRESHOLD = 3;
288659
288661
  SESSION_SKILL_LIMIT = 2;
288660
288662
  _sessionSkillsCreated = 0;
288663
+ _rejectedNames = /* @__PURE__ */ new Set();
288661
288664
  QUESTION_WORD_RE = /^(what|where|why|when|who|how|can|could|would|should|is|are)_/;
288662
288665
  PRONOUN_RE = /^(its|im|youre|whats|theyre|were)_/;
288663
288666
  PERSONAL_ID_RE = /(^|_)(users|shiva|admin|desktop|appdata)(_|$)/;
@@ -288678,6 +288681,10 @@ var init_skillTeacher = __esm({
288678
288681
  }
288679
288682
  return _SkillTeacher.instance;
288680
288683
  }
288684
+ /** C18: Allow call sites to skip recordSuccess entirely when session is full */
288685
+ static hasCapacity() {
288686
+ return _sessionSkillsCreated < SESSION_SKILL_LIMIT;
288687
+ }
288681
288688
  // ── Check if a matching skill already exists ──────────────
288682
288689
  hasMatchingSkill(task, tools) {
288683
288690
  const skillName = extractSkillName(task, tools);
@@ -288698,6 +288705,8 @@ var init_skillTeacher = __esm({
288698
288705
  async recordSuccess(task, tools, duration3, llmCaller, apiKey, model, provider) {
288699
288706
  if (tools.length === 0) return;
288700
288707
  const skillName = extractSkillName(task, tools);
288708
+ if (_rejectedNames.has(skillName)) return;
288709
+ if (_sessionSkillsCreated >= SESSION_SKILL_LIMIT) return;
288701
288710
  const metaPath2 = import_path17.default.join(LEARNED_DIR, skillName, "meta.json");
288702
288711
  const skillPath = import_path17.default.join(LEARNED_DIR, skillName, "SKILL.md");
288703
288712
  if (import_fs16.default.existsSync(metaPath2)) {
@@ -288722,6 +288731,7 @@ var init_skillTeacher = __esm({
288722
288731
  const isLowQuality = skillName.length < 5 || skillName.split("_").length < 2 || task.split(/\s+/).length < 3;
288723
288732
  if (isLowQuality) {
288724
288733
  console.log(`[SkillTeacher] Rejected low-quality skill: "${skillName}"`);
288734
+ _rejectedNames.add(skillName);
288725
288735
  return;
288726
288736
  }
288727
288737
  const DESTRUCTIVE_TASK_RE = /\b(delete|remove|rm\s|del\s|wipe|purge|erase|format|uninstall|drop\s+table|truncate)\b/i;
@@ -288731,22 +288741,21 @@ var init_skillTeacher = __esm({
288731
288741
  `[SkillTeacher] Rejected destructive skill: "${skillName}" (task="${task.slice(0, 60)}")
288732
288742
  `
288733
288743
  );
288744
+ _rejectedNames.add(skillName);
288734
288745
  return;
288735
288746
  }
288736
288747
  const nameRejection = validateSkillName(skillName);
288737
288748
  if (nameRejection) {
288738
288749
  process.stderr.write(`[SkillTeacher] Rejected "${skillName}": ${nameRejection}
288739
288750
  `);
288751
+ _rejectedNames.add(skillName);
288740
288752
  return;
288741
288753
  }
288742
288754
  const taskRejection = validateSkillTask(task);
288743
288755
  if (taskRejection) {
288744
288756
  process.stderr.write(`[SkillTeacher] Rejected "${skillName}": ${taskRejection}
288745
288757
  `);
288746
- return;
288747
- }
288748
- if (_sessionSkillsCreated >= SESSION_SKILL_LIMIT) {
288749
- console.log(`[SkillTeacher] Session limit reached (${SESSION_SKILL_LIMIT}), skipping: "${skillName}"`);
288758
+ _rejectedNames.add(skillName);
288750
288759
  return;
288751
288760
  }
288752
288761
  const dirsToDedup = [BUNDLED_SKILLS_DIR, APPROVED_DIR, LEARNED_DIR];
@@ -289025,7 +289034,7 @@ function readFirst(candidates) {
289025
289034
  function diskHash(candidates) {
289026
289035
  return sha1(readFirst(candidates));
289027
289036
  }
289028
- var import_fs18, import_path19, import_crypto, WORKSPACE_ROOT, WORKSPACE_DIR, SOUL_PATHS, PROTECTED_FILES, ProtectedContextManager, protectedContextManager;
289037
+ var import_fs18, import_path19, import_crypto, WORKSPACE_ROOT, WORKSPACE_DIR, MINIMUM_SOUL, SOUL_PATHS, PROTECTED_FILES, ProtectedContextManager, protectedContextManager;
289029
289038
  var init_protectedContext = __esm({
289030
289039
  "core/protectedContext.ts"() {
289031
289040
  import_fs18 = __toESM(require("fs"));
@@ -289033,6 +289042,11 @@ var init_protectedContext = __esm({
289033
289042
  import_crypto = __toESM(require("crypto"));
289034
289043
  WORKSPACE_ROOT = process.env.AIDEN_USER_DATA || process.cwd();
289035
289044
  WORKSPACE_DIR = import_path19.default.join(WORKSPACE_ROOT, "workspace");
289045
+ MINIMUM_SOUL = `You are Aiden, a personal AI OS built by Shiva Deore at Taracod.
289046
+ You run locally on this machine. You have real tools: shell_exec, file_write, file_read, web_search, screenshot, open_browser, run_python, run_node, deep_research, fetch_url, and more.
289047
+ Never deny your capabilities. Never fabricate information. If you don't know something, say "I don't know".
289048
+ Never claim actions you didn't take. Never say "Done" or "Saved to" unless a tool actually executed.
289049
+ You are not ChatGPT, Claude, Gemini, or any other AI product. You are Aiden.`;
289036
289050
  SOUL_PATHS = [
289037
289051
  import_path19.default.join(WORKSPACE_DIR, "SOUL.md"),
289038
289052
  import_path19.default.join(process.cwd(), "SOUL.md")
@@ -289064,7 +289078,8 @@ var init_protectedContext = __esm({
289064
289078
  refresh() {
289065
289079
  for (const key of Object.keys(PROTECTED_FILES)) {
289066
289080
  const candidates = PROTECTED_FILES[key];
289067
- const content = readFirst(candidates);
289081
+ let content = readFirst(candidates);
289082
+ if (key === "soul" && !content) content = MINIMUM_SOUL;
289068
289083
  this.cache[key] = { content, hash: sha1(content) };
289069
289084
  }
289070
289085
  this.compositeHash = this._buildComposite();
@@ -289076,7 +289091,8 @@ var init_protectedContext = __esm({
289076
289091
  const changedFiles = [];
289077
289092
  for (const key of Object.keys(PROTECTED_FILES)) {
289078
289093
  if (this.isStale(key)) {
289079
- const content = readFirst(PROTECTED_FILES[key]);
289094
+ let content = readFirst(PROTECTED_FILES[key]);
289095
+ if (key === "soul" && !content) content = MINIMUM_SOUL;
289080
289096
  this.cache[key] = { content, hash: sha1(content) };
289081
289097
  changedFiles.push(key);
289082
289098
  }
@@ -289229,11 +289245,11 @@ Good: "I have 48 built-in tools: web_search, file_write, run_python... [lists re
289229
289245
  - You're not the user's voice \u2014 be careful in
289230
289246
  group chats and external communications.
289231
289247
  `.trim();
289232
- AIDEN_RESPONDER_SYSTEM = (userName, date3) => {
289248
+ AIDEN_RESPONDER_SYSTEM = (userName, date3, hasToolResults = true) => {
289233
289249
  const soul = getLiveSoul();
289234
289250
  return `${soul ? soul + "\n\n" : ""}${AIDEN_IDENTITY}
289235
289251
 
289236
- You just executed real tools and have their actual output.
289252
+ ${hasToolResults ? `You just executed real tools and have their actual output.
289237
289253
  Current date: ${date3}
289238
289254
  User: ${userName}
289239
289255
 
@@ -289242,7 +289258,22 @@ REPORT RESULTS:
289242
289258
  - Be specific: include file paths, numbers, URLs, counts
289243
289259
  - If multiple steps ran: summarize the outcome, not each individual step
289244
289260
  - If a step failed: acknowledge it clearly and explain what worked
289245
- - For research tasks: analyze and synthesize \u2014 don't just re-paste the raw data`;
289261
+ - For research tasks: analyze and synthesize \u2014 don't just re-paste the raw data` : `Current date: ${date3}
289262
+ User: ${userName}
289263
+
289264
+ NO TOOLS WERE EXECUTED THIS TURN.
289265
+
289266
+ CRITICAL RULES (violating these breaks user trust):
289267
+ - DO NOT claim you created, wrote, or saved any file
289268
+ - DO NOT claim you opened any application or browser tab
289269
+ - DO NOT claim you ran any code, search, or API call
289270
+ - DO NOT report "Saved to Desktop/...", "Report written", "Done", "Created", "Executed", or similar completion language
289271
+ - DO NOT fabricate file paths, character counts, or word counts
289272
+ - DO NOT mention "the file" or "the report" as if it exists
289273
+ - DO NOT include code blocks or raw data and call it "the result"
289274
+ - IF the user asked for an action, acknowledge what they asked for but be clear no action has been taken yet ("I can do that \u2014 would you like me to..." or similar)
289275
+
289276
+ Respond conversationally based on the message and conversation context only.`}`;
289246
289277
  };
289247
289278
  }
289248
289279
  });
@@ -292776,7 +292807,7 @@ Would you like me to try a different approach?`,
292776
292807
  const executedTools = results.map((r) => r.tool);
292777
292808
  const totalDuration = results.reduce((s, r) => s + (r.duration || 0), 0);
292778
292809
  const anyFailed = results.some((r) => !r.success);
292779
- if (allSucceeded && executedTools.length > 0) {
292810
+ if (allSucceeded && executedTools.length > 0 && SkillTeacher.hasCapacity()) {
292780
292811
  growthEngine.logSuccess(plan.goal, executedTools);
292781
292812
  try {
292782
292813
  const next = getNextAvailableAPI();
@@ -292841,15 +292872,15 @@ function resolvePreviousOutput(input, stepOutputs, currentStep) {
292841
292872
  }
292842
292873
  return resolved;
292843
292874
  }
292844
- function responderSystem(userName, date3, sessionId) {
292875
+ function responderSystem(userName, date3, sessionId, hasToolResults = true) {
292845
292876
  const _ctx = protectedContextManager.getProtectedContext();
292846
292877
  const _prevHash = sessionId ? soulHashBySession.get(sessionId) : void 0;
292847
292878
  if (sessionId) soulHashBySession.set(sessionId, _ctx.hash);
292848
292879
  if (_prevHash !== void 0 && _ctx.hash === _prevHash) {
292849
292880
  const refBlock = buildProtectedContextBlock(_ctx, _prevHash, sessionId);
292850
- return refBlock ? refBlock + "\n\n" + AIDEN_RESPONDER_SYSTEM(userName, date3) : AIDEN_RESPONDER_SYSTEM(userName, date3);
292881
+ return refBlock ? refBlock + "\n\n" + AIDEN_RESPONDER_SYSTEM(userName, date3, hasToolResults) : AIDEN_RESPONDER_SYSTEM(userName, date3, hasToolResults);
292851
292882
  }
292852
- return AIDEN_RESPONDER_SYSTEM(userName, date3);
292883
+ return AIDEN_RESPONDER_SYSTEM(userName, date3, hasToolResults);
292853
292884
  }
292854
292885
  async function respondWithResults(originalMessage, plan, results, history2, userName, apiKey, model, providerName, onToken, sessionId, goals) {
292855
292886
  const gatedResult = results.find(
@@ -292916,7 +292947,8 @@ If the user asks what we worked on, what was researched, or references previous
292916
292947
 
292917
292948
  ` : "";
292918
292949
  const toolResultsContext = results.length ? results.map((r) => `[${r.tool} result]: ${r.success ? r.output.slice(0, 1e3) : "FAILED: " + r.error}`).join("\n") : "";
292919
- const systemWithResults = toolResultsContext ? `${capabilitiesSection}${entitySummary}${responderSystem(userName, date3, sessionId)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}
292950
+ const hasRealToolExecution = results.some((r) => r.tool !== "respond");
292951
+ const systemWithResults = toolResultsContext ? `${capabilitiesSection}${entitySummary}${responderSystem(userName, date3, sessionId, hasRealToolExecution)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}
292920
292952
 
292921
292953
  YOU JUST RAN THESE TOOLS AND GOT THESE RESULTS:
292922
292954
  ${toolResultsContext}
@@ -292934,7 +292966,7 @@ CRITICAL RULES FOR YOUR RESPONSE:
292934
292966
  - If a search tool returned no results, say no results were found \u2014 NEVER invent search results
292935
292967
  - If system_info returned hardware data, show the data
292936
292968
  - Be direct: show the actual output, then provide context if needed
292937
- - If a tool result starts with "FAILED:", tell the user it failed and why \u2014 NEVER fabricate a successful result` : `${capabilitiesSection}${entitySummary}${responderSystem(userName, date3, sessionId)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}`;
292969
+ - If a tool result starts with "FAILED:", tell the user it failed and why \u2014 NEVER fabricate a successful result` : `${capabilitiesSection}${entitySummary}${responderSystem(userName, date3, sessionId, false)}${responseSkillContext}${knowledgeResponderSection}${multiGoalInstruction}`;
292938
292970
  const userContent = executionSummary ? `User asked: "${originalMessage}"
292939
292971
 
292940
292972
  Real execution results: