@jungjaehoon/mama-os 0.9.3 → 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.
- package/README.md +18 -34
- package/dist/agent/agent-loop.d.ts +9 -3
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +220 -84
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/claude-cli-wrapper.d.ts +6 -4
- package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
- package/dist/agent/claude-cli-wrapper.js +17 -5
- package/dist/agent/claude-cli-wrapper.js.map +1 -1
- package/dist/agent/claude-client.js +3 -3
- package/dist/agent/claude-client.js.map +1 -1
- package/dist/agent/codex-mcp-process.d.ts +10 -0
- package/dist/agent/codex-mcp-process.d.ts.map +1 -1
- package/dist/agent/codex-mcp-process.js +226 -58
- package/dist/agent/codex-mcp-process.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +23 -1
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +210 -8
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +25 -0
- package/dist/agent/persistent-cli-adapter.d.ts.map +1 -1
- package/dist/agent/persistent-cli-adapter.js +1 -0
- package/dist/agent/persistent-cli-adapter.js.map +1 -1
- package/dist/agent/persistent-cli-process.d.ts +2 -0
- package/dist/agent/persistent-cli-process.d.ts.map +1 -1
- package/dist/agent/persistent-cli-process.js +15 -0
- package/dist/agent/persistent-cli-process.js.map +1 -1
- package/dist/agent/prompt-enhancer.d.ts +12 -3
- package/dist/agent/prompt-enhancer.d.ts.map +1 -1
- package/dist/agent/prompt-enhancer.js +239 -23
- package/dist/agent/prompt-enhancer.js.map +1 -1
- package/dist/agent/types.d.ts +8 -4
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/graph-api-types.d.ts +13 -0
- package/dist/api/graph-api-types.d.ts.map +1 -1
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +56 -8
- package/dist/api/graph-api.js.map +1 -1
- package/dist/api/skills-handler.d.ts.map +1 -1
- package/dist/api/skills-handler.js +26 -0
- package/dist/api/skills-handler.js.map +1 -1
- package/dist/api/upload-handler.d.ts.map +1 -1
- package/dist/api/upload-handler.js +60 -25
- package/dist/api/upload-handler.js.map +1 -1
- package/dist/cli/commands/run.js +1 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +384 -36
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +7 -1
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +129 -0
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts +9 -0
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +64 -1
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts +6 -5
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +3 -3
- package/dist/cli/config/types.js.map +1 -1
- package/dist/gateways/attachment-utils.d.ts +28 -0
- package/dist/gateways/attachment-utils.d.ts.map +1 -0
- package/dist/gateways/attachment-utils.js +201 -0
- package/dist/gateways/attachment-utils.js.map +1 -0
- package/dist/gateways/discord.d.ts +0 -14
- package/dist/gateways/discord.d.ts.map +1 -1
- package/dist/gateways/discord.js +61 -172
- package/dist/gateways/discord.js.map +1 -1
- package/dist/gateways/image-analyzer.d.ts.map +1 -1
- package/dist/gateways/image-analyzer.js +12 -4
- package/dist/gateways/image-analyzer.js.map +1 -1
- package/dist/gateways/message-router.d.ts +3 -0
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +34 -9
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/gateways/slack.d.ts +5 -0
- package/dist/gateways/slack.d.ts.map +1 -1
- package/dist/gateways/slack.js +136 -15
- package/dist/gateways/slack.js.map +1 -1
- package/dist/gateways/tool-status-tracker.d.ts +87 -0
- package/dist/gateways/tool-status-tracker.d.ts.map +1 -0
- package/dist/gateways/tool-status-tracker.js +283 -0
- package/dist/gateways/tool-status-tracker.js.map +1 -0
- package/dist/multi-agent/agent-process-manager.d.ts +15 -10
- package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
- package/dist/multi-agent/agent-process-manager.js +205 -110
- package/dist/multi-agent/agent-process-manager.js.map +1 -1
- package/dist/multi-agent/bmad-templates.d.ts +67 -0
- package/dist/multi-agent/bmad-templates.d.ts.map +1 -0
- package/dist/multi-agent/bmad-templates.js +248 -0
- package/dist/multi-agent/bmad-templates.js.map +1 -0
- package/dist/multi-agent/delegation-manager.d.ts +27 -0
- package/dist/multi-agent/delegation-manager.d.ts.map +1 -1
- package/dist/multi-agent/delegation-manager.js +41 -0
- package/dist/multi-agent/delegation-manager.js.map +1 -1
- package/dist/multi-agent/multi-agent-base.d.ts +5 -0
- package/dist/multi-agent/multi-agent-base.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-base.js +17 -20
- package/dist/multi-agent/multi-agent-base.js.map +1 -1
- package/dist/multi-agent/multi-agent-discord.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-discord.js +66 -95
- package/dist/multi-agent/multi-agent-discord.js.map +1 -1
- package/dist/multi-agent/multi-agent-slack.d.ts +3 -0
- package/dist/multi-agent/multi-agent-slack.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-slack.js +128 -21
- package/dist/multi-agent/multi-agent-slack.js.map +1 -1
- package/dist/multi-agent/runtime-process.d.ts +3 -0
- package/dist/multi-agent/runtime-process.d.ts.map +1 -1
- package/dist/multi-agent/runtime-process.js +4 -0
- package/dist/multi-agent/runtime-process.js.map +1 -1
- package/dist/multi-agent/swarm/swarm-task-runner.d.ts.map +1 -1
- package/dist/multi-agent/swarm/swarm-task-runner.js +58 -37
- package/dist/multi-agent/swarm/swarm-task-runner.js.map +1 -1
- package/dist/multi-agent/types.d.ts +20 -6
- package/dist/multi-agent/types.d.ts.map +1 -1
- package/dist/multi-agent/types.js.map +1 -1
- package/dist/multi-agent/workflow-engine.d.ts +7 -0
- package/dist/multi-agent/workflow-engine.d.ts.map +1 -1
- package/dist/multi-agent/workflow-engine.js +212 -31
- package/dist/multi-agent/workflow-engine.js.map +1 -1
- package/dist/multi-agent/workflow-types.d.ts +5 -1
- package/dist/multi-agent/workflow-types.d.ts.map +1 -1
- package/dist/onboarding/phase-7-integrations.js +1 -1
- package/dist/onboarding/phase-7-integrations.js.map +1 -1
- package/dist/setup/setup-prompt.d.ts +1 -1
- package/dist/setup/setup-prompt.d.ts.map +1 -1
- package/dist/setup/setup-prompt.js +1 -1
- package/dist/skills/skill-registry.d.ts +7 -0
- package/dist/skills/skill-registry.d.ts.map +1 -1
- package/dist/skills/skill-registry.js +18 -0
- package/dist/skills/skill-registry.js.map +1 -1
- package/package.json +1 -1
- package/public/viewer/js/modules/chat.js +145 -76
- package/public/viewer/js/modules/playground.js +148 -0
- package/public/viewer/js/modules/settings.js +110 -15
- package/public/viewer/js/modules/skills.js +59 -4
- package/public/viewer/js/utils/api.js +6 -0
- package/public/viewer/js/utils/format.js +11 -8
- package/public/viewer/src/modules/chat.ts +223 -83
- package/public/viewer/src/modules/playground.ts +173 -0
- package/public/viewer/src/modules/settings.ts +133 -16
- package/public/viewer/src/modules/skills.ts +61 -4
- package/public/viewer/src/utils/api.ts +14 -1
- package/public/viewer/src/utils/format.ts +11 -8
- package/public/viewer/viewer.html +171 -5
- package/templates/bmad/LICENSE +28 -0
- package/templates/bmad/architecture.md +343 -0
- package/templates/bmad/bmm-workflow-status.template.yaml +66 -0
- package/templates/bmad/prd.md +198 -0
- package/templates/bmad/product-brief.md +149 -0
- package/templates/bmad/sprint-status.template.yaml +35 -0
- package/templates/bmad/tech-spec.md +151 -0
- package/templates/personas/conductor.md +86 -15
- package/templates/playgrounds/cron-workflow-lab.html +1601 -0
- package/templates/playgrounds/skill-lab-playground.html +1625 -0
- package/templates/playgrounds/wave-visualizer.html +694 -0
- package/templates/skills/frontend-design.md +71 -0
- package/templates/skills/multi-agent-collab.md +145 -0
- package/templates/skills/playground.md +197 -0
- package/templates/skills/scheduling.md +84 -0
- package/dist/multi-agent/agent-process-pool.d.ts +0 -148
- package/dist/multi-agent/agent-process-pool.d.ts.map +0 -1
- package/dist/multi-agent/agent-process-pool.js +0 -308
- 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-
|
|
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
|
|
17
|
-
*
|
|
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,
|
|
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":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,OAAO,KAAK,EAEV,YAAY,EAIZ,cAAc,EACd,gBAAgB,EAChB,eAAe,EAIf,mBAAmB,EACnB,0BAA0B,EAE1B,YAAY,EACb,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../src/agent/agent-loop.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/agent-loop.js
CHANGED
|
@@ -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
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
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)
|
|
190
210
|
*/
|
|
191
|
-
function
|
|
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"
|
|
233
|
+
*/
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
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]
|
|
271
|
+
console.log(`[AgentLoop] Skill catalog: ${stateKey}`);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
/* skip unreadable */
|
|
229
275
|
}
|
|
230
276
|
}
|
|
231
277
|
}
|
|
232
278
|
catch {
|
|
233
|
-
|
|
279
|
+
/* directory read failed */
|
|
234
280
|
}
|
|
235
281
|
}
|
|
236
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
257
|
-
|
|
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
|
-
|
|
311
|
+
/* root directory read failed */
|
|
268
312
|
}
|
|
269
|
-
return
|
|
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
|
+
}
|
|
342
|
+
}
|
|
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
|
|
302
|
-
const
|
|
303
|
-
if (
|
|
383
|
+
// Load skill catalog (on-demand mode — full content injected per-message by PromptEnhancer)
|
|
384
|
+
const skillCatalog = loadInstalledSkills(verbose);
|
|
385
|
+
if (skillCatalog.length > 0) {
|
|
304
386
|
const skillDirective = [
|
|
305
|
-
'# Installed Skills
|
|
387
|
+
'# Installed Skills',
|
|
306
388
|
'',
|
|
307
|
-
'
|
|
308
|
-
'
|
|
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
|
-
|
|
392
|
+
...skillCatalog,
|
|
317
393
|
].join('\n');
|
|
318
394
|
layers.push(skillDirective);
|
|
319
395
|
if (verbose)
|
|
320
|
-
console.log(`[AgentLoop]
|
|
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
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
452
|
-
|
|
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
|
|
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');
|
|
@@ -475,17 +602,19 @@ class AgentLoop {
|
|
|
475
602
|
else {
|
|
476
603
|
// Claude backend: always use PersistentCLI for fast responses (~2-3s vs ~16-30s)
|
|
477
604
|
this.persistentCLI = new persistent_cli_adapter_js_1.PersistentCLIAdapter({
|
|
478
|
-
model: options.model
|
|
605
|
+
model: options.model,
|
|
479
606
|
sessionId,
|
|
480
607
|
systemPrompt: defaultSystemPrompt,
|
|
481
608
|
// Hybrid mode: pass MCP config even with Gateway tools enabled
|
|
482
609
|
mcpConfigPath: useMCPMode ? mcpConfigPath : undefined,
|
|
483
|
-
//
|
|
484
|
-
// Security is enforced by MAMA's RoleManager
|
|
485
|
-
//
|
|
486
|
-
dangerouslySkipPermissions:
|
|
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');
|
|
@@ -497,8 +626,7 @@ class AgentLoop {
|
|
|
497
626
|
this.mcpExecutor = new gateway_tool_executor_js_1.GatewayToolExecutor(executorOptions);
|
|
498
627
|
this.systemPromptOverride = options.systemPrompt;
|
|
499
628
|
this.maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
500
|
-
|
|
501
|
-
this.model = options.model ?? 'claude-sonnet-4-20250514';
|
|
629
|
+
this.model = options.model;
|
|
502
630
|
this.onTurn = options.onTurn;
|
|
503
631
|
this.onToolUse = options.onToolUse;
|
|
504
632
|
this.onTokenUsage = options.onTokenUsage;
|
|
@@ -624,6 +752,7 @@ class AgentLoop {
|
|
|
624
752
|
* Internal implementation of runWithContent (without lane queueing)
|
|
625
753
|
*/
|
|
626
754
|
async runWithContentInternal(content, options) {
|
|
755
|
+
this.currentStreamCallbacks = options?.streamCallbacks;
|
|
627
756
|
const history = [];
|
|
628
757
|
const totalUsage = { input_tokens: 0, output_tokens: 0 };
|
|
629
758
|
let turn = 0;
|
|
@@ -711,19 +840,19 @@ class AgentLoop {
|
|
|
711
840
|
throw new types_js_1.AgentError(`Emergency stop: Agent loop exceeded emergency maximum turns (${EMERGENCY_MAX_TURNS})`, 'EMERGENCY_MAX_TURNS', undefined, false);
|
|
712
841
|
}
|
|
713
842
|
let response;
|
|
843
|
+
const ext = this.currentStreamCallbacks;
|
|
714
844
|
const callbacks = {
|
|
715
845
|
onDelta: (text) => {
|
|
716
|
-
|
|
846
|
+
ext?.onDelta?.(text);
|
|
717
847
|
},
|
|
718
|
-
onToolUse: (name,
|
|
719
|
-
|
|
848
|
+
onToolUse: (name, input) => {
|
|
849
|
+
ext?.onToolUse?.(name, input);
|
|
720
850
|
},
|
|
721
|
-
onFinal: (
|
|
722
|
-
|
|
851
|
+
onFinal: (finalResponse) => {
|
|
852
|
+
ext?.onFinal?.(finalResponse);
|
|
723
853
|
},
|
|
724
854
|
onError: (error) => {
|
|
725
|
-
|
|
726
|
-
// Don't throw - let the promise rejection handle it
|
|
855
|
+
ext?.onError?.(error);
|
|
727
856
|
},
|
|
728
857
|
};
|
|
729
858
|
let piResult;
|
|
@@ -971,6 +1100,7 @@ class AgentLoop {
|
|
|
971
1100
|
if (ownedSession) {
|
|
972
1101
|
this.sessionPool.releaseSession(channelKey);
|
|
973
1102
|
}
|
|
1103
|
+
this.currentStreamCallbacks = undefined;
|
|
974
1104
|
}
|
|
975
1105
|
}
|
|
976
1106
|
/**
|
|
@@ -982,6 +1112,8 @@ class AgentLoop {
|
|
|
982
1112
|
for (const toolUse of toolUseBlocks) {
|
|
983
1113
|
let result;
|
|
984
1114
|
let isError = false;
|
|
1115
|
+
// Notify stream: tool execution starting
|
|
1116
|
+
this.currentStreamCallbacks?.onToolUse?.(toolUse.name, toolUse.input);
|
|
985
1117
|
try {
|
|
986
1118
|
// PreToolUse: search MAMA for contracts before Write operations
|
|
987
1119
|
let contractContext = '';
|
|
@@ -997,12 +1129,16 @@ class AgentLoop {
|
|
|
997
1129
|
this.onToolUse?.(toolUse.name, toolUse.input, toolResult);
|
|
998
1130
|
// PostToolUse: auto-extract contracts (fire-and-forget)
|
|
999
1131
|
this.postToolHandler?.processInBackground(toolUse.name, toolUse.input, toolResult);
|
|
1132
|
+
// Notify stream: tool completed successfully
|
|
1133
|
+
this.currentStreamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, false);
|
|
1000
1134
|
}
|
|
1001
1135
|
catch (error) {
|
|
1002
1136
|
isError = true;
|
|
1003
1137
|
result = error instanceof Error ? error.message : String(error);
|
|
1004
1138
|
// Notify tool use callback with error
|
|
1005
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);
|
|
1006
1142
|
}
|
|
1007
1143
|
results.push({
|
|
1008
1144
|
type: 'tool_result',
|