@jungjaehoon/mama-os 0.7.0 → 0.8.0
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/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +204 -62
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
- package/dist/agent/claude-cli-wrapper.js +41 -4
- package/dist/agent/claude-cli-wrapper.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +37 -1
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +44 -1
- package/dist/agent/persistent-cli-process.d.ts.map +1 -1
- package/dist/agent/persistent-cli-process.js +6 -2
- package/dist/agent/persistent-cli-process.js.map +1 -1
- package/dist/agent/session-pool.d.ts.map +1 -1
- package/dist/agent/session-pool.js +39 -2
- package/dist/agent/session-pool.js.map +1 -1
- package/dist/agent/types.d.ts +8 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/cron-handler.d.ts.map +1 -1
- package/dist/api/cron-handler.js +26 -0
- package/dist/api/cron-handler.js.map +1 -1
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +109 -0
- package/dist/api/graph-api.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +5 -3
- package/dist/api/index.js.map +1 -1
- package/dist/api/upload-handler.d.ts +12 -0
- package/dist/api/upload-handler.d.ts.map +1 -0
- package/dist/api/upload-handler.js +286 -0
- package/dist/api/upload-handler.js.map +1 -0
- package/dist/auth/claude-client.d.ts +12 -0
- package/dist/auth/claude-client.d.ts.map +1 -0
- package/dist/auth/claude-client.js +36 -0
- package/dist/auth/claude-client.js.map +1 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +47 -2
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +4 -0
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +44 -0
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +3 -4
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts +4 -0
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +1 -0
- package/dist/cli/config/types.js.map +1 -1
- package/dist/gateways/base-gateway.d.ts +30 -0
- package/dist/gateways/base-gateway.d.ts.map +1 -0
- package/dist/gateways/base-gateway.js +41 -0
- package/dist/gateways/base-gateway.js.map +1 -0
- package/dist/gateways/discord.d.ts +6 -19
- package/dist/gateways/discord.d.ts.map +1 -1
- package/dist/gateways/discord.js +24 -32
- package/dist/gateways/discord.js.map +1 -1
- package/dist/gateways/image-analyzer.d.ts +23 -0
- package/dist/gateways/image-analyzer.d.ts.map +1 -0
- package/dist/gateways/image-analyzer.js +129 -0
- package/dist/gateways/image-analyzer.js.map +1 -0
- package/dist/gateways/index.d.ts +2 -0
- package/dist/gateways/index.d.ts.map +1 -1
- package/dist/gateways/index.js +4 -1
- package/dist/gateways/index.js.map +1 -1
- package/dist/gateways/message-router.d.ts +11 -0
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +168 -23
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/gateways/slack.d.ts +6 -22
- package/dist/gateways/slack.d.ts.map +1 -1
- package/dist/gateways/slack.js +7 -35
- package/dist/gateways/slack.js.map +1 -1
- package/dist/gateways/telegram.d.ts +4 -18
- package/dist/gateways/telegram.d.ts.map +1 -1
- package/dist/gateways/telegram.js +6 -30
- package/dist/gateways/telegram.js.map +1 -1
- package/dist/multi-agent/agent-message-queue.d.ts +2 -0
- package/dist/multi-agent/agent-message-queue.d.ts.map +1 -1
- package/dist/multi-agent/agent-message-queue.js +13 -2
- package/dist/multi-agent/agent-message-queue.js.map +1 -1
- package/dist/multi-agent/multi-agent-discord.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-discord.js +3 -0
- package/dist/multi-agent/multi-agent-discord.js.map +1 -1
- package/dist/scheduler/cron-scheduler.js +1 -1
- package/dist/scheduler/cron-scheduler.js.map +1 -1
- package/package.json +3 -1
- package/public/viewer/js/modules/chat.js +106 -8
- package/public/viewer/js/modules/dashboard.js +82 -101
- package/public/viewer/js/modules/settings.js +68 -15
- package/public/viewer/js/utils/debug-logger.js +2 -2
- package/public/viewer/js/utils/dom.js +12 -0
- package/public/viewer/js/utils/format.js +43 -9
- package/public/viewer/viewer.html +284 -144
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../src/agent/agent-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,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
|
@@ -10,6 +10,39 @@
|
|
|
10
10
|
* - Sends tool_result back to Claude
|
|
11
11
|
* - Loops until stop_reason is "end_turn" or max turns reached
|
|
12
12
|
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
13
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
47
|
exports.AgentLoop = void 0;
|
|
15
48
|
exports.loadInstalledSkills = loadInstalledSkills;
|
|
@@ -31,6 +64,9 @@ const context_prompt_builder_js_1 = require("./context-prompt-builder.js");
|
|
|
31
64
|
const post_tool_handler_js_1 = require("./post-tool-handler.js");
|
|
32
65
|
const stop_continuation_handler_js_1 = require("./stop-continuation-handler.js");
|
|
33
66
|
const pre_compact_handler_js_1 = require("./pre-compact-handler.js");
|
|
67
|
+
const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
|
|
68
|
+
const { DebugLogger } = debugLogger;
|
|
69
|
+
const logger = new DebugLogger('AgentLoop');
|
|
34
70
|
/**
|
|
35
71
|
* Default configuration
|
|
36
72
|
*/
|
|
@@ -208,14 +244,22 @@ function loadInstalledSkills(verbose = false, options = {}) {
|
|
|
208
244
|
try {
|
|
209
245
|
const rootEntries = (0, fs_1.readdirSync)(skillsBase, { withFileTypes: true });
|
|
210
246
|
for (const entry of rootEntries) {
|
|
211
|
-
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
247
|
+
if (!entry.isFile() || !entry.name.endsWith('.md')) {
|
|
212
248
|
continue;
|
|
213
|
-
|
|
249
|
+
}
|
|
250
|
+
if (EXCLUDED_SKILL_FILES.has(entry.name)) {
|
|
214
251
|
continue;
|
|
252
|
+
}
|
|
215
253
|
const id = entry.name.replace(/\.md$/, '');
|
|
254
|
+
const stateKey = `mama/${id}`;
|
|
255
|
+
// Skip disabled skills (check state like subdirectory skills)
|
|
256
|
+
if (state[stateKey]?.enabled === false) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
216
259
|
// Skip if already loaded from subdirectory
|
|
217
|
-
if (blocks.some((b) => b.includes(`[Skill: mama/${id}]`)))
|
|
260
|
+
if (blocks.some((b) => b.includes(`[Skill: mama/${id}]`))) {
|
|
218
261
|
continue;
|
|
262
|
+
}
|
|
219
263
|
const fullPath = (0, path_1.join)(skillsBase, entry.name);
|
|
220
264
|
let content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
|
|
221
265
|
if (content.length > MAX_SKILL_FILE_CHARS) {
|
|
@@ -235,6 +279,18 @@ function loadComposedSystemPrompt(verbose = false, context) {
|
|
|
235
279
|
const mamaHome = (0, path_1.join)((0, os_1.homedir)(), '.mama');
|
|
236
280
|
const layers = [];
|
|
237
281
|
const backend = process.env.MAMA_BACKEND ?? 'claude';
|
|
282
|
+
// Load state for conditional loading (skills + system docs)
|
|
283
|
+
const stateFile = (0, path_1.join)(mamaHome, 'skills', 'state.json');
|
|
284
|
+
let state = {};
|
|
285
|
+
try {
|
|
286
|
+
if ((0, fs_1.existsSync)(stateFile)) {
|
|
287
|
+
state = JSON.parse((0, fs_1.readFileSync)(stateFile, 'utf-8'));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
logger.error(`Failed to parse state file ${stateFile}:`, err);
|
|
292
|
+
throw new Error(`Failed to parse state file ${stateFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
293
|
+
}
|
|
238
294
|
// Load persona files: SOUL.md, IDENTITY.md, USER.md
|
|
239
295
|
const personaFiles = backend === 'codex' ? ['USER.md'] : ['SOUL.md', 'IDENTITY.md', 'USER.md'];
|
|
240
296
|
for (const file of personaFiles) {
|
|
@@ -281,6 +337,23 @@ function loadComposedSystemPrompt(verbose = false, context) {
|
|
|
281
337
|
// Load CLAUDE.md (base instructions)
|
|
282
338
|
const claudeMd = loadSystemPrompt(verbose, backend);
|
|
283
339
|
layers.push(claudeMd);
|
|
340
|
+
// Load ONBOARDING.md only if not disabled in state
|
|
341
|
+
// This contains config schema + bot setup guides - only needed during initial setup
|
|
342
|
+
if (state['system/onboarding']?.enabled !== false) {
|
|
343
|
+
const onboardingPath = (0, path_1.join)(mamaHome, 'ONBOARDING.md');
|
|
344
|
+
if ((0, fs_1.existsSync)(onboardingPath)) {
|
|
345
|
+
const onboardingContent = (0, fs_1.readFileSync)(onboardingPath, 'utf-8');
|
|
346
|
+
layers.push(onboardingContent);
|
|
347
|
+
if (verbose) {
|
|
348
|
+
logger.debug('Loaded ONBOARDING.md (setup reference)');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
if (verbose) {
|
|
354
|
+
logger.debug('Skipped ONBOARDING.md (disabled in state)');
|
|
355
|
+
}
|
|
356
|
+
}
|
|
284
357
|
return layers.join('\n\n---\n\n');
|
|
285
358
|
}
|
|
286
359
|
/**
|
|
@@ -294,7 +367,7 @@ function getGatewayToolsPrompt() {
|
|
|
294
367
|
}
|
|
295
368
|
// TODO: Consider generating both gateway-tools.md and this fallback from a single source
|
|
296
369
|
// to prevent tool list drift (CodeRabbit review suggestion)
|
|
297
|
-
|
|
370
|
+
logger.warn('gateway-tools.md not found, using minimal prompt');
|
|
298
371
|
return `
|
|
299
372
|
## Gateway Tools
|
|
300
373
|
|
|
@@ -341,44 +414,61 @@ class AgentLoop {
|
|
|
341
414
|
const mcpConfigPath = this.toolsConfig.mcp_config?.replace('~', (0, os_1.homedir)()) ||
|
|
342
415
|
(0, path_1.join)((0, os_1.homedir)(), '.mama/mama-mcp-config.json');
|
|
343
416
|
const sessionId = (0, crypto_1.randomUUID)();
|
|
344
|
-
// Determine tool mode: Gateway
|
|
345
|
-
//
|
|
346
|
-
//
|
|
417
|
+
// Determine tool mode: Gateway, MCP, or Hybrid
|
|
418
|
+
// - gateway: ['*'] → Use internal GatewayToolExecutor for mama_*, discord_send, etc.
|
|
419
|
+
// - mcp: ['*'] or [...] → Use MCP servers for external tools (brave-devtools, etc.)
|
|
420
|
+
// - Both can be enabled for hybrid mode
|
|
347
421
|
const mcpTools = this.toolsConfig.mcp || [];
|
|
348
422
|
const gatewayTools = this.toolsConfig.gateway || [];
|
|
349
|
-
//
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
console.warn('[AgentLoop] Warning: Partial patterns (e.g., "mama_*") are not yet supported. ' +
|
|
353
|
-
'Use "*" for all tools or specific tool names. Falling back to Gateway mode.');
|
|
354
|
-
}
|
|
355
|
-
const useMCPMode = mcpTools.includes('*');
|
|
356
|
-
const useGatewayMode = !useMCPMode;
|
|
423
|
+
// Hybrid mode: Gateway + MCP both enabled
|
|
424
|
+
const useGatewayMode = gatewayTools.includes('*') || gatewayTools.length > 0;
|
|
425
|
+
const useMCPMode = mcpTools.includes('*') || mcpTools.length > 0;
|
|
357
426
|
this.isGatewayMode = useGatewayMode;
|
|
427
|
+
if (useGatewayMode && useMCPMode) {
|
|
428
|
+
logger.debug('🔀 Hybrid mode: Gateway + MCP tools enabled');
|
|
429
|
+
}
|
|
430
|
+
else if (useMCPMode) {
|
|
431
|
+
logger.debug('🔌 MCP-only mode');
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
logger.debug('⚙️ Gateway-only mode');
|
|
435
|
+
}
|
|
358
436
|
// Build system prompt
|
|
359
437
|
const basePrompt = options.systemPrompt || loadComposedSystemPrompt();
|
|
360
438
|
// Only include Gateway Tools prompt if using Gateway mode
|
|
361
439
|
const gatewayToolsPrompt = useGatewayMode ? getGatewayToolsPrompt() : '';
|
|
362
|
-
|
|
440
|
+
let defaultSystemPrompt = gatewayToolsPrompt
|
|
363
441
|
? `${basePrompt}\n\n---\n\n${gatewayToolsPrompt}`
|
|
364
442
|
: basePrompt;
|
|
365
|
-
// Monitor prompt size
|
|
443
|
+
// Monitor and enforce prompt size limits
|
|
366
444
|
const monitor = new prompt_size_monitor_js_1.PromptSizeMonitor();
|
|
367
|
-
const
|
|
445
|
+
const promptLayers = [
|
|
368
446
|
{ name: 'base', content: basePrompt, priority: 1 },
|
|
369
447
|
...(gatewayToolsPrompt
|
|
370
448
|
? [{ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 }]
|
|
371
449
|
: []),
|
|
372
|
-
]
|
|
450
|
+
];
|
|
451
|
+
const checkResult = monitor.check(promptLayers);
|
|
373
452
|
if (checkResult.warning) {
|
|
374
|
-
|
|
453
|
+
logger.warn(checkResult.warning);
|
|
454
|
+
}
|
|
455
|
+
// Actually enforce truncation if over budget
|
|
456
|
+
if (!checkResult.withinBudget) {
|
|
457
|
+
const { layers: trimmedLayers, result: enforceResult } = monitor.enforce(promptLayers);
|
|
458
|
+
if (enforceResult.truncatedLayers.length > 0) {
|
|
459
|
+
logger.warn(`Truncated layers: ${enforceResult.truncatedLayers.join(', ')}`);
|
|
460
|
+
}
|
|
461
|
+
const trimmedBase = trimmedLayers.find((l) => l.name === 'base')?.content || basePrompt;
|
|
462
|
+
const trimmedTools = trimmedLayers.find((l) => l.name === 'gatewayTools')?.content || '';
|
|
463
|
+
defaultSystemPrompt = trimmedTools ? `${trimmedBase}\n\n---\n\n${trimmedTools}` : trimmedBase;
|
|
464
|
+
logger.debug(`System prompt truncated: ${checkResult.totalChars} → ${defaultSystemPrompt.length} chars`);
|
|
375
465
|
}
|
|
376
466
|
// Choose backend (default: claude)
|
|
377
467
|
this.backend = options.backend ?? 'claude';
|
|
378
468
|
// Choose CLI mode: Persistent (fast, experimental) or Standard (stable)
|
|
379
469
|
this.usePersistentCLI = this.backend === 'codex' ? false : (options.usePersistentCLI ?? false);
|
|
380
470
|
if (this.backend === 'codex' && options.usePersistentCLI) {
|
|
381
|
-
|
|
471
|
+
logger.warn('Codex backend does not support persistent CLI mode; disabling');
|
|
382
472
|
}
|
|
383
473
|
if (this.usePersistentCLI) {
|
|
384
474
|
// Persistent CLI mode: keeps Claude process alive for multi-turn conversations
|
|
@@ -387,13 +477,17 @@ class AgentLoop {
|
|
|
387
477
|
model: options.model ?? 'claude-sonnet-4-20250514',
|
|
388
478
|
sessionId,
|
|
389
479
|
systemPrompt: defaultSystemPrompt,
|
|
480
|
+
// Hybrid mode: pass MCP config even with Gateway tools enabled
|
|
390
481
|
mcpConfigPath: useMCPMode ? mcpConfigPath : undefined,
|
|
391
|
-
// Headless daemon
|
|
392
|
-
|
|
482
|
+
// Headless daemon requires skipping permission prompts (no TTY available).
|
|
483
|
+
// Security is enforced by MAMA's RoleManager, not Claude CLI's interactive prompts.
|
|
484
|
+
// MAMA_TRUSTED_ENV must be set to enable this flag (defense in depth)
|
|
485
|
+
dangerouslySkipPermissions: process.env.MAMA_TRUSTED_ENV === 'true' && (options.dangerouslySkipPermissions ?? false),
|
|
486
|
+
// Gateway tools are processed by GatewayToolExecutor (hybrid with MCP)
|
|
393
487
|
useGatewayTools: useGatewayMode,
|
|
394
488
|
});
|
|
395
489
|
this.agent = this.persistentCLI;
|
|
396
|
-
|
|
490
|
+
logger.debug('🚀 Persistent CLI mode enabled - faster responses');
|
|
397
491
|
}
|
|
398
492
|
else {
|
|
399
493
|
if (this.backend === 'codex') {
|
|
@@ -405,7 +499,7 @@ class AgentLoop {
|
|
|
405
499
|
sandbox: 'read-only',
|
|
406
500
|
skipGitRepoCheck: true,
|
|
407
501
|
});
|
|
408
|
-
|
|
502
|
+
logger.debug('Codex CLI backend enabled');
|
|
409
503
|
}
|
|
410
504
|
else {
|
|
411
505
|
// Standard Claude CLI mode: spawns new process per message
|
|
@@ -413,22 +507,20 @@ class AgentLoop {
|
|
|
413
507
|
model: options.model ?? 'claude-sonnet-4-20250514',
|
|
414
508
|
sessionId,
|
|
415
509
|
systemPrompt: defaultSystemPrompt,
|
|
510
|
+
// Hybrid mode: pass MCP config even with Gateway tools enabled
|
|
416
511
|
mcpConfigPath: useMCPMode ? mcpConfigPath : undefined,
|
|
417
|
-
// Headless daemon
|
|
418
|
-
|
|
512
|
+
// Headless daemon requires skipping permission prompts (no TTY available).
|
|
513
|
+
// Security is enforced by MAMA's RoleManager, not Claude CLI's interactive prompts.
|
|
514
|
+
// MAMA_TRUSTED_ENV must be set to enable this flag (defense in depth)
|
|
515
|
+
dangerouslySkipPermissions: process.env.MAMA_TRUSTED_ENV === 'true' &&
|
|
516
|
+
(options.dangerouslySkipPermissions ?? false),
|
|
517
|
+
// Gateway tools are processed by GatewayToolExecutor (hybrid with MCP)
|
|
419
518
|
useGatewayTools: useGatewayMode,
|
|
420
519
|
});
|
|
421
520
|
this.agent = this.claudeCLI;
|
|
422
521
|
}
|
|
423
522
|
}
|
|
424
|
-
|
|
425
|
-
if (useMCPMode) {
|
|
426
|
-
console.log('[AgentLoop] MCP mode enabled - tools via MCP server (' + mcpConfigPath + ')');
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
console.log('[AgentLoop] Gateway mode enabled - tools via GatewayToolExecutor');
|
|
430
|
-
}
|
|
431
|
-
console.log('[AgentLoop] Config: gateway=' +
|
|
523
|
+
logger.debug('Config: gateway=' +
|
|
432
524
|
JSON.stringify(this.toolsConfig.gateway) +
|
|
433
525
|
' mcp=' +
|
|
434
526
|
JSON.stringify(this.toolsConfig.mcp));
|
|
@@ -559,7 +651,7 @@ class AgentLoop {
|
|
|
559
651
|
// Infinite loop prevention
|
|
560
652
|
let consecutiveToolCalls = 0;
|
|
561
653
|
let lastToolName = '';
|
|
562
|
-
const MAX_CONSECUTIVE_SAME_TOOL = 5
|
|
654
|
+
const MAX_CONSECUTIVE_SAME_TOOL = 15; // Increased from 5 - normal coding tasks often need 10+ consecutive Bash calls
|
|
563
655
|
const EMERGENCY_MAX_TURNS = Math.max(this.maxTurns + 10, 50); // Always above maxTurns
|
|
564
656
|
// Track channel key for session release
|
|
565
657
|
const channelKey = (0, session_pool_js_1.buildChannelKey)(options?.source ?? 'default', options?.channelId ?? this.sessionKey);
|
|
@@ -583,23 +675,37 @@ class AgentLoop {
|
|
|
583
675
|
}
|
|
584
676
|
try {
|
|
585
677
|
if (options?.systemPrompt) {
|
|
586
|
-
|
|
678
|
+
// Skip gateway tools if already embedded in systemPrompt (e.g. by MessageRouter)
|
|
679
|
+
const alreadyHasTools = options.systemPrompt.includes('# Gateway Tools');
|
|
680
|
+
const gatewayToolsPrompt = this.isGatewayMode && !alreadyHasTools ? getGatewayToolsPrompt() : '';
|
|
587
681
|
const fullPrompt = gatewayToolsPrompt
|
|
588
682
|
? `${options.systemPrompt}\n\n---\n\n${gatewayToolsPrompt}`
|
|
589
683
|
: options.systemPrompt;
|
|
590
|
-
// Monitor prompt size
|
|
684
|
+
// Monitor and enforce prompt size
|
|
591
685
|
const monitor = new prompt_size_monitor_js_1.PromptSizeMonitor();
|
|
592
|
-
const
|
|
686
|
+
const runLayers = [
|
|
593
687
|
{ name: 'systemPrompt', content: options.systemPrompt, priority: 1 },
|
|
594
688
|
...(gatewayToolsPrompt
|
|
595
689
|
? [{ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 }]
|
|
596
690
|
: []),
|
|
597
|
-
]
|
|
691
|
+
];
|
|
692
|
+
const checkResult = monitor.check(runLayers);
|
|
598
693
|
if (checkResult.warning) {
|
|
599
694
|
console.warn(`[AgentLoop] ${checkResult.warning}`);
|
|
600
695
|
}
|
|
601
|
-
|
|
602
|
-
|
|
696
|
+
let effectivePrompt = fullPrompt;
|
|
697
|
+
if (!checkResult.withinBudget) {
|
|
698
|
+
const { layers: trimmed, result: enforceResult } = monitor.enforce(runLayers);
|
|
699
|
+
if (enforceResult.truncatedLayers.length > 0) {
|
|
700
|
+
console.warn(`[AgentLoop] Truncated layers: ${enforceResult.truncatedLayers.join(', ')}`);
|
|
701
|
+
}
|
|
702
|
+
const tBase = trimmed.find((l) => l.name === 'systemPrompt')?.content || options.systemPrompt;
|
|
703
|
+
const tTools = trimmed.find((l) => l.name === 'gatewayTools')?.content || '';
|
|
704
|
+
effectivePrompt = tTools ? `${tBase}\n\n---\n\n${tTools}` : tBase;
|
|
705
|
+
console.log(`[AgentLoop] System prompt truncated: ${fullPrompt.length} → ${effectivePrompt.length} chars`);
|
|
706
|
+
}
|
|
707
|
+
console.log(`[AgentLoop] Setting systemPrompt: ${effectivePrompt.length} chars (base: ${options.systemPrompt.length}, tools: ${gatewayToolsPrompt.length})`);
|
|
708
|
+
this.agent.setSystemPrompt(effectivePrompt);
|
|
603
709
|
}
|
|
604
710
|
else {
|
|
605
711
|
console.log(`[AgentLoop] No systemPrompt in options, using default`);
|
|
@@ -667,10 +773,18 @@ class AgentLoop {
|
|
|
667
773
|
// Check if this is a recoverable session error
|
|
668
774
|
// 1. "No conversation found" - CLI session was lost (daemon restart, timeout)
|
|
669
775
|
// 2. "Session ID already in use" - concurrent request conflict
|
|
776
|
+
// 3. "Prompt is too long" - session context exceeded API limits
|
|
670
777
|
const isSessionNotFound = errorMessage.includes('No conversation found with session ID');
|
|
671
778
|
const isSessionInUse = errorMessage.includes('is already in use');
|
|
672
|
-
|
|
673
|
-
|
|
779
|
+
const isPromptTooLong = errorMessage.includes('Prompt is too long') ||
|
|
780
|
+
errorMessage.includes('prompt is too long') ||
|
|
781
|
+
errorMessage.includes('request_too_large');
|
|
782
|
+
if (isSessionNotFound || isSessionInUse || isPromptTooLong) {
|
|
783
|
+
const reason = isSessionNotFound
|
|
784
|
+
? 'not found in CLI'
|
|
785
|
+
: isSessionInUse
|
|
786
|
+
? 'already in use'
|
|
787
|
+
: 'prompt too long (context overflow)';
|
|
674
788
|
console.log(`[AgentLoop] Session ${reason}, retrying with new session`);
|
|
675
789
|
// Reset session in pool so it creates a new one
|
|
676
790
|
this.sessionPool.resetSession(channelKey);
|
|
@@ -681,6 +795,10 @@ class AgentLoop {
|
|
|
681
795
|
model: options?.model,
|
|
682
796
|
resumeSession: false, // Force new session
|
|
683
797
|
});
|
|
798
|
+
// Prepend reset notice so user knows context was lost
|
|
799
|
+
if (isPromptTooLong && piResult.response) {
|
|
800
|
+
piResult.response = `⚠️ Session reset: The previous conversation was too long, starting a new session.\n⚠️ 이전 대화가 너무 길어져 새 세션으로 전환되었습니다.\n\n${piResult.response}`;
|
|
801
|
+
}
|
|
684
802
|
console.log(`[AgentLoop] Retry successful with new session: ${newSessionId}`);
|
|
685
803
|
}
|
|
686
804
|
else {
|
|
@@ -1045,27 +1163,37 @@ class AgentLoop {
|
|
|
1045
1163
|
parts.push(`[Tool Result: ${status}]\n${block.content}`);
|
|
1046
1164
|
}
|
|
1047
1165
|
else if (block.type === 'image') {
|
|
1048
|
-
// Convert image to file path instruction for Claude Code
|
|
1049
|
-
// MANDATORY: Claude MUST use Read tool to view the image before responding
|
|
1050
1166
|
if (block.localPath) {
|
|
1051
|
-
parts.push(
|
|
1052
|
-
`
|
|
1053
|
-
`
|
|
1167
|
+
parts.push(`⚠️ CRITICAL: The user has uploaded an image file.\n` +
|
|
1168
|
+
`Image path: ${block.localPath}\n` +
|
|
1169
|
+
`You MUST call the Read tool on "${block.localPath}" to view this image FIRST.\n` +
|
|
1170
|
+
`DO NOT describe or guess the image contents without reading it.\n` +
|
|
1171
|
+
`DO NOT say you cannot read images - the Read tool supports image files.`);
|
|
1054
1172
|
}
|
|
1055
1173
|
else if (block.source?.data) {
|
|
1056
|
-
// Base64 image - save to workspace and reference it
|
|
1057
1174
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1058
1175
|
const fs = require('fs');
|
|
1059
1176
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1060
1177
|
const path = require('path');
|
|
1061
|
-
const mediaDir = path.join(
|
|
1178
|
+
const mediaDir = path.join((0, os_1.homedir)(), '.mama', 'workspace', 'media', 'inbound');
|
|
1062
1179
|
fs.mkdirSync(mediaDir, { recursive: true });
|
|
1063
|
-
|
|
1180
|
+
// Map MIME type to file extension (support PNG, JPEG, GIF, WebP)
|
|
1181
|
+
const mimeToExt = {
|
|
1182
|
+
'image/png': '.png',
|
|
1183
|
+
'image/jpeg': '.jpg',
|
|
1184
|
+
'image/jpg': '.jpg',
|
|
1185
|
+
'image/gif': '.gif',
|
|
1186
|
+
'image/webp': '.webp',
|
|
1187
|
+
};
|
|
1188
|
+
const ext = mimeToExt[block.source.media_type?.toLowerCase() || ''] || '.jpg';
|
|
1189
|
+
const imagePath = path.join(mediaDir, `${Date.now()}-${(0, crypto_1.randomUUID)().slice(0, 8)}${ext}`);
|
|
1064
1190
|
try {
|
|
1065
1191
|
fs.writeFileSync(imagePath, Buffer.from(block.source.data, 'base64'));
|
|
1066
|
-
parts.push(
|
|
1067
|
-
`
|
|
1068
|
-
`
|
|
1192
|
+
parts.push(`⚠️ CRITICAL: The user has uploaded an image file.\n` +
|
|
1193
|
+
`Image path: ${imagePath}\n` +
|
|
1194
|
+
`You MUST call the Read tool on "${imagePath}" to view this image FIRST.\n` +
|
|
1195
|
+
`DO NOT describe or guess the image contents without reading it.\n` +
|
|
1196
|
+
`DO NOT say you cannot read images - the Read tool supports image files.`);
|
|
1069
1197
|
}
|
|
1070
1198
|
catch {
|
|
1071
1199
|
parts.push('[Image attached but could not be processed]');
|
|
@@ -1111,9 +1239,11 @@ class AgentLoop {
|
|
|
1111
1239
|
parts.push(block.text);
|
|
1112
1240
|
}
|
|
1113
1241
|
else if (block.type === 'image' && block.localPath) {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
`
|
|
1242
|
+
parts.push(`⚠️ CRITICAL: The user has uploaded an image file.\n` +
|
|
1243
|
+
`Image path: ${block.localPath}\n` +
|
|
1244
|
+
`You MUST call the Read tool on "${block.localPath}" to view this image FIRST.\n` +
|
|
1245
|
+
`DO NOT describe or guess the image contents without reading it.\n` +
|
|
1246
|
+
`DO NOT say you cannot read images - the Read tool supports image files.`);
|
|
1117
1247
|
}
|
|
1118
1248
|
else if (block.type === 'image' && block.source?.data) {
|
|
1119
1249
|
// Base64-encoded image — save to disk so persistent CLI can read it
|
|
@@ -1121,13 +1251,25 @@ class AgentLoop {
|
|
|
1121
1251
|
const fs = require('fs');
|
|
1122
1252
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1123
1253
|
const path = require('path');
|
|
1124
|
-
const mediaDir = path.join(
|
|
1254
|
+
const mediaDir = path.join((0, os_1.homedir)(), '.mama', 'workspace', 'media', 'inbound');
|
|
1125
1255
|
fs.mkdirSync(mediaDir, { recursive: true });
|
|
1126
|
-
|
|
1256
|
+
// Map MIME type to file extension (support PNG, JPEG, GIF, WebP)
|
|
1257
|
+
const mimeToExt = {
|
|
1258
|
+
'image/png': '.png',
|
|
1259
|
+
'image/jpeg': '.jpg',
|
|
1260
|
+
'image/jpg': '.jpg',
|
|
1261
|
+
'image/gif': '.gif',
|
|
1262
|
+
'image/webp': '.webp',
|
|
1263
|
+
};
|
|
1264
|
+
const ext = mimeToExt[block.source.media_type?.toLowerCase() || ''] || '.jpg';
|
|
1265
|
+
const imagePath = path.join(mediaDir, `${Date.now()}-${(0, crypto_1.randomUUID)().slice(0, 8)}${ext}`);
|
|
1127
1266
|
try {
|
|
1128
1267
|
fs.writeFileSync(imagePath, Buffer.from(block.source.data, 'base64'));
|
|
1129
|
-
parts.push(
|
|
1130
|
-
`
|
|
1268
|
+
parts.push(`⚠️ CRITICAL: The user has uploaded an image file.\n` +
|
|
1269
|
+
`Image path: ${imagePath}\n` +
|
|
1270
|
+
`You MUST call the Read tool on "${imagePath}" to view this image FIRST.\n` +
|
|
1271
|
+
`DO NOT describe or guess the image contents without reading it.\n` +
|
|
1272
|
+
`DO NOT say you cannot read images - the Read tool supports image files.`);
|
|
1131
1273
|
}
|
|
1132
1274
|
catch {
|
|
1133
1275
|
parts.push('[Image attached but could not be processed]');
|