flux-vm 0.1.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.
- flux/__init__.py +8 -0
- flux/__main__.py +6 -0
- flux/a2a/__init__.py +44 -0
- flux/a2a/coordinator.py +165 -0
- flux/a2a/messages.py +171 -0
- flux/a2a/primitives.py +730 -0
- flux/a2a/signal_compiler.py +465 -0
- flux/a2a/transport.py +81 -0
- flux/a2a/trust.py +363 -0
- flux/adaptive/__init__.py +59 -0
- flux/adaptive/compiler_bridge.py +284 -0
- flux/adaptive/profiler.py +441 -0
- flux/adaptive/selector.py +430 -0
- flux/asm/__init__.py +30 -0
- flux/asm/binary_patcher.py +243 -0
- flux/asm/cross_assembler.py +596 -0
- flux/asm/elf_header.py +369 -0
- flux/asm/errors.py +112 -0
- flux/asm/linker.py +247 -0
- flux/asm/macros.py +334 -0
- flux/asm/opcodes_compat.py +254 -0
- flux/bytecode/__init__.py +36 -0
- flux/bytecode/decoder.py +384 -0
- flux/bytecode/encoder.py +444 -0
- flux/bytecode/formats.py +249 -0
- flux/bytecode/isa_unified.py +462 -0
- flux/bytecode/opcodes.py +261 -0
- flux/bytecode/opcodes_legacy.py +234 -0
- flux/bytecode/validator.py +316 -0
- flux/cli.py +1120 -0
- flux/compiler/__init__.py +0 -0
- flux/compiler/pipeline.py +96 -0
- flux/cost/__init__.py +24 -0
- flux/cost/energy.py +216 -0
- flux/cost/model.py +289 -0
- flux/creative/__init__.py +59 -0
- flux/creative/generative.py +573 -0
- flux/creative/live.py +439 -0
- flux/creative/sonification.py +445 -0
- flux/creative/visualization.py +279 -0
- flux/debugger.py +644 -0
- flux/disasm.py +456 -0
- flux/docs/__init__.py +33 -0
- flux/docs/generator.py +330 -0
- flux/docs/introspector.py +386 -0
- flux/docs/renderer.py +284 -0
- flux/docs/stats.py +261 -0
- flux/evolution/__init__.py +76 -0
- flux/evolution/evolution.py +425 -0
- flux/evolution/genome.py +733 -0
- flux/evolution/mutator.py +452 -0
- flux/evolution/pattern_mining.py +402 -0
- flux/evolution/validator.py +394 -0
- flux/fir/__init__.py +94 -0
- flux/fir/blocks.py +45 -0
- flux/fir/builder.py +255 -0
- flux/fir/instructions.py +606 -0
- flux/fir/printer.py +273 -0
- flux/fir/types.py +174 -0
- flux/fir/validator.py +211 -0
- flux/fir/values.py +22 -0
- flux/flywheel/__init__.py +59 -0
- flux/flywheel/engine.py +787 -0
- flux/flywheel/hypothesis.py +280 -0
- flux/flywheel/knowledge.py +435 -0
- flux/flywheel/metrics.py +215 -0
- flux/frontend/__init__.py +0 -0
- flux/frontend/c_frontend.py +842 -0
- flux/frontend/python_frontend.py +585 -0
- flux/jit/__init__.py +33 -0
- flux/jit/cache.py +187 -0
- flux/jit/compiler.py +356 -0
- flux/jit/ir_optimize.py +547 -0
- flux/jit/tracing.py +339 -0
- flux/mega/__init__.py +34 -0
- flux/mega/conductor.py +855 -0
- flux/mega/demo_mega.py +93 -0
- flux/memory/__init__.py +46 -0
- flux/memory/bandit.py +275 -0
- flux/memory/experience.py +429 -0
- flux/memory/learning.py +273 -0
- flux/memory/store.py +602 -0
- flux/migrate/__init__.py +10 -0
- flux/migrate/migrator.py +754 -0
- flux/migrate/report.py +124 -0
- flux/modules/__init__.py +48 -0
- flux/modules/card.py +117 -0
- flux/modules/container.py +316 -0
- flux/modules/granularity.py +128 -0
- flux/modules/namespace.py +87 -0
- flux/modules/reloader.py +322 -0
- flux/open_interp/__init__.py +16 -0
- flux/open_interp/argumentation.py +332 -0
- flux/open_interp/assembler.py +133 -0
- flux/open_interp/beachcomb.py +480 -0
- flux/open_interp/compiler.py +190 -0
- flux/open_interp/context_filter.py +62 -0
- flux/open_interp/contradiction_detector.py +311 -0
- flux/open_interp/decomposer.py +657 -0
- flux/open_interp/edge_profile.py +169 -0
- flux/open_interp/ethical_weight.py +49 -0
- flux/open_interp/ghost_loader.py +546 -0
- flux/open_interp/interpreter.py +257 -0
- flux/open_interp/l0_scrubber.py +487 -0
- flux/open_interp/necrosis_detector.py +128 -0
- flux/open_interp/paper_bridge.py +276 -0
- flux/open_interp/paper_decomposer.py +367 -0
- flux/open_interp/pruning.py +533 -0
- flux/open_interp/sandbox.py +141 -0
- flux/open_interp/semantic_router.py +282 -0
- flux/open_interp/term_obituary.py +173 -0
- flux/open_interp/tiling.py +327 -0
- flux/open_interp/vocab_signal.py +325 -0
- flux/open_interp/vocabulary.py +217 -0
- flux/open_interpreter.py +986 -0
- flux/optimizer/__init__.py +17 -0
- flux/optimizer/passes.py +105 -0
- flux/optimizer/pipeline.py +40 -0
- flux/parser/__init__.py +35 -0
- flux/parser/nodes.py +111 -0
- flux/parser/parser.py +437 -0
- flux/pipeline/__init__.py +20 -0
- flux/pipeline/debug.py +479 -0
- flux/pipeline/e2e.py +258 -0
- flux/pipeline/polyglot.py +234 -0
- flux/protocol/__init__.py +58 -0
- flux/protocol/channel.py +361 -0
- flux/protocol/message.py +323 -0
- flux/protocol/negotiation.py +367 -0
- flux/protocol/registry.py +242 -0
- flux/protocol/serialization.py +284 -0
- flux/py.typed +0 -0
- flux/reload/__init__.py +5 -0
- flux/reload/hot_loader.py +127 -0
- flux/repl.py +415 -0
- flux/retro/__init__.py +30 -0
- flux/retro/catalog.py +450 -0
- flux/retro/implementations/__init__.py +43 -0
- flux/retro/implementations/_asm.py +220 -0
- flux/retro/implementations/_builder.py +170 -0
- flux/retro/implementations/game_of_life.py +282 -0
- flux/retro/implementations/lunar_lander.py +241 -0
- flux/retro/implementations/mandelbrot.py +225 -0
- flux/retro/implementations/markov_text.py +220 -0
- flux/retro/implementations/mastermind.py +318 -0
- flux/retro/implementations/pong.py +239 -0
- flux/retro/implementations/snake.py +350 -0
- flux/retro/implementations/tetris.py +307 -0
- flux/retro/implementations/text_adventure.py +272 -0
- flux/retro/implementations/tic_tac_toe.py +262 -0
- flux/retro/research/__init__.py +22 -0
- flux/retro/research/metrics.py +115 -0
- flux/retro/research/runner.py +247 -0
- flux/retro/research/session.py +471 -0
- flux/retro/showcase/__init__.py +215 -0
- flux/retro/showcase/__main__.py +3 -0
- flux/reverse/__init__.py +32 -0
- flux/reverse/code_map.py +176 -0
- flux/reverse/engineer.py +302 -0
- flux/reverse/parsers/__init__.py +6 -0
- flux/reverse/parsers/c_reverse.py +583 -0
- flux/reverse/parsers/python_reverse.py +720 -0
- flux/runtime/__init__.py +16 -0
- flux/runtime/agent.py +138 -0
- flux/runtime/agent_runtime.py +206 -0
- flux/schema/__init__.py +51 -0
- flux/schema/architecture.py +502 -0
- flux/schema/builder_schema.py +372 -0
- flux/schema/opcode_schema.py +203 -0
- flux/schema/tile_schema.py +659 -0
- flux/security/__init__.py +11 -0
- flux/security/bytecode_verifier.py +699 -0
- flux/security/capabilities.py +121 -0
- flux/security/resource_limits.py +52 -0
- flux/security/sandbox.py +59 -0
- flux/simulation/__init__.py +60 -0
- flux/simulation/digital_twin.py +838 -0
- flux/simulation/oracle.py +484 -0
- flux/simulation/predictor.py +401 -0
- flux/simulation/speculator.py +344 -0
- flux/stdlib/__init__.py +67 -0
- flux/stdlib/agents.py +238 -0
- flux/stdlib/collections.py +379 -0
- flux/stdlib/intrinsics.py +231 -0
- flux/stdlib/math.py +271 -0
- flux/stdlib/strings.py +159 -0
- flux/swarm/__init__.py +62 -0
- flux/swarm/agent.py +432 -0
- flux/swarm/deadlock.py +360 -0
- flux/swarm/message_bus.py +286 -0
- flux/swarm/swarm.py +493 -0
- flux/swarm/topology.py +297 -0
- flux/synthesis/__init__.py +30 -0
- flux/synthesis/demo.py +182 -0
- flux/synthesis/report.py +363 -0
- flux/synthesis/synthesizer.py +639 -0
- flux/tiles/__init__.py +39 -0
- flux/tiles/graph.py +277 -0
- flux/tiles/library.py +1078 -0
- flux/tiles/ports.py +84 -0
- flux/tiles/registry.py +176 -0
- flux/tiles/tile.py +237 -0
- flux/types/__init__.py +41 -0
- flux/types/compat.py +179 -0
- flux/types/generic.py +343 -0
- flux/types/unify.py +826 -0
- flux/vm/__init__.py +37 -0
- flux/vm/evolution.py +292 -0
- flux/vm/interpreter.py +1598 -0
- flux/vm/memory.py +155 -0
- flux/vm/registers.py +156 -0
- flux_vm-0.1.0.dist-info/METADATA +415 -0
- flux_vm-0.1.0.dist-info/RECORD +217 -0
- flux_vm-0.1.0.dist-info/WHEEL +5 -0
- flux_vm-0.1.0.dist-info/entry_points.txt +2 -0
- flux_vm-0.1.0.dist-info/licenses/LICENSE +21 -0
- flux_vm-0.1.0.dist-info/top_level.txt +1 -0
flux/__init__.py
ADDED
flux/__main__.py
ADDED
flux/a2a/__init__.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""FLUX A2A Protocol Layer — Agent-to-Agent communication primitives.
|
|
2
|
+
|
|
3
|
+
This package provides:
|
|
4
|
+
- ``messages`` — Binary A2A message types (A2AMessage with 52-byte header)
|
|
5
|
+
- ``transport`` — Local in-process message delivery (LocalTransport)
|
|
6
|
+
- ``trust`` — INCREMENTS+2 trust engine (TrustEngine, InteractionRecord, AgentProfile)
|
|
7
|
+
- ``coordinator`` — Multi-agent orchestration (AgentCoordinator)
|
|
8
|
+
- ``primitives`` — Rich protocol primitives (Branch, Fork, CoIterate, Discuss, Synthesize, Reflect)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from flux.a2a.messages import A2AMessage
|
|
12
|
+
from flux.a2a.transport import LocalTransport
|
|
13
|
+
from flux.a2a.trust import InteractionRecord, AgentProfile, TrustEngine
|
|
14
|
+
from flux.a2a.coordinator import AgentCoordinator
|
|
15
|
+
from flux.a2a.primitives import (
|
|
16
|
+
BranchPrimitive, BranchBody,
|
|
17
|
+
ForkPrimitive, ForkInherit, ForkMutation,
|
|
18
|
+
CoIteratePrimitive,
|
|
19
|
+
DiscussPrimitive, Participant,
|
|
20
|
+
SynthesizePrimitive, SynthesisSource,
|
|
21
|
+
ReflectPrimitive,
|
|
22
|
+
parse_primitive,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Messages
|
|
27
|
+
"A2AMessage",
|
|
28
|
+
# Transport
|
|
29
|
+
"LocalTransport",
|
|
30
|
+
# Trust
|
|
31
|
+
"InteractionRecord",
|
|
32
|
+
"AgentProfile",
|
|
33
|
+
"TrustEngine",
|
|
34
|
+
# Coordinator
|
|
35
|
+
"AgentCoordinator",
|
|
36
|
+
# Protocol Primitives (Phase 1: adopted from flux-a2a-prototype)
|
|
37
|
+
"BranchPrimitive", "BranchBody",
|
|
38
|
+
"ForkPrimitive", "ForkInherit", "ForkMutation",
|
|
39
|
+
"CoIteratePrimitive",
|
|
40
|
+
"DiscussPrimitive", "Participant",
|
|
41
|
+
"SynthesizePrimitive", "SynthesisSource",
|
|
42
|
+
"ReflectPrimitive",
|
|
43
|
+
"parse_primitive",
|
|
44
|
+
]
|
flux/a2a/coordinator.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Agent Coordinator — Orchestrates multiple agents within a single VM.
|
|
2
|
+
|
|
3
|
+
Ties together :class:`LocalTransport` for message delivery and
|
|
4
|
+
:class:`TrustEngine` for trust-gated communication. Agents are
|
|
5
|
+
identified by string names; internally each is assigned a ``uuid.UUID``
|
|
6
|
+
for the A2A protocol layer.
|
|
7
|
+
|
|
8
|
+
Message flow:
|
|
9
|
+
|
|
10
|
+
agent_a.send_message("bob", TELL, payload=b"hi")
|
|
11
|
+
→ trust check (compute_trust >= threshold)
|
|
12
|
+
→ build A2AMessage
|
|
13
|
+
→ transport.send(message)
|
|
14
|
+
→ message lands in bob's mailbox
|
|
15
|
+
→ agent_b.get_messages("bob") drains the mailbox
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
import uuid
|
|
23
|
+
|
|
24
|
+
from flux.a2a.messages import A2AMessage
|
|
25
|
+
from flux.a2a.transport import LocalTransport
|
|
26
|
+
from flux.a2a.trust import TrustEngine
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AgentCoordinator:
|
|
30
|
+
"""Coordinates multiple agents running in the same VM.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
trust_threshold : float
|
|
35
|
+
Minimum trust score required for message delivery (default 0.3).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, trust_threshold: float = 0.3) -> None:
|
|
39
|
+
self._agents: Dict[str, Dict[str, Any]] = {}
|
|
40
|
+
self.trust: TrustEngine = TrustEngine()
|
|
41
|
+
self.transport: LocalTransport = LocalTransport()
|
|
42
|
+
self.trust_threshold: float = trust_threshold
|
|
43
|
+
|
|
44
|
+
# ── Agent Registration ────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
def register_agent(
|
|
47
|
+
self, agent_id: str, interpreter: Any = None
|
|
48
|
+
) -> uuid.UUID:
|
|
49
|
+
"""Register an agent with an optional VM interpreter.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
agent_id : str
|
|
54
|
+
Logical name for the agent.
|
|
55
|
+
interpreter : Any, optional
|
|
56
|
+
A :class:`flux.vm.interpreter.Interpreter` instance, or None.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
uuid.UUID
|
|
61
|
+
The generated UUID for this agent (used in A2A headers).
|
|
62
|
+
"""
|
|
63
|
+
aid = uuid.uuid4()
|
|
64
|
+
self.transport.register(aid)
|
|
65
|
+
self._agents[agent_id] = {
|
|
66
|
+
"uuid": aid,
|
|
67
|
+
"interpreter": interpreter,
|
|
68
|
+
}
|
|
69
|
+
return aid
|
|
70
|
+
|
|
71
|
+
def unregister_agent(self, agent_id: str) -> None:
|
|
72
|
+
"""Remove an agent from the coordinator."""
|
|
73
|
+
info = self._agents.pop(agent_id, None)
|
|
74
|
+
if info is not None:
|
|
75
|
+
self.transport.unregister(info["uuid"])
|
|
76
|
+
|
|
77
|
+
# ── Messaging ─────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
def send_message(
|
|
80
|
+
self,
|
|
81
|
+
sender: str,
|
|
82
|
+
receiver: str,
|
|
83
|
+
msg_type: int,
|
|
84
|
+
payload: bytes = b"",
|
|
85
|
+
priority: int = 5,
|
|
86
|
+
in_reply_to: Optional[uuid.UUID] = None,
|
|
87
|
+
) -> bool:
|
|
88
|
+
"""Send an A2A message between registered agents.
|
|
89
|
+
|
|
90
|
+
The message is only delivered if the trust score from *sender*
|
|
91
|
+
to *receiver* meets the configured threshold.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
sender : str
|
|
96
|
+
Name of the sending agent.
|
|
97
|
+
receiver : str
|
|
98
|
+
Name of the receiving agent.
|
|
99
|
+
msg_type : int
|
|
100
|
+
A2A message type (0x60–0x7B).
|
|
101
|
+
payload : bytes
|
|
102
|
+
Application-level payload.
|
|
103
|
+
priority : int
|
|
104
|
+
Delivery priority 0–15.
|
|
105
|
+
in_reply_to : uuid.UUID, optional
|
|
106
|
+
UUID this message is replying to.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
bool
|
|
111
|
+
``True`` if the message was delivered, ``False`` if blocked
|
|
112
|
+
by the trust gate or if either agent is unknown.
|
|
113
|
+
"""
|
|
114
|
+
# Trust gate
|
|
115
|
+
trust = self.trust.compute_trust(sender, receiver)
|
|
116
|
+
if trust < self.trust_threshold:
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
sender_info = self._agents.get(sender)
|
|
120
|
+
receiver_info = self._agents.get(receiver)
|
|
121
|
+
if sender_info is None or receiver_info is None:
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
msg = A2AMessage(
|
|
125
|
+
sender=sender_info["uuid"],
|
|
126
|
+
receiver=receiver_info["uuid"],
|
|
127
|
+
conversation_id=uuid.uuid4(),
|
|
128
|
+
in_reply_to=in_reply_to,
|
|
129
|
+
message_type=msg_type,
|
|
130
|
+
priority=priority,
|
|
131
|
+
trust_token=int(trust * 1e9),
|
|
132
|
+
capability_token=0,
|
|
133
|
+
payload=payload,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
delivered = self.transport.send(msg)
|
|
137
|
+
if delivered:
|
|
138
|
+
# Record positive interaction on success
|
|
139
|
+
self.trust.record_interaction(sender, receiver, True, 0.1)
|
|
140
|
+
return delivered
|
|
141
|
+
|
|
142
|
+
def get_messages(self, agent_id: str) -> List[A2AMessage]:
|
|
143
|
+
"""Drain the mailbox for *agent_id* and return all pending messages."""
|
|
144
|
+
info = self._agents.get(agent_id)
|
|
145
|
+
if info is None:
|
|
146
|
+
return []
|
|
147
|
+
return self.transport.receive(info["uuid"])
|
|
148
|
+
|
|
149
|
+
def pending_count(self, agent_id: str) -> int:
|
|
150
|
+
"""Return the number of pending messages for *agent_id*."""
|
|
151
|
+
info = self._agents.get(agent_id)
|
|
152
|
+
if info is None:
|
|
153
|
+
return 0
|
|
154
|
+
return self.transport.pending_count(info["uuid"])
|
|
155
|
+
|
|
156
|
+
# ── Queries ───────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
def get_agent_uuid(self, agent_name: str) -> Optional[uuid.UUID]:
|
|
159
|
+
"""Return the UUID for a named agent, or ``None``."""
|
|
160
|
+
info = self._agents.get(agent_name)
|
|
161
|
+
return info["uuid"] if info else None
|
|
162
|
+
|
|
163
|
+
def registered_agents(self) -> List[str]:
|
|
164
|
+
"""Return a list of all registered agent names."""
|
|
165
|
+
return list(self._agents.keys())
|
flux/a2a/messages.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""A2A Message Types — Fixed-format binary messages for agent-to-agent communication.
|
|
2
|
+
|
|
3
|
+
Message layout (binary, 52-byte header):
|
|
4
|
+
|
|
5
|
+
Offset Size Field
|
|
6
|
+
─────── ────── ──────────────────────────────────────
|
|
7
|
+
0 16 sender_uuid (128-bit UUID)
|
|
8
|
+
16 16 receiver_uuid (128-bit UUID)
|
|
9
|
+
32 8 conversation_id (compact 64-bit)
|
|
10
|
+
40 1 message_type (uint8, 0x60–0x7B)
|
|
11
|
+
41 1 priority (uint8, 0–15)
|
|
12
|
+
42 4 trust_token (uint32 LE)
|
|
13
|
+
46 4 capability_token (uint32 LE)
|
|
14
|
+
50 2 in_reply_to (uint16 LE, 0 = None)
|
|
15
|
+
52 var payload (arbitrary bytes)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import struct
|
|
21
|
+
import uuid
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
# Header format: <16s16s8sBBIIH = 16+16+8+1+1+4+4+2 = 52
|
|
26
|
+
_HEADER_STRUCT = struct.Struct("<16s16s8sBBIIH")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ── A2A Message ───────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class A2AMessage:
|
|
34
|
+
"""Fixed-format binary A2A message with a 52-byte header.
|
|
35
|
+
|
|
36
|
+
Attributes
|
|
37
|
+
----------
|
|
38
|
+
sender : uuid.UUID
|
|
39
|
+
128-bit UUID of the originating agent.
|
|
40
|
+
receiver : uuid.UUID
|
|
41
|
+
128-bit UUID of the target agent.
|
|
42
|
+
conversation_id : uuid.UUID
|
|
43
|
+
128-bit UUID grouping related messages. Serialized as the first
|
|
44
|
+
8 bytes (time_low + time_mid) for compactness; zero-padded on
|
|
45
|
+
deserialization.
|
|
46
|
+
in_reply_to : Optional[uuid.UUID]
|
|
47
|
+
When set, marks this message as a reply. Serialized as a uint16
|
|
48
|
+
extracted from the UUID bytes; deserialized as None when zero.
|
|
49
|
+
message_type : int
|
|
50
|
+
Protocol verb in range 0x60–0x7B (A2A opcode space).
|
|
51
|
+
priority : int
|
|
52
|
+
Delivery priority 0–15.
|
|
53
|
+
trust_token : int
|
|
54
|
+
32-bit unsigned trust evidence token.
|
|
55
|
+
capability_token : int
|
|
56
|
+
32-bit unsigned capability evidence token.
|
|
57
|
+
payload : bytes
|
|
58
|
+
Arbitrary application-level payload.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
sender: uuid.UUID
|
|
62
|
+
receiver: uuid.UUID
|
|
63
|
+
conversation_id: uuid.UUID
|
|
64
|
+
in_reply_to: Optional[uuid.UUID]
|
|
65
|
+
message_type: int
|
|
66
|
+
priority: int
|
|
67
|
+
trust_token: int
|
|
68
|
+
capability_token: int
|
|
69
|
+
payload: bytes = b""
|
|
70
|
+
|
|
71
|
+
HEADER_SIZE: int = 52 # fixed header length in bytes
|
|
72
|
+
|
|
73
|
+
# ── Validation helpers ────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
def __post_init__(self) -> None:
|
|
76
|
+
if not (0x60 <= self.message_type <= 0x7B):
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"message_type must be in 0x60–0x7B, got 0x{self.message_type:02X}"
|
|
79
|
+
)
|
|
80
|
+
if not (0 <= self.priority <= 15):
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"priority must be 0–15, got {self.priority}"
|
|
83
|
+
)
|
|
84
|
+
if not (0 <= self.trust_token <= 0xFFFFFFFF):
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"trust_token must fit in uint32, got {self.trust_token}"
|
|
87
|
+
)
|
|
88
|
+
if not (0 <= self.capability_token <= 0xFFFFFFFF):
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"capability_token must fit in uint32, got {self.capability_token}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# ── Serialization ─────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
def to_bytes(self) -> bytes:
|
|
96
|
+
"""Serialize to binary (52-byte header + payload).
|
|
97
|
+
|
|
98
|
+
The compact header drops the low 8 bytes of *conversation_id* and
|
|
99
|
+
reduces *in_reply_to* to a uint16 tag (0 when None).
|
|
100
|
+
"""
|
|
101
|
+
# conversation_id → first 8 bytes of UUID (time_low + time_mid + time_hi)
|
|
102
|
+
conv_bytes = self.conversation_id.bytes[:8]
|
|
103
|
+
|
|
104
|
+
# in_reply_to → uint16 from first 2 bytes, 0 if None
|
|
105
|
+
reply_u16 = 0
|
|
106
|
+
if self.in_reply_to is not None:
|
|
107
|
+
reply_u16 = int.from_bytes(
|
|
108
|
+
self.in_reply_to.bytes[:2], byteorder="little"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
header = _HEADER_STRUCT.pack(
|
|
112
|
+
self.sender.bytes,
|
|
113
|
+
self.receiver.bytes,
|
|
114
|
+
conv_bytes,
|
|
115
|
+
self.message_type,
|
|
116
|
+
self.priority,
|
|
117
|
+
self.trust_token,
|
|
118
|
+
self.capability_token,
|
|
119
|
+
reply_u16,
|
|
120
|
+
)
|
|
121
|
+
return header + self.payload
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def from_bytes(cls, data: bytes) -> A2AMessage:
|
|
125
|
+
"""Deserialize from binary.
|
|
126
|
+
|
|
127
|
+
Raises ``ValueError`` if the buffer is shorter than 52 bytes.
|
|
128
|
+
"""
|
|
129
|
+
if len(data) < cls.HEADER_SIZE:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"A2AMessage requires at least {cls.HEADER_SIZE} bytes, "
|
|
132
|
+
f"got {len(data)}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
(sender_b, receiver_b, conv_b, msg_type, prio,
|
|
136
|
+
trust_tok, cap_tok, reply_u16) = _HEADER_STRUCT.unpack_from(data, 0)
|
|
137
|
+
|
|
138
|
+
# Reconstruct full UUIDs from compact fields
|
|
139
|
+
sender = uuid.UUID(bytes=sender_b)
|
|
140
|
+
receiver = uuid.UUID(bytes=receiver_b)
|
|
141
|
+
conversation_id = uuid.UUID(bytes=conv_b + b"\x00" * 8)
|
|
142
|
+
|
|
143
|
+
# Reconstruct in_reply_to
|
|
144
|
+
in_reply_to = None
|
|
145
|
+
if reply_u16 != 0:
|
|
146
|
+
in_reply_to = uuid.UUID(
|
|
147
|
+
bytes=reply_u16.to_bytes(2, byteorder="little") + b"\x00" * 14
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
payload = data[cls.HEADER_SIZE:]
|
|
151
|
+
|
|
152
|
+
return cls(
|
|
153
|
+
sender=sender,
|
|
154
|
+
receiver=receiver,
|
|
155
|
+
conversation_id=conversation_id,
|
|
156
|
+
in_reply_to=in_reply_to,
|
|
157
|
+
message_type=msg_type,
|
|
158
|
+
priority=prio,
|
|
159
|
+
trust_token=trust_tok,
|
|
160
|
+
capability_token=cap_tok,
|
|
161
|
+
payload=payload,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def __repr__(self) -> str:
|
|
165
|
+
return (
|
|
166
|
+
f"A2AMessage(type=0x{self.message_type:02X}, "
|
|
167
|
+
f"sender={self.sender}, receiver={self.receiver}, "
|
|
168
|
+
f"conv={self.conversation_id}, "
|
|
169
|
+
f"priority={self.priority}, "
|
|
170
|
+
f"payload_len={len(self.payload)})"
|
|
171
|
+
)
|