@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,235 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* TASK AGENT WORKER
|
|
4
|
+
*
|
|
5
|
+
* Executes delegated operations via Task tool.
|
|
6
|
+
* Handles the actual execution of auto-delegated tool calls.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Task Agent Worker Class
|
|
13
|
+
*
|
|
14
|
+
* Processes delegated tool operations by calling Task tool
|
|
15
|
+
*/
|
|
16
|
+
export class TaskAgentWorker {
|
|
17
|
+
constructor(config = {}) {
|
|
18
|
+
this.taskExecutor = config.taskExecutor || null;
|
|
19
|
+
this.executionLog = [];
|
|
20
|
+
this.successCount = 0;
|
|
21
|
+
this.failureCount = 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Execute delegated tool call via Task
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} delegation - Delegation object from tool-wrapper
|
|
28
|
+
* @returns {Promise<Object>} Execution result
|
|
29
|
+
*/
|
|
30
|
+
async executeDelegation(delegation) {
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
|
|
33
|
+
console.log(chalk.cyan('\n📤 Task Agent Processing Delegation'));
|
|
34
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
35
|
+
console.log(chalk.white(`Tool: ${chalk.bold(delegation.originalTool)}`));
|
|
36
|
+
console.log(chalk.white(`Description: ${delegation.description}`));
|
|
37
|
+
console.log(chalk.dim(`Strategy: ${delegation.strategy}`));
|
|
38
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// Execute via Task tool
|
|
42
|
+
const result = await this._executeViaTask(delegation);
|
|
43
|
+
|
|
44
|
+
this.successCount++;
|
|
45
|
+
const executionTime = Date.now() - startTime;
|
|
46
|
+
|
|
47
|
+
const logEntry = {
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
delegation,
|
|
50
|
+
result,
|
|
51
|
+
success: true,
|
|
52
|
+
executionTime
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
this.executionLog.push(logEntry);
|
|
56
|
+
|
|
57
|
+
console.log(chalk.green(`✅ Delegation completed (${executionTime}ms)`));
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
success: true,
|
|
61
|
+
delegated: true,
|
|
62
|
+
result,
|
|
63
|
+
executionTime,
|
|
64
|
+
originalTool: delegation.originalTool,
|
|
65
|
+
strategy: delegation.strategy
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
this.failureCount++;
|
|
70
|
+
const executionTime = Date.now() - startTime;
|
|
71
|
+
|
|
72
|
+
const logEntry = {
|
|
73
|
+
timestamp: new Date().toISOString(),
|
|
74
|
+
delegation,
|
|
75
|
+
error: error.message,
|
|
76
|
+
success: false,
|
|
77
|
+
executionTime
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
this.executionLog.push(logEntry);
|
|
81
|
+
|
|
82
|
+
console.log(chalk.red(`❌ Delegation failed: ${error.message}`));
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
delegated: true,
|
|
87
|
+
error: error.message,
|
|
88
|
+
executionTime,
|
|
89
|
+
originalTool: delegation.originalTool,
|
|
90
|
+
strategy: delegation.strategy
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Execute via Task tool
|
|
97
|
+
*/
|
|
98
|
+
async _executeViaTask(delegation) {
|
|
99
|
+
if (!this.taskExecutor) {
|
|
100
|
+
// No task executor provided - return mock result
|
|
101
|
+
console.log(chalk.yellow('⚠️ No task executor configured - returning mock result'));
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
mock: true,
|
|
105
|
+
message: 'Task would be executed here',
|
|
106
|
+
delegation
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Call provided task executor
|
|
111
|
+
if (typeof this.taskExecutor === 'function') {
|
|
112
|
+
const taskParams = {
|
|
113
|
+
description: delegation.description,
|
|
114
|
+
context: {
|
|
115
|
+
originalTool: delegation.originalTool,
|
|
116
|
+
originalParams: delegation.originalParams,
|
|
117
|
+
delegatedBy: 'ToolWrapper',
|
|
118
|
+
strategy: delegation.strategy,
|
|
119
|
+
details: delegation.details
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return await this.taskExecutor(taskParams);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new Error('Invalid task executor - must be a function');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Set task executor function
|
|
131
|
+
*/
|
|
132
|
+
setTaskExecutor(executor) {
|
|
133
|
+
if (typeof executor !== 'function') {
|
|
134
|
+
throw new Error('Task executor must be a function');
|
|
135
|
+
}
|
|
136
|
+
this.taskExecutor = executor;
|
|
137
|
+
console.log(chalk.green('✅ Task executor configured'));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get execution statistics
|
|
142
|
+
*/
|
|
143
|
+
getStats() {
|
|
144
|
+
const total = this.successCount + this.failureCount;
|
|
145
|
+
const successRate = total > 0
|
|
146
|
+
? (this.successCount / total * 100).toFixed(2)
|
|
147
|
+
: 0;
|
|
148
|
+
|
|
149
|
+
const avgExecutionTime = this.executionLog.length > 0
|
|
150
|
+
? this.executionLog.reduce((sum, entry) => sum + entry.executionTime, 0) / this.executionLog.length
|
|
151
|
+
: 0;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
total,
|
|
155
|
+
successCount: this.successCount,
|
|
156
|
+
failureCount: this.failureCount,
|
|
157
|
+
successRate: parseFloat(successRate),
|
|
158
|
+
avgExecutionTime: Math.round(avgExecutionTime),
|
|
159
|
+
recentExecutions: this.executionLog.slice(-10)
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Print execution report
|
|
165
|
+
*/
|
|
166
|
+
printReport() {
|
|
167
|
+
const stats = this.getStats();
|
|
168
|
+
|
|
169
|
+
console.log(chalk.blue('\n📊 TASK AGENT WORKER REPORT'));
|
|
170
|
+
console.log(chalk.dim('═'.repeat(60)));
|
|
171
|
+
console.log(chalk.white(`Total Delegations: ${chalk.magenta(stats.total)}`));
|
|
172
|
+
console.log(chalk.white(`Successful: ${chalk.green(stats.successCount)}`));
|
|
173
|
+
console.log(chalk.white(`Failed: ${chalk.red(stats.failureCount)}`));
|
|
174
|
+
console.log(chalk.white(`Success Rate: ${chalk.cyan(stats.successRate + '%')}`));
|
|
175
|
+
console.log(chalk.white(`Avg Execution Time: ${chalk.yellow(stats.avgExecutionTime + 'ms')}`));
|
|
176
|
+
console.log(chalk.dim('═'.repeat(60)));
|
|
177
|
+
|
|
178
|
+
if (stats.recentExecutions.length > 0) {
|
|
179
|
+
console.log(chalk.white('\nRecent Delegations:'));
|
|
180
|
+
stats.recentExecutions.forEach((exec, i) => {
|
|
181
|
+
const status = exec.success ? chalk.green('✓') : chalk.red('✗');
|
|
182
|
+
const time = chalk.dim(`${exec.executionTime}ms`);
|
|
183
|
+
const tool = exec.delegation.originalTool;
|
|
184
|
+
console.log(` ${status} ${tool} - ${time}`);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get execution log
|
|
191
|
+
*/
|
|
192
|
+
getLog(limit = 20) {
|
|
193
|
+
return this.executionLog.slice(-limit);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Reset worker state
|
|
198
|
+
*/
|
|
199
|
+
reset() {
|
|
200
|
+
this.executionLog = [];
|
|
201
|
+
this.successCount = 0;
|
|
202
|
+
this.failureCount = 0;
|
|
203
|
+
console.log(chalk.green('✅ Task Agent Worker reset'));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Singleton instance
|
|
208
|
+
const taskWorker = new TaskAgentWorker();
|
|
209
|
+
|
|
210
|
+
export default taskWorker;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* INTEGRATION EXAMPLE:
|
|
214
|
+
*
|
|
215
|
+
* import taskWorker from '@cpretzinger/boss-claude/lib/task-agent-worker.js';
|
|
216
|
+
*
|
|
217
|
+
* // Configure task executor
|
|
218
|
+
* taskWorker.setTaskExecutor(async (taskParams) => {
|
|
219
|
+
* // Your Task tool implementation
|
|
220
|
+
* return await executeTaskTool(taskParams);
|
|
221
|
+
* });
|
|
222
|
+
*
|
|
223
|
+
* // Execute delegation
|
|
224
|
+
* const delegation = {
|
|
225
|
+
* description: 'Read config file',
|
|
226
|
+
* originalTool: 'Read',
|
|
227
|
+
* originalParams: { file_path: '/path/to/config.json' },
|
|
228
|
+
* strategy: 'ONYX_AUTO_DELEGATE'
|
|
229
|
+
* };
|
|
230
|
+
*
|
|
231
|
+
* const result = await taskWorker.executeDelegation(delegation);
|
|
232
|
+
*
|
|
233
|
+
* // Print report
|
|
234
|
+
* taskWorker.printReport();
|
|
235
|
+
*/
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
|
|
6
|
+
const BOSS_DIR = join(os.homedir(), '.boss-claude');
|
|
7
|
+
const MONITOR_LOG = join(BOSS_DIR, 'delegation-violations.log');
|
|
8
|
+
const SESSION_FILE = join(BOSS_DIR, 'current-session.json');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* REAL-TIME TOKEN MONITOR
|
|
12
|
+
*
|
|
13
|
+
* Screams "DELEGATION VIOLATION" if ONYX (Boss Claude) burns >100 tokens
|
|
14
|
+
* without using Task tool delegation.
|
|
15
|
+
*
|
|
16
|
+
* RULES:
|
|
17
|
+
* - Boss Claude (ONYX) must delegate tasks >100 tokens to Task tool
|
|
18
|
+
* - Task tool is the primary worker agent for comprehensive searches
|
|
19
|
+
* - Violations are logged and displayed in real-time
|
|
20
|
+
* - Session tracking persists across conversation turns
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export class TokenMonitor {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.threshold = 100;
|
|
26
|
+
this.sessionTokens = 0;
|
|
27
|
+
this.lastDelegation = null;
|
|
28
|
+
this.violations = [];
|
|
29
|
+
this.operations = [];
|
|
30
|
+
this.sessionStartTime = new Date();
|
|
31
|
+
|
|
32
|
+
// Load existing session if available
|
|
33
|
+
this._loadSession();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Start tracking an operation
|
|
38
|
+
* @param {string} operation - Operation description
|
|
39
|
+
* @param {number} estimatedTokens - Estimated token cost
|
|
40
|
+
* @returns {string} Operation ID
|
|
41
|
+
*/
|
|
42
|
+
startOperation(operation, estimatedTokens = 0) {
|
|
43
|
+
const opId = `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
44
|
+
|
|
45
|
+
const op = {
|
|
46
|
+
id: opId,
|
|
47
|
+
operation,
|
|
48
|
+
estimatedTokens,
|
|
49
|
+
startTime: new Date().toISOString(),
|
|
50
|
+
actualTokens: 0,
|
|
51
|
+
delegated: false,
|
|
52
|
+
delegationTool: null,
|
|
53
|
+
completed: false
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
this.operations.push(op);
|
|
57
|
+
|
|
58
|
+
console.log(chalk.cyan(`\n📊 TOKEN MONITOR: Starting operation "${operation}"`));
|
|
59
|
+
console.log(chalk.dim(`Estimated tokens: ${estimatedTokens}`));
|
|
60
|
+
|
|
61
|
+
if (estimatedTokens > this.threshold) {
|
|
62
|
+
console.log(chalk.yellow(`⚠️ WARNING: Operation exceeds ${this.threshold} token threshold`));
|
|
63
|
+
console.log(chalk.yellow(`💡 RECOMMENDATION: Delegate to Task tool for comprehensive work`));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return opId;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Record delegation to Task tool or other agents
|
|
71
|
+
* @param {string} opId - Operation ID
|
|
72
|
+
* @param {string} tool - Tool name (e.g., 'Task', 'Agent', etc.)
|
|
73
|
+
*/
|
|
74
|
+
recordDelegation(opId, tool = 'Task') {
|
|
75
|
+
const op = this.operations.find(o => o.id === opId);
|
|
76
|
+
|
|
77
|
+
if (!op) {
|
|
78
|
+
console.error(chalk.red(`❌ Operation ${opId} not found`));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
op.delegated = true;
|
|
83
|
+
op.delegationTool = tool;
|
|
84
|
+
op.delegationTime = new Date().toISOString();
|
|
85
|
+
this.lastDelegation = new Date();
|
|
86
|
+
|
|
87
|
+
console.log(chalk.green(`✅ Delegation recorded: ${tool}`));
|
|
88
|
+
this._saveSession();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Update actual token usage for an operation
|
|
93
|
+
* @param {string} opId - Operation ID
|
|
94
|
+
* @param {number} tokensUsed - Actual tokens consumed
|
|
95
|
+
*/
|
|
96
|
+
addTokens(opId, tokensUsed) {
|
|
97
|
+
const op = this.operations.find(o => o.id === opId);
|
|
98
|
+
|
|
99
|
+
if (!op) {
|
|
100
|
+
console.error(chalk.red(`❌ Operation ${opId} not found`));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
op.actualTokens += tokensUsed;
|
|
105
|
+
this.sessionTokens += tokensUsed;
|
|
106
|
+
|
|
107
|
+
// Check for violation
|
|
108
|
+
if (!op.delegated && op.actualTokens > this.threshold) {
|
|
109
|
+
this._screamViolation(op);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this._saveSession();
|
|
113
|
+
this._displayStatus(op);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Complete an operation
|
|
118
|
+
* @param {string} opId - Operation ID
|
|
119
|
+
*/
|
|
120
|
+
completeOperation(opId) {
|
|
121
|
+
const op = this.operations.find(o => o.id === opId);
|
|
122
|
+
|
|
123
|
+
if (!op) {
|
|
124
|
+
console.error(chalk.red(`❌ Operation ${opId} not found`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
op.completed = true;
|
|
129
|
+
op.endTime = new Date().toISOString();
|
|
130
|
+
|
|
131
|
+
// Final violation check
|
|
132
|
+
if (!op.delegated && op.actualTokens > this.threshold) {
|
|
133
|
+
this._screamViolation(op);
|
|
134
|
+
} else if (op.delegated) {
|
|
135
|
+
console.log(chalk.green(`\n✅ Operation completed with proper delegation`));
|
|
136
|
+
console.log(chalk.dim(`Tokens used: ${op.actualTokens} | Tool: ${op.delegationTool}`));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this._saveSession();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* SCREAM DELEGATION VIOLATION
|
|
144
|
+
* @param {Object} op - Operation object
|
|
145
|
+
*/
|
|
146
|
+
_screamViolation(op) {
|
|
147
|
+
const violation = {
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
operation: op.operation,
|
|
150
|
+
tokensUsed: op.actualTokens,
|
|
151
|
+
threshold: this.threshold,
|
|
152
|
+
excess: op.actualTokens - this.threshold,
|
|
153
|
+
estimatedTokens: op.estimatedTokens,
|
|
154
|
+
severity: this._calculateSeverity(op.actualTokens)
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
this.violations.push(violation);
|
|
158
|
+
|
|
159
|
+
// SCREAM TO CONSOLE
|
|
160
|
+
console.log('\n' + chalk.bgRed.white.bold('═'.repeat(80)));
|
|
161
|
+
console.log(chalk.bgRed.white.bold('║' + ' '.repeat(78) + '║'));
|
|
162
|
+
console.log(chalk.bgRed.white.bold('║' + ' '.repeat(25) + '🚨 DELEGATION VIOLATION 🚨' + ' '.repeat(26) + '║'));
|
|
163
|
+
console.log(chalk.bgRed.white.bold('║' + ' '.repeat(78) + '║'));
|
|
164
|
+
console.log(chalk.bgRed.white.bold('═'.repeat(80)));
|
|
165
|
+
|
|
166
|
+
console.log(chalk.red.bold('\n⚠️ ONYX BURNED TOKENS WITHOUT DELEGATION'));
|
|
167
|
+
console.log(chalk.dim('─'.repeat(80)));
|
|
168
|
+
console.log(chalk.white(`Operation: ${chalk.yellow(op.operation)}`));
|
|
169
|
+
console.log(chalk.white(`Tokens Used: ${chalk.red.bold(op.actualTokens)} (Threshold: ${this.threshold})`));
|
|
170
|
+
console.log(chalk.white(`Excess: ${chalk.red.bold('+' + violation.excess + ' tokens')}`));
|
|
171
|
+
console.log(chalk.white(`Severity: ${this._getSeverityColor(violation.severity)}`));
|
|
172
|
+
|
|
173
|
+
if (op.estimatedTokens > 0) {
|
|
174
|
+
console.log(chalk.white(`Estimated: ${op.estimatedTokens} | Actual: ${op.actualTokens}`));
|
|
175
|
+
const accuracy = Math.abs(100 - (op.actualTokens / op.estimatedTokens * 100));
|
|
176
|
+
if (accuracy > 20) {
|
|
177
|
+
console.log(chalk.yellow(`⚠️ Estimation was off by ${accuracy.toFixed(1)}%`));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(chalk.dim('\n' + '─'.repeat(80)));
|
|
182
|
+
console.log(chalk.red.bold('CANONICAL PROTOCOL VIOLATED:'));
|
|
183
|
+
console.log(chalk.white(' Rule: ONYX must delegate operations >100 tokens to Task tool'));
|
|
184
|
+
console.log(chalk.white(' Why: Task tool provides comprehensive search & analysis'));
|
|
185
|
+
console.log(chalk.white(' Fix: Use Task tool for multi-step operations'));
|
|
186
|
+
|
|
187
|
+
console.log(chalk.dim('\n' + '─'.repeat(80)));
|
|
188
|
+
console.log(chalk.yellow('RECOMMENDED ACTION:'));
|
|
189
|
+
console.log(chalk.white(' 1. Stop current operation'));
|
|
190
|
+
console.log(chalk.white(' 2. Invoke Task tool with clear instructions'));
|
|
191
|
+
console.log(chalk.white(' 3. Let Task tool handle comprehensive work'));
|
|
192
|
+
console.log(chalk.dim('─'.repeat(80)) + '\n');
|
|
193
|
+
|
|
194
|
+
// Log to file
|
|
195
|
+
this._logViolation(violation);
|
|
196
|
+
this._saveSession();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Display current status for an operation
|
|
201
|
+
*/
|
|
202
|
+
_displayStatus(op) {
|
|
203
|
+
const pct = (op.actualTokens / this.threshold * 100).toFixed(0);
|
|
204
|
+
const color = pct < 50 ? chalk.green : pct < 80 ? chalk.yellow : chalk.red;
|
|
205
|
+
|
|
206
|
+
console.log(color(`Token usage: ${op.actualTokens}/${this.threshold} (${pct}%)`));
|
|
207
|
+
|
|
208
|
+
if (!op.delegated && op.actualTokens > this.threshold * 0.8) {
|
|
209
|
+
console.log(chalk.yellow(`⚠️ Approaching threshold! Consider delegation.`));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Calculate severity of violation
|
|
215
|
+
*/
|
|
216
|
+
_calculateSeverity(tokensUsed) {
|
|
217
|
+
const excess = tokensUsed - this.threshold;
|
|
218
|
+
|
|
219
|
+
if (excess < 100) return 'LOW';
|
|
220
|
+
if (excess < 500) return 'MEDIUM';
|
|
221
|
+
if (excess < 1000) return 'HIGH';
|
|
222
|
+
return 'CRITICAL';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get colored severity indicator
|
|
227
|
+
*/
|
|
228
|
+
_getSeverityColor(severity) {
|
|
229
|
+
const colors = {
|
|
230
|
+
'LOW': chalk.yellow('⚠️ LOW'),
|
|
231
|
+
'MEDIUM': chalk.orange('⚠️ MEDIUM'),
|
|
232
|
+
'HIGH': chalk.red('🚨 HIGH'),
|
|
233
|
+
'CRITICAL': chalk.bgRed.white.bold('💀 CRITICAL')
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return colors[severity] || severity;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Log violation to file
|
|
241
|
+
*/
|
|
242
|
+
_logViolation(violation) {
|
|
243
|
+
const logEntry = [
|
|
244
|
+
`[${violation.timestamp}] VIOLATION`,
|
|
245
|
+
`Operation: ${violation.operation}`,
|
|
246
|
+
`Tokens: ${violation.tokensUsed} (excess: +${violation.excess})`,
|
|
247
|
+
`Severity: ${violation.severity}`,
|
|
248
|
+
`---`
|
|
249
|
+
].join('\n') + '\n';
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
appendFileSync(MONITOR_LOG, logEntry, 'utf8');
|
|
253
|
+
} catch (err) {
|
|
254
|
+
console.error(chalk.dim(`Failed to write violation log: ${err.message}`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Save session state to disk
|
|
260
|
+
*/
|
|
261
|
+
_saveSession() {
|
|
262
|
+
const session = {
|
|
263
|
+
startTime: this.sessionStartTime.toISOString(),
|
|
264
|
+
sessionTokens: this.sessionTokens,
|
|
265
|
+
operations: this.operations,
|
|
266
|
+
violations: this.violations,
|
|
267
|
+
lastUpdated: new Date().toISOString()
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2), 'utf8');
|
|
272
|
+
} catch (err) {
|
|
273
|
+
console.error(chalk.dim(`Failed to save session: ${err.message}`));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Load existing session from disk
|
|
279
|
+
*/
|
|
280
|
+
_loadSession() {
|
|
281
|
+
if (!existsSync(SESSION_FILE)) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const data = readFileSync(SESSION_FILE, 'utf8');
|
|
287
|
+
const session = JSON.parse(data);
|
|
288
|
+
|
|
289
|
+
this.sessionStartTime = new Date(session.startTime);
|
|
290
|
+
this.sessionTokens = session.sessionTokens || 0;
|
|
291
|
+
this.operations = session.operations || [];
|
|
292
|
+
this.violations = session.violations || [];
|
|
293
|
+
|
|
294
|
+
console.log(chalk.dim(`\n📊 Resumed session from ${this.sessionStartTime.toLocaleString()}`));
|
|
295
|
+
console.log(chalk.dim(`Session tokens: ${this.sessionTokens}`));
|
|
296
|
+
|
|
297
|
+
if (this.violations.length > 0) {
|
|
298
|
+
console.log(chalk.yellow(`⚠️ ${this.violations.length} violation(s) in this session`));
|
|
299
|
+
}
|
|
300
|
+
} catch (err) {
|
|
301
|
+
console.error(chalk.dim(`Failed to load session: ${err.message}`));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get session summary
|
|
307
|
+
*/
|
|
308
|
+
getSessionSummary() {
|
|
309
|
+
const completedOps = this.operations.filter(o => o.completed);
|
|
310
|
+
const delegatedOps = this.operations.filter(o => o.delegated);
|
|
311
|
+
const totalTokens = this.sessionTokens;
|
|
312
|
+
const violationCount = this.violations.length;
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
duration: Date.now() - this.sessionStartTime.getTime(),
|
|
316
|
+
operations: {
|
|
317
|
+
total: this.operations.length,
|
|
318
|
+
completed: completedOps.length,
|
|
319
|
+
delegated: delegatedOps.length,
|
|
320
|
+
delegationRate: (delegatedOps.length / this.operations.length * 100).toFixed(1) + '%'
|
|
321
|
+
},
|
|
322
|
+
tokens: {
|
|
323
|
+
total: totalTokens,
|
|
324
|
+
average: totalTokens / this.operations.length || 0
|
|
325
|
+
},
|
|
326
|
+
violations: {
|
|
327
|
+
count: violationCount,
|
|
328
|
+
severity: this._getViolationBreakdown()
|
|
329
|
+
},
|
|
330
|
+
compliance: violationCount === 0 ? '✅ COMPLIANT' : `❌ ${violationCount} VIOLATION(S)`
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Display formatted session summary
|
|
336
|
+
*/
|
|
337
|
+
displaySummary() {
|
|
338
|
+
const summary = this.getSessionSummary();
|
|
339
|
+
|
|
340
|
+
console.log(chalk.blue('\n═'.repeat(80)));
|
|
341
|
+
console.log(chalk.blue.bold(' TOKEN MONITOR SESSION SUMMARY'));
|
|
342
|
+
console.log(chalk.blue('═'.repeat(80)));
|
|
343
|
+
|
|
344
|
+
console.log(chalk.white('\n📊 OPERATIONS:'));
|
|
345
|
+
console.log(chalk.dim(` Total: ${summary.operations.total}`));
|
|
346
|
+
console.log(chalk.dim(` Completed: ${summary.operations.completed}`));
|
|
347
|
+
console.log(chalk.dim(` Delegated: ${summary.operations.delegated} (${summary.operations.delegationRate})`));
|
|
348
|
+
|
|
349
|
+
console.log(chalk.white('\n💰 TOKENS:'));
|
|
350
|
+
console.log(chalk.dim(` Total: ${summary.tokens.total.toLocaleString()}`));
|
|
351
|
+
console.log(chalk.dim(` Average: ${summary.tokens.average.toFixed(0)} per operation`));
|
|
352
|
+
|
|
353
|
+
console.log(chalk.white('\n🚨 VIOLATIONS:'));
|
|
354
|
+
if (summary.violations.count === 0) {
|
|
355
|
+
console.log(chalk.green(' ✅ No violations - COMPLIANT'));
|
|
356
|
+
} else {
|
|
357
|
+
console.log(chalk.red(` ❌ Total: ${summary.violations.count}`));
|
|
358
|
+
Object.entries(summary.violations.severity).forEach(([severity, count]) => {
|
|
359
|
+
console.log(chalk.dim(` ${severity}: ${count}`));
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log(chalk.white('\n🎯 COMPLIANCE:'));
|
|
364
|
+
console.log(summary.violations.count === 0 ?
|
|
365
|
+
chalk.green.bold(' ✅ FULLY COMPLIANT') :
|
|
366
|
+
chalk.red.bold(` ❌ ${summary.violations.count} VIOLATION(S) LOGGED`)
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
console.log(chalk.blue('\n═'.repeat(80)) + '\n');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get breakdown of violations by severity
|
|
374
|
+
*/
|
|
375
|
+
_getViolationBreakdown() {
|
|
376
|
+
const breakdown = { LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0 };
|
|
377
|
+
|
|
378
|
+
this.violations.forEach(v => {
|
|
379
|
+
breakdown[v.severity]++;
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return breakdown;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Reset session (start fresh)
|
|
387
|
+
*/
|
|
388
|
+
resetSession() {
|
|
389
|
+
this.sessionTokens = 0;
|
|
390
|
+
this.operations = [];
|
|
391
|
+
this.violations = [];
|
|
392
|
+
this.sessionStartTime = new Date();
|
|
393
|
+
this.lastDelegation = null;
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
if (existsSync(SESSION_FILE)) {
|
|
397
|
+
writeFileSync(SESSION_FILE, JSON.stringify({
|
|
398
|
+
startTime: this.sessionStartTime.toISOString(),
|
|
399
|
+
sessionTokens: 0,
|
|
400
|
+
operations: [],
|
|
401
|
+
violations: [],
|
|
402
|
+
lastUpdated: new Date().toISOString()
|
|
403
|
+
}, null, 2), 'utf8');
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
console.error(chalk.dim(`Failed to reset session: ${err.message}`));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
console.log(chalk.green('\n✅ Session reset - starting fresh'));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Quick check: Should this operation be delegated?
|
|
414
|
+
* @param {number} estimatedTokens - Estimated token cost
|
|
415
|
+
* @returns {boolean}
|
|
416
|
+
*/
|
|
417
|
+
shouldDelegate(estimatedTokens) {
|
|
418
|
+
return estimatedTokens > this.threshold;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Singleton instance
|
|
423
|
+
const monitor = new TokenMonitor();
|
|
424
|
+
|
|
425
|
+
export default monitor;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* USAGE EXAMPLES:
|
|
429
|
+
*
|
|
430
|
+
* 1. Start an operation:
|
|
431
|
+
* const opId = tokenMonitor.startOperation('Search codebase for patterns', 150);
|
|
432
|
+
*
|
|
433
|
+
* 2. Record delegation (if using Task tool):
|
|
434
|
+
* tokenMonitor.recordDelegation(opId, 'Task');
|
|
435
|
+
*
|
|
436
|
+
* 3. Update tokens as work progresses:
|
|
437
|
+
* tokenMonitor.addTokens(opId, 50);
|
|
438
|
+
* tokenMonitor.addTokens(opId, 75);
|
|
439
|
+
*
|
|
440
|
+
* 4. Complete operation:
|
|
441
|
+
* tokenMonitor.completeOperation(opId);
|
|
442
|
+
*
|
|
443
|
+
* 5. Display session summary:
|
|
444
|
+
* tokenMonitor.displaySummary();
|
|
445
|
+
*
|
|
446
|
+
* 6. Check if delegation needed:
|
|
447
|
+
* if (tokenMonitor.shouldDelegate(200)) {
|
|
448
|
+
* // Use Task tool
|
|
449
|
+
* }
|
|
450
|
+
*
|
|
451
|
+
* INTEGRATION WITH HIERARCHY VALIDATOR:
|
|
452
|
+
*
|
|
453
|
+
* import tokenMonitor from './token-monitor.js';
|
|
454
|
+
* import hierarchyValidator from './hierarchy-validator.js';
|
|
455
|
+
*
|
|
456
|
+
* // Before starting work
|
|
457
|
+
* const opId = tokenMonitor.startOperation('Complex analysis', 500);
|
|
458
|
+
*
|
|
459
|
+
* if (tokenMonitor.shouldDelegate(500)) {
|
|
460
|
+
* const delegation = await hierarchyValidator.checkDelegation('Complex analysis', 500);
|
|
461
|
+
* if (delegation.shouldDelegate) {
|
|
462
|
+
* tokenMonitor.recordDelegation(opId, delegation.agent);
|
|
463
|
+
* // Delegate to specialist
|
|
464
|
+
* }
|
|
465
|
+
* }
|
|
466
|
+
*/
|