@jungjaehoon/mama-os 0.18.2 → 0.19.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 +25 -0
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +67 -14
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
- package/dist/agent/code-act/host-bridge.js +98 -0
- package/dist/agent/code-act/host-bridge.js.map +1 -1
- package/dist/agent/code-act/type-definition-generator.d.ts.map +1 -1
- package/dist/agent/code-act/type-definition-generator.js +0 -1
- package/dist/agent/code-act/type-definition-generator.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +36 -1
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +938 -54
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +9 -0
- package/dist/agent/managed-agent-runtime-sync.d.ts +36 -0
- package/dist/agent/managed-agent-runtime-sync.d.ts.map +1 -0
- package/dist/agent/managed-agent-runtime-sync.js +207 -0
- package/dist/agent/managed-agent-runtime-sync.js.map +1 -0
- package/dist/agent/managed-agent-validation.d.ts +4 -0
- package/dist/agent/managed-agent-validation.d.ts.map +1 -0
- package/dist/agent/managed-agent-validation.js +84 -0
- package/dist/agent/managed-agent-validation.js.map +1 -0
- package/dist/agent/os-agent-capabilities.md +400 -0
- package/dist/agent/skill-loader.d.ts +2 -0
- package/dist/agent/skill-loader.d.ts.map +1 -1
- package/dist/agent/skill-loader.js +28 -0
- package/dist/agent/skill-loader.js.map +1 -1
- package/dist/agent/tool-registry.d.ts.map +1 -1
- package/dist/agent/tool-registry.js +66 -0
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +2 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/agent-handler.d.ts +34 -0
- package/dist/api/agent-handler.d.ts.map +1 -0
- package/dist/api/agent-handler.js +216 -0
- package/dist/api/agent-handler.js.map +1 -0
- package/dist/api/graph-api-types.d.ts +4 -0
- package/dist/api/graph-api-types.d.ts.map +1 -1
- package/dist/api/graph-api.d.ts +2 -2
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +480 -51
- package/dist/api/graph-api.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +4 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/token-handler.d.ts +1 -0
- package/dist/api/token-handler.d.ts.map +1 -1
- package/dist/api/token-handler.js +4 -3
- package/dist/api/token-handler.js.map +1 -1
- package/dist/api/ui-command-handler.d.ts +48 -0
- package/dist/api/ui-command-handler.d.ts.map +1 -0
- package/dist/api/ui-command-handler.js +160 -0
- package/dist/api/ui-command-handler.js.map +1 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +127 -1
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +16 -31
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
- package/dist/cli/runtime/agent-loop-init.js +31 -7
- package/dist/cli/runtime/agent-loop-init.js.map +1 -1
- package/dist/cli/runtime/api-routes-init.d.ts +3 -0
- package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-routes-init.js +283 -34
- package/dist/cli/runtime/api-routes-init.js.map +1 -1
- package/dist/cli/runtime/gateway-init.d.ts +2 -1
- package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
- package/dist/cli/runtime/gateway-init.js +5 -1
- package/dist/cli/runtime/gateway-init.js.map +1 -1
- package/dist/connectors/framework/raw-store.d.ts +4 -0
- package/dist/connectors/framework/raw-store.d.ts.map +1 -1
- package/dist/connectors/framework/raw-store.js +33 -10
- package/dist/connectors/framework/raw-store.js.map +1 -1
- package/dist/db/agent-store.d.ts +115 -0
- package/dist/db/agent-store.d.ts.map +1 -0
- package/dist/db/agent-store.js +248 -0
- package/dist/db/agent-store.js.map +1 -0
- package/dist/db/migrations/agent-activity-validation-columns.d.ts +3 -0
- package/dist/db/migrations/agent-activity-validation-columns.d.ts.map +1 -0
- package/dist/db/migrations/agent-activity-validation-columns.js +22 -0
- package/dist/db/migrations/agent-activity-validation-columns.js.map +1 -0
- package/dist/db/migrations/agent-metrics-response-avg.d.ts +3 -0
- package/dist/db/migrations/agent-metrics-response-avg.d.ts.map +1 -0
- package/dist/db/migrations/agent-metrics-response-avg.js +19 -0
- package/dist/db/migrations/agent-metrics-response-avg.js.map +1 -0
- package/dist/db/migrations/agent-store-tables.d.ts +3 -0
- package/dist/db/migrations/agent-store-tables.d.ts.map +1 -0
- package/dist/db/migrations/agent-store-tables.js +59 -0
- package/dist/db/migrations/agent-store-tables.js.map +1 -0
- package/dist/db/migrations/token-usage-agent-version.d.ts +3 -0
- package/dist/db/migrations/token-usage-agent-version.d.ts.map +1 -0
- package/dist/db/migrations/token-usage-agent-version.js +16 -0
- package/dist/db/migrations/token-usage-agent-version.js.map +1 -0
- package/dist/db/migrations/validation-session-tables.d.ts +3 -0
- package/dist/db/migrations/validation-session-tables.d.ts.map +1 -0
- package/dist/db/migrations/validation-session-tables.js +59 -0
- package/dist/db/migrations/validation-session-tables.js.map +1 -0
- package/dist/gateways/message-router.d.ts +10 -0
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +188 -14
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/gateways/types.d.ts +1 -1
- package/dist/gateways/types.d.ts.map +1 -1
- package/dist/multi-agent/agent-process-manager.js +1 -1
- package/dist/multi-agent/agent-process-manager.js.map +1 -1
- package/dist/multi-agent/conductor-persona.d.ts +13 -0
- package/dist/multi-agent/conductor-persona.d.ts.map +1 -0
- package/dist/multi-agent/conductor-persona.js +157 -0
- package/dist/multi-agent/conductor-persona.js.map +1 -0
- package/dist/multi-agent/dashboard-agent-persona.d.ts +1 -1
- package/dist/multi-agent/dashboard-agent-persona.d.ts.map +1 -1
- package/dist/multi-agent/dashboard-agent-persona.js +7 -3
- package/dist/multi-agent/dashboard-agent-persona.js.map +1 -1
- package/dist/multi-agent/delegation-manager.d.ts +5 -0
- package/dist/multi-agent/delegation-manager.d.ts.map +1 -1
- package/dist/multi-agent/delegation-manager.js +37 -0
- package/dist/multi-agent/delegation-manager.js.map +1 -1
- package/dist/multi-agent/ultrawork.d.ts +3 -0
- package/dist/multi-agent/ultrawork.d.ts.map +1 -1
- package/dist/multi-agent/ultrawork.js +9 -0
- package/dist/multi-agent/ultrawork.js.map +1 -1
- package/dist/validation/session-service.d.ts +72 -0
- package/dist/validation/session-service.d.ts.map +1 -0
- package/dist/validation/session-service.js +298 -0
- package/dist/validation/session-service.js.map +1 -0
- package/dist/validation/store.d.ts +25 -0
- package/dist/validation/store.d.ts.map +1 -0
- package/dist/validation/store.js +200 -0
- package/dist/validation/store.js.map +1 -0
- package/dist/validation/types.d.ts +119 -0
- package/dist/validation/types.d.ts.map +1 -0
- package/dist/validation/types.js +57 -0
- package/dist/validation/types.js.map +1 -0
- package/package.json +3 -3
- package/public/viewer/js/modules/agents.js +1148 -0
- package/public/viewer/js/modules/chat.js +20 -11
- package/public/viewer/js/modules/connector-feed.js +35 -0
- package/public/viewer/js/modules/dashboard.js +49 -0
- package/public/viewer/js/modules/memory.js +32 -0
- package/public/viewer/js/modules/settings.js +34 -79
- package/public/viewer/js/modules/wiki.js +59 -4
- package/public/viewer/js/utils/api.js +70 -0
- package/public/viewer/js/utils/dom.js +3 -0
- package/public/viewer/js/utils/ui-commands.js +93 -0
- package/public/viewer/log-viewer.html +2 -2
- package/public/viewer/src/modules/agents.ts +1299 -0
- package/public/viewer/src/modules/chat.ts +23 -14
- package/public/viewer/src/modules/connector-feed.ts +35 -0
- package/public/viewer/src/modules/dashboard.ts +50 -0
- package/public/viewer/src/modules/memory.ts +31 -0
- package/public/viewer/src/modules/settings.ts +36 -96
- package/public/viewer/src/modules/wiki.ts +73 -6
- package/public/viewer/src/types/global.d.ts +0 -9
- package/public/viewer/src/utils/api.ts +156 -2
- package/public/viewer/src/utils/dom.ts +6 -1
- package/public/viewer/src/utils/ui-commands.ts +118 -0
- package/public/viewer/viewer.css +105 -10
- package/public/viewer/viewer.html +1868 -777
- package/scripts/generate-gateway-tools.ts +5 -1
- package/public/viewer/js/modules/playground.js +0 -148
- package/public/viewer/js/modules/skills.js +0 -451
- package/public/viewer/src/modules/playground.ts +0 -173
- package/public/viewer/src/modules/skills.ts +0 -491
- package/templates/playgrounds/cron-workflow-lab.html +0 -1601
- package/templates/playgrounds/mama-log-viewer.html +0 -1341
- package/templates/playgrounds/skill-lab-playground.html +0 -1625
- package/templates/playgrounds/wave-visualizer.html +0 -694
- package/templates/skills/playground.md +0 -197
|
@@ -47,6 +47,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
exports.GatewayToolExecutor = void 0;
|
|
49
49
|
const fs_1 = require("fs");
|
|
50
|
+
const async_hooks_1 = require("async_hooks");
|
|
50
51
|
const crypto_1 = require("crypto");
|
|
51
52
|
const path_1 = require("path");
|
|
52
53
|
const os_1 = require("os");
|
|
@@ -60,9 +61,40 @@ const mama_tool_handlers_js_1 = require("./mama-tool-handlers.js");
|
|
|
60
61
|
const browser_tool_js_1 = require("../tools/browser-tool.js");
|
|
61
62
|
const role_manager_js_1 = require("./role-manager.js");
|
|
62
63
|
const config_manager_js_1 = require("../cli/config/config-manager.js");
|
|
64
|
+
const agent_store_js_1 = require("../db/agent-store.js");
|
|
63
65
|
const types_js_2 = require("../cli/config/types.js");
|
|
66
|
+
const managed_agent_runtime_sync_js_1 = require("./managed-agent-runtime-sync.js");
|
|
67
|
+
const managed_agent_validation_js_1 = require("./managed-agent-validation.js");
|
|
64
68
|
const { DebugLogger } = debugLogger;
|
|
65
69
|
const securityLogger = new DebugLogger('SecurityAudit');
|
|
70
|
+
const AGENT_DETAIL_TABS = new Set([
|
|
71
|
+
'config',
|
|
72
|
+
'persona',
|
|
73
|
+
'tools',
|
|
74
|
+
'activity',
|
|
75
|
+
'validation',
|
|
76
|
+
'history',
|
|
77
|
+
]);
|
|
78
|
+
const managedAgentMutationTails = new Map();
|
|
79
|
+
async function withManagedAgentMutationLock(agentId, fn) {
|
|
80
|
+
const previous = managedAgentMutationTails.get(agentId) ?? Promise.resolve();
|
|
81
|
+
let release;
|
|
82
|
+
const current = new Promise((resolve) => {
|
|
83
|
+
release = resolve;
|
|
84
|
+
});
|
|
85
|
+
const tail = previous.then(() => current);
|
|
86
|
+
managedAgentMutationTails.set(agentId, tail);
|
|
87
|
+
await previous;
|
|
88
|
+
try {
|
|
89
|
+
return await fn();
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
release();
|
|
93
|
+
if (managedAgentMutationTails.get(agentId) === tail) {
|
|
94
|
+
managedAgentMutationTails.delete(agentId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
66
98
|
function sanitizeCommandForAudit(command) {
|
|
67
99
|
const commandHash = (0, crypto_1.createHash)('sha256').update(command).digest('hex');
|
|
68
100
|
const commandPreview = command
|
|
@@ -71,6 +103,20 @@ function sanitizeCommandForAudit(command) {
|
|
|
71
103
|
.slice(0, 200);
|
|
72
104
|
return { commandHash, commandPreview };
|
|
73
105
|
}
|
|
106
|
+
function summarizeActivityOutput(output) {
|
|
107
|
+
if (output === undefined || output === null) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
if (typeof output === 'string') {
|
|
111
|
+
return output.slice(0, 500);
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return JSON.stringify(output).slice(0, 500);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return String(output).slice(0, 500);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
74
120
|
/**
|
|
75
121
|
* Valid MAMA gateway tools — derived from ToolRegistry (SSOT).
|
|
76
122
|
*/
|
|
@@ -90,6 +136,7 @@ class GatewayToolExecutor {
|
|
|
90
136
|
telegramGateway = null;
|
|
91
137
|
browserTool;
|
|
92
138
|
roleManager;
|
|
139
|
+
executionContextStorage = new async_hooks_1.AsyncLocalStorage();
|
|
93
140
|
currentContext = null;
|
|
94
141
|
memoryAgentProcessManager = null;
|
|
95
142
|
agentProcessManager = null;
|
|
@@ -111,6 +158,31 @@ class GatewayToolExecutor {
|
|
|
111
158
|
getAgentEventBus() {
|
|
112
159
|
return this.agentEventBus;
|
|
113
160
|
}
|
|
161
|
+
sessionsDb = null;
|
|
162
|
+
setSessionsDb(db) {
|
|
163
|
+
this.sessionsDb = db;
|
|
164
|
+
}
|
|
165
|
+
rawStore = null;
|
|
166
|
+
setRawStore(store) {
|
|
167
|
+
this.rawStore = store;
|
|
168
|
+
}
|
|
169
|
+
testInFlight = new Map();
|
|
170
|
+
uiCommandQueue = null;
|
|
171
|
+
setUICommandQueue(queue) {
|
|
172
|
+
this.uiCommandQueue = queue;
|
|
173
|
+
}
|
|
174
|
+
applyMultiAgentConfig = null;
|
|
175
|
+
setApplyMultiAgentConfig(fn) {
|
|
176
|
+
this.applyMultiAgentConfig = fn;
|
|
177
|
+
}
|
|
178
|
+
restartMultiAgentAgent = null;
|
|
179
|
+
setRestartMultiAgentAgent(fn) {
|
|
180
|
+
this.restartMultiAgentAgent = fn;
|
|
181
|
+
}
|
|
182
|
+
validationService = null;
|
|
183
|
+
setValidationService(svc) {
|
|
184
|
+
this.validationService = svc;
|
|
185
|
+
}
|
|
114
186
|
setMemoryAgent(processManager) {
|
|
115
187
|
this.memoryAgentProcessManager = processManager;
|
|
116
188
|
}
|
|
@@ -124,14 +196,146 @@ class GatewayToolExecutor {
|
|
|
124
196
|
getAgentProcessManager() {
|
|
125
197
|
return this.agentProcessManager;
|
|
126
198
|
}
|
|
199
|
+
normalizeExecutionContext(executionContext) {
|
|
200
|
+
const agentContext = executionContext?.agentContext ?? null;
|
|
201
|
+
const source = executionContext?.source ?? agentContext?.source ?? '';
|
|
202
|
+
const channelId = executionContext?.channelId ?? agentContext?.session?.channelId ?? '';
|
|
203
|
+
const agentId = executionContext?.agentId ??
|
|
204
|
+
(source === 'viewer' ? 'os-agent' : (agentContext?.roleName ?? ''));
|
|
205
|
+
return {
|
|
206
|
+
agentContext,
|
|
207
|
+
agentId,
|
|
208
|
+
source,
|
|
209
|
+
channelId,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
getExecutionState() {
|
|
213
|
+
const active = this.executionContextStorage.getStore();
|
|
214
|
+
if (active) {
|
|
215
|
+
return active;
|
|
216
|
+
}
|
|
217
|
+
return this.normalizeExecutionContext({
|
|
218
|
+
agentContext: this.currentContext,
|
|
219
|
+
agentId: this.currentAgentId,
|
|
220
|
+
source: this.currentSource,
|
|
221
|
+
channelId: this.currentChannelId,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
getActiveContext() {
|
|
225
|
+
return this.getExecutionState().agentContext;
|
|
226
|
+
}
|
|
227
|
+
getActiveRouting() {
|
|
228
|
+
const state = this.getExecutionState();
|
|
229
|
+
return {
|
|
230
|
+
agentId: state.agentId,
|
|
231
|
+
source: state.source,
|
|
232
|
+
channelId: state.channelId,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
async withExecutionContext(executionContext, fn) {
|
|
236
|
+
if (!executionContext) {
|
|
237
|
+
return fn();
|
|
238
|
+
}
|
|
239
|
+
const activeContext = this.normalizeExecutionContext(executionContext);
|
|
240
|
+
return this.executionContextStorage.run(activeContext, fn);
|
|
241
|
+
}
|
|
127
242
|
setCurrentAgentContext(agentId, source, channelId) {
|
|
128
243
|
this.currentAgentId = agentId;
|
|
129
244
|
this.currentSource = source;
|
|
130
245
|
this.currentChannelId = channelId;
|
|
131
246
|
}
|
|
247
|
+
clearCurrentAgentContext() {
|
|
248
|
+
this.currentAgentId = '';
|
|
249
|
+
this.currentSource = '';
|
|
250
|
+
this.currentChannelId = '';
|
|
251
|
+
}
|
|
132
252
|
setDisallowedGatewayTools(tools) {
|
|
133
253
|
this.disallowedGatewayTools = new Set(tools);
|
|
134
254
|
}
|
|
255
|
+
cleanupValidationSessionOnTelemetryFailure(session, error, label) {
|
|
256
|
+
if (!session || !this.validationService) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
260
|
+
try {
|
|
261
|
+
this.validationService.finalizeSession(session.id, {
|
|
262
|
+
execution_status: 'failed',
|
|
263
|
+
error_message: `${label}: ${message}`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
catch (cleanupErr) {
|
|
267
|
+
securityLogger.warn(`[Delegation telemetry] Failed to clean up validation session ${session.id}`, cleanupErr);
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
getPreferredViewerAgentTab() {
|
|
272
|
+
if (!this.uiCommandQueue) {
|
|
273
|
+
return 'activity';
|
|
274
|
+
}
|
|
275
|
+
const { channelId } = this.getActiveRouting();
|
|
276
|
+
const currentPageContext = this.uiCommandQueue.getPageContext(channelId || undefined);
|
|
277
|
+
if (!currentPageContext || currentPageContext.currentRoute !== 'agents') {
|
|
278
|
+
return 'activity';
|
|
279
|
+
}
|
|
280
|
+
const pageData = currentPageContext.pageData;
|
|
281
|
+
const activeTab = pageData?.activeTab;
|
|
282
|
+
if (typeof activeTab === 'string' && AGENT_DETAIL_TABS.has(activeTab)) {
|
|
283
|
+
return activeTab;
|
|
284
|
+
}
|
|
285
|
+
return 'activity';
|
|
286
|
+
}
|
|
287
|
+
syncViewerToAgentDetail(agentId, preferredTab) {
|
|
288
|
+
if (!this.uiCommandQueue) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const { source, channelId } = this.getActiveRouting();
|
|
292
|
+
if (source !== 'viewer') {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const desiredTab = preferredTab && AGENT_DETAIL_TABS.has(preferredTab)
|
|
296
|
+
? preferredTab
|
|
297
|
+
: this.getPreferredViewerAgentTab();
|
|
298
|
+
const currentPageContext = this.uiCommandQueue.getPageContext(channelId || undefined);
|
|
299
|
+
const currentPageData = currentPageContext?.pageData;
|
|
300
|
+
if (currentPageContext?.currentRoute === 'agents' &&
|
|
301
|
+
currentPageContext.selectedItem?.type === 'agent' &&
|
|
302
|
+
currentPageContext.selectedItem.id === agentId &&
|
|
303
|
+
currentPageData?.pageType === 'agent-detail' &&
|
|
304
|
+
currentPageData?.activeTab === desiredTab) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
this.uiCommandQueue.push({
|
|
308
|
+
type: 'navigate',
|
|
309
|
+
payload: {
|
|
310
|
+
route: 'agents',
|
|
311
|
+
params: {
|
|
312
|
+
id: agentId,
|
|
313
|
+
tab: desiredTab,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
resolveManagedAgentId(agentId) {
|
|
319
|
+
if (!this.sessionsDb) {
|
|
320
|
+
return agentId;
|
|
321
|
+
}
|
|
322
|
+
const normalized = agentId
|
|
323
|
+
.trim()
|
|
324
|
+
.toLowerCase()
|
|
325
|
+
.replace(/[_\s]+/g, '-');
|
|
326
|
+
const candidates = Array.from(new Set([
|
|
327
|
+
agentId,
|
|
328
|
+
agentId.trim(),
|
|
329
|
+
normalized,
|
|
330
|
+
normalized.endsWith('-agent') ? normalized.slice(0, -6) : `${normalized}-agent`,
|
|
331
|
+
])).filter(Boolean);
|
|
332
|
+
for (const candidate of candidates) {
|
|
333
|
+
if ((0, agent_store_js_1.getLatestVersion)(this.sessionsDb, candidate)) {
|
|
334
|
+
return candidate;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return agentId;
|
|
338
|
+
}
|
|
135
339
|
setReportPublisher(fn) {
|
|
136
340
|
this.reportPublisher = fn;
|
|
137
341
|
}
|
|
@@ -178,7 +382,7 @@ class GatewayToolExecutor {
|
|
|
178
382
|
* Get the current agent context
|
|
179
383
|
*/
|
|
180
384
|
getAgentContext() {
|
|
181
|
-
return this.
|
|
385
|
+
return this.getActiveContext();
|
|
182
386
|
}
|
|
183
387
|
setDiscordGateway(gateway) {
|
|
184
388
|
this.discordGateway = gateway;
|
|
@@ -233,14 +437,15 @@ class GatewayToolExecutor {
|
|
|
233
437
|
*/
|
|
234
438
|
checkToolPermission(toolName) {
|
|
235
439
|
// If no context set, allow all tools (backward compatibility)
|
|
236
|
-
|
|
440
|
+
const context = this.getActiveContext();
|
|
441
|
+
if (!context) {
|
|
237
442
|
return { allowed: true };
|
|
238
443
|
}
|
|
239
|
-
const role =
|
|
444
|
+
const role = context.role;
|
|
240
445
|
if (!this.roleManager.isToolAllowed(role, toolName)) {
|
|
241
446
|
return {
|
|
242
447
|
allowed: false,
|
|
243
|
-
error: `Permission denied: ${toolName} is not allowed for role "${
|
|
448
|
+
error: `Permission denied: ${toolName} is not allowed for role "${context.roleName}"`,
|
|
244
449
|
};
|
|
245
450
|
}
|
|
246
451
|
return { allowed: true };
|
|
@@ -252,14 +457,15 @@ class GatewayToolExecutor {
|
|
|
252
457
|
*/
|
|
253
458
|
checkPathPermission(path) {
|
|
254
459
|
// If no context set, allow all paths (backward compatibility)
|
|
255
|
-
|
|
460
|
+
const context = this.getActiveContext();
|
|
461
|
+
if (!context) {
|
|
256
462
|
return { allowed: true };
|
|
257
463
|
}
|
|
258
|
-
const role =
|
|
464
|
+
const role = context.role;
|
|
259
465
|
if (!this.roleManager.isPathAllowed(role, path)) {
|
|
260
466
|
return {
|
|
261
467
|
allowed: false,
|
|
262
|
-
error: `Permission denied: Access to "${path}" is not allowed for role "${
|
|
468
|
+
error: `Permission denied: Access to "${path}" is not allowed for role "${context.roleName}"`,
|
|
263
469
|
};
|
|
264
470
|
}
|
|
265
471
|
return { allowed: true };
|
|
@@ -272,7 +478,10 @@ class GatewayToolExecutor {
|
|
|
272
478
|
* @returns Tool execution result
|
|
273
479
|
* @throws AgentError on tool errors or permission denial
|
|
274
480
|
*/
|
|
275
|
-
async execute(toolName, input) {
|
|
481
|
+
async execute(toolName, input, executionContext) {
|
|
482
|
+
if (executionContext) {
|
|
483
|
+
return this.withExecutionContext(executionContext, () => this.execute(toolName, input));
|
|
484
|
+
}
|
|
276
485
|
if (!VALID_TOOLS.includes(toolName)) {
|
|
277
486
|
throw new types_js_1.AgentError(`Unknown tool: ${toolName}. Valid tools: ${VALID_TOOLS.join(', ')}`, 'UNKNOWN_TOOL', undefined, false);
|
|
278
487
|
}
|
|
@@ -356,6 +565,204 @@ class GatewayToolExecutor {
|
|
|
356
565
|
// Obsidian vault management via CLI
|
|
357
566
|
case 'obsidian':
|
|
358
567
|
return await this.executeObsidian(input);
|
|
568
|
+
// Agent lifecycle tools
|
|
569
|
+
case 'agent_test':
|
|
570
|
+
return await this.executeAgentTest(input);
|
|
571
|
+
// Agent management tools (Managed Agents pattern)
|
|
572
|
+
case 'agent_get': {
|
|
573
|
+
if (!this.sessionsDb) {
|
|
574
|
+
return { success: false, error: 'Sessions DB not available' };
|
|
575
|
+
}
|
|
576
|
+
const permError = this.checkViewerOnly();
|
|
577
|
+
if (permError) {
|
|
578
|
+
return { success: false, error: permError };
|
|
579
|
+
}
|
|
580
|
+
const agentId = this.resolveManagedAgentId(input.agent_id);
|
|
581
|
+
const latestVer = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
|
|
582
|
+
if (!latestVer) {
|
|
583
|
+
return { success: false, error: `Agent '${agentId}' not found` };
|
|
584
|
+
}
|
|
585
|
+
this.syncViewerToAgentDetail(agentId);
|
|
586
|
+
return {
|
|
587
|
+
success: true,
|
|
588
|
+
agent_id: latestVer.agent_id,
|
|
589
|
+
version: latestVer.version,
|
|
590
|
+
config: JSON.parse(latestVer.snapshot),
|
|
591
|
+
system: latestVer.persona_text,
|
|
592
|
+
change_note: latestVer.change_note,
|
|
593
|
+
created_at: latestVer.created_at,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
case 'agent_activity': {
|
|
597
|
+
if (!this.sessionsDb) {
|
|
598
|
+
return { success: false, error: 'Sessions DB not available' };
|
|
599
|
+
}
|
|
600
|
+
const permError = this.checkViewerOnly();
|
|
601
|
+
if (permError) {
|
|
602
|
+
return { success: false, error: permError };
|
|
603
|
+
}
|
|
604
|
+
const args = input;
|
|
605
|
+
const agentId = this.resolveManagedAgentId(args.agent_id);
|
|
606
|
+
const rawLimit = Number.parseInt(String(args.limit ?? 20), 10);
|
|
607
|
+
const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 100) : 20;
|
|
608
|
+
const latestVer = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
|
|
609
|
+
if (!latestVer) {
|
|
610
|
+
return { success: false, error: `Agent '${args.agent_id}' not found` };
|
|
611
|
+
}
|
|
612
|
+
this.syncViewerToAgentDetail(agentId, 'activity');
|
|
613
|
+
return {
|
|
614
|
+
success: true,
|
|
615
|
+
agent_id: agentId,
|
|
616
|
+
activity: (0, agent_store_js_1.getActivity)(this.sessionsDb, agentId, limit),
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
case 'agent_update': {
|
|
620
|
+
if (!this.sessionsDb) {
|
|
621
|
+
return { success: false, error: 'Sessions DB not available' };
|
|
622
|
+
}
|
|
623
|
+
const permError = this.checkViewerOnly();
|
|
624
|
+
if (permError) {
|
|
625
|
+
return { success: false, error: permError };
|
|
626
|
+
}
|
|
627
|
+
const updateArgs = input;
|
|
628
|
+
const updateError = (0, managed_agent_validation_js_1.validateManagedAgentChanges)(updateArgs.changes);
|
|
629
|
+
if (updateError) {
|
|
630
|
+
return { success: false, error: updateError };
|
|
631
|
+
}
|
|
632
|
+
const agentId = this.resolveManagedAgentId(updateArgs.agent_id);
|
|
633
|
+
const initialLatest = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
|
|
634
|
+
if (!initialLatest) {
|
|
635
|
+
return { success: false, error: `Agent '${updateArgs.agent_id}' not found` };
|
|
636
|
+
}
|
|
637
|
+
return withManagedAgentMutationLock(agentId, async () => {
|
|
638
|
+
const updateLatest = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId);
|
|
639
|
+
if (!updateLatest) {
|
|
640
|
+
return { success: false, error: `Agent '${updateArgs.agent_id}' not found` };
|
|
641
|
+
}
|
|
642
|
+
if (updateLatest.version !== updateArgs.version) {
|
|
643
|
+
return {
|
|
644
|
+
success: false,
|
|
645
|
+
error: `Version conflict: current v${updateLatest.version}, sent v${updateArgs.version}`,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const synced = await (0, managed_agent_runtime_sync_js_1.updateManagedAgentRuntime)({
|
|
649
|
+
agentId,
|
|
650
|
+
changes: updateArgs.changes,
|
|
651
|
+
}, {
|
|
652
|
+
loadConfig: config_manager_js_1.loadConfig,
|
|
653
|
+
saveConfig: config_manager_js_1.saveConfig,
|
|
654
|
+
applyMultiAgentConfig: this.applyMultiAgentConfig,
|
|
655
|
+
restartMultiAgentAgent: this.restartMultiAgentAgent,
|
|
656
|
+
});
|
|
657
|
+
const updatedV = (0, agent_store_js_1.createAgentVersion)(this.sessionsDb, {
|
|
658
|
+
agent_id: agentId,
|
|
659
|
+
snapshot: synced.snapshot,
|
|
660
|
+
persona_text: synced.personaText ?? updateLatest.persona_text,
|
|
661
|
+
change_note: updateArgs.change_note,
|
|
662
|
+
});
|
|
663
|
+
return {
|
|
664
|
+
success: true,
|
|
665
|
+
new_version: updatedV.version,
|
|
666
|
+
runtime_reloaded: synced.runtimeReloaded,
|
|
667
|
+
};
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
case 'agent_create': {
|
|
671
|
+
if (!this.sessionsDb) {
|
|
672
|
+
return { success: false, error: 'Sessions DB not available' };
|
|
673
|
+
}
|
|
674
|
+
const permError = this.checkViewerOnly();
|
|
675
|
+
if (permError) {
|
|
676
|
+
return { success: false, error: permError };
|
|
677
|
+
}
|
|
678
|
+
const createArgs = input;
|
|
679
|
+
const createError = (0, managed_agent_validation_js_1.validateManagedAgentCreateInput)(createArgs);
|
|
680
|
+
if (createError) {
|
|
681
|
+
return { success: false, error: createError };
|
|
682
|
+
}
|
|
683
|
+
return withManagedAgentMutationLock(createArgs.id, async () => {
|
|
684
|
+
const existingAgent = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, createArgs.id);
|
|
685
|
+
if (existingAgent) {
|
|
686
|
+
return { success: false, error: `Agent '${createArgs.id}' already exists` };
|
|
687
|
+
}
|
|
688
|
+
const synced = await (0, managed_agent_runtime_sync_js_1.createManagedAgentRuntime)({
|
|
689
|
+
id: createArgs.id,
|
|
690
|
+
name: createArgs.name,
|
|
691
|
+
model: createArgs.model,
|
|
692
|
+
tier: createArgs.tier,
|
|
693
|
+
backend: createArgs.backend,
|
|
694
|
+
system: createArgs.system,
|
|
695
|
+
}, {
|
|
696
|
+
loadConfig: config_manager_js_1.loadConfig,
|
|
697
|
+
saveConfig: config_manager_js_1.saveConfig,
|
|
698
|
+
applyMultiAgentConfig: this.applyMultiAgentConfig,
|
|
699
|
+
restartMultiAgentAgent: this.restartMultiAgentAgent,
|
|
700
|
+
});
|
|
701
|
+
const createdV = (0, agent_store_js_1.createAgentVersion)(this.sessionsDb, {
|
|
702
|
+
agent_id: createArgs.id,
|
|
703
|
+
snapshot: synced.snapshot,
|
|
704
|
+
persona_text: synced.personaText,
|
|
705
|
+
change_note: 'Created via agent_create tool',
|
|
706
|
+
});
|
|
707
|
+
return {
|
|
708
|
+
success: true,
|
|
709
|
+
id: createArgs.id,
|
|
710
|
+
version: createdV.version,
|
|
711
|
+
runtime_reloaded: synced.runtimeReloaded,
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
case 'agent_compare': {
|
|
716
|
+
if (!this.sessionsDb) {
|
|
717
|
+
return { success: false, error: 'Sessions DB not available' };
|
|
718
|
+
}
|
|
719
|
+
const permError = this.checkViewerOnly();
|
|
720
|
+
if (permError) {
|
|
721
|
+
return { success: false, error: permError };
|
|
722
|
+
}
|
|
723
|
+
const cmpArgs = input;
|
|
724
|
+
const agentId = this.resolveManagedAgentId(cmpArgs.agent_id);
|
|
725
|
+
const cmpResult = (0, agent_store_js_1.compareVersionMetrics)(this.sessionsDb, agentId, cmpArgs.version_a, cmpArgs.version_b);
|
|
726
|
+
this.syncViewerToAgentDetail(agentId, 'validation');
|
|
727
|
+
return { success: true, agent_id: agentId, ...cmpResult };
|
|
728
|
+
}
|
|
729
|
+
// Viewer control tools (SmartStore pattern)
|
|
730
|
+
case 'viewer_state': {
|
|
731
|
+
if (!this.uiCommandQueue) {
|
|
732
|
+
return { success: false, error: 'UI command queue not available' };
|
|
733
|
+
}
|
|
734
|
+
const permError = this.checkViewerOnly();
|
|
735
|
+
if (permError) {
|
|
736
|
+
return { success: false, error: permError };
|
|
737
|
+
}
|
|
738
|
+
const { channelId } = this.getActiveRouting();
|
|
739
|
+
const ctx = this.uiCommandQueue.getPageContext(channelId || undefined);
|
|
740
|
+
return { success: true, context: ctx || { currentRoute: 'unknown', pageData: null } };
|
|
741
|
+
}
|
|
742
|
+
case 'viewer_navigate': {
|
|
743
|
+
if (!this.uiCommandQueue) {
|
|
744
|
+
return { success: false, error: 'UI command queue not available' };
|
|
745
|
+
}
|
|
746
|
+
const permError = this.checkViewerOnly();
|
|
747
|
+
if (permError) {
|
|
748
|
+
return { success: false, error: permError };
|
|
749
|
+
}
|
|
750
|
+
const navArgs = input;
|
|
751
|
+
this.uiCommandQueue.push({ type: 'navigate', payload: navArgs });
|
|
752
|
+
return { success: true, navigated: navArgs.route };
|
|
753
|
+
}
|
|
754
|
+
case 'viewer_notify': {
|
|
755
|
+
if (!this.uiCommandQueue) {
|
|
756
|
+
return { success: false, error: 'UI command queue not available' };
|
|
757
|
+
}
|
|
758
|
+
const permError = this.checkViewerOnly();
|
|
759
|
+
if (permError) {
|
|
760
|
+
return { success: false, error: permError };
|
|
761
|
+
}
|
|
762
|
+
const args = input;
|
|
763
|
+
this.uiCommandQueue.push({ type: 'notify', payload: args });
|
|
764
|
+
return { success: true, notified: true };
|
|
765
|
+
}
|
|
359
766
|
// Multi-Agent delegation
|
|
360
767
|
case 'delegate':
|
|
361
768
|
return await this.executeDelegate(input);
|
|
@@ -486,7 +893,9 @@ class GatewayToolExecutor {
|
|
|
486
893
|
}
|
|
487
894
|
case 'agent_notices': {
|
|
488
895
|
const rawLimit = Number(input.limit);
|
|
489
|
-
const limit = Number.isFinite(rawLimit)
|
|
896
|
+
const limit = Number.isFinite(rawLimit)
|
|
897
|
+
? Math.min(Math.max(Math.floor(rawLimit), 1), 100)
|
|
898
|
+
: 10;
|
|
490
899
|
if (!this.agentEventBus) {
|
|
491
900
|
return { success: false, error: 'Agent event bus not available' };
|
|
492
901
|
}
|
|
@@ -537,7 +946,8 @@ class GatewayToolExecutor {
|
|
|
537
946
|
}
|
|
538
947
|
// Fallback security for contexts without path restrictions:
|
|
539
948
|
// Only allow reading from ~/.mama/ directory
|
|
540
|
-
|
|
949
|
+
const context = this.getActiveContext();
|
|
950
|
+
if (!context?.role.allowedPaths?.length) {
|
|
541
951
|
const mamaDir = (0, path_1.resolve)(homeDir, '.mama');
|
|
542
952
|
const resolvedPath = (0, path_1.resolve)(expandedPath);
|
|
543
953
|
// Use path.relative to prevent path traversal (e.g., ~/.mama-evil/)
|
|
@@ -587,7 +997,8 @@ class GatewayToolExecutor {
|
|
|
587
997
|
}
|
|
588
998
|
// Fallback security for contexts without path restrictions:
|
|
589
999
|
// Only allow writing to ~/.mama/ directory
|
|
590
|
-
|
|
1000
|
+
const context = this.getActiveContext();
|
|
1001
|
+
if (!context?.role.allowedPaths?.length) {
|
|
591
1002
|
const mamaDir = (0, path_1.resolve)(homeDir, '.mama');
|
|
592
1003
|
const resolvedPath = (0, path_1.resolve)(expandedPath);
|
|
593
1004
|
// Use path.relative to prevent path traversal (e.g., ~/.mama-evil/)
|
|
@@ -618,11 +1029,12 @@ class GatewayToolExecutor {
|
|
|
618
1029
|
const destructive = /(systemctl\s+(?:--user\s+)?(?:stop|disable)\s+mama(?:-os)?\b|(?:kill|pkill|killall)\b[^\n]*\bmama(?:-os)?\b|\brm\b(?:\s+(?:-[^\n\s]*[rf][^\n\s]*|--recursive|--force))+\s+(?:\/(?:\s|$)|~(?:\/|\s|$)|\$HOME(?:\/|\s|$)|\/home(?:\/|\s|$)))/i;
|
|
619
1030
|
if (destructive.test(command)) {
|
|
620
1031
|
const audit = sanitizeCommandForAudit(command);
|
|
1032
|
+
const context = this.getActiveContext();
|
|
621
1033
|
const details = {
|
|
622
1034
|
category: 'destructive',
|
|
623
1035
|
...audit,
|
|
624
|
-
source:
|
|
625
|
-
sessionId:
|
|
1036
|
+
source: context?.source || null,
|
|
1037
|
+
sessionId: context?.session?.sessionId || null,
|
|
626
1038
|
};
|
|
627
1039
|
securityLogger.warn('[SECURITY] Dangerous Bash command blocked', details);
|
|
628
1040
|
(0, security_monitor_js_1.recordSecurityEvent)({
|
|
@@ -656,12 +1068,13 @@ class GatewayToolExecutor {
|
|
|
656
1068
|
for (const pattern of dangerousPatterns) {
|
|
657
1069
|
if (pattern.test(command)) {
|
|
658
1070
|
const audit = sanitizeCommandForAudit(command);
|
|
1071
|
+
const context = this.getActiveContext();
|
|
659
1072
|
const details = {
|
|
660
1073
|
category: 'pattern',
|
|
661
1074
|
pattern: pattern.toString(),
|
|
662
1075
|
...audit,
|
|
663
|
-
source:
|
|
664
|
-
sessionId:
|
|
1076
|
+
source: context?.source || null,
|
|
1077
|
+
sessionId: context?.session?.sessionId || null,
|
|
665
1078
|
};
|
|
666
1079
|
securityLogger.warn('[SECURITY] Dangerous Bash pattern blocked', details);
|
|
667
1080
|
(0, security_monitor_js_1.recordSecurityEvent)({
|
|
@@ -975,15 +1388,16 @@ class GatewayToolExecutor {
|
|
|
975
1388
|
* Returns error message if not allowed
|
|
976
1389
|
*/
|
|
977
1390
|
checkViewerOnly() {
|
|
978
|
-
|
|
1391
|
+
const context = this.getActiveContext();
|
|
1392
|
+
if (!context) {
|
|
979
1393
|
// No context = backward compatibility, allow
|
|
980
1394
|
return null;
|
|
981
1395
|
}
|
|
982
|
-
if (
|
|
983
|
-
return `Permission denied: This operation is only available from MAMA OS Viewer. Current source: ${
|
|
1396
|
+
if (context.source !== 'viewer') {
|
|
1397
|
+
return `Permission denied: This operation is only available from MAMA OS Viewer. Current source: ${context.source}`;
|
|
984
1398
|
}
|
|
985
|
-
if (!
|
|
986
|
-
return `Permission denied: Role "${
|
|
1399
|
+
if (!context.role.systemControl) {
|
|
1400
|
+
return `Permission denied: Role "${context.roleName}" does not have system control permissions`;
|
|
987
1401
|
}
|
|
988
1402
|
return null;
|
|
989
1403
|
}
|
|
@@ -1124,9 +1538,8 @@ class GatewayToolExecutor {
|
|
|
1124
1538
|
try {
|
|
1125
1539
|
const config = await (0, config_manager_js_1.loadConfig)();
|
|
1126
1540
|
// Determine if we should show sensitive data
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
this.currentContext?.role.sensitiveAccess;
|
|
1541
|
+
const context = this.getActiveContext();
|
|
1542
|
+
const showSensitive = includeSensitive && context?.source === 'viewer' && context?.role.sensitiveAccess;
|
|
1130
1543
|
// Mask sensitive data
|
|
1131
1544
|
const maskedConfig = this.maskSensitiveData(config, showSensitive);
|
|
1132
1545
|
// Return specific section or full config
|
|
@@ -1557,7 +1970,8 @@ class GatewayToolExecutor {
|
|
|
1557
1970
|
}
|
|
1558
1971
|
// Fallback security for contexts without path restrictions:
|
|
1559
1972
|
// Only allow reading from ~/.mama/ directory
|
|
1560
|
-
|
|
1973
|
+
const context = this.getActiveContext();
|
|
1974
|
+
if (!context?.role.allowedPaths?.length) {
|
|
1561
1975
|
const mamaDir = (0, path_1.resolve)(homeDir, '.mama');
|
|
1562
1976
|
const resolvedPath = (0, path_1.resolve)(expandedPath);
|
|
1563
1977
|
// Use path.relative to prevent path traversal (e.g., ~/.mama-evil/)
|
|
@@ -1603,6 +2017,272 @@ class GatewayToolExecutor {
|
|
|
1603
2017
|
/**
|
|
1604
2018
|
* Execute delegate tool — dispatch a task to another agent
|
|
1605
2019
|
*/
|
|
2020
|
+
// ── Agent Test ─────────────────────────────────────────────────────────────
|
|
2021
|
+
async executeAgentTest(input) {
|
|
2022
|
+
const permError = this.checkViewerOnly();
|
|
2023
|
+
if (permError) {
|
|
2024
|
+
return { success: false, error: permError };
|
|
2025
|
+
}
|
|
2026
|
+
const { agent_id } = input;
|
|
2027
|
+
const sample_count = Number.parseInt(String(input.sample_count ?? 2), 10);
|
|
2028
|
+
const resolvedAgentId = this.resolveManagedAgentId(agent_id);
|
|
2029
|
+
if (!Number.isFinite(sample_count) || sample_count < 1) {
|
|
2030
|
+
securityLogger.warn('[Agent test] Invalid sample_count received', {
|
|
2031
|
+
agent_id: resolvedAgentId,
|
|
2032
|
+
sample_count: input.sample_count ?? null,
|
|
2033
|
+
});
|
|
2034
|
+
return {
|
|
2035
|
+
success: false,
|
|
2036
|
+
error: `Invalid sample_count for '${resolvedAgentId}': ${String(input.sample_count)}. Must be >= 1.`,
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
// Concurrency guard
|
|
2040
|
+
if (this.testInFlight.has(resolvedAgentId)) {
|
|
2041
|
+
return { success: false, error: 'test_already_running' };
|
|
2042
|
+
}
|
|
2043
|
+
const promise = this._runAgentTest(resolvedAgentId, sample_count, input.test_data);
|
|
2044
|
+
this.testInFlight.set(resolvedAgentId, promise);
|
|
2045
|
+
try {
|
|
2046
|
+
return await promise;
|
|
2047
|
+
}
|
|
2048
|
+
finally {
|
|
2049
|
+
this.testInFlight.delete(resolvedAgentId);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
async _runAgentTest(agentId, sampleCount, testData) {
|
|
2053
|
+
if (!this.agentProcessManager || !this.delegationManagerRef) {
|
|
2054
|
+
return {
|
|
2055
|
+
success: false,
|
|
2056
|
+
error: 'agent_timeout: multi-agent not configured',
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
const startTime = Date.now();
|
|
2060
|
+
// 1. Collect test data
|
|
2061
|
+
let items;
|
|
2062
|
+
if (testData && testData.length > 0) {
|
|
2063
|
+
const normalizedItems = [];
|
|
2064
|
+
for (let index = 0; index < testData.length; index++) {
|
|
2065
|
+
const rawItem = testData[index];
|
|
2066
|
+
if (typeof rawItem.input !== 'string') {
|
|
2067
|
+
return {
|
|
2068
|
+
success: false,
|
|
2069
|
+
error: `Invalid test_data[${index}].input: expected string`,
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
if (rawItem.expected !== undefined && typeof rawItem.expected !== 'string') {
|
|
2073
|
+
return {
|
|
2074
|
+
success: false,
|
|
2075
|
+
error: `Invalid test_data[${index}].expected: expected string`,
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
normalizedItems.push({
|
|
2079
|
+
input: rawItem.input,
|
|
2080
|
+
...(typeof rawItem.expected === 'string' ? { expected: rawItem.expected } : {}),
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
items = normalizedItems;
|
|
2084
|
+
}
|
|
2085
|
+
else if (this.rawStore) {
|
|
2086
|
+
const agentConfig = this.delegationManagerRef.getAgentConfig(agentId);
|
|
2087
|
+
const connectors = agentConfig?.connectors ?? [];
|
|
2088
|
+
if (connectors.length === 0) {
|
|
2089
|
+
return {
|
|
2090
|
+
success: false,
|
|
2091
|
+
error: 'connector_unavailable: no connectors configured',
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
const allItems = [];
|
|
2095
|
+
const missingConnectors = [];
|
|
2096
|
+
for (const conn of connectors) {
|
|
2097
|
+
if (!this.rawStore.hasConnector(conn)) {
|
|
2098
|
+
missingConnectors.push(conn);
|
|
2099
|
+
continue;
|
|
2100
|
+
}
|
|
2101
|
+
const recent = this.rawStore.getRecent(conn, sampleCount);
|
|
2102
|
+
for (const item of recent) {
|
|
2103
|
+
allItems.push({ input: `[${item.type}] ${item.content}` });
|
|
2104
|
+
}
|
|
2105
|
+
if (allItems.length >= sampleCount) {
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
if (allItems.length === 0) {
|
|
2110
|
+
const detail = missingConnectors.length > 0
|
|
2111
|
+
? `connector(s) not found: ${missingConnectors.join(', ')}`
|
|
2112
|
+
: 'no recent data';
|
|
2113
|
+
return {
|
|
2114
|
+
success: false,
|
|
2115
|
+
error: `connector_unavailable: ${detail}`,
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
items = allItems.slice(0, sampleCount);
|
|
2119
|
+
}
|
|
2120
|
+
else {
|
|
2121
|
+
return {
|
|
2122
|
+
success: false,
|
|
2123
|
+
error: 'connector_unavailable: rawStore not available',
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
// 2. Start validation session for agent_test
|
|
2127
|
+
const testVer = this.sessionsDb ? (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId) : null;
|
|
2128
|
+
const testAgentVersion = testVer?.version ?? 0;
|
|
2129
|
+
let testValSession = null;
|
|
2130
|
+
try {
|
|
2131
|
+
testValSession =
|
|
2132
|
+
this.validationService?.startSession(agentId, testAgentVersion, 'agent_test', {
|
|
2133
|
+
goal: `Test with ${items.length} items`,
|
|
2134
|
+
customBeforeSnapshot: JSON.stringify({
|
|
2135
|
+
schema_version: 1,
|
|
2136
|
+
test_input_summary: items.map((i) => i.input.slice(0, 80)).join('; '),
|
|
2137
|
+
sample_count: items.length,
|
|
2138
|
+
}),
|
|
2139
|
+
}) ?? null;
|
|
2140
|
+
}
|
|
2141
|
+
catch (telemetryErr) {
|
|
2142
|
+
securityLogger.warn('[Agent test telemetry] Failed to start validation session', {
|
|
2143
|
+
agentId,
|
|
2144
|
+
testAgentVersion,
|
|
2145
|
+
error: telemetryErr instanceof Error ? telemetryErr.message : String(telemetryErr),
|
|
2146
|
+
});
|
|
2147
|
+
}
|
|
2148
|
+
// 3. Log test_run start
|
|
2149
|
+
let testRunId = null;
|
|
2150
|
+
if (this.sessionsDb) {
|
|
2151
|
+
try {
|
|
2152
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2153
|
+
agent_id: agentId,
|
|
2154
|
+
agent_version: testAgentVersion,
|
|
2155
|
+
type: 'test_run',
|
|
2156
|
+
input_summary: `Testing with ${items.length} items`,
|
|
2157
|
+
run_id: testValSession?.id,
|
|
2158
|
+
execution_status: 'started',
|
|
2159
|
+
trigger_reason: 'agent_test',
|
|
2160
|
+
});
|
|
2161
|
+
testRunId = row.id;
|
|
2162
|
+
if (testValSession && this.validationService) {
|
|
2163
|
+
this.validationService.recordRun(testValSession.id, { activityId: row.id });
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
catch (telemetryErr) {
|
|
2167
|
+
securityLogger.warn('[Agent test telemetry] Failed to persist startup activity', {
|
|
2168
|
+
agentId,
|
|
2169
|
+
testValSessionId: testValSession?.id ?? null,
|
|
2170
|
+
error: telemetryErr instanceof Error ? telemetryErr.message : String(telemetryErr),
|
|
2171
|
+
});
|
|
2172
|
+
testValSession = this.cleanupValidationSessionOnTelemetryFailure(testValSession, telemetryErr, 'agent_test startup telemetry failed');
|
|
2173
|
+
testRunId = null;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
// 4. Delegate with a small concurrency limit to keep tests responsive
|
|
2177
|
+
const results = [];
|
|
2178
|
+
const workerCount = Math.min(3, items.length);
|
|
2179
|
+
let nextIndex = 0;
|
|
2180
|
+
const workers = Array.from({ length: workerCount }, async () => {
|
|
2181
|
+
for (;;) {
|
|
2182
|
+
const currentIndex = nextIndex;
|
|
2183
|
+
nextIndex++;
|
|
2184
|
+
if (currentIndex >= items.length) {
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
const item = items[currentIndex];
|
|
2188
|
+
try {
|
|
2189
|
+
const r = await this.executeDelegate({
|
|
2190
|
+
agentId,
|
|
2191
|
+
task: `Process this data:\n${item.input}`,
|
|
2192
|
+
});
|
|
2193
|
+
const rAny = r;
|
|
2194
|
+
const output = r.success
|
|
2195
|
+
? String(rAny.data?.response ?? '')
|
|
2196
|
+
: undefined;
|
|
2197
|
+
results[currentIndex] = {
|
|
2198
|
+
input: item.input,
|
|
2199
|
+
output,
|
|
2200
|
+
error: r.success ? undefined : String(rAny.error ?? 'unknown'),
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
catch (err) {
|
|
2204
|
+
results[currentIndex] = { input: item.input, error: String(err) };
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
await Promise.all(workers);
|
|
2209
|
+
// 5. Auto-score: pass/fail ratio
|
|
2210
|
+
const passed = results.filter((r, index) => {
|
|
2211
|
+
if (r.error) {
|
|
2212
|
+
return false;
|
|
2213
|
+
}
|
|
2214
|
+
const expected = items[index]?.expected;
|
|
2215
|
+
if (expected === undefined) {
|
|
2216
|
+
return true;
|
|
2217
|
+
}
|
|
2218
|
+
return (r.output ?? '').trim() === expected.trim();
|
|
2219
|
+
}).length;
|
|
2220
|
+
const failed = results.length - passed;
|
|
2221
|
+
const autoScore = results.length > 0 ? Math.round((passed / results.length) * 100) : 0;
|
|
2222
|
+
if (this.sessionsDb && testRunId) {
|
|
2223
|
+
try {
|
|
2224
|
+
(0, agent_store_js_1.updateActivityScore)(this.sessionsDb, testRunId, autoScore, {
|
|
2225
|
+
total: results.length,
|
|
2226
|
+
passed,
|
|
2227
|
+
failed,
|
|
2228
|
+
items: results.map((r, index) => ({
|
|
2229
|
+
input: r.input.slice(0, 100),
|
|
2230
|
+
result: r.error ||
|
|
2231
|
+
(items[index]?.expected !== undefined &&
|
|
2232
|
+
(r.output ?? '').trim() !== items[index].expected.trim())
|
|
2233
|
+
? 'fail'
|
|
2234
|
+
: 'pass',
|
|
2235
|
+
})),
|
|
2236
|
+
}, 'completed');
|
|
2237
|
+
}
|
|
2238
|
+
catch (telemetryErr) {
|
|
2239
|
+
securityLogger.warn('[Agent test telemetry] Failed to persist test score', {
|
|
2240
|
+
agentId,
|
|
2241
|
+
testRunId,
|
|
2242
|
+
autoScore,
|
|
2243
|
+
totalResults: results.length,
|
|
2244
|
+
error: telemetryErr instanceof Error ? telemetryErr.message : String(telemetryErr),
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
// 6. Finalize validation session with test metrics
|
|
2249
|
+
const testDurationMs = Date.now() - startTime;
|
|
2250
|
+
if (testValSession && this.validationService) {
|
|
2251
|
+
try {
|
|
2252
|
+
this.validationService.finalizeSession(testValSession.id, {
|
|
2253
|
+
execution_status: 'completed',
|
|
2254
|
+
metrics: {
|
|
2255
|
+
duration_ms: testDurationMs,
|
|
2256
|
+
completion_rate: results.length > 0 ? passed / results.length : 0,
|
|
2257
|
+
auto_score: autoScore,
|
|
2258
|
+
},
|
|
2259
|
+
test_input_summary: items.map((i) => i.input.slice(0, 80)).join('; '),
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
catch (telemetryErr) {
|
|
2263
|
+
securityLogger.warn('[Agent test telemetry] Failed to finalize validation session', {
|
|
2264
|
+
agentId,
|
|
2265
|
+
testValSessionId: testValSession.id,
|
|
2266
|
+
autoScore,
|
|
2267
|
+
totalResults: results.length,
|
|
2268
|
+
error: telemetryErr instanceof Error ? telemetryErr.message : String(telemetryErr),
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
return {
|
|
2273
|
+
success: true,
|
|
2274
|
+
data: {
|
|
2275
|
+
test_run_id: testRunId,
|
|
2276
|
+
agent_id: agentId,
|
|
2277
|
+
results,
|
|
2278
|
+
auto_score: autoScore,
|
|
2279
|
+
duration_ms: testDurationMs,
|
|
2280
|
+
validation_session_id: testValSession?.id ?? null,
|
|
2281
|
+
...(testRunId === null ? { warning: 'score_not_persisted' } : {}),
|
|
2282
|
+
},
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
// ── Delegation ────────────────────────────────────────────────────────────
|
|
1606
2286
|
async executeDelegate(input) {
|
|
1607
2287
|
const { agentId, task, background } = input;
|
|
1608
2288
|
// Resolve skill path safely — reject path traversal attempts
|
|
@@ -1623,7 +2303,8 @@ class GatewayToolExecutor {
|
|
|
1623
2303
|
// Permission check using existing DelegationManager
|
|
1624
2304
|
// Default to 'conductor' when no agent context is set (e.g., MessageRouter path, audit cron)
|
|
1625
2305
|
// Conductor is the default agent and the only tier-1 agent that should delegate
|
|
1626
|
-
const
|
|
2306
|
+
const { agentId: activeAgentId, source: activeSource, channelId: activeChannelId, } = this.getActiveRouting();
|
|
2307
|
+
const sourceAgentId = activeAgentId || 'conductor';
|
|
1627
2308
|
const check = this.delegationManagerRef.isDelegationAllowed(sourceAgentId, agentId);
|
|
1628
2309
|
if (!check.allowed) {
|
|
1629
2310
|
return {
|
|
@@ -1631,16 +2312,44 @@ class GatewayToolExecutor {
|
|
|
1631
2312
|
error: `Delegation denied: ${check.reason}`,
|
|
1632
2313
|
};
|
|
1633
2314
|
}
|
|
1634
|
-
// Background delegation: fire-and-forget
|
|
2315
|
+
// Background delegation: fire-and-forget with async validation finalize
|
|
1635
2316
|
if (background) {
|
|
1636
|
-
const source =
|
|
1637
|
-
const channelId =
|
|
1638
|
-
//
|
|
2317
|
+
const source = activeSource || 'viewer';
|
|
2318
|
+
const channelId = activeChannelId || 'default';
|
|
2319
|
+
// Start validation session for background delegation
|
|
2320
|
+
let bgAgentVersion = 0;
|
|
2321
|
+
let bgValSession = null;
|
|
2322
|
+
try {
|
|
2323
|
+
const bgVer = this.sessionsDb ? (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId) : null;
|
|
2324
|
+
bgAgentVersion = bgVer?.version ?? 0;
|
|
2325
|
+
bgValSession =
|
|
2326
|
+
this.validationService?.startSession(agentId, bgAgentVersion, 'delegate_run') ?? null;
|
|
2327
|
+
if (this.sessionsDb) {
|
|
2328
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2329
|
+
agent_id: agentId,
|
|
2330
|
+
agent_version: bgAgentVersion,
|
|
2331
|
+
type: 'task_start',
|
|
2332
|
+
input_summary: task?.slice(0, 200),
|
|
2333
|
+
run_id: bgValSession?.id,
|
|
2334
|
+
execution_status: 'started',
|
|
2335
|
+
trigger_reason: 'delegate_run',
|
|
2336
|
+
});
|
|
2337
|
+
if (bgValSession && this.validationService) {
|
|
2338
|
+
this.validationService.recordRun(bgValSession.id, { activityId: row.id });
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
catch (telemetryErr) {
|
|
2343
|
+
securityLogger.warn('[Delegation telemetry] Background bootstrap failed', telemetryErr);
|
|
2344
|
+
bgAgentVersion = 0;
|
|
2345
|
+
bgValSession = this.cleanupValidationSessionOnTelemetryFailure(bgValSession, telemetryErr, 'background delegate bootstrap failed');
|
|
2346
|
+
}
|
|
2347
|
+
// Fire-and-forget: finalize validation when complete
|
|
2348
|
+
const bgStartTime = Date.now();
|
|
1639
2349
|
void (async () => {
|
|
1640
2350
|
try {
|
|
1641
2351
|
const process = await this.agentProcessManager.getProcess(source, channelId, agentId);
|
|
1642
2352
|
let delegationPrompt = this.delegationManagerRef.buildDelegationPrompt(sourceAgentId, task);
|
|
1643
|
-
// Inject skill content if specified
|
|
1644
2353
|
if (input.skill) {
|
|
1645
2354
|
const skillPath = resolveSkillPath(input.skill);
|
|
1646
2355
|
if (skillPath && (0, fs_1.existsSync)(skillPath)) {
|
|
@@ -1648,10 +2357,82 @@ class GatewayToolExecutor {
|
|
|
1648
2357
|
delegationPrompt = skillContent + '\n\n---\n\n' + delegationPrompt;
|
|
1649
2358
|
}
|
|
1650
2359
|
}
|
|
1651
|
-
await process.sendMessage(delegationPrompt);
|
|
2360
|
+
const result = await process.sendMessage(delegationPrompt);
|
|
2361
|
+
const durationMs = Date.now() - bgStartTime;
|
|
2362
|
+
try {
|
|
2363
|
+
if (this.sessionsDb) {
|
|
2364
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2365
|
+
agent_id: agentId,
|
|
2366
|
+
agent_version: bgAgentVersion,
|
|
2367
|
+
type: 'task_complete',
|
|
2368
|
+
input_summary: task?.slice(0, 200),
|
|
2369
|
+
output_summary: summarizeActivityOutput(result?.response),
|
|
2370
|
+
duration_ms: durationMs,
|
|
2371
|
+
run_id: bgValSession?.id,
|
|
2372
|
+
execution_status: 'completed',
|
|
2373
|
+
trigger_reason: 'delegate_run',
|
|
2374
|
+
});
|
|
2375
|
+
if (bgValSession && this.validationService) {
|
|
2376
|
+
this.validationService.recordRun(bgValSession.id, {
|
|
2377
|
+
activityId: row.id,
|
|
2378
|
+
duration_ms: durationMs,
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
catch (telemetryErr) {
|
|
2384
|
+
securityLogger.warn('[Delegation telemetry] Background completion activity failed', telemetryErr);
|
|
2385
|
+
}
|
|
2386
|
+
try {
|
|
2387
|
+
if (bgValSession && this.validationService) {
|
|
2388
|
+
this.validationService.finalizeSession(bgValSession.id, {
|
|
2389
|
+
execution_status: 'completed',
|
|
2390
|
+
metrics: { duration_ms: durationMs },
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
catch (telemetryErr) {
|
|
2395
|
+
securityLogger.warn('[Delegation telemetry] Background completion finalize failed', telemetryErr);
|
|
2396
|
+
}
|
|
1652
2397
|
}
|
|
1653
|
-
catch {
|
|
1654
|
-
|
|
2398
|
+
catch (err) {
|
|
2399
|
+
const durationMs = Date.now() - bgStartTime;
|
|
2400
|
+
try {
|
|
2401
|
+
if (this.sessionsDb) {
|
|
2402
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2403
|
+
agent_id: agentId,
|
|
2404
|
+
agent_version: bgAgentVersion,
|
|
2405
|
+
type: 'task_error',
|
|
2406
|
+
input_summary: task?.slice(0, 200),
|
|
2407
|
+
error_message: String(err),
|
|
2408
|
+
duration_ms: durationMs,
|
|
2409
|
+
run_id: bgValSession?.id,
|
|
2410
|
+
execution_status: 'failed',
|
|
2411
|
+
trigger_reason: 'delegate_run',
|
|
2412
|
+
});
|
|
2413
|
+
if (bgValSession && this.validationService) {
|
|
2414
|
+
this.validationService.recordRun(bgValSession.id, {
|
|
2415
|
+
activityId: row.id,
|
|
2416
|
+
duration_ms: durationMs,
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
catch (telemetryErr) {
|
|
2422
|
+
securityLogger.warn('[Delegation telemetry] Background failure activity failed', telemetryErr);
|
|
2423
|
+
}
|
|
2424
|
+
try {
|
|
2425
|
+
if (bgValSession && this.validationService) {
|
|
2426
|
+
this.validationService.finalizeSession(bgValSession.id, {
|
|
2427
|
+
execution_status: 'failed',
|
|
2428
|
+
error_message: String(err),
|
|
2429
|
+
metrics: { duration_ms: durationMs },
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
catch (telemetryErr) {
|
|
2434
|
+
securityLogger.warn('[Delegation telemetry] Background failure finalize failed', telemetryErr);
|
|
2435
|
+
}
|
|
1655
2436
|
}
|
|
1656
2437
|
})();
|
|
1657
2438
|
return {
|
|
@@ -1660,9 +2441,37 @@ class GatewayToolExecutor {
|
|
|
1660
2441
|
};
|
|
1661
2442
|
}
|
|
1662
2443
|
// Synchronous delegation with retry + backoff for resilience
|
|
1663
|
-
const source =
|
|
1664
|
-
const channelId =
|
|
2444
|
+
const source = activeSource || 'viewer';
|
|
2445
|
+
const channelId = activeChannelId || 'default';
|
|
1665
2446
|
const startTime = Date.now();
|
|
2447
|
+
// Start validation session for this delegation
|
|
2448
|
+
let agentVersion = 0;
|
|
2449
|
+
let valSession = null;
|
|
2450
|
+
try {
|
|
2451
|
+
const ver = this.sessionsDb ? (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, agentId) : null;
|
|
2452
|
+
agentVersion = ver?.version ?? 0;
|
|
2453
|
+
valSession =
|
|
2454
|
+
this.validationService?.startSession(agentId, agentVersion, 'delegate_run') ?? null;
|
|
2455
|
+
if (this.sessionsDb) {
|
|
2456
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2457
|
+
agent_id: agentId,
|
|
2458
|
+
agent_version: agentVersion,
|
|
2459
|
+
type: 'task_start',
|
|
2460
|
+
input_summary: task?.slice(0, 200),
|
|
2461
|
+
run_id: valSession?.id,
|
|
2462
|
+
execution_status: 'started',
|
|
2463
|
+
trigger_reason: 'delegate_run',
|
|
2464
|
+
});
|
|
2465
|
+
if (valSession && this.validationService) {
|
|
2466
|
+
this.validationService.recordRun(valSession.id, { activityId: row.id });
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
catch (telemetryErr) {
|
|
2471
|
+
securityLogger.warn('[Delegation telemetry] Validation bootstrap failed', telemetryErr);
|
|
2472
|
+
agentVersion = 0;
|
|
2473
|
+
valSession = this.cleanupValidationSessionOnTelemetryFailure(valSession, telemetryErr, 'delegate bootstrap failed');
|
|
2474
|
+
}
|
|
1666
2475
|
const MAX_RETRIES = 3;
|
|
1667
2476
|
let lastError = null;
|
|
1668
2477
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
@@ -1680,7 +2489,6 @@ class GatewayToolExecutor {
|
|
|
1680
2489
|
// Inject channel history for fresh processes (no prior context)
|
|
1681
2490
|
const sessionId = process.getSessionId?.();
|
|
1682
2491
|
if (!sessionId || attempt > 0) {
|
|
1683
|
-
// Fresh process or retried after crash — inject channel history for context
|
|
1684
2492
|
try {
|
|
1685
2493
|
const { getChannelHistory } = await import('../gateways/channel-history.js');
|
|
1686
2494
|
const channelHistory = getChannelHistory();
|
|
@@ -1692,13 +2500,49 @@ class GatewayToolExecutor {
|
|
|
1692
2500
|
}
|
|
1693
2501
|
}
|
|
1694
2502
|
catch {
|
|
1695
|
-
// Channel history injection is best-effort
|
|
2503
|
+
// Channel history injection is best-effort
|
|
1696
2504
|
}
|
|
1697
2505
|
}
|
|
1698
2506
|
const result = await process.sendMessage(delegationPrompt);
|
|
2507
|
+
const durationMs = Date.now() - startTime;
|
|
2508
|
+
try {
|
|
2509
|
+
if (this.sessionsDb) {
|
|
2510
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2511
|
+
agent_id: agentId,
|
|
2512
|
+
agent_version: agentVersion,
|
|
2513
|
+
type: 'task_complete',
|
|
2514
|
+
input_summary: task?.slice(0, 200),
|
|
2515
|
+
output_summary: summarizeActivityOutput(result.response),
|
|
2516
|
+
duration_ms: durationMs,
|
|
2517
|
+
run_id: valSession?.id,
|
|
2518
|
+
execution_status: 'completed',
|
|
2519
|
+
trigger_reason: 'delegate_run',
|
|
2520
|
+
});
|
|
2521
|
+
if (valSession && this.validationService) {
|
|
2522
|
+
this.validationService.recordRun(valSession.id, {
|
|
2523
|
+
activityId: row.id,
|
|
2524
|
+
duration_ms: durationMs,
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
catch (telemetryErr) {
|
|
2530
|
+
securityLogger.warn('[Delegation telemetry] Completion activity failed', telemetryErr);
|
|
2531
|
+
}
|
|
2532
|
+
try {
|
|
2533
|
+
if (valSession && this.validationService) {
|
|
2534
|
+
this.validationService.finalizeSession(valSession.id, {
|
|
2535
|
+
execution_status: 'completed',
|
|
2536
|
+
metrics: { duration_ms: durationMs },
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
catch (telemetryErr) {
|
|
2541
|
+
securityLogger.warn('[Delegation telemetry] Completion finalize failed', telemetryErr);
|
|
2542
|
+
}
|
|
1699
2543
|
return {
|
|
1700
2544
|
success: true,
|
|
1701
|
-
data: { agentId, response: result.response, duration_ms:
|
|
2545
|
+
data: { agentId, response: result.response, duration_ms: durationMs },
|
|
1702
2546
|
};
|
|
1703
2547
|
}
|
|
1704
2548
|
catch (err) {
|
|
@@ -1706,7 +2550,6 @@ class GatewayToolExecutor {
|
|
|
1706
2550
|
const isBusy = lastError.message.includes('busy');
|
|
1707
2551
|
const isCrash = lastError.message.includes('exited with code');
|
|
1708
2552
|
if (isCrash) {
|
|
1709
|
-
// Force remove crashed process so next getProcess() creates a fresh one
|
|
1710
2553
|
this.agentProcessManager.stopProcess(source, channelId, agentId);
|
|
1711
2554
|
}
|
|
1712
2555
|
if (attempt < MAX_RETRIES - 1 && (isBusy || isCrash)) {
|
|
@@ -1716,6 +2559,43 @@ class GatewayToolExecutor {
|
|
|
1716
2559
|
break;
|
|
1717
2560
|
}
|
|
1718
2561
|
}
|
|
2562
|
+
const failedDurationMs = Date.now() - startTime;
|
|
2563
|
+
try {
|
|
2564
|
+
if (this.sessionsDb) {
|
|
2565
|
+
const row = (0, agent_store_js_1.logActivity)(this.sessionsDb, {
|
|
2566
|
+
agent_id: agentId,
|
|
2567
|
+
agent_version: agentVersion,
|
|
2568
|
+
type: 'task_error',
|
|
2569
|
+
input_summary: task?.slice(0, 200),
|
|
2570
|
+
error_message: lastError?.message,
|
|
2571
|
+
duration_ms: failedDurationMs,
|
|
2572
|
+
run_id: valSession?.id,
|
|
2573
|
+
execution_status: 'failed',
|
|
2574
|
+
trigger_reason: 'delegate_run',
|
|
2575
|
+
});
|
|
2576
|
+
if (valSession && this.validationService) {
|
|
2577
|
+
this.validationService.recordRun(valSession.id, {
|
|
2578
|
+
activityId: row.id,
|
|
2579
|
+
duration_ms: failedDurationMs,
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
catch (telemetryErr) {
|
|
2585
|
+
securityLogger.warn('[Delegation telemetry] Failure activity failed', telemetryErr);
|
|
2586
|
+
}
|
|
2587
|
+
try {
|
|
2588
|
+
if (valSession && this.validationService) {
|
|
2589
|
+
this.validationService.finalizeSession(valSession.id, {
|
|
2590
|
+
execution_status: 'failed',
|
|
2591
|
+
error_message: lastError?.message,
|
|
2592
|
+
metrics: { duration_ms: failedDurationMs },
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
catch (telemetryErr) {
|
|
2597
|
+
securityLogger.warn('[Delegation telemetry] Failure finalize failed', telemetryErr);
|
|
2598
|
+
}
|
|
1719
2599
|
return {
|
|
1720
2600
|
success: false,
|
|
1721
2601
|
error: `Delegation to ${agentId} failed after ${MAX_RETRIES} attempts: ${lastError?.message}`,
|
|
@@ -1772,8 +2652,9 @@ class GatewayToolExecutor {
|
|
|
1772
2652
|
const { CodeActSandbox, HostBridge } = await import('./code-act/index.js');
|
|
1773
2653
|
const sandbox = new CodeActSandbox();
|
|
1774
2654
|
const bridge = new HostBridge(this);
|
|
1775
|
-
const
|
|
1776
|
-
|
|
2655
|
+
const context = this.getActiveContext();
|
|
2656
|
+
const tier = (context?.tier ?? 1);
|
|
2657
|
+
bridge.injectInto(sandbox, tier, context?.role);
|
|
1777
2658
|
const result = await sandbox.execute(input.code);
|
|
1778
2659
|
return {
|
|
1779
2660
|
success: result.success,
|
|
@@ -1786,16 +2667,17 @@ class GatewayToolExecutor {
|
|
|
1786
2667
|
* Handle mama_add — auto-extract facts from conversation content with derived memory scopes.
|
|
1787
2668
|
*/
|
|
1788
2669
|
async handleMamaAdd(input) {
|
|
1789
|
-
|
|
2670
|
+
const context = this.getActiveContext();
|
|
2671
|
+
if (!context) {
|
|
1790
2672
|
return {
|
|
1791
2673
|
success: false,
|
|
1792
2674
|
error: 'mama_add requires an active agent context',
|
|
1793
2675
|
};
|
|
1794
2676
|
}
|
|
1795
2677
|
const scopes = (0, scope_context_js_1.deriveMemoryScopes)({
|
|
1796
|
-
source:
|
|
1797
|
-
channelId:
|
|
1798
|
-
userId:
|
|
2678
|
+
source: context.source,
|
|
2679
|
+
channelId: context.session.channelId,
|
|
2680
|
+
userId: context.session.userId,
|
|
1799
2681
|
projectId: process.env.MAMA_WORKSPACE || process.cwd(),
|
|
1800
2682
|
});
|
|
1801
2683
|
return this.handleMamaIngest({
|
|
@@ -1819,11 +2701,12 @@ class GatewayToolExecutor {
|
|
|
1819
2701
|
error: 'Memory ingest API not available.',
|
|
1820
2702
|
};
|
|
1821
2703
|
}
|
|
1822
|
-
const
|
|
2704
|
+
const context = this.getActiveContext();
|
|
2705
|
+
const fallbackScopes = context
|
|
1823
2706
|
? (0, scope_context_js_1.deriveMemoryScopes)({
|
|
1824
|
-
source:
|
|
1825
|
-
channelId:
|
|
1826
|
-
userId:
|
|
2707
|
+
source: context.source,
|
|
2708
|
+
channelId: context.session.channelId,
|
|
2709
|
+
userId: context.session.userId,
|
|
1827
2710
|
projectId: process.env.MAMA_WORKSPACE || process.cwd(),
|
|
1828
2711
|
})
|
|
1829
2712
|
: [];
|
|
@@ -1845,7 +2728,7 @@ class GatewayToolExecutor {
|
|
|
1845
2728
|
source: {
|
|
1846
2729
|
package: 'standalone',
|
|
1847
2730
|
source_type: 'gateway_tool_executor',
|
|
1848
|
-
source:
|
|
2731
|
+
source: context?.source || null,
|
|
1849
2732
|
},
|
|
1850
2733
|
});
|
|
1851
2734
|
return {
|
|
@@ -1870,11 +2753,12 @@ class GatewayToolExecutor {
|
|
|
1870
2753
|
error: 'query is required and recallMemory API must be available',
|
|
1871
2754
|
};
|
|
1872
2755
|
}
|
|
1873
|
-
const
|
|
2756
|
+
const context = this.getActiveContext();
|
|
2757
|
+
const fallbackScopes = context
|
|
1874
2758
|
? (0, scope_context_js_1.deriveMemoryScopes)({
|
|
1875
|
-
source:
|
|
1876
|
-
channelId:
|
|
1877
|
-
userId:
|
|
2759
|
+
source: context.source,
|
|
2760
|
+
channelId: context.session.channelId,
|
|
2761
|
+
userId: context.session.userId,
|
|
1878
2762
|
projectId: process.env.MAMA_WORKSPACE || process.cwd(),
|
|
1879
2763
|
})
|
|
1880
2764
|
: [];
|