network-ai 4.15.2 → 5.0.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/INTEGRATION_GUIDE.md +11 -4
- package/QUICKSTART.md +31 -4
- package/README.md +37 -15
- package/bin/dashboard.ts +146 -0
- package/bin/mcp-server.ts +3 -2
- package/dist/adapters/adapter-registry.d.ts +33 -1
- package/dist/adapters/adapter-registry.d.ts.map +1 -1
- package/dist/adapters/adapter-registry.js +49 -0
- package/dist/adapters/adapter-registry.js.map +1 -1
- package/dist/adapters/anthropic-computer-use-adapter.d.ts +132 -0
- package/dist/adapters/anthropic-computer-use-adapter.d.ts.map +1 -0
- package/dist/adapters/anthropic-computer-use-adapter.js +180 -0
- package/dist/adapters/anthropic-computer-use-adapter.js.map +1 -0
- package/dist/adapters/browser-agent-adapter.d.ts +121 -0
- package/dist/adapters/browser-agent-adapter.d.ts.map +1 -0
- package/dist/adapters/browser-agent-adapter.js +219 -0
- package/dist/adapters/browser-agent-adapter.js.map +1 -0
- package/dist/adapters/copilot-adapter.d.ts +59 -0
- package/dist/adapters/copilot-adapter.d.ts.map +1 -0
- package/dist/adapters/copilot-adapter.js +132 -0
- package/dist/adapters/copilot-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +15 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +22 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/langgraph-adapter.d.ts +70 -0
- package/dist/adapters/langgraph-adapter.d.ts.map +1 -0
- package/dist/adapters/langgraph-adapter.js +119 -0
- package/dist/adapters/langgraph-adapter.js.map +1 -0
- package/dist/adapters/openai-agents-adapter.d.ts +100 -0
- package/dist/adapters/openai-agents-adapter.d.ts.map +1 -0
- package/dist/adapters/openai-agents-adapter.js +118 -0
- package/dist/adapters/openai-agents-adapter.js.map +1 -0
- package/dist/adapters/pydantic-ai-adapter.d.ts +104 -0
- package/dist/adapters/pydantic-ai-adapter.d.ts.map +1 -0
- package/dist/adapters/pydantic-ai-adapter.js +163 -0
- package/dist/adapters/pydantic-ai-adapter.js.map +1 -0
- package/dist/adapters/vertex-ai-adapter.d.ts +122 -0
- package/dist/adapters/vertex-ai-adapter.d.ts.map +1 -0
- package/dist/adapters/vertex-ai-adapter.js +166 -0
- package/dist/adapters/vertex-ai-adapter.js.map +1 -0
- package/dist/bin/dashboard.d.ts +11 -0
- package/dist/bin/dashboard.d.ts.map +1 -0
- package/dist/bin/dashboard.js +135 -0
- package/dist/bin/dashboard.js.map +1 -0
- package/dist/bin/mcp-server.js +3 -2
- package/dist/bin/mcp-server.js.map +1 -1
- package/dist/index.d.ts +103 -559
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +295 -1074
- package/dist/index.js.map +1 -1
- package/dist/lib/adapter-test-harness.d.ts +88 -0
- package/dist/lib/adapter-test-harness.d.ts.map +1 -0
- package/dist/lib/adapter-test-harness.js +118 -0
- package/dist/lib/adapter-test-harness.js.map +1 -0
- package/dist/lib/agent-conversation.d.ts +115 -0
- package/dist/lib/agent-conversation.d.ts.map +1 -0
- package/dist/lib/agent-conversation.js +155 -0
- package/dist/lib/agent-conversation.js.map +1 -0
- package/dist/lib/agent-debate.d.ts +115 -0
- package/dist/lib/agent-debate.d.ts.map +1 -0
- package/dist/lib/agent-debate.js +146 -0
- package/dist/lib/agent-debate.js.map +1 -0
- package/dist/lib/agent-memory.d.ts +157 -0
- package/dist/lib/agent-memory.d.ts.map +1 -0
- package/dist/lib/agent-memory.js +336 -0
- package/dist/lib/agent-memory.js.map +1 -0
- package/dist/lib/agent-vcr.d.ts +133 -0
- package/dist/lib/agent-vcr.d.ts.map +1 -0
- package/dist/lib/agent-vcr.js +218 -0
- package/dist/lib/agent-vcr.js.map +1 -0
- package/dist/lib/anomaly-detector.d.ts +112 -0
- package/dist/lib/anomaly-detector.d.ts.map +1 -0
- package/dist/lib/anomaly-detector.js +178 -0
- package/dist/lib/anomaly-detector.js.map +1 -0
- package/dist/lib/approval-inbox.d.ts +147 -0
- package/dist/lib/approval-inbox.d.ts.map +1 -0
- package/dist/lib/approval-inbox.js +385 -0
- package/dist/lib/approval-inbox.js.map +1 -0
- package/dist/lib/auth-guardian.d.ts +170 -0
- package/dist/lib/auth-guardian.d.ts.map +1 -0
- package/dist/lib/auth-guardian.js +604 -0
- package/dist/lib/auth-guardian.js.map +1 -0
- package/dist/lib/auth-validator.d.ts +70 -0
- package/dist/lib/auth-validator.d.ts.map +1 -0
- package/dist/lib/auth-validator.js +32 -0
- package/dist/lib/auth-validator.js.map +1 -0
- package/dist/lib/blackboard-validator.d.ts +56 -0
- package/dist/lib/blackboard-validator.d.ts.map +1 -1
- package/dist/lib/blackboard-validator.js +181 -4
- package/dist/lib/blackboard-validator.js.map +1 -1
- package/dist/lib/comparison-runner.d.ts +99 -0
- package/dist/lib/comparison-runner.d.ts.map +1 -0
- package/dist/lib/comparison-runner.js +138 -0
- package/dist/lib/comparison-runner.js.map +1 -0
- package/dist/lib/config-watcher.d.ts +109 -0
- package/dist/lib/config-watcher.d.ts.map +1 -0
- package/dist/lib/config-watcher.js +215 -0
- package/dist/lib/config-watcher.js.map +1 -0
- package/dist/lib/cost-governor.d.ts +105 -0
- package/dist/lib/cost-governor.d.ts.map +1 -0
- package/dist/lib/cost-governor.js +128 -0
- package/dist/lib/cost-governor.js.map +1 -0
- package/dist/lib/cost-heatmap.d.ts +104 -0
- package/dist/lib/cost-heatmap.d.ts.map +1 -0
- package/dist/lib/cost-heatmap.js +161 -0
- package/dist/lib/cost-heatmap.js.map +1 -0
- package/dist/lib/coverage-reporter.d.ts +92 -0
- package/dist/lib/coverage-reporter.d.ts.map +1 -0
- package/dist/lib/coverage-reporter.js +177 -0
- package/dist/lib/coverage-reporter.js.map +1 -0
- package/dist/lib/dashboard-server.d.ts +71 -0
- package/dist/lib/dashboard-server.d.ts.map +1 -0
- package/dist/lib/dashboard-server.js +403 -0
- package/dist/lib/dashboard-server.js.map +1 -0
- package/dist/lib/dry-run.d.ts +73 -0
- package/dist/lib/dry-run.d.ts.map +1 -0
- package/dist/lib/dry-run.js +130 -0
- package/dist/lib/dry-run.js.map +1 -0
- package/dist/lib/errors.d.ts +15 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +38 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/event-bus.d.ts +167 -0
- package/dist/lib/event-bus.d.ts.map +1 -0
- package/dist/lib/event-bus.js +229 -0
- package/dist/lib/event-bus.js.map +1 -0
- package/dist/lib/explainability.d.ts +85 -0
- package/dist/lib/explainability.d.ts.map +1 -0
- package/dist/lib/explainability.js +102 -0
- package/dist/lib/explainability.js.map +1 -0
- package/dist/lib/goal-dsl.d.ts +157 -0
- package/dist/lib/goal-dsl.d.ts.map +1 -0
- package/dist/lib/goal-dsl.js +392 -0
- package/dist/lib/goal-dsl.js.map +1 -0
- package/dist/lib/job-queue.d.ts +183 -0
- package/dist/lib/job-queue.d.ts.map +1 -0
- package/dist/lib/job-queue.js +310 -0
- package/dist/lib/job-queue.js.map +1 -0
- package/dist/lib/learning-loop.d.ts +113 -0
- package/dist/lib/learning-loop.d.ts.map +1 -0
- package/dist/lib/learning-loop.js +181 -0
- package/dist/lib/learning-loop.js.map +1 -0
- package/dist/lib/lifecycle-hooks.d.ts +116 -0
- package/dist/lib/lifecycle-hooks.d.ts.map +1 -0
- package/dist/lib/lifecycle-hooks.js +148 -0
- package/dist/lib/lifecycle-hooks.js.map +1 -0
- package/dist/lib/locked-blackboard.d.ts.map +1 -1
- package/dist/lib/locked-blackboard.js +9 -5
- package/dist/lib/locked-blackboard.js.map +1 -1
- package/dist/lib/mcp-tool-consumer.d.ts +153 -0
- package/dist/lib/mcp-tool-consumer.d.ts.map +1 -0
- package/dist/lib/mcp-tool-consumer.js +320 -0
- package/dist/lib/mcp-tool-consumer.js.map +1 -0
- package/dist/lib/metrics.d.ts +119 -0
- package/dist/lib/metrics.d.ts.map +1 -0
- package/dist/lib/metrics.js +284 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/orchestrator-types.d.ts +309 -0
- package/dist/lib/orchestrator-types.d.ts.map +1 -0
- package/dist/lib/orchestrator-types.js +61 -0
- package/dist/lib/orchestrator-types.js.map +1 -0
- package/dist/lib/otel-bridge.d.ts +74 -0
- package/dist/lib/otel-bridge.d.ts.map +1 -0
- package/dist/lib/otel-bridge.js +167 -0
- package/dist/lib/otel-bridge.js.map +1 -0
- package/dist/lib/playground.d.ts +76 -0
- package/dist/lib/playground.d.ts.map +1 -0
- package/dist/lib/playground.js +224 -0
- package/dist/lib/playground.js.map +1 -0
- package/dist/lib/quadtree.d.ts +114 -0
- package/dist/lib/quadtree.d.ts.map +1 -0
- package/dist/lib/quadtree.js +259 -0
- package/dist/lib/quadtree.js.map +1 -0
- package/dist/lib/shared-blackboard.d.ts +101 -0
- package/dist/lib/shared-blackboard.d.ts.map +1 -0
- package/dist/lib/shared-blackboard.js +249 -0
- package/dist/lib/shared-blackboard.js.map +1 -0
- package/dist/lib/speculative-executor.d.ts +89 -0
- package/dist/lib/speculative-executor.d.ts.map +1 -0
- package/dist/lib/speculative-executor.js +107 -0
- package/dist/lib/speculative-executor.js.map +1 -0
- package/dist/lib/swarm-transport.d.ts +150 -0
- package/dist/lib/swarm-transport.d.ts.map +1 -0
- package/dist/lib/swarm-transport.js +307 -0
- package/dist/lib/swarm-transport.js.map +1 -0
- package/dist/lib/task-decomposer.d.ts +41 -0
- package/dist/lib/task-decomposer.d.ts.map +1 -0
- package/dist/lib/task-decomposer.js +272 -0
- package/dist/lib/task-decomposer.js.map +1 -0
- package/dist/lib/timeline-scrubber.d.ts +84 -0
- package/dist/lib/timeline-scrubber.d.ts.map +1 -0
- package/dist/lib/timeline-scrubber.js +173 -0
- package/dist/lib/timeline-scrubber.js.map +1 -0
- package/dist/lib/topology.d.ts +361 -0
- package/dist/lib/topology.d.ts.map +1 -0
- package/dist/lib/topology.js +591 -0
- package/dist/lib/topology.js.map +1 -0
- package/dist/security.d.ts +95 -0
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +267 -5
- package/dist/security.js.map +1 -1
- package/package.json +7 -5
- package/types/agent-adapter.d.ts +5 -0
package/dist/index.js
CHANGED
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
* @license MIT
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
16
|
-
exports.
|
|
15
|
+
exports.BaseAdapter = exports.AdapterRegistry = exports.validateJsonSchema = exports.QualityGateAgent = exports.BlackboardValidator = exports.NoOpAuthValidator = exports.ComparisonRunner = exports.CoverageReporter = exports.AgentVCR = exports.compileGoal = exports.validateGoal = exports.goalFromObject = exports.parseGoal = exports.MockAgentRegistry = exports.startPlayground = exports.FileJobStore = exports.JobQueue = exports.SwarmTransportClient = exports.SwarmTransportServer = exports.createAdapterTestSuite = exports.ApprovalInbox = exports.AgentDebate = exports.SpeculativeExecutor = exports.LearningLoop = exports.SharedLongTermMemory = exports.ProceduralMemory = exports.EpisodicMemory = exports.AgentMemory = exports.OrchestratorLifecycleHooks = exports.ConfigWatcher = exports.DryRunSimulator = exports.LookupCostModel = exports.CostGovernor = exports.AnomalyDetector = exports.TimelineScrubber = exports.CostHeatmap = exports.createOrchestratorMetrics = exports.Histogram = exports.Gauge = exports.Counter = exports.MetricsRegistry = exports.SpanStatus = exports.OTelBridge = exports.AgentConversationLog = exports.OrchestratorEventBus = exports.ExplainabilityTracer = exports.TaskDecomposer = exports.AuthGuardian = exports.SharedBlackboard = exports.SwarmOrchestrator = void 0;
|
|
16
|
+
exports.matchGlob = exports.AdapterHookManager = exports.QAOrchestratorAgent = exports.ComplianceMonitor = exports.WORKFLOW_STATES = exports.createDeliveryPipelineFSM = exports.ComplianceViolationError = exports.ComplianceMiddleware = exports.ToolAuthorizationMatrix = exports.JourneyFSM = exports.mapErrorToSkillResult = exports.TimeoutError = exports.ParallelLimitError = exports.AdapterNotInitializedError = exports.AdapterNotFoundError = exports.AdapterAlreadyRegisteredError = exports.ConflictError = exports.LockAcquisitionError = exports.ValidationError = exports.NamespaceViolationError = exports.IdentityVerificationError = exports.NetworkAIError = exports.LogLevel = exports.Logger = exports.McpInProcessTransport = exports.McpBridgeRouter = exports.McpBridgeClient = exports.McpBlackboardBridge = exports.FederatedBudget = exports.isFlushable = exports.ConsistentBackend = exports.mergeEntry = exports.compareClock = exports.isConcurrent = exports.happensBefore = exports.mergeClock = exports.tickClock = exports.CrdtBackend = exports.RedisBackend = exports.MemoryBackend = exports.FileBackend = exports.HttpClientTransport = exports.StdioClientTransport = exports.MCPToolConsumer = exports.CustomAdapter = exports.MCPAdapter = exports.CrewAIAdapter = exports.AutoGenAdapter = exports.LangChainAdapter = exports.OpenClawAdapter = void 0;
|
|
17
|
+
exports.AnthropicComputerUseAdapter = exports.LangGraphAdapter = exports.CopilotAdapter = exports.NemoClawAdapter = exports.MiniMaxAdapter = exports.CodexAdapter = exports.A2AAdapter = exports.CustomStreamingAdapter = exports.LangChainStreamingAdapter = exports.collectStream = exports.StreamingBaseAdapter = exports.ControlMcpTools = exports.ExtendedMcpTools = exports.McpBlackboardBridgeAdapter = exports.McpCombinedBridge = exports.McpSseTransport = exports.McpSseServer = exports.BLACKBOARD_TOOL_DEFINITIONS = exports.registerBlackboardTools = exports.BlackboardMCPTools = exports.QuadTree = exports.DashboardServer = exports.TopologyTracker = exports.parsePlanJSON = exports.topologicalLayers = exports.validateDAG = exports.createLLMPlanner = exports.runTeam = exports.TeamRunner = exports.GoalDecomposer = exports.adaptiveStrategy = exports.StrategyAgent = exports.WorkloadPartitioner = exports.AgentPool = exports.ansi = exports.ConsoleUI = exports.RuntimeExecutionError = exports.RuntimeApprovalError = exports.RuntimePolicyError = exports.AgentRuntime = exports.ApprovalGate = exports.FileAccessor = exports.ShellExecutor = exports.SandboxPolicy = exports.FanOutFanIn = exports.ConfidenceFilter = exports.PhasePipeline = exports.SemanticMemory = exports.SkillComposer = exports.matchToolPattern = void 0;
|
|
18
|
+
exports.BrowserAgentAdapter = exports.PydanticAIAdapter = exports.VertexAIAdapter = exports.OpenAIAgentsAdapter = void 0;
|
|
17
19
|
exports.createSwarmOrchestrator = createSwarmOrchestrator;
|
|
18
20
|
exports.getConfig = getConfig;
|
|
19
21
|
exports.setConfig = setConfig;
|
|
@@ -27,1069 +29,24 @@ const consistency_1 = require("./lib/consistency");
|
|
|
27
29
|
const blackboard_validator_1 = require("./lib/blackboard-validator");
|
|
28
30
|
const logger_1 = require("./lib/logger");
|
|
29
31
|
const errors_1 = require("./lib/errors");
|
|
32
|
+
// Extracted modules (Phase 1.1 — split god-file)
|
|
33
|
+
const shared_blackboard_1 = require("./lib/shared-blackboard");
|
|
34
|
+
const auth_guardian_1 = require("./lib/auth-guardian");
|
|
35
|
+
const explainability_1 = require("./lib/explainability");
|
|
36
|
+
const event_bus_1 = require("./lib/event-bus");
|
|
37
|
+
const agent_conversation_1 = require("./lib/agent-conversation");
|
|
38
|
+
const otel_bridge_1 = require("./lib/otel-bridge");
|
|
39
|
+
const metrics_1 = require("./lib/metrics");
|
|
40
|
+
const cost_heatmap_1 = require("./lib/cost-heatmap");
|
|
41
|
+
const anomaly_detector_1 = require("./lib/anomaly-detector");
|
|
42
|
+
const lifecycle_hooks_1 = require("./lib/lifecycle-hooks");
|
|
43
|
+
const task_decomposer_1 = require("./lib/task-decomposer");
|
|
44
|
+
const orchestrator_types_1 = require("./lib/orchestrator-types");
|
|
30
45
|
const log = logger_1.Logger.create('SwarmOrchestrator');
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
blackboardPath: './swarm-blackboard.md',
|
|
36
|
-
maxParallelAgents: Infinity,
|
|
37
|
-
defaultTimeout: 30000,
|
|
38
|
-
enableTracing: true,
|
|
39
|
-
grantTokenTTL: 300000, // 5 minutes in milliseconds
|
|
40
|
-
maxBlackboardValueSize: 1024 * 1024, // 1 MB max per entry
|
|
41
|
-
auditLogPath: './data/audit_log.jsonl',
|
|
42
|
-
trustConfigPath: './data/trust_levels.json',
|
|
43
|
-
};
|
|
44
|
-
// ============================================================================
|
|
45
|
-
// DEFAULT RESOURCE PROFILES -- Universal, domain-agnostic
|
|
46
|
-
// Users can override/extend these for any domain (coding, finance, devops, etc.)
|
|
47
|
-
// ============================================================================
|
|
48
|
-
const DEFAULT_RESOURCE_PROFILES = {
|
|
49
|
-
// --- Financial / Enterprise ---
|
|
50
|
-
SAP_API: { baseRisk: 0.5, defaultRestrictions: ['read_only', 'max_records:100'], description: 'SAP enterprise API' },
|
|
51
|
-
FINANCIAL_API: { baseRisk: 0.7, defaultRestrictions: ['read_only', 'no_pii_fields', 'audit_required'], description: 'Financial data API' },
|
|
52
|
-
DATA_EXPORT: { baseRisk: 0.6, defaultRestrictions: ['anonymize_pii', 'local_only'], description: 'Data export operations' },
|
|
53
|
-
// --- Coding / Development ---
|
|
54
|
-
FILE_SYSTEM: { baseRisk: 0.5, defaultRestrictions: ['workspace_only', 'no_system_dirs', 'max_file_size:10mb'], description: 'Read/write files in workspace' },
|
|
55
|
-
SHELL_EXEC: { baseRisk: 0.8, defaultRestrictions: ['sandbox_only', 'no_sudo', 'timeout:30s', 'audit_required'], description: 'Execute shell commands' },
|
|
56
|
-
GIT: { baseRisk: 0.4, defaultRestrictions: ['local_repo_only', 'no_force_push'], description: 'Git operations' },
|
|
57
|
-
PACKAGE_MANAGER: { baseRisk: 0.6, defaultRestrictions: ['audit_required', 'no_global_install', 'lockfile_required'], description: 'npm/pip/cargo package management' },
|
|
58
|
-
BUILD_TOOL: { baseRisk: 0.5, defaultRestrictions: ['workspace_only', 'timeout:120s'], description: 'Build and compilation' },
|
|
59
|
-
// --- Infrastructure / DevOps ---
|
|
60
|
-
DOCKER: { baseRisk: 0.7, defaultRestrictions: ['no_privileged', 'no_host_network', 'audit_required'], description: 'Container operations' },
|
|
61
|
-
CLOUD_DEPLOY: { baseRisk: 0.9, defaultRestrictions: ['staging_only', 'approval_required', 'rollback_ready'], description: 'Cloud deployment' },
|
|
62
|
-
DATABASE: { baseRisk: 0.6, defaultRestrictions: ['read_only', 'max_records:1000', 'no_schema_changes'], description: 'Database access' },
|
|
63
|
-
// --- Communication / External ---
|
|
64
|
-
EXTERNAL_SERVICE: { baseRisk: 0.4, defaultRestrictions: ['rate_limit:10_per_minute'], description: 'External API calls' },
|
|
65
|
-
EMAIL: { baseRisk: 0.5, defaultRestrictions: ['rate_limit:5_per_minute', 'no_attachments'], description: 'Email sending' },
|
|
66
|
-
WEBHOOK: { baseRisk: 0.4, defaultRestrictions: ['allowed_domains_only', 'no_credentials'], description: 'Webhook dispatch' },
|
|
67
|
-
};
|
|
68
|
-
const DEFAULT_AGENT_TRUST = [
|
|
69
|
-
{ agentId: 'orchestrator', trustLevel: 0.9, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
70
|
-
{ agentId: 'data_analyst', trustLevel: 0.8, allowedNamespaces: ['task:', 'analytics:', 'agent:'], allowedResources: ['SAP_API', 'DATABASE', 'DATA_EXPORT', 'EXTERNAL_SERVICE'] },
|
|
71
|
-
{ agentId: 'strategy_advisor', trustLevel: 0.7, allowedNamespaces: ['task:', 'strategy:'], allowedResources: ['EXTERNAL_SERVICE', 'DATA_EXPORT'] },
|
|
72
|
-
{ agentId: 'risk_assessor', trustLevel: 0.85, allowedNamespaces: ['task:', 'risk:', 'analytics:'], allowedResources: ['EXTERNAL_SERVICE', 'DATABASE'] },
|
|
73
|
-
// Coding agents
|
|
74
|
-
{ agentId: 'code_writer', trustLevel: 0.75, allowedNamespaces: ['task:', 'code:', 'build:'], allowedResources: ['FILE_SYSTEM', 'GIT', 'BUILD_TOOL', 'PACKAGE_MANAGER'] },
|
|
75
|
-
{ agentId: 'code_reviewer', trustLevel: 0.8, allowedNamespaces: ['task:', 'code:', 'review:'], allowedResources: ['FILE_SYSTEM', 'GIT'] },
|
|
76
|
-
{ agentId: 'test_runner', trustLevel: 0.75, allowedNamespaces: ['task:', 'test:', 'build:'], allowedResources: ['FILE_SYSTEM', 'SHELL_EXEC', 'BUILD_TOOL'] },
|
|
77
|
-
{ agentId: 'devops_agent', trustLevel: 0.7, allowedNamespaces: ['task:', 'deploy:', 'infra:'], allowedResources: ['DOCKER', 'SHELL_EXEC', 'CLOUD_DEPLOY', 'GIT'] },
|
|
78
|
-
];
|
|
79
|
-
// ============================================================================
|
|
80
|
-
// BLACKBOARD MANAGEMENT -- Secured with LockedBlackboard, identity verification,
|
|
81
|
-
// namespace scoping, value validation, and input sanitization
|
|
82
|
-
// ============================================================================
|
|
83
|
-
/**
|
|
84
|
-
* Namespace-scoped, identity-verified shared state for multi-agent coordination.
|
|
85
|
-
*
|
|
86
|
-
* Every write is identity-verified (agent token), namespace-checked,
|
|
87
|
-
* size-validated, input-sanitized, and atomically persisted through
|
|
88
|
-
* {@link LockedBlackboard}.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* const bb = new SharedBlackboard('./workspace');
|
|
93
|
-
* bb.registerAgent('analyst', 'secret-token', ['task:', 'analytics:']);
|
|
94
|
-
* bb.write('task:revenue', { q4: 42_000 }, 'analyst', 3600, 'secret-token');
|
|
95
|
-
* const entry = bb.read('task:revenue');
|
|
96
|
-
* ```
|
|
97
|
-
*/
|
|
98
|
-
class SharedBlackboard {
|
|
99
|
-
backend;
|
|
100
|
-
agentTokens = new Map(); // agentId -> verified token
|
|
101
|
-
agentNamespaces = new Map(); // agentId -> allowed prefixes
|
|
102
|
-
constructor(backendOrPath) {
|
|
103
|
-
if (typeof backendOrPath === 'string') {
|
|
104
|
-
if (!backendOrPath || backendOrPath.trim() === '') {
|
|
105
|
-
throw new errors_1.ValidationError('basePath must be a non-empty string');
|
|
106
|
-
}
|
|
107
|
-
this.backend = new blackboard_backend_1.FileBackend(backendOrPath);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
if (!backendOrPath || typeof backendOrPath !== 'object') {
|
|
111
|
-
throw new errors_1.ValidationError('backend must be a BlackboardBackend instance');
|
|
112
|
-
}
|
|
113
|
-
this.backend = backendOrPath;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Register a verified agent identity. Only agents with registered tokens
|
|
118
|
-
* can write to the blackboard. The orchestrator registers agents after
|
|
119
|
-
* verifying their identity through the AuthGuardian.
|
|
120
|
-
*/
|
|
121
|
-
registerAgent(agentId, verificationToken, allowedNamespaces = ['*']) {
|
|
122
|
-
if (!agentId || typeof agentId !== 'string' || agentId.trim() === '') {
|
|
123
|
-
throw new errors_1.ValidationError('agentId must be a non-empty string');
|
|
124
|
-
}
|
|
125
|
-
if (!verificationToken || typeof verificationToken !== 'string') {
|
|
126
|
-
throw new errors_1.ValidationError('verificationToken must be a non-empty string');
|
|
127
|
-
}
|
|
128
|
-
if (!Array.isArray(allowedNamespaces)) {
|
|
129
|
-
throw new errors_1.ValidationError('allowedNamespaces must be an array of strings');
|
|
130
|
-
}
|
|
131
|
-
this.agentTokens.set(agentId, verificationToken);
|
|
132
|
-
this.agentNamespaces.set(agentId, allowedNamespaces);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Check if an agent is allowed to access a key based on namespace rules.
|
|
136
|
-
*/
|
|
137
|
-
canAccessKey(agentId, key) {
|
|
138
|
-
const namespaces = this.agentNamespaces.get(agentId);
|
|
139
|
-
if (!namespaces)
|
|
140
|
-
return false;
|
|
141
|
-
if (namespaces.includes('*'))
|
|
142
|
-
return true;
|
|
143
|
-
return namespaces.some(ns => key.startsWith(ns));
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Verify that the calling agent is who they claim to be.
|
|
147
|
-
*/
|
|
148
|
-
verifyAgent(agentId, token) {
|
|
149
|
-
const registeredToken = this.agentTokens.get(agentId);
|
|
150
|
-
// If no token system is configured for this agent, allow (backward compat)
|
|
151
|
-
if (!registeredToken)
|
|
152
|
-
return true;
|
|
153
|
-
return token === registeredToken;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Validate value size and structure before writing.
|
|
157
|
-
* Prevents DoS via oversized writes and circular data.
|
|
158
|
-
*/
|
|
159
|
-
validateValue(value) {
|
|
160
|
-
try {
|
|
161
|
-
const serialized = JSON.stringify(value);
|
|
162
|
-
if (serialized.length > CONFIG.maxBlackboardValueSize) {
|
|
163
|
-
return { valid: false, reason: `Value exceeds max size (${serialized.length} > ${CONFIG.maxBlackboardValueSize} bytes)` };
|
|
164
|
-
}
|
|
165
|
-
return { valid: true };
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return { valid: false, reason: 'Value cannot be serialized (circular reference or invalid structure)' };
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Sanitize a key to prevent markdown injection.
|
|
173
|
-
*/
|
|
174
|
-
sanitizeKey(key) {
|
|
175
|
-
// Keys must be safe for markdown headings -- no #, newlines, or markdown syntax
|
|
176
|
-
return key.replace(/[#\n\r|`]/g, '_').slice(0, 256);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Read an entry from the blackboard by key.
|
|
180
|
-
*
|
|
181
|
-
* @param key - The entry key to look up
|
|
182
|
-
* @returns The entry, or `null` if not found or expired
|
|
183
|
-
* @throws {@link ValidationError} if `key` is not a non-empty string
|
|
184
|
-
*/
|
|
185
|
-
read(key) {
|
|
186
|
-
if (!key || typeof key !== 'string') {
|
|
187
|
-
throw new errors_1.ValidationError('key must be a non-empty string');
|
|
188
|
-
}
|
|
189
|
-
const entry = this.backend.read(key);
|
|
190
|
-
if (!entry)
|
|
191
|
-
return null;
|
|
192
|
-
// Normalize field name for backward compatibility
|
|
193
|
-
return {
|
|
194
|
-
key: entry.key,
|
|
195
|
-
value: entry.value,
|
|
196
|
-
sourceAgent: entry.source_agent ?? entry.sourceAgent ?? 'unknown',
|
|
197
|
-
timestamp: entry.timestamp,
|
|
198
|
-
ttl: entry.ttl,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Write to the blackboard with identity verification, namespace checks,
|
|
203
|
-
* value validation, and input sanitization. Uses LockedBlackboard for
|
|
204
|
-
* atomic file-system writes.
|
|
205
|
-
*
|
|
206
|
-
* @param key - The key to write
|
|
207
|
-
* @param value - The value (will be sanitized and size-checked)
|
|
208
|
-
* @param sourceAgent - Agent claiming to write (verified against registered token)
|
|
209
|
-
* @param ttl - Optional TTL in seconds
|
|
210
|
-
* @param agentToken - Optional verification token for identity check
|
|
211
|
-
*/
|
|
212
|
-
write(key, value, sourceAgent, ttl, agentToken) {
|
|
213
|
-
// 1. Verify agent identity
|
|
214
|
-
if (!this.verifyAgent(sourceAgent, agentToken)) {
|
|
215
|
-
throw new errors_1.IdentityVerificationError(sourceAgent);
|
|
216
|
-
}
|
|
217
|
-
// 2. Namespace check
|
|
218
|
-
if (!this.canAccessKey(sourceAgent, key)) {
|
|
219
|
-
throw new errors_1.NamespaceViolationError(sourceAgent, key);
|
|
220
|
-
}
|
|
221
|
-
// 3. Sanitize key
|
|
222
|
-
const safeKey = this.sanitizeKey(key);
|
|
223
|
-
// 4. Validate value size/structure
|
|
224
|
-
const validation = this.validateValue(value);
|
|
225
|
-
if (!validation.valid) {
|
|
226
|
-
throw new errors_1.ValidationError(validation.reason);
|
|
227
|
-
}
|
|
228
|
-
// 5. Sanitize value -- strip injection payloads from string content
|
|
229
|
-
let sanitizedValue;
|
|
230
|
-
try {
|
|
231
|
-
sanitizedValue = security_1.InputSanitizer.sanitizeObject(value);
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
sanitizedValue = value; // Fall back to raw if sanitization can't handle it
|
|
235
|
-
}
|
|
236
|
-
// 6. Write through backend (atomic when using FileBackend; in-memory for MemoryBackend)
|
|
237
|
-
const entry = this.backend.write(safeKey, sanitizedValue, sourceAgent, ttl);
|
|
238
|
-
// Normalize for backward compat
|
|
239
|
-
return {
|
|
240
|
-
key: entry.key,
|
|
241
|
-
value: entry.value,
|
|
242
|
-
sourceAgent: entry.source_agent ?? sourceAgent,
|
|
243
|
-
timestamp: entry.timestamp,
|
|
244
|
-
ttl: entry.ttl,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Check whether a key exists on the blackboard (not expired).
|
|
249
|
-
* @param key - The entry key to check
|
|
250
|
-
*/
|
|
251
|
-
exists(key) {
|
|
252
|
-
return this.read(key) !== null;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Get a full snapshot of all blackboard entries.
|
|
256
|
-
*/
|
|
257
|
-
getSnapshot() {
|
|
258
|
-
const raw = this.backend.getSnapshot();
|
|
259
|
-
const normalized = {};
|
|
260
|
-
for (const [key, entry] of Object.entries(raw)) {
|
|
261
|
-
normalized[key] = {
|
|
262
|
-
key: entry.key,
|
|
263
|
-
value: entry.value,
|
|
264
|
-
sourceAgent: entry.source_agent ?? entry.sourceAgent ?? 'unknown',
|
|
265
|
-
timestamp: entry.timestamp,
|
|
266
|
-
ttl: entry.ttl,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
return normalized;
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Get a namespace-scoped snapshot -- only returns keys an agent is allowed to see.
|
|
273
|
-
* Prevents data leakage between agents.
|
|
274
|
-
*/
|
|
275
|
-
getScopedSnapshot(agentId) {
|
|
276
|
-
if (!agentId || typeof agentId !== 'string') {
|
|
277
|
-
throw new errors_1.ValidationError('agentId must be a non-empty string');
|
|
278
|
-
}
|
|
279
|
-
const full = this.getSnapshot();
|
|
280
|
-
const scoped = {};
|
|
281
|
-
for (const [key, entry] of Object.entries(full)) {
|
|
282
|
-
if (this.canAccessKey(agentId, key)) {
|
|
283
|
-
scoped[key] = entry;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return scoped;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Clear all entries (for testing).
|
|
290
|
-
*/
|
|
291
|
-
clear() {
|
|
292
|
-
// Write an empty state through locked backend
|
|
293
|
-
const keys = this.backend.listKeys();
|
|
294
|
-
for (const key of keys) {
|
|
295
|
-
this.backend.delete(key);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
exports.SharedBlackboard = SharedBlackboard;
|
|
300
|
-
// ============================================================================
|
|
301
|
-
// AUTH GUARDIAN - UNIVERSAL PERMISSION WALL IMPLEMENTATION
|
|
302
|
-
// Now domain-agnostic: resource types, risk profiles, trust levels, and
|
|
303
|
-
// restrictions are all configurable. Works for coding, finance, devops, etc.
|
|
304
|
-
// Integrates with SecureSwarmGateway for HMAC tokens, rate limiting,
|
|
305
|
-
// input sanitization, and cryptographic audit logs.
|
|
306
|
-
// ============================================================================
|
|
307
|
-
/**
|
|
308
|
-
* Universal permission wall for multi-agent systems.
|
|
309
|
-
*
|
|
310
|
-
* Evaluates permission requests using a weighted formula of justification
|
|
311
|
-
* quality (40%), agent trust level (30%), and risk score (30%).
|
|
312
|
-
* Resource types, risk profiles, trust levels, and restrictions are all
|
|
313
|
-
* configurable — works for coding, finance, DevOps, or any domain.
|
|
314
|
-
*
|
|
315
|
-
* @example
|
|
316
|
-
* ```typescript
|
|
317
|
-
* const guardian = new AuthGuardian({
|
|
318
|
-
* trustLevels: [{ agentId: 'analyst', trustLevel: 0.8 }],
|
|
319
|
-
* resourceProfiles: { CUSTOM_API: { baseRisk: 0.5, defaultRestrictions: ['audit_required'] } },
|
|
320
|
-
* });
|
|
321
|
-
*
|
|
322
|
-
* const grant = await guardian.requestPermission(
|
|
323
|
-
* 'analyst', 'CUSTOM_API', 'Need to fetch Q4 revenue data for report', 'read'
|
|
324
|
-
* );
|
|
325
|
-
* if (grant.granted) {
|
|
326
|
-
* // Use grant.grantToken to prove authorization
|
|
327
|
-
* }
|
|
328
|
-
* ```
|
|
329
|
-
*/
|
|
330
|
-
class AuthGuardian {
|
|
331
|
-
activeGrants = new Map();
|
|
332
|
-
agentTrustLevels = new Map();
|
|
333
|
-
agentTrustConfigs = new Map();
|
|
334
|
-
resourceProfiles = new Map();
|
|
335
|
-
auditLog = [];
|
|
336
|
-
auditLogPath;
|
|
337
|
-
trustConfigPath;
|
|
338
|
-
signingAlgorithm;
|
|
339
|
-
hmacSecret;
|
|
340
|
-
ed25519PrivateKey;
|
|
341
|
-
ed25519PublicKey;
|
|
342
|
-
constructor(options) {
|
|
343
|
-
this.auditLogPath = options?.auditLogPath ?? CONFIG.auditLogPath;
|
|
344
|
-
this.trustConfigPath = options?.trustConfigPath ?? CONFIG.trustConfigPath;
|
|
345
|
-
this.signingAlgorithm = options?.algorithm ?? 'hmac-sha256';
|
|
346
|
-
if (this.signingAlgorithm === 'ed25519') {
|
|
347
|
-
const { publicKey, privateKey } = (0, crypto_1.generateKeyPairSync)('ed25519');
|
|
348
|
-
this.ed25519PrivateKey = privateKey;
|
|
349
|
-
this.ed25519PublicKey = publicKey;
|
|
350
|
-
this.hmacSecret = '';
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
this.ed25519PrivateKey = null;
|
|
354
|
-
this.ed25519PublicKey = null;
|
|
355
|
-
this.hmacSecret = options?.hmacSecret ?? (0, crypto_1.randomUUID)();
|
|
356
|
-
}
|
|
357
|
-
// Load resource profiles (user-provided + defaults)
|
|
358
|
-
const profiles = { ...DEFAULT_RESOURCE_PROFILES, ...(options?.resourceProfiles ?? {}) };
|
|
359
|
-
for (const [name, profile] of Object.entries(profiles)) {
|
|
360
|
-
this.resourceProfiles.set(name, profile);
|
|
361
|
-
}
|
|
362
|
-
// Load trust levels (try disk first, then user-provided, then defaults)
|
|
363
|
-
const trustConfigs = options?.trustLevels ?? this.loadTrustFromDisk() ?? DEFAULT_AGENT_TRUST;
|
|
364
|
-
for (const config of trustConfigs) {
|
|
365
|
-
this.agentTrustLevels.set(config.agentId, config.trustLevel);
|
|
366
|
-
this.agentTrustConfigs.set(config.agentId, config);
|
|
367
|
-
}
|
|
368
|
-
// Load existing audit log from disk
|
|
369
|
-
this.loadAuditFromDisk();
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Register a new resource type at runtime.
|
|
373
|
-
* Makes the system extensible for any domain.
|
|
374
|
-
*/
|
|
375
|
-
registerResourceType(name, profile) {
|
|
376
|
-
if (!name || typeof name !== 'string' || name.trim() === '') {
|
|
377
|
-
throw new errors_1.ValidationError('resource name must be a non-empty string');
|
|
378
|
-
}
|
|
379
|
-
if (!profile || typeof profile !== 'object' || typeof profile.baseRisk !== 'number') {
|
|
380
|
-
throw new errors_1.ValidationError('profile must be an object with a numeric baseRisk');
|
|
381
|
-
}
|
|
382
|
-
if (profile.baseRisk < 0 || profile.baseRisk > 1) {
|
|
383
|
-
throw new errors_1.ValidationError('profile.baseRisk must be between 0 and 1');
|
|
384
|
-
}
|
|
385
|
-
if (!Array.isArray(profile.defaultRestrictions)) {
|
|
386
|
-
throw new errors_1.ValidationError('profile.defaultRestrictions must be an array');
|
|
387
|
-
}
|
|
388
|
-
this.resourceProfiles.set(name, profile);
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Register or update an agent's trust configuration at runtime.
|
|
392
|
-
*/
|
|
393
|
-
registerAgentTrust(config) {
|
|
394
|
-
if (!config || typeof config !== 'object') {
|
|
395
|
-
throw new errors_1.ValidationError('config must be an object');
|
|
396
|
-
}
|
|
397
|
-
if (!config.agentId || typeof config.agentId !== 'string' || config.agentId.trim() === '') {
|
|
398
|
-
throw new errors_1.ValidationError('config.agentId must be a non-empty string');
|
|
399
|
-
}
|
|
400
|
-
if (typeof config.trustLevel !== 'number' || config.trustLevel < 0 || config.trustLevel > 1) {
|
|
401
|
-
throw new errors_1.ValidationError('config.trustLevel must be a number between 0 and 1');
|
|
402
|
-
}
|
|
403
|
-
this.agentTrustLevels.set(config.agentId, config.trustLevel);
|
|
404
|
-
this.agentTrustConfigs.set(config.agentId, config);
|
|
405
|
-
this.persistTrustToDisk();
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Request permission to access a resource.
|
|
409
|
-
* resourceType is now a free string -- validated against registered profiles.
|
|
410
|
-
*/
|
|
411
|
-
async requestPermission(agentId, resourceType, justification, scope) {
|
|
412
|
-
if (!agentId || typeof agentId !== 'string') {
|
|
413
|
-
throw new errors_1.ValidationError('agentId must be a non-empty string');
|
|
414
|
-
}
|
|
415
|
-
if (!resourceType || typeof resourceType !== 'string') {
|
|
416
|
-
throw new errors_1.ValidationError('resourceType must be a non-empty string');
|
|
417
|
-
}
|
|
418
|
-
if (!justification || typeof justification !== 'string') {
|
|
419
|
-
throw new errors_1.ValidationError('justification must be a non-empty string');
|
|
420
|
-
}
|
|
421
|
-
// Sanitize inputs
|
|
422
|
-
let safeAgentId;
|
|
423
|
-
let safeJustification;
|
|
424
|
-
try {
|
|
425
|
-
safeAgentId = security_1.InputSanitizer.sanitizeAgentId(agentId);
|
|
426
|
-
safeJustification = security_1.InputSanitizer.sanitizeString(justification, 2000);
|
|
427
|
-
}
|
|
428
|
-
catch {
|
|
429
|
-
safeAgentId = agentId.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 64) || 'unknown';
|
|
430
|
-
safeJustification = justification.slice(0, 2000);
|
|
431
|
-
}
|
|
432
|
-
this.log('permission_request', { agentId: safeAgentId, resourceType, justification: safeJustification, scope });
|
|
433
|
-
// Check if agent is allowed to access this resource type
|
|
434
|
-
const agentConfig = this.agentTrustConfigs.get(safeAgentId);
|
|
435
|
-
if (agentConfig && agentConfig.allowedResources && !agentConfig.allowedResources.includes('*')) {
|
|
436
|
-
if (!agentConfig.allowedResources.includes(resourceType)) {
|
|
437
|
-
this.log('permission_denied', { agentId: safeAgentId, resourceType, reason: 'resource_not_in_allowlist' });
|
|
438
|
-
return {
|
|
439
|
-
granted: false,
|
|
440
|
-
grantToken: null,
|
|
441
|
-
expiresAt: null,
|
|
442
|
-
restrictions: [],
|
|
443
|
-
reason: `Agent '${safeAgentId}' is not authorized to access '${resourceType}'. Allowed: ${agentConfig.allowedResources.join(', ')}`,
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
// Evaluate the permission request
|
|
448
|
-
const evaluation = this.evaluateRequest(safeAgentId, resourceType, safeJustification, scope);
|
|
449
|
-
if (!evaluation.approved) {
|
|
450
|
-
this.log('permission_denied', { agentId: safeAgentId, resourceType, reason: evaluation.reason });
|
|
451
|
-
return {
|
|
452
|
-
granted: false,
|
|
453
|
-
grantToken: null,
|
|
454
|
-
expiresAt: null,
|
|
455
|
-
restrictions: [],
|
|
456
|
-
reason: evaluation.reason,
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
// Generate grant token
|
|
460
|
-
const grantToken = this.generateGrantToken();
|
|
461
|
-
const expiresAt = new Date(Date.now() + CONFIG.grantTokenTTL).toISOString();
|
|
462
|
-
const grant = {
|
|
463
|
-
grantToken,
|
|
464
|
-
resourceType,
|
|
465
|
-
agentId: safeAgentId,
|
|
466
|
-
expiresAt,
|
|
467
|
-
restrictions: evaluation.restrictions,
|
|
468
|
-
scope,
|
|
469
|
-
};
|
|
470
|
-
this.activeGrants.set(grantToken, grant);
|
|
471
|
-
this.log('permission_granted', { grantToken, agentId: safeAgentId, resourceType, expiresAt, restrictions: evaluation.restrictions });
|
|
472
|
-
return {
|
|
473
|
-
granted: true,
|
|
474
|
-
grantToken,
|
|
475
|
-
expiresAt,
|
|
476
|
-
restrictions: evaluation.restrictions,
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Validate a grant token and return `true` if it is active and not expired.
|
|
481
|
-
*
|
|
482
|
-
* @param token - The grant token to validate
|
|
483
|
-
* @returns `true` if the token is valid, `false` otherwise
|
|
484
|
-
*/
|
|
485
|
-
validateToken(token) {
|
|
486
|
-
if (!token || typeof token !== 'string')
|
|
487
|
-
return false;
|
|
488
|
-
const grant = this.activeGrants.get(token);
|
|
489
|
-
if (!grant)
|
|
490
|
-
return false;
|
|
491
|
-
if (new Date(grant.expiresAt) < new Date()) {
|
|
492
|
-
this.activeGrants.delete(token);
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
495
|
-
return true;
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Validate a token and return the bound restrictions and scope.
|
|
499
|
-
* Used to enforce restrictions at the point of use.
|
|
500
|
-
*/
|
|
501
|
-
/**
|
|
502
|
-
* Validate a token and return the full grant object (including restrictions
|
|
503
|
-
* and scope) for point-of-use enforcement.
|
|
504
|
-
*
|
|
505
|
-
* @param token - The grant token to validate
|
|
506
|
-
* @returns The grant details, or `null` if invalid/expired
|
|
507
|
-
*/
|
|
508
|
-
validateTokenWithGrant(token) {
|
|
509
|
-
if (!token || typeof token !== 'string')
|
|
510
|
-
return null;
|
|
511
|
-
const grant = this.activeGrants.get(token);
|
|
512
|
-
if (!grant)
|
|
513
|
-
return null;
|
|
514
|
-
if (new Date(grant.expiresAt) < new Date()) {
|
|
515
|
-
this.activeGrants.delete(token);
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
return grant;
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Enforce restrictions on an operation. Returns an error string if
|
|
522
|
-
* the operation violates any restriction, or null if allowed.
|
|
523
|
-
*/
|
|
524
|
-
/**
|
|
525
|
-
* Enforce restrictions on an operation. Returns an error string if
|
|
526
|
-
* the operation violates any restriction, or `null` if all restrictions pass.
|
|
527
|
-
*
|
|
528
|
-
* @param grantToken - The grant token authorizing the operation
|
|
529
|
-
* @param operation - Description of the operation to check against restrictions
|
|
530
|
-
* @returns Error message string if a restriction is violated, or `null` if allowed
|
|
531
|
-
*/
|
|
532
|
-
enforceRestrictions(grantToken, operation) {
|
|
533
|
-
if (!grantToken || typeof grantToken !== 'string') {
|
|
534
|
-
return 'Invalid or expired grant token';
|
|
535
|
-
}
|
|
536
|
-
const grant = this.validateTokenWithGrant(grantToken);
|
|
537
|
-
if (!grant)
|
|
538
|
-
return 'Invalid or expired grant token';
|
|
539
|
-
for (const restriction of grant.restrictions) {
|
|
540
|
-
// Enforce read_only
|
|
541
|
-
if (restriction === 'read_only' && operation.type && operation.type !== 'read') {
|
|
542
|
-
return `Restriction 'read_only' violated: attempted '${operation.type}'`;
|
|
543
|
-
}
|
|
544
|
-
// Enforce max_records
|
|
545
|
-
const maxRecordsMatch = restriction.match(/^max_records:(\d+)$/);
|
|
546
|
-
if (maxRecordsMatch && operation.recordCount) {
|
|
547
|
-
const max = parseInt(maxRecordsMatch[1], 10);
|
|
548
|
-
if (operation.recordCount > max) {
|
|
549
|
-
return `Restriction '${restriction}' violated: requested ${operation.recordCount} records`;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
// Enforce sandbox_only
|
|
553
|
-
if (restriction === 'sandbox_only' && operation.targetPath) {
|
|
554
|
-
if (/^\/|^[A-Z]:\\(?:Windows|Program)/i.test(operation.targetPath)) {
|
|
555
|
-
return `Restriction 'sandbox_only' violated: path '${operation.targetPath}' is outside sandbox`;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
// Enforce no_sudo
|
|
559
|
-
if (restriction === 'no_sudo' && operation.command) {
|
|
560
|
-
if (/\bsudo\b/i.test(operation.command)) {
|
|
561
|
-
return `Restriction 'no_sudo' violated: command contains sudo`;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
// Enforce workspace_only
|
|
565
|
-
if (restriction === 'workspace_only' && operation.targetPath) {
|
|
566
|
-
if (/\.\.[/\\]/.test(operation.targetPath)) {
|
|
567
|
-
return `Restriction 'workspace_only' violated: path traversal detected`;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
// Enforce no_system_dirs
|
|
571
|
-
if (restriction === 'no_system_dirs' && operation.targetPath) {
|
|
572
|
-
if (/(?:\/etc|\/usr|\/var|\\Windows|\\System32)/i.test(operation.targetPath)) {
|
|
573
|
-
return `Restriction 'no_system_dirs' violated: system directory access`;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
// Enforce no_attachments
|
|
577
|
-
if (restriction === 'no_attachments' && operation.hasAttachments) {
|
|
578
|
-
return `Restriction 'no_attachments' violated`;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
return null; // All restrictions passed
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Revoke a grant token, immediately invalidating it.
|
|
585
|
-
* Silently no-ops if the token doesn't exist.
|
|
586
|
-
*
|
|
587
|
-
* @param token - The grant token to revoke
|
|
588
|
-
*/
|
|
589
|
-
revokeToken(token) {
|
|
590
|
-
this.activeGrants.delete(token);
|
|
591
|
-
this.log('permission_revoked', { token });
|
|
592
|
-
}
|
|
593
|
-
evaluateRequest(agentId, resourceType, justification, scope) {
|
|
594
|
-
// 1. Justification Quality (40% weight) -- now includes resource-relevance
|
|
595
|
-
const justificationScore = this.scoreJustification(justification, resourceType);
|
|
596
|
-
if (justificationScore < 0.3) {
|
|
597
|
-
return {
|
|
598
|
-
approved: false,
|
|
599
|
-
reason: 'Justification is insufficient. Please provide specific task context.',
|
|
600
|
-
restrictions: [],
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
// 2. Agent Trust Level (30% weight)
|
|
604
|
-
const trustLevel = this.agentTrustLevels.get(agentId) ?? 0.5;
|
|
605
|
-
if (trustLevel < 0.4) {
|
|
606
|
-
return {
|
|
607
|
-
approved: false,
|
|
608
|
-
reason: 'Agent trust level is below threshold. Escalate to human operator.',
|
|
609
|
-
restrictions: [],
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
// 3. Risk Assessment (30% weight)
|
|
613
|
-
const riskScore = this.assessRisk(resourceType, scope);
|
|
614
|
-
if (riskScore > 0.8) {
|
|
615
|
-
return {
|
|
616
|
-
approved: false,
|
|
617
|
-
reason: 'Risk assessment exceeds acceptable threshold. Narrow the requested scope.',
|
|
618
|
-
restrictions: [],
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
// Get restrictions from resource profile (data-driven, not hardcoded)
|
|
622
|
-
const profile = this.resourceProfiles.get(resourceType);
|
|
623
|
-
const restrictions = profile
|
|
624
|
-
? [...profile.defaultRestrictions]
|
|
625
|
-
: ['audit_required']; // Unknown resources get audited by default
|
|
626
|
-
// Calculate weighted approval
|
|
627
|
-
const weightedScore = (justificationScore * 0.4) + (trustLevel * 0.3) + ((1 - riskScore) * 0.3);
|
|
628
|
-
const approved = weightedScore >= 0.5;
|
|
629
|
-
return {
|
|
630
|
-
approved,
|
|
631
|
-
reason: approved ? undefined : 'Combined evaluation score below threshold.',
|
|
632
|
-
restrictions,
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Improved justification scoring with resource-relevance checking.
|
|
637
|
-
* Prevents trivial gaming by verifying the justification mentions
|
|
638
|
-
* concepts relevant to the requested resource.
|
|
639
|
-
*/
|
|
640
|
-
scoreJustification(justification, resourceType) {
|
|
641
|
-
let score = 0;
|
|
642
|
-
// Length scoring
|
|
643
|
-
if (justification.length > 20)
|
|
644
|
-
score += 0.15;
|
|
645
|
-
if (justification.length > 50)
|
|
646
|
-
score += 0.15;
|
|
647
|
-
// Intent keywords
|
|
648
|
-
if (/task|purpose|need|require|generate|analyze|process|build|deploy|test|review/i.test(justification))
|
|
649
|
-
score += 0.15;
|
|
650
|
-
// Specificity keywords
|
|
651
|
-
if (/specific|particular|exact|for\s+the|in\s+order\s+to|because|so\s+that/i.test(justification))
|
|
652
|
-
score += 0.15;
|
|
653
|
-
// Penalty for vague/test phrasing
|
|
654
|
-
if (/^test$|^debug$|^try$|^just\s+testing/i.test(justification.trim()))
|
|
655
|
-
score -= 0.3;
|
|
656
|
-
// Resource-relevance check: does the justification mention anything related
|
|
657
|
-
// to the requested resource? (+0.2 bonus for relevant context)
|
|
658
|
-
if (resourceType) {
|
|
659
|
-
const relevancePatterns = {
|
|
660
|
-
SAP_API: /sap|erp|invoice|procurement|purchase|material|vendor/i,
|
|
661
|
-
FINANCIAL_API: /financ|revenue|budget|accounting|payment|ledger|balance/i,
|
|
662
|
-
DATA_EXPORT: /export|report|csv|download|extract|migrate/i,
|
|
663
|
-
FILE_SYSTEM: /file|read|write|save|load|path|directory|workspace/i,
|
|
664
|
-
SHELL_EXEC: /command|script|compile|build|run|execute|terminal/i,
|
|
665
|
-
GIT: /git|commit|branch|merge|pull|push|repository|diff/i,
|
|
666
|
-
PACKAGE_MANAGER: /package|install|dependency|npm|pip|cargo|module/i,
|
|
667
|
-
BUILD_TOOL: /build|compile|webpack|tsc|make|gradle|cargo/i,
|
|
668
|
-
DOCKER: /container|docker|image|deploy|service|compose/i,
|
|
669
|
-
CLOUD_DEPLOY: /deploy|cloud|staging|production|release|infrastructure/i,
|
|
670
|
-
DATABASE: /database|query|sql|table|record|schema|migration/i,
|
|
671
|
-
EXTERNAL_SERVICE: /api|service|endpoint|webhook|request|fetch/i,
|
|
672
|
-
EMAIL: /email|mail|send|notification|alert|message/i,
|
|
673
|
-
WEBHOOK: /webhook|callback|notification|event|dispatch/i,
|
|
674
|
-
};
|
|
675
|
-
const pattern = relevancePatterns[resourceType];
|
|
676
|
-
if (pattern && pattern.test(justification)) {
|
|
677
|
-
score += 0.2;
|
|
678
|
-
}
|
|
679
|
-
else if (pattern && !pattern.test(justification)) {
|
|
680
|
-
// Justification doesn't mention anything relevant -- small penalty
|
|
681
|
-
score -= 0.1;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
// Bonus for mentioning a task/ticket ID
|
|
685
|
-
if (/(?:task|ticket|issue|jira|pr|bug)[_\-#]?\s*\d+/i.test(justification))
|
|
686
|
-
score += 0.1;
|
|
687
|
-
return Math.max(0, Math.min(score, 1));
|
|
688
|
-
}
|
|
689
|
-
assessRisk(resourceType, scope) {
|
|
690
|
-
// Look up base risk from registered profile (not hardcoded)
|
|
691
|
-
const profile = this.resourceProfiles.get(resourceType);
|
|
692
|
-
let risk = profile?.baseRisk ?? 0.5; // Unknown resources get medium risk
|
|
693
|
-
// Broad scopes increase risk
|
|
694
|
-
if (!scope || scope === '*' || scope === 'all') {
|
|
695
|
-
risk += 0.2;
|
|
696
|
-
}
|
|
697
|
-
// Write/delete operations increase risk
|
|
698
|
-
if (scope && /write|delete|update|modify|execute|deploy/i.test(scope)) {
|
|
699
|
-
risk += 0.2;
|
|
700
|
-
}
|
|
701
|
-
return Math.min(risk, 1);
|
|
702
|
-
}
|
|
703
|
-
generateGrantToken() {
|
|
704
|
-
const id = (0, crypto_1.randomUUID)().replace(/-/g, '');
|
|
705
|
-
const payload = `grant_${id}`;
|
|
706
|
-
if (this.signingAlgorithm === 'ed25519' && this.ed25519PrivateKey) {
|
|
707
|
-
const sig = (0, crypto_1.sign)(null, Buffer.from(payload), this.ed25519PrivateKey).toString('base64url');
|
|
708
|
-
return `${payload}.${sig}`;
|
|
709
|
-
}
|
|
710
|
-
// HMAC: append signature so tokens are tamper-evident
|
|
711
|
-
const sig = (0, crypto_1.createHmac)('sha256', this.hmacSecret).update(payload).digest('base64url');
|
|
712
|
-
return `${payload}.${sig}`;
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Verify a grant token's cryptographic signature.
|
|
716
|
-
* For Ed25519 tokens, this can be done by any party holding the public key.
|
|
717
|
-
* For HMAC tokens, only the issuing AuthGuardian can verify.
|
|
718
|
-
*
|
|
719
|
-
* @param token - The grant token to verify
|
|
720
|
-
* @returns `true` if the signature is valid
|
|
721
|
-
*/
|
|
722
|
-
verifyTokenSignature(token) {
|
|
723
|
-
const dotIndex = token.lastIndexOf('.');
|
|
724
|
-
if (dotIndex === -1)
|
|
725
|
-
return false;
|
|
726
|
-
const payload = token.slice(0, dotIndex);
|
|
727
|
-
const sig = token.slice(dotIndex + 1);
|
|
728
|
-
if (this.signingAlgorithm === 'ed25519' && this.ed25519PublicKey) {
|
|
729
|
-
try {
|
|
730
|
-
return (0, crypto_1.verify)(null, Buffer.from(payload), this.ed25519PublicKey, Buffer.from(sig, 'base64url'));
|
|
731
|
-
}
|
|
732
|
-
catch {
|
|
733
|
-
return false;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
const expected = (0, crypto_1.createHmac)('sha256', this.hmacSecret).update(payload).digest('base64url');
|
|
737
|
-
// Constant-time comparison
|
|
738
|
-
if (expected.length !== sig.length)
|
|
739
|
-
return false;
|
|
740
|
-
let result = 0;
|
|
741
|
-
for (let i = 0; i < expected.length; i++) {
|
|
742
|
-
result |= expected.charCodeAt(i) ^ sig.charCodeAt(i);
|
|
743
|
-
}
|
|
744
|
-
return result === 0;
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Get the signing algorithm used by this AuthGuardian instance.
|
|
748
|
-
*/
|
|
749
|
-
getSigningAlgorithm() {
|
|
750
|
-
return this.signingAlgorithm;
|
|
751
|
-
}
|
|
752
|
-
/**
|
|
753
|
-
* Export the Ed25519 public key in PEM format for third-party verification.
|
|
754
|
-
* Returns `null` if the instance uses HMAC signing.
|
|
755
|
-
*/
|
|
756
|
-
exportPublicKey() {
|
|
757
|
-
if (!this.ed25519PublicKey)
|
|
758
|
-
return null;
|
|
759
|
-
return this.ed25519PublicKey.export({ type: 'spki', format: 'pem' });
|
|
760
|
-
}
|
|
761
|
-
log(action, details) {
|
|
762
|
-
const entry = {
|
|
763
|
-
timestamp: new Date().toISOString(),
|
|
764
|
-
action,
|
|
765
|
-
details,
|
|
766
|
-
};
|
|
767
|
-
this.auditLog.push(entry);
|
|
768
|
-
// Persist to disk
|
|
769
|
-
try {
|
|
770
|
-
const dir = (0, path_1.join)('.', 'data');
|
|
771
|
-
if (!(0, fs_1.existsSync)(dir)) {
|
|
772
|
-
require('fs').mkdirSync(dir, { recursive: true });
|
|
773
|
-
}
|
|
774
|
-
(0, fs_1.appendFileSync)(this.auditLogPath, JSON.stringify(entry) + '\n');
|
|
775
|
-
}
|
|
776
|
-
catch {
|
|
777
|
-
// Non-fatal -- log is also in memory
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Get all active (non-expired) permission grants.
|
|
782
|
-
* Automatically cleans up expired grants before returning.
|
|
783
|
-
*/
|
|
784
|
-
getActiveGrants() {
|
|
785
|
-
// Clean expired grants
|
|
786
|
-
const now = new Date();
|
|
787
|
-
for (const [token, grant] of this.activeGrants.entries()) {
|
|
788
|
-
if (new Date(grant.expiresAt) < now) {
|
|
789
|
-
this.activeGrants.delete(token);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
return Array.from(this.activeGrants.values());
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* Get the full audit log of permission decisions.
|
|
796
|
-
* Returns a defensive copy.
|
|
797
|
-
*/
|
|
798
|
-
getAuditLog() {
|
|
799
|
-
return [...this.auditLog];
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* Get all registered resource profiles.
|
|
803
|
-
*/
|
|
804
|
-
getResourceProfiles() {
|
|
805
|
-
return Object.fromEntries(this.resourceProfiles);
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Get the allowed namespaces for an agent (used by blackboard scoping).
|
|
809
|
-
*/
|
|
810
|
-
getAgentNamespaces(agentId) {
|
|
811
|
-
if (!agentId || typeof agentId !== 'string')
|
|
812
|
-
return ['task:'];
|
|
813
|
-
const config = this.agentTrustConfigs.get(agentId);
|
|
814
|
-
return config?.allowedNamespaces ?? ['task:'];
|
|
815
|
-
}
|
|
816
|
-
// ---- Persistence helpers ----
|
|
817
|
-
loadTrustFromDisk() {
|
|
818
|
-
try {
|
|
819
|
-
if ((0, fs_1.existsSync)(this.trustConfigPath)) {
|
|
820
|
-
const raw = (0, fs_1.readFileSync)(this.trustConfigPath, 'utf-8');
|
|
821
|
-
return JSON.parse(raw);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
catch { /* ignore */ }
|
|
825
|
-
return null;
|
|
826
|
-
}
|
|
827
|
-
persistTrustToDisk() {
|
|
828
|
-
try {
|
|
829
|
-
const dir = (0, path_1.join)('.', 'data');
|
|
830
|
-
if (!(0, fs_1.existsSync)(dir)) {
|
|
831
|
-
require('fs').mkdirSync(dir, { recursive: true });
|
|
832
|
-
}
|
|
833
|
-
const configs = Array.from(this.agentTrustConfigs.values());
|
|
834
|
-
(0, fs_1.writeFileSync)(this.trustConfigPath, JSON.stringify(configs, null, 2));
|
|
835
|
-
}
|
|
836
|
-
catch {
|
|
837
|
-
// Non-fatal
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
loadAuditFromDisk() {
|
|
841
|
-
try {
|
|
842
|
-
if ((0, fs_1.existsSync)(this.auditLogPath)) {
|
|
843
|
-
const raw = (0, fs_1.readFileSync)(this.auditLogPath, 'utf-8');
|
|
844
|
-
const lines = raw.trim().split('\n').filter(l => l);
|
|
845
|
-
for (const line of lines) {
|
|
846
|
-
try {
|
|
847
|
-
this.auditLog.push(JSON.parse(line));
|
|
848
|
-
}
|
|
849
|
-
catch { /* skip malformed */ }
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
catch { /* ignore */ }
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
exports.AuthGuardian = AuthGuardian;
|
|
857
|
-
// ============================================================================
|
|
858
|
-
// TASK DECOMPOSITION ENGINE
|
|
859
|
-
// ============================================================================
|
|
860
|
-
/**
|
|
861
|
-
* Decomposes complex tasks into parallel sub-agent executions.
|
|
862
|
-
*
|
|
863
|
-
* Supports four synthesis strategies (`merge`, `vote`, `chain`, `first-success`)
|
|
864
|
-
* and caches results on the blackboard to avoid redundant work.
|
|
865
|
-
* Routes each sub-task through the {@link AdapterRegistry} so any
|
|
866
|
-
* registered framework can participate.
|
|
867
|
-
*/
|
|
868
|
-
class TaskDecomposer {
|
|
869
|
-
blackboard;
|
|
870
|
-
authGuardian;
|
|
871
|
-
adapterRegistry;
|
|
872
|
-
constructor(blackboard, authGuardian, adapterRegistry) {
|
|
873
|
-
if (!blackboard || !(blackboard instanceof SharedBlackboard)) {
|
|
874
|
-
throw new errors_1.ValidationError('blackboard must be an instance of SharedBlackboard');
|
|
875
|
-
}
|
|
876
|
-
if (!authGuardian || !(authGuardian instanceof AuthGuardian)) {
|
|
877
|
-
throw new errors_1.ValidationError('authGuardian must be an instance of AuthGuardian');
|
|
878
|
-
}
|
|
879
|
-
if (!adapterRegistry || !(adapterRegistry instanceof adapter_registry_1.AdapterRegistry)) {
|
|
880
|
-
throw new errors_1.ValidationError('adapterRegistry must be an instance of AdapterRegistry');
|
|
881
|
-
}
|
|
882
|
-
this.blackboard = blackboard;
|
|
883
|
-
this.authGuardian = authGuardian;
|
|
884
|
-
this.adapterRegistry = adapterRegistry;
|
|
885
|
-
}
|
|
886
|
-
/**
|
|
887
|
-
* Decomposes a complex task into parallel sub-agent calls
|
|
888
|
-
* This is the "Wall Breaker" - transforms impossible monolithic tasks
|
|
889
|
-
* into manageable parallel executions
|
|
890
|
-
*/
|
|
891
|
-
async executeParallel(tasks, synthesisStrategy = 'merge', context) {
|
|
892
|
-
if (!tasks || !Array.isArray(tasks)) {
|
|
893
|
-
throw new errors_1.ValidationError('tasks must be an array');
|
|
894
|
-
}
|
|
895
|
-
if (tasks.length === 0) {
|
|
896
|
-
throw new errors_1.ValidationError('tasks array must not be empty');
|
|
897
|
-
}
|
|
898
|
-
if (!context || typeof context !== 'object' || !context.agentId) {
|
|
899
|
-
throw new errors_1.ValidationError('context is required and must include agentId');
|
|
900
|
-
}
|
|
901
|
-
// No hard parallel limit — caller controls concurrency via task count
|
|
902
|
-
const startTime = Date.now();
|
|
903
|
-
const individualResults = [];
|
|
904
|
-
// Check blackboard for cached results first
|
|
905
|
-
const cachedTasks = [];
|
|
906
|
-
const uncachedTasks = [];
|
|
907
|
-
for (const task of tasks) {
|
|
908
|
-
const cacheKey = `task:${task.agentType}:${this.hashPayload(task.taskPayload)}`;
|
|
909
|
-
const cached = this.blackboard.read(cacheKey);
|
|
910
|
-
if (cached) {
|
|
911
|
-
individualResults.push({
|
|
912
|
-
agentType: task.agentType,
|
|
913
|
-
success: true,
|
|
914
|
-
result: cached.value,
|
|
915
|
-
executionTime: 0, // From cache
|
|
916
|
-
});
|
|
917
|
-
cachedTasks.push(task);
|
|
918
|
-
}
|
|
919
|
-
else {
|
|
920
|
-
uncachedTasks.push(task);
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
// Execute uncached tasks in parallel using Promise.all
|
|
924
|
-
if (uncachedTasks.length > 0) {
|
|
925
|
-
const parallelPromises = uncachedTasks.map(task => this.executeSingleTask(task, context));
|
|
926
|
-
const results = await Promise.all(parallelPromises);
|
|
927
|
-
for (let i = 0; i < results.length; i++) {
|
|
928
|
-
const task = uncachedTasks[i];
|
|
929
|
-
const result = results[i];
|
|
930
|
-
individualResults.push(result);
|
|
931
|
-
// Cache successful results
|
|
932
|
-
if (result.success) {
|
|
933
|
-
const cacheKey = `task:${task.agentType}:${this.hashPayload(task.taskPayload)}`;
|
|
934
|
-
this.blackboard.write(cacheKey, result.result, context.agentId, 3600, 'system-orchestrator-token'); // 1 hour TTL
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
// Synthesize results based on strategy
|
|
939
|
-
const synthesizedResult = this.synthesize(individualResults, synthesisStrategy);
|
|
940
|
-
const totalTime = Date.now() - startTime;
|
|
941
|
-
const successCount = individualResults.filter(r => r.success).length;
|
|
942
|
-
return {
|
|
943
|
-
synthesizedResult,
|
|
944
|
-
individualResults,
|
|
945
|
-
executionMetrics: {
|
|
946
|
-
totalTime,
|
|
947
|
-
successRate: successCount / individualResults.length,
|
|
948
|
-
synthesisStrategy,
|
|
949
|
-
},
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
async executeSingleTask(task, context) {
|
|
953
|
-
const taskStart = Date.now();
|
|
954
|
-
try {
|
|
955
|
-
// Build the handoff message
|
|
956
|
-
const handoff = {
|
|
957
|
-
handoffId: (0, crypto_1.randomUUID)(),
|
|
958
|
-
sourceAgent: context.agentId,
|
|
959
|
-
targetAgent: task.agentType,
|
|
960
|
-
taskType: 'delegate',
|
|
961
|
-
payload: task.taskPayload,
|
|
962
|
-
metadata: {
|
|
963
|
-
priority: 1,
|
|
964
|
-
deadline: Date.now() + CONFIG.defaultTimeout,
|
|
965
|
-
parentTaskId: context.taskId ?? null,
|
|
966
|
-
},
|
|
967
|
-
};
|
|
968
|
-
// Sanitize the instruction before sending to adapter
|
|
969
|
-
let sanitizedInstruction = task.taskPayload.instruction;
|
|
970
|
-
try {
|
|
971
|
-
sanitizedInstruction = security_1.InputSanitizer.sanitizeString(task.taskPayload.instruction, 10000);
|
|
972
|
-
}
|
|
973
|
-
catch { /* use original if sanitization fails */ }
|
|
974
|
-
// Use namespace-scoped snapshot -- target agent only sees keys it's allowed to see
|
|
975
|
-
const scopedSnapshot = this.blackboard.getScopedSnapshot(task.agentType);
|
|
976
|
-
// Route through the adapter registry (framework-agnostic)
|
|
977
|
-
const agentPayload = {
|
|
978
|
-
action: 'execute',
|
|
979
|
-
params: {},
|
|
980
|
-
handoff: {
|
|
981
|
-
handoffId: handoff.handoffId,
|
|
982
|
-
sourceAgent: handoff.sourceAgent,
|
|
983
|
-
targetAgent: handoff.targetAgent,
|
|
984
|
-
taskType: handoff.taskType,
|
|
985
|
-
instruction: sanitizedInstruction,
|
|
986
|
-
context: handoff.payload.context,
|
|
987
|
-
constraints: handoff.payload.constraints,
|
|
988
|
-
expectedOutput: handoff.payload.expectedOutput,
|
|
989
|
-
metadata: handoff.metadata,
|
|
990
|
-
},
|
|
991
|
-
blackboardSnapshot: scopedSnapshot,
|
|
992
|
-
};
|
|
993
|
-
const agentContext = {
|
|
994
|
-
agentId: context.agentId,
|
|
995
|
-
taskId: context.taskId,
|
|
996
|
-
sessionId: context.sessionId,
|
|
997
|
-
};
|
|
998
|
-
const result = await this.adapterRegistry.executeAgent(task.agentType, agentPayload, agentContext);
|
|
999
|
-
// Sanitize adapter output before returning/caching
|
|
1000
|
-
let sanitizedData = result.data;
|
|
1001
|
-
try {
|
|
1002
|
-
sanitizedData = security_1.InputSanitizer.sanitizeObject(result.data);
|
|
1003
|
-
}
|
|
1004
|
-
catch { /* use raw if sanitization fails */ }
|
|
1005
|
-
return {
|
|
1006
|
-
agentType: task.agentType,
|
|
1007
|
-
success: true,
|
|
1008
|
-
result: sanitizedData,
|
|
1009
|
-
executionTime: Date.now() - taskStart,
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
catch (error) {
|
|
1013
|
-
return {
|
|
1014
|
-
agentType: task.agentType,
|
|
1015
|
-
success: false,
|
|
1016
|
-
result: {
|
|
1017
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1018
|
-
recoverable: true,
|
|
1019
|
-
},
|
|
1020
|
-
executionTime: Date.now() - taskStart,
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
synthesize(results, strategy) {
|
|
1025
|
-
const successfulResults = results.filter(r => r.success);
|
|
1026
|
-
if (successfulResults.length === 0) {
|
|
1027
|
-
return {
|
|
1028
|
-
error: 'All parallel tasks failed',
|
|
1029
|
-
individualErrors: results.map(r => ({
|
|
1030
|
-
agent: r.agentType,
|
|
1031
|
-
error: r.result,
|
|
1032
|
-
})),
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
switch (strategy) {
|
|
1036
|
-
case 'merge':
|
|
1037
|
-
// Combine all results into a unified object
|
|
1038
|
-
return {
|
|
1039
|
-
merged: true,
|
|
1040
|
-
contributions: successfulResults.map(r => ({
|
|
1041
|
-
source: r.agentType,
|
|
1042
|
-
data: r.result,
|
|
1043
|
-
})),
|
|
1044
|
-
summary: this.generateMergeSummary(successfulResults),
|
|
1045
|
-
};
|
|
1046
|
-
case 'vote':
|
|
1047
|
-
// Return the result with highest "confidence" (simplified: most data)
|
|
1048
|
-
const scored = successfulResults.map(r => ({
|
|
1049
|
-
result: r,
|
|
1050
|
-
score: JSON.stringify(r.result).length,
|
|
1051
|
-
}));
|
|
1052
|
-
scored.sort((a, b) => b.score - a.score);
|
|
1053
|
-
return {
|
|
1054
|
-
voted: true,
|
|
1055
|
-
winner: scored[0].result.agentType,
|
|
1056
|
-
result: scored[0].result.result,
|
|
1057
|
-
};
|
|
1058
|
-
case 'chain':
|
|
1059
|
-
// Results should already be ordered; return the final one
|
|
1060
|
-
return {
|
|
1061
|
-
chained: true,
|
|
1062
|
-
finalResult: successfulResults[successfulResults.length - 1].result,
|
|
1063
|
-
chainLength: successfulResults.length,
|
|
1064
|
-
};
|
|
1065
|
-
case 'first-success':
|
|
1066
|
-
// Return the first successful result
|
|
1067
|
-
return {
|
|
1068
|
-
firstSuccess: true,
|
|
1069
|
-
source: successfulResults[0].agentType,
|
|
1070
|
-
result: successfulResults[0].result,
|
|
1071
|
-
};
|
|
1072
|
-
default:
|
|
1073
|
-
return successfulResults.map(r => r.result);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
generateMergeSummary(results) {
|
|
1077
|
-
const agents = results.map(r => r.agentType).join(', ');
|
|
1078
|
-
return `Synthesized from ${results.length} agents: ${agents}`;
|
|
1079
|
-
}
|
|
1080
|
-
hashPayload(payload) {
|
|
1081
|
-
// Simple hash for cache key generation
|
|
1082
|
-
const str = JSON.stringify(payload);
|
|
1083
|
-
let hash = 0;
|
|
1084
|
-
for (let i = 0; i < str.length; i++) {
|
|
1085
|
-
const char = str.charCodeAt(i);
|
|
1086
|
-
hash = ((hash << 5) - hash) + char;
|
|
1087
|
-
hash = hash & hash;
|
|
1088
|
-
}
|
|
1089
|
-
return Math.abs(hash).toString(16);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
exports.TaskDecomposer = TaskDecomposer;
|
|
46
|
+
// Types, interfaces, CONFIG, and default profiles are now in ./lib/orchestrator-types.ts
|
|
47
|
+
// SharedBlackboard is now in ./lib/shared-blackboard.ts
|
|
48
|
+
// AuthGuardian is now in ./lib/auth-guardian.ts
|
|
49
|
+
// TaskDecomposer is now in ./lib/task-decomposer.ts
|
|
1093
50
|
// ============================================================================
|
|
1094
51
|
// SWARM ORCHESTRATOR - MAIN SKILL IMPLEMENTATION
|
|
1095
52
|
// ============================================================================
|
|
@@ -1124,6 +81,15 @@ class SwarmOrchestrator {
|
|
|
1124
81
|
agentRegistry = new Map();
|
|
1125
82
|
gateway;
|
|
1126
83
|
qualityGate;
|
|
84
|
+
injectionShield;
|
|
85
|
+
tracer;
|
|
86
|
+
eventBus;
|
|
87
|
+
conversationLog;
|
|
88
|
+
otel;
|
|
89
|
+
metrics;
|
|
90
|
+
heatmap;
|
|
91
|
+
anomalyDetector;
|
|
92
|
+
lifecycleHooks;
|
|
1127
93
|
/** Named isolated blackboards, keyed by board name */
|
|
1128
94
|
namedBlackboards = new Map();
|
|
1129
95
|
/** Root workspace path -- used as the parent for named board subdirectories */
|
|
@@ -1138,19 +104,28 @@ class SwarmOrchestrator {
|
|
|
1138
104
|
throw new errors_1.ValidationError('workspacePath must not be empty');
|
|
1139
105
|
}
|
|
1140
106
|
this._workspacePath = workspacePath;
|
|
1141
|
-
this.blackboard = new SharedBlackboard(workspacePath);
|
|
1142
|
-
this.authGuardian = new AuthGuardian({
|
|
107
|
+
this.blackboard = new shared_blackboard_1.SharedBlackboard(workspacePath);
|
|
108
|
+
this.authGuardian = new auth_guardian_1.AuthGuardian({
|
|
1143
109
|
trustLevels: options?.trustLevels,
|
|
1144
110
|
resourceProfiles: options?.resourceProfiles,
|
|
1145
111
|
});
|
|
1146
112
|
this.adapters = adapterRegistry ?? new adapter_registry_1.AdapterRegistry();
|
|
1147
|
-
this.taskDecomposer = new TaskDecomposer(this.blackboard, this.authGuardian, this.adapters);
|
|
113
|
+
this.taskDecomposer = new task_decomposer_1.TaskDecomposer(this.blackboard, this.authGuardian, this.adapters);
|
|
1148
114
|
this.gateway = new security_1.SecureSwarmGateway();
|
|
1149
115
|
this.qualityGate = new blackboard_validator_1.QualityGateAgent({
|
|
1150
116
|
validationConfig: options?.validationConfig,
|
|
1151
117
|
qualityThreshold: options?.qualityThreshold,
|
|
1152
118
|
aiReviewCallback: options?.aiReviewCallback,
|
|
1153
119
|
});
|
|
120
|
+
this.injectionShield = new security_1.PromptInjectionShield();
|
|
121
|
+
this.tracer = new explainability_1.ExplainabilityTracer();
|
|
122
|
+
this.eventBus = new event_bus_1.OrchestratorEventBus({ snapshotInterval: 100 });
|
|
123
|
+
this.conversationLog = new agent_conversation_1.AgentConversationLog();
|
|
124
|
+
this.otel = new otel_bridge_1.OTelBridge();
|
|
125
|
+
this.metrics = (0, metrics_1.createOrchestratorMetrics)();
|
|
126
|
+
this.heatmap = new cost_heatmap_1.CostHeatmap();
|
|
127
|
+
this.anomalyDetector = new anomaly_detector_1.AnomalyDetector();
|
|
128
|
+
this.lifecycleHooks = new lifecycle_hooks_1.OrchestratorLifecycleHooks();
|
|
1154
129
|
// Register the orchestrator agent on the blackboard with full access
|
|
1155
130
|
this.blackboard.registerAgent('orchestrator', 'system-orchestrator-token', ['*']);
|
|
1156
131
|
}
|
|
@@ -1219,7 +194,7 @@ class SwarmOrchestrator {
|
|
|
1219
194
|
}
|
|
1220
195
|
// Use sanitized params from gateway
|
|
1221
196
|
const safeParams = gatewayResult.sanitizedParams ?? params;
|
|
1222
|
-
if (CONFIG.enableTracing) {
|
|
197
|
+
if (orchestrator_types_1.CONFIG.enableTracing) {
|
|
1223
198
|
try {
|
|
1224
199
|
this.blackboard.write(`trace:${traceId}`, {
|
|
1225
200
|
action,
|
|
@@ -1276,14 +251,59 @@ class SwarmOrchestrator {
|
|
|
1276
251
|
const targetAgent = params.targetAgent;
|
|
1277
252
|
const taskPayload = params.taskPayload;
|
|
1278
253
|
const priority = params.priority ?? 'normal';
|
|
1279
|
-
const timeout = params.timeout ?? CONFIG.defaultTimeout;
|
|
254
|
+
const timeout = params.timeout ?? orchestrator_types_1.CONFIG.defaultTimeout;
|
|
1280
255
|
const requiresAuth = params.requiresAuth ?? false;
|
|
1281
256
|
const resourceType = params.resourceType ?? 'EXTERNAL_SERVICE';
|
|
257
|
+
const delegationTraceId = this.tracer.record({
|
|
258
|
+
source: 'SwarmOrchestrator',
|
|
259
|
+
decision: 'delegation_start',
|
|
260
|
+
outcome: 'initiated',
|
|
261
|
+
agentId: context.agentId,
|
|
262
|
+
factors: [
|
|
263
|
+
{ name: 'targetAgent', value: targetAgent },
|
|
264
|
+
{ name: 'priority', value: priority },
|
|
265
|
+
{ name: 'requiresAuth', value: requiresAuth },
|
|
266
|
+
{ name: 'timeout', value: timeout },
|
|
267
|
+
],
|
|
268
|
+
});
|
|
269
|
+
this.eventBus.publish('orchestrator', 'delegation_start', 'info', {
|
|
270
|
+
targetAgent, priority, requiresAuth, timeout, traceId: delegationTraceId,
|
|
271
|
+
}, context.agentId, delegationTraceId);
|
|
272
|
+
this.metrics.delegationsTotal.inc({ agent: targetAgent });
|
|
273
|
+
const delegationStartMs = Date.now();
|
|
274
|
+
// Orchestrator-level beforeSpawn hook
|
|
275
|
+
const spawnCtx = await this.lifecycleHooks.runBeforeSpawn({
|
|
276
|
+
agentId: targetAgent,
|
|
277
|
+
instruction: taskPayload.instruction,
|
|
278
|
+
priority,
|
|
279
|
+
requiresAuth,
|
|
280
|
+
metadata: { timeout, resourceType },
|
|
281
|
+
aborted: false,
|
|
282
|
+
});
|
|
283
|
+
if (spawnCtx.aborted) {
|
|
284
|
+
return {
|
|
285
|
+
success: false,
|
|
286
|
+
error: {
|
|
287
|
+
code: 'LIFECYCLE_ABORTED',
|
|
288
|
+
message: spawnCtx.abortReason ?? 'Aborted by beforeSpawn hook',
|
|
289
|
+
recoverable: true,
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
1282
293
|
// Check permission wall if required -- now returns bound restrictions
|
|
1283
294
|
let grantToken = null;
|
|
1284
295
|
if (requiresAuth) {
|
|
1285
296
|
const authResult = await this.authGuardian.requestPermission(context.agentId, resourceType, `Delegating task to ${targetAgent}: ${taskPayload.instruction}`, 'delegate');
|
|
1286
297
|
if (!authResult.granted) {
|
|
298
|
+
this.tracer.record({
|
|
299
|
+
source: 'AuthGuardian', decision: 'permission_check', outcome: 'denied',
|
|
300
|
+
agentId: context.agentId, parentTraceId: delegationTraceId,
|
|
301
|
+
factors: [{ name: 'reason', value: authResult.reason }],
|
|
302
|
+
});
|
|
303
|
+
this.eventBus.publish('auth', 'permission_denied', 'warn', {
|
|
304
|
+
reason: authResult.reason, resourceType,
|
|
305
|
+
}, context.agentId, delegationTraceId);
|
|
306
|
+
this.metrics.permissionDenials.inc({ agent: context.agentId });
|
|
1287
307
|
return {
|
|
1288
308
|
success: false,
|
|
1289
309
|
error: {
|
|
@@ -1349,6 +369,31 @@ class SwarmOrchestrator {
|
|
|
1349
369
|
sanitizedInstruction = security_1.InputSanitizer.sanitizeString(taskPayload.instruction, 10000);
|
|
1350
370
|
}
|
|
1351
371
|
catch { /* use original if sanitization fails */ }
|
|
372
|
+
// Prompt injection detection
|
|
373
|
+
const injectionResult = this.injectionShield.analyze(sanitizedInstruction);
|
|
374
|
+
if (!injectionResult.safe) {
|
|
375
|
+
this.tracer.record({
|
|
376
|
+
source: 'PromptInjectionShield', decision: 'injection_scan', outcome: 'blocked',
|
|
377
|
+
agentId: context.agentId, parentTraceId: delegationTraceId,
|
|
378
|
+
factors: [
|
|
379
|
+
{ name: 'score', value: injectionResult.score },
|
|
380
|
+
{ name: 'matchedRules', value: injectionResult.matchedRules },
|
|
381
|
+
],
|
|
382
|
+
});
|
|
383
|
+
this.eventBus.publish('injection', 'blocked', 'error', {
|
|
384
|
+
score: injectionResult.score, rules: injectionResult.matchedRules,
|
|
385
|
+
}, context.agentId, delegationTraceId);
|
|
386
|
+
this.metrics.injectionBlocks.inc({ agent: context.agentId });
|
|
387
|
+
return {
|
|
388
|
+
success: false,
|
|
389
|
+
error: {
|
|
390
|
+
code: 'PROMPT_INJECTION_BLOCKED',
|
|
391
|
+
message: `Prompt injection detected (score=${injectionResult.score.toFixed(2)}, rules: ${injectionResult.matchedRules.join(', ')})`,
|
|
392
|
+
recoverable: true,
|
|
393
|
+
suggestedAction: 'Rephrase the instruction to remove injection patterns',
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
1352
397
|
// P1: Namespace-scoped snapshot -- target agent only sees keys it's allowed to see
|
|
1353
398
|
const scopedSnapshot = this.blackboard.getScopedSnapshot(targetAgent);
|
|
1354
399
|
const agentPayload = {
|
|
@@ -1372,10 +417,13 @@ class SwarmOrchestrator {
|
|
|
1372
417
|
taskId: context.taskId,
|
|
1373
418
|
sessionId: context.sessionId,
|
|
1374
419
|
};
|
|
420
|
+
const otelSpan = this.otel.startDelegation(context.agentId, targetAgent, handoff.handoffId);
|
|
1375
421
|
const result = await Promise.race([
|
|
1376
422
|
this.adapters.executeAgent(targetAgent, agentPayload, agentContext),
|
|
1377
423
|
this.timeoutPromise(timeout),
|
|
1378
424
|
]);
|
|
425
|
+
otelSpan.setAttribute('agent.result.success', result.success);
|
|
426
|
+
otelSpan.end();
|
|
1379
427
|
// P1: Sanitize adapter output before caching
|
|
1380
428
|
let sanitizedResult = result;
|
|
1381
429
|
try {
|
|
@@ -1387,7 +435,19 @@ class SwarmOrchestrator {
|
|
|
1387
435
|
taskInstruction: taskPayload.instruction,
|
|
1388
436
|
expectedOutput: taskPayload.expectedOutput,
|
|
1389
437
|
});
|
|
438
|
+
this.tracer.record({
|
|
439
|
+
source: 'QualityGateAgent', decision: 'quality_gate', outcome: gateResult.decision,
|
|
440
|
+
agentId: targetAgent, parentTraceId: delegationTraceId,
|
|
441
|
+
factors: [
|
|
442
|
+
{ name: 'score', value: gateResult.validation.score },
|
|
443
|
+
{ name: 'issueCount', value: gateResult.validation.issues.length },
|
|
444
|
+
],
|
|
445
|
+
});
|
|
446
|
+
this.eventBus.publish('quality', gateResult.decision, gateResult.decision === 'reject' ? 'warn' : 'info', {
|
|
447
|
+
score: gateResult.validation.score, issueCount: gateResult.validation.issues.length,
|
|
448
|
+
}, targetAgent, delegationTraceId);
|
|
1390
449
|
if (gateResult.decision === 'reject') {
|
|
450
|
+
this.metrics.qualityRejections.inc({ agent: targetAgent });
|
|
1391
451
|
return {
|
|
1392
452
|
success: false,
|
|
1393
453
|
error: {
|
|
@@ -1419,6 +479,35 @@ class SwarmOrchestrator {
|
|
|
1419
479
|
}
|
|
1420
480
|
// Approved -- cache result
|
|
1421
481
|
this.blackboard.write(cacheKey, sanitizedResult, context.agentId, 1800, 'system-orchestrator-token'); // 30 min TTL
|
|
482
|
+
this.metrics.delegationDurationMs.observe({ agent: targetAgent }, Date.now() - delegationStartMs);
|
|
483
|
+
this.heatmap.record(targetAgent, {
|
|
484
|
+
durationMs: Date.now() - delegationStartMs,
|
|
485
|
+
inputTokens: 0, outputTokens: 0, success: true,
|
|
486
|
+
});
|
|
487
|
+
this.anomalyDetector.observe(targetAgent, {
|
|
488
|
+
latencyMs: Date.now() - delegationStartMs, tokens: 0, success: true,
|
|
489
|
+
});
|
|
490
|
+
const resultMeta = sanitizedResult?.metadata;
|
|
491
|
+
this.conversationLog.recordTurn(targetAgent, {
|
|
492
|
+
instruction: taskPayload.instruction,
|
|
493
|
+
result: JSON.stringify(sanitizedResult).slice(0, 2000),
|
|
494
|
+
success: true,
|
|
495
|
+
tokensUsed: resultMeta?.tokensUsed,
|
|
496
|
+
executionTimeMs: resultMeta?.executionTimeMs,
|
|
497
|
+
adapter: resultMeta?.adapter,
|
|
498
|
+
qualityDecision: gateResult.decision,
|
|
499
|
+
correlationId: delegationTraceId,
|
|
500
|
+
sourceAgent: context.agentId,
|
|
501
|
+
});
|
|
502
|
+
// Orchestrator-level afterComplete hook
|
|
503
|
+
await this.lifecycleHooks.runAfterComplete({
|
|
504
|
+
agentId: targetAgent,
|
|
505
|
+
instruction: taskPayload.instruction,
|
|
506
|
+
result: sanitizedResult,
|
|
507
|
+
durationMs: Date.now() - delegationStartMs,
|
|
508
|
+
tokensUsed: resultMeta?.tokensUsed ?? 0,
|
|
509
|
+
metadata: { qualityDecision: gateResult.decision },
|
|
510
|
+
});
|
|
1422
511
|
return {
|
|
1423
512
|
success: true,
|
|
1424
513
|
data: {
|
|
@@ -1434,6 +523,29 @@ class SwarmOrchestrator {
|
|
|
1434
523
|
};
|
|
1435
524
|
}
|
|
1436
525
|
catch (error) {
|
|
526
|
+
this.metrics.delegationErrors.inc({ agent: targetAgent });
|
|
527
|
+
this.conversationLog.recordTurn(targetAgent, {
|
|
528
|
+
instruction: taskPayload.instruction,
|
|
529
|
+
success: false,
|
|
530
|
+
errorCode: 'DELEGATION_FAILED',
|
|
531
|
+
correlationId: delegationTraceId,
|
|
532
|
+
sourceAgent: context.agentId,
|
|
533
|
+
});
|
|
534
|
+
this.eventBus.publish('orchestrator', 'delegation_failed', 'error', {
|
|
535
|
+
error: error instanceof Error ? error.message : 'unknown',
|
|
536
|
+
}, targetAgent, delegationTraceId);
|
|
537
|
+
// Orchestrator-level onFailure hook
|
|
538
|
+
const failCtx = await this.lifecycleHooks.runOnFailure({
|
|
539
|
+
agentId: targetAgent,
|
|
540
|
+
instruction: taskPayload.instruction,
|
|
541
|
+
error: error instanceof Error ? error : String(error),
|
|
542
|
+
durationMs: Date.now() - delegationStartMs,
|
|
543
|
+
metadata: {},
|
|
544
|
+
suppress: false,
|
|
545
|
+
});
|
|
546
|
+
if (failCtx.suppress) {
|
|
547
|
+
return { success: true, data: { status: 'suppressed', agentTrace: [context.agentId, targetAgent] } };
|
|
548
|
+
}
|
|
1437
549
|
return {
|
|
1438
550
|
success: false,
|
|
1439
551
|
error: {
|
|
@@ -1707,7 +819,7 @@ class SwarmOrchestrator {
|
|
|
1707
819
|
selectedBackend = new consistency_1.ConsistentBackend(selectedBackend, options.consistency);
|
|
1708
820
|
log.info('Named blackboard wrapped with ConsistentBackend', { name, consistency: options.consistency });
|
|
1709
821
|
}
|
|
1710
|
-
board = new SharedBlackboard(selectedBackend);
|
|
822
|
+
board = new shared_blackboard_1.SharedBlackboard(selectedBackend);
|
|
1711
823
|
// Register the orchestrator agent on this board
|
|
1712
824
|
board.registerAgent('orchestrator', 'system-orchestrator-token', options?.allowedNamespaces ?? ['*']);
|
|
1713
825
|
this.namedBlackboards.set(name, board);
|
|
@@ -1816,10 +928,85 @@ exports.SwarmOrchestrator = SwarmOrchestrator;
|
|
|
1816
928
|
// ============================================================================
|
|
1817
929
|
// Default export for OpenClaw skill loader (backward compatible)
|
|
1818
930
|
exports.default = SwarmOrchestrator;
|
|
931
|
+
// Named exports for direct usage (re-exported from extracted modules)
|
|
932
|
+
var shared_blackboard_2 = require("./lib/shared-blackboard");
|
|
933
|
+
Object.defineProperty(exports, "SharedBlackboard", { enumerable: true, get: function () { return shared_blackboard_2.SharedBlackboard; } });
|
|
934
|
+
var auth_guardian_2 = require("./lib/auth-guardian");
|
|
935
|
+
Object.defineProperty(exports, "AuthGuardian", { enumerable: true, get: function () { return auth_guardian_2.AuthGuardian; } });
|
|
936
|
+
var task_decomposer_2 = require("./lib/task-decomposer");
|
|
937
|
+
Object.defineProperty(exports, "TaskDecomposer", { enumerable: true, get: function () { return task_decomposer_2.TaskDecomposer; } });
|
|
938
|
+
var explainability_2 = require("./lib/explainability");
|
|
939
|
+
Object.defineProperty(exports, "ExplainabilityTracer", { enumerable: true, get: function () { return explainability_2.ExplainabilityTracer; } });
|
|
940
|
+
var event_bus_2 = require("./lib/event-bus");
|
|
941
|
+
Object.defineProperty(exports, "OrchestratorEventBus", { enumerable: true, get: function () { return event_bus_2.OrchestratorEventBus; } });
|
|
942
|
+
var agent_conversation_2 = require("./lib/agent-conversation");
|
|
943
|
+
Object.defineProperty(exports, "AgentConversationLog", { enumerable: true, get: function () { return agent_conversation_2.AgentConversationLog; } });
|
|
944
|
+
var otel_bridge_2 = require("./lib/otel-bridge");
|
|
945
|
+
Object.defineProperty(exports, "OTelBridge", { enumerable: true, get: function () { return otel_bridge_2.OTelBridge; } });
|
|
946
|
+
Object.defineProperty(exports, "SpanStatus", { enumerable: true, get: function () { return otel_bridge_2.SpanStatus; } });
|
|
947
|
+
var metrics_2 = require("./lib/metrics");
|
|
948
|
+
Object.defineProperty(exports, "MetricsRegistry", { enumerable: true, get: function () { return metrics_2.MetricsRegistry; } });
|
|
949
|
+
Object.defineProperty(exports, "Counter", { enumerable: true, get: function () { return metrics_2.Counter; } });
|
|
950
|
+
Object.defineProperty(exports, "Gauge", { enumerable: true, get: function () { return metrics_2.Gauge; } });
|
|
951
|
+
Object.defineProperty(exports, "Histogram", { enumerable: true, get: function () { return metrics_2.Histogram; } });
|
|
952
|
+
Object.defineProperty(exports, "createOrchestratorMetrics", { enumerable: true, get: function () { return metrics_2.createOrchestratorMetrics; } });
|
|
953
|
+
var cost_heatmap_2 = require("./lib/cost-heatmap");
|
|
954
|
+
Object.defineProperty(exports, "CostHeatmap", { enumerable: true, get: function () { return cost_heatmap_2.CostHeatmap; } });
|
|
955
|
+
var timeline_scrubber_1 = require("./lib/timeline-scrubber");
|
|
956
|
+
Object.defineProperty(exports, "TimelineScrubber", { enumerable: true, get: function () { return timeline_scrubber_1.TimelineScrubber; } });
|
|
957
|
+
var anomaly_detector_2 = require("./lib/anomaly-detector");
|
|
958
|
+
Object.defineProperty(exports, "AnomalyDetector", { enumerable: true, get: function () { return anomaly_detector_2.AnomalyDetector; } });
|
|
959
|
+
var cost_governor_1 = require("./lib/cost-governor");
|
|
960
|
+
Object.defineProperty(exports, "CostGovernor", { enumerable: true, get: function () { return cost_governor_1.CostGovernor; } });
|
|
961
|
+
Object.defineProperty(exports, "LookupCostModel", { enumerable: true, get: function () { return cost_governor_1.LookupCostModel; } });
|
|
962
|
+
var dry_run_1 = require("./lib/dry-run");
|
|
963
|
+
Object.defineProperty(exports, "DryRunSimulator", { enumerable: true, get: function () { return dry_run_1.DryRunSimulator; } });
|
|
964
|
+
var config_watcher_1 = require("./lib/config-watcher");
|
|
965
|
+
Object.defineProperty(exports, "ConfigWatcher", { enumerable: true, get: function () { return config_watcher_1.ConfigWatcher; } });
|
|
966
|
+
var lifecycle_hooks_2 = require("./lib/lifecycle-hooks");
|
|
967
|
+
Object.defineProperty(exports, "OrchestratorLifecycleHooks", { enumerable: true, get: function () { return lifecycle_hooks_2.OrchestratorLifecycleHooks; } });
|
|
968
|
+
var agent_memory_1 = require("./lib/agent-memory");
|
|
969
|
+
Object.defineProperty(exports, "AgentMemory", { enumerable: true, get: function () { return agent_memory_1.AgentMemory; } });
|
|
970
|
+
Object.defineProperty(exports, "EpisodicMemory", { enumerable: true, get: function () { return agent_memory_1.EpisodicMemory; } });
|
|
971
|
+
Object.defineProperty(exports, "ProceduralMemory", { enumerable: true, get: function () { return agent_memory_1.ProceduralMemory; } });
|
|
972
|
+
Object.defineProperty(exports, "SharedLongTermMemory", { enumerable: true, get: function () { return agent_memory_1.SharedLongTermMemory; } });
|
|
973
|
+
var learning_loop_1 = require("./lib/learning-loop");
|
|
974
|
+
Object.defineProperty(exports, "LearningLoop", { enumerable: true, get: function () { return learning_loop_1.LearningLoop; } });
|
|
975
|
+
var speculative_executor_1 = require("./lib/speculative-executor");
|
|
976
|
+
Object.defineProperty(exports, "SpeculativeExecutor", { enumerable: true, get: function () { return speculative_executor_1.SpeculativeExecutor; } });
|
|
977
|
+
var agent_debate_1 = require("./lib/agent-debate");
|
|
978
|
+
Object.defineProperty(exports, "AgentDebate", { enumerable: true, get: function () { return agent_debate_1.AgentDebate; } });
|
|
979
|
+
var approval_inbox_1 = require("./lib/approval-inbox");
|
|
980
|
+
Object.defineProperty(exports, "ApprovalInbox", { enumerable: true, get: function () { return approval_inbox_1.ApprovalInbox; } });
|
|
981
|
+
var adapter_test_harness_1 = require("./lib/adapter-test-harness");
|
|
982
|
+
Object.defineProperty(exports, "createAdapterTestSuite", { enumerable: true, get: function () { return adapter_test_harness_1.createAdapterTestSuite; } });
|
|
983
|
+
var swarm_transport_1 = require("./lib/swarm-transport");
|
|
984
|
+
Object.defineProperty(exports, "SwarmTransportServer", { enumerable: true, get: function () { return swarm_transport_1.SwarmTransportServer; } });
|
|
985
|
+
Object.defineProperty(exports, "SwarmTransportClient", { enumerable: true, get: function () { return swarm_transport_1.SwarmTransportClient; } });
|
|
986
|
+
var job_queue_1 = require("./lib/job-queue");
|
|
987
|
+
Object.defineProperty(exports, "JobQueue", { enumerable: true, get: function () { return job_queue_1.JobQueue; } });
|
|
988
|
+
Object.defineProperty(exports, "FileJobStore", { enumerable: true, get: function () { return job_queue_1.FileJobStore; } });
|
|
989
|
+
var playground_1 = require("./lib/playground");
|
|
990
|
+
Object.defineProperty(exports, "startPlayground", { enumerable: true, get: function () { return playground_1.startPlayground; } });
|
|
991
|
+
Object.defineProperty(exports, "MockAgentRegistry", { enumerable: true, get: function () { return playground_1.MockAgentRegistry; } });
|
|
992
|
+
var goal_dsl_1 = require("./lib/goal-dsl");
|
|
993
|
+
Object.defineProperty(exports, "parseGoal", { enumerable: true, get: function () { return goal_dsl_1.parseGoal; } });
|
|
994
|
+
Object.defineProperty(exports, "goalFromObject", { enumerable: true, get: function () { return goal_dsl_1.goalFromObject; } });
|
|
995
|
+
Object.defineProperty(exports, "validateGoal", { enumerable: true, get: function () { return goal_dsl_1.validateGoal; } });
|
|
996
|
+
Object.defineProperty(exports, "compileGoal", { enumerable: true, get: function () { return goal_dsl_1.compileGoal; } });
|
|
997
|
+
var agent_vcr_1 = require("./lib/agent-vcr");
|
|
998
|
+
Object.defineProperty(exports, "AgentVCR", { enumerable: true, get: function () { return agent_vcr_1.AgentVCR; } });
|
|
999
|
+
var coverage_reporter_1 = require("./lib/coverage-reporter");
|
|
1000
|
+
Object.defineProperty(exports, "CoverageReporter", { enumerable: true, get: function () { return coverage_reporter_1.CoverageReporter; } });
|
|
1001
|
+
var comparison_runner_1 = require("./lib/comparison-runner");
|
|
1002
|
+
Object.defineProperty(exports, "ComparisonRunner", { enumerable: true, get: function () { return comparison_runner_1.ComparisonRunner; } });
|
|
1003
|
+
var auth_validator_1 = require("./lib/auth-validator");
|
|
1004
|
+
Object.defineProperty(exports, "NoOpAuthValidator", { enumerable: true, get: function () { return auth_validator_1.NoOpAuthValidator; } });
|
|
1819
1005
|
// Quality gate & validation exports
|
|
1820
1006
|
var blackboard_validator_2 = require("./lib/blackboard-validator");
|
|
1821
1007
|
Object.defineProperty(exports, "BlackboardValidator", { enumerable: true, get: function () { return blackboard_validator_2.BlackboardValidator; } });
|
|
1822
1008
|
Object.defineProperty(exports, "QualityGateAgent", { enumerable: true, get: function () { return blackboard_validator_2.QualityGateAgent; } });
|
|
1009
|
+
Object.defineProperty(exports, "validateJsonSchema", { enumerable: true, get: function () { return blackboard_validator_2.validateJsonSchema; } });
|
|
1823
1010
|
// Adapter system re-exports for convenience
|
|
1824
1011
|
var adapter_registry_2 = require("./adapters/adapter-registry");
|
|
1825
1012
|
Object.defineProperty(exports, "AdapterRegistry", { enumerable: true, get: function () { return adapter_registry_2.AdapterRegistry; } });
|
|
@@ -1837,6 +1024,10 @@ var mcp_adapter_1 = require("./adapters/mcp-adapter");
|
|
|
1837
1024
|
Object.defineProperty(exports, "MCPAdapter", { enumerable: true, get: function () { return mcp_adapter_1.MCPAdapter; } });
|
|
1838
1025
|
var custom_adapter_1 = require("./adapters/custom-adapter");
|
|
1839
1026
|
Object.defineProperty(exports, "CustomAdapter", { enumerable: true, get: function () { return custom_adapter_1.CustomAdapter; } });
|
|
1027
|
+
var mcp_tool_consumer_1 = require("./lib/mcp-tool-consumer");
|
|
1028
|
+
Object.defineProperty(exports, "MCPToolConsumer", { enumerable: true, get: function () { return mcp_tool_consumer_1.MCPToolConsumer; } });
|
|
1029
|
+
Object.defineProperty(exports, "StdioClientTransport", { enumerable: true, get: function () { return mcp_tool_consumer_1.StdioClientTransport; } });
|
|
1030
|
+
Object.defineProperty(exports, "HttpClientTransport", { enumerable: true, get: function () { return mcp_tool_consumer_1.HttpClientTransport; } });
|
|
1840
1031
|
// Phase 5 Part 2: Pluggable Backend API
|
|
1841
1032
|
var blackboard_backend_2 = require("./lib/blackboard-backend");
|
|
1842
1033
|
Object.defineProperty(exports, "FileBackend", { enumerable: true, get: function () { return blackboard_backend_2.FileBackend; } });
|
|
@@ -1884,6 +1075,7 @@ Object.defineProperty(exports, "AdapterNotFoundError", { enumerable: true, get:
|
|
|
1884
1075
|
Object.defineProperty(exports, "AdapterNotInitializedError", { enumerable: true, get: function () { return errors_2.AdapterNotInitializedError; } });
|
|
1885
1076
|
Object.defineProperty(exports, "ParallelLimitError", { enumerable: true, get: function () { return errors_2.ParallelLimitError; } });
|
|
1886
1077
|
Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_2.TimeoutError; } });
|
|
1078
|
+
Object.defineProperty(exports, "mapErrorToSkillResult", { enumerable: true, get: function () { return errors_2.mapErrorToSkillResult; } });
|
|
1887
1079
|
// ============================================================================
|
|
1888
1080
|
// Phase 4: Behavioral Control Plane
|
|
1889
1081
|
// ============================================================================
|
|
@@ -1950,6 +1142,14 @@ Object.defineProperty(exports, "createLLMPlanner", { enumerable: true, get: func
|
|
|
1950
1142
|
Object.defineProperty(exports, "validateDAG", { enumerable: true, get: function () { return goal_decomposer_1.validateDAG; } });
|
|
1951
1143
|
Object.defineProperty(exports, "topologicalLayers", { enumerable: true, get: function () { return goal_decomposer_1.topologicalLayers; } });
|
|
1952
1144
|
Object.defineProperty(exports, "parsePlanJSON", { enumerable: true, get: function () { return goal_decomposer_1.parsePlanJSON; } });
|
|
1145
|
+
// Live Agent Topology — Real-time agent graph + dashboard (Phase 11)
|
|
1146
|
+
var topology_1 = require("./lib/topology");
|
|
1147
|
+
Object.defineProperty(exports, "TopologyTracker", { enumerable: true, get: function () { return topology_1.TopologyTracker; } });
|
|
1148
|
+
var dashboard_server_1 = require("./lib/dashboard-server");
|
|
1149
|
+
Object.defineProperty(exports, "DashboardServer", { enumerable: true, get: function () { return dashboard_server_1.DashboardServer; } });
|
|
1150
|
+
// QuadTree — Barnes-Hut spatial indexing (Tier 1 scalability)
|
|
1151
|
+
var quadtree_1 = require("./lib/quadtree");
|
|
1152
|
+
Object.defineProperty(exports, "QuadTree", { enumerable: true, get: function () { return quadtree_1.QuadTree; } });
|
|
1953
1153
|
// MCP Blackboard Tool Bindings
|
|
1954
1154
|
var mcp_blackboard_tools_1 = require("./lib/mcp-blackboard-tools");
|
|
1955
1155
|
Object.defineProperty(exports, "BlackboardMCPTools", { enumerable: true, get: function () { return mcp_blackboard_tools_1.BlackboardMCPTools; } });
|
|
@@ -1994,7 +1194,7 @@ function createSwarmOrchestrator(config) {
|
|
|
1994
1194
|
}
|
|
1995
1195
|
if (config) {
|
|
1996
1196
|
const { adapters: adapterList, adapterRegistry, trustLevels, resourceProfiles, validationConfig, qualityThreshold, aiReviewCallback, ...rest } = config;
|
|
1997
|
-
Object.assign(CONFIG, rest);
|
|
1197
|
+
Object.assign(orchestrator_types_1.CONFIG, rest);
|
|
1998
1198
|
const registry = adapterRegistry ?? new adapter_registry_1.AdapterRegistry();
|
|
1999
1199
|
const orchestrator = new SwarmOrchestrator(undefined, registry, {
|
|
2000
1200
|
trustLevels,
|
|
@@ -2013,8 +1213,8 @@ function createSwarmOrchestrator(config) {
|
|
|
2013
1213
|
}
|
|
2014
1214
|
function getConfig(key) {
|
|
2015
1215
|
if (key !== undefined)
|
|
2016
|
-
return CONFIG[key];
|
|
2017
|
-
return { ...CONFIG };
|
|
1216
|
+
return orchestrator_types_1.CONFIG[key];
|
|
1217
|
+
return { ...orchestrator_types_1.CONFIG };
|
|
2018
1218
|
}
|
|
2019
1219
|
/**
|
|
2020
1220
|
* Update a CONFIG key at runtime. Changes take effect immediately for all
|
|
@@ -2027,7 +1227,7 @@ function getConfig(key) {
|
|
|
2027
1227
|
* ```
|
|
2028
1228
|
*/
|
|
2029
1229
|
function setConfig(key, value) {
|
|
2030
|
-
CONFIG[key] = value;
|
|
1230
|
+
orchestrator_types_1.CONFIG[key] = value;
|
|
2031
1231
|
}
|
|
2032
1232
|
// Phase 6: SSE transport + extended/control tools
|
|
2033
1233
|
var mcp_transport_sse_1 = require("./lib/mcp-transport-sse");
|
|
@@ -2059,4 +1259,25 @@ Object.defineProperty(exports, "MiniMaxAdapter", { enumerable: true, get: functi
|
|
|
2059
1259
|
// NemoClaw adapter (NVIDIA NemoClaw — sandboxed agent execution via OpenShell)
|
|
2060
1260
|
var nemoclaw_adapter_1 = require("./adapters/nemoclaw-adapter");
|
|
2061
1261
|
Object.defineProperty(exports, "NemoClawAdapter", { enumerable: true, get: function () { return nemoclaw_adapter_1.NemoClawAdapter; } });
|
|
1262
|
+
// Copilot adapter (GitHub Copilot — code generation, review, analysis)
|
|
1263
|
+
var copilot_adapter_1 = require("./adapters/copilot-adapter");
|
|
1264
|
+
Object.defineProperty(exports, "CopilotAdapter", { enumerable: true, get: function () { return copilot_adapter_1.CopilotAdapter; } });
|
|
1265
|
+
// LangGraph adapter
|
|
1266
|
+
var langgraph_adapter_1 = require("./adapters/langgraph-adapter");
|
|
1267
|
+
Object.defineProperty(exports, "LangGraphAdapter", { enumerable: true, get: function () { return langgraph_adapter_1.LangGraphAdapter; } });
|
|
1268
|
+
// Anthropic Computer Use adapter
|
|
1269
|
+
var anthropic_computer_use_adapter_1 = require("./adapters/anthropic-computer-use-adapter");
|
|
1270
|
+
Object.defineProperty(exports, "AnthropicComputerUseAdapter", { enumerable: true, get: function () { return anthropic_computer_use_adapter_1.AnthropicComputerUseAdapter; } });
|
|
1271
|
+
// OpenAI Agents SDK adapter
|
|
1272
|
+
var openai_agents_adapter_1 = require("./adapters/openai-agents-adapter");
|
|
1273
|
+
Object.defineProperty(exports, "OpenAIAgentsAdapter", { enumerable: true, get: function () { return openai_agents_adapter_1.OpenAIAgentsAdapter; } });
|
|
1274
|
+
// Vertex AI adapter
|
|
1275
|
+
var vertex_ai_adapter_1 = require("./adapters/vertex-ai-adapter");
|
|
1276
|
+
Object.defineProperty(exports, "VertexAIAdapter", { enumerable: true, get: function () { return vertex_ai_adapter_1.VertexAIAdapter; } });
|
|
1277
|
+
// Pydantic AI adapter
|
|
1278
|
+
var pydantic_ai_adapter_1 = require("./adapters/pydantic-ai-adapter");
|
|
1279
|
+
Object.defineProperty(exports, "PydanticAIAdapter", { enumerable: true, get: function () { return pydantic_ai_adapter_1.PydanticAIAdapter; } });
|
|
1280
|
+
// Browser Agent adapter
|
|
1281
|
+
var browser_agent_adapter_1 = require("./adapters/browser-agent-adapter");
|
|
1282
|
+
Object.defineProperty(exports, "BrowserAgentAdapter", { enumerable: true, get: function () { return browser_agent_adapter_1.BrowserAgentAdapter; } });
|
|
2062
1283
|
//# sourceMappingURL=index.js.map
|