@jungjaehoon/mama-os 0.29.1 → 0.30.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 (100) hide show
  1. package/dist/agent/agent-loop.d.ts +1 -12
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +6 -21
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/constants.d.ts.map +1 -1
  6. package/dist/agent/code-act/constants.js +2 -4
  7. package/dist/agent/code-act/constants.js.map +1 -1
  8. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  9. package/dist/agent/code-act/host-bridge.js +1 -290
  10. package/dist/agent/code-act/host-bridge.js.map +1 -1
  11. package/dist/agent/code-act/types.d.ts +1 -1
  12. package/dist/agent/code-act/types.d.ts.map +1 -1
  13. package/dist/agent/gateway-tool-executor.d.ts +1 -124
  14. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  15. package/dist/agent/gateway-tool-executor.js +18 -1163
  16. package/dist/agent/gateway-tool-executor.js.map +1 -1
  17. package/dist/agent/gateway-tools.md +0 -38
  18. package/dist/agent/os-agent-capabilities.md +0 -14
  19. package/dist/agent/tool-registry.d.ts +1 -1
  20. package/dist/agent/tool-registry.d.ts.map +1 -1
  21. package/dist/agent/tool-registry.js +0 -183
  22. package/dist/agent/tool-registry.js.map +1 -1
  23. package/dist/agent/types.d.ts +2 -110
  24. package/dist/agent/types.d.ts.map +1 -1
  25. package/dist/agent/types.js.map +1 -1
  26. package/dist/api/graph-api-types.d.ts +0 -25
  27. package/dist/api/graph-api-types.d.ts.map +1 -1
  28. package/dist/api/graph-api.d.ts.map +1 -1
  29. package/dist/api/graph-api.js +0 -50
  30. package/dist/api/graph-api.js.map +1 -1
  31. package/dist/cli/commands/start.d.ts +18 -0
  32. package/dist/cli/commands/start.d.ts.map +1 -1
  33. package/dist/cli/commands/start.js +180 -42
  34. package/dist/cli/commands/start.js.map +1 -1
  35. package/dist/cli/config/types.d.ts +21 -0
  36. package/dist/cli/config/types.d.ts.map +1 -1
  37. package/dist/cli/config/types.js +7 -0
  38. package/dist/cli/config/types.js.map +1 -1
  39. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  40. package/dist/cli/runtime/api-routes-init.js +0 -3
  41. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  42. package/dist/cli/runtime/gateway-wiring.d.ts +1 -1
  43. package/dist/cli/runtime/gateway-wiring.js +1 -1
  44. package/dist/gateways/discord.d.ts.map +1 -1
  45. package/dist/gateways/discord.js +0 -8
  46. package/dist/gateways/discord.js.map +1 -1
  47. package/dist/gateways/message-router.d.ts +13 -0
  48. package/dist/gateways/message-router.d.ts.map +1 -1
  49. package/dist/gateways/message-router.js +36 -0
  50. package/dist/gateways/message-router.js.map +1 -1
  51. package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
  52. package/dist/multi-agent/agent-process-manager.js +4 -3
  53. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  54. package/dist/multi-agent/tool-permission-manager.d.ts +0 -5
  55. package/dist/multi-agent/tool-permission-manager.d.ts.map +1 -1
  56. package/dist/multi-agent/tool-permission-manager.js +0 -40
  57. package/dist/multi-agent/tool-permission-manager.js.map +1 -1
  58. package/dist/operator/board-reground.d.ts +19 -0
  59. package/dist/operator/board-reground.d.ts.map +1 -0
  60. package/dist/operator/board-reground.js +31 -0
  61. package/dist/operator/board-reground.js.map +1 -0
  62. package/dist/operator/conductor-inbox.d.ts +73 -0
  63. package/dist/operator/conductor-inbox.d.ts.map +1 -0
  64. package/dist/operator/conductor-inbox.js +184 -0
  65. package/dist/operator/conductor-inbox.js.map +1 -0
  66. package/dist/operator/conductor-session.d.ts +55 -0
  67. package/dist/operator/conductor-session.d.ts.map +1 -0
  68. package/dist/operator/conductor-session.js +110 -0
  69. package/dist/operator/conductor-session.js.map +1 -0
  70. package/dist/operator/conductor.d.ts +68 -0
  71. package/dist/operator/conductor.d.ts.map +1 -0
  72. package/dist/operator/conductor.js +84 -0
  73. package/dist/operator/conductor.js.map +1 -0
  74. package/dist/operator/operator-trigger-loop.d.ts +13 -0
  75. package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
  76. package/dist/operator/operator-trigger-loop.js +39 -8
  77. package/dist/operator/operator-trigger-loop.js.map +1 -1
  78. package/dist/operator/report-run.d.ts +8 -2
  79. package/dist/operator/report-run.d.ts.map +1 -1
  80. package/dist/operator/report-run.js +9 -9
  81. package/dist/operator/report-run.js.map +1 -1
  82. package/dist/operator/trigger-author.d.ts +0 -1
  83. package/dist/operator/trigger-author.d.ts.map +1 -1
  84. package/dist/operator/trigger-author.js +24 -3
  85. package/dist/operator/trigger-author.js.map +1 -1
  86. package/dist/operator/workorder-consumer.d.ts +2 -0
  87. package/dist/operator/workorder-consumer.d.ts.map +1 -1
  88. package/dist/operator/workorder-consumer.js +57 -8
  89. package/dist/operator/workorder-consumer.js.map +1 -1
  90. package/package.json +1 -2
  91. package/public/viewer/src/utils/api.ts +0 -10
  92. package/scripts/backfill-channel-keys.ts +75 -17
  93. package/dist/multi-agent/conductor-persona.d.ts +0 -13
  94. package/dist/multi-agent/conductor-persona.d.ts.map +0 -1
  95. package/dist/multi-agent/conductor-persona.js +0 -157
  96. package/dist/multi-agent/conductor-persona.js.map +0 -1
  97. package/dist/tools/browser-tool.d.ts +0 -164
  98. package/dist/tools/browser-tool.d.ts.map +0 -1
  99. package/dist/tools/browser-tool.js +0 -315
  100. package/dist/tools/browser-tool.js.map +0 -1
@@ -70,13 +70,9 @@ const untrusted_content_js_1 = require("../utils/untrusted-content.js");
70
70
  const types_js_1 = require("./types.js");
71
71
  const sqlite_js_1 = __importDefault(require("../sqlite.js"));
72
72
  const mama_tool_handlers_js_1 = require("./mama-tool-handlers.js");
73
- const browser_tool_js_1 = require("../tools/browser-tool.js");
74
73
  const role_manager_js_1 = require("./role-manager.js");
75
74
  const config_manager_js_1 = require("../cli/config/config-manager.js");
76
75
  const agent_store_js_1 = require("../db/agent-store.js");
77
- const types_js_2 = require("../cli/config/types.js");
78
- const managed_agent_runtime_sync_js_1 = require("./managed-agent-runtime-sync.js");
79
- const managed_agent_validation_js_1 = require("./managed-agent-validation.js");
80
76
  const index_js_1 = require("../envelope/index.js");
81
77
  const wiki_publish_adapter_js_1 = require("../wiki-artifacts/wiki-publish-adapter.js");
82
78
  const changes_projection_js_1 = require("../operator/changes-projection.js");
@@ -140,14 +136,6 @@ const { DebugLogger } = debugLogger;
140
136
  const securityLogger = new DebugLogger('SecurityAudit');
141
137
  const TRUTHY_ENV_VALUES = new Set(['1', 'true', 'yes', 'on']);
142
138
  let trustedProvenanceRuntime = null;
143
- const AGENT_DETAIL_TABS = new Set([
144
- 'config',
145
- 'persona',
146
- 'tools',
147
- 'activity',
148
- 'validation',
149
- 'history',
150
- ]);
151
139
  const RECALL_TEXT_REDACTION_PATTERNS = [
152
140
  /MAMA_SYNTHETIC_[A-Z0-9_]+_DO_NOT_LEAK/g,
153
141
  /synthetic:\/\/raw[^\s"']*/g,
@@ -166,7 +154,6 @@ const RECALL_TEXT_REDACTION_PATTERNS = [
166
154
  ];
167
155
  const MAX_RECALL_TEXT_LENGTH = 280;
168
156
  const RECALL_TEXT_REDACTION_SCAN_LIMIT = MAX_RECALL_TEXT_LENGTH + 2048;
169
- const managedAgentMutationTails = new Map();
170
157
  const MEMORY_SCOPE_AUDIT_TOOLS = new Set([
171
158
  'mama_save',
172
159
  'mama_search',
@@ -174,15 +161,11 @@ const MEMORY_SCOPE_AUDIT_TOOLS = new Set([
174
161
  'mama_provenance',
175
162
  'context_compile',
176
163
  'mama_update',
177
- 'mama_add',
178
- 'mama_ingest',
179
164
  ]);
180
165
  const TEMPORAL_WRITE_TOOLS = new Set([
181
166
  'mama_save',
182
167
  'context_compile',
183
168
  'mama_update',
184
- 'mama_add',
185
- 'mama_ingest',
186
169
  'report_publish',
187
170
  'report_request',
188
171
  'workorder_request',
@@ -198,26 +181,7 @@ const TEMPORAL_WRITE_TOOLS = new Set([
198
181
  'slack_send',
199
182
  'telegram_send',
200
183
  'webchat_send',
201
- 'delegate',
202
- 'os_add_bot',
203
- 'os_set_permissions',
204
- 'os_set_model',
205
- 'os_restart_bot',
206
- 'os_stop_bot',
207
- 'agent_update',
208
- 'agent_create',
209
- 'viewer_navigate',
210
- 'viewer_notify',
211
184
  'save_integration_token',
212
- 'browser_navigate',
213
- 'browser_screenshot',
214
- 'browser_click',
215
- 'browser_type',
216
- 'browser_scroll',
217
- 'browser_evaluate',
218
- 'browser_pdf',
219
- 'browser_close',
220
- 'agent_test',
221
185
  ]);
222
186
  const MEMORY_READ_PERMISSION_BEFORE_ENVELOPE_TOOLS = new Set([
223
187
  'mama_save',
@@ -343,25 +307,6 @@ function sanitizeMamaRecallBundle(bundle) {
343
307
  },
344
308
  };
345
309
  }
346
- async function withManagedAgentMutationLock(agentId, fn) {
347
- const previous = managedAgentMutationTails.get(agentId) ?? Promise.resolve();
348
- let release;
349
- const current = new Promise((resolve) => {
350
- release = resolve;
351
- });
352
- const tail = previous.then(() => current);
353
- managedAgentMutationTails.set(agentId, tail);
354
- await previous;
355
- try {
356
- return await fn();
357
- }
358
- finally {
359
- release();
360
- if (managedAgentMutationTails.get(agentId) === tail) {
361
- managedAgentMutationTails.delete(agentId);
362
- }
363
- }
364
- }
365
310
  function sanitizeCommandForAudit(command) {
366
311
  const commandHash = (0, crypto_1.createHash)('sha256').update(command).digest('hex');
367
312
  const commandPreview = command
@@ -414,7 +359,6 @@ class GatewayToolExecutor {
414
359
  discordGateway = null;
415
360
  slackGateway = null;
416
361
  telegramGateway = null;
417
- browserTool;
418
362
  roleManager;
419
363
  executionContextStorage = new async_hooks_1.AsyncLocalStorage();
420
364
  envelopeEnforcer = new index_js_1.EnvelopeEnforcer();
@@ -463,18 +407,6 @@ class GatewayToolExecutor {
463
407
  * The setter stays because its callers are live - delete both together when a second
464
408
  * reader appears or the callers do not. */
465
409
  setRawStore(_store) { }
466
- uiCommandQueue = null;
467
- setUICommandQueue(queue) {
468
- this.uiCommandQueue = queue;
469
- }
470
- applyMultiAgentConfig = null;
471
- setApplyMultiAgentConfig(fn) {
472
- this.applyMultiAgentConfig = fn;
473
- }
474
- restartMultiAgentAgent = null;
475
- setRestartMultiAgentAgent(fn) {
476
- this.restartMultiAgentAgent = fn;
477
- }
478
410
  /** Same as setRawStore: the only reader was the delegation executor. */
479
411
  setValidationService(_svc) { }
480
412
  setMemoryAgent(processManager) {
@@ -555,15 +487,6 @@ class GatewayToolExecutor {
555
487
  getActiveContext() {
556
488
  return this.getExecutionState().agentContext;
557
489
  }
558
- /** Who and where the active turn is. Was typed by the delegation executor; now local. */
559
- getActiveRouting() {
560
- const state = this.getExecutionState();
561
- return {
562
- agentId: state.agentId,
563
- source: state.source,
564
- channelId: state.channelId,
565
- };
566
- }
567
490
  async withExecutionContext(executionContext, fn) {
568
491
  if (!executionContext) {
569
492
  return fn();
@@ -613,74 +536,6 @@ class GatewayToolExecutor {
613
536
  setDisallowedGatewayTools(tools) {
614
537
  this.disallowedGatewayTools = new Set(tools);
615
538
  }
616
- getPreferredViewerAgentTab() {
617
- if (!this.uiCommandQueue) {
618
- return 'activity';
619
- }
620
- const { channelId } = this.getActiveRouting();
621
- const currentPageContext = this.uiCommandQueue.getPageContext(channelId || undefined);
622
- if (!currentPageContext || currentPageContext.currentRoute !== 'agents') {
623
- return 'activity';
624
- }
625
- const pageData = currentPageContext.pageData;
626
- const activeTab = pageData?.activeTab;
627
- if (typeof activeTab === 'string' && AGENT_DETAIL_TABS.has(activeTab)) {
628
- return activeTab;
629
- }
630
- return 'activity';
631
- }
632
- syncViewerToAgentDetail(agentId, preferredTab) {
633
- if (!this.uiCommandQueue) {
634
- return;
635
- }
636
- const { source, channelId } = this.getActiveRouting();
637
- if (source !== 'viewer') {
638
- return;
639
- }
640
- const desiredTab = preferredTab && AGENT_DETAIL_TABS.has(preferredTab)
641
- ? preferredTab
642
- : this.getPreferredViewerAgentTab();
643
- const currentPageContext = this.uiCommandQueue.getPageContext(channelId || undefined);
644
- const currentPageData = currentPageContext?.pageData;
645
- if (currentPageContext?.currentRoute === 'agents' &&
646
- currentPageContext.selectedItem?.type === 'agent' &&
647
- currentPageContext.selectedItem.id === agentId &&
648
- currentPageData?.pageType === 'agent-detail' &&
649
- currentPageData?.activeTab === desiredTab) {
650
- return;
651
- }
652
- this.uiCommandQueue.push({
653
- type: 'navigate',
654
- payload: {
655
- route: 'agents',
656
- params: {
657
- id: agentId,
658
- tab: desiredTab,
659
- },
660
- },
661
- });
662
- }
663
- resolveManagedAgentId(agentId) {
664
- if (!this.sessionsDb) {
665
- return agentId;
666
- }
667
- const normalized = agentId
668
- .trim()
669
- .toLowerCase()
670
- .replace(/[_\s]+/g, '-');
671
- const candidates = Array.from(new Set([
672
- agentId,
673
- agentId.trim(),
674
- normalized,
675
- normalized.endsWith('-agent') ? normalized.slice(0, -6) : `${normalized}-agent`,
676
- ])).filter(Boolean);
677
- for (const candidate of candidates) {
678
- if ((0, agent_store_js_1.getLatestVersion)(this.sessionsDb, candidate)) {
679
- return candidate;
680
- }
681
- }
682
- return agentId;
683
- }
684
539
  setReportPublisher(fn) {
685
540
  this.reportPublisher = fn;
686
541
  }
@@ -733,9 +588,6 @@ class GatewayToolExecutor {
733
588
  };
734
589
  });
735
590
  this.wikiPublishAdapter = options.wikiPublishAdapter ?? null;
736
- this.browserTool = (0, browser_tool_js_1.getBrowserTool)({
737
- screenshotDir: (0, path_1.join)(privateWorkspaceRoot, 'media', 'outbound'),
738
- });
739
591
  // Pass rolesConfig from config.yaml to RoleManager
740
592
  this.roleManager = (0, role_manager_js_1.getRoleManager)(options.rolesConfig ? { rolesConfig: options.rolesConfig } : undefined);
741
593
  if (options.mamaApi) {
@@ -839,8 +691,6 @@ class GatewayToolExecutor {
839
691
  listDecisions: mama.list.bind(mama), // Note: mama exports listDecisions as 'list'
840
692
  suggest: mama.suggest.bind(mama),
841
693
  recallMemory: mama.recallMemory?.bind(mama),
842
- ingestMemory: mama.ingestMemory?.bind(mama),
843
- ingestWithTrustedProvenance: mama.ingestWithTrustedProvenance?.bind(mama),
844
694
  saveMemoryWithTrustedProvenance: mama.saveMemoryWithTrustedProvenance?.bind(mama),
845
695
  ingestConversationWithTrustedProvenance: mama.ingestConversationWithTrustedProvenance?.bind(mama),
846
696
  getMemoryProvenance: mama.getMemoryProvenance?.bind(mama),
@@ -1249,7 +1099,7 @@ class GatewayToolExecutor {
1249
1099
  return { requestedScopes: null, envelopeScopesSnapshot: null, mismatch: 0 };
1250
1100
  }
1251
1101
  const envelopeScopesSnapshot = ctx?.envelope?.scope.memory_scopes ?? null;
1252
- const requestedScopes = this.resolveAuditMemoryScopes(toolName, input, ctx);
1102
+ const requestedScopes = this.resolveAuditMemoryScopes(toolName, input);
1253
1103
  if (!ctx?.envelope || !envelopeScopesSnapshot) {
1254
1104
  return { requestedScopes, envelopeScopesSnapshot, mismatch: 0 };
1255
1105
  }
@@ -1271,7 +1121,7 @@ class GatewayToolExecutor {
1271
1121
  mismatch: hasOutOfEnvelopeScope ? 1 : 0,
1272
1122
  };
1273
1123
  }
1274
- resolveAuditMemoryScopes(toolName, input, ctx) {
1124
+ resolveAuditMemoryScopes(toolName, input) {
1275
1125
  if (toolName === 'mama_save') {
1276
1126
  return normalizeMemoryScopes(input.scopes);
1277
1127
  }
@@ -1284,17 +1134,6 @@ class GatewayToolExecutor {
1284
1134
  if (toolName === 'mama_update') {
1285
1135
  return null;
1286
1136
  }
1287
- if (toolName === 'mama_add') {
1288
- return this.deriveMemoryScopesFromActiveContext(ctx);
1289
- }
1290
- if (toolName === 'mama_ingest') {
1291
- const fallbackScopes = this.deriveMemoryScopesFromActiveContext(ctx) ?? [];
1292
- const inputScopes = normalizeMemoryScopes(input.scopes);
1293
- if (inputScopes && inputScopes.length > 0) {
1294
- return inputScopes;
1295
- }
1296
- return fallbackScopes.length > 0 ? fallbackScopes : null;
1297
- }
1298
1137
  return null;
1299
1138
  }
1300
1139
  deriveMemoryScopesFromActiveContext(ctx) {
@@ -1446,9 +1285,6 @@ class GatewayToolExecutor {
1446
1285
  contextPacketScopes,
1447
1286
  };
1448
1287
  }
1449
- supportsTrustedIngest(api) {
1450
- return Boolean(api.ingestWithTrustedProvenance);
1451
- }
1452
1288
  supportsTrustedSave(api) {
1453
1289
  return Boolean(api.saveWithTrustedProvenance);
1454
1290
  }
@@ -1549,7 +1385,7 @@ class GatewayToolExecutor {
1549
1385
  if (activeDisallowed?.includes(toolName) || this.disallowedGatewayTools.has(toolName)) {
1550
1386
  return {
1551
1387
  success: false,
1552
- error: `Tool "${toolName}" is not available. Use delegate() to assign this work to the appropriate sub-agent.`,
1388
+ error: `Tool "${toolName}" is not available to this run.`,
1553
1389
  };
1554
1390
  }
1555
1391
  const shouldCheckPermissionBeforeEnvelope = MEMORY_READ_PERMISSION_BEFORE_ENVELOPE_TOOLS.has(toolName) &&
@@ -1578,6 +1414,8 @@ class GatewayToolExecutor {
1578
1414
  }
1579
1415
  }
1580
1416
  try {
1417
+ // Lazy MAMA API init — only for tools that need it
1418
+ const getApi = () => this.initializeMAMAApi();
1581
1419
  // Handle non-MAMA tools first
1582
1420
  switch (toolName) {
1583
1421
  case 'Read':
@@ -1669,46 +1507,8 @@ class GatewayToolExecutor {
1669
1507
  result: (0, drive_tools_js_1.asUntrustedDriveEvidence)(await this.driveTools.upload(input)),
1670
1508
  };
1671
1509
  // Browser tools
1672
- case 'browser_navigate':
1673
- return await this.executeBrowserNavigate(input);
1674
- case 'browser_screenshot':
1675
- return await this.executeBrowserScreenshot(input);
1676
- case 'browser_click':
1677
- return await this.executeBrowserClick(input);
1678
- case 'browser_type':
1679
- return await this.executeBrowserType(input);
1680
- case 'browser_get_text':
1681
- return await this.executeBrowserGetText();
1682
- case 'browser_scroll':
1683
- return await this.executeBrowserScroll(input);
1684
- case 'browser_wait_for':
1685
- return await this.executeBrowserWaitFor(input);
1686
- case 'browser_evaluate':
1687
- return await this.executeBrowserEvaluate(input);
1688
- case 'browser_pdf':
1689
- return await this.executeBrowserPdf(input);
1690
- case 'browser_close':
1691
- return await this.executeBrowserClose();
1692
- // OS Management tools (viewer-only)
1693
- case 'os_add_bot':
1694
- return await this.executeAddBot(input);
1695
- case 'os_set_permissions':
1696
- return await this.executeSetPermissions(input);
1697
1510
  case 'os_get_config':
1698
1511
  return await this.executeGetConfig(input);
1699
- case 'os_set_model':
1700
- return await this.executeSetModel(input);
1701
- // OS Monitoring tools
1702
- case 'os_list_bots':
1703
- return await this.executeListBots(input);
1704
- case 'os_restart_bot':
1705
- return await this.executeRestartBot(input);
1706
- case 'os_stop_bot':
1707
- return await this.executeStopBot(input);
1708
- // PR Review tools
1709
- case 'pr_review_threads':
1710
- return await this.executePrReviewThreads(input);
1711
- // Webchat tools
1712
1512
  case 'webchat_send':
1713
1513
  return await this.executeWebchatSend(input // session_id omitted: all files use shared outbound dir
1714
1514
  );
@@ -1718,207 +1518,6 @@ class GatewayToolExecutor {
1718
1518
  // Obsidian vault management via CLI
1719
1519
  case 'obsidian':
1720
1520
  return await this.executeObsidian(input);
1721
- // Agent management tools (Managed Agents pattern)
1722
- case 'agent_get': {
1723
- if (!this.sessionsDb) {
1724
- return { success: false, error: 'Sessions DB not available' };
1725
- }
1726
- const permError = this.checkViewerOnly();
1727
- if (permError) {
1728
- return { success: false, error: permError };
1729
- }
1730
- const agentId = this.resolveManagedAgentId(input.agent_id);
1731
- const latestVer = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
1732
- if (!latestVer) {
1733
- return { success: false, error: `Agent '${agentId}' not found` };
1734
- }
1735
- this.syncViewerToAgentDetail(agentId);
1736
- return {
1737
- success: true,
1738
- agent_id: latestVer.agent_id,
1739
- version: latestVer.version,
1740
- config: JSON.parse(latestVer.snapshot),
1741
- system: latestVer.persona_text,
1742
- change_note: latestVer.change_note,
1743
- created_at: latestVer.created_at,
1744
- };
1745
- }
1746
- case 'agent_activity': {
1747
- if (!this.sessionsDb) {
1748
- return { success: false, error: 'Sessions DB not available' };
1749
- }
1750
- const permError = this.checkViewerOnly();
1751
- if (permError) {
1752
- return { success: false, error: permError };
1753
- }
1754
- const args = input;
1755
- const agentId = this.resolveManagedAgentId(args.agent_id);
1756
- const rawLimit = Number.parseInt(String(args.limit ?? 20), 10);
1757
- const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 100) : 20;
1758
- const latestVer = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
1759
- if (!latestVer) {
1760
- return { success: false, error: `Agent '${args.agent_id}' not found` };
1761
- }
1762
- this.syncViewerToAgentDetail(agentId, 'activity');
1763
- return {
1764
- success: true,
1765
- agent_id: agentId,
1766
- activity: (0, agent_store_js_1.getActivity)(this.sessionsDb, agentId, limit, {
1767
- includeTrace: args.include_trace === true,
1768
- }),
1769
- };
1770
- }
1771
- case 'agent_update': {
1772
- if (!this.sessionsDb) {
1773
- return { success: false, error: 'Sessions DB not available' };
1774
- }
1775
- const permError = this.checkViewerOnly();
1776
- if (permError) {
1777
- return { success: false, error: permError };
1778
- }
1779
- const updateArgs = input;
1780
- const updateError = (0, managed_agent_validation_js_1.validateManagedAgentChanges)(updateArgs.changes);
1781
- if (updateError) {
1782
- return { success: false, error: updateError };
1783
- }
1784
- const agentId = this.resolveManagedAgentId(updateArgs.agent_id);
1785
- const initialLatest = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
1786
- if (!initialLatest) {
1787
- return { success: false, error: `Agent '${updateArgs.agent_id}' not found` };
1788
- }
1789
- return withManagedAgentMutationLock(agentId, async () => {
1790
- const updateLatest = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
1791
- if (!updateLatest) {
1792
- return { success: false, error: `Agent '${updateArgs.agent_id}' not found` };
1793
- }
1794
- if (updateLatest.version !== updateArgs.version) {
1795
- return {
1796
- success: false,
1797
- error: `Version conflict: current v${updateLatest.version}, sent v${updateArgs.version}`,
1798
- };
1799
- }
1800
- const synced = await (0, managed_agent_runtime_sync_js_1.updateManagedAgentRuntime)({
1801
- agentId,
1802
- changes: updateArgs.changes,
1803
- }, {
1804
- loadConfig: config_manager_js_1.loadConfig,
1805
- saveConfig: config_manager_js_1.saveConfig,
1806
- applyMultiAgentConfig: this.applyMultiAgentConfig,
1807
- restartMultiAgentAgent: this.restartMultiAgentAgent,
1808
- });
1809
- const updatedV = (0, agent_store_js_1.createAgentVersion)(this.sessionsDb, {
1810
- agent_id: agentId,
1811
- snapshot: synced.snapshot,
1812
- persona_text: synced.personaText ?? updateLatest.persona_text,
1813
- change_note: updateArgs.change_note,
1814
- });
1815
- return {
1816
- success: true,
1817
- new_version: updatedV.version,
1818
- runtime_reloaded: synced.runtimeReloaded,
1819
- };
1820
- });
1821
- }
1822
- case 'agent_create': {
1823
- if (!this.sessionsDb) {
1824
- return { success: false, error: 'Sessions DB not available' };
1825
- }
1826
- const permError = this.checkViewerOnly();
1827
- if (permError) {
1828
- return { success: false, error: permError };
1829
- }
1830
- const createArgs = input;
1831
- const createError = (0, managed_agent_validation_js_1.validateManagedAgentCreateInput)(createArgs);
1832
- if (createError) {
1833
- return { success: false, error: createError };
1834
- }
1835
- return withManagedAgentMutationLock(createArgs.id, async () => {
1836
- const existingAgent = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, createArgs.id);
1837
- if (existingAgent) {
1838
- return { success: false, error: `Agent '${createArgs.id}' already exists` };
1839
- }
1840
- const synced = await (0, managed_agent_runtime_sync_js_1.createManagedAgentRuntime)({
1841
- id: createArgs.id,
1842
- name: createArgs.name,
1843
- model: createArgs.model,
1844
- tier: createArgs.tier,
1845
- backend: createArgs.backend,
1846
- system: createArgs.system,
1847
- }, {
1848
- loadConfig: config_manager_js_1.loadConfig,
1849
- saveConfig: config_manager_js_1.saveConfig,
1850
- applyMultiAgentConfig: this.applyMultiAgentConfig,
1851
- restartMultiAgentAgent: this.restartMultiAgentAgent,
1852
- });
1853
- const createdV = (0, agent_store_js_1.createAgentVersion)(this.sessionsDb, {
1854
- agent_id: createArgs.id,
1855
- snapshot: synced.snapshot,
1856
- persona_text: synced.personaText,
1857
- change_note: 'Created via agent_create tool',
1858
- });
1859
- return {
1860
- success: true,
1861
- id: createArgs.id,
1862
- version: createdV.version,
1863
- runtime_reloaded: synced.runtimeReloaded,
1864
- };
1865
- });
1866
- }
1867
- case 'agent_compare': {
1868
- if (!this.sessionsDb) {
1869
- return { success: false, error: 'Sessions DB not available' };
1870
- }
1871
- const permError = this.checkViewerOnly();
1872
- if (permError) {
1873
- return { success: false, error: permError };
1874
- }
1875
- const cmpArgs = input;
1876
- const agentId = this.resolveManagedAgentId(cmpArgs.agent_id);
1877
- const cmpResult = (0, agent_store_js_1.compareVersionMetrics)(this.sessionsDb, agentId, cmpArgs.version_a, cmpArgs.version_b);
1878
- this.syncViewerToAgentDetail(agentId, 'validation');
1879
- return { success: true, agent_id: agentId, ...cmpResult };
1880
- }
1881
- // Viewer control tools (SmartStore pattern)
1882
- case 'viewer_state': {
1883
- if (!this.uiCommandQueue) {
1884
- return { success: false, error: 'UI command queue not available' };
1885
- }
1886
- const permError = this.checkViewerOnly();
1887
- if (permError) {
1888
- return { success: false, error: permError };
1889
- }
1890
- const { channelId } = this.getActiveRouting();
1891
- const ctx = this.uiCommandQueue.getPageContext(channelId || undefined);
1892
- return { success: true, context: ctx || { currentRoute: 'unknown', pageData: null } };
1893
- }
1894
- case 'viewer_navigate': {
1895
- if (!this.uiCommandQueue) {
1896
- return { success: false, error: 'UI command queue not available' };
1897
- }
1898
- const permError = this.checkViewerOnly();
1899
- if (permError) {
1900
- return { success: false, error: permError };
1901
- }
1902
- const navArgs = input;
1903
- this.uiCommandQueue.push({ type: 'navigate', payload: navArgs });
1904
- return { success: true, navigated: navArgs.route };
1905
- }
1906
- case 'viewer_notify': {
1907
- if (!this.uiCommandQueue) {
1908
- return { success: false, error: 'UI command queue not available' };
1909
- }
1910
- const permError = this.checkViewerOnly();
1911
- if (permError) {
1912
- return { success: false, error: permError };
1913
- }
1914
- const args = input;
1915
- this.uiCommandQueue.push({ type: 'notify', payload: args });
1916
- return { success: true, notified: true };
1917
- }
1918
- }
1919
- // Lazy MAMA API init — only for tools that need it
1920
- const getApi = () => this.initializeMAMAApi();
1921
- switch (toolName) {
1922
1521
  case 'mama_save': {
1923
1522
  const saveInput = input;
1924
1523
  // Secret inviolability (plan v6 S1-T7): a secret saved as a
@@ -2016,36 +1615,6 @@ class GatewayToolExecutor {
2016
1615
  }
2017
1616
  case 'mama_load_checkpoint':
2018
1617
  return await (0, mama_tool_handlers_js_1.handleLoadCheckpoint)(await getApi(), input);
2019
- case 'mama_add': {
2020
- const addSecretScan = (0, secret_filter_js_1.scanMemoryWriteInput)(input);
2021
- if (!addSecretScan.clean) {
2022
- console.warn(`[Security] mama_add refused: secret-shaped content (${addSecretScan.matches.join(', ')})`);
2023
- return {
2024
- success: false,
2025
- code: 'secret_material_refused',
2026
- error: `Refusing to add: content matches secret pattern(s): ${addSecretScan.matches.join(', ')}. Secrets must never enter memory.`,
2027
- };
2028
- }
2029
- const api = await getApi();
2030
- return await this.handleMamaAdd(input, this.supportsTrustedIngest(api)
2031
- ? (await this.buildTrustedMemoryWriteOptions('mama_add', gatewayCallId)).options
2032
- : undefined);
2033
- }
2034
- case 'mama_ingest': {
2035
- const ingestSecretScan = (0, secret_filter_js_1.scanMemoryWriteInput)(input);
2036
- if (!ingestSecretScan.clean) {
2037
- console.warn(`[Security] mama_ingest refused: secret-shaped content (${ingestSecretScan.matches.join(', ')})`);
2038
- return {
2039
- success: false,
2040
- code: 'secret_material_refused',
2041
- error: `Refusing to ingest: content matches secret pattern(s): ${ingestSecretScan.matches.join(', ')}. Secrets must never enter memory.`,
2042
- };
2043
- }
2044
- const api = await getApi();
2045
- return await this.handleMamaIngest(input, this.supportsTrustedIngest(api)
2046
- ? (await this.buildTrustedMemoryWriteOptions('mama_ingest', gatewayCallId)).options
2047
- : undefined);
2048
- }
2049
1618
  case 'report_publish': {
2050
1619
  const slotsInput = input.slots;
2051
1620
  if (!slotsInput || typeof slotsInput !== 'object') {
@@ -2921,282 +2490,6 @@ class GatewayToolExecutor {
2921
2490
  return { success: false, error: `Failed to send to Telegram: ${err}` };
2922
2491
  }
2923
2492
  }
2924
- // ============================================================================
2925
- // Browser Tool Execution
2926
- // ============================================================================
2927
- /**
2928
- * Navigate to a URL
2929
- */
2930
- async executeBrowserNavigate(input) {
2931
- try {
2932
- const result = await this.browserTool.navigate(input.url);
2933
- return { success: true, title: result.title, url: result.url };
2934
- }
2935
- catch (err) {
2936
- return { success: false, error: `Navigation failed: ${err}` };
2937
- }
2938
- }
2939
- /**
2940
- * Take a screenshot
2941
- */
2942
- async executeBrowserScreenshot(input) {
2943
- try {
2944
- const result = input.full_page
2945
- ? await this.browserTool.screenshotFullPage(input.filename, this.getExecutionState().signal)
2946
- : await this.browserTool.screenshot(input.filename, this.getExecutionState().signal);
2947
- return { success: true, path: result.path };
2948
- }
2949
- catch (err) {
2950
- return { success: false, error: `Screenshot failed: ${err}` };
2951
- }
2952
- }
2953
- /**
2954
- * Click an element
2955
- */
2956
- async executeBrowserClick(input) {
2957
- try {
2958
- await this.browserTool.click(input.selector);
2959
- return { success: true };
2960
- }
2961
- catch (err) {
2962
- return { success: false, error: `Click failed: ${err}` };
2963
- }
2964
- }
2965
- /**
2966
- * Type text into an element
2967
- */
2968
- async executeBrowserType(input) {
2969
- try {
2970
- await this.browserTool.type(input.selector, input.text);
2971
- return { success: true };
2972
- }
2973
- catch (err) {
2974
- return { success: false, error: `Type failed: ${err}` };
2975
- }
2976
- }
2977
- /**
2978
- * Get page text content
2979
- */
2980
- async executeBrowserGetText() {
2981
- try {
2982
- const result = await this.browserTool.getText();
2983
- return { success: true, text: result.text };
2984
- }
2985
- catch (err) {
2986
- return { success: false, error: `Get text failed: ${err}` };
2987
- }
2988
- }
2989
- /**
2990
- * Scroll the page
2991
- */
2992
- async executeBrowserScroll(input) {
2993
- try {
2994
- await this.browserTool.scroll(input.direction, input.amount);
2995
- return { success: true };
2996
- }
2997
- catch (err) {
2998
- return { success: false, error: `Scroll failed: ${err}` };
2999
- }
3000
- }
3001
- /**
3002
- * Wait for element
3003
- */
3004
- async executeBrowserWaitFor(input) {
3005
- try {
3006
- await this.browserTool.waitFor(input.selector, input.timeout);
3007
- return { success: true };
3008
- }
3009
- catch (err) {
3010
- return { success: false, error: `Wait failed: ${err}` };
3011
- }
3012
- }
3013
- /**
3014
- * Evaluate JavaScript in page
3015
- */
3016
- async executeBrowserEvaluate(input) {
3017
- try {
3018
- const result = await this.browserTool.evaluate(input.script);
3019
- return { success: true, result: result.result };
3020
- }
3021
- catch (err) {
3022
- return { success: false, error: `Evaluate failed: ${err}` };
3023
- }
3024
- }
3025
- /**
3026
- * Generate PDF of page
3027
- */
3028
- async executeBrowserPdf(input) {
3029
- try {
3030
- const result = await this.browserTool.pdf(input.filename);
3031
- return { success: true, path: result.path };
3032
- }
3033
- catch (err) {
3034
- return { success: false, error: `PDF failed: ${err}` };
3035
- }
3036
- }
3037
- /**
3038
- * Close the browser
3039
- */
3040
- async executeBrowserClose() {
3041
- try {
3042
- await this.browserTool.close();
3043
- return { success: true };
3044
- }
3045
- catch (err) {
3046
- return { success: false, error: `Close failed: ${err}` };
3047
- }
3048
- }
3049
- // ============================================================================
3050
- // OS Management Tools (viewer-only)
3051
- // ============================================================================
3052
- /**
3053
- * Check if current context is from viewer (OS agent)
3054
- * Returns error message if not allowed
3055
- */
3056
- checkViewerOnly() {
3057
- const context = this.getActiveContext();
3058
- if (!context) {
3059
- // No context = backward compatibility, allow
3060
- return null;
3061
- }
3062
- if (context.source !== 'viewer') {
3063
- return `Permission denied: This operation is only available from MAMA OS Viewer. Current source: ${context.source}`;
3064
- }
3065
- if (!context.role.systemControl) {
3066
- return `Permission denied: Role "${context.roleName}" does not have system control permissions`;
3067
- }
3068
- return null;
3069
- }
3070
- /**
3071
- * Execute os_add_bot tool - Add a new bot to config
3072
- * Viewer-only: requires systemControl permission
3073
- */
3074
- async executeAddBot(input) {
3075
- // Check viewer-only permission
3076
- const permError = this.checkViewerOnly();
3077
- if (permError) {
3078
- return { success: false, error: permError };
3079
- }
3080
- const { platform, token, bot_token, app_token, default_channel_id, allowed_chats, room_ids } = input;
3081
- if (!platform) {
3082
- return { success: false, error: 'Platform is required (discord, telegram, slack, chatwork)' };
3083
- }
3084
- try {
3085
- const config = await (0, config_manager_js_1.loadConfig)();
3086
- switch (platform) {
3087
- case 'discord':
3088
- if (!token) {
3089
- return { success: false, error: 'Discord bot token is required' };
3090
- }
3091
- config.discord = {
3092
- enabled: true,
3093
- token,
3094
- default_channel_id,
3095
- };
3096
- break;
3097
- case 'telegram':
3098
- if (!token) {
3099
- return { success: false, error: 'Telegram bot token is required' };
3100
- }
3101
- config.telegram = {
3102
- enabled: true,
3103
- token,
3104
- allowed_chats,
3105
- };
3106
- break;
3107
- case 'slack':
3108
- if (!bot_token || !app_token) {
3109
- return { success: false, error: 'Slack requires both bot_token and app_token' };
3110
- }
3111
- config.slack = {
3112
- enabled: true,
3113
- bot_token,
3114
- app_token,
3115
- };
3116
- break;
3117
- case 'chatwork':
3118
- if (!token) {
3119
- return { success: false, error: 'Chatwork API token is required' };
3120
- }
3121
- config.chatwork = {
3122
- enabled: true,
3123
- api_token: token,
3124
- room_ids,
3125
- };
3126
- break;
3127
- default:
3128
- return { success: false, error: `Unknown platform: ${platform}` };
3129
- }
3130
- await (0, config_manager_js_1.saveConfig)(config);
3131
- return {
3132
- success: true,
3133
- message: `${platform} bot added successfully. Restart MAMA to apply changes.`,
3134
- };
3135
- }
3136
- catch (err) {
3137
- return { success: false, error: `Failed to add bot: ${err}` };
3138
- }
3139
- }
3140
- /**
3141
- * Execute os_set_permissions tool - Modify role permissions
3142
- * Viewer-only: requires systemControl permission
3143
- */
3144
- async executeSetPermissions(input) {
3145
- // Check viewer-only permission
3146
- const permError = this.checkViewerOnly();
3147
- if (permError) {
3148
- return { success: false, error: permError };
3149
- }
3150
- const { role, allowedTools, blockedTools, allowedPaths, systemControl, sensitiveAccess, mapSource, } = input;
3151
- if (!role) {
3152
- return { success: false, error: 'Role name is required' };
3153
- }
3154
- try {
3155
- const config = await (0, config_manager_js_1.loadConfig)();
3156
- // Initialize roles if not present
3157
- if (!config.roles) {
3158
- config.roles = { ...types_js_2.DEFAULT_ROLES };
3159
- }
3160
- // Get or create role definition
3161
- const existingRole = config.roles.definitions[role] || {
3162
- allowedTools: ['mama_*', 'Read'],
3163
- };
3164
- // Update role properties
3165
- const updatedRole = {
3166
- allowedTools: allowedTools ?? existingRole.allowedTools,
3167
- blockedTools: blockedTools ?? existingRole.blockedTools,
3168
- allowedPaths: allowedPaths ?? existingRole.allowedPaths,
3169
- systemControl: systemControl ?? existingRole.systemControl,
3170
- sensitiveAccess: sensitiveAccess ?? existingRole.sensitiveAccess,
3171
- };
3172
- // Clean up undefined values
3173
- if (!updatedRole.blockedTools?.length)
3174
- delete updatedRole.blockedTools;
3175
- if (!updatedRole.allowedPaths?.length)
3176
- delete updatedRole.allowedPaths;
3177
- if (updatedRole.systemControl === undefined)
3178
- delete updatedRole.systemControl;
3179
- if (updatedRole.sensitiveAccess === undefined)
3180
- delete updatedRole.sensitiveAccess;
3181
- config.roles.definitions[role] = updatedRole;
3182
- // Map source to role if specified
3183
- if (mapSource) {
3184
- config.roles.sourceMapping[mapSource] = role;
3185
- }
3186
- await (0, config_manager_js_1.saveConfig)(config);
3187
- // Update RoleManager with new config (trust anchor included: telegram
3188
- // allowed_chats may have changed alongside roles in the saved config)
3189
- this.roleManager.updateRolesConfig(config.roles);
3190
- this.roleManager.setTelegramTrust(config.telegram?.allowed_chats);
3191
- return {
3192
- success: true,
3193
- message: `Role "${role}" updated successfully.${mapSource ? ` Source "${mapSource}" now maps to this role.` : ''}`,
3194
- };
3195
- }
3196
- catch (err) {
3197
- return { success: false, error: `Failed to set permissions: ${err}` };
3198
- }
3199
- }
3200
2493
  /**
3201
2494
  * Execute os_get_config tool - Get current configuration
3202
2495
  * Masks sensitive data for non-viewer sources
@@ -3244,7 +2537,19 @@ class GatewayToolExecutor {
3244
2537
  // Show only length hint for debugging without revealing content
3245
2538
  masked[key] = `***[${value.length} chars]***`;
3246
2539
  }
3247
- else if (typeof value === 'object' && !Array.isArray(value)) {
2540
+ else if (isSensitive && typeof value !== 'object') {
2541
+ // Non-string sensitive scalars (numbers, booleans) must not pass
2542
+ // through either - the key marked them secret.
2543
+ masked[key] = '***';
2544
+ }
2545
+ else if (Array.isArray(value)) {
2546
+ // Arrays must be descended: a token inside multi_agent.agents[] or a
2547
+ // bots[] entry would otherwise return in clear text (review).
2548
+ masked[key] = value.map((item) => item !== null && typeof item === 'object' && !Array.isArray(item)
2549
+ ? this.maskSensitiveData(item, showSensitive)
2550
+ : item);
2551
+ }
2552
+ else if (typeof value === 'object') {
3248
2553
  masked[key] = this.maskSensitiveData(value, showSensitive);
3249
2554
  }
3250
2555
  else {
@@ -3253,361 +2558,6 @@ class GatewayToolExecutor {
3253
2558
  }
3254
2559
  return masked;
3255
2560
  }
3256
- /**
3257
- * Execute os_set_model tool - Set model configuration for a role or globally
3258
- * Viewer-only: requires systemControl permission
3259
- *
3260
- * Usage:
3261
- * - Set role-specific model: { role: 'chat_bot', model: 'claude-3-haiku-20240307' }
3262
- * - Set global model: { model: 'claude-sonnet-4-6' }
3263
- */
3264
- async executeSetModel(input) {
3265
- // Check viewer-only permission
3266
- const permError = this.checkViewerOnly();
3267
- if (permError) {
3268
- return { success: false, error: permError };
3269
- }
3270
- const { role, model, maxTurns, timeout } = input;
3271
- if (!model) {
3272
- return { success: false, error: 'Model name is required' };
3273
- }
3274
- // Validate model name format - Claude/Anthropic models only
3275
- // Valid formats per Anthropic API docs:
3276
- // - Pinned snapshots: claude-sonnet-4-20250514, claude-3-5-sonnet-20241022
3277
- // - Rolling aliases: claude-3-7-sonnet-latest, claude-opus-4-latest
3278
- // - Family aliases: claude-opus-4-5, claude-sonnet-4-0
3279
- const claudeModelPattern = /^claude-(?:opus|sonnet|haiku|3|3-5|3-7)-?[a-z0-9-]*(?:-\d{8}|-latest)?$/i;
3280
- const isValidModel = claudeModelPattern.test(model);
3281
- if (!isValidModel) {
3282
- return {
3283
- success: false,
3284
- error: `Invalid model name format: ${model}. Expected Claude model format (e.g., claude-sonnet-4-6, claude-opus-4-latest)`,
3285
- };
3286
- }
3287
- if (maxTurns !== undefined && (maxTurns < 1 || maxTurns > 100)) {
3288
- return { success: false, error: 'maxTurns must be between 1 and 100' };
3289
- }
3290
- if (timeout !== undefined && (timeout < 10000 || timeout > 600000)) {
3291
- return { success: false, error: 'timeout must be between 10000ms and 600000ms (10s-10min)' };
3292
- }
3293
- try {
3294
- const config = await (0, config_manager_js_1.loadConfig)();
3295
- // If role is specified, update that role's model
3296
- if (role) {
3297
- // Initialize roles if not present
3298
- if (!config.roles) {
3299
- config.roles = { ...types_js_2.DEFAULT_ROLES };
3300
- }
3301
- // Check if role exists
3302
- if (!config.roles.definitions[role]) {
3303
- return {
3304
- success: false,
3305
- error: `Role "${role}" not found. Available roles: ${Object.keys(config.roles.definitions).join(', ')}`,
3306
- };
3307
- }
3308
- // Update role-specific settings
3309
- config.roles.definitions[role].model = model;
3310
- if (maxTurns !== undefined) {
3311
- config.roles.definitions[role].maxTurns = maxTurns;
3312
- }
3313
- await (0, config_manager_js_1.saveConfig)(config);
3314
- // Update RoleManager with new config (trust anchor included)
3315
- this.roleManager.updateRolesConfig(config.roles);
3316
- this.roleManager.setTelegramTrust(config.telegram?.allowed_chats);
3317
- const changes = [`model: ${model}`];
3318
- if (maxTurns !== undefined)
3319
- changes.push(`maxTurns: ${maxTurns}`);
3320
- return {
3321
- success: true,
3322
- message: `Role "${role}" updated: ${changes.join(', ')}. New conversations for this role will use these settings.`,
3323
- };
3324
- }
3325
- // No role specified - update global agent config
3326
- if (!config.agent) {
3327
- config.agent = {
3328
- backend: 'claude',
3329
- model: 'claude-sonnet-4-6',
3330
- max_turns: 10,
3331
- timeout: 300000,
3332
- };
3333
- }
3334
- config.agent.model = model;
3335
- if (maxTurns !== undefined) {
3336
- config.agent.max_turns = maxTurns;
3337
- }
3338
- if (timeout !== undefined) {
3339
- config.agent.timeout = timeout;
3340
- }
3341
- await (0, config_manager_js_1.saveConfig)(config);
3342
- const changes = [`model: ${model}`];
3343
- if (maxTurns !== undefined)
3344
- changes.push(`maxTurns: ${maxTurns}`);
3345
- if (timeout !== undefined)
3346
- changes.push(`timeout: ${timeout}ms`);
3347
- return {
3348
- success: true,
3349
- message: `Global agent settings updated: ${changes.join(', ')}. New conversations will use these settings.`,
3350
- };
3351
- }
3352
- catch (err) {
3353
- return { success: false, error: `Failed to set model: ${err}` };
3354
- }
3355
- }
3356
- // ============================================================================
3357
- // OS Monitoring Tools (viewer-only)
3358
- // ============================================================================
3359
- /**
3360
- * Callback to get bot status from running gateways
3361
- * Set by the main application when gateways are initialized
3362
- */
3363
- botStatusCallback = null;
3364
- /**
3365
- * Callback to control bots
3366
- * Set by the main application when gateways are initialized
3367
- */
3368
- botControlCallback = null;
3369
- /**
3370
- * Set the bot status callback (called by main app)
3371
- */
3372
- setBotStatusCallback(callback) {
3373
- this.botStatusCallback = callback;
3374
- }
3375
- /**
3376
- * Set the bot control callback (called by main app)
3377
- */
3378
- setBotControlCallback(callback) {
3379
- this.botControlCallback = callback;
3380
- }
3381
- /**
3382
- * Execute os_list_bots tool - List all configured bots and their status
3383
- */
3384
- async executeListBots(input) {
3385
- const { platform } = input;
3386
- try {
3387
- const config = await (0, config_manager_js_1.loadConfig)();
3388
- const platforms = ['discord', 'telegram', 'slack', 'chatwork'];
3389
- const bots = [];
3390
- // Get runtime status if callback is available
3391
- const runtimeStatus = this.botStatusCallback?.() ?? new Map();
3392
- for (const p of platforms) {
3393
- // Skip if filtering by platform
3394
- if (platform && p !== platform)
3395
- continue;
3396
- const platformConfig = config[p];
3397
- const configured = !!platformConfig;
3398
- const enabled = configured && platformConfig.enabled === true;
3399
- const runtime = runtimeStatus.get(p);
3400
- let status;
3401
- if (!configured) {
3402
- status = 'not_configured';
3403
- }
3404
- else if (runtime?.running) {
3405
- status = 'running';
3406
- }
3407
- else if (runtime?.error) {
3408
- status = 'error';
3409
- }
3410
- else if (enabled) {
3411
- status = 'stopped';
3412
- }
3413
- else {
3414
- status = 'stopped';
3415
- }
3416
- bots.push({
3417
- platform: p,
3418
- enabled,
3419
- configured,
3420
- status,
3421
- error: runtime?.error,
3422
- });
3423
- }
3424
- return { success: true, bots };
3425
- }
3426
- catch (err) {
3427
- return { success: false, error: `Failed to list bots: ${err}` };
3428
- }
3429
- }
3430
- /**
3431
- * Execute os_restart_bot tool - Restart a bot
3432
- * Viewer-only: requires systemControl permission
3433
- */
3434
- async executeRestartBot(input) {
3435
- // Check viewer-only permission
3436
- const permError = this.checkViewerOnly();
3437
- if (permError) {
3438
- return { success: false, error: permError };
3439
- }
3440
- const { platform } = input;
3441
- if (!platform) {
3442
- return { success: false, error: 'Platform is required' };
3443
- }
3444
- if (!this.botControlCallback) {
3445
- return {
3446
- success: false,
3447
- error: 'Bot control not available. Please restart MAMA server to apply configuration changes.',
3448
- };
3449
- }
3450
- try {
3451
- // Stop then start
3452
- const stopResult = await this.botControlCallback(platform, 'stop');
3453
- if (!stopResult.success && stopResult.error !== 'Bot not running') {
3454
- return { success: false, error: `Failed to stop bot: ${stopResult.error}` };
3455
- }
3456
- // Small delay
3457
- await new Promise((resolve) => setTimeout(resolve, 1000));
3458
- const startResult = await this.botControlCallback(platform, 'start');
3459
- if (!startResult.success) {
3460
- return { success: false, error: `Failed to start bot: ${startResult.error}` };
3461
- }
3462
- return { success: true, message: `${platform} bot restarted successfully` };
3463
- }
3464
- catch (err) {
3465
- return { success: false, error: `Failed to restart bot: ${err}` };
3466
- }
3467
- }
3468
- /**
3469
- * Execute os_stop_bot tool - Stop a bot
3470
- * Viewer-only: requires systemControl permission
3471
- */
3472
- async executeStopBot(input) {
3473
- // Check viewer-only permission
3474
- const permError = this.checkViewerOnly();
3475
- if (permError) {
3476
- return { success: false, error: permError };
3477
- }
3478
- const { platform } = input;
3479
- if (!platform) {
3480
- return { success: false, error: 'Platform is required' };
3481
- }
3482
- if (!this.botControlCallback) {
3483
- return {
3484
- success: false,
3485
- error: 'Bot control not available. Manually disable the bot in config.yaml and restart MAMA.',
3486
- };
3487
- }
3488
- try {
3489
- const result = await this.botControlCallback(platform, 'stop');
3490
- if (!result.success) {
3491
- return { success: false, error: `Failed to stop bot: ${result.error}` };
3492
- }
3493
- return { success: true, message: `${platform} bot stopped successfully` };
3494
- }
3495
- catch (err) {
3496
- return { success: false, error: `Failed to stop bot: ${err}` };
3497
- }
3498
- }
3499
- // ============================================================================
3500
- // PR Review Tools
3501
- // ============================================================================
3502
- parsePRUrl(url) {
3503
- const match = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/);
3504
- if (!match)
3505
- return null;
3506
- return { owner: match[1], repo: match[2], prNumber: parseInt(match[3], 10) };
3507
- }
3508
- async executePrReviewThreads(input) {
3509
- let owner;
3510
- let repo;
3511
- let prNumber;
3512
- if (input.pr_url) {
3513
- const parsed = this.parsePRUrl(input.pr_url);
3514
- if (!parsed)
3515
- return { success: false, error: `Invalid PR URL: ${input.pr_url}` };
3516
- ({ owner, repo, prNumber } = parsed);
3517
- }
3518
- else if (input.owner && input.repo && input.pr_number) {
3519
- owner = input.owner;
3520
- repo = input.repo;
3521
- prNumber = input.pr_number;
3522
- }
3523
- else {
3524
- return { success: false, error: 'Provide pr_url or (owner, repo, pr_number)' };
3525
- }
3526
- try {
3527
- const query = `
3528
- query($owner: String!, $repo: String!, $prNumber: Int!) {
3529
- repository(owner: $owner, name: $repo) {
3530
- pullRequest(number: $prNumber) {
3531
- reviewThreads(last: 100) {
3532
- nodes {
3533
- id
3534
- isResolved
3535
- comments(first: 10) {
3536
- nodes { path line body author { login } }
3537
- }
3538
- }
3539
- }
3540
- }
3541
- }
3542
- }
3543
- `;
3544
- const { stdout } = await execFileAsync('gh', [
3545
- 'api',
3546
- 'graphql',
3547
- '-f',
3548
- `query=${query}`,
3549
- '-F',
3550
- `owner=${owner}`,
3551
- '-F',
3552
- `repo=${repo}`,
3553
- '-F',
3554
- `prNumber=${prNumber}`,
3555
- ], { timeout: 30000 });
3556
- let data;
3557
- try {
3558
- data = JSON.parse(stdout);
3559
- }
3560
- catch {
3561
- return {
3562
- success: false,
3563
- error: `Failed to parse GitHub API response: ${stdout.substring(0, 200)}`,
3564
- };
3565
- }
3566
- const threads = data.data?.repository?.pullRequest?.reviewThreads?.nodes ?? [];
3567
- const unresolved = threads
3568
- .filter((thread) => !thread.isResolved)
3569
- .map((thread) => ({
3570
- id: thread.id,
3571
- comments: thread.comments.nodes.map((comment) => ({
3572
- path: comment.path,
3573
- line: comment.line,
3574
- body: comment.body,
3575
- author: comment.author?.login ?? 'unknown',
3576
- })),
3577
- }));
3578
- // Build summary grouped by file
3579
- const byFile = new Map();
3580
- for (const t of unresolved) {
3581
- const first = t.comments[0];
3582
- if (!first)
3583
- continue;
3584
- const file = first.path || '(general)';
3585
- const list = byFile.get(file) || [];
3586
- list.push({ line: first.line, body: first.body, author: first.author });
3587
- byFile.set(file, list);
3588
- }
3589
- const summaryLines = [
3590
- `${unresolved.length} unresolved thread(s) across ${byFile.size} file(s)`,
3591
- '',
3592
- ];
3593
- for (const [file, items] of byFile) {
3594
- summaryLines.push(`**${file}** (${items.length})`);
3595
- for (const item of items) {
3596
- const lineRef = item.line ? `L${item.line} ` : '';
3597
- const body = item.body.length > 300 ? item.body.substring(0, 300) + '…' : item.body;
3598
- summaryLines.push(` • ${lineRef}@${item.author}: ${body}`);
3599
- }
3600
- summaryLines.push('');
3601
- }
3602
- if (byFile.size > 1) {
3603
- summaryLines.push(`💡 ${byFile.size} independent files — delegate fixes in parallel (DELEGATE_BG)`);
3604
- }
3605
- return { success: true, threads: unresolved, summary: summaryLines.join('\n') };
3606
- }
3607
- catch (err) {
3608
- return { success: false, error: `Failed to fetch PR threads: ${err}` };
3609
- }
3610
- }
3611
2561
  // ============================================================================
3612
2562
  // ============================================================================
3613
2563
  // Webchat Tools
@@ -3890,101 +2840,6 @@ class GatewayToolExecutor {
3890
2840
  : `Code-Act error: ${result.error?.message || 'Unknown error'}`,
3891
2841
  };
3892
2842
  }
3893
- /**
3894
- * Handle mama_add — auto-extract facts from conversation content with derived memory scopes.
3895
- */
3896
- async handleMamaAdd(input, options) {
3897
- const context = this.getActiveContext();
3898
- if (!context) {
3899
- return {
3900
- success: false,
3901
- error: 'mama_add requires an active agent context',
3902
- };
3903
- }
3904
- const scopes = (0, scope_context_js_1.deriveMemoryScopes)({
3905
- source: context.source,
3906
- channelId: context.session.channelId,
3907
- userId: context.session.userId,
3908
- projectId: process.env.MAMA_WORKSPACE || process.cwd(),
3909
- });
3910
- return this.handleMamaIngest({
3911
- ...input,
3912
- scopes,
3913
- }, options);
3914
- }
3915
- async handleMamaIngest(input, options) {
3916
- const { content } = input;
3917
- if (!content || typeof content !== 'string') {
3918
- return {
3919
- success: false,
3920
- error: 'content is required and must be a string',
3921
- };
3922
- }
3923
- try {
3924
- const api = await this.initializeMAMAApi();
3925
- if (options && !api.ingestWithTrustedProvenance) {
3926
- return {
3927
- success: false,
3928
- error: 'Trusted memory ingest API not available.',
3929
- };
3930
- }
3931
- if (!options && !api.ingestMemory) {
3932
- return {
3933
- success: false,
3934
- error: 'Memory ingest API not available.',
3935
- };
3936
- }
3937
- const activeState = this.getExecutionState();
3938
- const context = activeState.agentContext;
3939
- const envelopeScopes = activeState.envelope?.scope.memory_scopes ?? null;
3940
- const fallbackScopes = envelopeScopes && envelopeScopes.length > 0
3941
- ? envelopeScopes
3942
- : context
3943
- ? (0, scope_context_js_1.deriveMemoryScopes)({
3944
- source: context.source,
3945
- channelId: context.session.channelId,
3946
- userId: context.session.userId,
3947
- projectId: process.env.MAMA_WORKSPACE || process.cwd(),
3948
- })
3949
- : [];
3950
- let scopes = fallbackScopes;
3951
- if (Array.isArray(input.scopes) && input.scopes.length > 0) {
3952
- const derivedIds = new Set(fallbackScopes.map((s) => `${s.kind}:${s.id}`));
3953
- const allInDerived = input.scopes.every((s) => derivedIds.has(`${s.kind}:${s.id}`));
3954
- scopes = allInDerived ? input.scopes : fallbackScopes;
3955
- }
3956
- if (scopes.length === 0) {
3957
- return {
3958
- success: false,
3959
- error: 'mama_ingest requires scopes (provide via input or active agent context)',
3960
- };
3961
- }
3962
- const payload = {
3963
- content: content.substring(0, 10_000),
3964
- scopes,
3965
- source: {
3966
- package: 'standalone',
3967
- source_type: 'gateway_tool_executor',
3968
- source: context?.source || null,
3969
- },
3970
- };
3971
- const result = options
3972
- ? await api.ingestWithTrustedProvenance(payload, options)
3973
- : await api.ingestMemory(payload);
3974
- return {
3975
- success: true,
3976
- extracted: 1,
3977
- saved: 1,
3978
- result,
3979
- };
3980
- }
3981
- catch (err) {
3982
- return {
3983
- success: false,
3984
- error: `Ingest failed: ${err instanceof Error ? err.message : String(err)}`,
3985
- };
3986
- }
3987
- }
3988
2843
  /**
3989
2844
  * Answer what a stored claim rests on.
3990
2845
  *