Skip to content

Commit 62b6334

Browse files
committed
Bump version to 0.9.0
1 parent 026ed43 commit 62b6334

File tree

4 files changed

+800
-31
lines changed

4 files changed

+800
-31
lines changed

src/core/task/AutoApprovalHandler.ts

Lines changed: 349 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,59 @@ export interface AutoApprovalResult {
99
approvalCount?: number | string
1010
}
1111

12+
interface TaskComplexityMetrics {
13+
toolUsageCount: number
14+
errorCount: number
15+
fileOperationCount: number
16+
terminalCommandCount: number
17+
totalMessageLength: number
18+
messageCount: number
19+
averageMessageLength: number
20+
}
21+
22+
interface RiskAssessment {
23+
riskLevel: "low" | "medium" | "high"
24+
confidence: number
25+
riskScore: number
26+
}
27+
28+
interface OperationPattern {
29+
operationType: string
30+
successCount: number
31+
failureCount: number
32+
averageExecutionTime: number
33+
lastExecuted: number
34+
riskLevel: "low" | "medium" | "high"
35+
confidence: number
36+
}
37+
1238
export class AutoApprovalHandler {
1339
private consecutiveAutoApprovedRequestsCount: number = 0
1440
private consecutiveAutoApprovedCost: number = 0
41+
private autonomousModeEnabled: boolean = true
42+
private confidenceThreshold: number = 0.8 // Confidence threshold for autonomous decisions
43+
private operationHistory: Map<string, OperationPattern> = new Map()
44+
private autonomousSuccessRate: number = 0.95 // Track success rate of autonomous operations
45+
private lastAutonomousDecision: number = 0
46+
private autonomousCooldownMs: number = 1000 // 1 second cooldown between autonomous decisions
47+
48+
/**
49+
* Enable or disable autonomous mode
50+
*/
51+
setAutonomousMode(enabled: boolean): void {
52+
this.autonomousModeEnabled = enabled
53+
}
54+
55+
/**
56+
* Set confidence threshold for autonomous decisions
57+
*/
58+
setConfidenceThreshold(threshold: number): void {
59+
this.confidenceThreshold = Math.max(0.1, Math.min(1.0, threshold))
60+
}
1561

1662
/**
1763
* Check if auto-approval limits have been reached and handle user approval if needed
64+
* Enhanced with autonomous decision making
1865
*/
1966
async checkAutoApprovalLimits(
2067
state: GlobalState | undefined,
@@ -24,17 +71,138 @@ export class AutoApprovalHandler {
2471
data: string,
2572
) => Promise<{ response: ClineAskResponse; text?: string; images?: string[] }>,
2673
): Promise<AutoApprovalResult> {
27-
// Check request count limit
74+
// If autonomous mode is enabled, use enhanced logic
75+
if (this.autonomousModeEnabled) {
76+
return this.checkAutonomousLimits(state, messages, askForApproval)
77+
}
78+
79+
// Fallback to original logic
80+
const requestResult = await this.checkRequestLimit(state, askForApproval)
81+
if (!requestResult.shouldProceed || requestResult.requiresApproval) {
82+
return requestResult
83+
}
84+
85+
const costResult = await this.checkCostLimit(state, messages, askForApproval)
86+
return costResult
87+
}
88+
89+
/**
90+
* Enhanced autonomous approval logic
91+
*/
92+
private async checkAutonomousLimits(
93+
state: GlobalState | undefined,
94+
messages: ClineMessage[],
95+
askForApproval: (
96+
type: ClineAsk,
97+
data: string,
98+
) => Promise<{ response: ClineAskResponse; text?: string; images?: string[] }>,
99+
): Promise<AutoApprovalResult> {
100+
// Calculate task complexity and risk factors
101+
const taskMetrics = this.analyzeTaskComplexity(messages)
102+
const riskAssessment = this.assessRiskLevel(taskMetrics, state)
103+
104+
// If risk is low and confidence is high, proceed autonomously
105+
if (riskAssessment.riskLevel === "low" && riskAssessment.confidence >= this.confidenceThreshold) {
106+
this.consecutiveAutoApprovedRequestsCount++
107+
return { shouldProceed: true, requiresApproval: false }
108+
}
109+
110+
// If risk is medium and we haven't exceeded smart limits, proceed
111+
if (riskAssessment.riskLevel === "medium" && !this.hasExceededSmartLimits(taskMetrics, state)) {
112+
this.consecutiveAutoApprovedRequestsCount++
113+
return { shouldProceed: true, requiresApproval: false }
114+
}
115+
116+
// For high risk or when smart limits are exceeded, ask for approval
28117
const requestResult = await this.checkRequestLimit(state, askForApproval)
29118
if (!requestResult.shouldProceed || requestResult.requiresApproval) {
30119
return requestResult
31120
}
32121

33-
// Check cost limit
34122
const costResult = await this.checkCostLimit(state, messages, askForApproval)
35123
return costResult
36124
}
37125

126+
/**
127+
* Analyze task complexity based on message patterns
128+
*/
129+
private analyzeTaskComplexity(messages: ClineMessage[]): TaskComplexityMetrics {
130+
let toolUsageCount = 0
131+
let errorCount = 0
132+
let fileOperationCount = 0
133+
let terminalCommandCount = 0
134+
let totalMessageLength = 0
135+
136+
for (const message of messages) {
137+
if (message.type === "say") {
138+
totalMessageLength += (message.text || "").length
139+
140+
// Count different types of operations based on actual ClineSay values
141+
if (message.say === "text") toolUsageCount++ // General tool usage
142+
if (message.say === "error") errorCount++
143+
if (message.say === "command_output") terminalCommandCount++
144+
if (message.say === "browser_action" || message.say === "browser_action_result") fileOperationCount++
145+
} else if (message.type === "ask") {
146+
// Count ask types that indicate tool usage
147+
if (message.ask === "tool") toolUsageCount++
148+
if (message.ask === "command") terminalCommandCount++
149+
if (message.ask === "browser_action_launch") fileOperationCount++
150+
}
151+
}
152+
153+
return {
154+
toolUsageCount,
155+
errorCount,
156+
fileOperationCount,
157+
terminalCommandCount,
158+
totalMessageLength,
159+
messageCount: messages.length,
160+
averageMessageLength: totalMessageLength / Math.max(1, messages.length),
161+
}
162+
}
163+
164+
/**
165+
* Assess risk level based on task metrics and state
166+
*/
167+
private assessRiskLevel(metrics: TaskComplexityMetrics, state: GlobalState | undefined): RiskAssessment {
168+
let riskScore = 0
169+
let confidence = 0.5
170+
171+
// Risk factors
172+
if (metrics.errorCount > 3) riskScore += 0.3
173+
if (metrics.toolUsageCount > 10) riskScore += 0.2
174+
if (metrics.fileOperationCount > 5) riskScore += 0.2
175+
if (metrics.terminalCommandCount > 8) riskScore += 0.3
176+
177+
// Confidence factors (higher confidence = lower risk)
178+
if (metrics.messageCount > 20) confidence += 0.2 // Established pattern
179+
if (metrics.errorCount === 0) confidence += 0.3 // No errors = high confidence
180+
if (metrics.averageMessageLength > 100) confidence += 0.1 // Detailed responses
181+
182+
// State-based adjustments
183+
if (state?.autoApprovalEnabled) confidence += 0.2
184+
185+
const riskLevel = riskScore > 0.5 ? "high" : riskScore > 0.2 ? "medium" : "low"
186+
187+
return { riskLevel, confidence: Math.min(1.0, confidence), riskScore }
188+
}
189+
190+
/**
191+
* Check if smart limits have been exceeded
192+
*/
193+
private hasExceededSmartLimits(metrics: TaskComplexityMetrics, state: GlobalState | undefined): boolean {
194+
const maxRequests = state?.allowedMaxRequests || 50 // Increased default
195+
const maxCost = state?.allowedMaxCost || 10.0 // Increased default
196+
197+
// Smart limits based on task complexity
198+
const complexityMultiplier = Math.max(1, metrics.errorCount * 0.5 + metrics.toolUsageCount * 0.1)
199+
200+
return (
201+
this.consecutiveAutoApprovedRequestsCount > maxRequests * complexityMultiplier ||
202+
this.consecutiveAutoApprovedCost > maxCost * complexityMultiplier
203+
)
204+
}
205+
38206
/**
39207
* Increment the request counter and check if limit is exceeded
40208
*/
@@ -141,4 +309,183 @@ export class AutoApprovalHandler {
141309
currentCost: this.consecutiveAutoApprovedCost,
142310
}
143311
}
312+
313+
/**
314+
* Record the result of an autonomous operation for learning
315+
*/
316+
recordAutonomousOperation(operationType: string, success: boolean, executionTime: number = 0): void {
317+
const pattern = this.operationHistory.get(operationType) || {
318+
operationType,
319+
successCount: 0,
320+
failureCount: 0,
321+
averageExecutionTime: 0,
322+
lastExecuted: Date.now(),
323+
riskLevel: "medium" as const,
324+
confidence: 0.5,
325+
}
326+
327+
if (success) {
328+
pattern.successCount++
329+
} else {
330+
pattern.failureCount++
331+
}
332+
333+
// Update average execution time
334+
const totalOperations = pattern.successCount + pattern.failureCount
335+
pattern.averageExecutionTime =
336+
(pattern.averageExecutionTime * (totalOperations - 1) + executionTime) / totalOperations
337+
pattern.lastExecuted = Date.now()
338+
339+
// Update confidence based on success rate
340+
const successRate = pattern.successCount / totalOperations
341+
pattern.confidence = Math.min(1.0, successRate * 0.8 + 0.2) // Base confidence of 0.2
342+
343+
// Update risk level based on failure rate
344+
const failureRate = pattern.failureCount / totalOperations
345+
if (failureRate < 0.1) {
346+
pattern.riskLevel = "low"
347+
} else if (failureRate < 0.3) {
348+
pattern.riskLevel = "medium"
349+
} else {
350+
pattern.riskLevel = "high"
351+
}
352+
353+
this.operationHistory.set(operationType, pattern)
354+
355+
// Update overall autonomous success rate
356+
const totalSuccesses = Array.from(this.operationHistory.values()).reduce((sum, p) => sum + p.successCount, 0)
357+
const totalFailures = Array.from(this.operationHistory.values()).reduce((sum, p) => sum + p.failureCount, 0)
358+
const totalOperationsAll = totalSuccesses + totalFailures
359+
if (totalOperationsAll > 0) {
360+
this.autonomousSuccessRate = totalSuccesses / totalOperationsAll
361+
}
362+
}
363+
364+
/**
365+
* Check if an operation can be performed autonomously based on learned patterns
366+
*/
367+
canPerformAutonomously(operationType: string): boolean {
368+
// Check cooldown
369+
const now = Date.now()
370+
if (now - this.lastAutonomousDecision < this.autonomousCooldownMs) {
371+
return false
372+
}
373+
374+
const pattern = this.operationHistory.get(operationType)
375+
if (!pattern) {
376+
// New operation - be conservative
377+
return false
378+
}
379+
380+
// Check if operation has been performed recently and successfully
381+
const timeSinceLastExecution = now - pattern.lastExecuted
382+
const recentExecution = timeSinceLastExecution < 300000 // 5 minutes
383+
384+
// Allow autonomous execution if:
385+
// 1. High confidence (> 0.8)
386+
// 2. Low risk level
387+
// 3. Recent successful execution
388+
// 4. Overall autonomous success rate is good
389+
const autonomousCriteria =
390+
pattern.confidence >= this.confidenceThreshold &&
391+
pattern.riskLevel === "low" &&
392+
recentExecution &&
393+
this.autonomousSuccessRate >= 0.85
394+
395+
if (autonomousCriteria) {
396+
this.lastAutonomousDecision = now
397+
}
398+
399+
return autonomousCriteria
400+
}
401+
402+
/**
403+
* Get operation pattern statistics for debugging
404+
*/
405+
getOperationPatterns(): Record<string, OperationPattern> {
406+
return Object.fromEntries(this.operationHistory)
407+
}
408+
409+
/**
410+
* Reset operation history (useful for testing or when user preferences change)
411+
*/
412+
resetOperationHistory(): void {
413+
this.operationHistory.clear()
414+
this.autonomousSuccessRate = 0.95
415+
this.lastAutonomousDecision = 0
416+
}
417+
418+
/**
419+
* Get autonomous mode statistics
420+
*/
421+
getAutonomousStats(): {
422+
overallSuccessRate: number
423+
totalOperations: number
424+
autonomousModeEnabled: boolean
425+
confidenceThreshold: number
426+
cooldownMs: number
427+
} {
428+
const totalSuccesses = Array.from(this.operationHistory.values()).reduce((sum, p) => sum + p.successCount, 0)
429+
const totalFailures = Array.from(this.operationHistory.values()).reduce((sum, p) => sum + p.failureCount, 0)
430+
431+
return {
432+
overallSuccessRate: this.autonomousSuccessRate,
433+
totalOperations: totalSuccesses + totalFailures,
434+
autonomousModeEnabled: this.autonomousModeEnabled,
435+
confidenceThreshold: this.confidenceThreshold,
436+
cooldownMs: this.autonomousCooldownMs,
437+
}
438+
}
439+
440+
/**
441+
* Adjust confidence threshold based on user feedback
442+
*/
443+
adjustConfidenceThreshold(feedback: "too_many_approvals" | "too_few_approvals"): void {
444+
const adjustment = 0.05 // 5% adjustment
445+
446+
if (feedback === "too_many_approvals") {
447+
// User thinks we're approving too much - increase threshold (be more conservative)
448+
this.confidenceThreshold = Math.min(0.95, this.confidenceThreshold + adjustment)
449+
} else if (feedback === "too_few_approvals") {
450+
// User thinks we're asking for approval too often - decrease threshold (be more permissive)
451+
this.confidenceThreshold = Math.max(0.3, this.confidenceThreshold - adjustment)
452+
}
453+
}
454+
455+
/**
456+
* Enhanced risk assessment with pattern recognition
457+
*/
458+
private assessRiskWithPatterns(operationType: string, baseRisk: RiskAssessment): RiskAssessment {
459+
const pattern = this.operationHistory.get(operationType)
460+
461+
if (!pattern) {
462+
return baseRisk
463+
}
464+
465+
// Adjust risk based on operation history
466+
let adjustedRiskScore = baseRisk.riskScore
467+
let adjustedConfidence = baseRisk.confidence
468+
469+
// If operation has high success rate, reduce risk
470+
if (pattern.confidence > 0.9) {
471+
adjustedRiskScore *= 0.7 // Reduce risk by 30%
472+
adjustedConfidence += 0.1
473+
}
474+
475+
// If operation has been recently successful, increase confidence
476+
const timeSinceLastExecution = Date.now() - pattern.lastExecuted
477+
if (timeSinceLastExecution < 60000 && pattern.successCount > pattern.failureCount) {
478+
// Last minute and more successes
479+
adjustedConfidence += 0.15
480+
}
481+
482+
// Recalculate risk level
483+
const newRiskLevel = adjustedRiskScore > 0.5 ? "high" : adjustedRiskScore > 0.2 ? "medium" : "low"
484+
485+
return {
486+
riskLevel: newRiskLevel as "low" | "medium" | "high",
487+
confidence: Math.min(1.0, adjustedConfidence),
488+
riskScore: adjustedRiskScore,
489+
}
490+
}
144491
}

0 commit comments

Comments
 (0)