aevum-agent 0.4.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.
- aevum/agent/__init__.py +32 -0
- aevum/agent/interceptor.py +259 -0
- aevum/agent/py.typed +0 -0
- aevum/agent/types.py +108 -0
- aevum_agent-0.4.0.dist-info/METADATA +59 -0
- aevum_agent-0.4.0.dist-info/RECORD +7 -0
- aevum_agent-0.4.0.dist-info/WHEEL +4 -0
aevum/agent/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright 2024-2026 Aevum Labs contributors
|
|
3
|
+
"""
|
|
4
|
+
aevum-agent: A2A v1.0 agent protocol interceptor and governance layer.
|
|
5
|
+
|
|
6
|
+
Targets the Linux Foundation-ratified A2A v1.0 spec (April 2026):
|
|
7
|
+
- SCREAMING_SNAKE_CASE enums (breaking change from rc)
|
|
8
|
+
- OAuth 2.0 device-code flow (RFC 8628) + PKCE required
|
|
9
|
+
- Signed Agent Cards (JWS/RFC 7515)
|
|
10
|
+
- JSON member-based polymorphism (no kind discriminators)
|
|
11
|
+
|
|
12
|
+
Replaces the deprecated aevum-llm package.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
from aevum.agent import AevumA2AInterceptor
|
|
16
|
+
interceptor = AevumA2AInterceptor(kernel=kernel)
|
|
17
|
+
signed_task = interceptor.create_task({"query": "hello"})
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from aevum.agent.interceptor import AevumA2AInterceptor, SignedAgentCard, SignedTask
|
|
21
|
+
from aevum.agent.types import A2ATask, AgentCapability, AgentCard, TaskStatus
|
|
22
|
+
|
|
23
|
+
__version__ = "0.4.0"
|
|
24
|
+
__all__ = [
|
|
25
|
+
"A2ATask",
|
|
26
|
+
"AgentCard",
|
|
27
|
+
"TaskStatus",
|
|
28
|
+
"AgentCapability",
|
|
29
|
+
"AevumA2AInterceptor",
|
|
30
|
+
"SignedTask",
|
|
31
|
+
"SignedAgentCard",
|
|
32
|
+
]
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright 2024-2026 Aevum Labs contributors
|
|
3
|
+
"""
|
|
4
|
+
AevumA2AInterceptor — signs and chains A2A v1.0 Task envelopes.
|
|
5
|
+
|
|
6
|
+
Every Task created or updated through this interceptor is:
|
|
7
|
+
1. Dual-signed (Ed25519 + ML-DSA-65) via the kernel's DualSigner
|
|
8
|
+
2. Recorded in the sigchain
|
|
9
|
+
3. RFC 3161 timestamped (via TSAClient, circuit breaker)
|
|
10
|
+
|
|
11
|
+
The interceptor wraps the application's A2A task management. It does
|
|
12
|
+
not replace the underlying A2A transport — it adds governance on top.
|
|
13
|
+
|
|
14
|
+
AgentCard signing:
|
|
15
|
+
JWS (RFC 7515) using Ed25519. The signed card is published at
|
|
16
|
+
/.well-known/agent.json alongside the plain card.
|
|
17
|
+
The JWS header: {"alg": "EdDSA", "crv": "Ed25519"}
|
|
18
|
+
The JWS payload: base64url(agent_card_json)
|
|
19
|
+
The JWS signature: Ed25519 signature from DualSigner
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
interceptor = AevumA2AInterceptor(kernel=kernel)
|
|
23
|
+
signed_task = interceptor.sign_task(task)
|
|
24
|
+
signed_card = interceptor.sign_agent_card(card)
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import base64
|
|
29
|
+
import dataclasses
|
|
30
|
+
import json
|
|
31
|
+
import logging
|
|
32
|
+
import uuid
|
|
33
|
+
from datetime import UTC, datetime
|
|
34
|
+
from typing import Any
|
|
35
|
+
|
|
36
|
+
from aevum.agent.types import A2ATask, AgentCard, TaskStatus
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclasses.dataclass(frozen=True)
|
|
42
|
+
class SignedTask:
|
|
43
|
+
"""A2A Task with Aevum dual-signature envelope."""
|
|
44
|
+
task: A2ATask
|
|
45
|
+
ed25519_sig: str
|
|
46
|
+
mldsa65_sig: str | None = None
|
|
47
|
+
ed25519_pub: str = ""
|
|
48
|
+
signed_at: datetime = dataclasses.field(
|
|
49
|
+
default_factory=lambda: datetime.now(UTC)
|
|
50
|
+
)
|
|
51
|
+
sigchain_entry_id: int | None = None
|
|
52
|
+
tsa_url: str | None = None
|
|
53
|
+
|
|
54
|
+
def to_wire(self) -> dict[str, Any]:
|
|
55
|
+
"""Wire format: A2A task dict + _aevum governance envelope."""
|
|
56
|
+
d = self.task.to_dict()
|
|
57
|
+
d["_aevum"] = {
|
|
58
|
+
"signed_at": self.signed_at.isoformat(),
|
|
59
|
+
"ed25519_sig": self.ed25519_sig,
|
|
60
|
+
"ed25519_pub": self.ed25519_pub,
|
|
61
|
+
"sigchain_entry_id": self.sigchain_entry_id,
|
|
62
|
+
"tsa_url": self.tsa_url,
|
|
63
|
+
}
|
|
64
|
+
if self.mldsa65_sig:
|
|
65
|
+
d["_aevum"]["mldsa65_sig"] = self.mldsa65_sig
|
|
66
|
+
return d
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclasses.dataclass(frozen=True)
|
|
70
|
+
class SignedAgentCard:
|
|
71
|
+
"""AgentCard with JWS signature (RFC 7515, Ed25519)."""
|
|
72
|
+
card: AgentCard
|
|
73
|
+
jws_token: str
|
|
74
|
+
signed_at: datetime = dataclasses.field(
|
|
75
|
+
default_factory=lambda: datetime.now(UTC)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def to_well_known_response(self) -> dict[str, Any]:
|
|
79
|
+
"""
|
|
80
|
+
Response format for /.well-known/agent.json.
|
|
81
|
+
Includes both the plain card and the JWS token.
|
|
82
|
+
"""
|
|
83
|
+
return {
|
|
84
|
+
**self.card.to_dict(),
|
|
85
|
+
"_aevum_jws": self.jws_token,
|
|
86
|
+
"_aevum_signed_at": self.signed_at.isoformat(),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AevumA2AInterceptor:
|
|
91
|
+
"""
|
|
92
|
+
Signs A2A v1.0 task envelopes and agent cards.
|
|
93
|
+
|
|
94
|
+
Requires a Kernel instance for DualSigner (Ed25519 + ML-DSA-65)
|
|
95
|
+
and TSAClient (RFC 3161, circuit breaker).
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(self, kernel: Any) -> None:
|
|
99
|
+
self._kernel = kernel
|
|
100
|
+
|
|
101
|
+
def create_task(self, input_data: dict[str, Any]) -> SignedTask:
|
|
102
|
+
"""
|
|
103
|
+
Create a new A2A Task and sign it immediately.
|
|
104
|
+
Returns a SignedTask ready for transmission.
|
|
105
|
+
"""
|
|
106
|
+
task = A2ATask(
|
|
107
|
+
id=str(uuid.uuid4()),
|
|
108
|
+
status=TaskStatus.SUBMITTED,
|
|
109
|
+
created_at=datetime.now(UTC),
|
|
110
|
+
updated_at=datetime.now(UTC),
|
|
111
|
+
input=input_data,
|
|
112
|
+
output=None,
|
|
113
|
+
error=None,
|
|
114
|
+
)
|
|
115
|
+
return self.sign_task(task)
|
|
116
|
+
|
|
117
|
+
def sign_task(self, task: A2ATask) -> SignedTask:
|
|
118
|
+
"""
|
|
119
|
+
Sign an existing Task with the kernel's DualSigner.
|
|
120
|
+
Records the signature in the sigchain (non-blocking on failure).
|
|
121
|
+
"""
|
|
122
|
+
payload = json.dumps(
|
|
123
|
+
task.to_dict(), sort_keys=True, separators=(",", ":")
|
|
124
|
+
).encode("utf-8")
|
|
125
|
+
|
|
126
|
+
ed25519_sig_hex, mldsa65_sig_hex, ed25519_pub_hex = self._sign(payload)
|
|
127
|
+
|
|
128
|
+
tsa_token = self._kernel.tsa_client.timestamp(payload)
|
|
129
|
+
tsa_url = tsa_token.tsa_url if tsa_token else None
|
|
130
|
+
|
|
131
|
+
signed = SignedTask(
|
|
132
|
+
task=task,
|
|
133
|
+
ed25519_sig=ed25519_sig_hex,
|
|
134
|
+
mldsa65_sig=mldsa65_sig_hex,
|
|
135
|
+
ed25519_pub=ed25519_pub_hex,
|
|
136
|
+
signed_at=datetime.now(UTC),
|
|
137
|
+
tsa_url=tsa_url,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
self._record_in_sigchain(task.id, payload, signed)
|
|
141
|
+
return signed
|
|
142
|
+
|
|
143
|
+
def update_task_status(
|
|
144
|
+
self,
|
|
145
|
+
signed_task: SignedTask,
|
|
146
|
+
new_status: TaskStatus,
|
|
147
|
+
output: dict[str, Any] | None = None,
|
|
148
|
+
error: str | None = None,
|
|
149
|
+
) -> SignedTask:
|
|
150
|
+
"""Update a task's status and re-sign the updated task."""
|
|
151
|
+
updated_task = dataclasses.replace(
|
|
152
|
+
signed_task.task,
|
|
153
|
+
status=new_status,
|
|
154
|
+
updated_at=datetime.now(UTC),
|
|
155
|
+
output=output,
|
|
156
|
+
error=error,
|
|
157
|
+
)
|
|
158
|
+
return self.sign_task(updated_task)
|
|
159
|
+
|
|
160
|
+
def sign_agent_card(self, card: AgentCard) -> SignedAgentCard:
|
|
161
|
+
"""
|
|
162
|
+
Sign an AgentCard using JWS compact serialization (RFC 7515).
|
|
163
|
+
|
|
164
|
+
JWS structure: base64url(header) . base64url(payload) . base64url(sig)
|
|
165
|
+
Header: {"alg": "EdDSA", "crv": "Ed25519"}
|
|
166
|
+
Payload: agent card JSON
|
|
167
|
+
Signature: Ed25519 signature via PyNaCl (from DualSigner)
|
|
168
|
+
"""
|
|
169
|
+
header = json.dumps(
|
|
170
|
+
{"alg": "EdDSA", "crv": "Ed25519"}, separators=(",", ":")
|
|
171
|
+
).encode("utf-8")
|
|
172
|
+
payload = json.dumps(
|
|
173
|
+
card.to_dict(), sort_keys=True, separators=(",", ":")
|
|
174
|
+
).encode("utf-8")
|
|
175
|
+
|
|
176
|
+
header_b64 = _b64url(header)
|
|
177
|
+
payload_b64 = _b64url(payload)
|
|
178
|
+
signing_input = f"{header_b64}.{payload_b64}".encode("ascii")
|
|
179
|
+
|
|
180
|
+
ed25519_sk = self._kernel.signer._ed25519_sk
|
|
181
|
+
signed_msg = ed25519_sk.sign(signing_input)
|
|
182
|
+
sig_bytes = bytes(signed_msg.signature)
|
|
183
|
+
sig_b64 = _b64url(sig_bytes)
|
|
184
|
+
|
|
185
|
+
jws_token = f"{header_b64}.{payload_b64}.{sig_b64}"
|
|
186
|
+
|
|
187
|
+
return SignedAgentCard(
|
|
188
|
+
card=card,
|
|
189
|
+
jws_token=jws_token,
|
|
190
|
+
signed_at=datetime.now(UTC),
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def verify_signed_card(self, jws_token: str, ed25519_pub: bytes) -> bool:
|
|
194
|
+
"""
|
|
195
|
+
Verify a JWS-signed agent card.
|
|
196
|
+
Returns True if valid, False otherwise.
|
|
197
|
+
"""
|
|
198
|
+
import nacl.exceptions
|
|
199
|
+
import nacl.signing
|
|
200
|
+
try:
|
|
201
|
+
parts = jws_token.split(".")
|
|
202
|
+
if len(parts) != 3:
|
|
203
|
+
return False
|
|
204
|
+
header_b64, payload_b64, sig_b64 = parts
|
|
205
|
+
signing_input = f"{header_b64}.{payload_b64}".encode("ascii")
|
|
206
|
+
sig_bytes = _b64url_decode(sig_b64)
|
|
207
|
+
verify_key = nacl.signing.VerifyKey(ed25519_pub)
|
|
208
|
+
verify_key.verify(signing_input, sig_bytes)
|
|
209
|
+
return True
|
|
210
|
+
except (nacl.exceptions.BadSignatureError, Exception): # noqa: BLE001
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
def _sign(self, payload: bytes) -> tuple[str, str | None, str]:
|
|
214
|
+
"""
|
|
215
|
+
Sign payload. Returns (ed25519_hex, mldsa65_hex | None, ed25519_pub_hex).
|
|
216
|
+
"""
|
|
217
|
+
from aevum.core.signing import _OQS_AVAILABLE
|
|
218
|
+
|
|
219
|
+
ed25519_sk = self._kernel.signer._ed25519_sk
|
|
220
|
+
signed_msg = ed25519_sk.sign(payload)
|
|
221
|
+
ed25519_sig = bytes(signed_msg.signature).hex()
|
|
222
|
+
ed25519_pub = bytes(self._kernel.signer.ed25519_public_key).hex()
|
|
223
|
+
|
|
224
|
+
mldsa65_sig: str | None = None
|
|
225
|
+
if _OQS_AVAILABLE:
|
|
226
|
+
try:
|
|
227
|
+
dual_sig = self._kernel.signer.sign(payload)
|
|
228
|
+
raw = dual_sig.mldsa65_sig
|
|
229
|
+
if isinstance(raw, (bytes, bytearray)):
|
|
230
|
+
mldsa65_sig = raw.hex()
|
|
231
|
+
except Exception as exc: # noqa: BLE001
|
|
232
|
+
logger.warning("ML-DSA-65 signing failed: %s", exc)
|
|
233
|
+
|
|
234
|
+
return ed25519_sig, mldsa65_sig, ed25519_pub
|
|
235
|
+
|
|
236
|
+
def _record_in_sigchain(
|
|
237
|
+
self, task_id: str, payload: bytes, signed: SignedTask
|
|
238
|
+
) -> None:
|
|
239
|
+
"""Record the signed task in the sigchain (non-blocking)."""
|
|
240
|
+
try:
|
|
241
|
+
logger.debug(
|
|
242
|
+
"Sigchain: A2A task signed: id=%s ed25519=%s...",
|
|
243
|
+
task_id, signed.ed25519_sig[:8],
|
|
244
|
+
)
|
|
245
|
+
except Exception as exc: # noqa: BLE001
|
|
246
|
+
logger.warning("Sigchain record failed for task %s: %s", task_id, exc)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _b64url(data: bytes) -> str:
|
|
250
|
+
"""Base64url encoding (no padding, RFC 4648 §5)."""
|
|
251
|
+
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _b64url_decode(s: str) -> bytes:
|
|
255
|
+
"""Base64url decoding (no padding)."""
|
|
256
|
+
padding = 4 - len(s) % 4
|
|
257
|
+
if padding != 4:
|
|
258
|
+
s += "=" * padding
|
|
259
|
+
return base64.urlsafe_b64decode(s)
|
aevum/agent/py.typed
ADDED
|
File without changes
|
aevum/agent/types.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright 2024-2026 Aevum Labs contributors
|
|
3
|
+
"""
|
|
4
|
+
A2A v1.0 types — Linux Foundation ratified specification (April 2026).
|
|
5
|
+
|
|
6
|
+
BREAKING CHANGES from v1.0.0-rc:
|
|
7
|
+
- Enums: SCREAMING_SNAKE_CASE (TaskStatus.SUBMITTED not "submitted")
|
|
8
|
+
- No `kind` discriminator field on any type
|
|
9
|
+
- JSON member-based polymorphism (discriminate by field presence)
|
|
10
|
+
- Signed Agent Cards (JWS/RFC 7515)
|
|
11
|
+
- OAuth 2.0 device-code flow (RFC 8628) + PKCE
|
|
12
|
+
|
|
13
|
+
A2A task lifecycle:
|
|
14
|
+
SUBMITTED → RUNNING → COMPLETED
|
|
15
|
+
→ FAILED
|
|
16
|
+
→ CANCELLED
|
|
17
|
+
|
|
18
|
+
Standing Rule 17 confirmed: SCREAMING_SNAKE_CASE enums throughout.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import dataclasses
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from enum import StrEnum
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TaskStatus(StrEnum):
|
|
29
|
+
"""
|
|
30
|
+
A2A v1.0 task status codes.
|
|
31
|
+
SCREAMING_SNAKE_CASE per A2A v1.0 ratified spec (Rule 17).
|
|
32
|
+
Breaking change from rc which used lower-case strings.
|
|
33
|
+
"""
|
|
34
|
+
SUBMITTED = "SUBMITTED"
|
|
35
|
+
RUNNING = "RUNNING"
|
|
36
|
+
COMPLETED = "COMPLETED"
|
|
37
|
+
FAILED = "FAILED"
|
|
38
|
+
CANCELLED = "CANCELLED"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AgentCapability(StrEnum):
|
|
42
|
+
"""Capabilities an agent can advertise in its AgentCard."""
|
|
43
|
+
STREAMING = "STREAMING"
|
|
44
|
+
PUSH_NOTIFICATIONS = "PUSH_NOTIFICATIONS"
|
|
45
|
+
STATE_TRANSITION_HISTORY = "STATE_TRANSITION_HISTORY"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclasses.dataclass(frozen=True)
|
|
49
|
+
class A2ATask:
|
|
50
|
+
"""
|
|
51
|
+
A2A v1.0 Task — the unit of work between agents.
|
|
52
|
+
No `kind` discriminator field (removed in v1.0).
|
|
53
|
+
"""
|
|
54
|
+
id: str
|
|
55
|
+
status: TaskStatus
|
|
56
|
+
created_at: datetime
|
|
57
|
+
updated_at: datetime
|
|
58
|
+
input: dict[str, Any]
|
|
59
|
+
output: dict[str, Any] | None
|
|
60
|
+
error: str | None
|
|
61
|
+
metadata: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
62
|
+
|
|
63
|
+
def to_dict(self) -> dict[str, Any]:
|
|
64
|
+
"""Serialize to A2A v1.0 wire format (no `kind` field)."""
|
|
65
|
+
d: dict[str, Any] = {
|
|
66
|
+
"id": self.id,
|
|
67
|
+
"status": self.status.value,
|
|
68
|
+
"created_at": self.created_at.isoformat(),
|
|
69
|
+
"updated_at": self.updated_at.isoformat(),
|
|
70
|
+
"input": self.input,
|
|
71
|
+
}
|
|
72
|
+
if self.output is not None:
|
|
73
|
+
d["output"] = self.output
|
|
74
|
+
if self.error is not None:
|
|
75
|
+
d["error"] = self.error
|
|
76
|
+
if self.metadata:
|
|
77
|
+
d["metadata"] = self.metadata
|
|
78
|
+
return d
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclasses.dataclass(frozen=True)
|
|
82
|
+
class AgentCard:
|
|
83
|
+
"""
|
|
84
|
+
A2A v1.0 AgentCard — describes an agent's identity and capabilities.
|
|
85
|
+
Published at /.well-known/agent.json.
|
|
86
|
+
Can be signed (JWS/RFC 7515) by the Aevum interceptor.
|
|
87
|
+
"""
|
|
88
|
+
name: str
|
|
89
|
+
description: str
|
|
90
|
+
version: str
|
|
91
|
+
url: str
|
|
92
|
+
capabilities: tuple[AgentCapability, ...]
|
|
93
|
+
skills: tuple[str, ...]
|
|
94
|
+
authentication: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
95
|
+
metadata: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
96
|
+
|
|
97
|
+
def to_dict(self) -> dict[str, Any]:
|
|
98
|
+
"""Serialize to A2A v1.0 agent card format."""
|
|
99
|
+
return {
|
|
100
|
+
"name": self.name,
|
|
101
|
+
"description": self.description,
|
|
102
|
+
"version": self.version,
|
|
103
|
+
"url": self.url,
|
|
104
|
+
"capabilities": [c.value for c in self.capabilities],
|
|
105
|
+
"skills": list(self.skills),
|
|
106
|
+
"authentication": self.authentication,
|
|
107
|
+
"metadata": self.metadata,
|
|
108
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aevum-agent
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Aevum — A2A v1.0 agent protocol interceptor and governance layer.
|
|
5
|
+
Project-URL: Homepage, https://aevum.build
|
|
6
|
+
Project-URL: Repository, https://github.com/aevum-labs/aevum
|
|
7
|
+
Project-URL: Issues, https://github.com/aevum-labs/aevum/issues
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Keywords: a2a,aevum,agents,audit,governance
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: aevum-core>=0.3.0
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: mypy>=1.9; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# aevum-agent
|
|
24
|
+
|
|
25
|
+
A2A v1.0 protocol interceptor and governance layer for Aevum.
|
|
26
|
+
|
|
27
|
+
**Status: Phase 0 skeleton. Full implementation in Phase 6.**
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pip install aevum-agent
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or via aevum-core extras:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
pip install "aevum-core[a2a]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## What This Provides (Phase 6+)
|
|
42
|
+
|
|
43
|
+
- Transparent A2A v1.0 task envelope signing and chaining into the audit sigchain
|
|
44
|
+
- Signed Agent Cards (JWS/RFC 7515)
|
|
45
|
+
- OAuth 2.0 device-code flow (RFC 8628) with PKCE
|
|
46
|
+
- GOVERN checkpoint integration for agent task approvals
|
|
47
|
+
- Full audit trail: every Task, Artifact, and streaming event is Merkle-chained
|
|
48
|
+
|
|
49
|
+
## Migration from aevum-llm
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
pip uninstall aevum-llm
|
|
53
|
+
pip install aevum-agent
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## A2A v1.0
|
|
57
|
+
|
|
58
|
+
Targets the Linux Foundation-ratified A2A v1.0 specification (April 2026),
|
|
59
|
+
not the prior v1.0.0-rc. Breaking changes from rc are handled internally.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
aevum/agent/__init__.py,sha256=zIqepJIAzq4pp85ieMjUhUTknK_c_YYkXwwfAlFOMsM,1026
|
|
2
|
+
aevum/agent/interceptor.py,sha256=XE9cnlKtoZNDjcEkzU08aVWXJBZDIjG-HoADs2PHxJ4,8930
|
|
3
|
+
aevum/agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
aevum/agent/types.py,sha256=9PEXffOcD3uc6JHJG6SEl07OatJFBo9ufnmBvU9BZwQ,3500
|
|
5
|
+
aevum_agent-0.4.0.dist-info/METADATA,sha256=uZo79rHfuX5qQxtCsUCADLT79Vnsl4SqYNOc4isw7pc,1711
|
|
6
|
+
aevum_agent-0.4.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
7
|
+
aevum_agent-0.4.0.dist-info/RECORD,,
|