@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
package/dist/api/graph-api.js
CHANGED
|
@@ -10,11 +10,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.
|
|
13
|
+
exports.VIEWER_CSS_PATH = exports.VIEWER_HTML_PATH = exports.DEFAULT_GRAPH_LIMIT = void 0;
|
|
14
14
|
exports.createGraphHandler = createGraphHandler;
|
|
15
15
|
exports.mapDecisionRowToGraphNode = mapDecisionRowToGraphNode;
|
|
16
16
|
exports.parseGraphLimit = parseGraphLimit;
|
|
17
17
|
exports.buildGraphMeta = buildGraphMeta;
|
|
18
|
+
exports.validateConfigUpdate = validateConfigUpdate;
|
|
18
19
|
exports.getAllNodes = getAllNodes;
|
|
19
20
|
exports.getAllEdges = getAllEdges;
|
|
20
21
|
exports.getAllCheckpoints = getAllCheckpoints;
|
|
@@ -26,6 +27,9 @@ const path_1 = __importDefault(require("path"));
|
|
|
26
27
|
const os_1 = __importDefault(require("os"));
|
|
27
28
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
28
29
|
const auth_middleware_js_1 = require("./auth-middleware.js");
|
|
30
|
+
const agent_handler_js_1 = require("./agent-handler.js");
|
|
31
|
+
const ui_command_handler_js_1 = require("./ui-command-handler.js");
|
|
32
|
+
const store_js_1 = require("../validation/store.js");
|
|
29
33
|
// mama-core is pure JS with no .d.ts — require + any is intentional
|
|
30
34
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
35
|
const { getAdapter, initDB, vectorSearch } = require('@jungjaehoon/mama-core/memory-store');
|
|
@@ -62,8 +66,6 @@ const VIEWER_HTML_PATH = path_1.default.join(VIEWER_DIR, 'viewer.html');
|
|
|
62
66
|
exports.VIEWER_HTML_PATH = VIEWER_HTML_PATH;
|
|
63
67
|
const VIEWER_CSS_PATH = path_1.default.join(VIEWER_DIR, 'viewer.css');
|
|
64
68
|
exports.VIEWER_CSS_PATH = VIEWER_CSS_PATH;
|
|
65
|
-
const VIEWER_JS_PATH = path_1.default.join(VIEWER_DIR, 'viewer.js');
|
|
66
|
-
exports.VIEWER_JS_PATH = VIEWER_JS_PATH;
|
|
67
69
|
const SW_JS_PATH = path_1.default.join(VIEWER_DIR, 'sw.js');
|
|
68
70
|
const MANIFEST_JSON_PATH = path_1.default.join(VIEWER_DIR, 'manifest.json');
|
|
69
71
|
const VIEWER_ICON_DIR = path_1.default.join(VIEWER_DIR, 'icons');
|
|
@@ -76,6 +78,14 @@ const SIMILAR_QUERY_DECISION_CHARS = 400;
|
|
|
76
78
|
// Model pattern helpers (used in multiple validation functions)
|
|
77
79
|
const isClaudeModel = (model) => /^claude-/i.test(model);
|
|
78
80
|
const isCodexModel = (model) => /^(gpt-|o\d|codex)/i.test(model);
|
|
81
|
+
const supportedManagedBackends = ['claude', 'codex', 'codex-mcp', 'gemini'];
|
|
82
|
+
const isCodexFamilyBackend = (backend) => backend === 'codex' || backend === 'codex-mcp';
|
|
83
|
+
const VALIDATION_TRIGGER_TYPES = new Set([
|
|
84
|
+
'agent_test',
|
|
85
|
+
'delegate_run',
|
|
86
|
+
'system_run',
|
|
87
|
+
'audit',
|
|
88
|
+
]);
|
|
79
89
|
const isOpus46Model = (model) => /^claude-opus-4-6(?:$|-)/i.test(model) || model.toLowerCase() === 'claude-opus-4-latest';
|
|
80
90
|
const VALID_EFFORT_LEVELS = new Set(['low', 'medium', 'high', 'max']);
|
|
81
91
|
// Allowed directories for persona files (security: prevent arbitrary file read)
|
|
@@ -313,9 +323,6 @@ function handleViewerRequest(_req, res) {
|
|
|
313
323
|
function handleCssRequest(_req, res) {
|
|
314
324
|
serveStaticFile(res, VIEWER_CSS_PATH, 'text/css');
|
|
315
325
|
}
|
|
316
|
-
function handleJsRequest(_req, res) {
|
|
317
|
-
serveStaticFile(res, VIEWER_JS_PATH, 'application/javascript');
|
|
318
|
-
}
|
|
319
326
|
async function handleGraphRequest(_req, res, params) {
|
|
320
327
|
const startTime = Date.now();
|
|
321
328
|
try {
|
|
@@ -450,6 +457,28 @@ function readBody(req) {
|
|
|
450
457
|
req.on('error', reject);
|
|
451
458
|
});
|
|
452
459
|
}
|
|
460
|
+
async function readBodyOrRespond(req, res) {
|
|
461
|
+
try {
|
|
462
|
+
return await readBody(req);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
466
|
+
const statusCode = message === 'Invalid JSON'
|
|
467
|
+
? 400
|
|
468
|
+
: message.includes('Request body too large (max 1MB)')
|
|
469
|
+
? 413
|
|
470
|
+
: 500;
|
|
471
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
472
|
+
res.end(JSON.stringify({
|
|
473
|
+
error: message === 'Invalid JSON'
|
|
474
|
+
? 'Invalid JSON'
|
|
475
|
+
: message.includes('Request body too large (max 1MB)')
|
|
476
|
+
? 'Request body too large (max 1MB)'
|
|
477
|
+
: message,
|
|
478
|
+
}));
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
453
482
|
async function handleUpdateRequest(req, res) {
|
|
454
483
|
try {
|
|
455
484
|
const body = await readBody(req);
|
|
@@ -783,6 +812,20 @@ async function handleCheckpointsRequest(_req, res) {
|
|
|
783
812
|
}
|
|
784
813
|
}
|
|
785
814
|
function createGraphHandler(options = {}) {
|
|
815
|
+
const parsePositiveInt = (value, fallback, max) => {
|
|
816
|
+
const parsed = Number.parseInt(value ?? '', 10);
|
|
817
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
818
|
+
return fallback;
|
|
819
|
+
}
|
|
820
|
+
return Math.min(parsed, max);
|
|
821
|
+
};
|
|
822
|
+
const parseValidationTriggerType = (value) => {
|
|
823
|
+
if (!value) {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
const normalized = value.trim().toLowerCase();
|
|
827
|
+
return VALIDATION_TRIGGER_TYPES.has(normalized) ? normalized : null;
|
|
828
|
+
};
|
|
786
829
|
return async function graphHandler(req, res) {
|
|
787
830
|
if (!req.url) {
|
|
788
831
|
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
@@ -826,16 +869,6 @@ function createGraphHandler(options = {}) {
|
|
|
826
869
|
handleCssRequest(req, res);
|
|
827
870
|
return true;
|
|
828
871
|
}
|
|
829
|
-
// Route: GET/HEAD /viewer.css - serve stylesheet (legacy path)
|
|
830
|
-
if (pathname === '/viewer.css' && (req.method === 'GET' || req.method === 'HEAD')) {
|
|
831
|
-
handleCssRequest(req, res);
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
// Route: GET/HEAD /viewer.js - serve JavaScript
|
|
835
|
-
if (pathname === '/viewer.js' && (req.method === 'GET' || req.method === 'HEAD')) {
|
|
836
|
-
handleJsRequest(req, res);
|
|
837
|
-
return true;
|
|
838
|
-
}
|
|
839
872
|
// Route: GET/HEAD /sw.js - serve Service Worker
|
|
840
873
|
if (pathname === '/sw.js' && (req.method === 'GET' || req.method === 'HEAD')) {
|
|
841
874
|
serveStaticFile(res, SW_JS_PATH, 'application/javascript');
|
|
@@ -851,11 +884,6 @@ function createGraphHandler(options = {}) {
|
|
|
851
884
|
serveStaticFile(res, MANIFEST_JSON_PATH, 'application/json');
|
|
852
885
|
return true;
|
|
853
886
|
}
|
|
854
|
-
// Route: GET/HEAD /manifest.json - legacy or custom host compatibility
|
|
855
|
-
if (pathname === '/manifest.json' && (req.method === 'GET' || req.method === 'HEAD')) {
|
|
856
|
-
serveStaticFile(res, MANIFEST_JSON_PATH, 'application/json');
|
|
857
|
-
return true;
|
|
858
|
-
}
|
|
859
887
|
// Route: GET/HEAD /favicon.ico - serve favicon
|
|
860
888
|
if (pathname === '/favicon.ico' && (req.method === 'GET' || req.method === 'HEAD')) {
|
|
861
889
|
serveStaticFile(res, VIEWER_FAVICON_PATH, 'image/x-icon');
|
|
@@ -897,24 +925,6 @@ function createGraphHandler(options = {}) {
|
|
|
897
925
|
serveStaticFile(res, filePath, 'application/javascript');
|
|
898
926
|
return true;
|
|
899
927
|
}
|
|
900
|
-
// Route: GET/HEAD /js/utils/*.js - serve utility modules (legacy path)
|
|
901
|
-
if (pathname.startsWith('/js/utils/') &&
|
|
902
|
-
pathname.endsWith('.js') &&
|
|
903
|
-
(req.method === 'GET' || req.method === 'HEAD')) {
|
|
904
|
-
const fileName = pathname.split('/').pop();
|
|
905
|
-
const filePath = path_1.default.join(VIEWER_DIR, 'js', 'utils', fileName);
|
|
906
|
-
serveStaticFile(res, filePath, 'application/javascript');
|
|
907
|
-
return true;
|
|
908
|
-
}
|
|
909
|
-
// Route: GET/HEAD /js/modules/*.js - serve feature modules (legacy path)
|
|
910
|
-
if (pathname.startsWith('/js/modules/') &&
|
|
911
|
-
pathname.endsWith('.js') &&
|
|
912
|
-
(req.method === 'GET' || req.method === 'HEAD')) {
|
|
913
|
-
const fileName = pathname.split('/').pop();
|
|
914
|
-
const filePath = path_1.default.join(VIEWER_DIR, 'js', 'modules', fileName);
|
|
915
|
-
serveStaticFile(res, filePath, 'application/javascript');
|
|
916
|
-
return true;
|
|
917
|
-
}
|
|
918
928
|
// ── Auth gate: all routes below require authentication ──
|
|
919
929
|
// Static assets (viewer, css, js, icons) are served above without auth.
|
|
920
930
|
// All data API routes below must pass isAuthenticated().
|
|
@@ -1269,6 +1279,425 @@ function createGraphHandler(options = {}) {
|
|
|
1269
1279
|
await handleExportRequest(req, res, params);
|
|
1270
1280
|
return true;
|
|
1271
1281
|
}
|
|
1282
|
+
// ── UI Command API (SmartStore bidirectional communication) ──
|
|
1283
|
+
// Route: GET /api/ui/commands — viewer polls for pending commands
|
|
1284
|
+
if (pathname === '/api/ui/commands' && req.method === 'GET') {
|
|
1285
|
+
if (options.uiCommandQueue) {
|
|
1286
|
+
(0, ui_command_handler_js_1.handleGetUICommands)(res, options.uiCommandQueue);
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1290
|
+
res.end(JSON.stringify({ commands: [] }));
|
|
1291
|
+
}
|
|
1292
|
+
return true;
|
|
1293
|
+
}
|
|
1294
|
+
// Route: GET /api/ui/page-context — agent reads current viewer state
|
|
1295
|
+
if (pathname === '/api/ui/page-context' && req.method === 'GET') {
|
|
1296
|
+
if (options.uiCommandQueue) {
|
|
1297
|
+
const { handleGetPageContext } = await import('./ui-command-handler.js');
|
|
1298
|
+
handleGetPageContext(res, options.uiCommandQueue);
|
|
1299
|
+
}
|
|
1300
|
+
else {
|
|
1301
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1302
|
+
res.end(JSON.stringify({ success: true, context: null }));
|
|
1303
|
+
}
|
|
1304
|
+
return true;
|
|
1305
|
+
}
|
|
1306
|
+
// Route: POST /api/ui/page-context — viewer reports current page state
|
|
1307
|
+
if (pathname === '/api/ui/page-context' && req.method === 'POST') {
|
|
1308
|
+
if (options.uiCommandQueue) {
|
|
1309
|
+
const body = await readBodyOrRespond(req, res);
|
|
1310
|
+
if (!body) {
|
|
1311
|
+
return true;
|
|
1312
|
+
}
|
|
1313
|
+
(0, ui_command_handler_js_1.handlePostPageContext)(res, body, options.uiCommandQueue);
|
|
1314
|
+
}
|
|
1315
|
+
else {
|
|
1316
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1317
|
+
res.end(JSON.stringify({ success: true }));
|
|
1318
|
+
}
|
|
1319
|
+
return true;
|
|
1320
|
+
}
|
|
1321
|
+
// Route: POST /api/ui/commands — agent pushes UI commands
|
|
1322
|
+
if (pathname === '/api/ui/commands' && req.method === 'POST') {
|
|
1323
|
+
if (options.uiCommandQueue) {
|
|
1324
|
+
const body = await readBodyOrRespond(req, res);
|
|
1325
|
+
if (!body) {
|
|
1326
|
+
return true;
|
|
1327
|
+
}
|
|
1328
|
+
(0, ui_command_handler_js_1.handlePostUICommand)(res, body, options.uiCommandQueue);
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1332
|
+
res.end(JSON.stringify({ success: true }));
|
|
1333
|
+
}
|
|
1334
|
+
return true;
|
|
1335
|
+
}
|
|
1336
|
+
// Route: POST /api/ui/commands/ack — viewer acknowledges applied commands
|
|
1337
|
+
if (pathname === '/api/ui/commands/ack' && req.method === 'POST') {
|
|
1338
|
+
if (options.uiCommandQueue) {
|
|
1339
|
+
const body = await readBodyOrRespond(req, res);
|
|
1340
|
+
if (!body) {
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
const { handlePostUICommandAck } = await import('./ui-command-handler.js');
|
|
1344
|
+
handlePostUICommandAck(res, body, options.uiCommandQueue);
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1348
|
+
res.end(JSON.stringify({ success: true, acknowledged: 0 }));
|
|
1349
|
+
}
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
// ── Agent Management API (Managed Agents pattern) ──
|
|
1353
|
+
// Route: GET /api/agents/:id/versions/:v1/compare/:v2 — before/after (must be before /api/agents/:id)
|
|
1354
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/versions\/\d+\/compare\/\d+$/) &&
|
|
1355
|
+
req.method === 'GET') {
|
|
1356
|
+
const parts = pathname.split('/');
|
|
1357
|
+
const agentId = decodeURIComponent(parts[3]);
|
|
1358
|
+
const v1 = parseInt(parts[5], 10);
|
|
1359
|
+
const v2 = parseInt(parts[7], 10);
|
|
1360
|
+
if (options.sessionsDb) {
|
|
1361
|
+
(0, agent_handler_js_1.handleCompareVersions)(res, agentId, v1, v2, options.sessionsDb);
|
|
1362
|
+
}
|
|
1363
|
+
else {
|
|
1364
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1365
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1366
|
+
}
|
|
1367
|
+
return true;
|
|
1368
|
+
}
|
|
1369
|
+
// Route: GET /api/agents/:id/versions — version history
|
|
1370
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/versions$/) && req.method === 'GET') {
|
|
1371
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1372
|
+
if (options.sessionsDb) {
|
|
1373
|
+
(0, agent_handler_js_1.handleListVersions)(res, agentId, options.sessionsDb);
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1377
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1378
|
+
}
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
// Route: GET /api/agents/:id/metrics?from=&to= — metrics for period
|
|
1382
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/metrics$/) && req.method === 'GET') {
|
|
1383
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1384
|
+
// Parse query params from raw URL
|
|
1385
|
+
const rawUrl = req.url || pathname;
|
|
1386
|
+
const qIdx = rawUrl.indexOf('?');
|
|
1387
|
+
const params = qIdx >= 0 ? new URLSearchParams(rawUrl.slice(qIdx)) : new URLSearchParams();
|
|
1388
|
+
const from = params.get('from') || '2020-01-01';
|
|
1389
|
+
const to = params.get('to') || '2099-12-31';
|
|
1390
|
+
if (options.sessionsDb) {
|
|
1391
|
+
(0, agent_handler_js_1.handleGetAgentMetrics)(res, agentId, from, to, options.sessionsDb);
|
|
1392
|
+
}
|
|
1393
|
+
else {
|
|
1394
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1395
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1396
|
+
}
|
|
1397
|
+
return true;
|
|
1398
|
+
}
|
|
1399
|
+
// Route: GET /api/agents/:id/activity?limit= — activity log
|
|
1400
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/activity$/) && req.method === 'GET') {
|
|
1401
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1402
|
+
const rawUrl = req.url || pathname;
|
|
1403
|
+
const qIdx = rawUrl.indexOf('?');
|
|
1404
|
+
const params = qIdx >= 0 ? new URLSearchParams(rawUrl.slice(qIdx)) : new URLSearchParams();
|
|
1405
|
+
const limit = parsePositiveInt(params.get('limit'), 20, 100);
|
|
1406
|
+
if (options.sessionsDb) {
|
|
1407
|
+
(0, agent_handler_js_1.handleGetAgentActivity)(res, agentId, options.sessionsDb, limit);
|
|
1408
|
+
}
|
|
1409
|
+
else {
|
|
1410
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1411
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1412
|
+
}
|
|
1413
|
+
return true;
|
|
1414
|
+
}
|
|
1415
|
+
// Route: POST /api/agents/:id/archive — archive agent
|
|
1416
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/archive$/) && req.method === 'POST') {
|
|
1417
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1418
|
+
if (options.sessionsDb) {
|
|
1419
|
+
(0, agent_handler_js_1.handleArchiveAgent)(res, agentId, options.sessionsDb);
|
|
1420
|
+
}
|
|
1421
|
+
else {
|
|
1422
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1423
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1424
|
+
}
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
// ── Validation API ──────────────────────────────────────────────────
|
|
1428
|
+
// Route: GET /api/agents/:id/validation/summary?trigger_type=
|
|
1429
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/validation\/summary$/) && req.method === 'GET') {
|
|
1430
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1431
|
+
if (options.sessionsDb) {
|
|
1432
|
+
const triggerType = parseValidationTriggerType(params.get('trigger_type'));
|
|
1433
|
+
if (!triggerType) {
|
|
1434
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1435
|
+
res.end(JSON.stringify({ error: 'trigger_type required' }));
|
|
1436
|
+
return true;
|
|
1437
|
+
}
|
|
1438
|
+
const summary = (0, store_js_1.getValidationSummary)(options.sessionsDb, agentId, triggerType);
|
|
1439
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1440
|
+
res.end(JSON.stringify({ summary }));
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1444
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1445
|
+
}
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1448
|
+
// Route: GET /api/agents/:id/validation/history?trigger_type=
|
|
1449
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/validation\/history$/) && req.method === 'GET') {
|
|
1450
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1451
|
+
if (options.sessionsDb) {
|
|
1452
|
+
const rawUrl = req.url || pathname;
|
|
1453
|
+
const qIdx = rawUrl.indexOf('?');
|
|
1454
|
+
const params = qIdx >= 0 ? new URLSearchParams(rawUrl.slice(qIdx)) : new URLSearchParams();
|
|
1455
|
+
const limit = parsePositiveInt(params.get('limit'), 50, 200);
|
|
1456
|
+
const triggerType = parseValidationTriggerType(params.get('trigger_type'));
|
|
1457
|
+
if (!triggerType) {
|
|
1458
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1459
|
+
res.end(JSON.stringify({ error: 'trigger_type required' }));
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
const history = (0, store_js_1.listValidationHistory)(options.sessionsDb, agentId, limit, triggerType);
|
|
1463
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1464
|
+
res.end(JSON.stringify({ history }));
|
|
1465
|
+
}
|
|
1466
|
+
else {
|
|
1467
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1468
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1469
|
+
}
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
// Route: GET /api/validation-sessions/:id
|
|
1473
|
+
if (pathname.match(/^\/api\/validation-sessions\/[^/]+$/) && req.method === 'GET') {
|
|
1474
|
+
const sessionId = decodeURIComponent(pathname.split('/')[3]);
|
|
1475
|
+
if (options.sessionsDb) {
|
|
1476
|
+
const detail = (0, store_js_1.getValidationSessionDetail)(options.sessionsDb, sessionId);
|
|
1477
|
+
if (!detail) {
|
|
1478
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1479
|
+
res.end(JSON.stringify({ error: 'Session not found' }));
|
|
1480
|
+
}
|
|
1481
|
+
else {
|
|
1482
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1483
|
+
res.end(JSON.stringify(detail));
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
else {
|
|
1487
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1488
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1489
|
+
}
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
// Route: POST /api/agents/:id/validation/approve
|
|
1493
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/validation\/approve$/) && req.method === 'POST') {
|
|
1494
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1495
|
+
if (options.sessionsDb) {
|
|
1496
|
+
const rawUrl = req.url || pathname;
|
|
1497
|
+
const qIdx = rawUrl.indexOf('?');
|
|
1498
|
+
const params = qIdx >= 0 ? new URLSearchParams(rawUrl.slice(qIdx)) : new URLSearchParams();
|
|
1499
|
+
let sessionId = params.get('session_id');
|
|
1500
|
+
if (!sessionId) {
|
|
1501
|
+
const body = await readBodyOrRespond(req, res);
|
|
1502
|
+
if (!body) {
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
sessionId = body?.session_id;
|
|
1506
|
+
}
|
|
1507
|
+
if (!sessionId) {
|
|
1508
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1509
|
+
res.end(JSON.stringify({ error: 'session_id required' }));
|
|
1510
|
+
}
|
|
1511
|
+
else {
|
|
1512
|
+
const detail = (0, store_js_1.getValidationSessionDetail)(options.sessionsDb, sessionId);
|
|
1513
|
+
if (!detail) {
|
|
1514
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1515
|
+
res.end(JSON.stringify({ error: 'Session not found' }));
|
|
1516
|
+
return true;
|
|
1517
|
+
}
|
|
1518
|
+
if (detail.session.agent_id !== agentId) {
|
|
1519
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
1520
|
+
res.end(JSON.stringify({ error: 'session does not belong to requested agent' }));
|
|
1521
|
+
return true;
|
|
1522
|
+
}
|
|
1523
|
+
try {
|
|
1524
|
+
(0, store_js_1.approveValidationSession)(options.sessionsDb, sessionId);
|
|
1525
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1526
|
+
res.end(JSON.stringify({ success: true, approved_session: sessionId }));
|
|
1527
|
+
}
|
|
1528
|
+
catch (error) {
|
|
1529
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1530
|
+
const statusCode = /not found|required|invalid/i.test(message) ? 400 : 500;
|
|
1531
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
1532
|
+
res.end(JSON.stringify({ error: message }));
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
else {
|
|
1537
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1538
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1539
|
+
}
|
|
1540
|
+
return true;
|
|
1541
|
+
}
|
|
1542
|
+
// Route: GET /api/agents/:id/validation/compare?session=X&baseline=approved
|
|
1543
|
+
if (pathname.match(/^\/api\/agents\/[^/]+\/validation\/compare$/) && req.method === 'GET') {
|
|
1544
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1545
|
+
if (options.sessionsDb) {
|
|
1546
|
+
const rawUrl = req.url || pathname;
|
|
1547
|
+
const qIdx = rawUrl.indexOf('?');
|
|
1548
|
+
const params = qIdx >= 0 ? new URLSearchParams(rawUrl.slice(qIdx)) : new URLSearchParams();
|
|
1549
|
+
const sessionId = params.get('session');
|
|
1550
|
+
const baselineMode = params.get('baseline') || 'approved';
|
|
1551
|
+
if (!sessionId) {
|
|
1552
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1553
|
+
res.end(JSON.stringify({ error: 'session query parameter required' }));
|
|
1554
|
+
}
|
|
1555
|
+
else {
|
|
1556
|
+
const current = (0, store_js_1.getValidationSessionDetail)(options.sessionsDb, sessionId);
|
|
1557
|
+
if (!current) {
|
|
1558
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1559
|
+
res.end(JSON.stringify({ error: 'Session not found' }));
|
|
1560
|
+
}
|
|
1561
|
+
else if (current.session.agent_id !== agentId) {
|
|
1562
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
1563
|
+
res.end(JSON.stringify({ error: 'session does not belong to requested agent' }));
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
let baselineSessionId = null;
|
|
1567
|
+
if (baselineMode === 'approved') {
|
|
1568
|
+
const state = (0, store_js_1.getAgentValidationState)(options.sessionsDb, agentId, current.session.trigger_type);
|
|
1569
|
+
baselineSessionId = state?.approved_session_id ?? null;
|
|
1570
|
+
}
|
|
1571
|
+
else {
|
|
1572
|
+
baselineSessionId = baselineMode;
|
|
1573
|
+
}
|
|
1574
|
+
const baseline = baselineSessionId
|
|
1575
|
+
? (0, store_js_1.getValidationSessionDetail)(options.sessionsDb, baselineSessionId)
|
|
1576
|
+
: null;
|
|
1577
|
+
if (baselineMode !== 'approved' && baselineSessionId && !baseline) {
|
|
1578
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1579
|
+
res.end(JSON.stringify({ error: 'baseline session not found' }));
|
|
1580
|
+
return true;
|
|
1581
|
+
}
|
|
1582
|
+
if (baseline && baseline.session.agent_id !== agentId) {
|
|
1583
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
1584
|
+
res.end(JSON.stringify({ error: 'baseline session does not belong to requested agent' }));
|
|
1585
|
+
return true;
|
|
1586
|
+
}
|
|
1587
|
+
if (baseline && baseline.session.trigger_type !== current.session.trigger_type) {
|
|
1588
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1589
|
+
res.end(JSON.stringify({
|
|
1590
|
+
error: 'baseline session trigger_type does not match current session',
|
|
1591
|
+
}));
|
|
1592
|
+
return true;
|
|
1593
|
+
}
|
|
1594
|
+
const baselineMetrics = new Map((baseline?.metrics ?? []).map((m) => [
|
|
1595
|
+
m.name,
|
|
1596
|
+
m.value,
|
|
1597
|
+
]));
|
|
1598
|
+
const deltas = current.metrics.map((m) => {
|
|
1599
|
+
const bVal = baselineMetrics.get(m.name);
|
|
1600
|
+
return {
|
|
1601
|
+
name: m.name,
|
|
1602
|
+
current: m.value,
|
|
1603
|
+
baseline: bVal ?? null,
|
|
1604
|
+
delta: bVal !== undefined ? m.value - bVal : null,
|
|
1605
|
+
direction: m.direction,
|
|
1606
|
+
};
|
|
1607
|
+
});
|
|
1608
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1609
|
+
res.end(JSON.stringify({ current, baseline, deltas }));
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
else {
|
|
1614
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1615
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1616
|
+
}
|
|
1617
|
+
return true;
|
|
1618
|
+
}
|
|
1619
|
+
// Route: GET /api/agents/activity-summary — aggregated activity with alerts
|
|
1620
|
+
if (pathname === '/api/agents/activity-summary' && req.method === 'GET') {
|
|
1621
|
+
const rawUrl = req.url || pathname;
|
|
1622
|
+
const qIdx = rawUrl.indexOf('?');
|
|
1623
|
+
const params = qIdx >= 0 ? new URLSearchParams(rawUrl.slice(qIdx)) : new URLSearchParams();
|
|
1624
|
+
const since = params.get('since') || new Date(Date.now() - 86400000).toISOString().slice(0, 10);
|
|
1625
|
+
if (options.sessionsDb) {
|
|
1626
|
+
(0, agent_handler_js_1.handleGetActivitySummary)(res, options.sessionsDb, since);
|
|
1627
|
+
}
|
|
1628
|
+
else {
|
|
1629
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1630
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1631
|
+
}
|
|
1632
|
+
return true;
|
|
1633
|
+
}
|
|
1634
|
+
// Route: GET /api/agents — list all agents
|
|
1635
|
+
if (pathname === '/api/agents' && req.method === 'GET') {
|
|
1636
|
+
const config = loadMAMAConfig();
|
|
1637
|
+
if (options.sessionsDb) {
|
|
1638
|
+
(0, agent_handler_js_1.handleGetAgents)(res, config, options.sessionsDb);
|
|
1639
|
+
}
|
|
1640
|
+
else {
|
|
1641
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1642
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1643
|
+
}
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1646
|
+
// Route: POST /api/agents — create new agent
|
|
1647
|
+
if (pathname === '/api/agents' && req.method === 'POST') {
|
|
1648
|
+
const body = await readBodyOrRespond(req, res);
|
|
1649
|
+
if (!body) {
|
|
1650
|
+
return true;
|
|
1651
|
+
}
|
|
1652
|
+
if (options.sessionsDb) {
|
|
1653
|
+
await (0, agent_handler_js_1.handleCreateAgent)(res, body, options.sessionsDb, {
|
|
1654
|
+
loadConfig: async () => loadMAMAConfig(),
|
|
1655
|
+
saveConfig: async (config) => saveMAMAConfig(config),
|
|
1656
|
+
applyMultiAgentConfig: options.applyMultiAgentConfig ?? null,
|
|
1657
|
+
restartMultiAgentAgent: options.restartMultiAgentAgent ?? null,
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
else {
|
|
1661
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1662
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1663
|
+
}
|
|
1664
|
+
return true;
|
|
1665
|
+
}
|
|
1666
|
+
// Route: GET /api/agents/:id — single agent detail (must be after /api/agents/:id/*)
|
|
1667
|
+
if (pathname.match(/^\/api\/agents\/[^/]+$/) && req.method === 'GET') {
|
|
1668
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1669
|
+
const config = loadMAMAConfig();
|
|
1670
|
+
if (options.sessionsDb) {
|
|
1671
|
+
(0, agent_handler_js_1.handleGetAgent)(res, agentId, config, options.sessionsDb);
|
|
1672
|
+
}
|
|
1673
|
+
else {
|
|
1674
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1675
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1676
|
+
}
|
|
1677
|
+
return true;
|
|
1678
|
+
}
|
|
1679
|
+
// Route: POST /api/agents/:id — update agent (Managed Agents: version required)
|
|
1680
|
+
if (pathname.match(/^\/api\/agents\/[^/]+$/) && req.method === 'POST') {
|
|
1681
|
+
const agentId = decodeURIComponent(pathname.split('/')[3]);
|
|
1682
|
+
const body = await readBodyOrRespond(req, res);
|
|
1683
|
+
if (!body) {
|
|
1684
|
+
return true;
|
|
1685
|
+
}
|
|
1686
|
+
if (options.sessionsDb) {
|
|
1687
|
+
await (0, agent_handler_js_1.handleUpdateAgent)(res, agentId, body, options.sessionsDb, {
|
|
1688
|
+
loadConfig: async () => loadMAMAConfig(),
|
|
1689
|
+
saveConfig: async (config) => saveMAMAConfig(config),
|
|
1690
|
+
applyMultiAgentConfig: options.applyMultiAgentConfig ?? null,
|
|
1691
|
+
restartMultiAgentAgent: options.restartMultiAgentAgent ?? null,
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1696
|
+
res.end(JSON.stringify({ error: 'Sessions DB not available' }));
|
|
1697
|
+
}
|
|
1698
|
+
return true;
|
|
1699
|
+
}
|
|
1700
|
+
// ── Legacy Multi-Agent API (redirect-compatible) ──
|
|
1272
1701
|
// Route: GET /api/multi-agent/status - get multi-agent system status
|
|
1273
1702
|
if (pathname === '/api/multi-agent/status' && req.method === 'GET') {
|
|
1274
1703
|
await handleMultiAgentStatusRequest(req, res, options);
|
|
@@ -1968,8 +2397,8 @@ function validateConfigUpdate(config) {
|
|
|
1968
2397
|
errors.push('timeout must be at least 1000ms');
|
|
1969
2398
|
}
|
|
1970
2399
|
if (config.agent.backend &&
|
|
1971
|
-
!
|
|
1972
|
-
errors.push('agent.backend must be "claude"
|
|
2400
|
+
!supportedManagedBackends.includes(String(config.agent.backend).toLowerCase())) {
|
|
2401
|
+
errors.push('agent.backend must be "claude", "codex", "codex-mcp", or "gemini"');
|
|
1973
2402
|
}
|
|
1974
2403
|
if (config.agent.backend && config.agent.model && typeof config.agent.model === 'string') {
|
|
1975
2404
|
const backend = String(config.agent.backend).toLowerCase();
|
|
@@ -1977,8 +2406,8 @@ function validateConfigUpdate(config) {
|
|
|
1977
2406
|
if (backend === 'claude' && !isClaudeModel(model)) {
|
|
1978
2407
|
errors.push('agent.model must be a Claude model when agent.backend is "claude"');
|
|
1979
2408
|
}
|
|
1980
|
-
if (backend
|
|
1981
|
-
errors.push(
|
|
2409
|
+
if (isCodexFamilyBackend(backend) && !isCodexModel(model)) {
|
|
2410
|
+
errors.push(`agent.model must be a Codex/OpenAI model when agent.backend is "${backend}"`);
|
|
1982
2411
|
}
|
|
1983
2412
|
}
|
|
1984
2413
|
}
|
|
@@ -2002,16 +2431,16 @@ function validateConfigUpdate(config) {
|
|
|
2002
2431
|
const modelRaw = cfg.model;
|
|
2003
2432
|
if (backendRaw !== undefined) {
|
|
2004
2433
|
const backend = String(backendRaw).toLowerCase();
|
|
2005
|
-
if (!
|
|
2006
|
-
errors.push(`multi_agent.agents.${agentId}.backend must be "claude"
|
|
2434
|
+
if (!supportedManagedBackends.includes(backend)) {
|
|
2435
|
+
errors.push(`multi_agent.agents.${agentId}.backend must be "claude", "codex", "codex-mcp", or "gemini"`);
|
|
2007
2436
|
continue;
|
|
2008
2437
|
}
|
|
2009
2438
|
if (typeof modelRaw === 'string' && modelRaw.trim()) {
|
|
2010
2439
|
if (backend === 'claude' && !isClaudeModel(modelRaw)) {
|
|
2011
2440
|
errors.push(`multi_agent.agents.${agentId}.model must be a Claude model when backend is "claude"`);
|
|
2012
2441
|
}
|
|
2013
|
-
if (backend
|
|
2014
|
-
errors.push(`multi_agent.agents.${agentId}.model must be a Codex/OpenAI model when backend is "
|
|
2442
|
+
if (isCodexFamilyBackend(backend) && !isCodexModel(modelRaw)) {
|
|
2443
|
+
errors.push(`multi_agent.agents.${agentId}.model must be a Codex/OpenAI model when backend is "${backend}"`);
|
|
2015
2444
|
}
|
|
2016
2445
|
}
|
|
2017
2446
|
}
|
|
@@ -2318,8 +2747,8 @@ async function handleMultiAgentUpdateAgentRequest(req, res, pathname, options =
|
|
|
2318
2747
|
}
|
|
2319
2748
|
if (body.backend !== undefined &&
|
|
2320
2749
|
(typeof body.backend !== 'string' ||
|
|
2321
|
-
!
|
|
2322
|
-
validationErrors.push('backend must be "claude"
|
|
2750
|
+
!supportedManagedBackends.includes(String(body.backend).toLowerCase()))) {
|
|
2751
|
+
validationErrors.push('backend must be "claude", "codex", "codex-mcp", or "gemini"');
|
|
2323
2752
|
}
|
|
2324
2753
|
const nextBackend = (body.backend !== undefined ? String(body.backend).toLowerCase() : currentAgent.backend);
|
|
2325
2754
|
const nextModel = (body.model !== undefined ? body.model : currentAgent.model);
|
|
@@ -2327,8 +2756,8 @@ async function handleMultiAgentUpdateAgentRequest(req, res, pathname, options =
|
|
|
2327
2756
|
if (nextBackend === 'claude' && !isClaudeModel(nextModel)) {
|
|
2328
2757
|
validationErrors.push('model must be a Claude model when backend is "claude"');
|
|
2329
2758
|
}
|
|
2330
|
-
if (nextBackend
|
|
2331
|
-
validationErrors.push(
|
|
2759
|
+
if (isCodexFamilyBackend(nextBackend) && !isCodexModel(nextModel)) {
|
|
2760
|
+
validationErrors.push(`model must be a Codex/OpenAI model when backend is "${nextBackend}"`);
|
|
2332
2761
|
}
|
|
2333
2762
|
}
|
|
2334
2763
|
if (body.effort !== undefined) {
|