@jungjaehoon/mama-os 0.10.0 → 0.10.2
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 +59 -41
- package/dist/agent/agent-loop.d.ts +25 -4
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +466 -136
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/claude-cli-wrapper.d.ts +13 -4
- package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
- package/dist/agent/claude-cli-wrapper.js +17 -9
- 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/code-act/constants.d.ts +8 -0
- package/dist/agent/code-act/constants.d.ts.map +1 -0
- package/dist/agent/code-act/constants.js +65 -0
- package/dist/agent/code-act/constants.js.map +1 -0
- package/dist/agent/code-act/host-bridge.d.ts +35 -0
- package/dist/agent/code-act/host-bridge.d.ts.map +1 -0
- package/dist/agent/code-act/host-bridge.js +381 -0
- package/dist/agent/code-act/host-bridge.js.map +1 -0
- package/dist/agent/code-act/index.d.ts +8 -0
- package/dist/agent/code-act/index.d.ts.map +1 -0
- package/dist/agent/code-act/index.js +16 -0
- package/dist/agent/code-act/index.js.map +1 -0
- package/dist/agent/code-act/sandbox.d.ts +20 -0
- package/dist/agent/code-act/sandbox.d.ts.map +1 -0
- package/dist/agent/code-act/sandbox.js +226 -0
- package/dist/agent/code-act/sandbox.js.map +1 -0
- package/dist/agent/code-act/type-definition-generator.d.ts +12 -0
- package/dist/agent/code-act/type-definition-generator.d.ts.map +1 -0
- package/dist/agent/code-act/type-definition-generator.js +45 -0
- package/dist/agent/code-act/type-definition-generator.js.map +1 -0
- package/dist/agent/code-act/types.d.ts +37 -0
- package/dist/agent/code-act/types.d.ts.map +1 -0
- package/dist/agent/code-act/types.js +10 -0
- package/dist/agent/code-act/types.js.map +1 -0
- package/dist/agent/codex-mcp-process.d.ts +20 -0
- package/dist/agent/codex-mcp-process.d.ts.map +1 -1
- package/dist/agent/codex-mcp-process.js +324 -59
- package/dist/agent/codex-mcp-process.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +24 -17
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +259 -130
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +35 -0
- package/dist/agent/mama-tool-handlers.d.ts +11 -0
- package/dist/agent/mama-tool-handlers.d.ts.map +1 -0
- package/dist/agent/mama-tool-handlers.js +125 -0
- package/dist/agent/mama-tool-handlers.js.map +1 -0
- package/dist/agent/mcp-executor.d.ts +0 -16
- package/dist/agent/mcp-executor.d.ts.map +1 -1
- package/dist/agent/mcp-executor.js +5 -114
- package/dist/agent/mcp-executor.js.map +1 -1
- package/dist/agent/persistent-cli-adapter.d.ts.map +1 -1
- package/dist/agent/persistent-cli-adapter.js +7 -1
- package/dist/agent/persistent-cli-adapter.js.map +1 -1
- package/dist/agent/persistent-cli-process.d.ts +7 -0
- package/dist/agent/persistent-cli-process.d.ts.map +1 -1
- package/dist/agent/persistent-cli-process.js +23 -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/session-pool.d.ts +1 -1
- package/dist/agent/session-pool.d.ts.map +1 -1
- package/dist/agent/session-pool.js +8 -2
- package/dist/agent/session-pool.js.map +1 -1
- package/dist/agent/types.d.ts +33 -5
- 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 +25 -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 +96 -9
- 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/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +16 -0
- package/dist/cli/commands/init.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 +747 -53
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +27 -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 +162 -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 +124 -16
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts +24 -9
- 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/context-injector.d.ts.map +1 -1
- package/dist/gateways/context-injector.js +8 -27
- package/dist/gateways/context-injector.js.map +1 -1
- 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 +4 -0
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +77 -58
- 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 -25
- 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/gateways/types.d.ts +2 -0
- package/dist/gateways/types.d.ts.map +1 -1
- package/dist/mcp/code-act-server.d.ts +14 -0
- package/dist/mcp/code-act-server.d.ts.map +1 -0
- package/dist/mcp/code-act-server.js +206 -0
- package/dist/mcp/code-act-server.js.map +1 -0
- package/dist/multi-agent/agent-process-manager.d.ts +19 -11
- package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
- package/dist/multi-agent/agent-process-manager.js +241 -137
- package/dist/multi-agent/agent-process-manager.js.map +1 -1
- package/dist/multi-agent/background-task-manager.d.ts +2 -2
- package/dist/multi-agent/background-task-manager.js +2 -2
- 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/council-engine.d.ts +60 -0
- package/dist/multi-agent/council-engine.d.ts.map +1 -0
- package/dist/multi-agent/council-engine.js +284 -0
- package/dist/multi-agent/council-engine.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 +23 -10
- package/dist/multi-agent/multi-agent-base.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-base.js +125 -49
- package/dist/multi-agent/multi-agent-base.js.map +1 -1
- package/dist/multi-agent/multi-agent-discord.d.ts +3 -35
- package/dist/multi-agent/multi-agent-discord.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-discord.js +123 -395
- package/dist/multi-agent/multi-agent-discord.js.map +1 -1
- package/dist/multi-agent/multi-agent-slack.d.ts +3 -25
- package/dist/multi-agent/multi-agent-slack.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-slack.js +223 -255
- 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 +7 -0
- package/dist/multi-agent/runtime-process.js.map +1 -1
- package/dist/multi-agent/shared-context.d.ts.map +1 -1
- package/dist/multi-agent/shared-context.js +4 -4
- package/dist/multi-agent/shared-context.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/system-reminder.d.ts +1 -1
- package/dist/multi-agent/system-reminder.js +1 -1
- package/dist/multi-agent/types.d.ts +38 -21
- package/dist/multi-agent/types.d.ts.map +1 -1
- package/dist/multi-agent/types.js +1 -3
- package/dist/multi-agent/types.js.map +1 -1
- package/dist/multi-agent/ultrawork-state.d.ts +57 -0
- package/dist/multi-agent/ultrawork-state.d.ts.map +1 -0
- package/dist/multi-agent/ultrawork-state.js +191 -0
- package/dist/multi-agent/ultrawork-state.js.map +1 -0
- package/dist/multi-agent/ultrawork.d.ts +39 -21
- package/dist/multi-agent/ultrawork.d.ts.map +1 -1
- package/dist/multi-agent/ultrawork.js +591 -45
- package/dist/multi-agent/ultrawork.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 +240 -34
- package/dist/multi-agent/workflow-engine.js.map +1 -1
- package/dist/multi-agent/workflow-types.d.ts +74 -1
- package/dist/multi-agent/workflow-types.d.ts.map +1 -1
- package/dist/onboarding/complete-autonomous-prompt.d.ts +1 -1
- package/dist/onboarding/complete-autonomous-prompt.d.ts.map +1 -1
- package/dist/onboarding/complete-autonomous-prompt.js +27 -10
- package/dist/onboarding/complete-autonomous-prompt.js.map +1 -1
- package/dist/onboarding/phase-7-integrations.d.ts.map +1 -1
- package/dist/onboarding/phase-7-integrations.js +23 -3
- package/dist/onboarding/phase-7-integrations.js.map +1 -1
- package/dist/onboarding/phase-9-finalization.d.ts.map +1 -1
- package/dist/onboarding/phase-9-finalization.js +33 -0
- package/dist/onboarding/phase-9-finalization.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 +6 -3
- 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/AGENTS.claude.md +23 -0
- package/templates/AGENTS.codex.md +32 -0
- 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/architect.md +70 -0
- package/templates/personas/conductor.md +373 -0
- package/templates/personas/developer.md +20 -7
- package/templates/personas/pm.md +49 -33
- package/templates/personas/reviewer.md +18 -5
- package/templates/playgrounds/cron-workflow-lab.html +1601 -0
- package/templates/playgrounds/mama-log-viewer.html +1341 -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/dist/multi-agent/pr-review-poller.d.ts +0 -197
- package/dist/multi-agent/pr-review-poller.d.ts.map +0 -1
- package/dist/multi-agent/pr-review-poller.js +0 -972
- package/dist/multi-agent/pr-review-poller.js.map +0 -1
- package/templates/personas/sisyphus-builtin-en.md +0 -161
- package/templates/personas/sisyphus.md +0 -218
package/dist/agent/agent-loop.js
CHANGED
|
@@ -45,7 +45,9 @@ 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;
|
|
50
|
+
exports.loadBackendAgentsMd = loadBackendAgentsMd;
|
|
49
51
|
exports.loadComposedSystemPrompt = loadComposedSystemPrompt;
|
|
50
52
|
exports.getGatewayToolsPrompt = getGatewayToolsPrompt;
|
|
51
53
|
const fs_1 = require("fs");
|
|
@@ -53,7 +55,8 @@ const prompt_size_monitor_js_1 = require("./prompt-size-monitor.js");
|
|
|
53
55
|
const codex_mcp_process_js_1 = require("./codex-mcp-process.js");
|
|
54
56
|
const persistent_cli_adapter_js_1 = require("./persistent-cli-adapter.js");
|
|
55
57
|
const gateway_tool_executor_js_1 = require("./gateway-tool-executor.js");
|
|
56
|
-
const index_js_1 = require("
|
|
58
|
+
const index_js_1 = require("./code-act/index.js");
|
|
59
|
+
const index_js_2 = require("../concurrency/index.js");
|
|
57
60
|
const session_pool_js_1 = require("./session-pool.js");
|
|
58
61
|
const os_1 = require("os");
|
|
59
62
|
const path_1 = require("path");
|
|
@@ -183,16 +186,57 @@ function collectMarkdownFiles(dir, prefix = '') {
|
|
|
183
186
|
}
|
|
184
187
|
return results;
|
|
185
188
|
}
|
|
189
|
+
// ─── Skill On-Demand Injection ───────────────────────────────────────────────
|
|
186
190
|
/**
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
* Parse YAML frontmatter from skill .md file
|
|
192
|
+
*/
|
|
193
|
+
function parseSkillFrontmatter(content) {
|
|
194
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
195
|
+
if (!match)
|
|
196
|
+
return { name: '', description: '', keywords: [] };
|
|
197
|
+
const block = match[1];
|
|
198
|
+
const name = (block.match(/^name:\s*(.+)$/m)?.[1] ?? '').trim();
|
|
199
|
+
const description = (block.match(/^description:\s*(.+)$/m)?.[1] ?? '').trim();
|
|
200
|
+
const kwBlock = block.match(/^keywords:\n((?:[ \t]+-[ \t]*.+\n?)+)/m);
|
|
201
|
+
const keywords = kwBlock
|
|
202
|
+
? kwBlock[1]
|
|
203
|
+
.trim()
|
|
204
|
+
.split('\n')
|
|
205
|
+
.map((l) => l.replace(/^[ \t]*-[ \t]*/, '').trim())
|
|
206
|
+
.filter((k) => k.length > 0)
|
|
207
|
+
: [];
|
|
208
|
+
return { name, description, keywords };
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Find the main .md file for a directory skill (for frontmatter parsing)
|
|
212
|
+
*/
|
|
213
|
+
function findMainSkillFile(skillDir, skillName) {
|
|
214
|
+
for (const name of [`${skillName}.md`, 'skill.md', 'index.md']) {
|
|
215
|
+
const p = (0, path_1.join)(skillDir, name);
|
|
216
|
+
if ((0, fs_1.existsSync)(p))
|
|
217
|
+
return p;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const entries = (0, fs_1.readdirSync)(skillDir, { withFileTypes: true });
|
|
221
|
+
for (const e of entries) {
|
|
222
|
+
if (e.isFile() && e.name.endsWith('.md') && !EXCLUDED_SKILL_FILES.has(e.name)) {
|
|
223
|
+
return (0, path_1.join)(skillDir, e.name);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
/* ignore */
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Build skill catalog (one line per enabled skill) for system prompt.
|
|
234
|
+
* Format: "- [source/skillId] keywords: kw1, kw2 | description"
|
|
190
235
|
*/
|
|
191
|
-
function
|
|
236
|
+
function buildSkillCatalog(verbose = false) {
|
|
192
237
|
const skillsBase = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'skills');
|
|
193
238
|
const stateFile = (0, path_1.join)(skillsBase, 'state.json');
|
|
194
|
-
const
|
|
195
|
-
// Load state (enabled/disabled tracking)
|
|
239
|
+
const catalog = [];
|
|
196
240
|
let state = {};
|
|
197
241
|
try {
|
|
198
242
|
if ((0, fs_1.existsSync)(stateFile)) {
|
|
@@ -200,7 +244,7 @@ function loadInstalledSkills(verbose = false, options = {}) {
|
|
|
200
244
|
}
|
|
201
245
|
}
|
|
202
246
|
catch {
|
|
203
|
-
|
|
247
|
+
/* no state file */
|
|
204
248
|
}
|
|
205
249
|
const sources = ['mama', 'cowork', 'external'];
|
|
206
250
|
for (const source of sources) {
|
|
@@ -213,76 +257,132 @@ function loadInstalledSkills(verbose = false, options = {}) {
|
|
|
213
257
|
if (!entry.isDirectory())
|
|
214
258
|
continue;
|
|
215
259
|
const stateKey = `${source}/${entry.name}`;
|
|
216
|
-
// Skip disabled skills
|
|
217
260
|
if (state[stateKey]?.enabled === false)
|
|
218
261
|
continue;
|
|
219
262
|
const skillDir = (0, path_1.join)(sourceDir, entry.name);
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
263
|
+
const mainFile = findMainSkillFile(skillDir, entry.name);
|
|
264
|
+
if (!mainFile)
|
|
265
|
+
continue;
|
|
266
|
+
try {
|
|
267
|
+
const content = (0, fs_1.readFileSync)(mainFile, 'utf-8');
|
|
268
|
+
const fm = parseSkillFrontmatter(content);
|
|
269
|
+
const description = fm.description || '';
|
|
270
|
+
const keywords = fm.keywords.length > 0 ? fm.keywords.join(', ') : entry.name;
|
|
271
|
+
catalog.push(`- [${stateKey}] keywords: ${keywords} | ${description}`);
|
|
227
272
|
if (verbose)
|
|
228
|
-
console.log(`[AgentLoop]
|
|
273
|
+
console.log(`[AgentLoop] Skill catalog: ${stateKey}`);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
/* skip unreadable */
|
|
229
277
|
}
|
|
230
278
|
}
|
|
231
279
|
}
|
|
232
280
|
catch {
|
|
233
|
-
|
|
281
|
+
/* directory read failed */
|
|
234
282
|
}
|
|
235
283
|
}
|
|
236
|
-
//
|
|
284
|
+
// Flat .md files at root
|
|
237
285
|
try {
|
|
238
286
|
const rootEntries = (0, fs_1.readdirSync)(skillsBase, { withFileTypes: true });
|
|
239
287
|
for (const entry of rootEntries) {
|
|
240
|
-
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
288
|
+
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
241
289
|
continue;
|
|
242
|
-
|
|
243
|
-
if (EXCLUDED_SKILL_FILES.has(entry.name)) {
|
|
290
|
+
if (EXCLUDED_SKILL_FILES.has(entry.name))
|
|
244
291
|
continue;
|
|
245
|
-
}
|
|
246
292
|
const id = entry.name.replace(/\.md$/, '');
|
|
247
293
|
const stateKey = `mama/${id}`;
|
|
248
|
-
|
|
249
|
-
if (state[stateKey]?.enabled === false) {
|
|
294
|
+
if (state[stateKey]?.enabled === false)
|
|
250
295
|
continue;
|
|
251
|
-
}
|
|
252
|
-
// Skip if already loaded from subdirectory
|
|
253
|
-
if (blocks.some((b) => b.includes(`[Skill: mama/${id}]`))) {
|
|
296
|
+
if (catalog.some((l) => l.includes(`[${stateKey}]`)))
|
|
254
297
|
continue;
|
|
298
|
+
try {
|
|
299
|
+
const content = (0, fs_1.readFileSync)((0, path_1.join)(skillsBase, entry.name), 'utf-8');
|
|
300
|
+
const fm = parseSkillFrontmatter(content);
|
|
301
|
+
const description = fm.description || '';
|
|
302
|
+
const keywords = fm.keywords.length > 0 ? fm.keywords.join(', ') : id;
|
|
303
|
+
catalog.push(`- [${stateKey}] keywords: ${keywords} | ${description}`);
|
|
304
|
+
if (verbose)
|
|
305
|
+
console.log(`[AgentLoop] Skill catalog (flat): ${stateKey}`);
|
|
255
306
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (content.length > MAX_SKILL_FILE_CHARS) {
|
|
259
|
-
content = content.slice(0, MAX_SKILL_FILE_CHARS) + '\n\n[... truncated]';
|
|
307
|
+
catch {
|
|
308
|
+
/* skip */
|
|
260
309
|
}
|
|
261
|
-
blocks.push(`# [Skill: mama/${id}]\n\n${content}`);
|
|
262
|
-
if (verbose)
|
|
263
|
-
console.log(`[AgentLoop] Loaded root skill: ${id}`);
|
|
264
310
|
}
|
|
265
311
|
}
|
|
266
312
|
catch {
|
|
267
|
-
|
|
313
|
+
/* root directory read failed */
|
|
268
314
|
}
|
|
269
|
-
return
|
|
315
|
+
return catalog;
|
|
270
316
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
317
|
+
/**
|
|
318
|
+
* Load full skill content on-demand for per-message injection.
|
|
319
|
+
* @param skillId - Skill identifier like "mama/playground"
|
|
320
|
+
*/
|
|
321
|
+
function loadSkillContent(skillId) {
|
|
322
|
+
const skillsBase = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'skills');
|
|
323
|
+
// Try directory skill first
|
|
324
|
+
const skillDir = (0, path_1.join)(skillsBase, skillId);
|
|
325
|
+
if ((0, fs_1.existsSync)(skillDir)) {
|
|
326
|
+
const mdFiles = collectMarkdownFiles(skillDir);
|
|
327
|
+
if (mdFiles.length > 0) {
|
|
328
|
+
const parts = mdFiles.map((f) => `## ${f.path}\n\n${f.content}`);
|
|
329
|
+
return `# [Skill: ${skillId}]\n\n${parts.join('\n\n---\n\n')}`;
|
|
280
330
|
}
|
|
281
331
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
332
|
+
// Try flat .md file: "mama/playground" → skills/playground.md
|
|
333
|
+
const idParts = skillId.split('/');
|
|
334
|
+
if (idParts.length >= 2) {
|
|
335
|
+
const flatPath = (0, path_1.join)(skillsBase, `${idParts[idParts.length - 1]}.md`);
|
|
336
|
+
if ((0, fs_1.existsSync)(flatPath)) {
|
|
337
|
+
try {
|
|
338
|
+
return (0, fs_1.readFileSync)(flatPath, 'utf-8');
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
/* skip */
|
|
342
|
+
}
|
|
343
|
+
}
|
|
285
344
|
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Load installed & enabled skills from ~/.mama/skills/
|
|
349
|
+
* Returns skill catalog lines for system prompt injection (on-demand mode).
|
|
350
|
+
* Full skill content is injected per-message via detectSkillMatch() in PromptEnhancer.
|
|
351
|
+
*/
|
|
352
|
+
function loadInstalledSkills(verbose = false, _options = {}) {
|
|
353
|
+
return buildSkillCatalog(verbose);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Load backend-specific AGENTS.md from ~/.mama/
|
|
357
|
+
* Maps backend to file: 'claude' → AGENTS.claude.md, 'codex-mcp' → AGENTS.codex.md
|
|
358
|
+
*/
|
|
359
|
+
function loadBackendAgentsMd(backend, verbose = false) {
|
|
360
|
+
if (!backend) {
|
|
361
|
+
return '';
|
|
362
|
+
}
|
|
363
|
+
const keyMap = {
|
|
364
|
+
claude: 'claude',
|
|
365
|
+
'codex-mcp': 'codex',
|
|
366
|
+
};
|
|
367
|
+
const key = keyMap[backend];
|
|
368
|
+
if (!key) {
|
|
369
|
+
return '';
|
|
370
|
+
}
|
|
371
|
+
const filePath = (0, path_1.join)((0, os_1.homedir)(), '.mama', `AGENTS.${key}.md`);
|
|
372
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
373
|
+
if (verbose) {
|
|
374
|
+
console.log(`[AgentLoop] Loaded backend AGENTS.md: AGENTS.${key}.md`);
|
|
375
|
+
}
|
|
376
|
+
return (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
377
|
+
}
|
|
378
|
+
if (verbose) {
|
|
379
|
+
console.log(`[AgentLoop] Backend AGENTS.md not found: AGENTS.${key}.md`);
|
|
380
|
+
}
|
|
381
|
+
return '';
|
|
382
|
+
}
|
|
383
|
+
function loadComposedSystemPrompt(verbose = false, context) {
|
|
384
|
+
const mamaHome = (0, path_1.join)((0, os_1.homedir)(), '.mama');
|
|
385
|
+
const layers = [];
|
|
286
386
|
// Load persona files: SOUL.md, IDENTITY.md, USER.md
|
|
287
387
|
const personaFiles = ['SOUL.md', 'IDENTITY.md', 'USER.md'];
|
|
288
388
|
for (const file of personaFiles) {
|
|
@@ -298,55 +398,54 @@ function loadComposedSystemPrompt(verbose = false, context) {
|
|
|
298
398
|
console.log(`[AgentLoop] Persona file not found (skipping): ${file}`);
|
|
299
399
|
}
|
|
300
400
|
}
|
|
301
|
-
// Load
|
|
302
|
-
const
|
|
303
|
-
if (
|
|
401
|
+
// Load skill catalog (on-demand mode — full content injected per-message by PromptEnhancer)
|
|
402
|
+
const skillCatalog = loadInstalledSkills(verbose);
|
|
403
|
+
if (skillCatalog.length > 0) {
|
|
304
404
|
const skillDirective = [
|
|
305
|
-
'# Installed Skills
|
|
405
|
+
'# Installed Skills',
|
|
306
406
|
'',
|
|
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',
|
|
407
|
+
'To invoke a skill, include its keywords in your message.',
|
|
408
|
+
'The full skill instructions will be injected automatically when matched.',
|
|
315
409
|
'',
|
|
316
|
-
|
|
410
|
+
...skillCatalog,
|
|
317
411
|
].join('\n');
|
|
318
412
|
layers.push(skillDirective);
|
|
319
413
|
if (verbose)
|
|
320
|
-
console.log(`[AgentLoop]
|
|
414
|
+
console.log(`[AgentLoop] Skill catalog: ${skillCatalog.length} skills`);
|
|
321
415
|
}
|
|
322
|
-
// Add context
|
|
416
|
+
// Add minimal context if AgentContext is provided (role awareness)
|
|
323
417
|
if (context) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
418
|
+
layers.push((0, context_prompt_builder_js_1.buildMinimalContext)(context));
|
|
419
|
+
}
|
|
420
|
+
// Load backend-specific AGENTS.md (e.g., AGENTS.claude.md, AGENTS.codex.md)
|
|
421
|
+
const backendAgentsMd = loadBackendAgentsMd(context?.backend, verbose);
|
|
422
|
+
if (backendAgentsMd) {
|
|
423
|
+
layers.push(backendAgentsMd);
|
|
328
424
|
}
|
|
329
425
|
// Load CLAUDE.md (base instructions)
|
|
330
426
|
const claudeMd = loadSystemPrompt(verbose);
|
|
331
427
|
layers.push(claudeMd);
|
|
332
|
-
// Load ONBOARDING.md only
|
|
333
|
-
|
|
334
|
-
if (
|
|
428
|
+
// Load ONBOARDING.md only during initial setup (before SOUL.md is created)
|
|
429
|
+
const soulPath = (0, path_1.join)(mamaHome, 'SOUL.md');
|
|
430
|
+
if (!(0, fs_1.existsSync)(soulPath)) {
|
|
335
431
|
const onboardingPath = (0, path_1.join)(mamaHome, 'ONBOARDING.md');
|
|
336
432
|
if ((0, fs_1.existsSync)(onboardingPath)) {
|
|
337
433
|
const onboardingContent = (0, fs_1.readFileSync)(onboardingPath, 'utf-8');
|
|
338
434
|
layers.push(onboardingContent);
|
|
339
435
|
if (verbose) {
|
|
340
|
-
logger.debug('Loaded ONBOARDING.md (setup
|
|
436
|
+
logger.debug('Loaded ONBOARDING.md (initial setup)');
|
|
341
437
|
}
|
|
342
438
|
}
|
|
343
439
|
}
|
|
344
440
|
else {
|
|
345
441
|
if (verbose) {
|
|
346
|
-
logger.debug('Skipped ONBOARDING.md (
|
|
442
|
+
logger.debug('Skipped ONBOARDING.md (SOUL.md exists, setup complete)');
|
|
347
443
|
}
|
|
348
444
|
}
|
|
349
|
-
|
|
445
|
+
const result = layers.join('\n\n---\n\n');
|
|
446
|
+
// Debug: log each layer's size to find what's consuming context
|
|
447
|
+
logger.debug(`[SystemPrompt] Total: ${result.length} chars, layers: ${layers.map((l, i) => `L${i}=${l.length}`).join(', ')}`);
|
|
448
|
+
return result;
|
|
350
449
|
}
|
|
351
450
|
/**
|
|
352
451
|
* Load Gateway Tools prompt from MD file
|
|
@@ -390,11 +489,14 @@ class AgentLoop {
|
|
|
390
489
|
sessionPool;
|
|
391
490
|
toolsConfig;
|
|
392
491
|
isGatewayMode;
|
|
492
|
+
useCodeAct;
|
|
393
493
|
backend;
|
|
394
494
|
postToolHandler;
|
|
395
495
|
stopContinuationHandler;
|
|
396
496
|
preCompactHandler;
|
|
397
497
|
preCompactInjected = false;
|
|
498
|
+
currentStreamCallbacks;
|
|
499
|
+
currentTier = 1;
|
|
398
500
|
constructor(_oauthManager, options = {}, _clientOptions, executorOptions) {
|
|
399
501
|
// Initialize tools config (hybrid Gateway/MCP routing)
|
|
400
502
|
this.toolsConfig = {
|
|
@@ -414,6 +516,7 @@ class AgentLoop {
|
|
|
414
516
|
const useGatewayMode = gatewayTools.includes('*') || gatewayTools.length > 0;
|
|
415
517
|
const useMCPMode = mcpTools.includes('*') || mcpTools.length > 0;
|
|
416
518
|
this.isGatewayMode = useGatewayMode;
|
|
519
|
+
this.useCodeAct = options.useCodeAct ?? false;
|
|
417
520
|
if (useGatewayMode && useMCPMode) {
|
|
418
521
|
logger.debug('🔀 Hybrid mode: Gateway + MCP tools enabled');
|
|
419
522
|
}
|
|
@@ -423,38 +526,111 @@ class AgentLoop {
|
|
|
423
526
|
else {
|
|
424
527
|
logger.debug('⚙️ Gateway-only mode');
|
|
425
528
|
}
|
|
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
|
|
529
|
+
// Build system prompt with layered truncation support
|
|
434
530
|
const monitor = new prompt_size_monitor_js_1.PromptSizeMonitor();
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
531
|
+
let promptLayers;
|
|
532
|
+
if (options.systemPrompt) {
|
|
533
|
+
// Custom system prompt (e.g., multi-agent): treat as a single critical layer
|
|
534
|
+
promptLayers = [{ name: 'custom', content: options.systemPrompt, priority: 1 }];
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
// Composed prompt: build layers with individual priorities for graceful truncation
|
|
538
|
+
// Priority 1 (never cut): CLAUDE.md base instructions
|
|
539
|
+
// Priority 2 (cut if extreme): personas (SOUL, IDENTITY, USER) + gateway tools
|
|
540
|
+
// Priority 3 (cut first): context prompt + skills + onboarding
|
|
541
|
+
const mamaHome = (0, path_1.join)((0, os_1.homedir)(), '.mama');
|
|
542
|
+
const claudeMd = loadSystemPrompt();
|
|
543
|
+
const personaFiles = ['SOUL.md', 'IDENTITY.md', 'USER.md'];
|
|
544
|
+
const personaParts = [];
|
|
545
|
+
for (const file of personaFiles) {
|
|
546
|
+
const p = (0, path_1.join)(mamaHome, file);
|
|
547
|
+
if ((0, fs_1.existsSync)(p))
|
|
548
|
+
personaParts.push((0, fs_1.readFileSync)(p, 'utf-8'));
|
|
549
|
+
}
|
|
550
|
+
const skillCatalog = loadInstalledSkills();
|
|
551
|
+
// Only load ONBOARDING.md during initial setup (before SOUL.md exists)
|
|
552
|
+
const onboardingContent = !(0, fs_1.existsSync)((0, path_1.join)(mamaHome, 'SOUL.md'))
|
|
553
|
+
? (() => {
|
|
554
|
+
const op = (0, path_1.join)(mamaHome, 'ONBOARDING.md');
|
|
555
|
+
return (0, fs_1.existsSync)(op) ? (0, fs_1.readFileSync)(op, 'utf-8') : '';
|
|
556
|
+
})()
|
|
557
|
+
: '';
|
|
558
|
+
promptLayers = [
|
|
559
|
+
{ name: 'claudeMd', content: claudeMd, priority: 1 },
|
|
560
|
+
...(personaParts.length > 0
|
|
561
|
+
? [
|
|
562
|
+
{
|
|
563
|
+
name: 'personas',
|
|
564
|
+
content: personaParts.join('\n\n---\n\n'),
|
|
565
|
+
priority: 2,
|
|
566
|
+
},
|
|
567
|
+
]
|
|
568
|
+
: []),
|
|
569
|
+
...(skillCatalog.length > 0
|
|
570
|
+
? [
|
|
571
|
+
{
|
|
572
|
+
name: 'skills',
|
|
573
|
+
content: [
|
|
574
|
+
'# Installed Skills',
|
|
575
|
+
'',
|
|
576
|
+
'To invoke a skill, include its keywords in your message.',
|
|
577
|
+
'',
|
|
578
|
+
...skillCatalog,
|
|
579
|
+
].join('\n'),
|
|
580
|
+
priority: 3,
|
|
581
|
+
},
|
|
582
|
+
]
|
|
583
|
+
: []),
|
|
584
|
+
...(onboardingContent
|
|
585
|
+
? [{ name: 'onboarding', content: onboardingContent, priority: 4 }]
|
|
586
|
+
: []),
|
|
587
|
+
];
|
|
588
|
+
}
|
|
589
|
+
const backend = options.backend ?? 'claude';
|
|
590
|
+
// Load backend-specific AGENTS.md (e.g., AGENTS.claude.md, AGENTS.codex.md)
|
|
591
|
+
const backendAgentsMd = loadBackendAgentsMd(backend);
|
|
592
|
+
if (backendAgentsMd) {
|
|
593
|
+
promptLayers.push({ name: 'backendAgents', content: backendAgentsMd, priority: 2 });
|
|
594
|
+
}
|
|
595
|
+
if (useGatewayMode) {
|
|
596
|
+
if (this.useCodeAct) {
|
|
597
|
+
// Code-Act mode: replace verbose gateway tools markdown with compact .d.ts
|
|
598
|
+
const tierForTypeDefs = options.agentContext?.tier === 1 ||
|
|
599
|
+
options.agentContext?.tier === 2 ||
|
|
600
|
+
options.agentContext?.tier === 3
|
|
601
|
+
? options.agentContext.tier
|
|
602
|
+
: 1;
|
|
603
|
+
const typeDefs = index_js_1.TypeDefinitionGenerator.generate(tierForTypeDefs);
|
|
604
|
+
const codeActBackend = backend === 'codex-mcp' ? 'codex-mcp' : 'claude';
|
|
605
|
+
const codeActPrompt = (0, index_js_1.getCodeActInstructions)(codeActBackend) + '\n```typescript\n' + typeDefs + '\n```';
|
|
606
|
+
promptLayers.push({ name: 'codeAct', content: codeActPrompt, priority: 2 });
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
const gatewayToolsPrompt = getGatewayToolsPrompt();
|
|
610
|
+
if (gatewayToolsPrompt) {
|
|
611
|
+
promptLayers.push({ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 });
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
441
615
|
const checkResult = monitor.check(promptLayers);
|
|
442
616
|
if (checkResult.warning) {
|
|
443
617
|
logger.warn(checkResult.warning);
|
|
444
618
|
}
|
|
445
|
-
//
|
|
619
|
+
// Enforce truncation if over budget (priority > 1 layers trimmed first)
|
|
446
620
|
if (!checkResult.withinBudget) {
|
|
447
621
|
const { layers: trimmedLayers, result: enforceResult } = monitor.enforce(promptLayers);
|
|
448
622
|
if (enforceResult.truncatedLayers.length > 0) {
|
|
449
623
|
logger.warn(`Truncated layers: ${enforceResult.truncatedLayers.join(', ')}`);
|
|
450
624
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
defaultSystemPrompt = trimmedTools ? `${trimmedBase}\n\n---\n\n${trimmedTools}` : trimmedBase;
|
|
454
|
-
logger.debug(`System prompt truncated: ${checkResult.totalChars} → ${defaultSystemPrompt.length} chars`);
|
|
625
|
+
promptLayers = trimmedLayers;
|
|
626
|
+
logger.debug(`System prompt truncated: ${checkResult.totalChars} → ${enforceResult.totalChars} chars`);
|
|
455
627
|
}
|
|
628
|
+
const defaultSystemPrompt = promptLayers
|
|
629
|
+
.filter((l) => l.content.length > 0)
|
|
630
|
+
.map((l) => l.content)
|
|
631
|
+
.join('\n\n---\n\n');
|
|
456
632
|
// Choose backend (default: claude)
|
|
457
|
-
this.backend =
|
|
633
|
+
this.backend = backend;
|
|
458
634
|
if (this.backend === 'codex-mcp') {
|
|
459
635
|
// Codex MCP mode: standard MCP protocol
|
|
460
636
|
const workspaceDir = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'workspace');
|
|
@@ -469,23 +645,28 @@ class AgentLoop {
|
|
|
469
645
|
systemPrompt: defaultSystemPrompt,
|
|
470
646
|
compactPrompt: 'Summarize the conversation concisely, preserving key decisions and context.',
|
|
471
647
|
timeoutMs: options.timeoutMs,
|
|
648
|
+
codexHome: (0, path_1.join)((0, os_1.homedir)(), '.mama', '.codex'),
|
|
472
649
|
});
|
|
473
650
|
logger.debug('Codex MCP backend enabled');
|
|
474
651
|
}
|
|
475
652
|
else {
|
|
476
653
|
// Claude backend: always use PersistentCLI for fast responses (~2-3s vs ~16-30s)
|
|
477
654
|
this.persistentCLI = new persistent_cli_adapter_js_1.PersistentCLIAdapter({
|
|
478
|
-
model: options.model
|
|
655
|
+
model: options.model,
|
|
479
656
|
sessionId,
|
|
480
657
|
systemPrompt: defaultSystemPrompt,
|
|
481
|
-
//
|
|
658
|
+
// MCP config: only pass when MCP mode is enabled (gateway mode uses GatewayToolExecutor)
|
|
482
659
|
mcpConfigPath: useMCPMode ? mcpConfigPath : undefined,
|
|
483
|
-
//
|
|
484
|
-
// Security is enforced by MAMA's RoleManager
|
|
485
|
-
//
|
|
486
|
-
dangerouslySkipPermissions:
|
|
660
|
+
// MAMA OS is a headless daemon (no TTY) — Claude CLI's interactive permission prompts
|
|
661
|
+
// cannot work. Security is enforced by MAMA's own RoleManager layer (config.yaml roles).
|
|
662
|
+
// DO NOT gate this on env vars — MAMA manages permissions via its config, not Claude CLI.
|
|
663
|
+
dangerouslySkipPermissions: options.dangerouslySkipPermissions ?? false,
|
|
487
664
|
// Gateway tools are processed by GatewayToolExecutor (hybrid with MCP)
|
|
488
665
|
useGatewayTools: useGatewayMode,
|
|
666
|
+
// Code-Act: available as optional tool alongside direct tools (no disallowedTools)
|
|
667
|
+
disallowedTools: undefined,
|
|
668
|
+
// Pass configured timeout (default in PersistentCLI: 120s — too short for complex tasks)
|
|
669
|
+
requestTimeout: options.timeoutMs,
|
|
489
670
|
});
|
|
490
671
|
this.agent = this.persistentCLI;
|
|
491
672
|
logger.debug('🚀 Claude PersistentCLI mode enabled - faster responses');
|
|
@@ -497,12 +678,11 @@ class AgentLoop {
|
|
|
497
678
|
this.mcpExecutor = new gateway_tool_executor_js_1.GatewayToolExecutor(executorOptions);
|
|
498
679
|
this.systemPromptOverride = options.systemPrompt;
|
|
499
680
|
this.maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
500
|
-
|
|
501
|
-
this.model = options.model ?? 'claude-sonnet-4-20250514';
|
|
681
|
+
this.model = options.model;
|
|
502
682
|
this.onTurn = options.onTurn;
|
|
503
683
|
this.onToolUse = options.onToolUse;
|
|
504
684
|
this.onTokenUsage = options.onTokenUsage;
|
|
505
|
-
this.laneManager = (0,
|
|
685
|
+
this.laneManager = (0, index_js_2.getGlobalLaneManager)();
|
|
506
686
|
this.useLanes = options.useLanes ?? false;
|
|
507
687
|
this.sessionKey = options.sessionKey ?? 'default';
|
|
508
688
|
this.sessionPool = (0, session_pool_js_1.getSessionPool)();
|
|
@@ -624,10 +804,21 @@ class AgentLoop {
|
|
|
624
804
|
* Internal implementation of runWithContent (without lane queueing)
|
|
625
805
|
*/
|
|
626
806
|
async runWithContentInternal(content, options) {
|
|
807
|
+
this.currentStreamCallbacks = options?.streamCallbacks;
|
|
627
808
|
const history = [];
|
|
628
809
|
const totalUsage = { input_tokens: 0, output_tokens: 0 };
|
|
629
810
|
let turn = 0;
|
|
630
811
|
let stopReason = 'end_turn';
|
|
812
|
+
// Propagate agentContext to executor for tier-aware tool permissions
|
|
813
|
+
if (options?.agentContext) {
|
|
814
|
+
this.mcpExecutor.setAgentContext?.(options.agentContext);
|
|
815
|
+
const rawTier = options.agentContext.tier ?? 1;
|
|
816
|
+
this.currentTier = (rawTier === 1 || rawTier === 2 || rawTier === 3 ? rawTier : 1);
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
this.mcpExecutor.setAgentContext?.(null);
|
|
820
|
+
this.currentTier = 1;
|
|
821
|
+
}
|
|
631
822
|
// Infinite loop prevention
|
|
632
823
|
let consecutiveToolCalls = 0;
|
|
633
824
|
let lastToolName = '';
|
|
@@ -640,10 +831,19 @@ class AgentLoop {
|
|
|
640
831
|
// MessageRouter already calls getSession() and passes the result via options
|
|
641
832
|
let sessionIsNew = options?.resumeSession === undefined ? true : !options.resumeSession;
|
|
642
833
|
let ownedSession = false;
|
|
643
|
-
// Set session ID on the agent
|
|
834
|
+
// Set session ID on the agent
|
|
835
|
+
// Claude PersistentCLI: process alive → CONTINUE (stdin message), process dead → NEW (spawn with --session-id)
|
|
836
|
+
// Codex: threadId alive → CONTINUE (codex-reply), threadId null → NEW (codex tool)
|
|
837
|
+
const isCodex = this.backend === 'codex-mcp';
|
|
838
|
+
const sessionLabel = (isNew) => {
|
|
839
|
+
if (isCodex) {
|
|
840
|
+
return isNew ? 'NEW thread' : 'CONTINUE thread';
|
|
841
|
+
}
|
|
842
|
+
return isNew ? 'NEW process' : 'CONTINUE session';
|
|
843
|
+
};
|
|
644
844
|
if (options?.cliSessionId) {
|
|
645
845
|
this.agent.setSessionId(options.cliSessionId);
|
|
646
|
-
console.log(`[AgentLoop]
|
|
846
|
+
console.log(`[AgentLoop] [${isCodex ? 'codex' : 'claude'}] ${channelKey} (${sessionLabel(sessionIsNew)})`);
|
|
647
847
|
}
|
|
648
848
|
else {
|
|
649
849
|
// Fallback: get session from pool (for direct AgentLoop usage)
|
|
@@ -655,13 +855,27 @@ class AgentLoop {
|
|
|
655
855
|
sessionIsNew = isNew;
|
|
656
856
|
ownedSession = true;
|
|
657
857
|
this.agent.setSessionId(cliSessionId);
|
|
658
|
-
console.log(`[AgentLoop]
|
|
858
|
+
console.log(`[AgentLoop] [${isCodex ? 'codex' : 'claude'}] ${channelKey} (${sessionLabel(isNew)})`);
|
|
659
859
|
}
|
|
660
860
|
try {
|
|
661
861
|
if (options?.systemPrompt) {
|
|
662
862
|
// Skip gateway tools if already embedded in systemPrompt (e.g. by MessageRouter)
|
|
663
|
-
const alreadyHasTools = options.systemPrompt.includes('
|
|
664
|
-
|
|
863
|
+
const alreadyHasTools = options.systemPrompt.includes('## Gateway Tools') ||
|
|
864
|
+
options.systemPrompt.includes('# Code Execution') ||
|
|
865
|
+
options.systemPrompt.includes('## Code-Act');
|
|
866
|
+
let gatewayToolsPrompt = '';
|
|
867
|
+
const isResumingSession = options?.resumeSession === true;
|
|
868
|
+
if (this.isGatewayMode && !alreadyHasTools && !isResumingSession) {
|
|
869
|
+
if (this.useCodeAct) {
|
|
870
|
+
const typeDefs = index_js_1.TypeDefinitionGenerator.generate(this.currentTier);
|
|
871
|
+
const codeActBackend = this.backend === 'codex-mcp' ? 'codex-mcp' : 'claude';
|
|
872
|
+
gatewayToolsPrompt =
|
|
873
|
+
(0, index_js_1.getCodeActInstructions)(codeActBackend) + '\n```typescript\n' + typeDefs + '\n```';
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
gatewayToolsPrompt = getGatewayToolsPrompt();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
665
879
|
const fullPrompt = gatewayToolsPrompt
|
|
666
880
|
? `${options.systemPrompt}\n\n---\n\n${gatewayToolsPrompt}`
|
|
667
881
|
: options.systemPrompt;
|
|
@@ -711,29 +925,32 @@ class AgentLoop {
|
|
|
711
925
|
throw new types_js_1.AgentError(`Emergency stop: Agent loop exceeded emergency maximum turns (${EMERGENCY_MAX_TURNS})`, 'EMERGENCY_MAX_TURNS', undefined, false);
|
|
712
926
|
}
|
|
713
927
|
let response;
|
|
928
|
+
const ext = this.currentStreamCallbacks;
|
|
714
929
|
const callbacks = {
|
|
715
930
|
onDelta: (text) => {
|
|
716
|
-
|
|
931
|
+
ext?.onDelta?.(text);
|
|
932
|
+
},
|
|
933
|
+
onToolUse: (name, input) => {
|
|
934
|
+
ext?.onToolUse?.(name, input);
|
|
717
935
|
},
|
|
718
|
-
|
|
719
|
-
|
|
936
|
+
onToolComplete: (name, toolUseId, isError) => {
|
|
937
|
+
ext?.onToolComplete?.(name, toolUseId, isError);
|
|
720
938
|
},
|
|
721
|
-
onFinal: (
|
|
722
|
-
|
|
939
|
+
onFinal: (finalResponse) => {
|
|
940
|
+
ext?.onFinal?.(finalResponse);
|
|
723
941
|
},
|
|
724
942
|
onError: (error) => {
|
|
725
|
-
|
|
726
|
-
// Don't throw - let the promise rejection handle it
|
|
943
|
+
ext?.onError?.(error);
|
|
727
944
|
},
|
|
728
945
|
};
|
|
729
946
|
let piResult;
|
|
730
|
-
//
|
|
731
|
-
//
|
|
732
|
-
// Subsequent turns (tool loop) or resumed sessions: --resume (skip system prompt)
|
|
947
|
+
// Claude: First turn → --session-id (inject system prompt), subsequent → --resume
|
|
948
|
+
// Codex: resumeSession only controls threadId reset (false=new thread, true=continue)
|
|
733
949
|
const shouldResume = !sessionIsNew || turn > 1;
|
|
734
950
|
// Both Claude PersistentCLI and Codex MCP preserve context - only send new messages
|
|
735
951
|
const promptText = this.formatLastMessageOnly(history);
|
|
736
952
|
try {
|
|
953
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- onFinal signature differs between PersistentCLI and Codex
|
|
737
954
|
piResult = await this.agent.prompt(promptText, callbacks, {
|
|
738
955
|
model: options?.model,
|
|
739
956
|
resumeSession: shouldResume,
|
|
@@ -753,7 +970,9 @@ class AgentLoop {
|
|
|
753
970
|
const isSessionInUse = errorMessage.includes('is already in use');
|
|
754
971
|
const isPromptTooLong = errorMessage.includes('Prompt is too long') ||
|
|
755
972
|
errorMessage.includes('prompt is too long') ||
|
|
756
|
-
errorMessage.includes('request_too_large')
|
|
973
|
+
errorMessage.includes('request_too_large') ||
|
|
974
|
+
errorMessage.includes('context window') ||
|
|
975
|
+
errorMessage.includes('context_length_exceeded');
|
|
757
976
|
if (isSessionNotFound || isSessionInUse || isPromptTooLong) {
|
|
758
977
|
const reason = isSessionNotFound
|
|
759
978
|
? 'not found in CLI'
|
|
@@ -762,10 +981,10 @@ class AgentLoop {
|
|
|
762
981
|
: 'prompt too long (context overflow)';
|
|
763
982
|
console.log(`[AgentLoop] Session ${reason}, retrying with new session`);
|
|
764
983
|
// Reset session in pool so it creates a new one
|
|
765
|
-
this.sessionPool.resetSession(channelKey);
|
|
766
|
-
const newSessionId = this.sessionPool.getSessionId(channelKey);
|
|
984
|
+
const newSessionId = this.sessionPool.resetSession(channelKey);
|
|
767
985
|
this.agent.setSessionId(newSessionId);
|
|
768
986
|
// Retry with new session (--session-id instead of --resume)
|
|
987
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- onFinal signature differs between PersistentCLI and Codex
|
|
769
988
|
piResult = await this.agent.prompt(promptText, callbacks, {
|
|
770
989
|
model: options?.model,
|
|
771
990
|
resumeSession: false, // Force new session
|
|
@@ -784,9 +1003,16 @@ class AgentLoop {
|
|
|
784
1003
|
// Build content blocks - include tool_use blocks if present
|
|
785
1004
|
const contentBlocks = [];
|
|
786
1005
|
let parsedToolCalls = [];
|
|
787
|
-
// Parse tool_call blocks from text response (Gateway Tools mode ONLY)
|
|
1006
|
+
// Parse tool_call / code_act blocks from text response (Gateway Tools mode ONLY)
|
|
788
1007
|
if (this.isGatewayMode) {
|
|
789
1008
|
parsedToolCalls = this.parseToolCallsFromText(piResult.response || '');
|
|
1009
|
+
// Code-Act: parse ```js blocks only if enabled
|
|
1010
|
+
if (this.useCodeAct) {
|
|
1011
|
+
const codeActCalls = this.parseCodeActBlocks(piResult.response || '');
|
|
1012
|
+
if (codeActCalls.length > 0) {
|
|
1013
|
+
parsedToolCalls.push(...codeActCalls);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
790
1016
|
const textWithoutToolCalls = this.removeToolCallBlocks(piResult.response || '');
|
|
791
1017
|
if (textWithoutToolCalls.trim()) {
|
|
792
1018
|
contentBlocks.push({ type: 'text', text: textWithoutToolCalls });
|
|
@@ -971,6 +1197,7 @@ class AgentLoop {
|
|
|
971
1197
|
if (ownedSession) {
|
|
972
1198
|
this.sessionPool.releaseSession(channelKey);
|
|
973
1199
|
}
|
|
1200
|
+
this.currentStreamCallbacks = undefined;
|
|
974
1201
|
}
|
|
975
1202
|
}
|
|
976
1203
|
/**
|
|
@@ -982,27 +1209,63 @@ class AgentLoop {
|
|
|
982
1209
|
for (const toolUse of toolUseBlocks) {
|
|
983
1210
|
let result;
|
|
984
1211
|
let isError = false;
|
|
1212
|
+
// Notify stream: tool execution starting
|
|
1213
|
+
this.currentStreamCallbacks?.onToolUse?.(toolUse.name, toolUse.input);
|
|
985
1214
|
try {
|
|
986
|
-
//
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1215
|
+
// Code-Act: execute JS code in sandbox
|
|
1216
|
+
if (toolUse.name === index_js_1.CODE_ACT_MARKER) {
|
|
1217
|
+
const codeInput = toolUse.input;
|
|
1218
|
+
const code = typeof codeInput?.code === 'string' ? codeInput.code : '';
|
|
1219
|
+
const codeActResult = code
|
|
1220
|
+
? await this.executeCodeAct(code, this.currentTier)
|
|
1221
|
+
: {
|
|
1222
|
+
success: false,
|
|
1223
|
+
error: {
|
|
1224
|
+
name: 'ValidationError',
|
|
1225
|
+
message: 'Missing or invalid "code" field in code_act input',
|
|
1226
|
+
},
|
|
1227
|
+
logs: [],
|
|
1228
|
+
metrics: { durationMs: 0, hostCallCount: 0, memoryUsedBytes: 0 },
|
|
1229
|
+
};
|
|
1230
|
+
result = JSON.stringify(codeActResult, null, 2);
|
|
1231
|
+
if (!codeActResult.success) {
|
|
1232
|
+
isError = true;
|
|
1233
|
+
}
|
|
1234
|
+
this.onToolUse?.(toolUse.name, toolUse.input, codeActResult);
|
|
1235
|
+
this.currentStreamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, isError);
|
|
990
1236
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1237
|
+
else {
|
|
1238
|
+
// PreToolUse: search MAMA for contracts before Write operations
|
|
1239
|
+
let contractContext = '';
|
|
1240
|
+
if (toolUse.name === 'Write' && toolUse.input) {
|
|
1241
|
+
contractContext = await this.searchContractsForTool(toolUse.name, toolUse.input);
|
|
1242
|
+
}
|
|
1243
|
+
const toolResult = await this.mcpExecutor.execute(toolUse.name, toolUse.input);
|
|
1244
|
+
result = JSON.stringify(toolResult, null, 2);
|
|
1245
|
+
// Check if tool execution failed
|
|
1246
|
+
const hasSuccess = 'success' in toolResult;
|
|
1247
|
+
const toolFailed = hasSuccess && !toolResult.success;
|
|
1248
|
+
if (toolFailed) {
|
|
1249
|
+
isError = true;
|
|
1250
|
+
}
|
|
1251
|
+
if (contractContext) {
|
|
1252
|
+
result = `${contractContext}\n\n---\n\n${result}`;
|
|
1253
|
+
}
|
|
1254
|
+
// Notify tool use callback
|
|
1255
|
+
this.onToolUse?.(toolUse.name, toolUse.input, toolResult);
|
|
1256
|
+
// PostToolUse: auto-extract contracts (fire-and-forget)
|
|
1257
|
+
this.postToolHandler?.processInBackground(toolUse.name, toolUse.input, toolResult);
|
|
1258
|
+
// Notify stream: tool completed (check actual status)
|
|
1259
|
+
this.currentStreamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, isError);
|
|
995
1260
|
}
|
|
996
|
-
// Notify tool use callback
|
|
997
|
-
this.onToolUse?.(toolUse.name, toolUse.input, toolResult);
|
|
998
|
-
// PostToolUse: auto-extract contracts (fire-and-forget)
|
|
999
|
-
this.postToolHandler?.processInBackground(toolUse.name, toolUse.input, toolResult);
|
|
1000
1261
|
}
|
|
1001
1262
|
catch (error) {
|
|
1002
1263
|
isError = true;
|
|
1003
1264
|
result = error instanceof Error ? error.message : String(error);
|
|
1004
1265
|
// Notify tool use callback with error
|
|
1005
1266
|
this.onToolUse?.(toolUse.name, toolUse.input, { error: result });
|
|
1267
|
+
// Notify stream: tool completed with error
|
|
1268
|
+
this.currentStreamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, true);
|
|
1006
1269
|
}
|
|
1007
1270
|
results.push({
|
|
1008
1271
|
type: 'tool_result',
|
|
@@ -1078,10 +1341,77 @@ class AgentLoop {
|
|
|
1078
1341
|
return toolCalls;
|
|
1079
1342
|
}
|
|
1080
1343
|
/**
|
|
1081
|
-
*
|
|
1344
|
+
* Parse ```js code blocks as code_act tool calls (Code-Act mode)
|
|
1345
|
+
*/
|
|
1346
|
+
parseCodeActBlocks(text) {
|
|
1347
|
+
const blocks = [];
|
|
1348
|
+
const codeActRegex = /```(?:js|javascript)\s*\n([\s\S]*?)\n```/g;
|
|
1349
|
+
let match;
|
|
1350
|
+
while ((match = codeActRegex.exec(text)) !== null) {
|
|
1351
|
+
const code = match[1].trim();
|
|
1352
|
+
if (code) {
|
|
1353
|
+
blocks.push({
|
|
1354
|
+
type: 'tool_use',
|
|
1355
|
+
id: `code_act_${(0, crypto_1.randomUUID)()}`,
|
|
1356
|
+
name: index_js_1.CODE_ACT_MARKER,
|
|
1357
|
+
input: { code },
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return blocks;
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Execute Code-Act JS code in a sandboxed QuickJS environment
|
|
1365
|
+
*/
|
|
1366
|
+
async executeCodeAct(code, tier = 1) {
|
|
1367
|
+
try {
|
|
1368
|
+
const sandbox = new index_js_1.CodeActSandbox();
|
|
1369
|
+
const bridge = new index_js_1.HostBridge(this.mcpExecutor);
|
|
1370
|
+
bridge.onToolUse = (toolName, input, result) => {
|
|
1371
|
+
if (result === undefined) {
|
|
1372
|
+
// Tool starting — surface to stream
|
|
1373
|
+
this.currentStreamCallbacks?.onToolUse?.(toolName, input);
|
|
1374
|
+
}
|
|
1375
|
+
if (result !== undefined) {
|
|
1376
|
+
// Tool completed — notify callback
|
|
1377
|
+
this.onToolUse?.(toolName, input, result);
|
|
1378
|
+
const isError = typeof result === 'object' &&
|
|
1379
|
+
result !== null &&
|
|
1380
|
+
'success' in result &&
|
|
1381
|
+
!result.success;
|
|
1382
|
+
this.currentStreamCallbacks?.onToolComplete?.(toolName, `code_act_sub_${Date.now()}`, isError);
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
bridge.injectInto(sandbox, tier);
|
|
1386
|
+
const result = await sandbox.execute(code);
|
|
1387
|
+
if (result.logs.length > 0) {
|
|
1388
|
+
console.log(`[CodeAct] console output: ${result.logs.join('\n')}`);
|
|
1389
|
+
}
|
|
1390
|
+
return result;
|
|
1391
|
+
}
|
|
1392
|
+
catch (err) {
|
|
1393
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1394
|
+
console.error(`[CodeAct] Sandbox initialization failed: ${message}`);
|
|
1395
|
+
return {
|
|
1396
|
+
success: false,
|
|
1397
|
+
error: {
|
|
1398
|
+
name: 'SandboxError',
|
|
1399
|
+
message: `Failed to initialize Code-Act sandbox: ${message}`,
|
|
1400
|
+
},
|
|
1401
|
+
logs: [],
|
|
1402
|
+
metrics: { durationMs: 0, hostCallCount: 0, memoryUsedBytes: 0 },
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Remove tool_call and code_act blocks from text (to avoid duplication in response)
|
|
1082
1408
|
*/
|
|
1083
1409
|
removeToolCallBlocks(text) {
|
|
1084
|
-
|
|
1410
|
+
let result = text.replace(/```tool_call\s*\n[\s\S]*?\n```/g, '');
|
|
1411
|
+
if (this.useCodeAct) {
|
|
1412
|
+
result = result.replace(/```(?:js|javascript)\s*\n[\s\S]*?\n```/g, '');
|
|
1413
|
+
}
|
|
1414
|
+
return result.trim();
|
|
1085
1415
|
}
|
|
1086
1416
|
extractTextFromContent(content) {
|
|
1087
1417
|
return content
|