0xray 2.1.4 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.md +31 -0
- package/dist/cli/commands/govern.d.ts +29 -0
- package/dist/cli/commands/govern.js +152 -0
- package/dist/cli/index.js +29 -216
- package/dist/core/framework-logger.js +30 -1
- package/dist/delegation/index.d.ts +1 -2
- package/dist/delegation/index.js +1 -2
- package/dist/enforcement/enforcer-tools.js +1 -1
- package/dist/execution/proposal-applier.d.ts +1 -9
- package/dist/execution/proposal-applier.js +18 -173
- package/dist/governance/codex-policy.service.d.ts +7 -25
- package/dist/governance/codex-policy.service.js +8 -26
- package/dist/governance/governance-core.d.ts +22 -28
- package/dist/governance/governance-core.js +31 -101
- package/dist/governance/governance-service.d.ts +1 -0
- package/dist/governance/governance-service.js +67 -4
- package/dist/governance/governance-types.d.ts +5 -2
- package/dist/inference/index.d.ts +3 -4
- package/dist/inference/inference-accumulator.js +1 -1
- package/dist/inference/inference-cycle.d.ts +12 -18
- package/dist/inference/inference-cycle.js +120 -335
- package/dist/integrations/base/index.d.ts +0 -1
- package/dist/integrations/base/index.js +0 -2
- package/dist/integrations/grok/hooks/pre-tool-use.js +4 -18
- package/dist/integrations/hermes-agent/bridge.mjs +9 -0
- package/dist/mcps/config/server-config-registry.js +1 -1
- package/dist/mcps/governance.server.js +3 -4
- package/dist/mcps/knowledge-skills/api-design.server.js +10 -0
- package/dist/mcps/knowledge-skills/architecture-patterns.server.js +10 -0
- package/dist/mcps/knowledge-skills/bug-triage-specialist.server.js +10 -0
- package/dist/mcps/knowledge-skills/code-analyzer.server.js +10 -0
- package/dist/mcps/knowledge-skills/code-review.server.js +10 -0
- package/dist/mcps/knowledge-skills/content-creator.server.js +10 -0
- package/dist/mcps/knowledge-skills/database-design.server.js +10 -0
- package/dist/mcps/knowledge-skills/devops-deployment.server.js +10 -0
- package/dist/mcps/knowledge-skills/git-workflow.server.js +10 -0
- package/dist/mcps/knowledge-skills/growth-strategist.server.js +10 -0
- package/dist/mcps/knowledge-skills/log-monitor.server.js +10 -0
- package/dist/mcps/knowledge-skills/mobile-development.server.js +10 -0
- package/dist/mcps/knowledge-skills/multimodal-looker.server.js +10 -0
- package/dist/mcps/knowledge-skills/performance-optimization.server.js +10 -0
- package/dist/mcps/knowledge-skills/project-analysis.server.js +10 -0
- package/dist/mcps/knowledge-skills/refactoring-strategies.server.js +10 -0
- package/dist/mcps/knowledge-skills/security-audit.server.js +10 -0
- package/dist/mcps/knowledge-skills/seo-consultant.server.js +10 -0
- package/dist/mcps/knowledge-skills/skill-invocation.server.d.ts +4 -0
- package/dist/mcps/knowledge-skills/skill-invocation.server.js +33 -14
- package/dist/mcps/knowledge-skills/strategist.server.js +10 -0
- package/dist/mcps/knowledge-skills/tech-writer.server.js +10 -0
- package/dist/mcps/knowledge-skills/testing-strategy.server.js +10 -0
- package/dist/mcps/knowledge-skills/ui-ux-design.server.js +10 -0
- package/dist/metrics/agent-metrics.d.ts +2 -1
- package/dist/nucleus/govern-http.d.ts +59 -0
- package/dist/nucleus/govern-http.js +172 -0
- package/dist/nucleus/index.d.ts +14 -0
- package/dist/nucleus/index.js +9 -0
- package/dist/nucleus/kernel.d.ts +51 -0
- package/dist/nucleus/kernel.js +62 -0
- package/dist/nucleus/plugin-registry.d.ts +104 -0
- package/dist/nucleus/plugin-registry.js +147 -0
- package/dist/orchestrator/orchestrator.d.ts +0 -1
- package/dist/orchestrator/orchestrator.js +0 -21
- package/dist/postprocessor/PostProcessor.d.ts +13 -1
- package/dist/postprocessor/PostProcessor.js +41 -1
- package/dist/postprocessor/autofix/AutoFixEngine.d.ts +0 -8
- package/dist/postprocessor/autofix/AutoFixEngine.js +0 -54
- package/dist/postprocessor/metamorphosis/MetamorphosisEngine.d.ts +25 -0
- package/dist/postprocessor/metamorphosis/MetamorphosisEngine.js +10 -0
- package/dist/postprocessor/metamorphosis/SelfProposalEngine.d.ts +68 -0
- package/dist/postprocessor/metamorphosis/SelfProposalEngine.js +371 -0
- package/dist/skills/orchestrator/SKILL.md +1 -1
- package/dist/state/index.d.ts +0 -2
- package/package.json +4 -3
- package/scripts/node/universal-version-manager.js +2 -2
- package/src/mcps/config/__tests__/server-config-registry.test.ts +6 -15
- package/src/mcps/config/server-config-registry.ts +1 -1
- package/src/mcps/governance.server.ts +3 -5
- package/src/mcps/knowledge-skills/api-design.server.ts +9 -0
- package/src/mcps/knowledge-skills/architecture-patterns.server.ts +9 -0
- package/src/mcps/knowledge-skills/bug-triage-specialist.server.ts +9 -0
- package/src/mcps/knowledge-skills/code-analyzer.server.ts +9 -0
- package/src/mcps/knowledge-skills/code-review.server.ts +9 -0
- package/src/mcps/knowledge-skills/content-creator.server.ts +9 -0
- package/src/mcps/knowledge-skills/database-design.server.ts +9 -0
- package/src/mcps/knowledge-skills/devops-deployment.server.ts +9 -0
- package/src/mcps/knowledge-skills/git-workflow.server.ts +9 -0
- package/src/mcps/knowledge-skills/growth-strategist.server.ts +9 -0
- package/src/mcps/knowledge-skills/log-monitor.server.ts +9 -0
- package/src/mcps/knowledge-skills/mobile-development.server.ts +9 -0
- package/src/mcps/knowledge-skills/multimodal-looker.server.ts +9 -0
- package/src/mcps/knowledge-skills/performance-optimization.server.ts +9 -0
- package/src/mcps/knowledge-skills/project-analysis.server.ts +9 -0
- package/src/mcps/knowledge-skills/refactoring-strategies.server.ts +9 -0
- package/src/mcps/knowledge-skills/security-audit.server.ts +9 -0
- package/src/mcps/knowledge-skills/seo-consultant.server.ts +9 -0
- package/src/mcps/knowledge-skills/session-management.server.ts +1 -0
- package/src/mcps/knowledge-skills/skill-invocation.server.ts +44 -29
- package/src/mcps/knowledge-skills/strategist.server.ts +9 -0
- package/src/mcps/knowledge-skills/tech-writer.server.ts +9 -0
- package/src/mcps/knowledge-skills/testing-strategy.server.ts +9 -0
- package/src/mcps/knowledge-skills/ui-ux-design.server.ts +9 -0
- package/src/skills/orchestrator/SKILL.md +1 -1
- package/.opencode/agents/testing-best-practices.yml +0 -12
- package/dist/analytics/consent-manager.d.ts +0 -93
- package/dist/analytics/consent-manager.js +0 -258
- package/dist/analytics/simple-pattern-analyzer.d.ts +0 -80
- package/dist/analytics/simple-pattern-analyzer.js +0 -297
- package/dist/cli/commands/analytics-disable.d.ts +0 -9
- package/dist/cli/commands/analytics-disable.js +0 -72
- package/dist/cli/commands/analytics-enable-action.d.ts +0 -8
- package/dist/cli/commands/analytics-enable-action.js +0 -82
- package/dist/cli/commands/analytics-preview.d.ts +0 -9
- package/dist/cli/commands/analytics-preview.js +0 -106
- package/dist/cli/commands/analytics-status.d.ts +0 -9
- package/dist/cli/commands/analytics-status.js +0 -68
- package/dist/cli/server.d.ts +0 -1
- package/dist/cli/server.js +0 -147
- package/dist/core/errors.d.ts +0 -3
- package/dist/core/errors.js +0 -10
- package/dist/core/logging-config.d.ts +0 -11
- package/dist/core/logging-config.js +0 -43
- package/dist/core/trace-context.d.ts +0 -12
- package/dist/core/trace-context.js +0 -33
- package/dist/delegation/voting-coordinator.d.ts +0 -49
- package/dist/delegation/voting-coordinator.js +0 -375
- package/dist/enforcement/test-auto-healing.d.ts +0 -83
- package/dist/enforcement/test-auto-healing.js +0 -325
- package/dist/integrations/base/ExampleIntegration.d.ts +0 -47
- package/dist/integrations/base/ExampleIntegration.js +0 -181
- package/dist/mcps/knowledge-skills/testing-best-practices.server.d.ts +0 -34
- package/dist/mcps/knowledge-skills/testing-best-practices.server.js +0 -831
- package/dist/mcps/orchestrator.server.d.ts +0 -10
- package/dist/mcps/orchestrator.server.js +0 -19
- package/dist/orchestrator/self-direction-activation.d.ts +0 -109
- package/dist/orchestrator/self-direction-activation.js +0 -245
- package/dist/orchestrator/universal-librarian-consultation.d.ts +0 -98
- package/dist/orchestrator/universal-librarian-consultation.js +0 -216
- package/dist/orchestrator/universal-registry-bridge.d.ts +0 -85
- package/dist/orchestrator/universal-registry-bridge.js +0 -247
- package/dist/performance/performance-budget-enforcer.d.ts +0 -158
- package/dist/performance/performance-budget-enforcer.js +0 -434
- package/dist/performance/performance-regression-tester.d.ts +0 -108
- package/dist/performance/performance-regression-tester.js +0 -342
- package/dist/skills/testing-best-practices/SKILL.md +0 -37
- package/dist/state/context-providers.d.ts +0 -4
- package/dist/state/context-providers.js +0 -1
- package/dist/state/state-types.d.ts +0 -5
- package/dist/state/state-types.js +0 -1
- package/dist/utils/batch-operations.d.ts +0 -25
- package/dist/utils/batch-operations.js +0 -292
- package/dist/utils/test-template-generator.d.ts +0 -25
- package/dist/utils/test-template-generator.js +0 -178
- package/dist/utils/token-manager.d.ts +0 -48
- package/dist/utils/token-manager.js +0 -163
- package/src/mcps/knowledge-skills/testing-best-practices.server.test.ts +0 -136
- package/src/mcps/knowledge-skills/testing-best-practices.server.ts +0 -1183
- package/src/mcps/orchestrator.server.ts +0 -34
- package/src/opencode/agents/testing-best-practices.yml +0 -12
- package/src/skills/testing-best-practices/SKILL.md +0 -37
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelfProposalEngine — Phase 2.2
|
|
3
|
+
*
|
|
4
|
+
* Concrete MetamorphosisEngine that reads activity.log, parses structured
|
|
5
|
+
* log entries, detects semantic patterns, generates governance proposals
|
|
6
|
+
* (type: 'metamorphosis'), and submits them through the full 3-agent +
|
|
7
|
+
* Dynamo governance pipeline.
|
|
8
|
+
*
|
|
9
|
+
* Safety constraints:
|
|
10
|
+
* - Max proposals per hour (default 1, configurable)
|
|
11
|
+
* - Circuit breaker: after N consecutive failures, halt for cooldown (default 3/24h)
|
|
12
|
+
* - Only proposes changes to whitelisted targets
|
|
13
|
+
* - All proposals go through full governance with metamorphosisThreshold (default 0.7)
|
|
14
|
+
* - Detection thresholds are configurable (not hard-coded)
|
|
15
|
+
*
|
|
16
|
+
* Coupling note: currently triggered by PostProcessor.onPhase(). A future
|
|
17
|
+
* refactor should extract log-reading into a dedicated LogReader that can
|
|
18
|
+
* run on its own schedule or fs.watch, decoupling from PostProcessor lifecycle.
|
|
19
|
+
*/
|
|
20
|
+
import { promises as fs } from 'fs';
|
|
21
|
+
import { join } from 'path';
|
|
22
|
+
import { frameworkLogger } from '../../core/framework-logger.js';
|
|
23
|
+
import { handleGovernRequest } from '../../nucleus/govern-http.js';
|
|
24
|
+
const DEFAULT_CONFIG = {
|
|
25
|
+
maxProposalsPerHour: 1,
|
|
26
|
+
circuitBreakerThreshold: 3,
|
|
27
|
+
circuitBreakerCooldownHours: 24,
|
|
28
|
+
activityLogPath: 'logs/framework/activity.log',
|
|
29
|
+
whitelistedTargets: ['config/', 'features.json', 'src/processors/'],
|
|
30
|
+
maxLogLines: 100,
|
|
31
|
+
errorThreshold: 5,
|
|
32
|
+
warningThreshold: 10,
|
|
33
|
+
governanceRejectionThreshold: 3,
|
|
34
|
+
proposalConfidence: 0.7,
|
|
35
|
+
metamorphosisThreshold: 0.7,
|
|
36
|
+
projectRoot: process.cwd(),
|
|
37
|
+
};
|
|
38
|
+
const LOG_LINE_PATTERN = /^(\d{4}-\d{2}-\d{2}T[^ ]+)\s+(?:\[([^\]]*)\]\s+)?(?:\[([^\]]*)\]\s+)?\[([^\]]*)\]\s+(\S+)\s+-\s+(\w+)(?:\s+\|(.+))?$/;
|
|
39
|
+
function parseLogEntry(line) {
|
|
40
|
+
const match = line.match(LOG_LINE_PATTERN);
|
|
41
|
+
if (!match) {
|
|
42
|
+
// Fallback: try JSON parse for entangled log entries
|
|
43
|
+
try {
|
|
44
|
+
const json = JSON.parse(line);
|
|
45
|
+
if (json && typeof json === 'object' && json.action) {
|
|
46
|
+
return {
|
|
47
|
+
timestamp: json.timestamp ? new Date(json.timestamp).toISOString() : new Date().toISOString(),
|
|
48
|
+
component: json.component || 'unknown',
|
|
49
|
+
action: json.action,
|
|
50
|
+
status: (json.status || json.level || 'info').toLowerCase(),
|
|
51
|
+
details: json.details || json,
|
|
52
|
+
raw: line,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const [, timestamp, , , component, action, status, detailsStr] = match;
|
|
60
|
+
let details;
|
|
61
|
+
if (detailsStr) {
|
|
62
|
+
try {
|
|
63
|
+
details = JSON.parse(detailsStr.trim());
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
details = { rawDetails: detailsStr.trim() };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const entry = {
|
|
70
|
+
timestamp: timestamp ?? new Date().toISOString(),
|
|
71
|
+
component: component || 'unknown',
|
|
72
|
+
action: action ?? 'unknown',
|
|
73
|
+
status: (status ?? 'info').toLowerCase(),
|
|
74
|
+
raw: line,
|
|
75
|
+
};
|
|
76
|
+
if (details !== undefined) {
|
|
77
|
+
entry.details = details;
|
|
78
|
+
}
|
|
79
|
+
return entry;
|
|
80
|
+
}
|
|
81
|
+
export class SelfProposalEngine {
|
|
82
|
+
name = 'self-proposal';
|
|
83
|
+
config;
|
|
84
|
+
proposalHistory = [];
|
|
85
|
+
consecutiveFailures = 0;
|
|
86
|
+
circuitBreakerUntil = 0;
|
|
87
|
+
constructor(config) {
|
|
88
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
89
|
+
}
|
|
90
|
+
async onPhase(phase, context) {
|
|
91
|
+
if (phase !== 'monitoring-complete' && phase !== 'post-process-complete') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
await frameworkLogger.log('self-proposal', 'phase-hook', 'info', {
|
|
95
|
+
phase,
|
|
96
|
+
message: 'Self-proposal engine triggered by phase',
|
|
97
|
+
});
|
|
98
|
+
if (this.isCircuitBreakerActive()) {
|
|
99
|
+
await frameworkLogger.log('self-proposal', 'circuit-breaker-active', 'warning', {
|
|
100
|
+
message: `Circuit breaker active until ${new Date(this.circuitBreakerUntil).toISOString()}`,
|
|
101
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (this.isRateLimited()) {
|
|
106
|
+
await frameworkLogger.log('self-proposal', 'rate-limited', 'info', {
|
|
107
|
+
message: 'Max proposals per hour reached',
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
await this.evaluateAndPropose();
|
|
112
|
+
}
|
|
113
|
+
async onProposal(proposal) {
|
|
114
|
+
await frameworkLogger.log('self-proposal', 'external-proposal', 'info', {
|
|
115
|
+
proposalId: proposal.id,
|
|
116
|
+
type: proposal.type,
|
|
117
|
+
target: proposal.target,
|
|
118
|
+
impact: proposal.impact,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
isCircuitBreakerActive() {
|
|
122
|
+
if (this.consecutiveFailures >= this.config.circuitBreakerThreshold) {
|
|
123
|
+
if (Date.now() < this.circuitBreakerUntil) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
this.consecutiveFailures = 0;
|
|
127
|
+
this.circuitBreakerUntil = 0;
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
isRateLimited() {
|
|
132
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
133
|
+
const recentProposals = this.proposalHistory.filter(a => a.timestamp > oneHourAgo);
|
|
134
|
+
return recentProposals.length >= this.config.maxProposalsPerHour;
|
|
135
|
+
}
|
|
136
|
+
async evaluateAndPropose() {
|
|
137
|
+
let logEntries = [];
|
|
138
|
+
try {
|
|
139
|
+
const logContent = await fs.readFile(this.config.activityLogPath, 'utf-8');
|
|
140
|
+
const lines = logContent.trim().split('\n');
|
|
141
|
+
logEntries = lines.slice(-this.config.maxLogLines).filter(line => line.trim().length > 0);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
await frameworkLogger.log('self-proposal', 'log-read-error', 'warning', {
|
|
145
|
+
path: this.config.activityLogPath,
|
|
146
|
+
message: 'Activity log not found or unreadable — skipping self-proposal',
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const patterns = this.detectPatterns(logEntries);
|
|
151
|
+
if (patterns.length === 0) {
|
|
152
|
+
await frameworkLogger.log('self-proposal', 'no-patterns', 'info', {
|
|
153
|
+
message: 'No actionable patterns detected in activity log',
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
for (const pattern of patterns) {
|
|
158
|
+
await this.submitProposal(pattern);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Detect actionable patterns from parsed activity log entries.
|
|
163
|
+
* Uses structured parsing instead of raw string matching to handle
|
|
164
|
+
* the actual xray log format:
|
|
165
|
+
* ISO_TIMESTAMP [jobId] [traceId.spanId] [component] action - STATUS | {details}
|
|
166
|
+
*/
|
|
167
|
+
detectPatterns(entries) {
|
|
168
|
+
const parsed = entries.map(parseLogEntry).filter((e) => e !== null);
|
|
169
|
+
const proposals = [];
|
|
170
|
+
const errorEntries = parsed.filter(e => e.status === 'error');
|
|
171
|
+
const warningEntries = parsed.filter(e => e.status === 'warning');
|
|
172
|
+
const rejectedEntries = parsed.filter(e => e.component === 'governance' && (e.status === 'rejected' || e.details?.decision === 'reject'));
|
|
173
|
+
if (errorEntries.length >= this.config.errorThreshold) {
|
|
174
|
+
const components = [...new Set(errorEntries.map(e => e.component))];
|
|
175
|
+
proposals.push({
|
|
176
|
+
id: `meta-errors-${Date.now()}`,
|
|
177
|
+
type: 'modify',
|
|
178
|
+
target: 'config/error-handling',
|
|
179
|
+
description: `Elevated error rate: ${errorEntries.length} errors in last ${this.config.maxLogLines} entries. Components: ${components.join(', ')}. Propose adjusting error handling thresholds.`,
|
|
180
|
+
rationale: `High error count (${errorEntries.length}) across ${components.length} component(s) indicates a systemic issue. Affected components: ${components.join(', ')}. Governance review recommended for self-optimization.`,
|
|
181
|
+
impact: 'medium',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (warningEntries.length >= this.config.warningThreshold) {
|
|
185
|
+
const components = [...new Set(warningEntries.map(e => e.component))];
|
|
186
|
+
proposals.push({
|
|
187
|
+
id: `meta-warnings-${Date.now()}`,
|
|
188
|
+
type: 'modify',
|
|
189
|
+
target: 'config/monitoring',
|
|
190
|
+
description: `Elevated warning rate: ${warningEntries.length} warnings in last ${this.config.maxLogLines} entries. Components: ${components.join(', ')}. Propose adjusting monitoring sensitivity.`,
|
|
191
|
+
rationale: `Frequent warnings (${warningEntries.length}) across ${components.length} component(s) may indicate configuration drift. Affected: ${components.join(', ')}.`,
|
|
192
|
+
impact: 'low',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (rejectedEntries.length >= this.config.governanceRejectionThreshold) {
|
|
196
|
+
proposals.push({
|
|
197
|
+
id: `meta-governance-rejections-${Date.now()}`,
|
|
198
|
+
type: 'modify',
|
|
199
|
+
target: 'config/governance',
|
|
200
|
+
description: `Repeated governance rejections: ${rejectedEntries.length} in last ${this.config.maxLogLines} entries. Propose reviewing governance thresholds or submission patterns.`,
|
|
201
|
+
rationale: `${rejectedEntries.length} governance rejections suggest a pattern mismatch between proposals and governance criteria. Review recommended.`,
|
|
202
|
+
impact: 'medium',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return proposals.filter(p => this.config.whitelistedTargets.some(t => p.target.startsWith(t)));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Apply an approved self-evolution proposal by recording the change
|
|
209
|
+
* in a state file within the first matching whitelisted target directory.
|
|
210
|
+
* Returns true if the change was written and verified, false otherwise.
|
|
211
|
+
*/
|
|
212
|
+
async applySelfProposal(pattern, score, decision) {
|
|
213
|
+
const matchedTarget = this.config.whitelistedTargets.find(t => pattern.target.startsWith(t));
|
|
214
|
+
if (!matchedTarget) {
|
|
215
|
+
await frameworkLogger.log('self-proposal', 'apply-no-matching-target', 'warning', {
|
|
216
|
+
proposalId: pattern.id,
|
|
217
|
+
target: pattern.target,
|
|
218
|
+
message: 'No whitelisted target matches this proposal — skipping apply',
|
|
219
|
+
});
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
const root = this.config.projectRoot;
|
|
223
|
+
const stateDir = join(root, matchedTarget);
|
|
224
|
+
const statePath = join(stateDir, 'self-evolution-state.json');
|
|
225
|
+
let applied = [];
|
|
226
|
+
try {
|
|
227
|
+
const content = await fs.readFile(statePath, 'utf-8');
|
|
228
|
+
const parsed = JSON.parse(content);
|
|
229
|
+
if (Array.isArray(parsed.applied))
|
|
230
|
+
applied = parsed.applied;
|
|
231
|
+
}
|
|
232
|
+
catch { }
|
|
233
|
+
applied.push({
|
|
234
|
+
proposalId: pattern.id,
|
|
235
|
+
target: pattern.target,
|
|
236
|
+
type: pattern.type,
|
|
237
|
+
description: pattern.description,
|
|
238
|
+
rationale: pattern.rationale,
|
|
239
|
+
impact: pattern.impact,
|
|
240
|
+
metamorphosisScore: score,
|
|
241
|
+
decision: decision,
|
|
242
|
+
appliedAt: new Date().toISOString(),
|
|
243
|
+
});
|
|
244
|
+
try {
|
|
245
|
+
await fs.mkdir(stateDir, { recursive: true });
|
|
246
|
+
await fs.writeFile(statePath, JSON.stringify({ applied }, null, 2), 'utf-8');
|
|
247
|
+
}
|
|
248
|
+
catch (writeError) {
|
|
249
|
+
await frameworkLogger.log('self-proposal', 'apply-write-error', 'error', {
|
|
250
|
+
proposalId: pattern.id,
|
|
251
|
+
path: statePath,
|
|
252
|
+
error: writeError instanceof Error ? writeError.message : String(writeError),
|
|
253
|
+
});
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const verify = await fs.readFile(statePath, 'utf-8');
|
|
258
|
+
const parsed = JSON.parse(verify);
|
|
259
|
+
const found = Array.isArray(parsed.applied) && parsed.applied.some((a) => a.proposalId === pattern.id);
|
|
260
|
+
if (!found) {
|
|
261
|
+
await frameworkLogger.log('self-proposal', 'apply-verify-failed', 'error', {
|
|
262
|
+
proposalId: pattern.id,
|
|
263
|
+
message: 'Applied change not found in state file after write — verification failed',
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return found;
|
|
267
|
+
}
|
|
268
|
+
catch (verifyError) {
|
|
269
|
+
await frameworkLogger.log('self-proposal', 'apply-verify-error', 'error', {
|
|
270
|
+
proposalId: pattern.id,
|
|
271
|
+
error: verifyError instanceof Error ? verifyError.message : String(verifyError),
|
|
272
|
+
});
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async submitProposal(pattern) {
|
|
277
|
+
const governanceRequest = {
|
|
278
|
+
proposals: [{
|
|
279
|
+
id: pattern.id,
|
|
280
|
+
type: 'metamorphosis',
|
|
281
|
+
title: `Self-proposal: ${pattern.description.slice(0, 80)}`,
|
|
282
|
+
description: pattern.description,
|
|
283
|
+
evidence: [pattern.rationale],
|
|
284
|
+
source: 'metamorphosis',
|
|
285
|
+
confidence: this.config.proposalConfidence,
|
|
286
|
+
}],
|
|
287
|
+
context: {
|
|
288
|
+
project: 'xray-self',
|
|
289
|
+
phase: 'self-evolution',
|
|
290
|
+
source: 'self-proposal-engine',
|
|
291
|
+
},
|
|
292
|
+
options: {
|
|
293
|
+
requireExternalDynamo: true,
|
|
294
|
+
metamorphosisThreshold: this.config.metamorphosisThreshold,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
await frameworkLogger.log('self-proposal', 'submitting', 'info', {
|
|
298
|
+
proposalId: pattern.id,
|
|
299
|
+
target: pattern.target,
|
|
300
|
+
impact: pattern.impact,
|
|
301
|
+
});
|
|
302
|
+
try {
|
|
303
|
+
const response = await handleGovernRequest(governanceRequest);
|
|
304
|
+
const result = response.results[0];
|
|
305
|
+
if (!result) {
|
|
306
|
+
await frameworkLogger.log('self-proposal', 'no-result', 'warning', {
|
|
307
|
+
proposalId: pattern.id,
|
|
308
|
+
message: 'Governance returned no results',
|
|
309
|
+
});
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const decision = result.finalDecision;
|
|
313
|
+
const attempt = {
|
|
314
|
+
timestamp: Date.now(),
|
|
315
|
+
result: decision === 'approve' ? 'approved' : decision === 'reject' ? 'rejected' : 'needs_revision',
|
|
316
|
+
};
|
|
317
|
+
if (result.metamorphosisScore !== undefined) {
|
|
318
|
+
attempt.metamorphosisScore = result.metamorphosisScore;
|
|
319
|
+
}
|
|
320
|
+
this.proposalHistory.push(attempt);
|
|
321
|
+
if (decision === 'approve' && (result.metamorphosisScore ?? 0) >= this.config.metamorphosisThreshold) {
|
|
322
|
+
this.consecutiveFailures = 0;
|
|
323
|
+
await frameworkLogger.log('self-proposal', 'approved', 'success', {
|
|
324
|
+
proposalId: pattern.id,
|
|
325
|
+
metamorphosisScore: result.metamorphosisScore,
|
|
326
|
+
message: 'Self-evolution proposal approved by governance — applying change',
|
|
327
|
+
});
|
|
328
|
+
const applied = await this.applySelfProposal(pattern, result.metamorphosisScore, decision);
|
|
329
|
+
if (applied) {
|
|
330
|
+
await frameworkLogger.log('self-proposal', 'applied', 'success', {
|
|
331
|
+
proposalId: pattern.id,
|
|
332
|
+
target: pattern.target,
|
|
333
|
+
message: 'Self-evolution change applied and verified',
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
this.consecutiveFailures++;
|
|
338
|
+
if (this.consecutiveFailures >= this.config.circuitBreakerThreshold) {
|
|
339
|
+
this.circuitBreakerUntil = Date.now() + this.config.circuitBreakerCooldownHours * 60 * 60 * 1000;
|
|
340
|
+
}
|
|
341
|
+
await frameworkLogger.log('self-proposal', 'apply-failed', 'error', {
|
|
342
|
+
proposalId: pattern.id,
|
|
343
|
+
target: pattern.target,
|
|
344
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
345
|
+
message: 'Self-evolution change apply failed — circuit breaker may activate',
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
this.consecutiveFailures++;
|
|
351
|
+
if (this.consecutiveFailures >= this.config.circuitBreakerThreshold) {
|
|
352
|
+
this.circuitBreakerUntil = Date.now() + this.config.circuitBreakerCooldownHours * 60 * 60 * 1000;
|
|
353
|
+
}
|
|
354
|
+
await frameworkLogger.log('self-proposal', 'not-approved', 'warning', {
|
|
355
|
+
proposalId: pattern.id,
|
|
356
|
+
decision: decision,
|
|
357
|
+
metamorphosisScore: result.metamorphosisScore,
|
|
358
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
359
|
+
circuitBreakerUntil: this.circuitBreakerUntil || 'not active',
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
this.consecutiveFailures++;
|
|
365
|
+
await frameworkLogger.log('self-proposal', 'submission-error', 'error', {
|
|
366
|
+
proposalId: pattern.id,
|
|
367
|
+
error: error instanceof Error ? error.message : String(error),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
package/dist/state/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0xray",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Multi-agent orchestration and Codex governance for OpenCode, Hermes, Grok Build, and OpenClaw",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
"test": "vitest run",
|
|
59
59
|
"test:batch": "npm test",
|
|
60
60
|
"test:unit": "npm test -- src/__tests__/unit/config-loader.test.ts src/__tests__/unit/state-manager.test.ts src/__tests__/unit/state-manager-persistence.test.ts src/__tests__/unit/context-loader.test.ts src/__tests__/unit/pattern-analyzer.test.ts src/__tests__/unit/complexity-calibrator.test.ts",
|
|
61
|
-
"test:core-framework": "npm test -- src/__tests__/unit/
|
|
61
|
+
"test:core-framework": "npm test -- src/__tests__/unit/ast-code-parser.test.ts src/__tests__/unit/v2-deletion-protection.test.ts",
|
|
62
62
|
"test:security": "npm test -- src/__tests__/unit/security/security-hardener.test.ts src/__tests__/unit/security/security-headers.test.ts",
|
|
63
63
|
"test:performance": "npm test -- src/__tests__/unit/monitoring.test.ts src/__tests__/unit/benchmark.test.ts src/__tests__/unit/analytics.test.ts",
|
|
64
|
-
"test:session-management": "npm test -- src/__tests__/unit/session-state-manager.test.ts src/__tests__/unit/session-
|
|
64
|
+
"test:session-management": "npm test -- src/__tests__/unit/session-state-manager.test.ts src/__tests__/unit/session-migration-logic.test.ts",
|
|
65
65
|
"test:code-analysis": "npm test -- src/__tests__/unit/codebase-context-analyzer.test.ts src/__tests__/unit/dependency-graph-builder.test.ts src/__tests__/unit/rule-enforcer.test.ts src/__tests__/unit/codex-parser.test.ts src/__tests__/unit/codex-injector.test.ts",
|
|
66
66
|
"test:processors": "npm test -- src/__tests__/unit/processor-activation.test.ts src/__tests__/unit/typescript-compilation-processor.test.ts",
|
|
67
67
|
"test:miscellaneous": "npm test -- src/__tests__/unit/blocked-test.test.ts",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"test:e2e": "node scripts/node/validate-mcp-connectivity.js && node scripts/node/validate-external-processes.js",
|
|
88
88
|
"validate": "echo \"[xray] Use 'npm test' or 'npm run test:comprehensive'.\"",
|
|
89
89
|
"validate:quick": "npm run validate",
|
|
90
|
+
"verify:consumer": "bash scripts/verify-consumer.sh",
|
|
90
91
|
"debug:inference:force": "STRRAY_FORCE_MCP_GOVERNANCE=true XRAY_DEV_PATH=dist node dist/cli/index.js inference:run --force",
|
|
91
92
|
"validate:full": "npm run validate",
|
|
92
93
|
"test:unified": "node scripts/test/test-unified-framework.mjs",
|
|
@@ -155,9 +155,9 @@ const detectedCodex = detectCodexInfo();
|
|
|
155
155
|
const OFFICIAL_VERSIONS = {
|
|
156
156
|
// Framework version
|
|
157
157
|
framework: {
|
|
158
|
-
version: "2.
|
|
158
|
+
version: "2.2.0",
|
|
159
159
|
displayName: "xray: Self-Healing AI Governance OS",
|
|
160
|
-
lastUpdated: "2026-06-
|
|
160
|
+
lastUpdated: "2026-06-10",
|
|
161
161
|
// Counts (auto-calculated, but can be overridden)
|
|
162
162
|
...CALCULATED_COUNTS,
|
|
163
163
|
},
|
|
@@ -166,45 +166,36 @@ describe('ServerConfigRegistry', () => {
|
|
|
166
166
|
|
|
167
167
|
it('should use XRAY_DEV_PATH env var in dynamic config', () => {
|
|
168
168
|
const originalXrayEnv = process.env.XRAY_DEV_PATH;
|
|
169
|
-
const originalStrrayEnv = ;
|
|
170
169
|
process.env.XRAY_DEV_PATH = 'custom-dist';
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
|
|
173
171
|
const config = registry.createDynamicConfig('test-server');
|
|
174
172
|
expect(config.args[0]).toContain('custom-dist');
|
|
175
|
-
|
|
173
|
+
|
|
176
174
|
process.env.XRAY_DEV_PATH = originalXrayEnv;
|
|
177
|
-
= originalStrrayEnv;
|
|
178
175
|
});
|
|
179
176
|
});
|
|
180
177
|
|
|
181
178
|
describe('environment path handling', () => {
|
|
182
179
|
it('should use resolved framework path when XRAY_DEV_PATH is not set', () => {
|
|
183
180
|
const originalXrayEnv = process.env.XRAY_DEV_PATH;
|
|
184
|
-
const originalStrrayEnv = ;
|
|
185
181
|
delete process.env.XRAY_DEV_PATH;
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
|
|
188
183
|
const freshRegistry = new ServerConfigRegistry();
|
|
189
184
|
const config = freshRegistry.get('code-review');
|
|
190
185
|
expect(config?.args[0]).toContain('.server.js');
|
|
191
|
-
|
|
186
|
+
|
|
192
187
|
process.env.XRAY_DEV_PATH = originalXrayEnv;
|
|
193
|
-
= originalStrrayEnv;
|
|
194
188
|
});
|
|
195
189
|
|
|
196
190
|
it('should use XRAY_DEV_PATH when set', () => {
|
|
197
191
|
const originalXrayEnv = process.env.XRAY_DEV_PATH;
|
|
198
|
-
const originalStrrayEnv = ;
|
|
199
192
|
process.env.XRAY_DEV_PATH = 'dist';
|
|
200
|
-
|
|
201
|
-
|
|
193
|
+
|
|
202
194
|
const freshRegistry = new ServerConfigRegistry();
|
|
203
195
|
const config = freshRegistry.get('code-review');
|
|
204
196
|
expect(config?.args[0]).toContain('dist/');
|
|
205
|
-
|
|
197
|
+
|
|
206
198
|
process.env.XRAY_DEV_PATH = originalXrayEnv;
|
|
207
|
-
= originalStrrayEnv;
|
|
208
199
|
});
|
|
209
200
|
});
|
|
210
201
|
|
|
@@ -28,7 +28,7 @@ import * as fs from "fs";
|
|
|
28
28
|
import * as path from "path";
|
|
29
29
|
import { fileURLToPath } from "url";
|
|
30
30
|
import { randomUUID } from "crypto";
|
|
31
|
-
import {
|
|
31
|
+
import { handleGovernRequest } from "../nucleus/index.js";
|
|
32
32
|
import { getCodexPolicyService } from "../governance/codex-policy.service.js";
|
|
33
33
|
import { initializeGovernanceIntegration, shutdownGovernanceIntegration } from "../integrations/governance/index.js";
|
|
34
34
|
import { featuresConfigLoader } from "../core/features-config.js";
|
|
@@ -243,8 +243,6 @@ class GovernanceServer {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
private async handleGovernProposals(args: GovernProposalsArgs): Promise<CallToolResult> {
|
|
246
|
-
const service = getGovernanceService();
|
|
247
|
-
|
|
248
246
|
const request: GovernanceRequest = {
|
|
249
247
|
proposals: args.proposals.map((p, i) => ({
|
|
250
248
|
id: p.id || `prop-${Date.now()}-${i}`,
|
|
@@ -261,11 +259,11 @@ class GovernanceServer {
|
|
|
261
259
|
},
|
|
262
260
|
};
|
|
263
261
|
|
|
264
|
-
frameworkLogger.log("governance-mcp", "delegating-to-
|
|
262
|
+
frameworkLogger.log("governance-mcp", "delegating-to-kernel", "info", {
|
|
265
263
|
proposalCount: request.proposals.length,
|
|
266
264
|
});
|
|
267
265
|
|
|
268
|
-
const response = await
|
|
266
|
+
const response = await handleGovernRequest(request);
|
|
269
267
|
|
|
270
268
|
return {
|
|
271
269
|
content: [
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
|
|
9
9
|
import { frameworkLogger } from "../../core/framework-logger.js";
|
|
10
|
+
import { pluginRegistry } from "../../nucleus/plugin-registry.js";
|
|
10
11
|
|
|
11
12
|
interface DesignApiEndpointsArgs {
|
|
12
13
|
resource: string;
|
|
@@ -56,6 +57,14 @@ class XrayApiDesignServer extends XrayKnowledgeSkillBase {
|
|
|
56
57
|
"validate-api-design": async (args) => this.validateApiDesign(args as unknown as ValidateApiDesignArgs),
|
|
57
58
|
};
|
|
58
59
|
this.setupToolHandlers();
|
|
60
|
+
pluginRegistry.registerToolPlugin({
|
|
61
|
+
name: "api-design",
|
|
62
|
+
callTool: async (toolName, args) => {
|
|
63
|
+
const handler = this.handlers[toolName];
|
|
64
|
+
if (!handler) throw new Error(`Unknown tool: ${toolName}`);
|
|
65
|
+
return handler(args);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
private async designApiEndpoints(args: DesignApiEndpointsArgs) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
|
|
2
2
|
import { frameworkLogger } from "../../core/framework-logger.js";
|
|
3
|
+
import { pluginRegistry } from "../../nucleus/plugin-registry.js";
|
|
3
4
|
import * as fs from "fs";
|
|
4
5
|
import * as path from "path";
|
|
5
6
|
|
|
@@ -49,6 +50,14 @@ class XrayArchitecturePatternsServer extends XrayKnowledgeSkillBase {
|
|
|
49
50
|
"recommend-patterns": async (args) => this.recommendPatterns(args as unknown as RecommendPatternsArgs),
|
|
50
51
|
};
|
|
51
52
|
this.setupToolHandlers();
|
|
53
|
+
pluginRegistry.registerToolPlugin({
|
|
54
|
+
name: "architecture-patterns",
|
|
55
|
+
callTool: async (toolName, args) => {
|
|
56
|
+
const handler = this.handlers[toolName];
|
|
57
|
+
if (!handler) throw new Error(`Unknown tool: ${toolName}`);
|
|
58
|
+
return handler(args);
|
|
59
|
+
},
|
|
60
|
+
});
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
private async analyzeArchitecture(args: AnalyzeArchitectureArgs) {
|
|
@@ -11,6 +11,7 @@ import * as path from "path";
|
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
|
|
13
13
|
import { frameworkLogger } from "../../core/framework-logger.js";
|
|
14
|
+
import { pluginRegistry } from "../../nucleus/plugin-registry.js";
|
|
14
15
|
|
|
15
16
|
interface BugReport {
|
|
16
17
|
id: string;
|
|
@@ -254,6 +255,14 @@ class BugTriageSpecialistServer extends XrayKnowledgeSkillBase {
|
|
|
254
255
|
},
|
|
255
256
|
};
|
|
256
257
|
this.setupToolHandlers();
|
|
258
|
+
pluginRegistry.registerToolPlugin({
|
|
259
|
+
name: "bug-triage-specialist",
|
|
260
|
+
callTool: async (toolName, args) => {
|
|
261
|
+
const handler = this.handlers[toolName];
|
|
262
|
+
if (!handler) throw new Error(`Unknown tool: ${toolName}`);
|
|
263
|
+
return handler(args);
|
|
264
|
+
},
|
|
265
|
+
});
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
private triageBugs(errorLogs: string[], context: Record<string, unknown>): TriageResult {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
|
|
14
14
|
import { frameworkLogger } from "../../core/framework-logger.js";
|
|
15
|
+
import { pluginRegistry } from "../../nucleus/plugin-registry.js";
|
|
15
16
|
import * as fs from "fs";
|
|
16
17
|
import * as path from "path";
|
|
17
18
|
|
|
@@ -310,6 +311,14 @@ class CodeAnalyzerServer extends XrayKnowledgeSkillBase {
|
|
|
310
311
|
"find_duplicates": async (args) => this.handleFindDuplicates(args),
|
|
311
312
|
};
|
|
312
313
|
this.setupToolHandlers();
|
|
314
|
+
pluginRegistry.registerToolPlugin({
|
|
315
|
+
name: "code-analyzer",
|
|
316
|
+
callTool: async (toolName, args) => {
|
|
317
|
+
const handler = this.handlers[toolName];
|
|
318
|
+
if (!handler) throw new Error(`Unknown tool: ${toolName}`);
|
|
319
|
+
return handler(args);
|
|
320
|
+
},
|
|
321
|
+
});
|
|
313
322
|
}
|
|
314
323
|
|
|
315
324
|
private handleAnalyzeCode(args: unknown) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
|
|
9
9
|
import { frameworkLogger } from "../../core/framework-logger.js";
|
|
10
|
+
import { pluginRegistry } from "../../nucleus/plugin-registry.js";
|
|
10
11
|
import * as fs from "fs";
|
|
11
12
|
import * as path from "path";
|
|
12
13
|
|
|
@@ -190,6 +191,14 @@ class XrayCodeReviewServer extends XrayKnowledgeSkillBase {
|
|
|
190
191
|
"analyze_proposal": async (args) => this.analyzeProposal(args as AnalyzeProposalArgs),
|
|
191
192
|
};
|
|
192
193
|
this.setupToolHandlers();
|
|
194
|
+
pluginRegistry.registerToolPlugin({
|
|
195
|
+
name: "code-review",
|
|
196
|
+
callTool: async (toolName, args) => {
|
|
197
|
+
const handler = this.handlers[toolName];
|
|
198
|
+
if (!handler) throw new Error(`Unknown tool: ${toolName}`);
|
|
199
|
+
return handler(args);
|
|
200
|
+
},
|
|
201
|
+
});
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
private async analyzeCodeQuality(args: AnalyzeCodeQualityArgs) {
|
|
@@ -9,6 +9,7 @@ import * as path from "path";
|
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
10
|
import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
|
|
11
11
|
import { frameworkLogger } from "../../core/framework-logger.js";
|
|
12
|
+
import { pluginRegistry } from "../../nucleus/plugin-registry.js";
|
|
12
13
|
|
|
13
14
|
class SEOCopywriterServer extends XrayKnowledgeSkillBase {
|
|
14
15
|
constructor() {
|
|
@@ -251,6 +252,14 @@ class SEOCopywriterServer extends XrayKnowledgeSkillBase {
|
|
|
251
252
|
},
|
|
252
253
|
};
|
|
253
254
|
this.setupToolHandlers();
|
|
255
|
+
pluginRegistry.registerToolPlugin({
|
|
256
|
+
name: "content-creator",
|
|
257
|
+
callTool: async (toolName, args) => {
|
|
258
|
+
const handler = this.handlers[toolName];
|
|
259
|
+
if (!handler) throw new Error(`Unknown tool: ${toolName}`);
|
|
260
|
+
return handler(args);
|
|
261
|
+
},
|
|
262
|
+
});
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
265
|
|