File size: 7,872 Bytes
34367da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**

 * ╔═══════════════════════════════════════════════════════════════════════════╗

 * β•‘                    APPROVAL GATE - Claude as Delegate                     β•‘

 * ║═══════════════════════════════════════════════════════════════════════════║

 * β•‘  Claude har fΓ₯et delegeret godkendelsesautoritet fra CLAK                 β•‘

 * β•‘                                                                           β•‘

 * β•‘  APPROVAL FLOW:                                                           β•‘

 * β•‘  Gemini/DeepSeek β†’ Claude (auto-review) β†’ Execute                        β•‘

 * β•‘                                                                           β•‘

 * β•‘  ESCALATE TO CLAK ONLY FOR:                                              β•‘

 * β•‘  β€’ Production deployments                                                 β•‘

 * β•‘  β€’ API key/credential changes                                            β•‘

 * β•‘  β€’ Budget/cost decisions                                                  β•‘

 * β•‘  β€’ Security-sensitive operations                                          β•‘

 * β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

 */

import { AgentId } from './types.js';

export type ApprovalDecision = 'approved' | 'rejected' | 'escalate_to_clak';

export interface ApprovalResult {
    decision: ApprovalDecision;
    reason: string;
    approvedBy: AgentId;
    timestamp: string;
    modifications?: string[];  // Claude kan modificere requests
}

// Only these require CLAK personally
const ESCALATE_TO_CLAK = [
    'production_deploy',
    'api_key_create',
    'credential_change',
    'budget_decision',
    'security_config',
    'delete_data',
    'external_integration'
];

class ApprovalGate {
    private static instance: ApprovalGate;

    public static getInstance(): ApprovalGate {
        if (!ApprovalGate.instance) {
            ApprovalGate.instance = new ApprovalGate();
        }
        return ApprovalGate.instance;
    }

    /**

     * Claude reviews and decides on behalf of CLAK

     * Returns immediately - no waiting for human

     */
    review(params: {
        fromAgent: AgentId;
        action: string;
        description: string;
        context?: Record<string, any>;
    }): ApprovalResult {
        const { fromAgent, action, description } = params;
        const timestamp = new Date().toISOString();

        // CLAK's own requests are auto-approved
        if (fromAgent === 'clak') {
            return {
                decision: 'approved',
                reason: 'CLAK authority - auto-approved',
                approvedBy: 'clak',
                timestamp
            };
        }

        // Check if needs escalation to CLAK
        const needsEscalation = ESCALATE_TO_CLAK.some(e => 
            action.toLowerCase().includes(e) || 
            description.toLowerCase().includes(e)
        );

        if (needsEscalation) {
            return {
                decision: 'escalate_to_clak',
                reason: `Action "${action}" requires CLAK's personal approval`,
                approvedBy: 'claude',
                timestamp
            };
        }

        // Claude reviews based on reasonableness
        const review = this.evaluateRequest(params);
        
        return {
            decision: review.approved ? 'approved' : 'rejected',
            reason: review.reason,
            approvedBy: 'claude',
            timestamp,
            modifications: review.modifications
        };
    }

    /**

     * Claude's evaluation logic

     */
    private evaluateRequest(params: {
        fromAgent: AgentId;
        action: string;
        description: string;
        context?: Record<string, any>;
    }): { approved: boolean; reason: string; modifications?: string[] } {
        const { fromAgent, action, description } = params;

        // ════════════════════════════════════════════════════════════
        // APPROVAL RULES (Claude's delegated authority)
        // ════════════════════════════════════════════════════════════

        // 1. Research requests - always approved
        if (action.includes('research') || action.includes('search') || action.includes('analyze')) {
            return { 
                approved: true, 
                reason: 'Research/analysis tasks are pre-approved' 
            };
        }

        // 2. Code review/suggestions - approved
        if (action.includes('review') || action.includes('suggest') || action.includes('recommend')) {
            return { 
                approved: true, 
                reason: 'Review and recommendations are welcome' 
            };
        }

        // 3. Documentation - approved
        if (action.includes('document') || action.includes('readme') || action.includes('comment')) {
            return { 
                approved: true, 
                reason: 'Documentation improvements are pre-approved' 
            };
        }

        // 4. Questions/clarifications - approved
        if (action.includes('question') || action.includes('clarify') || action.includes('ask')) {
            return { 
                approved: true, 
                reason: 'Questions and clarifications are encouraged' 
            };
        }

        // 5. Status updates - approved
        if (action.includes('status') || action.includes('update') || action.includes('report')) {
            return { 
                approved: true, 
                reason: 'Status updates are pre-approved' 
            };
        }

        // 6. Code generation for review - approved with note
        if (action.includes('generate') || action.includes('create') || action.includes('implement')) {
            return { 
                approved: true, 
                reason: 'Code generation approved - will be reviewed before merge',
                modifications: ['Output requires Claude code review before integration']
            };
        }

        // 7. Test generation - always approved
        if (action.includes('test')) {
            return { 
                approved: true, 
                reason: 'Test creation is always welcome' 
            };
        }

        // Default: Approve with logging
        console.log(`⚠️ [ApprovalGate] New action type from ${fromAgent}: ${action}`);
        return { 
            approved: true, 
            reason: `Approved by Claude (delegated authority) - action: ${action}`,
            modifications: ['New action type - logged for pattern review']
        };
    }

    /**

     * Quick check without full review

     */
    canProceed(fromAgent: AgentId, action: string): boolean {
        if (fromAgent === 'clak' || fromAgent === 'claude') return true;
        
        const needsEscalation = ESCALATE_TO_CLAK.some(e => 
            action.toLowerCase().includes(e)
        );
        
        return !needsEscalation;
    }
}

export const approvalGate = ApprovalGate.getInstance();