codetether 1.2.2__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.
- a2a_server/__init__.py +29 -0
- a2a_server/a2a_agent_card.py +365 -0
- a2a_server/a2a_errors.py +1133 -0
- a2a_server/a2a_executor.py +926 -0
- a2a_server/a2a_router.py +1033 -0
- a2a_server/a2a_types.py +344 -0
- a2a_server/agent_card.py +408 -0
- a2a_server/agents_server.py +271 -0
- a2a_server/auth_api.py +349 -0
- a2a_server/billing_api.py +638 -0
- a2a_server/billing_service.py +712 -0
- a2a_server/billing_webhooks.py +501 -0
- a2a_server/config.py +96 -0
- a2a_server/database.py +2165 -0
- a2a_server/email_inbound.py +398 -0
- a2a_server/email_notifications.py +486 -0
- a2a_server/enhanced_agents.py +919 -0
- a2a_server/enhanced_server.py +160 -0
- a2a_server/hosted_worker.py +1049 -0
- a2a_server/integrated_agents_server.py +347 -0
- a2a_server/keycloak_auth.py +750 -0
- a2a_server/livekit_bridge.py +439 -0
- a2a_server/marketing_tools.py +1364 -0
- a2a_server/mcp_client.py +196 -0
- a2a_server/mcp_http_server.py +2256 -0
- a2a_server/mcp_server.py +191 -0
- a2a_server/message_broker.py +725 -0
- a2a_server/mock_mcp.py +273 -0
- a2a_server/models.py +494 -0
- a2a_server/monitor_api.py +5904 -0
- a2a_server/opencode_bridge.py +1594 -0
- a2a_server/redis_task_manager.py +518 -0
- a2a_server/server.py +726 -0
- a2a_server/task_manager.py +668 -0
- a2a_server/task_queue.py +742 -0
- a2a_server/tenant_api.py +333 -0
- a2a_server/tenant_middleware.py +219 -0
- a2a_server/tenant_service.py +760 -0
- a2a_server/user_auth.py +721 -0
- a2a_server/vault_client.py +576 -0
- a2a_server/worker_sse.py +873 -0
- agent_worker/__init__.py +8 -0
- agent_worker/worker.py +4877 -0
- codetether/__init__.py +10 -0
- codetether/__main__.py +4 -0
- codetether/cli.py +112 -0
- codetether/worker_cli.py +57 -0
- codetether-1.2.2.dist-info/METADATA +570 -0
- codetether-1.2.2.dist-info/RECORD +66 -0
- codetether-1.2.2.dist-info/WHEEL +5 -0
- codetether-1.2.2.dist-info/entry_points.txt +4 -0
- codetether-1.2.2.dist-info/licenses/LICENSE +202 -0
- codetether-1.2.2.dist-info/top_level.txt +5 -0
- codetether_voice_agent/__init__.py +6 -0
- codetether_voice_agent/agent.py +445 -0
- codetether_voice_agent/codetether_mcp.py +345 -0
- codetether_voice_agent/config.py +16 -0
- codetether_voice_agent/functiongemma_caller.py +380 -0
- codetether_voice_agent/session_playback.py +247 -0
- codetether_voice_agent/tools/__init__.py +21 -0
- codetether_voice_agent/tools/definitions.py +135 -0
- codetether_voice_agent/tools/handlers.py +380 -0
- run_server.py +314 -0
- ui/monitor-tailwind.html +1790 -0
- ui/monitor.html +1775 -0
- ui/monitor.js +2662 -0
a2a_server/a2a_types.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A Protocol State Types and Mappings
|
|
3
|
+
|
|
4
|
+
This module provides alignment between our internal task states and the
|
|
5
|
+
official A2A protocol task states as defined in the specification.
|
|
6
|
+
|
|
7
|
+
A2A Spec States:
|
|
8
|
+
- submitted: Task created and acknowledged
|
|
9
|
+
- working: Actively processing
|
|
10
|
+
- completed: Finished successfully (TERMINAL)
|
|
11
|
+
- failed: Done but failed (TERMINAL)
|
|
12
|
+
- cancelled: Cancelled before completion (TERMINAL)
|
|
13
|
+
- input-required: Awaiting additional input
|
|
14
|
+
- rejected: Agent declined the task (TERMINAL)
|
|
15
|
+
- auth-required: Needs out-of-band authentication
|
|
16
|
+
|
|
17
|
+
Our Internal States (TaskStatus):
|
|
18
|
+
- SUBMITTED: Initial state after creation
|
|
19
|
+
- PENDING: Legacy alias for submitted (deprecated)
|
|
20
|
+
- WORKING: Actively processing
|
|
21
|
+
- INPUT_REQUIRED: Awaiting user input
|
|
22
|
+
- COMPLETED: Finished successfully (TERMINAL)
|
|
23
|
+
- FAILED: Failed (TERMINAL)
|
|
24
|
+
- CANCELLED: Cancelled (TERMINAL)
|
|
25
|
+
- REJECTED: Agent declined (TERMINAL)
|
|
26
|
+
- AUTH_REQUIRED: Needs authentication
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from enum import Enum
|
|
30
|
+
from typing import Set
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class A2ATaskState(str, Enum):
|
|
34
|
+
"""
|
|
35
|
+
Official A2A protocol task states.
|
|
36
|
+
|
|
37
|
+
These are the states defined in the A2A specification that should be
|
|
38
|
+
used when communicating with external A2A clients and servers.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
SUBMITTED = 'submitted'
|
|
42
|
+
WORKING = 'working'
|
|
43
|
+
COMPLETED = 'completed'
|
|
44
|
+
FAILED = 'failed'
|
|
45
|
+
CANCELLED = 'cancelled'
|
|
46
|
+
INPUT_REQUIRED = 'input-required'
|
|
47
|
+
REJECTED = 'rejected'
|
|
48
|
+
AUTH_REQUIRED = 'auth-required'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Terminal states - once a task reaches these, it cannot transition further
|
|
52
|
+
A2A_TERMINAL_STATES: Set[A2ATaskState] = {
|
|
53
|
+
A2ATaskState.COMPLETED,
|
|
54
|
+
A2ATaskState.FAILED,
|
|
55
|
+
A2ATaskState.CANCELLED,
|
|
56
|
+
A2ATaskState.REJECTED,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Non-terminal states - task can still transition
|
|
60
|
+
A2A_ACTIVE_STATES: Set[A2ATaskState] = {
|
|
61
|
+
A2ATaskState.SUBMITTED,
|
|
62
|
+
A2ATaskState.WORKING,
|
|
63
|
+
A2ATaskState.INPUT_REQUIRED,
|
|
64
|
+
A2ATaskState.AUTH_REQUIRED,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_a2a_terminal_state(state: A2ATaskState) -> bool:
|
|
69
|
+
"""
|
|
70
|
+
Check if an A2A state is terminal.
|
|
71
|
+
|
|
72
|
+
Terminal states indicate the task has reached a final state and
|
|
73
|
+
cannot transition to any other state.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
state: The A2A task state to check
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
True if the state is terminal, False otherwise
|
|
80
|
+
"""
|
|
81
|
+
return state in A2A_TERMINAL_STATES
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def is_a2a_active_state(state: A2ATaskState) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Check if an A2A state is active (non-terminal).
|
|
87
|
+
|
|
88
|
+
Active states indicate the task is still in progress and can
|
|
89
|
+
transition to other states.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
state: The A2A task state to check
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
True if the state is active, False otherwise
|
|
96
|
+
"""
|
|
97
|
+
return state in A2A_ACTIVE_STATES
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Import here to avoid circular imports - we need the actual TaskStatus
|
|
101
|
+
# This will be populated after models.py is updated
|
|
102
|
+
def _get_task_status():
|
|
103
|
+
"""Lazy import of TaskStatus to avoid circular imports."""
|
|
104
|
+
from a2a_server.models import TaskStatus
|
|
105
|
+
|
|
106
|
+
return TaskStatus
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# Mapping from internal TaskStatus to A2A protocol states
|
|
110
|
+
_INTERNAL_TO_A2A_MAP = {
|
|
111
|
+
'submitted': A2ATaskState.SUBMITTED,
|
|
112
|
+
'pending': A2ATaskState.SUBMITTED, # Legacy mapping
|
|
113
|
+
'working': A2ATaskState.WORKING,
|
|
114
|
+
'input-required': A2ATaskState.INPUT_REQUIRED,
|
|
115
|
+
'completed': A2ATaskState.COMPLETED,
|
|
116
|
+
'failed': A2ATaskState.FAILED,
|
|
117
|
+
'cancelled': A2ATaskState.CANCELLED,
|
|
118
|
+
'rejected': A2ATaskState.REJECTED,
|
|
119
|
+
'auth-required': A2ATaskState.AUTH_REQUIRED,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Mapping from A2A protocol states to internal TaskStatus values
|
|
123
|
+
_A2A_TO_INTERNAL_MAP = {
|
|
124
|
+
A2ATaskState.SUBMITTED: 'submitted',
|
|
125
|
+
A2ATaskState.WORKING: 'working',
|
|
126
|
+
A2ATaskState.INPUT_REQUIRED: 'input-required',
|
|
127
|
+
A2ATaskState.COMPLETED: 'completed',
|
|
128
|
+
A2ATaskState.FAILED: 'failed',
|
|
129
|
+
A2ATaskState.CANCELLED: 'cancelled',
|
|
130
|
+
A2ATaskState.REJECTED: 'rejected',
|
|
131
|
+
A2ATaskState.AUTH_REQUIRED: 'auth-required',
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def internal_to_a2a_state(internal_state) -> A2ATaskState:
|
|
136
|
+
"""
|
|
137
|
+
Convert an internal TaskStatus to the corresponding A2A protocol state.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
internal_state: TaskStatus enum value or string representation
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
The corresponding A2ATaskState
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValueError: If the internal state has no A2A mapping
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
>>> internal_to_a2a_state(TaskStatus.PENDING)
|
|
150
|
+
<A2ATaskState.SUBMITTED: 'submitted'>
|
|
151
|
+
|
|
152
|
+
>>> internal_to_a2a_state(TaskStatus.WORKING)
|
|
153
|
+
<A2ATaskState.WORKING: 'working'>
|
|
154
|
+
|
|
155
|
+
>>> internal_to_a2a_state('completed')
|
|
156
|
+
<A2ATaskState.COMPLETED: 'completed'>
|
|
157
|
+
"""
|
|
158
|
+
# Handle both enum and string values
|
|
159
|
+
if hasattr(internal_state, 'value'):
|
|
160
|
+
state_value = internal_state.value
|
|
161
|
+
else:
|
|
162
|
+
state_value = str(internal_state).lower()
|
|
163
|
+
|
|
164
|
+
if state_value not in _INTERNAL_TO_A2A_MAP:
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Unknown internal state '{state_value}'. "
|
|
167
|
+
f'Valid states: {list(_INTERNAL_TO_A2A_MAP.keys())}'
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return _INTERNAL_TO_A2A_MAP[state_value]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def a2a_to_internal_state(a2a_state: A2ATaskState):
|
|
174
|
+
"""
|
|
175
|
+
Convert an A2A protocol state to the corresponding internal TaskStatus.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
a2a_state: A2ATaskState enum value or string representation
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
The corresponding TaskStatus enum value
|
|
182
|
+
|
|
183
|
+
Raises:
|
|
184
|
+
ValueError: If the A2A state has no internal mapping
|
|
185
|
+
|
|
186
|
+
Examples:
|
|
187
|
+
>>> a2a_to_internal_state(A2ATaskState.SUBMITTED)
|
|
188
|
+
<TaskStatus.SUBMITTED: 'submitted'>
|
|
189
|
+
|
|
190
|
+
>>> a2a_to_internal_state('working')
|
|
191
|
+
<TaskStatus.WORKING: 'working'>
|
|
192
|
+
"""
|
|
193
|
+
TaskStatus = _get_task_status()
|
|
194
|
+
|
|
195
|
+
# Handle both enum and string values
|
|
196
|
+
if isinstance(a2a_state, str):
|
|
197
|
+
try:
|
|
198
|
+
a2a_state = A2ATaskState(a2a_state)
|
|
199
|
+
except ValueError:
|
|
200
|
+
raise ValueError(
|
|
201
|
+
f"Unknown A2A state '{a2a_state}'. "
|
|
202
|
+
f'Valid states: {[s.value for s in A2ATaskState]}'
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if a2a_state not in _A2A_TO_INTERNAL_MAP:
|
|
206
|
+
raise ValueError(
|
|
207
|
+
f"Unknown A2A state '{a2a_state}'. "
|
|
208
|
+
f'Valid states: {list(_A2A_TO_INTERNAL_MAP.keys())}'
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
internal_value = _A2A_TO_INTERNAL_MAP[a2a_state]
|
|
212
|
+
return TaskStatus(internal_value)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def is_internal_terminal_state(internal_state) -> bool:
|
|
216
|
+
"""
|
|
217
|
+
Check if an internal TaskStatus is terminal.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
internal_state: TaskStatus enum value or string representation
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
True if the state is terminal, False otherwise
|
|
224
|
+
|
|
225
|
+
Examples:
|
|
226
|
+
>>> is_internal_terminal_state(TaskStatus.COMPLETED)
|
|
227
|
+
True
|
|
228
|
+
|
|
229
|
+
>>> is_internal_terminal_state(TaskStatus.WORKING)
|
|
230
|
+
False
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
a2a_state = internal_to_a2a_state(internal_state)
|
|
234
|
+
return is_a2a_terminal_state(a2a_state)
|
|
235
|
+
except ValueError:
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def is_internal_active_state(internal_state) -> bool:
|
|
240
|
+
"""
|
|
241
|
+
Check if an internal TaskStatus is active (non-terminal).
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
internal_state: TaskStatus enum value or string representation
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
True if the state is active, False otherwise
|
|
248
|
+
"""
|
|
249
|
+
try:
|
|
250
|
+
a2a_state = internal_to_a2a_state(internal_state)
|
|
251
|
+
return is_a2a_active_state(a2a_state)
|
|
252
|
+
except ValueError:
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# Valid state transitions as defined by A2A protocol
|
|
257
|
+
# Key is the current state, value is set of valid next states
|
|
258
|
+
VALID_STATE_TRANSITIONS = {
|
|
259
|
+
A2ATaskState.SUBMITTED: {
|
|
260
|
+
A2ATaskState.WORKING,
|
|
261
|
+
A2ATaskState.COMPLETED,
|
|
262
|
+
A2ATaskState.FAILED,
|
|
263
|
+
A2ATaskState.CANCELLED,
|
|
264
|
+
A2ATaskState.REJECTED,
|
|
265
|
+
A2ATaskState.INPUT_REQUIRED,
|
|
266
|
+
A2ATaskState.AUTH_REQUIRED,
|
|
267
|
+
},
|
|
268
|
+
A2ATaskState.WORKING: {
|
|
269
|
+
A2ATaskState.COMPLETED,
|
|
270
|
+
A2ATaskState.FAILED,
|
|
271
|
+
A2ATaskState.CANCELLED,
|
|
272
|
+
A2ATaskState.INPUT_REQUIRED,
|
|
273
|
+
A2ATaskState.AUTH_REQUIRED,
|
|
274
|
+
},
|
|
275
|
+
A2ATaskState.INPUT_REQUIRED: {
|
|
276
|
+
A2ATaskState.WORKING,
|
|
277
|
+
A2ATaskState.COMPLETED,
|
|
278
|
+
A2ATaskState.FAILED,
|
|
279
|
+
A2ATaskState.CANCELLED,
|
|
280
|
+
},
|
|
281
|
+
A2ATaskState.AUTH_REQUIRED: {
|
|
282
|
+
A2ATaskState.WORKING,
|
|
283
|
+
A2ATaskState.COMPLETED,
|
|
284
|
+
A2ATaskState.FAILED,
|
|
285
|
+
A2ATaskState.CANCELLED,
|
|
286
|
+
},
|
|
287
|
+
# Terminal states cannot transition
|
|
288
|
+
A2ATaskState.COMPLETED: set(),
|
|
289
|
+
A2ATaskState.FAILED: set(),
|
|
290
|
+
A2ATaskState.CANCELLED: set(),
|
|
291
|
+
A2ATaskState.REJECTED: set(),
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def is_valid_transition(
|
|
296
|
+
from_state: A2ATaskState, to_state: A2ATaskState
|
|
297
|
+
) -> bool:
|
|
298
|
+
"""
|
|
299
|
+
Check if a state transition is valid according to A2A protocol.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
from_state: Current state
|
|
303
|
+
to_state: Target state
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
True if the transition is valid, False otherwise
|
|
307
|
+
|
|
308
|
+
Examples:
|
|
309
|
+
>>> is_valid_transition(A2ATaskState.SUBMITTED, A2ATaskState.WORKING)
|
|
310
|
+
True
|
|
311
|
+
|
|
312
|
+
>>> is_valid_transition(A2ATaskState.COMPLETED, A2ATaskState.WORKING)
|
|
313
|
+
False
|
|
314
|
+
"""
|
|
315
|
+
if from_state not in VALID_STATE_TRANSITIONS:
|
|
316
|
+
return False
|
|
317
|
+
return to_state in VALID_STATE_TRANSITIONS[from_state]
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def get_valid_next_states(current_state: A2ATaskState) -> Set[A2ATaskState]:
|
|
321
|
+
"""
|
|
322
|
+
Get the set of valid states that can be transitioned to from the current state.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
current_state: The current A2A task state
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
Set of valid next states (empty set for terminal states)
|
|
329
|
+
|
|
330
|
+
Examples:
|
|
331
|
+
>>> get_valid_next_states(A2ATaskState.WORKING)
|
|
332
|
+
{<A2ATaskState.COMPLETED>, <A2ATaskState.FAILED>, ...}
|
|
333
|
+
|
|
334
|
+
>>> get_valid_next_states(A2ATaskState.COMPLETED)
|
|
335
|
+
set()
|
|
336
|
+
"""
|
|
337
|
+
return VALID_STATE_TRANSITIONS.get(current_state, set())
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# Convenience constants for commonly used state sets
|
|
341
|
+
TERMINAL_STATES = A2A_TERMINAL_STATES
|
|
342
|
+
ACTIVE_STATES = A2A_ACTIVE_STATES
|
|
343
|
+
WAITING_STATES = {A2ATaskState.INPUT_REQUIRED, A2ATaskState.AUTH_REQUIRED}
|
|
344
|
+
PROCESSING_STATES = {A2ATaskState.SUBMITTED, A2ATaskState.WORKING}
|