lockstock-integrations 1.1.0__py3-none-any.whl → 1.1.1__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.
- lockstock_core/__init__.py +10 -0
- lockstock_core/client.py +281 -0
- lockstock_core/types.py +264 -0
- {lockstock_integrations-1.1.0.dist-info → lockstock_integrations-1.1.1.dist-info}/METADATA +1 -1
- {lockstock_integrations-1.1.0.dist-info → lockstock_integrations-1.1.1.dist-info}/RECORD +6 -3
- {lockstock_integrations-1.1.0.dist-info → lockstock_integrations-1.1.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LockStock Core Client
|
|
3
|
+
---------------------
|
|
4
|
+
Shared client for all SDK integrations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .client import LockStockClient
|
|
8
|
+
from .types import VerifyResult, AuditEntry, AgentCard
|
|
9
|
+
|
|
10
|
+
__all__ = ["LockStockClient", "VerifyResult", "AuditEntry", "AgentCard"]
|
lockstock_core/client.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LockStock Core Client
|
|
3
|
+
---------------------
|
|
4
|
+
HTTP client for LockStock API interactions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
import hmac
|
|
9
|
+
import time
|
|
10
|
+
from typing import Optional, Dict, Any, List
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from .types import VerifyResult, VerifyStatus, AuditEntry, AgentCard, map_tool_to_capability, Matrix, get_generator, normalize_task_for_signature
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LockStockClient:
|
|
17
|
+
"""
|
|
18
|
+
Client for interacting with the LockStock API.
|
|
19
|
+
|
|
20
|
+
Provides capability verification, audit logging, and identity management.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
DEFAULT_ENDPOINT = "https://lockstock-api-i9kp.onrender.com"
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
agent_id: str,
|
|
28
|
+
secret: Optional[str] = None,
|
|
29
|
+
api_key: Optional[str] = None,
|
|
30
|
+
endpoint: str = DEFAULT_ENDPOINT,
|
|
31
|
+
timeout: float = 30.0
|
|
32
|
+
):
|
|
33
|
+
"""
|
|
34
|
+
Initialize LockStock client.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
agent_id: The agent's unique identifier
|
|
38
|
+
secret: The agent's HMAC secret (for signing requests)
|
|
39
|
+
api_key: Admin API key (for provisioning/management)
|
|
40
|
+
endpoint: LockStock API endpoint
|
|
41
|
+
timeout: Request timeout in seconds
|
|
42
|
+
"""
|
|
43
|
+
self.agent_id = agent_id
|
|
44
|
+
self.secret = secret
|
|
45
|
+
self.api_key = api_key
|
|
46
|
+
self.endpoint = endpoint.rstrip("/")
|
|
47
|
+
self.timeout = timeout
|
|
48
|
+
|
|
49
|
+
# State tracking
|
|
50
|
+
self._current_hash = "0" * 64 # Genesis hash
|
|
51
|
+
self._sequence = 0
|
|
52
|
+
self._state_matrix = Matrix.identity()
|
|
53
|
+
|
|
54
|
+
# HTTP client
|
|
55
|
+
self._client = httpx.AsyncClient(timeout=timeout)
|
|
56
|
+
|
|
57
|
+
async def verify(
|
|
58
|
+
self,
|
|
59
|
+
task: str,
|
|
60
|
+
tool_name: Optional[str] = None,
|
|
61
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
62
|
+
) -> VerifyResult:
|
|
63
|
+
"""
|
|
64
|
+
Verify that the agent is authorized to perform a task.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
task: The capability/task to verify (e.g., "DEPLOY", "RESTART")
|
|
68
|
+
tool_name: Optional tool name (will be mapped to capability)
|
|
69
|
+
metadata: Optional metadata to include in audit
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
VerifyResult with authorization status
|
|
73
|
+
"""
|
|
74
|
+
# Map tool name to capability if provided
|
|
75
|
+
if tool_name and task == "UNKNOWN":
|
|
76
|
+
task = map_tool_to_capability(tool_name)
|
|
77
|
+
|
|
78
|
+
# Prepare request
|
|
79
|
+
self._sequence += 1
|
|
80
|
+
timestamp = int(time.time())
|
|
81
|
+
|
|
82
|
+
# Apply generator matrix for this task (topological state transition)
|
|
83
|
+
generator = get_generator(task)
|
|
84
|
+
new_matrix = self._state_matrix.multiply(generator)
|
|
85
|
+
|
|
86
|
+
# Create signature with the NEW matrix (uses timestamp, not sequence)
|
|
87
|
+
signature = self._sign_request(task, self._current_hash, timestamp, new_matrix)
|
|
88
|
+
|
|
89
|
+
payload = {
|
|
90
|
+
"client_id": self.agent_id,
|
|
91
|
+
"task": task.lower(),
|
|
92
|
+
"parent_hash": self._current_hash,
|
|
93
|
+
"state_matrix": new_matrix.to_dict(),
|
|
94
|
+
"signature": signature,
|
|
95
|
+
"timestamp": timestamp,
|
|
96
|
+
"sequence": self._sequence
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
response = await self._client.post(
|
|
101
|
+
f"{self.endpoint}/verify",
|
|
102
|
+
json=payload,
|
|
103
|
+
headers=self._headers()
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
data = response.json()
|
|
107
|
+
|
|
108
|
+
status = VerifyStatus(data.get("status", "rejected"))
|
|
109
|
+
|
|
110
|
+
if status == VerifyStatus.ACCEPTED:
|
|
111
|
+
# Update state from server response
|
|
112
|
+
self._current_hash = data.get("state_hash", self._current_hash)
|
|
113
|
+
if "state_matrix" in data:
|
|
114
|
+
self._state_matrix = Matrix.from_dict(data["state_matrix"])
|
|
115
|
+
|
|
116
|
+
return VerifyResult(
|
|
117
|
+
status=status,
|
|
118
|
+
reason=data.get("reason"),
|
|
119
|
+
state_hash=data.get("state_hash"),
|
|
120
|
+
server_timestamp=data.get("server_timestamp")
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
except httpx.HTTPError as e:
|
|
124
|
+
return VerifyResult(
|
|
125
|
+
status=VerifyStatus.REJECTED,
|
|
126
|
+
reason=f"HTTP error: {str(e)}"
|
|
127
|
+
)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
return VerifyResult(
|
|
130
|
+
status=VerifyStatus.REJECTED,
|
|
131
|
+
reason=f"Client error: {str(e)}"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
async def verify_tool(self, tool_name: str, tool_input: Optional[Dict] = None) -> VerifyResult:
|
|
135
|
+
"""
|
|
136
|
+
Verify authorization for a specific tool call.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
tool_name: Name of the tool being called
|
|
140
|
+
tool_input: The tool's input parameters
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
VerifyResult with authorization status
|
|
144
|
+
"""
|
|
145
|
+
capability = map_tool_to_capability(tool_name)
|
|
146
|
+
return await self.verify(task=capability, tool_name=tool_name)
|
|
147
|
+
|
|
148
|
+
async def bootstrap(self) -> Dict[str, Any]:
|
|
149
|
+
"""
|
|
150
|
+
Initialize the agent's identity with the server.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Bootstrap response with root hash and matrix
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
response = await self._client.post(
|
|
157
|
+
f"{self.endpoint}/bootstrap",
|
|
158
|
+
json={"client_id": self.agent_id},
|
|
159
|
+
headers=self._headers()
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
data = response.json()
|
|
163
|
+
|
|
164
|
+
if "root_hash" in data:
|
|
165
|
+
self._current_hash = data["root_hash"]
|
|
166
|
+
if "root_matrix" in data:
|
|
167
|
+
self._state_matrix = Matrix.from_dict(data["root_matrix"])
|
|
168
|
+
self._sequence = 0
|
|
169
|
+
|
|
170
|
+
return data
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
return {"error": str(e)}
|
|
174
|
+
|
|
175
|
+
async def get_agent_card(self) -> Optional[AgentCard]:
|
|
176
|
+
"""
|
|
177
|
+
Retrieve the agent's card from the server.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
AgentCard if found, None otherwise
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
response = await self._client.get(
|
|
184
|
+
f"{self.endpoint}/api/guard/agents/{self.agent_id}",
|
|
185
|
+
headers=self._headers()
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if response.status_code == 200:
|
|
189
|
+
data = response.json()
|
|
190
|
+
return AgentCard(
|
|
191
|
+
agent_id=data.get("agent_id", self.agent_id),
|
|
192
|
+
display_name=data.get("display_name", ""),
|
|
193
|
+
capabilities=data.get("capabilities", []),
|
|
194
|
+
status=data.get("status", "unknown"),
|
|
195
|
+
fingerprint=data.get("fingerprint"),
|
|
196
|
+
owner_id=data.get("owner_id"),
|
|
197
|
+
created_at=data.get("created_at")
|
|
198
|
+
)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
except Exception:
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
async def log_audit(
|
|
205
|
+
self,
|
|
206
|
+
action: str,
|
|
207
|
+
status: str,
|
|
208
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
209
|
+
) -> bool:
|
|
210
|
+
"""
|
|
211
|
+
Log an action to the audit trail.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
action: The action that was performed
|
|
215
|
+
status: Status of the action (e.g., "APPROVED", "DENIED")
|
|
216
|
+
metadata: Additional metadata to log
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
True if logged successfully
|
|
220
|
+
"""
|
|
221
|
+
# Audit is automatically logged via verify() calls
|
|
222
|
+
# This method is for explicit logging of non-verified actions
|
|
223
|
+
entry = AuditEntry(
|
|
224
|
+
sequence=self._sequence,
|
|
225
|
+
action=action,
|
|
226
|
+
timestamp=int(time.time()),
|
|
227
|
+
state_hash=self._current_hash,
|
|
228
|
+
signature=self._sign_request(action, self._current_hash, self._sequence),
|
|
229
|
+
metadata=metadata or {}
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# In production, this would POST to an audit endpoint
|
|
233
|
+
# For now, we just track locally
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
def _sign_request(self, task: str, parent_hash: str, timestamp: int, matrix: Matrix = None) -> str:
|
|
237
|
+
"""Create HMAC signature for a request.
|
|
238
|
+
|
|
239
|
+
The signature format matches the Rust server:
|
|
240
|
+
{client_id}|{Task}|{parent_hash}|{a11},{a12},{a21},{a22}|{timestamp}
|
|
241
|
+
|
|
242
|
+
IMPORTANT: The task name must match Rust's Debug format (e.g., "Heartbeat", "FileRead")
|
|
243
|
+
because the server uses {:?} formatting for the Task enum in signature generation.
|
|
244
|
+
"""
|
|
245
|
+
if not self.secret:
|
|
246
|
+
return "unsigned"
|
|
247
|
+
|
|
248
|
+
# Use provided matrix or current state
|
|
249
|
+
m = matrix if matrix else self._state_matrix
|
|
250
|
+
matrix_str = f"{m.a11},{m.a12},{m.a21},{m.a22}"
|
|
251
|
+
|
|
252
|
+
# Normalize task to Rust Debug format (e.g., "HEARTBEAT" -> "Heartbeat")
|
|
253
|
+
rust_task = normalize_task_for_signature(task)
|
|
254
|
+
|
|
255
|
+
# Build message in server's expected format
|
|
256
|
+
message = f"{self.agent_id}|{rust_task}|{parent_hash}|{matrix_str}|{timestamp}"
|
|
257
|
+
|
|
258
|
+
signature = hmac.new(
|
|
259
|
+
self.secret.encode(),
|
|
260
|
+
message.encode(),
|
|
261
|
+
hashlib.sha256
|
|
262
|
+
).hexdigest()
|
|
263
|
+
|
|
264
|
+
return signature
|
|
265
|
+
|
|
266
|
+
def _headers(self) -> Dict[str, str]:
|
|
267
|
+
"""Build request headers."""
|
|
268
|
+
headers = {"Content-Type": "application/json"}
|
|
269
|
+
if self.api_key:
|
|
270
|
+
headers["x-admin-key"] = self.api_key
|
|
271
|
+
return headers
|
|
272
|
+
|
|
273
|
+
async def close(self):
|
|
274
|
+
"""Close the HTTP client."""
|
|
275
|
+
await self._client.aclose()
|
|
276
|
+
|
|
277
|
+
async def __aenter__(self):
|
|
278
|
+
return self
|
|
279
|
+
|
|
280
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
281
|
+
await self.close()
|
lockstock_core/types.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LockStock Core Types
|
|
3
|
+
--------------------
|
|
4
|
+
Shared types for all SDK integrations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import List, Optional, Dict, Any
|
|
9
|
+
from enum import Enum
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VerifyStatus(str, Enum):
|
|
13
|
+
ACCEPTED = "accepted"
|
|
14
|
+
REJECTED = "rejected"
|
|
15
|
+
BUFFERED = "buffered"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class VerifyResult:
|
|
20
|
+
"""Result of a capability verification request."""
|
|
21
|
+
status: VerifyStatus
|
|
22
|
+
reason: Optional[str] = None
|
|
23
|
+
state_hash: Optional[str] = None
|
|
24
|
+
server_timestamp: Optional[int] = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def authorized(self) -> bool:
|
|
28
|
+
return self.status == VerifyStatus.ACCEPTED
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class AuditEntry:
|
|
33
|
+
"""An entry in the agent's audit trail."""
|
|
34
|
+
sequence: int
|
|
35
|
+
action: str
|
|
36
|
+
timestamp: int
|
|
37
|
+
state_hash: str
|
|
38
|
+
signature: str
|
|
39
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class AgentCard:
|
|
44
|
+
"""Agent identity and capabilities."""
|
|
45
|
+
agent_id: str
|
|
46
|
+
display_name: str
|
|
47
|
+
capabilities: List[str]
|
|
48
|
+
status: str = "active"
|
|
49
|
+
fingerprint: Optional[str] = None
|
|
50
|
+
owner_id: Optional[str] = None
|
|
51
|
+
created_at: Optional[str] = None
|
|
52
|
+
|
|
53
|
+
def has_capability(self, capability: str) -> bool:
|
|
54
|
+
"""Check if agent has a specific capability."""
|
|
55
|
+
return capability.upper() in [c.upper() for c in self.capabilities]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Mapping from common tool names to LockStock capabilities
|
|
59
|
+
# These map to the Task enum variants in Rust (case-insensitive)
|
|
60
|
+
TOOL_CAPABILITY_MAP = {
|
|
61
|
+
# File operations -> FileRead, FileWrite
|
|
62
|
+
"read": "FILEREAD",
|
|
63
|
+
"read_file": "FILEREAD",
|
|
64
|
+
"Read": "FILEREAD",
|
|
65
|
+
"Glob": "FILEREAD",
|
|
66
|
+
"glob": "FILEREAD",
|
|
67
|
+
"Grep": "FILEREAD",
|
|
68
|
+
"grep": "FILEREAD",
|
|
69
|
+
"write": "FILEWRITE",
|
|
70
|
+
"write_file": "FILEWRITE",
|
|
71
|
+
"Write": "FILEWRITE",
|
|
72
|
+
"edit": "FILEWRITE",
|
|
73
|
+
"Edit": "FILEWRITE",
|
|
74
|
+
"NotebookEdit": "FILEWRITE",
|
|
75
|
+
|
|
76
|
+
# Shell/execution -> Shell (alias for Deploy)
|
|
77
|
+
"bash": "SHELL",
|
|
78
|
+
"Bash": "SHELL",
|
|
79
|
+
"shell": "SHELL",
|
|
80
|
+
"execute": "SHELL",
|
|
81
|
+
"run_command": "SHELL",
|
|
82
|
+
"KillShell": "SHELL",
|
|
83
|
+
|
|
84
|
+
# Network operations -> Network
|
|
85
|
+
"web_fetch": "NETWORK",
|
|
86
|
+
"WebFetch": "NETWORK",
|
|
87
|
+
"web_search": "NETWORK",
|
|
88
|
+
"WebSearch": "NETWORK",
|
|
89
|
+
"http_request": "NETWORK",
|
|
90
|
+
"fetch": "NETWORK",
|
|
91
|
+
|
|
92
|
+
# Database operations -> Database
|
|
93
|
+
"sql": "DATABASE",
|
|
94
|
+
"query": "DATABASE",
|
|
95
|
+
"db_read": "DATABASE",
|
|
96
|
+
"db_write": "DATABASE",
|
|
97
|
+
"LSP": "DATABASE", # Language Server Protocol reads code
|
|
98
|
+
|
|
99
|
+
# Agent operations -> Teleport, Handoff
|
|
100
|
+
"task": "TELEPORT",
|
|
101
|
+
"Task": "TELEPORT",
|
|
102
|
+
"spawn_agent": "TELEPORT",
|
|
103
|
+
"TaskOutput": "TELEPORT",
|
|
104
|
+
"handoff": "HANDOFF",
|
|
105
|
+
"delegate": "DELEGATE",
|
|
106
|
+
|
|
107
|
+
# State operations -> Checkpoint, Teleport
|
|
108
|
+
"checkpoint": "CHECKPOINT",
|
|
109
|
+
"save_state": "CHECKPOINT",
|
|
110
|
+
"export_passport": "TELEPORT",
|
|
111
|
+
"import_passport": "TELEPORT",
|
|
112
|
+
|
|
113
|
+
# Monitoring -> Heartbeat
|
|
114
|
+
"heartbeat": "HEARTBEAT",
|
|
115
|
+
"health_check": "HEARTBEAT",
|
|
116
|
+
|
|
117
|
+
# Operations -> Deploy, Restart, Backup, Rollback
|
|
118
|
+
"deploy": "DEPLOY",
|
|
119
|
+
"restart": "RESTART",
|
|
120
|
+
"backup": "BACKUP",
|
|
121
|
+
"rollback": "ROLLBACK",
|
|
122
|
+
|
|
123
|
+
# Task management tools
|
|
124
|
+
"TaskCreate": "FILEWRITE",
|
|
125
|
+
"TaskUpdate": "FILEWRITE",
|
|
126
|
+
"TaskList": "FILEREAD",
|
|
127
|
+
"TaskGet": "FILEREAD",
|
|
128
|
+
|
|
129
|
+
# Skills
|
|
130
|
+
"Skill": "SHELL",
|
|
131
|
+
"AskUserQuestion": "HEARTBEAT", # Interactive, low privilege
|
|
132
|
+
"EnterPlanMode": "HEARTBEAT",
|
|
133
|
+
"ExitPlanMode": "HEARTBEAT",
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def map_tool_to_capability(tool_name: str) -> str:
|
|
138
|
+
"""Map a tool name to a LockStock capability."""
|
|
139
|
+
return TOOL_CAPABILITY_MAP.get(tool_name, "UNKNOWN")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Mapping from any case to Rust Debug format (used in HMAC signatures)
|
|
143
|
+
# The Rust server uses {:?} format which produces these exact strings
|
|
144
|
+
TASK_RUST_DEBUG_FORMAT = {
|
|
145
|
+
# Operations
|
|
146
|
+
"deploy": "Deploy",
|
|
147
|
+
"restart": "Restart",
|
|
148
|
+
"backup": "Backup",
|
|
149
|
+
"rollback": "Rollback",
|
|
150
|
+
# Agent lifecycle
|
|
151
|
+
"heartbeat": "Heartbeat",
|
|
152
|
+
"checkpoint": "Checkpoint",
|
|
153
|
+
"teleport": "Teleport",
|
|
154
|
+
# MCP tool categories
|
|
155
|
+
"fileread": "FileRead",
|
|
156
|
+
"file_read": "FileRead",
|
|
157
|
+
"read": "FileRead",
|
|
158
|
+
"query": "FileRead",
|
|
159
|
+
"filewrite": "FileWrite",
|
|
160
|
+
"file_write": "FileWrite",
|
|
161
|
+
"write": "FileWrite",
|
|
162
|
+
"edit": "FileWrite",
|
|
163
|
+
"network": "Network",
|
|
164
|
+
"http": "Network",
|
|
165
|
+
"fetch": "Network",
|
|
166
|
+
"shell": "Shell",
|
|
167
|
+
"bash": "Shell",
|
|
168
|
+
"exec": "Shell",
|
|
169
|
+
"execute": "Shell",
|
|
170
|
+
"database": "Database",
|
|
171
|
+
"db": "Database",
|
|
172
|
+
"sql": "Database",
|
|
173
|
+
# A2A protocol
|
|
174
|
+
"handoff": "Handoff",
|
|
175
|
+
"hand_off": "Handoff",
|
|
176
|
+
"delegate": "Delegate",
|
|
177
|
+
# Wildcard
|
|
178
|
+
"all": "All",
|
|
179
|
+
"admin": "All",
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def normalize_task_for_signature(task: str) -> str:
|
|
184
|
+
"""
|
|
185
|
+
Convert a task name to the Rust Debug format used in HMAC signatures.
|
|
186
|
+
|
|
187
|
+
The Rust server uses {:?} format for Task enum in signatures, which produces
|
|
188
|
+
PascalCase variants like 'Heartbeat', 'FileRead', etc.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
task: Task name in any case (e.g., "HEARTBEAT", "heartbeat", "Heartbeat")
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Task name in Rust Debug format (e.g., "Heartbeat")
|
|
195
|
+
"""
|
|
196
|
+
normalized = TASK_RUST_DEBUG_FORMAT.get(task.lower())
|
|
197
|
+
if normalized:
|
|
198
|
+
return normalized
|
|
199
|
+
|
|
200
|
+
# Fallback: try to capitalize properly for unknown tasks
|
|
201
|
+
# This handles cases like "deploy" -> "Deploy"
|
|
202
|
+
return task.capitalize()
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# Prime field modulus (F_65537)
|
|
206
|
+
MODULUS = 65537
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class Matrix:
|
|
210
|
+
"""2x2 matrix over F_65537 for topological state transitions."""
|
|
211
|
+
|
|
212
|
+
def __init__(self, a11: int, a12: int, a21: int, a22: int):
|
|
213
|
+
self.a11 = a11 % MODULUS
|
|
214
|
+
self.a12 = a12 % MODULUS
|
|
215
|
+
self.a21 = a21 % MODULUS
|
|
216
|
+
self.a22 = a22 % MODULUS
|
|
217
|
+
|
|
218
|
+
def multiply(self, other: "Matrix") -> "Matrix":
|
|
219
|
+
"""Matrix multiplication in F_65537."""
|
|
220
|
+
return Matrix(
|
|
221
|
+
(self.a11 * other.a11 + self.a12 * other.a21) % MODULUS,
|
|
222
|
+
(self.a11 * other.a12 + self.a12 * other.a22) % MODULUS,
|
|
223
|
+
(self.a21 * other.a11 + self.a22 * other.a21) % MODULUS,
|
|
224
|
+
(self.a21 * other.a12 + self.a22 * other.a22) % MODULUS,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def to_dict(self) -> Dict[str, int]:
|
|
228
|
+
"""Convert to dictionary for JSON serialization."""
|
|
229
|
+
return {"a11": self.a11, "a12": self.a12, "a21": self.a21, "a22": self.a22}
|
|
230
|
+
|
|
231
|
+
@classmethod
|
|
232
|
+
def from_dict(cls, d: Dict[str, int]) -> "Matrix":
|
|
233
|
+
"""Create from dictionary."""
|
|
234
|
+
return cls(d.get("a11", 1), d.get("a12", 0), d.get("a21", 0), d.get("a22", 1))
|
|
235
|
+
|
|
236
|
+
@classmethod
|
|
237
|
+
def identity(cls) -> "Matrix":
|
|
238
|
+
"""Return identity matrix."""
|
|
239
|
+
return cls(1, 0, 0, 1)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# Generator matrices for each task (matching Rust server)
|
|
243
|
+
GENERATOR_MATRICES = {
|
|
244
|
+
"DEPLOY": Matrix(1, 1, 0, 1),
|
|
245
|
+
"RESTART": Matrix(1, 0, 1, 1),
|
|
246
|
+
"BACKUP": Matrix(2, 1, 1, 1),
|
|
247
|
+
"ROLLBACK": Matrix(1, 1, 1, 2),
|
|
248
|
+
"HEARTBEAT": Matrix(0, 65536, 1, 0), # 90-degree rotation
|
|
249
|
+
"CHECKPOINT": Matrix(2, 1, 1, 2),
|
|
250
|
+
"TELEPORT": Matrix(3, 1, 1, 1),
|
|
251
|
+
"FILEREAD": Matrix(1, 2, 0, 1),
|
|
252
|
+
"FILEWRITE": Matrix(1, 3, 0, 1),
|
|
253
|
+
"NETWORK": Matrix(1, 0, 2, 1),
|
|
254
|
+
"SHELL": Matrix(1, 4, 0, 1), # Upper triangular (distinct from Deploy, v1.0.8+)
|
|
255
|
+
"DATABASE": Matrix(1, 0, 3, 1),
|
|
256
|
+
"HANDOFF": Matrix(3, 2, 1, 1),
|
|
257
|
+
"DELEGATE": Matrix(1, 1, 2, 3),
|
|
258
|
+
"ALL": Matrix(1, 0, 0, 1), # Identity
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def get_generator(task: str) -> Matrix:
|
|
263
|
+
"""Get the generator matrix for a task."""
|
|
264
|
+
return GENERATOR_MATRICES.get(task.upper(), Matrix.identity())
|
|
@@ -5,12 +5,15 @@ lockstock_a2a/task_handler.py,sha256=hKpk9smt12nqTlJua861LpdKiiXpcRGRYhCW0SwS438
|
|
|
5
5
|
lockstock_claude/__init__.py,sha256=yI1e13JC_KKgYrap2AIkVl5biCsHKfb9rPtgbN1QNtg,320
|
|
6
6
|
lockstock_claude/hooks.py,sha256=uJ-i3s9pmwcgSIpc84TlxNAyP2a1S8Auyo6o1H3KrcU,5116
|
|
7
7
|
lockstock_claude/skills.py,sha256=4f6HprTBfjmPvcDzJ40fcsMoF543EybbE0_KM_kBtpQ,4023
|
|
8
|
+
lockstock_core/__init__.py,sha256=Euan9X4O6XBgQ8IYoC5hj2ruTNU0vPLvBTCIPZZNQdU,258
|
|
9
|
+
lockstock_core/client.py,sha256=gNacFIoH9DA9Dw2moyfPHGj7mlexrJ7VxIo0e3iRX8M,9110
|
|
10
|
+
lockstock_core/types.py,sha256=4OtO7PWcZyXxbmuFcoXXxjO3tKHV1oNGpm5yExLTCQE,7426
|
|
8
11
|
lockstock_langgraph/__init__.py,sha256=7fBIYNCQygvUvG7IQOVY042sNNmu4UwkJoy0VLQn4Ik,425
|
|
9
12
|
lockstock_langgraph/checkpointer.py,sha256=XJ2c_92OSBq4aNpOT112nTy3yI3stFcSCb8OTx04nos,6317
|
|
10
13
|
lockstock_langgraph/middleware.py,sha256=ZSwoxdkn3cC6aIBnMgd1jYGHYKa1yQXRwnts3Ds4sfQ,9174
|
|
11
14
|
lockstock_openai/__init__.py,sha256=Pc4phRUJEqrnREggyGJnc1HDKa7puVAR6-G9WZ4dtXM,402
|
|
12
15
|
lockstock_openai/guardrails.py,sha256=fy84bDrhy6CVk2b4_yOl5XTxw9Lp1HT4Mw9WcaAHOsg,11920
|
|
13
16
|
lockstock_openai/tracing.py,sha256=VQWzG0y6t8q5rJZFbbP-bi9tsV0KaUqqH4Kr9-vr_e8,6025
|
|
14
|
-
lockstock_integrations-1.1.
|
|
15
|
-
lockstock_integrations-1.1.
|
|
16
|
-
lockstock_integrations-1.1.
|
|
17
|
+
lockstock_integrations-1.1.1.dist-info/METADATA,sha256=H022KMhtIDctPxNHkdc70WRfzjyO72xwK6Ed5u69Au8,5629
|
|
18
|
+
lockstock_integrations-1.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
19
|
+
lockstock_integrations-1.1.1.dist-info/RECORD,,
|
|
File without changes
|