@cpretzinger/boss-claude 1.0.0 → 1.0.2
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/README.md +304 -1
- package/bin/boss-claude.js +1138 -0
- package/bin/commands/mode.js +250 -0
- package/bin/onyx-guard.js +259 -0
- package/bin/onyx-guard.sh +251 -0
- package/bin/prompts.js +284 -0
- package/bin/rollback.js +85 -0
- package/bin/setup-wizard.js +492 -0
- package/config/.env.example +17 -0
- package/lib/README.md +83 -0
- package/lib/agent-logger.js +61 -0
- package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
- package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
- package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
- package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
- package/lib/agents/memory-supervisor.js +526 -0
- package/lib/agents/registry.js +135 -0
- package/lib/auto-monitor.js +131 -0
- package/lib/checkpoint-hook.js +112 -0
- package/lib/checkpoint.js +319 -0
- package/lib/commentator.js +213 -0
- package/lib/context-scribe.js +120 -0
- package/lib/delegation-strategies.js +326 -0
- package/lib/hierarchy-validator.js +643 -0
- package/lib/index.js +15 -0
- package/lib/init-with-mode.js +261 -0
- package/lib/init.js +44 -6
- package/lib/memory-result-aggregator.js +252 -0
- package/lib/memory.js +35 -7
- package/lib/mode-enforcer.js +473 -0
- package/lib/onyx-banner.js +169 -0
- package/lib/onyx-identity.js +214 -0
- package/lib/onyx-monitor.js +381 -0
- package/lib/onyx-reminder.js +188 -0
- package/lib/onyx-tool-interceptor.js +341 -0
- package/lib/onyx-wrapper.js +315 -0
- package/lib/orchestrator-gate.js +334 -0
- package/lib/output-formatter.js +296 -0
- package/lib/postgres.js +1 -1
- package/lib/prompt-injector.js +220 -0
- package/lib/prompts.js +532 -0
- package/lib/session.js +153 -6
- package/lib/setup/README.md +187 -0
- package/lib/setup/env-manager.js +785 -0
- package/lib/setup/error-recovery.js +630 -0
- package/lib/setup/explain-scopes.js +385 -0
- package/lib/setup/github-instructions.js +333 -0
- package/lib/setup/github-repo.js +254 -0
- package/lib/setup/import-credentials.js +498 -0
- package/lib/setup/index.js +62 -0
- package/lib/setup/init-postgres.js +785 -0
- package/lib/setup/init-redis.js +456 -0
- package/lib/setup/integration-test.js +652 -0
- package/lib/setup/progress.js +357 -0
- package/lib/setup/rollback.js +670 -0
- package/lib/setup/rollback.test.js +452 -0
- package/lib/setup/setup-with-rollback.example.js +351 -0
- package/lib/setup/summary.js +400 -0
- package/lib/setup/test-github-setup.js +10 -0
- package/lib/setup/test-postgres-init.js +98 -0
- package/lib/setup/verify-setup.js +102 -0
- package/lib/task-agent-worker.js +235 -0
- package/lib/token-monitor.js +466 -0
- package/lib/tool-wrapper-integration.js +369 -0
- package/lib/tool-wrapper.js +387 -0
- package/lib/validators/README.md +497 -0
- package/lib/validators/config.js +583 -0
- package/lib/validators/config.test.js +175 -0
- package/lib/validators/github.js +310 -0
- package/lib/validators/github.test.js +61 -0
- package/lib/validators/index.js +15 -0
- package/lib/validators/postgres.js +525 -0
- package/package.json +98 -13
- package/scripts/benchmark-memory.js +433 -0
- package/scripts/check-secrets.sh +12 -0
- package/scripts/fetch-todos.mjs +148 -0
- package/scripts/graceful-shutdown.sh +156 -0
- package/scripts/install-onyx-hooks.js +373 -0
- package/scripts/install.js +119 -18
- package/scripts/redis-monitor.js +284 -0
- package/scripts/redis-setup.js +412 -0
- package/scripts/test-memory-retrieval.js +201 -0
- package/scripts/validate-exports.js +68 -0
- package/scripts/validate-package.js +120 -0
- package/scripts/verify-onyx-deployment.js +309 -0
- package/scripts/verify-redis-deployment.js +354 -0
- package/scripts/verify-redis-init.js +219 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ONYX WRAPPER - Claude Code Integration
|
|
4
|
+
*
|
|
5
|
+
* Drop-in replacement for direct tool calls that enforces ONYX delegation rules.
|
|
6
|
+
* This wrapper intercepts ALL tool calls and applies pre-hook validation.
|
|
7
|
+
*
|
|
8
|
+
* USAGE:
|
|
9
|
+
* import { onyxWrapper } from '@cpretzinger/boss-claude/lib/onyx-wrapper.js';
|
|
10
|
+
*
|
|
11
|
+
* // Wrap tool execution
|
|
12
|
+
* const result = await onyxWrapper.executeTool('Read', { file_path: '/path/to/file.js' });
|
|
13
|
+
*
|
|
14
|
+
* INTEGRATION:
|
|
15
|
+
* Replace direct tool calls in ONYX agent with onyxWrapper.executeTool()
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import onyxInterceptor from './onyx-tool-interceptor.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* ONYX Wrapper Class
|
|
23
|
+
* Wraps tool execution and enforces delegation rules
|
|
24
|
+
*/
|
|
25
|
+
export class OnyxWrapper {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.interceptor = onyxInterceptor;
|
|
28
|
+
this.executionLog = [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Execute tool with pre-hook validation
|
|
33
|
+
*
|
|
34
|
+
* @param {string} toolName - Name of the tool (Read, Write, Edit, etc.)
|
|
35
|
+
* @param {Object} toolParams - Tool parameters
|
|
36
|
+
* @param {Object} options - Execution options
|
|
37
|
+
* @returns {Promise<Object>} Tool execution result
|
|
38
|
+
*/
|
|
39
|
+
async executeTool(toolName, toolParams = {}, options = {}) {
|
|
40
|
+
const startTime = Date.now();
|
|
41
|
+
|
|
42
|
+
// PRE-HOOK: Intercept before execution
|
|
43
|
+
const interceptionResult = await this.interceptor.interceptToolCall(toolName, toolParams);
|
|
44
|
+
|
|
45
|
+
// Log execution attempt
|
|
46
|
+
this._logExecution(toolName, toolParams, interceptionResult, startTime);
|
|
47
|
+
|
|
48
|
+
if (!interceptionResult.allowed) {
|
|
49
|
+
// Tool blocked - handle delegation or rejection
|
|
50
|
+
return this._handleBlockedTool(toolName, toolParams, interceptionResult, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (interceptionResult.override) {
|
|
54
|
+
console.log(chalk.yellow('\n⚠️ ONYX delegation override - proceeding with caution'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Tool allowed - execute if executor provided
|
|
58
|
+
if (options.executor && typeof options.executor === 'function') {
|
|
59
|
+
try {
|
|
60
|
+
const result = await options.executor(toolName, toolParams);
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
tool: toolName,
|
|
64
|
+
result,
|
|
65
|
+
override: interceptionResult.override || false,
|
|
66
|
+
executionTime: Date.now() - startTime
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
tool: toolName,
|
|
72
|
+
error: error.message,
|
|
73
|
+
executionTime: Date.now() - startTime
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// No executor - return allowed status
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
allowed: true,
|
|
82
|
+
tool: toolName,
|
|
83
|
+
params: toolParams,
|
|
84
|
+
override: interceptionResult.override || false,
|
|
85
|
+
message: 'Tool allowed - no executor provided (dry run)',
|
|
86
|
+
executionTime: Date.now() - startTime
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Handle blocked tool - auto-delegate or reject
|
|
92
|
+
*/
|
|
93
|
+
async _handleBlockedTool(toolName, toolParams, interceptionResult, options) {
|
|
94
|
+
if (interceptionResult.delegateTo === 'Task') {
|
|
95
|
+
// Auto-delegate to Task tool
|
|
96
|
+
console.log(chalk.cyan('\n📤 Auto-delegating to Task tool...'));
|
|
97
|
+
console.log(chalk.dim(interceptionResult.delegationMessage));
|
|
98
|
+
|
|
99
|
+
if (options.taskExecutor && typeof options.taskExecutor === 'function') {
|
|
100
|
+
// Execute via Task tool
|
|
101
|
+
try {
|
|
102
|
+
const taskResult = await options.taskExecutor({
|
|
103
|
+
description: interceptionResult.delegationMessage,
|
|
104
|
+
context: {
|
|
105
|
+
originalTool: toolName,
|
|
106
|
+
originalParams: toolParams,
|
|
107
|
+
delegatedBy: 'ONYX',
|
|
108
|
+
delegationReason: interceptionResult.reason
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
success: true,
|
|
114
|
+
delegated: true,
|
|
115
|
+
delegateTo: 'Task',
|
|
116
|
+
tool: toolName,
|
|
117
|
+
originalParams: toolParams,
|
|
118
|
+
delegationMessage: interceptionResult.delegationMessage,
|
|
119
|
+
taskResult
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
delegated: true,
|
|
125
|
+
delegateTo: 'Task',
|
|
126
|
+
tool: toolName,
|
|
127
|
+
error: error.message
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// No task executor - return delegation info
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
delegated: true,
|
|
136
|
+
delegateTo: 'Task',
|
|
137
|
+
tool: toolName,
|
|
138
|
+
originalParams: toolParams,
|
|
139
|
+
delegationMessage: interceptionResult.delegationMessage,
|
|
140
|
+
message: 'Delegation required - no task executor provided'
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (interceptionResult.rejected) {
|
|
145
|
+
// Tool rejected entirely
|
|
146
|
+
console.log(chalk.red('\n❌ Tool call rejected by ONYX interceptor'));
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
blocked: true,
|
|
151
|
+
rejected: true,
|
|
152
|
+
tool: toolName,
|
|
153
|
+
reason: interceptionResult.reason
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Generic block
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
blocked: true,
|
|
161
|
+
tool: toolName,
|
|
162
|
+
reason: interceptionResult.reason
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Log tool execution attempt
|
|
168
|
+
*/
|
|
169
|
+
_logExecution(toolName, toolParams, interceptionResult, startTime) {
|
|
170
|
+
const logEntry = {
|
|
171
|
+
timestamp: new Date().toISOString(),
|
|
172
|
+
tool: toolName,
|
|
173
|
+
params: toolParams,
|
|
174
|
+
allowed: interceptionResult.allowed,
|
|
175
|
+
blocked: interceptionResult.blocked || false,
|
|
176
|
+
delegated: interceptionResult.delegateTo ? true : false,
|
|
177
|
+
override: interceptionResult.override || false,
|
|
178
|
+
startTime
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
this.executionLog.push(logEntry);
|
|
182
|
+
|
|
183
|
+
// Keep only last 100 entries
|
|
184
|
+
if (this.executionLog.length > 100) {
|
|
185
|
+
this.executionLog.shift();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get execution log
|
|
191
|
+
*/
|
|
192
|
+
getExecutionLog(limit = 20) {
|
|
193
|
+
return this.executionLog.slice(-limit);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Print execution summary
|
|
198
|
+
*/
|
|
199
|
+
printExecutionSummary() {
|
|
200
|
+
console.log(chalk.blue('\n📊 ONYX TOOL EXECUTION SUMMARY'));
|
|
201
|
+
console.log(chalk.dim('═'.repeat(80)));
|
|
202
|
+
|
|
203
|
+
const stats = this._calculateStats();
|
|
204
|
+
|
|
205
|
+
console.log(chalk.white(`Total Calls: ${chalk.magenta(stats.total)}`));
|
|
206
|
+
console.log(chalk.white(`Allowed: ${chalk.green(stats.allowed)}`));
|
|
207
|
+
console.log(chalk.white(`Blocked: ${chalk.red(stats.blocked)}`));
|
|
208
|
+
console.log(chalk.white(`Delegated: ${chalk.cyan(stats.delegated)}`));
|
|
209
|
+
console.log(chalk.white(`Overrides: ${chalk.yellow(stats.overrides)}`));
|
|
210
|
+
|
|
211
|
+
console.log(chalk.dim('\n─'.repeat(80)));
|
|
212
|
+
|
|
213
|
+
if (stats.topTools.length > 0) {
|
|
214
|
+
console.log(chalk.white('\nTop Tools Used:'));
|
|
215
|
+
stats.topTools.forEach((item, i) => {
|
|
216
|
+
console.log(chalk.dim(` ${i + 1}. ${item.tool} (${item.count} calls)`));
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
console.log(chalk.dim('═'.repeat(80)));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Calculate execution statistics
|
|
225
|
+
*/
|
|
226
|
+
_calculateStats() {
|
|
227
|
+
const stats = {
|
|
228
|
+
total: this.executionLog.length,
|
|
229
|
+
allowed: 0,
|
|
230
|
+
blocked: 0,
|
|
231
|
+
delegated: 0,
|
|
232
|
+
overrides: 0,
|
|
233
|
+
toolCounts: {}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
this.executionLog.forEach(log => {
|
|
237
|
+
if (log.allowed) stats.allowed++;
|
|
238
|
+
if (log.blocked) stats.blocked++;
|
|
239
|
+
if (log.delegated) stats.delegated++;
|
|
240
|
+
if (log.override) stats.overrides++;
|
|
241
|
+
|
|
242
|
+
stats.toolCounts[log.tool] = (stats.toolCounts[log.tool] || 0) + 1;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Sort tools by usage
|
|
246
|
+
stats.topTools = Object.entries(stats.toolCounts)
|
|
247
|
+
.map(([tool, count]) => ({ tool, count }))
|
|
248
|
+
.sort((a, b) => b.count - a.count)
|
|
249
|
+
.slice(0, 5);
|
|
250
|
+
|
|
251
|
+
return stats;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Clear execution log
|
|
256
|
+
*/
|
|
257
|
+
clearLog() {
|
|
258
|
+
this.executionLog = [];
|
|
259
|
+
console.log(chalk.green('\n✅ Execution log cleared'));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Singleton instance
|
|
264
|
+
const onyxWrapper = new OnyxWrapper();
|
|
265
|
+
|
|
266
|
+
export default onyxWrapper;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* INTEGRATION EXAMPLES:
|
|
270
|
+
*
|
|
271
|
+
* 1. Basic usage - dry run (no executor):
|
|
272
|
+
*
|
|
273
|
+
* import onyxWrapper from '@cpretzinger/boss-claude/lib/onyx-wrapper.js';
|
|
274
|
+
*
|
|
275
|
+
* const result = await onyxWrapper.executeTool('Read', {
|
|
276
|
+
* file_path: '/path/to/file.js'
|
|
277
|
+
* });
|
|
278
|
+
*
|
|
279
|
+
* if (result.success && result.allowed) {
|
|
280
|
+
* console.log('Tool allowed');
|
|
281
|
+
* } else if (result.delegated) {
|
|
282
|
+
* console.log('Delegated to:', result.delegateTo);
|
|
283
|
+
* }
|
|
284
|
+
*
|
|
285
|
+
* 2. With executor function:
|
|
286
|
+
*
|
|
287
|
+
* const result = await onyxWrapper.executeTool('Read', {
|
|
288
|
+
* file_path: '/path/to/file.js'
|
|
289
|
+
* }, {
|
|
290
|
+
* executor: async (tool, params) => {
|
|
291
|
+
* // Your actual tool execution logic
|
|
292
|
+
* return await actualReadTool(params);
|
|
293
|
+
* }
|
|
294
|
+
* });
|
|
295
|
+
*
|
|
296
|
+
* 3. With Task delegation executor:
|
|
297
|
+
*
|
|
298
|
+
* const result = await onyxWrapper.executeTool('Grep', {
|
|
299
|
+
* pattern: 'function',
|
|
300
|
+
* path: '/src'
|
|
301
|
+
* }, {
|
|
302
|
+
* taskExecutor: async (taskParams) => {
|
|
303
|
+
* // Execute via Task tool
|
|
304
|
+
* return await taskTool.execute(taskParams);
|
|
305
|
+
* }
|
|
306
|
+
* });
|
|
307
|
+
*
|
|
308
|
+
* 4. Print execution summary:
|
|
309
|
+
*
|
|
310
|
+
* onyxWrapper.printExecutionSummary();
|
|
311
|
+
*
|
|
312
|
+
* 5. Get interceptor report:
|
|
313
|
+
*
|
|
314
|
+
* onyxWrapper.interceptor.printReport();
|
|
315
|
+
*/
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ORCHESTRATOR GATE - Pre-Action Hook System
|
|
3
|
+
*
|
|
4
|
+
* Purpose: Enforce mode checks BEFORE every significant action
|
|
5
|
+
* Pattern: Decorator pattern wrapping critical operations
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* 1. Intercepts all major operations (delegate, execute, review)
|
|
9
|
+
* 2. Validates mode + capabilities + token budget
|
|
10
|
+
* 3. Logs all actions (allowed or blocked)
|
|
11
|
+
* 4. Provides clear error messages when blocked
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { getEnforcer, MODES, MODE_CAPABILITIES } from './mode-enforcer.js';
|
|
15
|
+
import { loadIdentity } from './identity.js';
|
|
16
|
+
|
|
17
|
+
// ==========================================
|
|
18
|
+
// ORCHESTRATOR GATE CLASS
|
|
19
|
+
// ==========================================
|
|
20
|
+
export class OrchestratorGate {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.enforcer = getEnforcer();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* BEFORE DELEGATE - Check if delegation is allowed
|
|
27
|
+
*
|
|
28
|
+
* @param {string} targetAgent - Agent to delegate to
|
|
29
|
+
* @param {object} task - Task description
|
|
30
|
+
* @param {number} estimatedTokens - Estimated cost
|
|
31
|
+
*/
|
|
32
|
+
async beforeDelegate(targetAgent, task, estimatedTokens = 0) {
|
|
33
|
+
console.log(`\n[ORCHESTRATOR GATE] Checking delegation permission...`);
|
|
34
|
+
console.log(`Target agent: ${targetAgent}`);
|
|
35
|
+
console.log(`Task: ${task.description || 'N/A'}`);
|
|
36
|
+
console.log(`Estimated tokens: ${estimatedTokens}`);
|
|
37
|
+
|
|
38
|
+
// 1. Check if current mode allows delegation
|
|
39
|
+
await this.enforcer.checkCapability(
|
|
40
|
+
'canDelegate',
|
|
41
|
+
`Delegating to ${targetAgent}: ${task.description || 'task'}`
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// 2. Check token budget if applicable
|
|
45
|
+
if (estimatedTokens > 0) {
|
|
46
|
+
await this.enforcer.enforceTokenBudget(
|
|
47
|
+
estimatedTokens,
|
|
48
|
+
`Delegation to ${targetAgent}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 3. Verify orchestrator mode is active
|
|
53
|
+
const currentMode = await this.enforcer.getCurrentMode();
|
|
54
|
+
if (currentMode !== MODES.ORCHESTRATOR) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`[GATE BLOCKED] Delegation requires orchestrator mode.\n` +
|
|
57
|
+
`Current mode: ${currentMode}\n` +
|
|
58
|
+
`Switch with: boss-claude mode orchestrator`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log(`[ORCHESTRATOR GATE] ✅ Delegation approved`);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* BEFORE EXECUTE - Check if direct execution is allowed
|
|
68
|
+
*
|
|
69
|
+
* @param {object} action - Action to execute
|
|
70
|
+
* @param {number} estimatedTokens - Estimated cost
|
|
71
|
+
*/
|
|
72
|
+
async beforeExecute(action, estimatedTokens = 0) {
|
|
73
|
+
console.log(`\n[ORCHESTRATOR GATE] Checking execution permission...`);
|
|
74
|
+
console.log(`Action: ${action.description || 'N/A'}`);
|
|
75
|
+
console.log(`Estimated tokens: ${estimatedTokens}`);
|
|
76
|
+
|
|
77
|
+
// 1. Check if current mode allows execution
|
|
78
|
+
await this.enforcer.checkCapability(
|
|
79
|
+
'canExecute',
|
|
80
|
+
`Executing: ${action.description || 'action'}`
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// 2. Check token budget
|
|
84
|
+
if (estimatedTokens > 0) {
|
|
85
|
+
await this.enforcer.enforceTokenBudget(
|
|
86
|
+
estimatedTokens,
|
|
87
|
+
`Direct execution: ${action.description || 'action'}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log(`[ORCHESTRATOR GATE] ✅ Execution approved`);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* BEFORE REVIEW - Check if code review is allowed
|
|
97
|
+
*
|
|
98
|
+
* @param {string} workerAgent - Agent whose code is being reviewed
|
|
99
|
+
* @param {object} code - Code to review
|
|
100
|
+
*/
|
|
101
|
+
async beforeReview(workerAgent, code) {
|
|
102
|
+
console.log(`\n[ORCHESTRATOR GATE] Checking review permission...`);
|
|
103
|
+
console.log(`Worker agent: ${workerAgent}`);
|
|
104
|
+
|
|
105
|
+
// 1. Check if current mode allows review
|
|
106
|
+
await this.enforcer.checkCapability(
|
|
107
|
+
'canReview',
|
|
108
|
+
`Reviewing code from ${workerAgent}`
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// 2. Verify orchestrator or review mode is active
|
|
112
|
+
const currentMode = await this.enforcer.getCurrentMode();
|
|
113
|
+
if (currentMode !== MODES.ORCHESTRATOR && currentMode !== MODES.REVIEW) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`[GATE BLOCKED] Code review requires orchestrator or review mode.\n` +
|
|
116
|
+
`Current mode: ${currentMode}\n` +
|
|
117
|
+
`Switch with: boss-claude mode orchestrator`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(`[ORCHESTRATOR GATE] ✅ Review approved`);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* BEFORE LEARN - Check if learning/memory write is allowed
|
|
127
|
+
*
|
|
128
|
+
* @param {string} learningType - Type of learning (success|failure|pattern)
|
|
129
|
+
* @param {object} data - Learning data
|
|
130
|
+
*/
|
|
131
|
+
async beforeLearn(learningType, data) {
|
|
132
|
+
console.log(`\n[ORCHESTRATOR GATE] Checking learning permission...`);
|
|
133
|
+
console.log(`Learning type: ${learningType}`);
|
|
134
|
+
|
|
135
|
+
// 1. Check if current mode allows learning
|
|
136
|
+
await this.enforcer.checkCapability(
|
|
137
|
+
'canLearn',
|
|
138
|
+
`Recording learning: ${learningType}`
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
console.log(`[ORCHESTRATOR GATE] ✅ Learning approved`);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* BEFORE CONFIG CHANGE - Check if global config modification is allowed
|
|
147
|
+
*
|
|
148
|
+
* @param {string} configKey - Config key being modified
|
|
149
|
+
* @param {any} newValue - New value
|
|
150
|
+
*/
|
|
151
|
+
async beforeConfigChange(configKey, newValue) {
|
|
152
|
+
console.log(`\n[ORCHESTRATOR GATE] Checking config change permission...`);
|
|
153
|
+
console.log(`Config key: ${configKey}`);
|
|
154
|
+
|
|
155
|
+
// 1. Check if current mode allows global config changes
|
|
156
|
+
await this.enforcer.checkCapability(
|
|
157
|
+
'canModifyGlobalConfig',
|
|
158
|
+
`Modifying global config: ${configKey}`
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// 2. Verify orchestrator mode is active
|
|
162
|
+
const currentMode = await this.enforcer.getCurrentMode();
|
|
163
|
+
if (currentMode !== MODES.ORCHESTRATOR) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`[GATE BLOCKED] Global config changes require orchestrator mode.\n` +
|
|
166
|
+
`Current mode: ${currentMode}\n` +
|
|
167
|
+
`Config key: ${configKey}\n` +
|
|
168
|
+
`Switch with: boss-claude mode orchestrator`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`[ORCHESTRATOR GATE] ✅ Config change approved`);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* GET GATE STATUS - Current mode and capabilities
|
|
178
|
+
*/
|
|
179
|
+
async getStatus() {
|
|
180
|
+
const currentMode = await this.enforcer.getCurrentMode();
|
|
181
|
+
const metadata = await this.enforcer.getModeMetadata();
|
|
182
|
+
const capabilities = MODE_CAPABILITIES[currentMode];
|
|
183
|
+
const agentIdentity = await this.enforcer.getAgentIdentity();
|
|
184
|
+
const bossIdentity = await loadIdentity();
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
mode: currentMode,
|
|
188
|
+
metadata,
|
|
189
|
+
capabilities,
|
|
190
|
+
agent: agentIdentity,
|
|
191
|
+
boss: {
|
|
192
|
+
level: bossIdentity.level,
|
|
193
|
+
xp: bossIdentity.xp,
|
|
194
|
+
token_bank: bossIdentity.token_bank
|
|
195
|
+
},
|
|
196
|
+
restrictions: {
|
|
197
|
+
canDelegate: capabilities.canDelegate,
|
|
198
|
+
canReview: capabilities.canReview,
|
|
199
|
+
canExecute: capabilities.canExecute,
|
|
200
|
+
canLearn: capabilities.canLearn,
|
|
201
|
+
canModifyGlobalConfig: capabilities.canModifyGlobalConfig,
|
|
202
|
+
maxTokenBudget: capabilities.maxTokenBudget
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* PRINT STATUS - Human-readable gate status
|
|
209
|
+
*/
|
|
210
|
+
async printStatus() {
|
|
211
|
+
const status = await this.getStatus();
|
|
212
|
+
|
|
213
|
+
console.log(`\n╔══════════════════════════════════════════════════════════╗`);
|
|
214
|
+
console.log(`║ ORCHESTRATOR GATE STATUS ║`);
|
|
215
|
+
console.log(`╚══════════════════════════════════════════════════════════╝`);
|
|
216
|
+
console.log(`\nMode: ${status.mode.toUpperCase()}`);
|
|
217
|
+
console.log(`Set by: ${status.metadata?.setBy || 'N/A'}`);
|
|
218
|
+
console.log(`Set at: ${status.metadata?.setAt || 'N/A'}`);
|
|
219
|
+
console.log(`Reason: ${status.metadata?.reason || 'N/A'}`);
|
|
220
|
+
|
|
221
|
+
if (status.agent) {
|
|
222
|
+
console.log(`\nAgent: ${status.agent.agent}`);
|
|
223
|
+
console.log(`Domain: ${status.agent.domain || 'N/A'}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`\nBoss Identity:`);
|
|
227
|
+
console.log(` Level: ${status.boss.level}`);
|
|
228
|
+
console.log(` XP: ${status.boss.xp}`);
|
|
229
|
+
console.log(` Token Bank: ${status.boss.token_bank}`);
|
|
230
|
+
|
|
231
|
+
console.log(`\nCapabilities:`);
|
|
232
|
+
console.log(` Delegate: ${status.restrictions.canDelegate ? '✅' : '❌'}`);
|
|
233
|
+
console.log(` Review: ${status.restrictions.canReview ? '✅' : '❌'}`);
|
|
234
|
+
console.log(` Execute: ${status.restrictions.canExecute ? '✅' : '❌'}`);
|
|
235
|
+
console.log(` Learn: ${status.restrictions.canLearn ? '✅' : '❌'}`);
|
|
236
|
+
console.log(` Modify Config: ${status.restrictions.canModifyGlobalConfig ? '✅' : '❌'}`);
|
|
237
|
+
console.log(` Max Token Budget: ${status.restrictions.maxTokenBudget.toLocaleString()}`);
|
|
238
|
+
|
|
239
|
+
console.log(`\n`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ==========================================
|
|
244
|
+
// SINGLETON INSTANCE
|
|
245
|
+
// ==========================================
|
|
246
|
+
let gateInstance = null;
|
|
247
|
+
|
|
248
|
+
export function getGate() {
|
|
249
|
+
if (!gateInstance) {
|
|
250
|
+
gateInstance = new OrchestratorGate();
|
|
251
|
+
}
|
|
252
|
+
return gateInstance;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ==========================================
|
|
256
|
+
// CONVENIENCE WRAPPER FUNCTIONS
|
|
257
|
+
// ==========================================
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Initialize orchestrator mode - Call at session start
|
|
261
|
+
*/
|
|
262
|
+
export async function initOrchestratorMode(sessionId = null) {
|
|
263
|
+
const enforcer = getEnforcer();
|
|
264
|
+
|
|
265
|
+
await enforcer.setMode(MODES.ORCHESTRATOR, {
|
|
266
|
+
agent: 'boss-claude',
|
|
267
|
+
reason: 'session-start',
|
|
268
|
+
sessionId
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await enforcer.setAgentIdentity('boss-claude', 'orchestrator');
|
|
272
|
+
|
|
273
|
+
console.log(`\n[ORCHESTRATOR MODE] Initialized`);
|
|
274
|
+
console.log(`Session ID: ${sessionId || 'N/A'}`);
|
|
275
|
+
console.log(`Mode: ${MODES.ORCHESTRATOR}`);
|
|
276
|
+
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Switch to specialist mode
|
|
282
|
+
*/
|
|
283
|
+
export async function switchToSpecialist(agentName, domain) {
|
|
284
|
+
const enforcer = getEnforcer();
|
|
285
|
+
|
|
286
|
+
await enforcer.setMode(MODES.SPECIALIST, {
|
|
287
|
+
agent: agentName,
|
|
288
|
+
reason: 'specialist-delegation',
|
|
289
|
+
domain
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
await enforcer.setAgentIdentity(agentName, domain);
|
|
293
|
+
|
|
294
|
+
console.log(`\n[MODE SWITCH] Switched to specialist mode`);
|
|
295
|
+
console.log(`Agent: ${agentName}`);
|
|
296
|
+
console.log(`Domain: ${domain}`);
|
|
297
|
+
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Return to orchestrator mode
|
|
303
|
+
*/
|
|
304
|
+
export async function returnToOrchestrator() {
|
|
305
|
+
const enforcer = getEnforcer();
|
|
306
|
+
|
|
307
|
+
await enforcer.setMode(MODES.ORCHESTRATOR, {
|
|
308
|
+
agent: 'boss-claude',
|
|
309
|
+
reason: 'specialist-task-complete'
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
await enforcer.setAgentIdentity('boss-claude', 'orchestrator');
|
|
313
|
+
|
|
314
|
+
console.log(`\n[MODE SWITCH] Returned to orchestrator mode`);
|
|
315
|
+
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Emergency reset to safe default
|
|
321
|
+
*/
|
|
322
|
+
export async function emergencyReset() {
|
|
323
|
+
const enforcer = getEnforcer();
|
|
324
|
+
await enforcer.resetMode();
|
|
325
|
+
console.log(`\n[EMERGENCY RESET] Mode reset to WORKER (safe default)`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ==========================================
|
|
329
|
+
// EXPORTS
|
|
330
|
+
// ==========================================
|
|
331
|
+
export {
|
|
332
|
+
MODES,
|
|
333
|
+
MODE_CAPABILITIES
|
|
334
|
+
};
|