@jungjaehoon/mama-os 0.9.4 → 0.9.5

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 (129) hide show
  1. package/README.md +18 -34
  2. package/dist/agent/agent-loop.d.ts +9 -3
  3. package/dist/agent/agent-loop.d.ts.map +1 -1
  4. package/dist/agent/agent-loop.js +218 -81
  5. package/dist/agent/agent-loop.js.map +1 -1
  6. package/dist/agent/claude-cli-wrapper.d.ts +2 -0
  7. package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
  8. package/dist/agent/claude-cli-wrapper.js.map +1 -1
  9. package/dist/agent/gateway-tool-executor.d.ts +8 -0
  10. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  11. package/dist/agent/gateway-tool-executor.js +173 -5
  12. package/dist/agent/gateway-tool-executor.js.map +1 -1
  13. package/dist/agent/gateway-tools.md +24 -0
  14. package/dist/agent/persistent-cli-adapter.d.ts.map +1 -1
  15. package/dist/agent/persistent-cli-adapter.js +1 -0
  16. package/dist/agent/persistent-cli-adapter.js.map +1 -1
  17. package/dist/agent/prompt-enhancer.d.ts +12 -3
  18. package/dist/agent/prompt-enhancer.d.ts.map +1 -1
  19. package/dist/agent/prompt-enhancer.js +239 -23
  20. package/dist/agent/prompt-enhancer.js.map +1 -1
  21. package/dist/agent/types.d.ts +6 -2
  22. package/dist/agent/types.d.ts.map +1 -1
  23. package/dist/agent/types.js.map +1 -1
  24. package/dist/api/graph-api-types.d.ts +13 -0
  25. package/dist/api/graph-api-types.d.ts.map +1 -1
  26. package/dist/api/graph-api.d.ts.map +1 -1
  27. package/dist/api/graph-api.js +26 -4
  28. package/dist/api/graph-api.js.map +1 -1
  29. package/dist/api/skills-handler.d.ts.map +1 -1
  30. package/dist/api/skills-handler.js +26 -0
  31. package/dist/api/skills-handler.js.map +1 -1
  32. package/dist/api/upload-handler.d.ts.map +1 -1
  33. package/dist/api/upload-handler.js +60 -25
  34. package/dist/api/upload-handler.js.map +1 -1
  35. package/dist/cli/commands/run.js +1 -1
  36. package/dist/cli/commands/run.js.map +1 -1
  37. package/dist/cli/commands/start.d.ts.map +1 -1
  38. package/dist/cli/commands/start.js +297 -34
  39. package/dist/cli/commands/start.js.map +1 -1
  40. package/dist/cli/commands/status.js +1 -1
  41. package/dist/cli/commands/status.js.map +1 -1
  42. package/dist/cli/commands/stop.d.ts.map +1 -1
  43. package/dist/cli/commands/stop.js +80 -0
  44. package/dist/cli/commands/stop.js.map +1 -1
  45. package/dist/cli/config/config-manager.d.ts +9 -0
  46. package/dist/cli/config/config-manager.d.ts.map +1 -1
  47. package/dist/cli/config/config-manager.js +64 -1
  48. package/dist/cli/config/config-manager.js.map +1 -1
  49. package/dist/cli/config/types.d.ts +3 -4
  50. package/dist/cli/config/types.d.ts.map +1 -1
  51. package/dist/cli/config/types.js.map +1 -1
  52. package/dist/gateways/attachment-utils.d.ts +28 -0
  53. package/dist/gateways/attachment-utils.d.ts.map +1 -0
  54. package/dist/gateways/attachment-utils.js +201 -0
  55. package/dist/gateways/attachment-utils.js.map +1 -0
  56. package/dist/gateways/discord.d.ts +0 -14
  57. package/dist/gateways/discord.d.ts.map +1 -1
  58. package/dist/gateways/discord.js +61 -172
  59. package/dist/gateways/discord.js.map +1 -1
  60. package/dist/gateways/image-analyzer.d.ts.map +1 -1
  61. package/dist/gateways/image-analyzer.js +11 -3
  62. package/dist/gateways/image-analyzer.js.map +1 -1
  63. package/dist/gateways/message-router.d.ts +3 -0
  64. package/dist/gateways/message-router.d.ts.map +1 -1
  65. package/dist/gateways/message-router.js +34 -9
  66. package/dist/gateways/message-router.js.map +1 -1
  67. package/dist/gateways/slack.d.ts +5 -0
  68. package/dist/gateways/slack.d.ts.map +1 -1
  69. package/dist/gateways/slack.js +129 -7
  70. package/dist/gateways/slack.js.map +1 -1
  71. package/dist/gateways/tool-status-tracker.d.ts +87 -0
  72. package/dist/gateways/tool-status-tracker.d.ts.map +1 -0
  73. package/dist/gateways/tool-status-tracker.js +283 -0
  74. package/dist/gateways/tool-status-tracker.js.map +1 -0
  75. package/dist/multi-agent/agent-process-manager.d.ts +3 -10
  76. package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
  77. package/dist/multi-agent/agent-process-manager.js +108 -103
  78. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  79. package/dist/multi-agent/delegation-manager.d.ts +27 -0
  80. package/dist/multi-agent/delegation-manager.d.ts.map +1 -1
  81. package/dist/multi-agent/delegation-manager.js +41 -0
  82. package/dist/multi-agent/delegation-manager.js.map +1 -1
  83. package/dist/multi-agent/multi-agent-base.d.ts +4 -0
  84. package/dist/multi-agent/multi-agent-base.d.ts.map +1 -1
  85. package/dist/multi-agent/multi-agent-base.js +10 -17
  86. package/dist/multi-agent/multi-agent-base.js.map +1 -1
  87. package/dist/multi-agent/multi-agent-discord.d.ts.map +1 -1
  88. package/dist/multi-agent/multi-agent-discord.js +42 -93
  89. package/dist/multi-agent/multi-agent-discord.js.map +1 -1
  90. package/dist/multi-agent/multi-agent-slack.d.ts +1 -0
  91. package/dist/multi-agent/multi-agent-slack.d.ts.map +1 -1
  92. package/dist/multi-agent/multi-agent-slack.js +52 -4
  93. package/dist/multi-agent/multi-agent-slack.js.map +1 -1
  94. package/dist/multi-agent/swarm/swarm-task-runner.d.ts.map +1 -1
  95. package/dist/multi-agent/swarm/swarm-task-runner.js +58 -37
  96. package/dist/multi-agent/swarm/swarm-task-runner.js.map +1 -1
  97. package/dist/multi-agent/types.d.ts +0 -6
  98. package/dist/multi-agent/types.d.ts.map +1 -1
  99. package/dist/multi-agent/types.js.map +1 -1
  100. package/dist/multi-agent/workflow-engine.d.ts.map +1 -1
  101. package/dist/multi-agent/workflow-engine.js +11 -10
  102. package/dist/multi-agent/workflow-engine.js.map +1 -1
  103. package/dist/skills/skill-registry.d.ts +7 -0
  104. package/dist/skills/skill-registry.d.ts.map +1 -1
  105. package/dist/skills/skill-registry.js +18 -0
  106. package/dist/skills/skill-registry.js.map +1 -1
  107. package/package.json +1 -1
  108. package/public/viewer/js/modules/chat.js +145 -76
  109. package/public/viewer/js/modules/playground.js +148 -0
  110. package/public/viewer/js/modules/skills.js +59 -4
  111. package/public/viewer/js/utils/api.js +6 -0
  112. package/public/viewer/js/utils/format.js +1 -1
  113. package/public/viewer/src/modules/chat.ts +223 -83
  114. package/public/viewer/src/modules/playground.ts +173 -0
  115. package/public/viewer/src/modules/skills.ts +61 -4
  116. package/public/viewer/src/utils/api.ts +12 -0
  117. package/public/viewer/src/utils/format.ts +1 -1
  118. package/public/viewer/viewer.html +170 -5
  119. package/templates/playgrounds/cron-workflow-lab.html +1601 -0
  120. package/templates/playgrounds/skill-lab-playground.html +1625 -0
  121. package/templates/playgrounds/wave-visualizer.html +694 -0
  122. package/templates/skills/frontend-design.md +71 -0
  123. package/templates/skills/multi-agent-collab.md +145 -0
  124. package/templates/skills/playground.md +197 -0
  125. package/templates/skills/scheduling.md +84 -0
  126. package/dist/multi-agent/agent-process-pool.d.ts +0 -148
  127. package/dist/multi-agent/agent-process-pool.d.ts.map +0 -1
  128. package/dist/multi-agent/agent-process-pool.js +0 -308
  129. package/dist/multi-agent/agent-process-pool.js.map +0 -1
package/README.md CHANGED
@@ -156,6 +156,9 @@ gateways:
156
156
  # In Slack
157
157
  @mama what's the status?
158
158
  @mama /report
159
+
160
+ # File upload support
161
+ @mama [attach image] translate this
159
162
  ```
160
163
 
161
164
  ### Telegram Bot
@@ -222,6 +225,20 @@ Built-in web interface for managing MAMA and chatting with your configured backe
222
225
  - Topic filtering and search
223
226
  - Export decisions (JSON, Markdown, CSV)
224
227
 
228
+ **🧩 Skills Tab**
229
+
230
+ - Browse installed skills with status badges (published/draft/coworking)
231
+ - Click to open in Skill Lab Playground for editing
232
+ - Skill verification with 12-point checklist
233
+
234
+ **🧪 Playground Tab**
235
+
236
+ - **Skill Lab** — Step-by-step skill creation, modification, and verification
237
+ - **Cron Workflow Lab** — Visual cron-scheduled workflow designer
238
+ - **Wave Visualizer** — Audio waveform playground for voice skill development
239
+ - Bidirectional sync with Skills Tab (select skill → opens in Skill Lab)
240
+ - "Open in new tab" for full-screen editing
241
+
225
242
  **⚙️ Settings Tab**
226
243
 
227
244
  - Configure gateway tokens
@@ -400,7 +417,6 @@ multi_agent:
400
417
  bot_token: 'DISCORD_BOT_TOKEN_2'
401
418
  tier: 1 # Full access for code changes
402
419
  auto_continue: true
403
- pool_size: 3 # Enable 3 parallel processes for this agent
404
420
  auto_respond_keywords: ['bug', 'code', 'implement', '구현']
405
421
 
406
422
  reviewer:
@@ -439,38 +455,6 @@ multi_agent:
439
455
  global_cooldown_ms: 2000
440
456
  ```
441
457
 
442
- ### Process Pool (Parallel Execution)
443
-
444
- Each agent runs as a separate backend CLI subprocess. By default, each agent has **1 process** (sequential execution). Configure `pool_size` to enable parallel task execution per agent.
445
-
446
- ```yaml
447
- multi_agent:
448
- agents:
449
- developer:
450
- pool_size: 5 # 5 parallel backend CLI processes for this agent
451
- ```
452
-
453
- **How it works:**
454
-
455
- - When a task arrives and all processes are busy, a new process is spawned (up to `pool_size`)
456
- - Idle processes are reused automatically (no cold start penalty)
457
- - Processes auto-terminate after 5 minutes of inactivity (`idleTimeoutMs`)
458
- - Hung processes (busy > 15 minutes) are auto-killed (`hungTimeoutMs`)
459
-
460
- **Pool status per agent:**
461
-
462
- | State | Description |
463
- | ------- | ----------------------------------- |
464
- | `total` | Total processes currently in pool |
465
- | `busy` | Processes handling active requests |
466
- | `idle` | Processes ready for immediate reuse |
467
-
468
- **Default:** `pool_size: 1` (sequential execution, safe default)
469
-
470
- **Recommendation:** Start with `pool_size: 3` for implementation agents (Developer) and keep `pool_size: 1` for advisory agents (Reviewer, Explorer).
471
-
472
- > **Note:** Each process spawns a separate backend CLI subprocess. Higher pool sizes increase memory and API usage proportionally.
473
-
474
458
  ### Delegation
475
459
 
476
460
  Tier 1 agents can delegate tasks to other agents:
@@ -910,4 +894,4 @@ The multi-agent swarm architecture was inspired by [oh-my-opencode](https://gith
910
894
  ---
911
895
 
912
896
  **Author:** SpineLift Team
913
- **Last Updated:** 2026-02-06
897
+ **Last Updated:** 2026-02-20
@@ -11,12 +11,17 @@
11
11
  */
12
12
  import type { OAuthManager } from '../auth/index.js';
13
13
  import type { ContentBlock, ToolDefinition, AgentLoopOptions, AgentLoopResult, ClaudeClientOptions, GatewayToolExecutorOptions, AgentContext } from './types.js';
14
+ /**
15
+ * Load full skill content on-demand for per-message injection.
16
+ * @param skillId - Skill identifier like "mama/playground"
17
+ */
18
+ export declare function loadSkillContent(skillId: string): string | null;
14
19
  /**
15
20
  * Load installed & enabled skills from ~/.mama/skills/
16
- * Returns skill content blocks for system prompt injection.
17
- * Reads all .md files recursively (commands/, skills/, etc.)
21
+ * Returns skill catalog lines for system prompt injection (on-demand mode).
22
+ * Full skill content is injected per-message via detectSkillMatch() in PromptEnhancer.
18
23
  */
19
- export declare function loadInstalledSkills(verbose?: boolean, options?: {
24
+ export declare function loadInstalledSkills(verbose?: boolean, _options?: {
20
25
  onlyCommands?: boolean;
21
26
  }): string[];
22
27
  export declare function loadComposedSystemPrompt(verbose?: boolean, context?: AgentContext): string;
@@ -46,6 +51,7 @@ export declare class AgentLoop {
46
51
  private readonly stopContinuationHandler;
47
52
  private readonly preCompactHandler;
48
53
  private preCompactInjected;
54
+ private currentStreamCallbacks?;
49
55
  constructor(_oauthManager: OAuthManager, options?: AgentLoopOptions, _clientOptions?: ClaudeClientOptions, executorOptions?: GatewayToolExecutorOptions);
50
56
  /**
51
57
  * Set session key for lane-based concurrency
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../src/agent/agent-loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../src/agent/agent-loop.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  })();
46
46
  Object.defineProperty(exports, "__esModule", { value: true });
47
47
  exports.AgentLoop = void 0;
48
+ exports.loadSkillContent = loadSkillContent;
48
49
  exports.loadInstalledSkills = loadInstalledSkills;
49
50
  exports.loadComposedSystemPrompt = loadComposedSystemPrompt;
50
51
  exports.getGatewayToolsPrompt = getGatewayToolsPrompt;
@@ -183,16 +184,57 @@ function collectMarkdownFiles(dir, prefix = '') {
183
184
  }
184
185
  return results;
185
186
  }
187
+ // ─── Skill On-Demand Injection ───────────────────────────────────────────────
186
188
  /**
187
- * Load installed & enabled skills from ~/.mama/skills/
188
- * Returns skill content blocks for system prompt injection.
189
- * Reads all .md files recursively (commands/, skills/, etc.)
189
+ * Parse YAML frontmatter from skill .md file
190
+ */
191
+ function parseSkillFrontmatter(content) {
192
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
193
+ if (!match)
194
+ return { name: '', description: '', keywords: [] };
195
+ const block = match[1];
196
+ const name = (block.match(/^name:\s*(.+)$/m)?.[1] ?? '').trim();
197
+ const description = (block.match(/^description:\s*(.+)$/m)?.[1] ?? '').trim();
198
+ const kwBlock = block.match(/^keywords:\n((?:[ \t]+-[ \t]*.+\n?)+)/m);
199
+ const keywords = kwBlock
200
+ ? kwBlock[1]
201
+ .trim()
202
+ .split('\n')
203
+ .map((l) => l.replace(/^[ \t]*-[ \t]*/, '').trim())
204
+ .filter((k) => k.length > 0)
205
+ : [];
206
+ return { name, description, keywords };
207
+ }
208
+ /**
209
+ * Find the main .md file for a directory skill (for frontmatter parsing)
210
+ */
211
+ function findMainSkillFile(skillDir, skillName) {
212
+ for (const name of [`${skillName}.md`, 'skill.md', 'index.md']) {
213
+ const p = (0, path_1.join)(skillDir, name);
214
+ if ((0, fs_1.existsSync)(p))
215
+ return p;
216
+ }
217
+ try {
218
+ const entries = (0, fs_1.readdirSync)(skillDir, { withFileTypes: true });
219
+ for (const e of entries) {
220
+ if (e.isFile() && e.name.endsWith('.md') && !EXCLUDED_SKILL_FILES.has(e.name)) {
221
+ return (0, path_1.join)(skillDir, e.name);
222
+ }
223
+ }
224
+ }
225
+ catch {
226
+ /* ignore */
227
+ }
228
+ return null;
229
+ }
230
+ /**
231
+ * Build skill catalog (one line per enabled skill) for system prompt.
232
+ * Format: "- [source/skillId] keywords: kw1, kw2 | description"
190
233
  */
191
- function loadInstalledSkills(verbose = false, options = {}) {
234
+ function buildSkillCatalog(verbose = false) {
192
235
  const skillsBase = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'skills');
193
236
  const stateFile = (0, path_1.join)(skillsBase, 'state.json');
194
- const blocks = [];
195
- // Load state (enabled/disabled tracking)
237
+ const catalog = [];
196
238
  let state = {};
197
239
  try {
198
240
  if ((0, fs_1.existsSync)(stateFile)) {
@@ -200,7 +242,7 @@ function loadInstalledSkills(verbose = false, options = {}) {
200
242
  }
201
243
  }
202
244
  catch {
203
- // No state file
245
+ /* no state file */
204
246
  }
205
247
  const sources = ['mama', 'cowork', 'external'];
206
248
  for (const source of sources) {
@@ -213,60 +255,100 @@ function loadInstalledSkills(verbose = false, options = {}) {
213
255
  if (!entry.isDirectory())
214
256
  continue;
215
257
  const stateKey = `${source}/${entry.name}`;
216
- // Skip disabled skills
217
258
  if (state[stateKey]?.enabled === false)
218
259
  continue;
219
260
  const skillDir = (0, path_1.join)(sourceDir, entry.name);
220
- let mdFiles = collectMarkdownFiles(skillDir);
221
- if (options.onlyCommands) {
222
- mdFiles = mdFiles.filter((f) => f.path.startsWith('commands/'));
223
- }
224
- if (mdFiles.length > 0) {
225
- const parts = mdFiles.map((f) => `## ${f.path}\n\n${f.content}`);
226
- blocks.push(`# [Skill: ${source}/${entry.name}]\n\n${parts.join('\n\n---\n\n')}`);
261
+ const mainFile = findMainSkillFile(skillDir, entry.name);
262
+ if (!mainFile)
263
+ continue;
264
+ try {
265
+ const content = (0, fs_1.readFileSync)(mainFile, 'utf-8');
266
+ const fm = parseSkillFrontmatter(content);
267
+ const description = fm.description || '';
268
+ const keywords = fm.keywords.length > 0 ? fm.keywords.join(', ') : entry.name;
269
+ catalog.push(`- [${stateKey}] keywords: ${keywords} | ${description}`);
227
270
  if (verbose)
228
- console.log(`[AgentLoop] Loaded skill: ${source}/${entry.name} (${mdFiles.length} files)`);
271
+ console.log(`[AgentLoop] Skill catalog: ${stateKey}`);
272
+ }
273
+ catch {
274
+ /* skip unreadable */
229
275
  }
230
276
  }
231
277
  }
232
278
  catch {
233
- // Directory read failed
279
+ /* directory read failed */
234
280
  }
235
281
  }
236
- // Also load flat .md skill files from ~/.mama/skills/ root
282
+ // Flat .md files at root
237
283
  try {
238
284
  const rootEntries = (0, fs_1.readdirSync)(skillsBase, { withFileTypes: true });
239
285
  for (const entry of rootEntries) {
240
- if (!entry.isFile() || !entry.name.endsWith('.md')) {
286
+ if (!entry.isFile() || !entry.name.endsWith('.md'))
241
287
  continue;
242
- }
243
- if (EXCLUDED_SKILL_FILES.has(entry.name)) {
288
+ if (EXCLUDED_SKILL_FILES.has(entry.name))
244
289
  continue;
245
- }
246
290
  const id = entry.name.replace(/\.md$/, '');
247
291
  const stateKey = `mama/${id}`;
248
- // Skip disabled skills (check state like subdirectory skills)
249
- if (state[stateKey]?.enabled === false) {
292
+ if (state[stateKey]?.enabled === false)
250
293
  continue;
251
- }
252
- // Skip if already loaded from subdirectory
253
- if (blocks.some((b) => b.includes(`[Skill: mama/${id}]`))) {
294
+ if (catalog.some((l) => l.includes(`[${stateKey}]`)))
254
295
  continue;
296
+ try {
297
+ const content = (0, fs_1.readFileSync)((0, path_1.join)(skillsBase, entry.name), 'utf-8');
298
+ const fm = parseSkillFrontmatter(content);
299
+ const description = fm.description || '';
300
+ const keywords = fm.keywords.length > 0 ? fm.keywords.join(', ') : id;
301
+ catalog.push(`- [${stateKey}] keywords: ${keywords} | ${description}`);
302
+ if (verbose)
303
+ console.log(`[AgentLoop] Skill catalog (flat): ${stateKey}`);
255
304
  }
256
- const fullPath = (0, path_1.join)(skillsBase, entry.name);
257
- let content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
258
- if (content.length > MAX_SKILL_FILE_CHARS) {
259
- content = content.slice(0, MAX_SKILL_FILE_CHARS) + '\n\n[... truncated]';
305
+ catch {
306
+ /* skip */
260
307
  }
261
- blocks.push(`# [Skill: mama/${id}]\n\n${content}`);
262
- if (verbose)
263
- console.log(`[AgentLoop] Loaded root skill: ${id}`);
264
308
  }
265
309
  }
266
310
  catch {
267
- // Root directory read failed
311
+ /* root directory read failed */
312
+ }
313
+ return catalog;
314
+ }
315
+ /**
316
+ * Load full skill content on-demand for per-message injection.
317
+ * @param skillId - Skill identifier like "mama/playground"
318
+ */
319
+ function loadSkillContent(skillId) {
320
+ const skillsBase = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'skills');
321
+ // Try directory skill first
322
+ const skillDir = (0, path_1.join)(skillsBase, skillId);
323
+ if ((0, fs_1.existsSync)(skillDir)) {
324
+ const mdFiles = collectMarkdownFiles(skillDir);
325
+ if (mdFiles.length > 0) {
326
+ const parts = mdFiles.map((f) => `## ${f.path}\n\n${f.content}`);
327
+ return `# [Skill: ${skillId}]\n\n${parts.join('\n\n---\n\n')}`;
328
+ }
329
+ }
330
+ // Try flat .md file: "mama/playground" → skills/playground.md
331
+ const idParts = skillId.split('/');
332
+ if (idParts.length >= 2) {
333
+ const flatPath = (0, path_1.join)(skillsBase, `${idParts[idParts.length - 1]}.md`);
334
+ if ((0, fs_1.existsSync)(flatPath)) {
335
+ try {
336
+ return (0, fs_1.readFileSync)(flatPath, 'utf-8');
337
+ }
338
+ catch {
339
+ /* skip */
340
+ }
341
+ }
268
342
  }
269
- return blocks;
343
+ return null;
344
+ }
345
+ /**
346
+ * Load installed & enabled skills from ~/.mama/skills/
347
+ * Returns skill catalog lines for system prompt injection (on-demand mode).
348
+ * Full skill content is injected per-message via detectSkillMatch() in PromptEnhancer.
349
+ */
350
+ function loadInstalledSkills(verbose = false, _options = {}) {
351
+ return buildSkillCatalog(verbose);
270
352
  }
271
353
  function loadComposedSystemPrompt(verbose = false, context) {
272
354
  const mamaHome = (0, path_1.join)((0, os_1.homedir)(), '.mama');
@@ -298,26 +380,20 @@ function loadComposedSystemPrompt(verbose = false, context) {
298
380
  console.log(`[AgentLoop] Persona file not found (skipping): ${file}`);
299
381
  }
300
382
  }
301
- // Load installed & enabled skills (HIGH PRIORITYbefore CLAUDE.md)
302
- const skillBlocks = loadInstalledSkills(verbose);
303
- if (skillBlocks.length > 0) {
383
+ // Load skill catalog (on-demand modefull content injected per-message by PromptEnhancer)
384
+ const skillCatalog = loadInstalledSkills(verbose);
385
+ if (skillCatalog.length > 0) {
304
386
  const skillDirective = [
305
- '# Installed Skills (PRIORITY)',
387
+ '# Installed Skills',
306
388
  '',
307
- '**IMPORTANT:** The following skills/plugins are installed by the user.',
308
- 'When a user request matches a skill by keywords or description, you MUST:',
309
- '1. Find the matching skill section below (check "keywords" in frontmatter or skill name)',
310
- '2. Follow its "지시사항" / instructions EXACTLY as written — do NOT improvise alternatives',
311
- '3. Use the tools available to you (fetch, Bash, etc.) as the skill directs',
312
- '4. DO NOT create separate scripts or files unless the skill explicitly instructs it',
313
- '5. For [INSTALLED PLUGIN COMMAND] messages, find matching "commands/{name}.md"',
314
- '6. DO NOT use the Skill tool — these are NOT system skills',
389
+ 'To invoke a skill, include its keywords in your message.',
390
+ 'The full skill instructions will be injected automatically when matched.',
315
391
  '',
316
- skillBlocks.join('\n\n---\n\n'),
392
+ ...skillCatalog,
317
393
  ].join('\n');
318
394
  layers.push(skillDirective);
319
395
  if (verbose)
320
- console.log(`[AgentLoop] Injected ${skillBlocks.length} installed skills`);
396
+ console.log(`[AgentLoop] Skill catalog: ${skillCatalog.length} skills`);
321
397
  }
322
398
  // Add context prompt if AgentContext is provided (role awareness)
323
399
  if (context) {
@@ -395,6 +471,7 @@ class AgentLoop {
395
471
  stopContinuationHandler;
396
472
  preCompactHandler;
397
473
  preCompactInjected = false;
474
+ currentStreamCallbacks;
398
475
  constructor(_oauthManager, options = {}, _clientOptions, executorOptions) {
399
476
  // Initialize tools config (hybrid Gateway/MCP routing)
400
477
  this.toolsConfig = {
@@ -423,38 +500,88 @@ class AgentLoop {
423
500
  else {
424
501
  logger.debug('⚙️ Gateway-only mode');
425
502
  }
426
- // Build system prompt
427
- const basePrompt = options.systemPrompt || loadComposedSystemPrompt();
428
- // Only include Gateway Tools prompt if using Gateway mode
429
- const gatewayToolsPrompt = useGatewayMode ? getGatewayToolsPrompt() : '';
430
- let defaultSystemPrompt = gatewayToolsPrompt
431
- ? `${basePrompt}\n\n---\n\n${gatewayToolsPrompt}`
432
- : basePrompt;
433
- // Monitor and enforce prompt size limits
503
+ // Build system prompt with layered truncation support
434
504
  const monitor = new prompt_size_monitor_js_1.PromptSizeMonitor();
435
- const promptLayers = [
436
- { name: 'base', content: basePrompt, priority: 1 },
437
- ...(gatewayToolsPrompt
438
- ? [{ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 }]
439
- : []),
440
- ];
505
+ let promptLayers;
506
+ if (options.systemPrompt) {
507
+ // Custom system prompt (e.g., multi-agent): treat as a single critical layer
508
+ promptLayers = [{ name: 'custom', content: options.systemPrompt, priority: 1 }];
509
+ }
510
+ else {
511
+ // Composed prompt: build layers with individual priorities for graceful truncation
512
+ // Priority 1 (never cut): CLAUDE.md base instructions
513
+ // Priority 2 (cut if extreme): personas (SOUL, IDENTITY, USER) + gateway tools
514
+ // Priority 3 (cut first): context prompt + skills + onboarding
515
+ const mamaHome = (0, path_1.join)((0, os_1.homedir)(), '.mama');
516
+ const claudeMd = loadSystemPrompt();
517
+ const personaFiles = ['SOUL.md', 'IDENTITY.md', 'USER.md'];
518
+ const personaParts = [];
519
+ for (const file of personaFiles) {
520
+ const p = (0, path_1.join)(mamaHome, file);
521
+ if ((0, fs_1.existsSync)(p))
522
+ personaParts.push((0, fs_1.readFileSync)(p, 'utf-8'));
523
+ }
524
+ const skillCatalog = loadInstalledSkills();
525
+ const onboardingPath = (0, path_1.join)(mamaHome, 'ONBOARDING.md');
526
+ const onboardingContent = (0, fs_1.existsSync)(onboardingPath)
527
+ ? (0, fs_1.readFileSync)(onboardingPath, 'utf-8')
528
+ : '';
529
+ promptLayers = [
530
+ { name: 'claudeMd', content: claudeMd, priority: 1 },
531
+ ...(personaParts.length > 0
532
+ ? [
533
+ {
534
+ name: 'personas',
535
+ content: personaParts.join('\n\n---\n\n'),
536
+ priority: 2,
537
+ },
538
+ ]
539
+ : []),
540
+ ...(skillCatalog.length > 0
541
+ ? [
542
+ {
543
+ name: 'skills',
544
+ content: [
545
+ '# Installed Skills',
546
+ '',
547
+ 'To invoke a skill, include its keywords in your message.',
548
+ '',
549
+ ...skillCatalog,
550
+ ].join('\n'),
551
+ priority: 3,
552
+ },
553
+ ]
554
+ : []),
555
+ ...(onboardingContent
556
+ ? [{ name: 'onboarding', content: onboardingContent, priority: 4 }]
557
+ : []),
558
+ ];
559
+ }
560
+ if (useGatewayMode) {
561
+ const gatewayToolsPrompt = getGatewayToolsPrompt();
562
+ if (gatewayToolsPrompt) {
563
+ promptLayers.push({ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 });
564
+ }
565
+ }
441
566
  const checkResult = monitor.check(promptLayers);
442
567
  if (checkResult.warning) {
443
568
  logger.warn(checkResult.warning);
444
569
  }
445
- // Actually enforce truncation if over budget
570
+ // Enforce truncation if over budget (priority > 1 layers trimmed first)
446
571
  if (!checkResult.withinBudget) {
447
572
  const { layers: trimmedLayers, result: enforceResult } = monitor.enforce(promptLayers);
448
573
  if (enforceResult.truncatedLayers.length > 0) {
449
574
  logger.warn(`Truncated layers: ${enforceResult.truncatedLayers.join(', ')}`);
450
575
  }
451
- const trimmedBase = trimmedLayers.find((l) => l.name === 'base')?.content || basePrompt;
452
- const trimmedTools = trimmedLayers.find((l) => l.name === 'gatewayTools')?.content || '';
453
- defaultSystemPrompt = trimmedTools ? `${trimmedBase}\n\n---\n\n${trimmedTools}` : trimmedBase;
454
- logger.debug(`System prompt truncated: ${checkResult.totalChars} → ${defaultSystemPrompt.length} chars`);
576
+ promptLayers = trimmedLayers;
577
+ logger.debug(`System prompt truncated: ${checkResult.totalChars} ${enforceResult.totalChars} chars`);
455
578
  }
579
+ const defaultSystemPrompt = promptLayers
580
+ .filter((l) => l.content.length > 0)
581
+ .map((l) => l.content)
582
+ .join('\n\n---\n\n');
456
583
  // Choose backend (default: claude)
457
- this.backend = options.backend ?? 'claude';
584
+ this.backend = options.backend;
458
585
  if (this.backend === 'codex-mcp') {
459
586
  // Codex MCP mode: standard MCP protocol
460
587
  const workspaceDir = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'workspace');
@@ -480,12 +607,14 @@ class AgentLoop {
480
607
  systemPrompt: defaultSystemPrompt,
481
608
  // Hybrid mode: pass MCP config even with Gateway tools enabled
482
609
  mcpConfigPath: useMCPMode ? mcpConfigPath : undefined,
483
- // Headless daemon requires skipping permission prompts (no TTY available).
484
- // Security is enforced by MAMA's RoleManager, not Claude CLI's interactive prompts.
485
- // MAMA_TRUSTED_ENV must be set to enable this flag (defense in depth)
486
- dangerouslySkipPermissions: process.env.MAMA_TRUSTED_ENV === 'true' && (options.dangerouslySkipPermissions ?? false),
610
+ // MAMA OS is a headless daemon (no TTY) — Claude CLI's interactive permission prompts
611
+ // cannot work. Security is enforced by MAMA's own RoleManager layer (config.yaml roles).
612
+ // DO NOT gate this on env vars MAMA manages permissions via its config, not Claude CLI.
613
+ dangerouslySkipPermissions: options.dangerouslySkipPermissions ?? false,
487
614
  // Gateway tools are processed by GatewayToolExecutor (hybrid with MCP)
488
615
  useGatewayTools: useGatewayMode,
616
+ // Pass configured timeout (default in PersistentCLI: 120s — too short for complex tasks)
617
+ requestTimeout: options.timeoutMs,
489
618
  });
490
619
  this.agent = this.persistentCLI;
491
620
  logger.debug('🚀 Claude PersistentCLI mode enabled - faster responses');
@@ -623,6 +752,7 @@ class AgentLoop {
623
752
  * Internal implementation of runWithContent (without lane queueing)
624
753
  */
625
754
  async runWithContentInternal(content, options) {
755
+ this.currentStreamCallbacks = options?.streamCallbacks;
626
756
  const history = [];
627
757
  const totalUsage = { input_tokens: 0, output_tokens: 0 };
628
758
  let turn = 0;
@@ -710,19 +840,19 @@ class AgentLoop {
710
840
  throw new types_js_1.AgentError(`Emergency stop: Agent loop exceeded emergency maximum turns (${EMERGENCY_MAX_TURNS})`, 'EMERGENCY_MAX_TURNS', undefined, false);
711
841
  }
712
842
  let response;
843
+ const ext = this.currentStreamCallbacks;
713
844
  const callbacks = {
714
845
  onDelta: (text) => {
715
- console.log('[Streaming] Delta received:', text.length, 'chars');
846
+ ext?.onDelta?.(text);
716
847
  },
717
- onToolUse: (name, _input) => {
718
- console.log(`[Streaming] Tool called: ${name}`);
848
+ onToolUse: (name, input) => {
849
+ ext?.onToolUse?.(name, input);
719
850
  },
720
- onFinal: (_finalResponse) => {
721
- console.log('[Streaming] Stream complete');
851
+ onFinal: (finalResponse) => {
852
+ ext?.onFinal?.(finalResponse);
722
853
  },
723
854
  onError: (error) => {
724
- console.error('[Streaming] Error:', error);
725
- // Don't throw - let the promise rejection handle it
855
+ ext?.onError?.(error);
726
856
  },
727
857
  };
728
858
  let piResult;
@@ -970,6 +1100,7 @@ class AgentLoop {
970
1100
  if (ownedSession) {
971
1101
  this.sessionPool.releaseSession(channelKey);
972
1102
  }
1103
+ this.currentStreamCallbacks = undefined;
973
1104
  }
974
1105
  }
975
1106
  /**
@@ -981,6 +1112,8 @@ class AgentLoop {
981
1112
  for (const toolUse of toolUseBlocks) {
982
1113
  let result;
983
1114
  let isError = false;
1115
+ // Notify stream: tool execution starting
1116
+ this.currentStreamCallbacks?.onToolUse?.(toolUse.name, toolUse.input);
984
1117
  try {
985
1118
  // PreToolUse: search MAMA for contracts before Write operations
986
1119
  let contractContext = '';
@@ -996,12 +1129,16 @@ class AgentLoop {
996
1129
  this.onToolUse?.(toolUse.name, toolUse.input, toolResult);
997
1130
  // PostToolUse: auto-extract contracts (fire-and-forget)
998
1131
  this.postToolHandler?.processInBackground(toolUse.name, toolUse.input, toolResult);
1132
+ // Notify stream: tool completed successfully
1133
+ this.currentStreamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, false);
999
1134
  }
1000
1135
  catch (error) {
1001
1136
  isError = true;
1002
1137
  result = error instanceof Error ? error.message : String(error);
1003
1138
  // Notify tool use callback with error
1004
1139
  this.onToolUse?.(toolUse.name, toolUse.input, { error: result });
1140
+ // Notify stream: tool completed with error
1141
+ this.currentStreamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, true);
1005
1142
  }
1006
1143
  results.push({
1007
1144
  type: 'tool_result',