@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,188 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
|
|
12
|
+
// Load environment variables from ~/.boss-claude/.env
|
|
13
|
+
const envPath = join(os.homedir(), '.boss-claude', '.env');
|
|
14
|
+
if (existsSync(envPath)) {
|
|
15
|
+
dotenv.config({ path: envPath });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let redis = null;
|
|
19
|
+
|
|
20
|
+
function getRedis() {
|
|
21
|
+
if (!redis) {
|
|
22
|
+
if (!process.env.REDIS_URL) {
|
|
23
|
+
throw new Error('REDIS_URL not found. Please run: boss-claude init');
|
|
24
|
+
}
|
|
25
|
+
redis = new Redis(process.env.REDIS_URL);
|
|
26
|
+
}
|
|
27
|
+
return redis;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const REMINDER_KEY = 'boss:onyx:message_count';
|
|
31
|
+
const DEFAULT_INTERVAL = 5; // Show reminder every 5 messages
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Increment message counter and check if reminder should be displayed
|
|
35
|
+
* @param {number} interval - Number of messages between reminders (default: 5)
|
|
36
|
+
* @returns {Promise<{shouldShow: boolean, count: number, reminder: string|null}>}
|
|
37
|
+
*/
|
|
38
|
+
export async function checkOnyxReminder(interval = DEFAULT_INTERVAL) {
|
|
39
|
+
try {
|
|
40
|
+
const client = getRedis();
|
|
41
|
+
|
|
42
|
+
// Increment counter
|
|
43
|
+
const count = await client.incr(REMINDER_KEY);
|
|
44
|
+
|
|
45
|
+
// Check if we should show reminder
|
|
46
|
+
const shouldShow = count % interval === 0;
|
|
47
|
+
|
|
48
|
+
let reminder = null;
|
|
49
|
+
if (shouldShow) {
|
|
50
|
+
reminder = formatReminder(count);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
shouldShow,
|
|
55
|
+
count,
|
|
56
|
+
reminder
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('ONYX Reminder Error:', error.message);
|
|
60
|
+
return {
|
|
61
|
+
shouldShow: false,
|
|
62
|
+
count: 0,
|
|
63
|
+
reminder: null
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Format the ONYX reminder message
|
|
70
|
+
* @param {number} count - Current message count
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
function formatReminder(count) {
|
|
74
|
+
return `
|
|
75
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
76
|
+
ā” CRITICAL REMINDER ā”
|
|
77
|
+
|
|
78
|
+
${chalk.bold.red('ONYX = ORCHESTRATOR ONLY')}
|
|
79
|
+
|
|
80
|
+
${chalk.yellow('ONYX must NEVER execute tasks directly.')}
|
|
81
|
+
${chalk.yellow('ONYX must ALWAYS delegate to specialized engineers.')}
|
|
82
|
+
|
|
83
|
+
Message Count: ${count}
|
|
84
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Reset the message counter (useful for new sessions)
|
|
90
|
+
* @returns {Promise<void>}
|
|
91
|
+
*/
|
|
92
|
+
export async function resetOnyxCounter() {
|
|
93
|
+
try {
|
|
94
|
+
const client = getRedis();
|
|
95
|
+
await client.del(REMINDER_KEY);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Error resetting ONYX counter:', error.message);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get current message count
|
|
103
|
+
* @returns {Promise<number>}
|
|
104
|
+
*/
|
|
105
|
+
export async function getOnyxMessageCount() {
|
|
106
|
+
try {
|
|
107
|
+
const client = getRedis();
|
|
108
|
+
const count = await client.get(REMINDER_KEY);
|
|
109
|
+
return parseInt(count || '0', 10);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('Error getting ONYX count:', error.message);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Set custom interval for reminders
|
|
118
|
+
* @param {number} interval - Number of messages between reminders
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
export async function setOnyxInterval(interval) {
|
|
122
|
+
try {
|
|
123
|
+
const client = getRedis();
|
|
124
|
+
await client.set('boss:onyx:interval', interval);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Error setting ONYX interval:', error.message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get current reminder interval
|
|
132
|
+
* @returns {Promise<number>}
|
|
133
|
+
*/
|
|
134
|
+
export async function getOnyxInterval() {
|
|
135
|
+
try {
|
|
136
|
+
const client = getRedis();
|
|
137
|
+
const interval = await client.get('boss:onyx:interval');
|
|
138
|
+
return parseInt(interval || DEFAULT_INTERVAL.toString(), 10);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('Error getting ONYX interval:', error.message);
|
|
141
|
+
return DEFAULT_INTERVAL;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Auto-hook for Claude Code - Call this after every message
|
|
147
|
+
* This is the main function to integrate into conversation flow
|
|
148
|
+
* @param {number} customInterval - Optional custom interval
|
|
149
|
+
* @returns {Promise<string|null>}
|
|
150
|
+
*/
|
|
151
|
+
export async function onyxAutoReminder(customInterval = null) {
|
|
152
|
+
try {
|
|
153
|
+
const interval = customInterval || await getOnyxInterval();
|
|
154
|
+
const { shouldShow, reminder } = await checkOnyxReminder(interval);
|
|
155
|
+
|
|
156
|
+
if (shouldShow && reminder) {
|
|
157
|
+
console.log(reminder);
|
|
158
|
+
return reminder;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return null;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('ONYX Auto-Reminder Error:', error.message);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Manual trigger - Force display the reminder
|
|
170
|
+
* @returns {Promise<string>}
|
|
171
|
+
*/
|
|
172
|
+
export async function showOnyxReminder() {
|
|
173
|
+
const count = await getOnyxMessageCount();
|
|
174
|
+
const reminder = formatReminder(count);
|
|
175
|
+
console.log(reminder);
|
|
176
|
+
return reminder;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Export for CLI usage
|
|
180
|
+
export default {
|
|
181
|
+
checkOnyxReminder,
|
|
182
|
+
resetOnyxCounter,
|
|
183
|
+
getOnyxMessageCount,
|
|
184
|
+
setOnyxInterval,
|
|
185
|
+
getOnyxInterval,
|
|
186
|
+
onyxAutoReminder,
|
|
187
|
+
showOnyxReminder
|
|
188
|
+
};
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ONYX TOOL INTERCEPTOR - Pre-Hook System
|
|
4
|
+
*
|
|
5
|
+
* Automatically intercepts Read/Write/Edit/Bash/Grep/Glob tool calls
|
|
6
|
+
* and blocks them, forcing delegation to Task tool instead.
|
|
7
|
+
*
|
|
8
|
+
* EXECUTABLE CODE - Not documentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import inquirer from 'inquirer';
|
|
13
|
+
|
|
14
|
+
// Define ONYX agent name
|
|
15
|
+
const ONYX_AGENT = 'ONYX';
|
|
16
|
+
|
|
17
|
+
// Tools forbidden for ONYX (must delegate instead)
|
|
18
|
+
const FORBIDDEN_TOOLS = [
|
|
19
|
+
'Read',
|
|
20
|
+
'Write',
|
|
21
|
+
'Edit',
|
|
22
|
+
'Bash',
|
|
23
|
+
'Grep',
|
|
24
|
+
'Glob',
|
|
25
|
+
'NotebookEdit'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// ONYX is allowed to use these tools
|
|
29
|
+
const ALLOWED_TOOLS = [
|
|
30
|
+
'Task', // Primary delegation tool
|
|
31
|
+
'WebFetch', // Research allowed
|
|
32
|
+
'WebSearch', // Research allowed
|
|
33
|
+
'TodoWrite', // Planning allowed
|
|
34
|
+
'Skill' // Can invoke skills
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* ONYX Tool Interceptor Class
|
|
39
|
+
* Hooks into tool calls and enforces delegation rules
|
|
40
|
+
*/
|
|
41
|
+
export class OnyxToolInterceptor {
|
|
42
|
+
constructor(config = {}) {
|
|
43
|
+
this.agentName = config.agentName || ONYX_AGENT;
|
|
44
|
+
this.forbiddenTools = config.forbiddenTools || FORBIDDEN_TOOLS;
|
|
45
|
+
this.allowedTools = config.allowedTools || ALLOWED_TOOLS;
|
|
46
|
+
this.violations = [];
|
|
47
|
+
this.blockCount = 0;
|
|
48
|
+
this.delegationCount = 0;
|
|
49
|
+
this.enabled = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* PRE-HOOK: Intercept tool call before execution
|
|
54
|
+
* Returns { allowed: boolean, reason: string, delegateTo?: string }
|
|
55
|
+
*/
|
|
56
|
+
async interceptToolCall(toolName, toolParams = {}) {
|
|
57
|
+
if (!this.enabled) {
|
|
58
|
+
return { allowed: true, reason: 'Interceptor disabled' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if this is ONYX agent
|
|
62
|
+
if (this.agentName !== ONYX_AGENT) {
|
|
63
|
+
return { allowed: true, reason: 'Not ONYX agent - no restrictions' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check if tool is forbidden
|
|
67
|
+
const isForbidden = this.forbiddenTools.includes(toolName);
|
|
68
|
+
const isAllowed = this.allowedTools.includes(toolName);
|
|
69
|
+
|
|
70
|
+
if (isForbidden) {
|
|
71
|
+
this.blockCount++;
|
|
72
|
+
|
|
73
|
+
console.log(chalk.red('\nšØ ONYX TOOL BLOCK TRIGGERED'));
|
|
74
|
+
console.log(chalk.dim('ā'.repeat(80)));
|
|
75
|
+
console.log(chalk.white(`Agent: ${chalk.bold(this.agentName)}`));
|
|
76
|
+
console.log(chalk.white(`Forbidden Tool: ${chalk.red(toolName)}`));
|
|
77
|
+
console.log(chalk.white(`Params: ${chalk.dim(JSON.stringify(toolParams, null, 2))}`));
|
|
78
|
+
|
|
79
|
+
console.log(chalk.yellow('\nš ONYX DELEGATION RULE:'));
|
|
80
|
+
console.log(chalk.dim(' ONYX is META-BOSS and NEVER touches files directly'));
|
|
81
|
+
console.log(chalk.dim(' ONYX delegates to Task tool ā Task tool uses file operations'));
|
|
82
|
+
console.log(chalk.red(` FORBIDDEN: ONYX ā ${toolName} (direct file access)`));
|
|
83
|
+
console.log(chalk.green(` ALLOWED: ONYX ā Task ā ${toolName} (delegated access)`));
|
|
84
|
+
|
|
85
|
+
console.log(chalk.dim('\nā'.repeat(80)));
|
|
86
|
+
|
|
87
|
+
// Generate delegation message
|
|
88
|
+
const delegationMessage = this._generateDelegationMessage(toolName, toolParams);
|
|
89
|
+
|
|
90
|
+
// Prompt user for action
|
|
91
|
+
const action = await this._promptBlockedAction(toolName, delegationMessage);
|
|
92
|
+
|
|
93
|
+
if (action.action === 'delegate') {
|
|
94
|
+
this.delegationCount++;
|
|
95
|
+
console.log(chalk.green('\nā
Delegating to Task tool...'));
|
|
96
|
+
console.log(chalk.dim(`Message: ${delegationMessage}`));
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
allowed: false,
|
|
100
|
+
blocked: true,
|
|
101
|
+
reason: 'ONYX cannot use file tools directly - must delegate to Task',
|
|
102
|
+
delegateTo: 'Task',
|
|
103
|
+
delegationMessage,
|
|
104
|
+
toolName,
|
|
105
|
+
toolParams
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (action.action === 'override') {
|
|
110
|
+
// Log violation but allow
|
|
111
|
+
this._recordViolation(toolName, toolParams, action.justification);
|
|
112
|
+
|
|
113
|
+
console.log(chalk.yellow('\nā ļø Override granted - violation logged'));
|
|
114
|
+
console.log(chalk.dim(`Justification: ${action.justification}`));
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
allowed: true,
|
|
118
|
+
override: true,
|
|
119
|
+
reason: `Override: ${action.justification}`,
|
|
120
|
+
violation: true
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (action.action === 'reject') {
|
|
125
|
+
console.log(chalk.red('\nā Tool call rejected - ONYX must delegate'));
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
allowed: false,
|
|
129
|
+
blocked: true,
|
|
130
|
+
rejected: true,
|
|
131
|
+
reason: 'ONYX delegation rule violation - must use Task tool'
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (isAllowed) {
|
|
137
|
+
// Tool is explicitly allowed for ONYX
|
|
138
|
+
return {
|
|
139
|
+
allowed: true,
|
|
140
|
+
reason: `${toolName} is allowed for ONYX`
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Unknown tool - allow but warn
|
|
145
|
+
console.log(chalk.yellow(`\nā ļø Unknown tool for ONYX: ${toolName}`));
|
|
146
|
+
console.log(chalk.dim('Allowing by default - add to ALLOWED_TOOLS or FORBIDDEN_TOOLS'));
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
allowed: true,
|
|
150
|
+
reason: 'Unknown tool - allowed by default',
|
|
151
|
+
warning: true
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Generate Task tool delegation message
|
|
157
|
+
*/
|
|
158
|
+
_generateDelegationMessage(toolName, toolParams) {
|
|
159
|
+
const messages = {
|
|
160
|
+
Read: `Read the file at ${toolParams.file_path || '[file_path]'}`,
|
|
161
|
+
Write: `Write content to ${toolParams.file_path || '[file_path]'}`,
|
|
162
|
+
Edit: `Edit ${toolParams.file_path || '[file_path]'} - replace "${(toolParams.old_string || '').substring(0, 50)}..." with new content`,
|
|
163
|
+
Bash: `Execute bash command: ${toolParams.command || '[command]'}`,
|
|
164
|
+
Grep: `Search for pattern "${toolParams.pattern || '[pattern]'}" in codebase`,
|
|
165
|
+
Glob: `Find files matching pattern "${toolParams.pattern || '[pattern]'}"`
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const baseMessage = messages[toolName] || `Execute ${toolName} tool`;
|
|
169
|
+
|
|
170
|
+
return `ONYX DELEGATION: ${baseMessage}\n\nReason: ONYX is meta-boss and delegates file operations to specialist agents.`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Prompt user when tool is blocked
|
|
175
|
+
*/
|
|
176
|
+
async _promptBlockedAction(toolName, delegationMessage) {
|
|
177
|
+
const choices = [
|
|
178
|
+
{
|
|
179
|
+
name: `ā
Delegate to Task tool (RECOMMENDED)`,
|
|
180
|
+
value: 'delegate',
|
|
181
|
+
short: 'Delegate'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: `ā ļø Override: Allow ONYX to use ${toolName} (logs violation)`,
|
|
185
|
+
value: 'override',
|
|
186
|
+
short: 'Override'
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: `ā Reject: Block this operation entirely`,
|
|
190
|
+
value: 'reject',
|
|
191
|
+
short: 'Reject'
|
|
192
|
+
}
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
const answer = await inquirer.prompt([
|
|
196
|
+
{
|
|
197
|
+
type: 'list',
|
|
198
|
+
name: 'action',
|
|
199
|
+
message: chalk.bold(`ONYX attempted to use ${chalk.red(toolName)}. How to proceed?`),
|
|
200
|
+
choices,
|
|
201
|
+
default: 0
|
|
202
|
+
}
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
if (answer.action === 'override') {
|
|
206
|
+
const justification = await inquirer.prompt([
|
|
207
|
+
{
|
|
208
|
+
type: 'input',
|
|
209
|
+
name: 'justification',
|
|
210
|
+
message: chalk.yellow('Provide justification for override (will be logged):'),
|
|
211
|
+
validate: (input) => {
|
|
212
|
+
if (input.length < 20) {
|
|
213
|
+
return 'Justification must be at least 20 characters';
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
action: 'override',
|
|
222
|
+
justification: justification.justification
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { action: answer.action };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Record violation for audit trail
|
|
231
|
+
*/
|
|
232
|
+
_recordViolation(toolName, toolParams, justification) {
|
|
233
|
+
const violation = {
|
|
234
|
+
timestamp: new Date().toISOString(),
|
|
235
|
+
agent: this.agentName,
|
|
236
|
+
tool: toolName,
|
|
237
|
+
params: toolParams,
|
|
238
|
+
justification,
|
|
239
|
+
severity: 'medium'
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
this.violations.push(violation);
|
|
243
|
+
|
|
244
|
+
return violation;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get statistics
|
|
249
|
+
*/
|
|
250
|
+
getStats() {
|
|
251
|
+
return {
|
|
252
|
+
blockCount: this.blockCount,
|
|
253
|
+
delegationCount: this.delegationCount,
|
|
254
|
+
violationCount: this.violations.length,
|
|
255
|
+
complianceRate: this.delegationCount / (this.delegationCount + this.violations.length) * 100 || 0,
|
|
256
|
+
violations: this.violations
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Print report
|
|
262
|
+
*/
|
|
263
|
+
printReport() {
|
|
264
|
+
const stats = this.getStats();
|
|
265
|
+
|
|
266
|
+
console.log(chalk.blue('\nš ONYX TOOL INTERCEPTOR REPORT'));
|
|
267
|
+
console.log(chalk.dim('ā'.repeat(80)));
|
|
268
|
+
console.log(chalk.white(`Total Blocks: ${chalk.magenta(stats.blockCount)}`));
|
|
269
|
+
console.log(chalk.white(`Delegations: ${chalk.green(stats.delegationCount)}`));
|
|
270
|
+
console.log(chalk.white(`Violations: ${chalk.red(stats.violationCount)}`));
|
|
271
|
+
console.log(chalk.white(`Compliance Rate: ${chalk.cyan(stats.complianceRate.toFixed(1) + '%')}`));
|
|
272
|
+
console.log(chalk.dim('ā'.repeat(80)));
|
|
273
|
+
|
|
274
|
+
if (stats.violations.length > 0) {
|
|
275
|
+
console.log(chalk.yellow('\nā ļø VIOLATIONS:'));
|
|
276
|
+
stats.violations.forEach((v, i) => {
|
|
277
|
+
console.log(chalk.dim(`\n${i + 1}. ${v.tool} - ${v.timestamp}`));
|
|
278
|
+
console.log(chalk.dim(` Justification: ${v.justification}`));
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Enable/disable interceptor
|
|
285
|
+
*/
|
|
286
|
+
setEnabled(enabled) {
|
|
287
|
+
this.enabled = enabled;
|
|
288
|
+
console.log(chalk.cyan(`\nONYX Tool Interceptor: ${enabled ? 'ENABLED' : 'DISABLED'}`));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Reset counters
|
|
293
|
+
*/
|
|
294
|
+
reset() {
|
|
295
|
+
this.blockCount = 0;
|
|
296
|
+
this.delegationCount = 0;
|
|
297
|
+
this.violations = [];
|
|
298
|
+
console.log(chalk.green('\nā
ONYX Tool Interceptor reset'));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Singleton instance
|
|
303
|
+
const interceptor = new OnyxToolInterceptor();
|
|
304
|
+
|
|
305
|
+
export default interceptor;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* INTEGRATION EXAMPLE:
|
|
309
|
+
*
|
|
310
|
+
* // In ONYX agent wrapper or Claude Code integration:
|
|
311
|
+
*
|
|
312
|
+
* import onyxInterceptor from '@cpretzinger/boss-claude/lib/onyx-tool-interceptor.js';
|
|
313
|
+
*
|
|
314
|
+
* async function executeToolCall(toolName, toolParams) {
|
|
315
|
+
* // PRE-HOOK: Intercept before execution
|
|
316
|
+
* const result = await onyxInterceptor.interceptToolCall(toolName, toolParams);
|
|
317
|
+
*
|
|
318
|
+
* if (!result.allowed) {
|
|
319
|
+
* if (result.delegateTo === 'Task') {
|
|
320
|
+
* // Auto-delegate to Task tool
|
|
321
|
+
* return await executeTaskTool({
|
|
322
|
+
* description: result.delegationMessage,
|
|
323
|
+
* context: { originalTool: toolName, originalParams: toolParams }
|
|
324
|
+
* });
|
|
325
|
+
* }
|
|
326
|
+
*
|
|
327
|
+
* // Tool call blocked
|
|
328
|
+
* throw new Error(result.reason);
|
|
329
|
+
* }
|
|
330
|
+
*
|
|
331
|
+
* if (result.override) {
|
|
332
|
+
* console.warn('ONYX delegation override - violation logged');
|
|
333
|
+
* }
|
|
334
|
+
*
|
|
335
|
+
* // Execute original tool
|
|
336
|
+
* return await executeTool(toolName, toolParams);
|
|
337
|
+
* }
|
|
338
|
+
*
|
|
339
|
+
* // Print report at end of session
|
|
340
|
+
* onyxInterceptor.printReport();
|
|
341
|
+
*/
|