@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.
- package/dist/agent/agent-loop.d.ts +1 -12
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +6 -21
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/code-act/constants.d.ts.map +1 -1
- package/dist/agent/code-act/constants.js +2 -4
- package/dist/agent/code-act/constants.js.map +1 -1
- package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
- package/dist/agent/code-act/host-bridge.js +1 -257
- package/dist/agent/code-act/host-bridge.js.map +1 -1
- package/dist/agent/code-act/types.d.ts +1 -1
- package/dist/agent/code-act/types.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +1 -124
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +17 -1161
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +0 -34
- package/dist/agent/os-agent-capabilities.md +0 -14
- package/dist/agent/tool-registry.d.ts +1 -1
- package/dist/agent/tool-registry.d.ts.map +1 -1
- package/dist/agent/tool-registry.js +0 -176
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +2 -110
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/commands/start.d.ts +18 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +180 -23
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/config/types.d.ts +21 -0
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +7 -0
- package/dist/cli/config/types.js.map +1 -1
- package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-routes-init.js +0 -3
- package/dist/cli/runtime/api-routes-init.js.map +1 -1
- package/dist/operator/board-reground.d.ts +19 -0
- package/dist/operator/board-reground.d.ts.map +1 -0
- package/dist/operator/board-reground.js +31 -0
- package/dist/operator/board-reground.js.map +1 -0
- package/dist/operator/conductor-inbox.d.ts +73 -0
- package/dist/operator/conductor-inbox.d.ts.map +1 -0
- package/dist/operator/conductor-inbox.js +184 -0
- package/dist/operator/conductor-inbox.js.map +1 -0
- package/dist/operator/conductor-session.d.ts +55 -0
- package/dist/operator/conductor-session.d.ts.map +1 -0
- package/dist/operator/conductor-session.js +110 -0
- package/dist/operator/conductor-session.js.map +1 -0
- package/dist/operator/conductor.d.ts +68 -0
- package/dist/operator/conductor.d.ts.map +1 -0
- package/dist/operator/conductor.js +84 -0
- package/dist/operator/conductor.js.map +1 -0
- package/dist/operator/operator-trigger-loop.d.ts +13 -0
- package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
- package/dist/operator/operator-trigger-loop.js +39 -8
- package/dist/operator/operator-trigger-loop.js.map +1 -1
- package/dist/operator/report-run.d.ts +8 -2
- package/dist/operator/report-run.d.ts.map +1 -1
- package/dist/operator/report-run.js +9 -9
- package/dist/operator/report-run.js.map +1 -1
- package/package.json +1 -2
- package/dist/multi-agent/conductor-persona.d.ts +0 -13
- package/dist/multi-agent/conductor-persona.d.ts.map +0 -1
- package/dist/multi-agent/conductor-persona.js +0 -157
- package/dist/multi-agent/conductor-persona.js.map +0 -1
- package/dist/tools/browser-tool.d.ts +0 -164
- package/dist/tools/browser-tool.d.ts.map +0 -1
- package/dist/tools/browser-tool.js +0 -315
- 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
|
|
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
|
|
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
|
|
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
|
*
|