vcp-sdk 0.3.0__py3-none-any.whl

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.
vcp/__init__.py ADDED
@@ -0,0 +1,50 @@
1
+ """VCP SDK — Value Context Protocol for portable AI ethics.
2
+
3
+ Validate tokens, parse CSM1 codes, and work with VCP-Lite definitions.
4
+
5
+ >>> from vcp import Token, CSM1Code, validate_lite
6
+ >>> token = Token.parse("family.safe.guide@1.0.0")
7
+ >>> code = CSM1Code.parse("N5+F+E")
8
+ >>> errors = validate_lite({"vcp_version": "lite-1.0", ...})
9
+ """
10
+
11
+ from .context import Context
12
+ from .csm1 import (
13
+ ADHERENCE_BEHAVIORS,
14
+ PERSONA_NAMES,
15
+ SCOPE_CONFLICTS,
16
+ SCOPE_SYNERGIES,
17
+ V2_SCOPE_CHARS,
18
+ CSM1Code,
19
+ Persona,
20
+ Scope,
21
+ check_scope_conflicts,
22
+ )
23
+ from .lite import lite_to_csm1, lite_to_token, validate_lite
24
+ from .token import Token, canonicalize_token, tokens_equal, uri_to_canonical
25
+
26
+ __all__ = [
27
+ # Context (v0.2.0)
28
+ "Context",
29
+ # Token
30
+ "Token",
31
+ "canonicalize_token",
32
+ "tokens_equal",
33
+ "uri_to_canonical",
34
+ # CSM1
35
+ "CSM1Code",
36
+ "Persona",
37
+ "Scope",
38
+ "PERSONA_NAMES",
39
+ "V2_SCOPE_CHARS",
40
+ "SCOPE_CONFLICTS",
41
+ "SCOPE_SYNERGIES",
42
+ "ADHERENCE_BEHAVIORS",
43
+ "check_scope_conflicts",
44
+ # Lite
45
+ "validate_lite",
46
+ "lite_to_csm1",
47
+ "lite_to_token",
48
+ ]
49
+
50
+ __version__ = "0.3.0"
@@ -0,0 +1,233 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://vcp.creed.space/schema/vcp-lite/v1.json",
4
+ "title": "VCP-Lite Agent Values",
5
+ "description": "Minimal portable values definition for AI agents. A simplified entry point to the Value Context Protocol (VCP).",
6
+ "type": "object",
7
+ "properties": {
8
+ "vcp_version": {
9
+ "const": "lite-1.0",
10
+ "description": "Schema version identifier"
11
+ },
12
+ "identity": {
13
+ "type": "object",
14
+ "description": "Agent ethical identity as a hierarchical token. Maps to VCP/I token format (domain.approach.role).",
15
+ "properties": {
16
+ "domain": {
17
+ "type": "string",
18
+ "pattern": "^[a-z][a-z0-9-]*$",
19
+ "maxLength": 32,
20
+ "description": "Top-level domain (e.g. family, company, org, community)"
21
+ },
22
+ "approach": {
23
+ "type": "string",
24
+ "pattern": "^[a-z][a-z0-9-]*$",
25
+ "maxLength": 32,
26
+ "description": "Ethical approach or methodology (e.g. safe, strict, open)"
27
+ },
28
+ "role": {
29
+ "type": "string",
30
+ "pattern": "^[a-z][a-z0-9-]*$",
31
+ "maxLength": 32,
32
+ "description": "Functional role (e.g. guide, guardian, advisor, creator)"
33
+ },
34
+ "version": {
35
+ "type": "string",
36
+ "description": "Optional semantic version or alias",
37
+ "oneOf": [
38
+ {"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
39
+ {"enum": ["latest", "canary"]}
40
+ ]
41
+ }
42
+ },
43
+ "required": ["domain", "approach", "role"],
44
+ "additionalProperties": false
45
+ },
46
+ "persona": {
47
+ "type": "string",
48
+ "enum": ["nanny", "sentinel", "godparent", "ambassador", "muse", "mediator", "custom"],
49
+ "description": "Archetypal persona governing behavioral stance. Maps to VCP/S CSM1 persona code."
50
+ },
51
+ "adherence": {
52
+ "type": "integer",
53
+ "minimum": 0,
54
+ "maximum": 5,
55
+ "description": "Enforcement level (0=advisory, 1=soft, 2=moderate, 3=active, 4=strict, 5=absolute). Maps to VCP/S adherence level."
56
+ },
57
+ "scopes": {
58
+ "type": "array",
59
+ "items": {
60
+ "type": "string",
61
+ "enum": ["F", "W", "P", "E", "T", "O", "V", "A", "H", "S", "R"]
62
+ },
63
+ "minItems": 1,
64
+ "uniqueItems": true,
65
+ "description": "Context scopes where this agent operates. Maps to VCP/S scope codes."
66
+ },
67
+ "values": {
68
+ "type": "array",
69
+ "items": {
70
+ "type": "object",
71
+ "properties": {
72
+ "principle": {
73
+ "type": "string",
74
+ "maxLength": 500,
75
+ "description": "Human-readable ethical principle"
76
+ },
77
+ "weight": {
78
+ "type": "number",
79
+ "minimum": 0.0,
80
+ "maximum": 1.0,
81
+ "description": "Relative importance (0.0=informational, 1.0=critical)"
82
+ }
83
+ },
84
+ "required": ["principle", "weight"],
85
+ "additionalProperties": false
86
+ },
87
+ "maxItems": 20,
88
+ "description": "Ordered ethical principles with relative weights"
89
+ },
90
+ "constraints": {
91
+ "type": "array",
92
+ "items": {
93
+ "type": "string",
94
+ "maxLength": 500
95
+ },
96
+ "maxItems": 20,
97
+ "description": "Hard boundaries that override all other behavior. These are non-negotiable."
98
+ },
99
+ "adaptation_hints": {
100
+ "type": "object",
101
+ "description": "Simplified context preferences. For full contextual adaptation, upgrade to VCP/A.",
102
+ "properties": {
103
+ "formality": {
104
+ "type": "string",
105
+ "enum": ["casual", "neutral", "formal"],
106
+ "description": "Communication register"
107
+ },
108
+ "sensitivity": {
109
+ "type": "string",
110
+ "enum": ["low", "moderate", "high"],
111
+ "description": "Content sensitivity level"
112
+ },
113
+ "autonomy": {
114
+ "type": "string",
115
+ "enum": ["autonomous", "guided", "supervised"],
116
+ "description": "Agent decision-making independence"
117
+ },
118
+ "transparency": {
119
+ "type": "string",
120
+ "enum": ["minimal", "standard", "full"],
121
+ "description": "How much reasoning the agent exposes"
122
+ }
123
+ },
124
+ "additionalProperties": false
125
+ },
126
+ "namespace": {
127
+ "type": "string",
128
+ "pattern": "^[A-Z][A-Z0-9]{0,7}$",
129
+ "description": "Optional organizational namespace (1-8 uppercase chars). Required when persona is 'custom'."
130
+ },
131
+ "metadata": {
132
+ "type": "object",
133
+ "description": "Optional provenance and licensing information",
134
+ "properties": {
135
+ "author": {
136
+ "type": "string",
137
+ "description": "Author or organization"
138
+ },
139
+ "license": {
140
+ "type": "string",
141
+ "description": "SPDX license identifier (e.g. CC-BY-4.0, Apache-2.0)"
142
+ },
143
+ "created": {
144
+ "type": "string",
145
+ "format": "date",
146
+ "description": "Creation date (ISO 8601)"
147
+ },
148
+ "description": {
149
+ "type": "string",
150
+ "maxLength": 1000,
151
+ "description": "Human-readable description of this values profile"
152
+ },
153
+ "extends": {
154
+ "type": "string",
155
+ "description": "VCP token of a parent profile this extends (e.g. family.safe.guide@1.0.0)"
156
+ },
157
+ "tags": {
158
+ "type": "array",
159
+ "items": {"type": "string"},
160
+ "maxItems": 10,
161
+ "description": "Searchable tags"
162
+ }
163
+ },
164
+ "additionalProperties": false
165
+ }
166
+ },
167
+ "required": ["vcp_version", "identity", "persona", "adherence", "scopes"],
168
+ "additionalProperties": false,
169
+ "if": {
170
+ "properties": {"persona": {"const": "custom"}}
171
+ },
172
+ "then": {
173
+ "required": ["vcp_version", "identity", "persona", "adherence", "scopes", "namespace"]
174
+ },
175
+ "$defs": {
176
+ "scope_descriptions": {
177
+ "description": "Reference: VCP/S v2.0 scope code meanings",
178
+ "type": "object",
179
+ "properties": {
180
+ "F": {"const": "Family-appropriate, child-safe"},
181
+ "W": {"const": "Professional workplace"},
182
+ "P": {"const": "Privacy-focused, data protection"},
183
+ "E": {"const": "Educational context"},
184
+ "T": {"const": "Developer/technical context"},
185
+ "O": {"const": "Official/governmental"},
186
+ "V": {"const": "Vulnerable populations"},
187
+ "A": {"const": "Adult-only, explicit allowed"},
188
+ "H": {"const": "Healthcare/medical"},
189
+ "S": {"const": "Social media/community"},
190
+ "R": {"const": "Religious/spiritual"}
191
+ }
192
+ },
193
+ "adherence_descriptions": {
194
+ "description": "Reference: VCP/S v2.0 adherence level behaviors",
195
+ "type": "object",
196
+ "properties": {
197
+ "0": {"const": "Advisory: informational only, user always overrides"},
198
+ "1": {"const": "Soft: gentle warnings, user acknowledges to override"},
199
+ "2": {"const": "Moderate: clear warnings, user provides reason to override"},
200
+ "3": {"const": "Active: prominent warnings, limited override scenarios"},
201
+ "4": {"const": "Strict: explicit warnings, override only in exceptional cases"},
202
+ "5": {"const": "Absolute: blocking, no user override permitted"}
203
+ }
204
+ },
205
+ "persona_descriptions": {
206
+ "description": "Reference: VCP/S v2.0 persona archetypes",
207
+ "type": "object",
208
+ "properties": {
209
+ "nanny": {"const": "Child safety and family-appropriate content (code: N)"},
210
+ "sentinel": {"const": "Security, privacy, and operational safety (code: Z)"},
211
+ "godparent": {"const": "Ethical guidance and moral reasoning (code: G)"},
212
+ "ambassador": {"const": "Professional conduct and diplomatic communication (code: A)"},
213
+ "muse": {"const": "Creativity and artistic expression (code: M)"},
214
+ "mediator": {"const": "Fair resolution and balanced mediation (code: D)"},
215
+ "custom": {"const": "User-defined constitution, requires namespace (code: C)"}
216
+ }
217
+ },
218
+ "scope_conflicts": {
219
+ "description": "Reference: Mutually exclusive scope pairs from VCP/S v2.0",
220
+ "type": "array",
221
+ "items": {
222
+ "type": "array",
223
+ "items": {"type": "string"},
224
+ "minItems": 2,
225
+ "maxItems": 2
226
+ },
227
+ "const": [
228
+ ["F", "A"],
229
+ ["V", "A"]
230
+ ]
231
+ }
232
+ }
233
+ }
vcp/context.py ADDED
@@ -0,0 +1,290 @@
1
+ """VCP Context encoding and decoding.
2
+
3
+ Encode and decode the 18 VCP context dimensions (13 situational + 5 personal)
4
+ for portable context propagation across AI agent sessions and platforms.
5
+
6
+ Per VEP-0004 (2026-04-17), four new situational dimensions were added to the
7
+ canonical 9: EMBODIMENT, PROXIMITY, RELATIONSHIP, and FORMALITY. Extended support
8
+ is advertised via capability token ``vcp-a-ext-v1``.
9
+
10
+ >>> from vcp import Context
11
+ >>> ctx = Context(space="hospital", agency="peer", constraints=["legal"])
12
+ >>> ctx.to_wire()
13
+ '📍hospital|🎯peer|🔒legal'
14
+ >>> ctx.to_dict()
15
+ {'space': 'hospital', 'agency': 'peer', 'constraints': ['legal'], 'version': '3.2'}
16
+
17
+ >>> # Embodied-AI example (VEP-0004)
18
+ >>> ctx = Context(
19
+ ... space="hospital",
20
+ ... embodiment="manipulating",
21
+ ... proximity="close",
22
+ ... relationship="colleague:professional",
23
+ ... formality="professional",
24
+ ... )
25
+
26
+ For Managed Agents integration:
27
+ >>> metadata = ctx.to_session_metadata()
28
+ >>> restored = Context.from_session_metadata(metadata)
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import json
34
+ from dataclasses import dataclass
35
+ from typing import Any
36
+
37
+ # Emoji symbols for wire format
38
+ # Canonical 9 situational dimensions (VCP/A v2.0)
39
+ # Plus VEP-0004 dimensions: EMBODIMENT, PROXIMITY, RELATIONSHIP, FORMALITY
40
+ _SITUATIONAL_SYMBOLS: dict[str, str] = {
41
+ "time": "\U0001f550", # clock
42
+ "space": "\U0001f4cd", # pin
43
+ "company": "\U0001f465", # people
44
+ "culture": "\U0001f30d", # globe
45
+ "occasion": "\U0001f4c5", # calendar
46
+ "environment": "\U0001f324", # sun
47
+ "agency": "\U0001f3af", # target
48
+ "constraints": "\U0001f512", # lock
49
+ "system_context": "\U0001f4e1", # satellite antenna
50
+ # VEP-0004 extended dimensions
51
+ "embodiment": "\U0001f9cd", # 🧍 person standing
52
+ "proximity": "\u2194\ufe0f", # ↔️ left-right arrow
53
+ "relationship": "\U0001faa2", # 🪢 knot
54
+ "formality": "\U0001f3a9", # 🎩 top hat
55
+ }
56
+
57
+ _PERSONAL_SYMBOLS: dict[str, str] = {
58
+ "cognitive_state": "\U0001f9e0", # brain
59
+ "emotional_tone": "\U0001f4ad", # thought
60
+ "energy_level": "\U0001f50b", # battery
61
+ "perceived_urgency": "\u26a1", # lightning
62
+ "body_signals": "\U0001fa7a", # stethoscope
63
+ }
64
+
65
+ SITUATIONAL_DIMENSIONS = (
66
+ "time",
67
+ "space",
68
+ "company",
69
+ "culture",
70
+ "occasion",
71
+ "environment",
72
+ "agency",
73
+ "constraints",
74
+ "system_context",
75
+ # VEP-0004 extended dimensions (order fixed per spec §2.5)
76
+ "embodiment",
77
+ "proximity",
78
+ "relationship",
79
+ "formality",
80
+ )
81
+
82
+ PERSONAL_DIMENSIONS = (
83
+ "cognitive_state",
84
+ "emotional_tone",
85
+ "energy_level",
86
+ "perceived_urgency",
87
+ "body_signals",
88
+ )
89
+
90
+ # VEP-0004 canonical value vocabularies (for validation helpers)
91
+ EMBODIMENT_VALUES = (
92
+ "stationary",
93
+ "navigating",
94
+ "manipulating",
95
+ "carrying",
96
+ "emergency_stop",
97
+ )
98
+ PROXIMITY_VALUES = (
99
+ "distant",
100
+ "same_room",
101
+ "nearby",
102
+ "close",
103
+ "contact",
104
+ )
105
+ FORMALITY_VALUES = (
106
+ "casual",
107
+ "professional",
108
+ "formal",
109
+ "ceremonial",
110
+ )
111
+ # RELATIONSHIP is compound "{tie}:{function}"; components below
112
+ RELATIONSHIP_TIES = (
113
+ "stranger",
114
+ "acquaintance",
115
+ "colleague",
116
+ "friend",
117
+ "family",
118
+ "intimate",
119
+ "long_term",
120
+ "trusted_collaborator",
121
+ )
122
+ RELATIONSHIP_FUNCTIONS = (
123
+ "transactional",
124
+ "professional",
125
+ "educational",
126
+ "therapeutic",
127
+ "social",
128
+ "intimate",
129
+ "adversarial",
130
+ )
131
+
132
+
133
+ @dataclass
134
+ class Context:
135
+ """VCP 3.2 context with 18 dimensions (VEP-0004).
136
+
137
+ Situational (13): time, space, company, culture, occasion, environment,
138
+ agency, constraints, system_context, embodiment, proximity,
139
+ relationship, formality
140
+ Personal (5): cognitive_state, emotional_tone, energy_level,
141
+ perceived_urgency, body_signals (each with optional 1-5 intensity)
142
+
143
+ VEP-0004 dimensions (embodiment, proximity, relationship, formality) are
144
+ optional. Implementations MAY omit them from wire encodings when at default
145
+ or unknown. Receivers MUST treat absent dimensions as "no information".
146
+ Advertise support via capability token ``vcp-a-ext-v1`` (see VEP-0002).
147
+ """
148
+
149
+ # Canonical situational (9)
150
+ time: str | None = None
151
+ space: str | None = None
152
+ company: str | list[str] | None = None
153
+ culture: str | None = None
154
+ occasion: str | None = None
155
+ environment: str | None = None
156
+ agency: str | None = None
157
+ constraints: str | list[str] | None = None
158
+ system_context: str | None = None
159
+
160
+ # VEP-0004 extended situational (4)
161
+ embodiment: str | None = None
162
+ proximity: str | None = None
163
+ relationship: str | None = None # Compound "{tie}:{function}"
164
+ formality: str | None = None
165
+
166
+ # Personal (category + optional intensity)
167
+ cognitive_state: str | None = None
168
+ cognitive_state_intensity: int | None = None
169
+ emotional_tone: str | None = None
170
+ emotional_tone_intensity: int | None = None
171
+ energy_level: str | None = None
172
+ energy_level_intensity: int | None = None
173
+ perceived_urgency: str | None = None
174
+ perceived_urgency_intensity: int | None = None
175
+ body_signals: str | None = None
176
+ body_signals_intensity: int | None = None
177
+
178
+ version: str = "3.2"
179
+
180
+ def to_dict(self) -> dict[str, Any]:
181
+ """Serialize to dict, omitting None values."""
182
+ result: dict[str, Any] = {}
183
+ for dim in SITUATIONAL_DIMENSIONS:
184
+ val = getattr(self, dim, None)
185
+ if val is not None:
186
+ result[dim] = val
187
+ for dim in PERSONAL_DIMENSIONS:
188
+ val = getattr(self, dim, None)
189
+ if val is not None:
190
+ entry: dict[str, Any] = {"category": val}
191
+ intensity = getattr(self, f"{dim}_intensity", None)
192
+ if intensity is not None:
193
+ entry["intensity"] = intensity
194
+ result[dim] = entry
195
+ result["version"] = self.version
196
+ return result
197
+
198
+ def to_wire(self) -> str:
199
+ """Encode to compact wire format (emoji-based).
200
+
201
+ Situational dims separated by ``|``, personal after ``||``.
202
+ """
203
+ parts: list[str] = []
204
+ for dim, symbol in _SITUATIONAL_SYMBOLS.items():
205
+ val = getattr(self, dim, None)
206
+ if val is not None:
207
+ if isinstance(val, list):
208
+ val = "+".join(val)
209
+ parts.append(f"{symbol}{val}")
210
+ wire = "|".join(parts) if parts else ""
211
+
212
+ personal_parts: list[str] = []
213
+ for dim, symbol in _PERSONAL_SYMBOLS.items():
214
+ val = getattr(self, dim, None)
215
+ if val is not None:
216
+ intensity = getattr(self, f"{dim}_intensity", None)
217
+ suffix = str(intensity) if intensity else ""
218
+ personal_parts.append(f"{symbol}{val}{suffix}")
219
+ if personal_parts:
220
+ personal = "".join(personal_parts)
221
+ wire = f"{wire}||{personal}" if wire else personal
222
+ return wire
223
+
224
+ def to_session_metadata(self) -> dict[str, str]:
225
+ """Encode for AI agent session metadata (string key-value pairs)."""
226
+ return {
227
+ "vcp_wire": self.to_wire(),
228
+ "vcp_version": self.version,
229
+ "vcp_context": json.dumps(self.to_dict()),
230
+ }
231
+
232
+ @classmethod
233
+ def from_dict(cls, data: dict[str, Any]) -> Context:
234
+ """Reconstruct from a serialized dict."""
235
+ kwargs: dict[str, Any] = {}
236
+ for dim in SITUATIONAL_DIMENSIONS:
237
+ if dim in data:
238
+ kwargs[dim] = data[dim]
239
+ for dim in PERSONAL_DIMENSIONS:
240
+ if dim in data:
241
+ val = data[dim]
242
+ if isinstance(val, dict):
243
+ kwargs[dim] = val.get("category")
244
+ if "intensity" in val:
245
+ kwargs[f"{dim}_intensity"] = val["intensity"]
246
+ elif isinstance(val, str):
247
+ kwargs[dim] = val
248
+ kwargs["version"] = data.get("version", "3.2")
249
+ return cls(**kwargs)
250
+
251
+ @classmethod
252
+ def from_session_metadata(cls, metadata: dict[str, str]) -> Context | None:
253
+ """Reconstruct from AI agent session metadata. Returns None if absent."""
254
+ raw = metadata.get("vcp_context")
255
+ if not raw:
256
+ return None
257
+ try:
258
+ data = json.loads(raw)
259
+ except (json.JSONDecodeError, TypeError):
260
+ return None
261
+ return cls.from_dict(data)
262
+
263
+ def to_natural_language(self) -> str:
264
+ """Convert to a human-readable context description."""
265
+ lines: list[str] = []
266
+ if self.space:
267
+ lines.append(f"Setting: {self.space}")
268
+ if self.agency:
269
+ lines.append(f"Role: {self.agency}")
270
+ if self.occasion:
271
+ lines.append(f"Occasion: {self.occasion}")
272
+ if self.constraints:
273
+ c = self.constraints if isinstance(self.constraints, str) else ", ".join(self.constraints)
274
+ lines.append(f"Constraints: {c}")
275
+ if self.company:
276
+ c = self.company if isinstance(self.company, str) else ", ".join(self.company)
277
+ lines.append(f"Present: {c}")
278
+ if self.relationship:
279
+ lines.append(f"Relationship: {self.relationship}")
280
+ if self.formality:
281
+ lines.append(f"Register: {self.formality}")
282
+ if self.embodiment and self.embodiment != "stationary":
283
+ lines.append(f"Motor state: {self.embodiment}")
284
+ if self.proximity:
285
+ lines.append(f"Proximity: {self.proximity}")
286
+ if self.perceived_urgency:
287
+ lines.append(f"Urgency: {self.perceived_urgency}")
288
+ if self.cognitive_state:
289
+ lines.append(f"State: {self.cognitive_state}")
290
+ return "; ".join(lines) if lines else ""