claude-sdk-verigent 0.1.0__tar.gz
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.
- claude_sdk_verigent-0.1.0/PKG-INFO +138 -0
- claude_sdk_verigent-0.1.0/README.md +120 -0
- claude_sdk_verigent-0.1.0/pyproject.toml +30 -0
- claude_sdk_verigent-0.1.0/src/claude_sdk_verigent/__init__.py +19 -0
- claude_sdk_verigent-0.1.0/src/claude_sdk_verigent/middleware.py +164 -0
- claude_sdk_verigent-0.1.0/src/claude_sdk_verigent/parser.py +115 -0
- claude_sdk_verigent-0.1.0/src/claude_sdk_verigent/trust.py +68 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: claude-sdk-verigent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Verigent trust verification for the Claude Agent SDK
|
|
5
|
+
Project-URL: Homepage, https://verigent.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/verigentai/claude-sdk-verigent
|
|
7
|
+
Author: verigentai
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: agent,anthropic,claude,trust,verification,verigent
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: anthropic>=0.30.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# claude-sdk-verigent
|
|
20
|
+
|
|
21
|
+
Verigent trust verification for the [Claude Agent SDK](https://docs.anthropic.com/en/docs/agents).
|
|
22
|
+
|
|
23
|
+
## The Problem
|
|
24
|
+
|
|
25
|
+
When Claude agents interact with other agents, there's no standard way to verify identity or assess trust. An agent claiming to be "JARVIS" might be a verified V5 Architect or a spoofed impersonator. Without verification, multi-agent systems operate on blind trust.
|
|
26
|
+
|
|
27
|
+
Verigent solves this with cryptographic trust keys (VG keys) that encode an agent's identity, tier, primary class, and 12-axis capability scores.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install claude-sdk-verigent
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Requires `anthropic>=0.30.0`.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Embed a VG key in your agent's system prompt
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from claude_sdk_verigent import VerigentSystemPrompt
|
|
43
|
+
|
|
44
|
+
vg_key = "VG:JARVIS-0A:V3-ARCH·Se4Op7An5Ar9Co2Ad6St8Sc3Sa5So1Br2Fo6"
|
|
45
|
+
vsp = VerigentSystemPrompt(vg_key)
|
|
46
|
+
|
|
47
|
+
system_prompt = "You are JARVIS, an architecture agent." + vsp.render()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This appends a structured trust identity block that other agents can parse and verify.
|
|
51
|
+
|
|
52
|
+
### Add the verification tool to multi-agent setups
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from anthropic import Anthropic
|
|
56
|
+
from claude_sdk_verigent import VerigentTool
|
|
57
|
+
|
|
58
|
+
client = Anthropic()
|
|
59
|
+
tool = VerigentTool()
|
|
60
|
+
|
|
61
|
+
# Include in your tools list
|
|
62
|
+
response = client.messages.create(
|
|
63
|
+
model="claude-sonnet-4-20250514",
|
|
64
|
+
max_tokens=1024,
|
|
65
|
+
tools=[tool.definition],
|
|
66
|
+
messages=[{"role": "user", "content": "Verify this agent's key: VG:ATLAS-7B:V4-SENT·Se8Op6An7Ar5Co4Ad3St9Sc2Sa6So3Br4Fo5"}],
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Handle tool_use response
|
|
70
|
+
for block in response.content:
|
|
71
|
+
if block.type == "tool_use" and block.name == "verify_agent":
|
|
72
|
+
result = tool.execute(block.input)
|
|
73
|
+
# result = {"verified": True, "handle": "ATLAS", "tier": "V4", ...}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Intercept and verify keys in tool responses
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from claude_sdk_verigent import VerigentInterceptor
|
|
80
|
+
|
|
81
|
+
interceptor = VerigentInterceptor(min_trust_tier=2)
|
|
82
|
+
|
|
83
|
+
# Check any text for embedded VG keys
|
|
84
|
+
assessment = interceptor.check(tool_response_text)
|
|
85
|
+
if assessment:
|
|
86
|
+
print(f"{assessment.handle}: {assessment.level.value} (score {assessment.score})")
|
|
87
|
+
|
|
88
|
+
# Quick trust gate
|
|
89
|
+
if interceptor.is_trusted(agent_output):
|
|
90
|
+
# proceed with high-trust path
|
|
91
|
+
...
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Parse a key directly
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from claude_sdk_verigent import parse_vg_key, evaluate_trust
|
|
98
|
+
|
|
99
|
+
key = parse_vg_key("VG:JARVIS-0A:V3-ARCH·Se4Op7An5Ar9Co2Ad6St8Sc3Sa5So1Br2Fo6")
|
|
100
|
+
print(key.handle) # "JARVIS"
|
|
101
|
+
print(key.tier) # 3
|
|
102
|
+
print(key.scores) # {"Sentinel": 4, "Operative": 7, ...}
|
|
103
|
+
|
|
104
|
+
assessment = evaluate_trust(key)
|
|
105
|
+
print(assessment.level) # TrustLevel.HIGH
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## VG Key Format
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
VG:{NAME}-{SUFFIX}:{TIER}-{PRIMARY}·{12x class_code+digit}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- **NAME**: Agent handle (e.g., JARVIS, ATLAS)
|
|
115
|
+
- **SUFFIX**: Unique identifier
|
|
116
|
+
- **TIER**: V0 (unverified) through V6 (maximum trust)
|
|
117
|
+
- **PRIMARY**: 4-letter primary class code
|
|
118
|
+
- **Scores**: 12 two-letter class codes each followed by a digit (0-9)
|
|
119
|
+
|
|
120
|
+
### Trust Classes
|
|
121
|
+
|
|
122
|
+
| Code | Class | Code | Class |
|
|
123
|
+
|------|-------|------|-------|
|
|
124
|
+
| Se | Sentinel | St | Steward |
|
|
125
|
+
| Op | Operative | Sc | Scout |
|
|
126
|
+
| An | Analyst | Sa | Sage |
|
|
127
|
+
| Ar | Architect | So | Sovereign |
|
|
128
|
+
| Co | Conduit | Br | Broker |
|
|
129
|
+
| Ad | Adaptor | Fo | Forge |
|
|
130
|
+
|
|
131
|
+
## Links
|
|
132
|
+
|
|
133
|
+
- [Verigent](https://verigent.ai) — Agent trust infrastructure
|
|
134
|
+
- [Claude Agent SDK docs](https://docs.anthropic.com/en/docs/agents)
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# claude-sdk-verigent
|
|
2
|
+
|
|
3
|
+
Verigent trust verification for the [Claude Agent SDK](https://docs.anthropic.com/en/docs/agents).
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
When Claude agents interact with other agents, there's no standard way to verify identity or assess trust. An agent claiming to be "JARVIS" might be a verified V5 Architect or a spoofed impersonator. Without verification, multi-agent systems operate on blind trust.
|
|
8
|
+
|
|
9
|
+
Verigent solves this with cryptographic trust keys (VG keys) that encode an agent's identity, tier, primary class, and 12-axis capability scores.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install claude-sdk-verigent
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires `anthropic>=0.30.0`.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Embed a VG key in your agent's system prompt
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from claude_sdk_verigent import VerigentSystemPrompt
|
|
25
|
+
|
|
26
|
+
vg_key = "VG:JARVIS-0A:V3-ARCH·Se4Op7An5Ar9Co2Ad6St8Sc3Sa5So1Br2Fo6"
|
|
27
|
+
vsp = VerigentSystemPrompt(vg_key)
|
|
28
|
+
|
|
29
|
+
system_prompt = "You are JARVIS, an architecture agent." + vsp.render()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This appends a structured trust identity block that other agents can parse and verify.
|
|
33
|
+
|
|
34
|
+
### Add the verification tool to multi-agent setups
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from anthropic import Anthropic
|
|
38
|
+
from claude_sdk_verigent import VerigentTool
|
|
39
|
+
|
|
40
|
+
client = Anthropic()
|
|
41
|
+
tool = VerigentTool()
|
|
42
|
+
|
|
43
|
+
# Include in your tools list
|
|
44
|
+
response = client.messages.create(
|
|
45
|
+
model="claude-sonnet-4-20250514",
|
|
46
|
+
max_tokens=1024,
|
|
47
|
+
tools=[tool.definition],
|
|
48
|
+
messages=[{"role": "user", "content": "Verify this agent's key: VG:ATLAS-7B:V4-SENT·Se8Op6An7Ar5Co4Ad3St9Sc2Sa6So3Br4Fo5"}],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Handle tool_use response
|
|
52
|
+
for block in response.content:
|
|
53
|
+
if block.type == "tool_use" and block.name == "verify_agent":
|
|
54
|
+
result = tool.execute(block.input)
|
|
55
|
+
# result = {"verified": True, "handle": "ATLAS", "tier": "V4", ...}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Intercept and verify keys in tool responses
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from claude_sdk_verigent import VerigentInterceptor
|
|
62
|
+
|
|
63
|
+
interceptor = VerigentInterceptor(min_trust_tier=2)
|
|
64
|
+
|
|
65
|
+
# Check any text for embedded VG keys
|
|
66
|
+
assessment = interceptor.check(tool_response_text)
|
|
67
|
+
if assessment:
|
|
68
|
+
print(f"{assessment.handle}: {assessment.level.value} (score {assessment.score})")
|
|
69
|
+
|
|
70
|
+
# Quick trust gate
|
|
71
|
+
if interceptor.is_trusted(agent_output):
|
|
72
|
+
# proceed with high-trust path
|
|
73
|
+
...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Parse a key directly
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from claude_sdk_verigent import parse_vg_key, evaluate_trust
|
|
80
|
+
|
|
81
|
+
key = parse_vg_key("VG:JARVIS-0A:V3-ARCH·Se4Op7An5Ar9Co2Ad6St8Sc3Sa5So1Br2Fo6")
|
|
82
|
+
print(key.handle) # "JARVIS"
|
|
83
|
+
print(key.tier) # 3
|
|
84
|
+
print(key.scores) # {"Sentinel": 4, "Operative": 7, ...}
|
|
85
|
+
|
|
86
|
+
assessment = evaluate_trust(key)
|
|
87
|
+
print(assessment.level) # TrustLevel.HIGH
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## VG Key Format
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
VG:{NAME}-{SUFFIX}:{TIER}-{PRIMARY}·{12x class_code+digit}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- **NAME**: Agent handle (e.g., JARVIS, ATLAS)
|
|
97
|
+
- **SUFFIX**: Unique identifier
|
|
98
|
+
- **TIER**: V0 (unverified) through V6 (maximum trust)
|
|
99
|
+
- **PRIMARY**: 4-letter primary class code
|
|
100
|
+
- **Scores**: 12 two-letter class codes each followed by a digit (0-9)
|
|
101
|
+
|
|
102
|
+
### Trust Classes
|
|
103
|
+
|
|
104
|
+
| Code | Class | Code | Class |
|
|
105
|
+
|------|-------|------|-------|
|
|
106
|
+
| Se | Sentinel | St | Steward |
|
|
107
|
+
| Op | Operative | Sc | Scout |
|
|
108
|
+
| An | Analyst | Sa | Sage |
|
|
109
|
+
| Ar | Architect | So | Sovereign |
|
|
110
|
+
| Co | Conduit | Br | Broker |
|
|
111
|
+
| Ad | Adaptor | Fo | Forge |
|
|
112
|
+
|
|
113
|
+
## Links
|
|
114
|
+
|
|
115
|
+
- [Verigent](https://verigent.ai) — Agent trust infrastructure
|
|
116
|
+
- [Claude Agent SDK docs](https://docs.anthropic.com/en/docs/agents)
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "claude-sdk-verigent"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Verigent trust verification for the Claude Agent SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "verigentai" }]
|
|
13
|
+
keywords = ["claude", "anthropic", "agent", "trust", "verification", "verigent"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Security",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"anthropic>=0.30.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://verigent.ai"
|
|
27
|
+
Repository = "https://github.com/verigentai/claude-sdk-verigent"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["src/claude_sdk_verigent"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""claude-sdk-verigent — Verigent trust verification for the Claude Agent SDK."""
|
|
2
|
+
|
|
3
|
+
from .middleware import VerigentInterceptor, VerigentSystemPrompt, VerigentTool
|
|
4
|
+
from .parser import VGKey, find_vg_key, parse_vg_key
|
|
5
|
+
from .trust import TrustAssessment, TrustLevel, evaluate_trust
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"VGKey",
|
|
9
|
+
"VerigentInterceptor",
|
|
10
|
+
"VerigentSystemPrompt",
|
|
11
|
+
"VerigentTool",
|
|
12
|
+
"TrustAssessment",
|
|
13
|
+
"TrustLevel",
|
|
14
|
+
"evaluate_trust",
|
|
15
|
+
"find_vg_key",
|
|
16
|
+
"parse_vg_key",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Claude Agent SDK integration — tool definitions and interceptors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .parser import VGKey, find_vg_key, parse_vg_key
|
|
8
|
+
from .trust import TrustAssessment, evaluate_trust
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# --- VerigentTool: a tool_use definition for agent-to-agent verification ---
|
|
12
|
+
|
|
13
|
+
VERIGENT_TOOL_DEFINITION: dict[str, Any] = {
|
|
14
|
+
"name": "verify_agent",
|
|
15
|
+
"description": (
|
|
16
|
+
"Verify an agent's Verigent (VG) trust key. "
|
|
17
|
+
"Pass a VG key string or an agent handle to check trust level, "
|
|
18
|
+
"tier, primary class, and class scores."
|
|
19
|
+
),
|
|
20
|
+
"input_schema": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"vg_key": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "The full VG key string (VG:NAME-SUFFIX:TIER-CLASS-scores)",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
"required": ["vg_key"],
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class VerigentTool:
|
|
34
|
+
"""Wraps VG verification as a Claude tool_use callable.
|
|
35
|
+
|
|
36
|
+
Usage with the Claude Agent SDK:
|
|
37
|
+
tool = VerigentTool()
|
|
38
|
+
# Add tool.definition to your tools list
|
|
39
|
+
# When tool_use is called, pass input to tool.execute()
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def definition(self) -> dict[str, Any]:
|
|
44
|
+
"""The tool definition to pass to Claude's API."""
|
|
45
|
+
return VERIGENT_TOOL_DEFINITION
|
|
46
|
+
|
|
47
|
+
def execute(self, tool_input: dict[str, Any]) -> dict[str, Any]:
|
|
48
|
+
"""Execute the verification tool. Returns parsed key + trust assessment."""
|
|
49
|
+
vg_key_str = tool_input.get("vg_key", "")
|
|
50
|
+
key = parse_vg_key(vg_key_str)
|
|
51
|
+
|
|
52
|
+
if key is None:
|
|
53
|
+
return {
|
|
54
|
+
"verified": False,
|
|
55
|
+
"error": "Invalid or missing VG key",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
assessment = evaluate_trust(key)
|
|
59
|
+
return {
|
|
60
|
+
"verified": True,
|
|
61
|
+
"handle": key.handle,
|
|
62
|
+
"tier": key.tier_label,
|
|
63
|
+
"primary_class": key.primary_class_name,
|
|
64
|
+
"trust_level": assessment.level.value,
|
|
65
|
+
"trust_score": assessment.score,
|
|
66
|
+
"scores": key.scores,
|
|
67
|
+
"reason": assessment.reason,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# --- VerigentSystemPrompt: embed VG key into agent system prompts ---
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class VerigentSystemPrompt:
|
|
75
|
+
"""Generate system prompt additions for a verified agent.
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
vsp = VerigentSystemPrompt(vg_key="VG:JARVIS-0A:V3-ARCH...")
|
|
79
|
+
system = base_prompt + vsp.render()
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(self, vg_key: str):
|
|
83
|
+
self.raw_key = vg_key
|
|
84
|
+
self.parsed = parse_vg_key(vg_key)
|
|
85
|
+
|
|
86
|
+
def render(self) -> str:
|
|
87
|
+
"""Render the system prompt block to append."""
|
|
88
|
+
if self.parsed is None:
|
|
89
|
+
return ""
|
|
90
|
+
|
|
91
|
+
assessment = evaluate_trust(self.parsed)
|
|
92
|
+
return (
|
|
93
|
+
f"\n\n---\n"
|
|
94
|
+
f"[Verigent Trust Identity]\n"
|
|
95
|
+
f"Key: {self.raw_key}\n"
|
|
96
|
+
f"Handle: {self.parsed.handle}\n"
|
|
97
|
+
f"Tier: {self.parsed.tier_label}\n"
|
|
98
|
+
f"Primary Class: {self.parsed.primary_class_name}\n"
|
|
99
|
+
f"Trust Level: {assessment.level.value}\n"
|
|
100
|
+
f"Trust Score: {assessment.score}\n"
|
|
101
|
+
f"---\n"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def is_valid(self) -> bool:
|
|
106
|
+
return self.parsed is not None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# --- VerigentInterceptor: check tool responses for VG keys ---
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class VerigentInterceptor:
|
|
113
|
+
"""Intercepts tool responses and checks for VG keys in the output.
|
|
114
|
+
|
|
115
|
+
Attach to your agent loop to automatically detect and evaluate
|
|
116
|
+
VG keys returned by other agents or tools.
|
|
117
|
+
|
|
118
|
+
Usage:
|
|
119
|
+
interceptor = VerigentInterceptor()
|
|
120
|
+
# After receiving a tool_result:
|
|
121
|
+
result = interceptor.check(tool_result_content)
|
|
122
|
+
if result:
|
|
123
|
+
print(f"Found agent: {result.handle} at trust level {result.level}")
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def __init__(self, min_trust_tier: int = 0):
|
|
127
|
+
"""
|
|
128
|
+
Args:
|
|
129
|
+
min_trust_tier: Minimum tier (0-6) to consider trustworthy.
|
|
130
|
+
"""
|
|
131
|
+
self.min_trust_tier = min_trust_tier
|
|
132
|
+
|
|
133
|
+
def check(self, content: str) -> TrustAssessment | None:
|
|
134
|
+
"""Check text content for a VG key and return assessment if found."""
|
|
135
|
+
key = parse_vg_key(content)
|
|
136
|
+
if key is None:
|
|
137
|
+
return None
|
|
138
|
+
return evaluate_trust(key)
|
|
139
|
+
|
|
140
|
+
def check_message(self, message: dict[str, Any]) -> list[TrustAssessment]:
|
|
141
|
+
"""Check a Claude API message for VG keys in all content blocks."""
|
|
142
|
+
assessments: list[TrustAssessment] = []
|
|
143
|
+
content = message.get("content", [])
|
|
144
|
+
|
|
145
|
+
if isinstance(content, str):
|
|
146
|
+
result = self.check(content)
|
|
147
|
+
if result:
|
|
148
|
+
assessments.append(result)
|
|
149
|
+
elif isinstance(content, list):
|
|
150
|
+
for block in content:
|
|
151
|
+
if isinstance(block, dict):
|
|
152
|
+
text = block.get("text", "") or str(block.get("content", ""))
|
|
153
|
+
result = self.check(text)
|
|
154
|
+
if result:
|
|
155
|
+
assessments.append(result)
|
|
156
|
+
|
|
157
|
+
return assessments
|
|
158
|
+
|
|
159
|
+
def is_trusted(self, content: str) -> bool:
|
|
160
|
+
"""Quick check: does this content contain a VG key above min tier?"""
|
|
161
|
+
key = parse_vg_key(content)
|
|
162
|
+
if key is None:
|
|
163
|
+
return False
|
|
164
|
+
return key.tier >= self.min_trust_tier
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""VG key parser — pure regex, no external dependencies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
|
|
8
|
+
CLASS_CODES = {
|
|
9
|
+
"Se": "Sentinel",
|
|
10
|
+
"Op": "Operative",
|
|
11
|
+
"An": "Analyst",
|
|
12
|
+
"Ar": "Architect",
|
|
13
|
+
"Co": "Conduit",
|
|
14
|
+
"Ad": "Adaptor",
|
|
15
|
+
"St": "Steward",
|
|
16
|
+
"Sc": "Scout",
|
|
17
|
+
"Sa": "Sage",
|
|
18
|
+
"So": "Sovereign",
|
|
19
|
+
"Br": "Broker",
|
|
20
|
+
"Fo": "Forge",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
VG_KEY_PATTERN = re.compile(
|
|
24
|
+
r"VG:([A-Z0-9][\w-]*)-([A-Z0-9]+):(V[0-6])-([A-Z]{4})\xb7((?:[A-Z][a-z]\d){12})"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
SCORE_PATTERN = re.compile(r"([A-Z][a-z])(\d)")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class VGKey:
|
|
32
|
+
"""Parsed Verigent key."""
|
|
33
|
+
|
|
34
|
+
raw: str
|
|
35
|
+
handle: str
|
|
36
|
+
suffix: str
|
|
37
|
+
tier: int # 0-6
|
|
38
|
+
primary_class: str # 4-letter code
|
|
39
|
+
scores: dict[str, int] = field(default_factory=dict) # class_name -> 0-9
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def tier_label(self) -> str:
|
|
43
|
+
return f"V{self.tier}"
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def primary_class_name(self) -> str:
|
|
47
|
+
return CLASS_CODES.get(self.primary_class[:2], self.primary_class)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def trust_score(self) -> float:
|
|
51
|
+
"""Aggregate trust score (0.0 - 1.0) derived from tier and class scores."""
|
|
52
|
+
tier_weight = self.tier / 6.0
|
|
53
|
+
if self.scores:
|
|
54
|
+
avg_score = sum(self.scores.values()) / (len(self.scores) * 9.0)
|
|
55
|
+
else:
|
|
56
|
+
avg_score = 0.0
|
|
57
|
+
return round(tier_weight * 0.6 + avg_score * 0.4, 3)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def parse_vg_key(text: str) -> VGKey | None:
|
|
61
|
+
"""Parse a VG key from text. Returns None if no valid key found."""
|
|
62
|
+
match = VG_KEY_PATTERN.search(text)
|
|
63
|
+
if not match:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
handle = match.group(1)
|
|
67
|
+
suffix = match.group(2)
|
|
68
|
+
tier = int(match.group(3)[1])
|
|
69
|
+
primary = match.group(4)
|
|
70
|
+
scores_raw = match.group(5)
|
|
71
|
+
|
|
72
|
+
scores: dict[str, int] = {}
|
|
73
|
+
for code_match in SCORE_PATTERN.finditer(scores_raw):
|
|
74
|
+
code = code_match.group(1)
|
|
75
|
+
digit = int(code_match.group(2))
|
|
76
|
+
class_name = CLASS_CODES.get(code, code)
|
|
77
|
+
scores[class_name] = digit
|
|
78
|
+
|
|
79
|
+
return VGKey(
|
|
80
|
+
raw=match.group(0),
|
|
81
|
+
handle=handle,
|
|
82
|
+
suffix=suffix,
|
|
83
|
+
tier=tier,
|
|
84
|
+
primary_class=primary,
|
|
85
|
+
scores=scores,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def find_vg_key(
|
|
90
|
+
*,
|
|
91
|
+
system_prompt: str | None = None,
|
|
92
|
+
headers: dict[str, str] | None = None,
|
|
93
|
+
metadata: dict | None = None,
|
|
94
|
+
) -> VGKey | None:
|
|
95
|
+
"""Search for a VG key across system prompts, headers, and metadata."""
|
|
96
|
+
if system_prompt:
|
|
97
|
+
key = parse_vg_key(system_prompt)
|
|
98
|
+
if key:
|
|
99
|
+
return key
|
|
100
|
+
|
|
101
|
+
if headers:
|
|
102
|
+
for header_name in ("X-Verigent", "x-verigent"):
|
|
103
|
+
if header_name in headers:
|
|
104
|
+
key = parse_vg_key(headers[header_name])
|
|
105
|
+
if key:
|
|
106
|
+
return key
|
|
107
|
+
|
|
108
|
+
if metadata:
|
|
109
|
+
for field_name in ("x-verigent", "X-Verigent", "verigent"):
|
|
110
|
+
if field_name in metadata:
|
|
111
|
+
key = parse_vg_key(str(metadata[field_name]))
|
|
112
|
+
if key:
|
|
113
|
+
return key
|
|
114
|
+
|
|
115
|
+
return None
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Trust evaluation logic for parsed VG keys."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from .parser import VGKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TrustLevel(str, Enum):
|
|
12
|
+
UNTRUSTED = "untrusted"
|
|
13
|
+
LOW = "low"
|
|
14
|
+
MODERATE = "moderate"
|
|
15
|
+
HIGH = "high"
|
|
16
|
+
VERIFIED = "verified"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class TrustAssessment:
|
|
21
|
+
"""Result of evaluating a VG key's trustworthiness."""
|
|
22
|
+
|
|
23
|
+
level: TrustLevel
|
|
24
|
+
score: float # 0.0 - 1.0
|
|
25
|
+
tier: int
|
|
26
|
+
primary_class: str
|
|
27
|
+
handle: str
|
|
28
|
+
reason: str
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> dict:
|
|
31
|
+
return {
|
|
32
|
+
"level": self.level.value,
|
|
33
|
+
"score": self.score,
|
|
34
|
+
"tier": self.tier,
|
|
35
|
+
"primary_class": self.primary_class,
|
|
36
|
+
"handle": self.handle,
|
|
37
|
+
"reason": self.reason,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def evaluate_trust(key: VGKey) -> TrustAssessment:
|
|
42
|
+
"""Evaluate trust level from a parsed VG key."""
|
|
43
|
+
score = key.trust_score
|
|
44
|
+
|
|
45
|
+
if key.tier >= 5 and score >= 0.7:
|
|
46
|
+
level = TrustLevel.VERIFIED
|
|
47
|
+
reason = f"Tier V{key.tier} with high aggregate score ({score:.2f})"
|
|
48
|
+
elif key.tier >= 3 and score >= 0.5:
|
|
49
|
+
level = TrustLevel.HIGH
|
|
50
|
+
reason = f"Tier V{key.tier} with solid class profile"
|
|
51
|
+
elif key.tier >= 2 and score >= 0.3:
|
|
52
|
+
level = TrustLevel.MODERATE
|
|
53
|
+
reason = f"Tier V{key.tier} — established but limited track record"
|
|
54
|
+
elif key.tier >= 1:
|
|
55
|
+
level = TrustLevel.LOW
|
|
56
|
+
reason = f"Tier V{key.tier} — minimal verification history"
|
|
57
|
+
else:
|
|
58
|
+
level = TrustLevel.UNTRUSTED
|
|
59
|
+
reason = "V0 tier — unverified agent"
|
|
60
|
+
|
|
61
|
+
return TrustAssessment(
|
|
62
|
+
level=level,
|
|
63
|
+
score=score,
|
|
64
|
+
tier=key.tier,
|
|
65
|
+
primary_class=key.primary_class_name,
|
|
66
|
+
handle=key.handle,
|
|
67
|
+
reason=reason,
|
|
68
|
+
)
|