aether-context 0.3.1__tar.gz → 0.4.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.
- {aether_context-0.3.1/aether_context.egg-info → aether_context-0.4.0}/PKG-INFO +5 -1
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/__init__.py +1 -1
- aether_context-0.4.0/aether_context/contracts/__init__.py +534 -0
- aether_context-0.4.0/aether_context/contracts/schema-v1.json +2201 -0
- aether_context-0.4.0/aether_context/crypto.py +105 -0
- aether_context-0.4.0/aether_context/engine.py +1637 -0
- aether_context-0.4.0/aether_context/policy/__init__.py +66 -0
- aether_context-0.4.0/aether_context/retention.py +647 -0
- aether_context-0.4.0/aether_context/scale.py +298 -0
- aether_context-0.4.0/aether_context/service/__init__.py +279 -0
- aether_context-0.4.0/aether_context/storage_v2/__init__.py +125 -0
- {aether_context-0.3.1 → aether_context-0.4.0/aether_context.egg-info}/PKG-INFO +5 -1
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context.egg-info/SOURCES.txt +12 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context.egg-info/entry_points.txt +1 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context.egg-info/requires.txt +5 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/pyproject.toml +6 -3
- aether_context-0.4.0/tests/test_context_ipc.py +65 -0
- aether_context-0.4.0/tests/test_hosted_context.py +423 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_release_parity.py +16 -4
- aether_context-0.4.0/tests/test_retention_scale.py +1601 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/LICENSE +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/NOTICE.md +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/README.md +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/_log.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/cli.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/config.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/context_pool.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/encoder.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/errors.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/local_llm.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/mpo.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/py.typed +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/quantize.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/session.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/slice_loader.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/tokenizer.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/ui.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context/witness.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context.egg-info/dependency_links.txt +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/aether_context.egg-info/top_level.txt +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/setup.cfg +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_aether_agent.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_commands.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_config.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_library.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_profile.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_profile_commands.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_runner.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_runner_define_command.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_runner_lock.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_slash.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_slash_agents_columns.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_slash_cmd.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agent_store.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_agents_view.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_api_eval.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_auth.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_brains.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_bridge.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_cli.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_cli_disk.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_cli_dispatch.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_cli_doctor.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_cli_setup.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_cli_surface.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_config.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_context_pool.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_encoder.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_end_to_end.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_errors.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_hardware.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_install_security.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_local_llm.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_local_llm_openai.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_moat_seal.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_mpo.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_multi_runner.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_ollama_ctl.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_ollama_integration.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_onboarding.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_pool_quantize.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_profiles.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_progress.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_protocol_lockstep.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_quantize.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_repl_agents.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_repl_multi.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_repl_preflight.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_run_agent_events_system.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_safety.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_session.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_session_fallback.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_slash.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_slash_agents.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_slash_custom_invoke.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_slash_ollama.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_slice_loader.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_smoke.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_tokenizer.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_toolparse.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_transport.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_ui.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_web_tools.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_witness.py +0 -0
- {aether_context-0.3.1 → aether_context-0.4.0}/tests/test_workflow_yaml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aether-context
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Unlimited Context — virtual memory for an LLM's attention. Local-first, numpy-only core.
|
|
5
5
|
Author: Aether AI
|
|
6
6
|
Maintainer: Aether AI
|
|
@@ -34,6 +34,9 @@ Requires-Dist: transformers>=4.40; extra == "hf"
|
|
|
34
34
|
Requires-Dist: torch>=2.2; extra == "hf"
|
|
35
35
|
Provides-Extra: fast
|
|
36
36
|
Requires-Dist: hnswlib>=0.8; extra == "fast"
|
|
37
|
+
Provides-Extra: hosted
|
|
38
|
+
Requires-Dist: pydantic<3,>=2.10; extra == "hosted"
|
|
39
|
+
Requires-Dist: cryptography<51,>=44; extra == "hosted"
|
|
37
40
|
Provides-Extra: all
|
|
38
41
|
Requires-Dist: aether-context[fast,hf,llamacpp]; extra == "all"
|
|
39
42
|
Provides-Extra: dev
|
|
@@ -42,6 +45,7 @@ Requires-Dist: pytest-cov; extra == "dev"
|
|
|
42
45
|
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
43
46
|
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
44
47
|
Requires-Dist: pyyaml>=6; extra == "dev"
|
|
48
|
+
Requires-Dist: aether-context[hosted]; extra == "dev"
|
|
45
49
|
Dynamic: license-file
|
|
46
50
|
|
|
47
51
|
<div align="center">
|
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
"""Closed SC-CONTEXT-01 wire contracts. Requires the optional hosted extra."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from typing import Annotated, Literal
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
10
|
+
|
|
11
|
+
Ident = Annotated[str, Field(pattern=r"^[A-Za-z0-9][A-Za-z0-9_.:@/-]{0,199}$")]
|
|
12
|
+
Digest = Annotated[str, Field(pattern=r"^[0-9a-f]{64}$")]
|
|
13
|
+
Sha = Annotated[str, Field(pattern=r"^[0-9a-f]{40}$")]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def canonical(value: object) -> bytes:
|
|
17
|
+
"""Canonical JSON subset: UTF-8, sorted keys, integers, no floats or NaN.
|
|
18
|
+
|
|
19
|
+
Wire timestamps are integer Unix seconds. This is deliberately a smaller
|
|
20
|
+
domain than arbitrary JSON so JS and Python produce the same digest.
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(value, BaseModel):
|
|
23
|
+
value = value.model_dump(mode="json")
|
|
24
|
+
|
|
25
|
+
def validate(item: object) -> None:
|
|
26
|
+
if item is None or isinstance(item, (bool, str)):
|
|
27
|
+
return
|
|
28
|
+
if isinstance(item, int) and abs(item) <= 9007199254740991:
|
|
29
|
+
return
|
|
30
|
+
if isinstance(item, list):
|
|
31
|
+
for child in item:
|
|
32
|
+
validate(child)
|
|
33
|
+
return
|
|
34
|
+
if isinstance(item, dict) and all(isinstance(k, str) and k.isascii() for k in item):
|
|
35
|
+
for child in item.values():
|
|
36
|
+
validate(child)
|
|
37
|
+
return
|
|
38
|
+
raise ValueError("canonical JSON requires safe integers and string keys")
|
|
39
|
+
|
|
40
|
+
validate(value)
|
|
41
|
+
return json.dumps(
|
|
42
|
+
value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False
|
|
43
|
+
).encode("utf-8")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def digest(value: object) -> str:
|
|
47
|
+
return hashlib.sha256(canonical(value)).hexdigest()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Closed(BaseModel):
|
|
51
|
+
model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class NamespaceV1(Closed):
|
|
55
|
+
owner_id: Ident
|
|
56
|
+
project_id: Ident
|
|
57
|
+
objective_id: Ident
|
|
58
|
+
context_cycle_id: Ident
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ContextRequestV1(Closed):
|
|
62
|
+
schema_version: Literal["ContextRequestV1"] = "ContextRequestV1"
|
|
63
|
+
mode: Literal["required", "preferred", "disabled"] = "required"
|
|
64
|
+
pool_gb: int = Field(default=1, ge=1, le=32)
|
|
65
|
+
captain_mode: Literal["durable_handoff", "interactive_checkpoint", "interactive_required"] = (
|
|
66
|
+
"durable_handoff"
|
|
67
|
+
)
|
|
68
|
+
retention_class: Literal["ephemeral", "audit"] = "ephemeral"
|
|
69
|
+
resume_context_cycle_id: Ident | None = None
|
|
70
|
+
promotion_mode: Literal["verified_only", "disabled"] = "verified_only"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ContextProfileV1(Closed):
|
|
74
|
+
schema_version: Literal["ContextProfileV1"] = "ContextProfileV1"
|
|
75
|
+
name: Literal["bounded-canary/v1", "hosted-v1"] = "bounded-canary/v1"
|
|
76
|
+
index_implementation: Literal["sqlite-hmac-inverted/v1"] = "sqlite-hmac-inverted/v1"
|
|
77
|
+
index_version: str = Field(min_length=3, max_length=40)
|
|
78
|
+
embedding_version: Literal["none-lexical/v1"] = "none-lexical/v1"
|
|
79
|
+
storage_version: Literal["sqlite-wal-aes256gcm/v2"] = "sqlite-wal-aes256gcm/v2"
|
|
80
|
+
max_records: int = Field(default=10000, ge=1, le=1000000)
|
|
81
|
+
max_indexed_bytes: int = Field(default=64_000_000, ge=1024, le=32_000_000_000)
|
|
82
|
+
max_record_bytes: int = Field(default=32768, ge=256, le=262144)
|
|
83
|
+
max_capsule_tokens: int = Field(default=4096, ge=128, le=65536)
|
|
84
|
+
max_candidates: int = Field(default=256, ge=1, le=4096)
|
|
85
|
+
resident_cache_bytes: int = Field(default=8_388_608, ge=1_048_576, le=268_435_456)
|
|
86
|
+
disk_free_floor: int = Field(default=268_435_456, ge=0)
|
|
87
|
+
retention_seconds: int = Field(default=86400, ge=60, le=604800)
|
|
88
|
+
max_checkpoint_bytes: int = Field(default=64_000_000, ge=1024, le=128_000_000)
|
|
89
|
+
max_concurrent_cycles: int = Field(default=4, ge=1, le=64)
|
|
90
|
+
max_snapshot_bytes: int = Field(default=8_000_000, ge=1024, le=16_000_000)
|
|
91
|
+
max_operations_per_cycle: int = Field(default=20000, ge=16, le=2000000)
|
|
92
|
+
max_calls_per_minute: int = Field(default=600, ge=10, le=6000)
|
|
93
|
+
search_slo_ms: int = Field(default=500, ge=1, le=10000)
|
|
94
|
+
rebuild_slo_seconds: int = Field(default=300, ge=1, le=86400)
|
|
95
|
+
benchmark_receipt: Digest | None = None
|
|
96
|
+
|
|
97
|
+
@model_validator(mode="after")
|
|
98
|
+
def production_evidence(self) -> "ContextProfileV1":
|
|
99
|
+
if self.name == "hosted-v1" and self.benchmark_receipt is None:
|
|
100
|
+
raise ValueError("hosted-v1 requires a measured benchmark receipt")
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class ContextBindingV1(Closed):
|
|
105
|
+
schema_version: Literal["ContextBindingV1"] = "ContextBindingV1"
|
|
106
|
+
namespace: NamespaceV1
|
|
107
|
+
context_bucket_id: Ident
|
|
108
|
+
repository_id: Ident
|
|
109
|
+
repo_main_sha: Sha
|
|
110
|
+
project_graph_id: Ident
|
|
111
|
+
graph_revision: int = Field(ge=1)
|
|
112
|
+
graph_checksum: Digest
|
|
113
|
+
plan_digest: Digest
|
|
114
|
+
execution_profile_digest: Digest
|
|
115
|
+
shared_ir_digest: Digest
|
|
116
|
+
authorization_receipt_ref: Ident
|
|
117
|
+
authorization_digest: Digest
|
|
118
|
+
policy_digest: Digest
|
|
119
|
+
redaction_digest: Digest
|
|
120
|
+
profile_digest: Digest
|
|
121
|
+
captain_binding_id: Ident
|
|
122
|
+
retention_class: Literal["ephemeral", "audit"] = "ephemeral"
|
|
123
|
+
created_at: int = Field(ge=0)
|
|
124
|
+
expires_at: int = Field(ge=0)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class CapabilityV1(Closed):
|
|
128
|
+
schema_version: Literal["ContextCapabilityV1"] = "ContextCapabilityV1"
|
|
129
|
+
namespace: NamespaceV1
|
|
130
|
+
principal_id: Ident
|
|
131
|
+
lane_id: Ident
|
|
132
|
+
task_id: Ident
|
|
133
|
+
role: Literal["worker", "reviewer", "coordinator", "verifier", "captain", "durability"]
|
|
134
|
+
operations: list[
|
|
135
|
+
Literal[
|
|
136
|
+
"append",
|
|
137
|
+
"retrieve",
|
|
138
|
+
"checkpoint",
|
|
139
|
+
"seal",
|
|
140
|
+
"status",
|
|
141
|
+
"hydrate",
|
|
142
|
+
"expire",
|
|
143
|
+
"retention",
|
|
144
|
+
]
|
|
145
|
+
] = Field(min_length=1, max_length=8)
|
|
146
|
+
source_class: Literal[
|
|
147
|
+
"user_intent",
|
|
148
|
+
"project_memory_verified",
|
|
149
|
+
"repository_verified",
|
|
150
|
+
"tool_observation",
|
|
151
|
+
"ci_verified",
|
|
152
|
+
"worker_summary",
|
|
153
|
+
"model_note",
|
|
154
|
+
"legacy_untrusted",
|
|
155
|
+
]
|
|
156
|
+
binding_digest: Digest
|
|
157
|
+
policy_digest: Digest
|
|
158
|
+
profile_digest: Digest
|
|
159
|
+
fence: int = Field(ge=1)
|
|
160
|
+
expires_at: int = Field(ge=0)
|
|
161
|
+
max_bytes: int = Field(ge=1, le=262144)
|
|
162
|
+
max_tokens: int = Field(ge=1, le=65536)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class SignedV1(Closed):
|
|
166
|
+
payload: dict
|
|
167
|
+
key_id: Ident
|
|
168
|
+
signature: str = Field(min_length=64, max_length=128)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class PromotionCandidateV1(Closed):
|
|
172
|
+
record_id: Ident
|
|
173
|
+
content_digest: Digest
|
|
174
|
+
evidence_refs: list[Digest] = Field(min_length=1, max_length=32)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class ContextCheckpointReceiptV1(Closed):
|
|
178
|
+
schema_version: Literal["ContextCheckpointReceiptV1"] = "ContextCheckpointReceiptV1"
|
|
179
|
+
namespace: NamespaceV1
|
|
180
|
+
checkpoint_number: int = Field(ge=1)
|
|
181
|
+
cursor: int = Field(ge=0)
|
|
182
|
+
segment_chain_root: Digest
|
|
183
|
+
manifest_checksum: Digest
|
|
184
|
+
durability: Literal["local"]
|
|
185
|
+
bytes: int = Field(ge=1)
|
|
186
|
+
records: int = Field(ge=0)
|
|
187
|
+
policy_digest: Digest
|
|
188
|
+
key_ref: Ident | None = None
|
|
189
|
+
key_provider: str | None = Field(default=None, min_length=1, max_length=100)
|
|
190
|
+
key_version: Ident | None = None
|
|
191
|
+
|
|
192
|
+
@model_validator(mode="after")
|
|
193
|
+
def key_reference_pair(self) -> "ContextCheckpointReceiptV1":
|
|
194
|
+
if len({item is None for item in (self.key_ref, self.key_provider, self.key_version)}) != 1:
|
|
195
|
+
raise ValueError("checkpoint key reference, provider and version must be paired")
|
|
196
|
+
return self
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ContextHydrateReceiptV1(Closed):
|
|
200
|
+
"""Host-replacement evidence bound to one exact checkpoint replay."""
|
|
201
|
+
|
|
202
|
+
schema_version: Literal["ContextHydrateReceiptV1"] = "ContextHydrateReceiptV1"
|
|
203
|
+
operation: Literal["hydrate"] = "hydrate"
|
|
204
|
+
namespace: NamespaceV1
|
|
205
|
+
context_cycle_id: Ident
|
|
206
|
+
manifest_checksum: Digest
|
|
207
|
+
checkpoint_number: int = Field(ge=1)
|
|
208
|
+
cursor: int = Field(ge=0)
|
|
209
|
+
root: Digest
|
|
210
|
+
key_ref: Ident | None = None
|
|
211
|
+
key_provider: str | None = Field(default=None, min_length=1, max_length=100)
|
|
212
|
+
key_version: Ident | None = None
|
|
213
|
+
replay_digest: Digest
|
|
214
|
+
fence: int = Field(ge=1)
|
|
215
|
+
|
|
216
|
+
@model_validator(mode="after")
|
|
217
|
+
def key_reference_pair(self) -> "ContextHydrateReceiptV1":
|
|
218
|
+
if len({item is None for item in (self.key_ref, self.key_provider, self.key_version)}) != 1:
|
|
219
|
+
raise ValueError("hydrate key reference, provider and version must be paired")
|
|
220
|
+
if self.context_cycle_id != self.namespace.context_cycle_id:
|
|
221
|
+
raise ValueError("hydrate cycle does not match namespace")
|
|
222
|
+
return self
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class ContextFreezeReceiptV1(Closed):
|
|
226
|
+
schema_version: Literal["ContextFreezeReceiptV1"] = "ContextFreezeReceiptV1"
|
|
227
|
+
context_cycle_id: Ident
|
|
228
|
+
cursor: int = Field(ge=0)
|
|
229
|
+
root: Digest
|
|
230
|
+
binding_digest: Digest
|
|
231
|
+
promotion_candidates: list[PromotionCandidateV1] = Field(
|
|
232
|
+
default_factory=list, max_length=1000
|
|
233
|
+
)
|
|
234
|
+
promotion_candidates_root: Digest
|
|
235
|
+
|
|
236
|
+
@model_validator(mode="after")
|
|
237
|
+
def candidate_root(self) -> "ContextFreezeReceiptV1":
|
|
238
|
+
if digest([item.model_dump() for item in self.promotion_candidates]) != (
|
|
239
|
+
self.promotion_candidates_root
|
|
240
|
+
):
|
|
241
|
+
raise ValueError("promotion candidate root mismatch")
|
|
242
|
+
return self
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class AppendRequestV1(Closed):
|
|
246
|
+
idempotency_key: Ident
|
|
247
|
+
text: str = Field(min_length=1, max_length=262144)
|
|
248
|
+
plane: Literal["P2", "P3", "P4"] = "P3"
|
|
249
|
+
content_type: Literal[
|
|
250
|
+
"summary", "decision", "artifact_ref", "tool_evidence", "candidate_fact"
|
|
251
|
+
] = "summary"
|
|
252
|
+
action_ref: Ident
|
|
253
|
+
evidence_refs: list[Digest] = Field(default_factory=list, max_length=32)
|
|
254
|
+
supersedes: Ident | None = None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class RetrieveRequestV1(Closed):
|
|
258
|
+
turn_id: Ident
|
|
259
|
+
query: str = Field(max_length=8192)
|
|
260
|
+
native_window: int = Field(ge=256, le=10000000)
|
|
261
|
+
remaining_prompt_budget: int = Field(ge=0, le=10000000)
|
|
262
|
+
used_prompt_tokens: int = Field(default=0, ge=0, le=10000000)
|
|
263
|
+
event_cursor: int | None = Field(default=None, ge=0)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class RemoteDeletionReceiptV1(Closed):
|
|
267
|
+
"""Independent object-store evidence for the last committed checkpoint."""
|
|
268
|
+
|
|
269
|
+
schema_version: Literal["ContextRemoteDeletionReceiptV1"] = (
|
|
270
|
+
"ContextRemoteDeletionReceiptV1"
|
|
271
|
+
)
|
|
272
|
+
namespace: NamespaceV1
|
|
273
|
+
manifest_checksum: Digest
|
|
274
|
+
object_ref_digest: Digest
|
|
275
|
+
deleted: Literal[True]
|
|
276
|
+
issued_at: int = Field(ge=0)
|
|
277
|
+
expires_at: int = Field(ge=0)
|
|
278
|
+
|
|
279
|
+
@model_validator(mode="after")
|
|
280
|
+
def short_lived(self) -> "RemoteDeletionReceiptV1":
|
|
281
|
+
if self.expires_at <= self.issued_at or self.expires_at - self.issued_at > 900:
|
|
282
|
+
raise ValueError("remote deletion evidence must be short-lived")
|
|
283
|
+
return self
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class RetentionCommandV1(Closed):
|
|
287
|
+
"""Short-lived authority for an idempotent hold, release, or expiry."""
|
|
288
|
+
|
|
289
|
+
schema_version: Literal["ContextRetentionCommandV1"] = "ContextRetentionCommandV1"
|
|
290
|
+
operation: Literal["hold", "release", "expire"]
|
|
291
|
+
namespace: NamespaceV1
|
|
292
|
+
idempotency_key: Ident
|
|
293
|
+
expected_revision: int = Field(ge=1)
|
|
294
|
+
reason_code: Literal[
|
|
295
|
+
"audit_hold",
|
|
296
|
+
"legal_hold",
|
|
297
|
+
"incident_hold",
|
|
298
|
+
"hold_released",
|
|
299
|
+
"policy_expiry",
|
|
300
|
+
"operator_expiry",
|
|
301
|
+
]
|
|
302
|
+
remote_deletion_receipt: SignedV1 | None = None
|
|
303
|
+
checkpoint_object_ref_digest: Digest | None = None
|
|
304
|
+
issued_at: int = Field(ge=0)
|
|
305
|
+
expires_at: int = Field(ge=0)
|
|
306
|
+
|
|
307
|
+
@model_validator(mode="after")
|
|
308
|
+
def operation_reason(self) -> "RetentionCommandV1":
|
|
309
|
+
allowed = {
|
|
310
|
+
"hold": {"audit_hold", "legal_hold", "incident_hold"},
|
|
311
|
+
"release": {"hold_released"},
|
|
312
|
+
"expire": {"policy_expiry", "operator_expiry"},
|
|
313
|
+
}
|
|
314
|
+
if self.reason_code not in allowed[self.operation]:
|
|
315
|
+
raise ValueError("retention reason does not match operation")
|
|
316
|
+
if self.operation == "expire":
|
|
317
|
+
if (self.remote_deletion_receipt is None) != (
|
|
318
|
+
self.checkpoint_object_ref_digest is None
|
|
319
|
+
):
|
|
320
|
+
raise ValueError("remote deletion receipt and object reference must be paired")
|
|
321
|
+
elif (
|
|
322
|
+
self.remote_deletion_receipt is not None
|
|
323
|
+
or self.checkpoint_object_ref_digest is not None
|
|
324
|
+
):
|
|
325
|
+
raise ValueError("remote deletion evidence applies only to expiry")
|
|
326
|
+
if self.expires_at <= self.issued_at or self.expires_at - self.issued_at > 900:
|
|
327
|
+
raise ValueError("retention authority must be short-lived")
|
|
328
|
+
return self
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class ManagedKeyProviderReceiptV1(Closed):
|
|
332
|
+
"""Independent qualification for a shared managed key provider."""
|
|
333
|
+
|
|
334
|
+
schema_version: Literal["ContextManagedKeyProviderReceiptV1"] = (
|
|
335
|
+
"ContextManagedKeyProviderReceiptV1"
|
|
336
|
+
)
|
|
337
|
+
provider: str = Field(min_length=1, max_length=100)
|
|
338
|
+
key_version: Ident
|
|
339
|
+
shared_hydrate_supported: Literal[True]
|
|
340
|
+
verified_destruction_supported: Literal[True]
|
|
341
|
+
issued_at: int = Field(ge=0)
|
|
342
|
+
expires_at: int = Field(ge=0)
|
|
343
|
+
|
|
344
|
+
@model_validator(mode="after")
|
|
345
|
+
def bounded_qualification(self) -> "ManagedKeyProviderReceiptV1":
|
|
346
|
+
if self.expires_at <= self.issued_at or self.expires_at - self.issued_at > 604800:
|
|
347
|
+
raise ValueError("managed key qualification may be valid for at most seven days")
|
|
348
|
+
return self
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class KeyDestructionReceiptV1(Closed):
|
|
352
|
+
"""Independent managed-KMS evidence that an exact cycle key was destroyed."""
|
|
353
|
+
|
|
354
|
+
schema_version: Literal["ContextKeyDestructionReceiptV1"] = (
|
|
355
|
+
"ContextKeyDestructionReceiptV1"
|
|
356
|
+
)
|
|
357
|
+
provider: str = Field(min_length=1, max_length=100)
|
|
358
|
+
key_ref: Ident
|
|
359
|
+
namespace_digest: Digest
|
|
360
|
+
key_version: Ident
|
|
361
|
+
destroyed: Literal[True]
|
|
362
|
+
destroyed_at: int = Field(ge=0)
|
|
363
|
+
issued_at: int = Field(ge=0)
|
|
364
|
+
expires_at: int = Field(ge=0)
|
|
365
|
+
|
|
366
|
+
@model_validator(mode="after")
|
|
367
|
+
def short_lived(self) -> "KeyDestructionReceiptV1":
|
|
368
|
+
if not self.destroyed_at <= self.issued_at < self.expires_at:
|
|
369
|
+
raise ValueError("managed key destruction time ordering is invalid")
|
|
370
|
+
if self.expires_at - self.issued_at > 900:
|
|
371
|
+
raise ValueError("managed key destruction evidence must be short-lived")
|
|
372
|
+
return self
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class RuntimeBuildManifestV1(Closed):
|
|
376
|
+
"""Signed build identity for the exact installed runtime package bytes."""
|
|
377
|
+
|
|
378
|
+
schema_version: Literal["ContextRuntimeBuildManifestV1"] = (
|
|
379
|
+
"ContextRuntimeBuildManifestV1"
|
|
380
|
+
)
|
|
381
|
+
distribution: Literal["aether-context"] = "aether-context"
|
|
382
|
+
version: str = Field(min_length=1, max_length=40)
|
|
383
|
+
source_revision: Sha
|
|
384
|
+
package_tree_digest: Digest
|
|
385
|
+
wheel_digest: Digest
|
|
386
|
+
recall_dataset_digest: Digest
|
|
387
|
+
data_plane_case_generator_digest: Digest | None = None
|
|
388
|
+
built_at: int = Field(ge=0)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class ExecutorStabilityReceiptV1(Closed):
|
|
392
|
+
"""Independent CI/executor observation bound to one measured build."""
|
|
393
|
+
|
|
394
|
+
schema_version: Literal["ContextExecutorStabilityReceiptV1"] = (
|
|
395
|
+
"ContextExecutorStabilityReceiptV1"
|
|
396
|
+
)
|
|
397
|
+
source_revision: Sha
|
|
398
|
+
profile_benchmark_digest: Digest
|
|
399
|
+
target_concurrency: int = Field(ge=1)
|
|
400
|
+
ci_receipt_digest: Digest
|
|
401
|
+
executor_stable: Literal[True]
|
|
402
|
+
started_at: int = Field(ge=0)
|
|
403
|
+
finished_at: int = Field(ge=0)
|
|
404
|
+
issued_at: int = Field(ge=0)
|
|
405
|
+
expires_at: int = Field(ge=0)
|
|
406
|
+
|
|
407
|
+
@model_validator(mode="after")
|
|
408
|
+
def ordered_observation(self) -> "ExecutorStabilityReceiptV1":
|
|
409
|
+
if not self.started_at <= self.finished_at <= self.issued_at < self.expires_at:
|
|
410
|
+
raise ValueError("executor observation ordering is invalid")
|
|
411
|
+
if self.expires_at - self.issued_at > 604800:
|
|
412
|
+
raise ValueError("executor evidence may be valid for at most seven days")
|
|
413
|
+
return self
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class DataPlaneIsolationReceiptV1(Closed):
|
|
417
|
+
"""Independent randomized isolation evidence from the hosted request boundary."""
|
|
418
|
+
|
|
419
|
+
schema_version: Literal["ContextDataPlaneIsolationReceiptV1"] = (
|
|
420
|
+
"ContextDataPlaneIsolationReceiptV1"
|
|
421
|
+
)
|
|
422
|
+
source_revision: Sha
|
|
423
|
+
profile_benchmark_digest: Digest
|
|
424
|
+
boundary: Literal["hosted-data-plane/v1"] = "hosted-data-plane/v1"
|
|
425
|
+
case_generator_digest: Digest
|
|
426
|
+
cases: int = Field(ge=1_000_000)
|
|
427
|
+
unauthorized_records: int = Field(ge=0)
|
|
428
|
+
result_digest: Digest
|
|
429
|
+
started_at: int = Field(ge=0)
|
|
430
|
+
finished_at: int = Field(ge=0)
|
|
431
|
+
issued_at: int = Field(ge=0)
|
|
432
|
+
expires_at: int = Field(ge=0)
|
|
433
|
+
|
|
434
|
+
@model_validator(mode="after")
|
|
435
|
+
def ordered_observation(self) -> "DataPlaneIsolationReceiptV1":
|
|
436
|
+
if not self.started_at <= self.finished_at <= self.issued_at < self.expires_at:
|
|
437
|
+
raise ValueError("data-plane isolation observation ordering is invalid")
|
|
438
|
+
if self.expires_at - self.issued_at > 604800:
|
|
439
|
+
raise ValueError("data-plane isolation evidence may be valid for at most seven days")
|
|
440
|
+
return self
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class HostedScaleReceiptV1(Closed):
|
|
444
|
+
"""Independent, measured evidence for one exact hosted profile."""
|
|
445
|
+
|
|
446
|
+
schema_version: Literal["HostedScaleReceiptV1"] = "HostedScaleReceiptV1"
|
|
447
|
+
profile_benchmark_digest: Digest
|
|
448
|
+
source_revision: Sha
|
|
449
|
+
source_tree_clean: Literal[True]
|
|
450
|
+
index_implementation: str = Field(min_length=1, max_length=100)
|
|
451
|
+
index_version: str = Field(min_length=1, max_length=100)
|
|
452
|
+
embedding_version: str = Field(min_length=1, max_length=100)
|
|
453
|
+
storage_version: str = Field(min_length=1, max_length=100)
|
|
454
|
+
configured_max_records: int = Field(ge=1)
|
|
455
|
+
configured_max_indexed_bytes: int = Field(ge=1024)
|
|
456
|
+
indexed_records: int = Field(ge=1)
|
|
457
|
+
indexed_bytes: int = Field(ge=1)
|
|
458
|
+
reachable_tokens: int = Field(ge=1)
|
|
459
|
+
resident_peak_bytes: int = Field(ge=1)
|
|
460
|
+
retrieval_samples: int = Field(ge=20)
|
|
461
|
+
target_concurrency: int = Field(ge=1)
|
|
462
|
+
retrieval_p50_ms: int = Field(ge=0)
|
|
463
|
+
retrieval_p95_ms: int = Field(ge=0)
|
|
464
|
+
retrieval_p99_ms: int = Field(ge=0)
|
|
465
|
+
checkpoint_ms: int = Field(ge=0)
|
|
466
|
+
checkpoint_receipt_digest: Digest
|
|
467
|
+
hydrate_ms: int = Field(ge=0)
|
|
468
|
+
hydrate_receipt_digest: Digest
|
|
469
|
+
rebuild_ms: int = Field(ge=0)
|
|
470
|
+
rebuild_receipt_digest: Digest
|
|
471
|
+
recall_dataset_name: str = Field(min_length=1, max_length=100)
|
|
472
|
+
recall_dataset_digest: Digest
|
|
473
|
+
recall_basis_points: int = Field(ge=0, le=10000)
|
|
474
|
+
isolation_cases: int = Field(ge=1_000_000)
|
|
475
|
+
unauthorized_records: int = Field(ge=0)
|
|
476
|
+
isolation_result_digest: Digest
|
|
477
|
+
isolation_evidence_class: Literal["predicate", "data_plane"]
|
|
478
|
+
data_plane_isolation_receipt: SignedV1 | None = None
|
|
479
|
+
executor_stable: bool
|
|
480
|
+
executor_stability_receipt: SignedV1 | None = None
|
|
481
|
+
started_at: int = Field(ge=0)
|
|
482
|
+
finished_at: int = Field(ge=0)
|
|
483
|
+
issued_at: int = Field(ge=0)
|
|
484
|
+
expires_at: int = Field(ge=0)
|
|
485
|
+
|
|
486
|
+
@model_validator(mode="after")
|
|
487
|
+
def ordered_measurements(self) -> "HostedScaleReceiptV1":
|
|
488
|
+
if not (
|
|
489
|
+
self.retrieval_p50_ms <= self.retrieval_p95_ms <= self.retrieval_p99_ms
|
|
490
|
+
and self.started_at <= self.finished_at <= self.issued_at < self.expires_at
|
|
491
|
+
):
|
|
492
|
+
raise ValueError("scale measurement ordering is invalid")
|
|
493
|
+
if self.expires_at - self.issued_at > 604800:
|
|
494
|
+
raise ValueError("scale evidence may be valid for at most seven days")
|
|
495
|
+
if self.executor_stable != (self.executor_stability_receipt is not None):
|
|
496
|
+
raise ValueError("executor stability claim requires independent evidence")
|
|
497
|
+
if (self.isolation_evidence_class == "data_plane") != (
|
|
498
|
+
self.data_plane_isolation_receipt is not None
|
|
499
|
+
):
|
|
500
|
+
raise ValueError("data-plane isolation claim requires independent evidence")
|
|
501
|
+
return self
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
class ClosureProofV1(Closed):
|
|
505
|
+
schema_version: Literal["ContextClosureProofV1"] = "ContextClosureProofV1"
|
|
506
|
+
namespace: NamespaceV1
|
|
507
|
+
binding_digest: Digest
|
|
508
|
+
final_root: Digest
|
|
509
|
+
final_cursor: int = Field(ge=0)
|
|
510
|
+
execution_dag_digest: Digest
|
|
511
|
+
plan_ir_digest: Digest
|
|
512
|
+
shared_ir_digest: Digest
|
|
513
|
+
repo_main_sha: Sha
|
|
514
|
+
git_head_sha: Sha
|
|
515
|
+
pr_head_sha: Sha
|
|
516
|
+
pr_url: str = Field(pattern=r"^https://[^/?#]+/[^/?#]+/[^/?#]+/pull/[1-9][0-9]*$")
|
|
517
|
+
ci_receipt: Digest
|
|
518
|
+
nano_receipt: Digest
|
|
519
|
+
proof_receipt: Digest
|
|
520
|
+
memory_candidate_digest: Digest
|
|
521
|
+
promotion_set_root: Digest
|
|
522
|
+
accepted_record_ids: list[Ident] = Field(default_factory=list, max_length=1000)
|
|
523
|
+
rejected_record_ids: list[Ident] = Field(default_factory=list, max_length=1000)
|
|
524
|
+
expires_at: int = Field(ge=0)
|
|
525
|
+
|
|
526
|
+
@model_validator(mode="after")
|
|
527
|
+
def exact_heads(self) -> "ClosureProofV1":
|
|
528
|
+
if self.git_head_sha != self.pr_head_sha:
|
|
529
|
+
raise ValueError("PR head must equal the independently verified Git head")
|
|
530
|
+
if len(set(self.accepted_record_ids)) != len(self.accepted_record_ids):
|
|
531
|
+
raise ValueError("duplicate promoted record")
|
|
532
|
+
if set(self.accepted_record_ids) & set(self.rejected_record_ids):
|
|
533
|
+
raise ValueError("promotion sets overlap")
|
|
534
|
+
return self
|