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