File size: 1,365 Bytes
06c3755
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// ADR-002 + ADR-009: guards are pure functions, configured, no hot-plug, no defense layers.
// A guard returns a "hit" only when its threshold is exceeded (strict greater-than).
// Multiple guards can hit at the same time; runGuards returns them all.

import type { Metrics } from './metrics.js'
import type { ShellConfig } from './config.js'

export type GuardId = 'token' | 'iter' | 'toolRate' | 'time' | 'errorRate'

export type GuardHit = {
  id: GuardId
  reason: string
}

export const runGuards = (m: Metrics, c: ShellConfig): GuardHit[] => {
  const hits: GuardHit[] = []

  if (m.lastRequestTokens > c.maxTokens) {
    hits.push({ id: 'token', reason: `request tokens ${m.lastRequestTokens} > limit ${c.maxTokens}` })
  }
  if (m.stepCount > c.maxSteps) {
    hits.push({ id: 'iter', reason: `step count ${m.stepCount} > limit ${c.maxSteps}` })
  }
  if (m.toolCallCount > c.maxToolCalls) {
    hits.push({ id: 'toolRate', reason: `tool call count ${m.toolCallCount} > limit ${c.maxToolCalls}` })
  }
  if (m.elapsedMs > c.maxElapsedMs) {
    hits.push({ id: 'time', reason: `elapsed ${m.elapsedMs}ms > limit ${c.maxElapsedMs}ms` })
  }
  if (m.consecutiveToolErrors > c.maxConsecutiveToolErrors) {
    hits.push({ id: 'errorRate', reason: `consecutive tool errors ${m.consecutiveToolErrors} > limit ${c.maxConsecutiveToolErrors}` })
  }

  return hits
}