@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.
Files changed (95) hide show
  1. package/dist/agent/agent-loop.d.ts.map +1 -1
  2. package/dist/agent/agent-loop.js +204 -62
  3. package/dist/agent/agent-loop.js.map +1 -1
  4. package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
  5. package/dist/agent/claude-cli-wrapper.js +41 -4
  6. package/dist/agent/claude-cli-wrapper.js.map +1 -1
  7. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  8. package/dist/agent/gateway-tool-executor.js +37 -1
  9. package/dist/agent/gateway-tool-executor.js.map +1 -1
  10. package/dist/agent/gateway-tools.md +44 -1
  11. package/dist/agent/persistent-cli-process.d.ts.map +1 -1
  12. package/dist/agent/persistent-cli-process.js +6 -2
  13. package/dist/agent/persistent-cli-process.js.map +1 -1
  14. package/dist/agent/session-pool.d.ts.map +1 -1
  15. package/dist/agent/session-pool.js +39 -2
  16. package/dist/agent/session-pool.js.map +1 -1
  17. package/dist/agent/types.d.ts +8 -0
  18. package/dist/agent/types.d.ts.map +1 -1
  19. package/dist/agent/types.js.map +1 -1
  20. package/dist/api/cron-handler.d.ts.map +1 -1
  21. package/dist/api/cron-handler.js +26 -0
  22. package/dist/api/cron-handler.js.map +1 -1
  23. package/dist/api/graph-api.d.ts.map +1 -1
  24. package/dist/api/graph-api.js +109 -0
  25. package/dist/api/graph-api.js.map +1 -1
  26. package/dist/api/index.d.ts.map +1 -1
  27. package/dist/api/index.js +5 -3
  28. package/dist/api/index.js.map +1 -1
  29. package/dist/api/upload-handler.d.ts +12 -0
  30. package/dist/api/upload-handler.d.ts.map +1 -0
  31. package/dist/api/upload-handler.js +286 -0
  32. package/dist/api/upload-handler.js.map +1 -0
  33. package/dist/auth/claude-client.d.ts +12 -0
  34. package/dist/auth/claude-client.d.ts.map +1 -0
  35. package/dist/auth/claude-client.js +36 -0
  36. package/dist/auth/claude-client.js.map +1 -0
  37. package/dist/cli/commands/start.d.ts.map +1 -1
  38. package/dist/cli/commands/start.js +47 -2
  39. package/dist/cli/commands/start.js.map +1 -1
  40. package/dist/cli/commands/stop.d.ts +4 -0
  41. package/dist/cli/commands/stop.d.ts.map +1 -1
  42. package/dist/cli/commands/stop.js +44 -0
  43. package/dist/cli/commands/stop.js.map +1 -1
  44. package/dist/cli/config/config-manager.d.ts.map +1 -1
  45. package/dist/cli/config/config-manager.js +3 -4
  46. package/dist/cli/config/config-manager.js.map +1 -1
  47. package/dist/cli/config/types.d.ts +4 -0
  48. package/dist/cli/config/types.d.ts.map +1 -1
  49. package/dist/cli/config/types.js +1 -0
  50. package/dist/cli/config/types.js.map +1 -1
  51. package/dist/gateways/base-gateway.d.ts +30 -0
  52. package/dist/gateways/base-gateway.d.ts.map +1 -0
  53. package/dist/gateways/base-gateway.js +41 -0
  54. package/dist/gateways/base-gateway.js.map +1 -0
  55. package/dist/gateways/discord.d.ts +6 -19
  56. package/dist/gateways/discord.d.ts.map +1 -1
  57. package/dist/gateways/discord.js +24 -32
  58. package/dist/gateways/discord.js.map +1 -1
  59. package/dist/gateways/image-analyzer.d.ts +23 -0
  60. package/dist/gateways/image-analyzer.d.ts.map +1 -0
  61. package/dist/gateways/image-analyzer.js +129 -0
  62. package/dist/gateways/image-analyzer.js.map +1 -0
  63. package/dist/gateways/index.d.ts +2 -0
  64. package/dist/gateways/index.d.ts.map +1 -1
  65. package/dist/gateways/index.js +4 -1
  66. package/dist/gateways/index.js.map +1 -1
  67. package/dist/gateways/message-router.d.ts +11 -0
  68. package/dist/gateways/message-router.d.ts.map +1 -1
  69. package/dist/gateways/message-router.js +168 -23
  70. package/dist/gateways/message-router.js.map +1 -1
  71. package/dist/gateways/slack.d.ts +6 -22
  72. package/dist/gateways/slack.d.ts.map +1 -1
  73. package/dist/gateways/slack.js +7 -35
  74. package/dist/gateways/slack.js.map +1 -1
  75. package/dist/gateways/telegram.d.ts +4 -18
  76. package/dist/gateways/telegram.d.ts.map +1 -1
  77. package/dist/gateways/telegram.js +6 -30
  78. package/dist/gateways/telegram.js.map +1 -1
  79. package/dist/multi-agent/agent-message-queue.d.ts +2 -0
  80. package/dist/multi-agent/agent-message-queue.d.ts.map +1 -1
  81. package/dist/multi-agent/agent-message-queue.js +13 -2
  82. package/dist/multi-agent/agent-message-queue.js.map +1 -1
  83. package/dist/multi-agent/multi-agent-discord.d.ts.map +1 -1
  84. package/dist/multi-agent/multi-agent-discord.js +3 -0
  85. package/dist/multi-agent/multi-agent-discord.js.map +1 -1
  86. package/dist/scheduler/cron-scheduler.js +1 -1
  87. package/dist/scheduler/cron-scheduler.js.map +1 -1
  88. package/package.json +3 -1
  89. package/public/viewer/js/modules/chat.js +106 -8
  90. package/public/viewer/js/modules/dashboard.js +82 -101
  91. package/public/viewer/js/modules/settings.js +68 -15
  92. package/public/viewer/js/utils/debug-logger.js +2 -2
  93. package/public/viewer/js/utils/dom.js +12 -0
  94. package/public/viewer/js/utils/format.js +43 -9
  95. 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":"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"}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../src/agent/agent-loop.ts"],"names":[],"mappings":"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"}
@@ -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
- if (EXCLUDED_SKILL_FILES.has(entry.name))
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
- console.warn('[AgentLoop] gateway-tools.md not found, using minimal prompt');
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 (default) or MCP
345
- // Currently only '*' (all tools) is supported for mode selection
346
- // Partial patterns like 'mama_*' or 'browser_*' are reserved for future hybrid routing
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
- // Warn if partial patterns are used (not yet supported)
350
- const hasPartialPattern = (arr) => arr.some((t) => t.includes('*') && t !== '*');
351
- if (hasPartialPattern(mcpTools) || hasPartialPattern(gatewayTools)) {
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
- const defaultSystemPrompt = gatewayToolsPrompt
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 checkResult = monitor.check([
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
- console.warn(`[AgentLoop] ${checkResult.warning}`);
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
- console.warn('[AgentLoop] Codex backend does not support persistent CLI mode; disabling');
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 no interactive permission prompts possible
392
- dangerouslySkipPermissions: false, // Changed to false for safety
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
- console.log('[AgentLoop] 🚀 Persistent CLI mode enabled - faster responses');
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
- console.log('[AgentLoop] Codex CLI backend enabled');
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 no interactive permission prompts possible
418
- dangerouslySkipPermissions: false, // Changed to false for safety
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
- // Log tool mode for transparency
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
- const gatewayToolsPrompt = this.isGatewayMode ? getGatewayToolsPrompt() : '';
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 checkResult = monitor.check([
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
- console.log(`[AgentLoop] Setting systemPrompt: ${fullPrompt.length} chars (base: ${options.systemPrompt.length}, tools: ${gatewayToolsPrompt.length})`);
602
- this.agent.setSystemPrompt(fullPrompt);
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
- if (isSessionNotFound || isSessionInUse) {
673
- const reason = isSessionNotFound ? 'not found in CLI' : 'already in use';
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(`**[MANDATORY IMAGE]** The user has attached an image at: ${block.localPath}\n` +
1052
- `YOU MUST use the Read tool to view this image BEFORE responding to the user's request.\n` +
1053
- `Do NOT respond without first reading the image. The user expects you to see and analyze the image content.`);
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(process.env.HOME || '', '.mama', 'workspace', 'media', 'inbound');
1178
+ const mediaDir = path.join((0, os_1.homedir)(), '.mama', 'workspace', 'media', 'inbound');
1062
1179
  fs.mkdirSync(mediaDir, { recursive: true });
1063
- const imagePath = path.join(mediaDir, `${Date.now()}.jpg`);
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(`**[MANDATORY IMAGE]** The user has attached an image at: ${imagePath}\n` +
1067
- `YOU MUST use the Read tool to view this image BEFORE responding to the user's request.\n` +
1068
- `Do NOT respond without first reading the image. The user expects you to see and analyze the image content.`);
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
- // Include image instructions for persistent CLI as well
1115
- parts.push(`**[MANDATORY IMAGE]** The user has attached an image at: ${block.localPath}\n` +
1116
- `YOU MUST use the Read tool to view this image BEFORE responding to the user's request.`);
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(process.env.HOME || '', '.mama', 'workspace', 'media', 'inbound');
1254
+ const mediaDir = path.join((0, os_1.homedir)(), '.mama', 'workspace', 'media', 'inbound');
1125
1255
  fs.mkdirSync(mediaDir, { recursive: true });
1126
- const imagePath = path.join(mediaDir, `${Date.now()}.jpg`);
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(`**[MANDATORY IMAGE]** The user has attached an image at: ${imagePath}\n` +
1130
- `YOU MUST use the Read tool to view this image BEFORE responding to the user's request.`);
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]');