@miller-tech/uap 1.13.12 → 1.13.13
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/dist/.tsbuildinfo +1 -1
- package/dist/benchmarks/token-throughput.d.ts +46 -46
- package/dist/bin/cli.js +0 -0
- package/dist/bin/llama-server-optimize.js +0 -0
- package/dist/bin/policy.js +0 -0
- package/dist/cli/hooks.js +1 -0
- package/dist/cli/hooks.js.map +1 -1
- package/dist/models/types.d.ts +12 -12
- package/dist/policies/schemas/policy.d.ts +12 -12
- package/dist/types/config.d.ts +24 -24
- package/package.json +1 -1
- package/templates/hooks/loop-protection.sh +250 -0
- package/templates/hooks/post-compact.sh +14 -0
- package/templates/hooks/post-tool-use-edit-write.sh +15 -0
- package/templates/hooks/pre-compact.sh +9 -0
- package/templates/hooks/pre-tool-use-bash.sh +6 -0
- package/templates/hooks/pre-tool-use-edit-write.sh +10 -0
- package/templates/hooks/session-start.sh +64 -44
- package/templates/hooks/stop.sh +9 -0
- package/tools/agents/scripts/anthropic_proxy.py +129 -1
- package/tools/agents/scripts/__pycache__/anthropic_proxy.cpython-313.pyc +0 -0
- package/tools/agents/scripts/__pycache__/tool_call_wrapper.cpython-313.pyc +0 -0
package/dist/types/config.d.ts
CHANGED
|
@@ -1272,7 +1272,7 @@ export declare const ModelConfigSchema: z.ZodObject<{
|
|
|
1272
1272
|
}, "strip", z.ZodTypeAny, {
|
|
1273
1273
|
id: string;
|
|
1274
1274
|
name: string;
|
|
1275
|
-
provider: "
|
|
1275
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
1276
1276
|
apiModel: string;
|
|
1277
1277
|
maxContextTokens: number;
|
|
1278
1278
|
capabilities: string[];
|
|
@@ -1283,7 +1283,7 @@ export declare const ModelConfigSchema: z.ZodObject<{
|
|
|
1283
1283
|
}, {
|
|
1284
1284
|
id: string;
|
|
1285
1285
|
name: string;
|
|
1286
|
-
provider: "
|
|
1286
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
1287
1287
|
apiModel: string;
|
|
1288
1288
|
endpoint?: string | undefined;
|
|
1289
1289
|
apiKeyEnvVar?: string | undefined;
|
|
@@ -1303,12 +1303,12 @@ export declare const RoutingRuleSchema: z.ZodObject<{
|
|
|
1303
1303
|
priority: number;
|
|
1304
1304
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
1305
1305
|
keywords?: string[] | undefined;
|
|
1306
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
1306
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
1307
1307
|
}, {
|
|
1308
1308
|
targetRole: "planner" | "executor" | "reviewer" | "fallback";
|
|
1309
1309
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
1310
1310
|
keywords?: string[] | undefined;
|
|
1311
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
1311
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
1312
1312
|
priority?: number | undefined;
|
|
1313
1313
|
}>;
|
|
1314
1314
|
export declare const MultiModelSchema: z.ZodObject<{
|
|
@@ -1328,7 +1328,7 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1328
1328
|
}, "strip", z.ZodTypeAny, {
|
|
1329
1329
|
id: string;
|
|
1330
1330
|
name: string;
|
|
1331
|
-
provider: "
|
|
1331
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
1332
1332
|
apiModel: string;
|
|
1333
1333
|
maxContextTokens: number;
|
|
1334
1334
|
capabilities: string[];
|
|
@@ -1340,7 +1340,7 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1340
1340
|
}, {
|
|
1341
1341
|
id: string;
|
|
1342
1342
|
name: string;
|
|
1343
|
-
provider: "
|
|
1343
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
1344
1344
|
apiModel: string;
|
|
1345
1345
|
endpoint?: string | undefined;
|
|
1346
1346
|
apiKeyEnvVar?: string | undefined;
|
|
@@ -1377,12 +1377,12 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1377
1377
|
priority: number;
|
|
1378
1378
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
1379
1379
|
keywords?: string[] | undefined;
|
|
1380
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
1380
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
1381
1381
|
}, {
|
|
1382
1382
|
targetRole: "planner" | "executor" | "reviewer" | "fallback";
|
|
1383
1383
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
1384
1384
|
keywords?: string[] | undefined;
|
|
1385
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
1385
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
1386
1386
|
priority?: number | undefined;
|
|
1387
1387
|
}>, "many">>;
|
|
1388
1388
|
costOptimization: z.ZodOptional<z.ZodObject<{
|
|
@@ -1443,7 +1443,7 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1443
1443
|
models: (string | {
|
|
1444
1444
|
id: string;
|
|
1445
1445
|
name: string;
|
|
1446
|
-
provider: "
|
|
1446
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
1447
1447
|
apiModel: string;
|
|
1448
1448
|
maxContextTokens: number;
|
|
1449
1449
|
capabilities: string[];
|
|
@@ -1465,7 +1465,7 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1465
1465
|
priority: number;
|
|
1466
1466
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
1467
1467
|
keywords?: string[] | undefined;
|
|
1468
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
1468
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
1469
1469
|
}[] | undefined;
|
|
1470
1470
|
costOptimization?: {
|
|
1471
1471
|
enabled: boolean;
|
|
@@ -1492,7 +1492,7 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1492
1492
|
models?: (string | {
|
|
1493
1493
|
id: string;
|
|
1494
1494
|
name: string;
|
|
1495
|
-
provider: "
|
|
1495
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
1496
1496
|
apiModel: string;
|
|
1497
1497
|
endpoint?: string | undefined;
|
|
1498
1498
|
apiKeyEnvVar?: string | undefined;
|
|
@@ -1512,7 +1512,7 @@ export declare const MultiModelSchema: z.ZodObject<{
|
|
|
1512
1512
|
targetRole: "planner" | "executor" | "reviewer" | "fallback";
|
|
1513
1513
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
1514
1514
|
keywords?: string[] | undefined;
|
|
1515
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
1515
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
1516
1516
|
priority?: number | undefined;
|
|
1517
1517
|
}[] | undefined;
|
|
1518
1518
|
costOptimization?: {
|
|
@@ -2583,7 +2583,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2583
2583
|
}, "strip", z.ZodTypeAny, {
|
|
2584
2584
|
id: string;
|
|
2585
2585
|
name: string;
|
|
2586
|
-
provider: "
|
|
2586
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
2587
2587
|
apiModel: string;
|
|
2588
2588
|
maxContextTokens: number;
|
|
2589
2589
|
capabilities: string[];
|
|
@@ -2595,7 +2595,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2595
2595
|
}, {
|
|
2596
2596
|
id: string;
|
|
2597
2597
|
name: string;
|
|
2598
|
-
provider: "
|
|
2598
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
2599
2599
|
apiModel: string;
|
|
2600
2600
|
endpoint?: string | undefined;
|
|
2601
2601
|
apiKeyEnvVar?: string | undefined;
|
|
@@ -2632,12 +2632,12 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2632
2632
|
priority: number;
|
|
2633
2633
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
2634
2634
|
keywords?: string[] | undefined;
|
|
2635
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
2635
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
2636
2636
|
}, {
|
|
2637
2637
|
targetRole: "planner" | "executor" | "reviewer" | "fallback";
|
|
2638
2638
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
2639
2639
|
keywords?: string[] | undefined;
|
|
2640
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
2640
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
2641
2641
|
priority?: number | undefined;
|
|
2642
2642
|
}>, "many">>;
|
|
2643
2643
|
costOptimization: z.ZodOptional<z.ZodObject<{
|
|
@@ -2698,7 +2698,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2698
2698
|
models: (string | {
|
|
2699
2699
|
id: string;
|
|
2700
2700
|
name: string;
|
|
2701
|
-
provider: "
|
|
2701
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
2702
2702
|
apiModel: string;
|
|
2703
2703
|
maxContextTokens: number;
|
|
2704
2704
|
capabilities: string[];
|
|
@@ -2720,7 +2720,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2720
2720
|
priority: number;
|
|
2721
2721
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
2722
2722
|
keywords?: string[] | undefined;
|
|
2723
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
2723
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
2724
2724
|
}[] | undefined;
|
|
2725
2725
|
costOptimization?: {
|
|
2726
2726
|
enabled: boolean;
|
|
@@ -2747,7 +2747,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2747
2747
|
models?: (string | {
|
|
2748
2748
|
id: string;
|
|
2749
2749
|
name: string;
|
|
2750
|
-
provider: "
|
|
2750
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
2751
2751
|
apiModel: string;
|
|
2752
2752
|
endpoint?: string | undefined;
|
|
2753
2753
|
apiKeyEnvVar?: string | undefined;
|
|
@@ -2767,7 +2767,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
2767
2767
|
targetRole: "planner" | "executor" | "reviewer" | "fallback";
|
|
2768
2768
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
2769
2769
|
keywords?: string[] | undefined;
|
|
2770
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
2770
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
2771
2771
|
priority?: number | undefined;
|
|
2772
2772
|
}[] | undefined;
|
|
2773
2773
|
costOptimization?: {
|
|
@@ -3092,7 +3092,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
3092
3092
|
models: (string | {
|
|
3093
3093
|
id: string;
|
|
3094
3094
|
name: string;
|
|
3095
|
-
provider: "
|
|
3095
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
3096
3096
|
apiModel: string;
|
|
3097
3097
|
maxContextTokens: number;
|
|
3098
3098
|
capabilities: string[];
|
|
@@ -3114,7 +3114,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
3114
3114
|
priority: number;
|
|
3115
3115
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
3116
3116
|
keywords?: string[] | undefined;
|
|
3117
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
3117
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
3118
3118
|
}[] | undefined;
|
|
3119
3119
|
costOptimization?: {
|
|
3120
3120
|
enabled: boolean;
|
|
@@ -3353,7 +3353,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
3353
3353
|
models?: (string | {
|
|
3354
3354
|
id: string;
|
|
3355
3355
|
name: string;
|
|
3356
|
-
provider: "
|
|
3356
|
+
provider: "anthropic" | "deepseek" | "openai" | "zhipu" | "ollama" | "custom";
|
|
3357
3357
|
apiModel: string;
|
|
3358
3358
|
endpoint?: string | undefined;
|
|
3359
3359
|
apiKeyEnvVar?: string | undefined;
|
|
@@ -3373,7 +3373,7 @@ export declare const AgentContextConfigSchema: z.ZodObject<{
|
|
|
3373
3373
|
targetRole: "planner" | "executor" | "reviewer" | "fallback";
|
|
3374
3374
|
complexity?: "low" | "medium" | "high" | "critical" | undefined;
|
|
3375
3375
|
keywords?: string[] | undefined;
|
|
3376
|
-
taskType?: "planning" | "coding" | "refactoring" | "bug-fix" | "
|
|
3376
|
+
taskType?: "planning" | "review" | "coding" | "refactoring" | "bug-fix" | "documentation" | undefined;
|
|
3377
3377
|
priority?: number | undefined;
|
|
3378
3378
|
}[] | undefined;
|
|
3379
3379
|
costOptimization?: {
|
package/package.json
CHANGED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================
|
|
3
|
+
# UAP Loop Protection & Token Budget Circuit Breaker
|
|
4
|
+
# ============================================================
|
|
5
|
+
# Shared library sourced by all UAP hooks.
|
|
6
|
+
# Tracks hook invocation frequency and detects runaway loops
|
|
7
|
+
# that waste tokens (and money) by emitting the same system
|
|
8
|
+
# reminders, build-gate warnings, or compliance blocks
|
|
9
|
+
# hundreds of times in a single session.
|
|
10
|
+
#
|
|
11
|
+
# MECHANISM:
|
|
12
|
+
# - Maintains a lightweight state file per session
|
|
13
|
+
# - Counts hook invocations per type within sliding windows
|
|
14
|
+
# - Suppresses redundant output after thresholds are hit
|
|
15
|
+
# - Logs suppressed events for post-mortem analysis
|
|
16
|
+
# - Provides hard circuit-breaker after extreme loop counts
|
|
17
|
+
#
|
|
18
|
+
# USAGE (source from any hook script):
|
|
19
|
+
# source "$(dirname "$0")/loop-protection.sh"
|
|
20
|
+
# if lp_should_suppress "post-tool-use-edit-write"; then
|
|
21
|
+
# exit 0 # skip output, loop detected
|
|
22
|
+
# fi
|
|
23
|
+
# lp_record_invocation "post-tool-use-edit-write"
|
|
24
|
+
#
|
|
25
|
+
# CONFIGURATION (environment variables):
|
|
26
|
+
# UAP_LP_DISABLED=1 Disable loop protection entirely
|
|
27
|
+
# UAP_LP_SOFT_LIMIT=15 Warn after N invocations per hook (default: 15)
|
|
28
|
+
# UAP_LP_HARD_LIMIT=50 Suppress output after N (default: 50)
|
|
29
|
+
# UAP_LP_CIRCUIT_BREAK=200 Hard stop — emit circuit breaker msg (default: 200)
|
|
30
|
+
# UAP_LP_WINDOW_SECS=300 Sliding window in seconds (default: 300 = 5 min)
|
|
31
|
+
# UAP_LP_DEDUP_SECS=5 Min seconds between identical outputs (default: 5)
|
|
32
|
+
# ============================================================
|
|
33
|
+
|
|
34
|
+
# Guard against re-sourcing
|
|
35
|
+
if [ "${_UAP_LOOP_PROTECTION_LOADED:-}" = "1" ]; then
|
|
36
|
+
return 0 2>/dev/null || true
|
|
37
|
+
fi
|
|
38
|
+
_UAP_LOOP_PROTECTION_LOADED=1
|
|
39
|
+
|
|
40
|
+
# --- Configuration ---
|
|
41
|
+
LP_DISABLED="${UAP_LP_DISABLED:-0}"
|
|
42
|
+
LP_SOFT_LIMIT="${UAP_LP_SOFT_LIMIT:-15}"
|
|
43
|
+
LP_HARD_LIMIT="${UAP_LP_HARD_LIMIT:-50}"
|
|
44
|
+
LP_CIRCUIT_BREAK="${UAP_LP_CIRCUIT_BREAK:-200}"
|
|
45
|
+
LP_WINDOW_SECS="${UAP_LP_WINDOW_SECS:-300}"
|
|
46
|
+
LP_DEDUP_SECS="${UAP_LP_DEDUP_SECS:-5}"
|
|
47
|
+
|
|
48
|
+
# --- State file location ---
|
|
49
|
+
LP_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-.}}}"
|
|
50
|
+
LP_STATE_DIR="${LP_PROJECT_DIR}/.uap/loop-protection"
|
|
51
|
+
LP_SESSION_ID="${UAP_SESSION_ID:-${SESSION_ID:-default}}"
|
|
52
|
+
LP_STATE_FILE="${LP_STATE_DIR}/session-${LP_SESSION_ID}.state"
|
|
53
|
+
LP_LOG_FILE="${LP_STATE_DIR}/loop-events.log"
|
|
54
|
+
|
|
55
|
+
# --- Ensure state directory exists ---
|
|
56
|
+
_lp_init() {
|
|
57
|
+
if [ "$LP_DISABLED" = "1" ]; then
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
mkdir -p "$LP_STATE_DIR" 2>/dev/null || true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# --- Get current epoch seconds (portable) ---
|
|
64
|
+
_lp_now() {
|
|
65
|
+
date +%s 2>/dev/null || echo "0"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# --- Read counter for a hook type from state file ---
|
|
69
|
+
# Format: hook_type|count|first_ts|last_ts|suppressed_count
|
|
70
|
+
_lp_read_state() {
|
|
71
|
+
local hook_type="$1"
|
|
72
|
+
if [ ! -f "$LP_STATE_FILE" ]; then
|
|
73
|
+
echo "0|0|0|0"
|
|
74
|
+
return
|
|
75
|
+
fi
|
|
76
|
+
local line
|
|
77
|
+
line=$(grep "^${hook_type}|" "$LP_STATE_FILE" 2>/dev/null | tail -1)
|
|
78
|
+
if [ -z "$line" ]; then
|
|
79
|
+
echo "0|0|0|0"
|
|
80
|
+
return
|
|
81
|
+
fi
|
|
82
|
+
echo "$line" | cut -d'|' -f2-5
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# --- Write/update counter for a hook type ---
|
|
86
|
+
_lp_write_state() {
|
|
87
|
+
local hook_type="$1"
|
|
88
|
+
local count="$2"
|
|
89
|
+
local first_ts="$3"
|
|
90
|
+
local last_ts="$4"
|
|
91
|
+
local suppressed="$5"
|
|
92
|
+
|
|
93
|
+
# Atomic update: remove old line, append new
|
|
94
|
+
if [ -f "$LP_STATE_FILE" ]; then
|
|
95
|
+
grep -v "^${hook_type}|" "$LP_STATE_FILE" > "${LP_STATE_FILE}.tmp" 2>/dev/null || true
|
|
96
|
+
mv "${LP_STATE_FILE}.tmp" "$LP_STATE_FILE" 2>/dev/null || true
|
|
97
|
+
fi
|
|
98
|
+
echo "${hook_type}|${count}|${first_ts}|${last_ts}|${suppressed}" >> "$LP_STATE_FILE"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# --- Log a loop event for post-mortem ---
|
|
102
|
+
_lp_log_event() {
|
|
103
|
+
local level="$1"
|
|
104
|
+
local hook_type="$2"
|
|
105
|
+
local message="$3"
|
|
106
|
+
local now
|
|
107
|
+
now=$(_lp_now)
|
|
108
|
+
echo "${now}|${level}|${hook_type}|${message}" >> "$LP_LOG_FILE" 2>/dev/null || true
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# ============================================================
|
|
112
|
+
# PUBLIC API
|
|
113
|
+
# ============================================================
|
|
114
|
+
|
|
115
|
+
# Check if a hook invocation should be suppressed.
|
|
116
|
+
# Returns 0 (true/suppress) if the hook has been called too many times.
|
|
117
|
+
# Returns 1 (false/allow) if the hook should proceed normally.
|
|
118
|
+
lp_should_suppress() {
|
|
119
|
+
local hook_type="${1:-unknown}"
|
|
120
|
+
|
|
121
|
+
if [ "$LP_DISABLED" = "1" ]; then
|
|
122
|
+
return 1 # don't suppress
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
_lp_init
|
|
126
|
+
|
|
127
|
+
local state
|
|
128
|
+
state=$(_lp_read_state "$hook_type")
|
|
129
|
+
local count first_ts last_ts suppressed
|
|
130
|
+
count=$(echo "$state" | cut -d'|' -f1)
|
|
131
|
+
first_ts=$(echo "$state" | cut -d'|' -f2)
|
|
132
|
+
last_ts=$(echo "$state" | cut -d'|' -f3)
|
|
133
|
+
suppressed=$(echo "$state" | cut -d'|' -f4)
|
|
134
|
+
|
|
135
|
+
local now
|
|
136
|
+
now=$(_lp_now)
|
|
137
|
+
|
|
138
|
+
# Reset window if first_ts is too old
|
|
139
|
+
if [ "$first_ts" != "0" ] && [ $((now - first_ts)) -gt "$LP_WINDOW_SECS" ]; then
|
|
140
|
+
count=0
|
|
141
|
+
first_ts="$now"
|
|
142
|
+
suppressed=0
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# Dedup: if called within LP_DEDUP_SECS of last call, always suppress
|
|
146
|
+
if [ "$last_ts" != "0" ] && [ $((now - last_ts)) -lt "$LP_DEDUP_SECS" ]; then
|
|
147
|
+
suppressed=$((suppressed + 1))
|
|
148
|
+
_lp_write_state "$hook_type" "$count" "$first_ts" "$now" "$suppressed"
|
|
149
|
+
return 0 # suppress (dedup)
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Check thresholds
|
|
153
|
+
if [ "$count" -ge "$LP_CIRCUIT_BREAK" ]; then
|
|
154
|
+
# Circuit breaker: emit ONE final warning then suppress everything
|
|
155
|
+
if [ "$suppressed" -eq 0 ] || [ $((count % LP_CIRCUIT_BREAK)) -eq 0 ]; then
|
|
156
|
+
_lp_log_event "CIRCUIT_BREAK" "$hook_type" "count=${count} in window"
|
|
157
|
+
fi
|
|
158
|
+
suppressed=$((suppressed + 1))
|
|
159
|
+
_lp_write_state "$hook_type" "$count" "$first_ts" "$now" "$suppressed"
|
|
160
|
+
return 0 # suppress
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
if [ "$count" -ge "$LP_HARD_LIMIT" ]; then
|
|
164
|
+
# Hard limit: suppress output, log
|
|
165
|
+
if [ $((count % 10)) -eq 0 ]; then
|
|
166
|
+
_lp_log_event "HARD_LIMIT" "$hook_type" "count=${count} suppressed=${suppressed}"
|
|
167
|
+
fi
|
|
168
|
+
suppressed=$((suppressed + 1))
|
|
169
|
+
_lp_write_state "$hook_type" "$count" "$first_ts" "$now" "$suppressed"
|
|
170
|
+
return 0 # suppress
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
return 1 # allow
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Record a hook invocation (call AFTER producing output).
|
|
177
|
+
lp_record_invocation() {
|
|
178
|
+
local hook_type="${1:-unknown}"
|
|
179
|
+
|
|
180
|
+
if [ "$LP_DISABLED" = "1" ]; then
|
|
181
|
+
return 0
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
_lp_init
|
|
185
|
+
|
|
186
|
+
local state
|
|
187
|
+
state=$(_lp_read_state "$hook_type")
|
|
188
|
+
local count first_ts last_ts suppressed
|
|
189
|
+
count=$(echo "$state" | cut -d'|' -f1)
|
|
190
|
+
first_ts=$(echo "$state" | cut -d'|' -f2)
|
|
191
|
+
last_ts=$(echo "$state" | cut -d'|' -f3)
|
|
192
|
+
suppressed=$(echo "$state" | cut -d'|' -f4)
|
|
193
|
+
|
|
194
|
+
local now
|
|
195
|
+
now=$(_lp_now)
|
|
196
|
+
|
|
197
|
+
# Reset window if expired
|
|
198
|
+
if [ "$first_ts" = "0" ] || [ $((now - first_ts)) -gt "$LP_WINDOW_SECS" ]; then
|
|
199
|
+
count=1
|
|
200
|
+
first_ts="$now"
|
|
201
|
+
suppressed=0
|
|
202
|
+
else
|
|
203
|
+
count=$((count + 1))
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
_lp_write_state "$hook_type" "$count" "$first_ts" "$now" "$suppressed"
|
|
207
|
+
|
|
208
|
+
# Emit warnings at soft limit
|
|
209
|
+
if [ "$count" -eq "$LP_SOFT_LIMIT" ]; then
|
|
210
|
+
_lp_log_event "SOFT_LIMIT" "$hook_type" "count=${count} - approaching rate limit"
|
|
211
|
+
echo "[UAP-LOOP-PROTECTION] Hook '${hook_type}' has fired ${count} times in ${LP_WINDOW_SECS}s. Output will be suppressed after ${LP_HARD_LIMIT} to prevent token waste." >&2
|
|
212
|
+
fi
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Get the circuit breaker warning message (for hooks that want to emit it).
|
|
216
|
+
lp_circuit_breaker_message() {
|
|
217
|
+
local hook_type="${1:-unknown}"
|
|
218
|
+
local state
|
|
219
|
+
state=$(_lp_read_state "$hook_type")
|
|
220
|
+
local count
|
|
221
|
+
count=$(echo "$state" | cut -d'|' -f1)
|
|
222
|
+
local suppressed
|
|
223
|
+
suppressed=$(echo "$state" | cut -d'|' -f4)
|
|
224
|
+
|
|
225
|
+
echo "[UAP-CIRCUIT-BREAKER] Hook '${hook_type}' triggered ${count} times (${suppressed} suppressed). This is a runaway loop. Review your approach — repeated hook warnings are consuming tokens without progress."
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# Get loop protection stats as a summary string.
|
|
229
|
+
lp_stats() {
|
|
230
|
+
if [ ! -f "$LP_STATE_FILE" ]; then
|
|
231
|
+
echo "No loop protection data for this session."
|
|
232
|
+
return
|
|
233
|
+
fi
|
|
234
|
+
echo "=== UAP Loop Protection Stats ==="
|
|
235
|
+
while IFS='|' read -r hook count first_ts last_ts suppressed; do
|
|
236
|
+
echo " ${hook}: ${count} calls, ${suppressed} suppressed"
|
|
237
|
+
done < "$LP_STATE_FILE"
|
|
238
|
+
echo "================================="
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Reset state for a specific hook or all hooks.
|
|
242
|
+
lp_reset() {
|
|
243
|
+
local hook_type="${1:-}"
|
|
244
|
+
if [ -z "$hook_type" ]; then
|
|
245
|
+
rm -f "$LP_STATE_FILE" 2>/dev/null || true
|
|
246
|
+
elif [ -f "$LP_STATE_FILE" ]; then
|
|
247
|
+
grep -v "^${hook_type}|" "$LP_STATE_FILE" > "${LP_STATE_FILE}.tmp" 2>/dev/null || true
|
|
248
|
+
mv "${LP_STATE_FILE}.tmp" "$LP_STATE_FILE" 2>/dev/null || true
|
|
249
|
+
fi
|
|
250
|
+
}
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
# Always exits 0 (never blocks).
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
+
# --- Loop Protection: suppress if compaction is happening in rapid succession ---
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
if [ -f "${HOOK_DIR}/loop-protection.sh" ]; then
|
|
11
|
+
source "${HOOK_DIR}/loop-protection.sh"
|
|
12
|
+
if lp_should_suppress "post-compact"; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
fi
|
|
16
|
+
|
|
8
17
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-.}}}"
|
|
9
18
|
DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
|
|
10
19
|
COORD_DB="${PROJECT_DIR}/agents/data/coordination/coordination.db"
|
|
@@ -108,4 +117,9 @@ fi
|
|
|
108
117
|
|
|
109
118
|
output+="</system-reminder>"$'\n'
|
|
110
119
|
|
|
120
|
+
# --- Record invocation for loop tracking ---
|
|
121
|
+
if type lp_record_invocation &>/dev/null; then
|
|
122
|
+
lp_record_invocation "post-compact"
|
|
123
|
+
fi
|
|
124
|
+
|
|
111
125
|
echo "$output"
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
# Always exits 0 (never blocks).
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
|
|
9
|
+
# --- Loop Protection: suppress output if firing too fast ---
|
|
10
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
if [ -f "${HOOK_DIR}/loop-protection.sh" ]; then
|
|
12
|
+
source "${HOOK_DIR}/loop-protection.sh"
|
|
13
|
+
if lp_should_suppress "post-tool-edit-write"; then
|
|
14
|
+
cat > /dev/null # drain stdin
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
fi
|
|
18
|
+
|
|
9
19
|
# Read tool input from stdin (JSON)
|
|
10
20
|
INPUT=$(cat)
|
|
11
21
|
|
|
@@ -35,4 +45,9 @@ if [ ! -f "${BACKUP_DIR}/${RELATIVE_PATH}" ] 2>/dev/null; then
|
|
|
35
45
|
fi
|
|
36
46
|
fi
|
|
37
47
|
|
|
48
|
+
# --- Record invocation for loop tracking ---
|
|
49
|
+
if type lp_record_invocation &>/dev/null; then
|
|
50
|
+
lp_record_invocation "post-tool-edit-write"
|
|
51
|
+
fi
|
|
52
|
+
|
|
38
53
|
exit 0
|
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
# Fails safely - never blocks the agent.
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
10
|
+
# --- Loop Protection: suppress if compaction is looping ---
|
|
11
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
if [ -f "${HOOK_DIR}/loop-protection.sh" ]; then
|
|
13
|
+
source "${HOOK_DIR}/loop-protection.sh"
|
|
14
|
+
if lp_should_suppress "pre-compact"; then
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
fi
|
|
18
|
+
|
|
10
19
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-.}}}"
|
|
11
20
|
DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
|
|
12
21
|
COORD_DB="${PROJECT_DIR}/agents/data/coordination/coordination.db"
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
# Enforces: iac-pipeline-enforcement, worktree-enforcement, git safety policies.
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
+
# --- Loop Protection: track frequency of blocking events ---
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
if [ -f "${HOOK_DIR}/loop-protection.sh" ]; then
|
|
11
|
+
source "${HOOK_DIR}/loop-protection.sh"
|
|
12
|
+
fi
|
|
13
|
+
|
|
8
14
|
# Read tool input from stdin (JSON)
|
|
9
15
|
INPUT=$(cat)
|
|
10
16
|
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
# Enforces: worktree-file-guard, worktree-enforcement policies.
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
+
# --- Loop Protection: track frequency of blocking events ---
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
if [ -f "${HOOK_DIR}/loop-protection.sh" ]; then
|
|
11
|
+
source "${HOOK_DIR}/loop-protection.sh"
|
|
12
|
+
fi
|
|
13
|
+
|
|
8
14
|
# Read tool input from stdin (JSON)
|
|
9
15
|
INPUT=$(cat)
|
|
10
16
|
|
|
@@ -40,5 +46,9 @@ if echo "$FILE_PATH" | grep -q '\.worktrees/'; then
|
|
|
40
46
|
fi
|
|
41
47
|
|
|
42
48
|
# BLOCK: path is outside worktrees and not exempt
|
|
49
|
+
# Record the block event for loop detection
|
|
50
|
+
if type lp_record_invocation &>/dev/null; then
|
|
51
|
+
lp_record_invocation "pre-tool-edit-block"
|
|
52
|
+
fi
|
|
43
53
|
echo '{"decision":"block","reason":"WORKTREE POLICY VIOLATION: File path is outside .worktrees/. All edits must target files inside a worktree. Run: uap worktree create <slug> then edit files in .worktrees/NNN-<slug>/. See policies/worktree-file-guard.md"}' >&2
|
|
44
54
|
exit 2
|