@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.
Files changed (171) hide show
  1. package/dist/agent/agent-loop.d.ts +25 -0
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +67 -14
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  6. package/dist/agent/code-act/host-bridge.js +98 -0
  7. package/dist/agent/code-act/host-bridge.js.map +1 -1
  8. package/dist/agent/code-act/type-definition-generator.d.ts.map +1 -1
  9. package/dist/agent/code-act/type-definition-generator.js +0 -1
  10. package/dist/agent/code-act/type-definition-generator.js.map +1 -1
  11. package/dist/agent/gateway-tool-executor.d.ts +36 -1
  12. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  13. package/dist/agent/gateway-tool-executor.js +938 -54
  14. package/dist/agent/gateway-tool-executor.js.map +1 -1
  15. package/dist/agent/gateway-tools.md +9 -0
  16. package/dist/agent/managed-agent-runtime-sync.d.ts +36 -0
  17. package/dist/agent/managed-agent-runtime-sync.d.ts.map +1 -0
  18. package/dist/agent/managed-agent-runtime-sync.js +207 -0
  19. package/dist/agent/managed-agent-runtime-sync.js.map +1 -0
  20. package/dist/agent/managed-agent-validation.d.ts +4 -0
  21. package/dist/agent/managed-agent-validation.d.ts.map +1 -0
  22. package/dist/agent/managed-agent-validation.js +84 -0
  23. package/dist/agent/managed-agent-validation.js.map +1 -0
  24. package/dist/agent/os-agent-capabilities.md +400 -0
  25. package/dist/agent/skill-loader.d.ts +2 -0
  26. package/dist/agent/skill-loader.d.ts.map +1 -1
  27. package/dist/agent/skill-loader.js +28 -0
  28. package/dist/agent/skill-loader.js.map +1 -1
  29. package/dist/agent/tool-registry.d.ts.map +1 -1
  30. package/dist/agent/tool-registry.js +66 -0
  31. package/dist/agent/tool-registry.js.map +1 -1
  32. package/dist/agent/types.d.ts +2 -1
  33. package/dist/agent/types.d.ts.map +1 -1
  34. package/dist/agent/types.js.map +1 -1
  35. package/dist/api/agent-handler.d.ts +34 -0
  36. package/dist/api/agent-handler.d.ts.map +1 -0
  37. package/dist/api/agent-handler.js +216 -0
  38. package/dist/api/agent-handler.js.map +1 -0
  39. package/dist/api/graph-api-types.d.ts +4 -0
  40. package/dist/api/graph-api-types.d.ts.map +1 -1
  41. package/dist/api/graph-api.d.ts +2 -2
  42. package/dist/api/graph-api.d.ts.map +1 -1
  43. package/dist/api/graph-api.js +480 -51
  44. package/dist/api/graph-api.js.map +1 -1
  45. package/dist/api/index.d.ts.map +1 -1
  46. package/dist/api/index.js +4 -0
  47. package/dist/api/index.js.map +1 -1
  48. package/dist/api/token-handler.d.ts +1 -0
  49. package/dist/api/token-handler.d.ts.map +1 -1
  50. package/dist/api/token-handler.js +4 -3
  51. package/dist/api/token-handler.js.map +1 -1
  52. package/dist/api/ui-command-handler.d.ts +48 -0
  53. package/dist/api/ui-command-handler.d.ts.map +1 -0
  54. package/dist/api/ui-command-handler.js +160 -0
  55. package/dist/api/ui-command-handler.js.map +1 -0
  56. package/dist/cli/commands/start.d.ts.map +1 -1
  57. package/dist/cli/commands/start.js +127 -1
  58. package/dist/cli/commands/start.js.map +1 -1
  59. package/dist/cli/config/config-manager.d.ts.map +1 -1
  60. package/dist/cli/config/config-manager.js +16 -31
  61. package/dist/cli/config/config-manager.js.map +1 -1
  62. package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
  63. package/dist/cli/runtime/agent-loop-init.js +31 -7
  64. package/dist/cli/runtime/agent-loop-init.js.map +1 -1
  65. package/dist/cli/runtime/api-routes-init.d.ts +3 -0
  66. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  67. package/dist/cli/runtime/api-routes-init.js +283 -34
  68. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  69. package/dist/cli/runtime/gateway-init.d.ts +2 -1
  70. package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
  71. package/dist/cli/runtime/gateway-init.js +5 -1
  72. package/dist/cli/runtime/gateway-init.js.map +1 -1
  73. package/dist/connectors/framework/raw-store.d.ts +4 -0
  74. package/dist/connectors/framework/raw-store.d.ts.map +1 -1
  75. package/dist/connectors/framework/raw-store.js +33 -10
  76. package/dist/connectors/framework/raw-store.js.map +1 -1
  77. package/dist/db/agent-store.d.ts +115 -0
  78. package/dist/db/agent-store.d.ts.map +1 -0
  79. package/dist/db/agent-store.js +248 -0
  80. package/dist/db/agent-store.js.map +1 -0
  81. package/dist/db/migrations/agent-activity-validation-columns.d.ts +3 -0
  82. package/dist/db/migrations/agent-activity-validation-columns.d.ts.map +1 -0
  83. package/dist/db/migrations/agent-activity-validation-columns.js +22 -0
  84. package/dist/db/migrations/agent-activity-validation-columns.js.map +1 -0
  85. package/dist/db/migrations/agent-metrics-response-avg.d.ts +3 -0
  86. package/dist/db/migrations/agent-metrics-response-avg.d.ts.map +1 -0
  87. package/dist/db/migrations/agent-metrics-response-avg.js +19 -0
  88. package/dist/db/migrations/agent-metrics-response-avg.js.map +1 -0
  89. package/dist/db/migrations/agent-store-tables.d.ts +3 -0
  90. package/dist/db/migrations/agent-store-tables.d.ts.map +1 -0
  91. package/dist/db/migrations/agent-store-tables.js +59 -0
  92. package/dist/db/migrations/agent-store-tables.js.map +1 -0
  93. package/dist/db/migrations/token-usage-agent-version.d.ts +3 -0
  94. package/dist/db/migrations/token-usage-agent-version.d.ts.map +1 -0
  95. package/dist/db/migrations/token-usage-agent-version.js +16 -0
  96. package/dist/db/migrations/token-usage-agent-version.js.map +1 -0
  97. package/dist/db/migrations/validation-session-tables.d.ts +3 -0
  98. package/dist/db/migrations/validation-session-tables.d.ts.map +1 -0
  99. package/dist/db/migrations/validation-session-tables.js +59 -0
  100. package/dist/db/migrations/validation-session-tables.js.map +1 -0
  101. package/dist/gateways/message-router.d.ts +10 -0
  102. package/dist/gateways/message-router.d.ts.map +1 -1
  103. package/dist/gateways/message-router.js +188 -14
  104. package/dist/gateways/message-router.js.map +1 -1
  105. package/dist/gateways/types.d.ts +1 -1
  106. package/dist/gateways/types.d.ts.map +1 -1
  107. package/dist/multi-agent/agent-process-manager.js +1 -1
  108. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  109. package/dist/multi-agent/conductor-persona.d.ts +13 -0
  110. package/dist/multi-agent/conductor-persona.d.ts.map +1 -0
  111. package/dist/multi-agent/conductor-persona.js +157 -0
  112. package/dist/multi-agent/conductor-persona.js.map +1 -0
  113. package/dist/multi-agent/dashboard-agent-persona.d.ts +1 -1
  114. package/dist/multi-agent/dashboard-agent-persona.d.ts.map +1 -1
  115. package/dist/multi-agent/dashboard-agent-persona.js +7 -3
  116. package/dist/multi-agent/dashboard-agent-persona.js.map +1 -1
  117. package/dist/multi-agent/delegation-manager.d.ts +5 -0
  118. package/dist/multi-agent/delegation-manager.d.ts.map +1 -1
  119. package/dist/multi-agent/delegation-manager.js +37 -0
  120. package/dist/multi-agent/delegation-manager.js.map +1 -1
  121. package/dist/multi-agent/ultrawork.d.ts +3 -0
  122. package/dist/multi-agent/ultrawork.d.ts.map +1 -1
  123. package/dist/multi-agent/ultrawork.js +9 -0
  124. package/dist/multi-agent/ultrawork.js.map +1 -1
  125. package/dist/validation/session-service.d.ts +72 -0
  126. package/dist/validation/session-service.d.ts.map +1 -0
  127. package/dist/validation/session-service.js +298 -0
  128. package/dist/validation/session-service.js.map +1 -0
  129. package/dist/validation/store.d.ts +25 -0
  130. package/dist/validation/store.d.ts.map +1 -0
  131. package/dist/validation/store.js +200 -0
  132. package/dist/validation/store.js.map +1 -0
  133. package/dist/validation/types.d.ts +119 -0
  134. package/dist/validation/types.d.ts.map +1 -0
  135. package/dist/validation/types.js +57 -0
  136. package/dist/validation/types.js.map +1 -0
  137. package/package.json +3 -3
  138. package/public/viewer/js/modules/agents.js +1148 -0
  139. package/public/viewer/js/modules/chat.js +20 -11
  140. package/public/viewer/js/modules/connector-feed.js +35 -0
  141. package/public/viewer/js/modules/dashboard.js +49 -0
  142. package/public/viewer/js/modules/memory.js +32 -0
  143. package/public/viewer/js/modules/settings.js +34 -79
  144. package/public/viewer/js/modules/wiki.js +59 -4
  145. package/public/viewer/js/utils/api.js +70 -0
  146. package/public/viewer/js/utils/dom.js +3 -0
  147. package/public/viewer/js/utils/ui-commands.js +93 -0
  148. package/public/viewer/log-viewer.html +2 -2
  149. package/public/viewer/src/modules/agents.ts +1299 -0
  150. package/public/viewer/src/modules/chat.ts +23 -14
  151. package/public/viewer/src/modules/connector-feed.ts +35 -0
  152. package/public/viewer/src/modules/dashboard.ts +50 -0
  153. package/public/viewer/src/modules/memory.ts +31 -0
  154. package/public/viewer/src/modules/settings.ts +36 -96
  155. package/public/viewer/src/modules/wiki.ts +73 -6
  156. package/public/viewer/src/types/global.d.ts +0 -9
  157. package/public/viewer/src/utils/api.ts +156 -2
  158. package/public/viewer/src/utils/dom.ts +6 -1
  159. package/public/viewer/src/utils/ui-commands.ts +118 -0
  160. package/public/viewer/viewer.css +105 -10
  161. package/public/viewer/viewer.html +1868 -777
  162. package/scripts/generate-gateway-tools.ts +5 -1
  163. package/public/viewer/js/modules/playground.js +0 -148
  164. package/public/viewer/js/modules/skills.js +0 -451
  165. package/public/viewer/src/modules/playground.ts +0 -173
  166. package/public/viewer/src/modules/skills.ts +0 -491
  167. package/templates/playgrounds/cron-workflow-lab.html +0 -1601
  168. package/templates/playgrounds/mama-log-viewer.html +0 -1341
  169. package/templates/playgrounds/skill-lab-playground.html +0 -1625
  170. package/templates/playgrounds/wave-visualizer.html +0 -694
  171. 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.currentContext;
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
- if (!this.currentContext) {
440
+ const context = this.getActiveContext();
441
+ if (!context) {
237
442
  return { allowed: true };
238
443
  }
239
- const role = this.currentContext.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 "${this.currentContext.roleName}"`,
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
- if (!this.currentContext) {
460
+ const context = this.getActiveContext();
461
+ if (!context) {
256
462
  return { allowed: true };
257
463
  }
258
- const role = this.currentContext.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 "${this.currentContext.roleName}"`,
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) ? Math.min(Math.max(Math.floor(rawLimit), 1), 100) : 10;
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
- if (!this.currentContext?.role.allowedPaths?.length) {
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
- if (!this.currentContext?.role.allowedPaths?.length) {
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: this.currentContext?.source || null,
625
- sessionId: this.currentContext?.session?.sessionId || null,
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: this.currentContext?.source || null,
664
- sessionId: this.currentContext?.session?.sessionId || null,
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
- if (!this.currentContext) {
1391
+ const context = this.getActiveContext();
1392
+ if (!context) {
979
1393
  // No context = backward compatibility, allow
980
1394
  return null;
981
1395
  }
982
- if (this.currentContext.source !== 'viewer') {
983
- return `Permission denied: This operation is only available from MAMA OS Viewer. Current source: ${this.currentContext.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 (!this.currentContext.role.systemControl) {
986
- return `Permission denied: Role "${this.currentContext.roleName}" does not have system control permissions`;
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 showSensitive = includeSensitive &&
1128
- this.currentContext?.source === 'viewer' &&
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
- if (!this.currentContext?.role.allowedPaths?.length) {
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 sourceAgentId = this.currentAgentId || 'conductor';
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 = this.currentSource || 'viewer';
1637
- const channelId = this.currentChannelId || 'default';
1638
- // Fire-and-forget: spawn process and send message without awaiting result
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
- // Background task failures are silently ignored
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 = this.currentSource || 'viewer';
1664
- const channelId = this.currentChannelId || 'default';
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 — proceed without it
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: Date.now() - startTime },
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 tier = (this.currentContext?.tier ?? 1);
1776
- bridge.injectInto(sandbox, tier, this.currentContext?.role);
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
- if (!this.currentContext) {
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: this.currentContext.source,
1797
- channelId: this.currentContext.session.channelId,
1798
- userId: this.currentContext.session.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 fallbackScopes = this.currentContext
2704
+ const context = this.getActiveContext();
2705
+ const fallbackScopes = context
1823
2706
  ? (0, scope_context_js_1.deriveMemoryScopes)({
1824
- source: this.currentContext.source,
1825
- channelId: this.currentContext.session.channelId,
1826
- userId: this.currentContext.session.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: this.currentContext?.source || null,
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 fallbackScopes = this.currentContext
2756
+ const context = this.getActiveContext();
2757
+ const fallbackScopes = context
1874
2758
  ? (0, scope_context_js_1.deriveMemoryScopes)({
1875
- source: this.currentContext.source,
1876
- channelId: this.currentContext.session.channelId,
1877
- userId: this.currentContext.session.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
  : [];