agentenv-framework-protocol 0.1.369__tar.gz → 0.1.370__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.
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/PKG-INFO +46 -1
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/README.md +45 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/pyproject.toml +1 -1
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/__init__.py +38 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/extensions.py +147 -1
- agentenv_framework_protocol-0.1.370/src/agentenv_protocol/a2a_agent/proxy.py +516 -0
- agentenv_framework_protocol-0.1.370/tests/test_a2a_proxy.py +511 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/LICENSE +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/NOTICE +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/THIRD_PARTY_NOTICES.md +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/advanced_agent.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/basic_agent.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/custom_extensions_agent.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/multimodal_agent.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/reference_model.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/examples/streaming_agent.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/client.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/preflight.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/transfers.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/src/agentenv_protocol/types.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_a2a_agent.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_a2a_agent_examples.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_a2a_object_transfers.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_a2a_peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_a2a_staging.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_a2a_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_client.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_data_objects.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_intake.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_manifest.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_preflight.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_tool_context_fastmcp.py +0 -0
- {agentenv_framework_protocol-0.1.369 → agentenv_framework_protocol-0.1.370}/tests/test_tool_context_injection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentenv-framework-protocol
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.370
|
|
4
4
|
Summary: Open data-plane protocol and server SDK for agent environments.
|
|
5
5
|
Project-URL: Homepage, https://www.agentenvframework.com
|
|
6
6
|
Project-URL: Documentation, https://www.agentenvframework.com/docs
|
|
@@ -743,6 +743,51 @@ The answer is agent-wide rather than per caller, and like every extension route
|
|
|
743
743
|
probe carries no access check of its own, so expose an agent only to the control plane
|
|
744
744
|
that drives it.
|
|
745
745
|
|
|
746
|
+
`PROXY_V1` hands an agent's turns to a driver outside its sandbox, and `ProxyAgent`
|
|
747
|
+
(`agentenv_protocol.a2a_agent.proxy`) serves it. The proxy has no model: each message it
|
|
748
|
+
gets stays an open turn, its task working, until the driver replies, and every action the
|
|
749
|
+
driver's model takes runs in the proxy, where the agent would work. The driver's first
|
|
750
|
+
`POST /ext/proxy/claim` (`{"token"}`, at least 22 characters) sets the token every later
|
|
751
|
+
call carries in its body. A claim with another token gets 409, and any other call with a
|
|
752
|
+
wrong one 401. The token is held in memory only, never in the environment a command
|
|
753
|
+
inherits. Content travels as parts, `{"kind": "text", "text": ...}`; a message with any
|
|
754
|
+
other part fails as `proxy.unsupported_part` before the driver sees it. The other
|
|
755
|
+
operations:
|
|
756
|
+
|
|
757
|
+
- `next` (`{"token", "after"}`) waits up to 25 s for the turns' version to pass `after`,
|
|
758
|
+
then returns `{"version", "turns": [{"task_id", "context_id", "parts", "system_prompt",
|
|
759
|
+
"skills"}]}`. A turn its sender cancels drops out of the list.
|
|
760
|
+
- `tools` (`{"token", "task_id"}`) lists what the turn can call, with input schemas: the
|
|
761
|
+
tools of each env server registered through mcp-config, and `bash`. Two servers offering
|
|
762
|
+
one name are refused with 409.
|
|
763
|
+
- `act` (`{"token", "task_id", "action_id", "action"}`) runs one action: `{"kind": "tool",
|
|
764
|
+
"server", "name", "arguments"}` calls an env server through `ToolSession`, with the URL
|
|
765
|
+
and headers registered for it, and `{"kind": "bash", "command"}` runs `bash -c` in the
|
|
766
|
+
proxy's folder, as its user, in a process group of its own; either takes
|
|
767
|
+
`"timeout_seconds"` (3600 for `bash`, 600 for a tool by default). The driver picks
|
|
768
|
+
`action_id`, so a retried `act` returns the action it already started. It answers
|
|
769
|
+
`{"done", "result"}` at once when the action ends within the long-poll; `result`
|
|
770
|
+
(`{"token", "action_id"}`) long-polls one that doesn't. A `bash` result is
|
|
771
|
+
`{"exit_code", "stdout", "stderr", "timed_out"}`, a tool's `{"content", "is_error"}`.
|
|
772
|
+
- `record` (`{"token", "task_id", "message", "model", "usage"}`) records one model call: an
|
|
773
|
+
OpenAI chat message, an Anthropic message or a string; anything else gets 400. Once a turn
|
|
774
|
+
records a call, each action must be one the last recorded call made, by name and
|
|
775
|
+
arguments, and all of them must run before the next `record` or the `reply`, else 409.
|
|
776
|
+
Calls to tools the turn doesn't offer aren't checked.
|
|
777
|
+
- `reply` (`{"token", "task_id", "parts"}`, or `"error"` in place of `"parts"`) ends the
|
|
778
|
+
turn: parts complete the task, and an error fails it as `proxy.driver_error`.
|
|
779
|
+
|
|
780
|
+
A turn's actions belong to it: when it closes, by reply or withdrawal, those still running
|
|
781
|
+
are stopped, their whole process groups killed, and its results forgotten; the app's
|
|
782
|
+
lifespan stops what runs when it shuts down. The proxy writes the turn's trajectory as it
|
|
783
|
+
goes, readable live, as OTel GenAI spans in format `agentenv-proxy/v1`: `chat` for each
|
|
784
|
+
recorded call, `execute_tool` for each action, and `chain` for the turn. Skills it is given
|
|
785
|
+
are written to `~/skills/<name>/`, once each, and a turn names each one's folder.
|
|
786
|
+
`ProxyAgent(shell=False)` is a proxy for an agent that works in no sandbox: it offers no
|
|
787
|
+
`bash` and keeps no skills. It holds the env servers' headers as any agent does, so run it
|
|
788
|
+
with no credentials in its environment and expose it only to the control plane that drives
|
|
789
|
+
it.
|
|
790
|
+
|
|
746
791
|
Runnable, self-contained reference agents live in [`examples/`](https://github.com/scaleapi/agentenv-framework/tree/main/packages/agentenv-protocol/examples):
|
|
747
792
|
the normal, streaming, and multimodal `run(request)` paths, single- and
|
|
748
793
|
multi-operation custom extensions, and advanced ASGI-lifespan plus
|
|
@@ -697,6 +697,51 @@ The answer is agent-wide rather than per caller, and like every extension route
|
|
|
697
697
|
probe carries no access check of its own, so expose an agent only to the control plane
|
|
698
698
|
that drives it.
|
|
699
699
|
|
|
700
|
+
`PROXY_V1` hands an agent's turns to a driver outside its sandbox, and `ProxyAgent`
|
|
701
|
+
(`agentenv_protocol.a2a_agent.proxy`) serves it. The proxy has no model: each message it
|
|
702
|
+
gets stays an open turn, its task working, until the driver replies, and every action the
|
|
703
|
+
driver's model takes runs in the proxy, where the agent would work. The driver's first
|
|
704
|
+
`POST /ext/proxy/claim` (`{"token"}`, at least 22 characters) sets the token every later
|
|
705
|
+
call carries in its body. A claim with another token gets 409, and any other call with a
|
|
706
|
+
wrong one 401. The token is held in memory only, never in the environment a command
|
|
707
|
+
inherits. Content travels as parts, `{"kind": "text", "text": ...}`; a message with any
|
|
708
|
+
other part fails as `proxy.unsupported_part` before the driver sees it. The other
|
|
709
|
+
operations:
|
|
710
|
+
|
|
711
|
+
- `next` (`{"token", "after"}`) waits up to 25 s for the turns' version to pass `after`,
|
|
712
|
+
then returns `{"version", "turns": [{"task_id", "context_id", "parts", "system_prompt",
|
|
713
|
+
"skills"}]}`. A turn its sender cancels drops out of the list.
|
|
714
|
+
- `tools` (`{"token", "task_id"}`) lists what the turn can call, with input schemas: the
|
|
715
|
+
tools of each env server registered through mcp-config, and `bash`. Two servers offering
|
|
716
|
+
one name are refused with 409.
|
|
717
|
+
- `act` (`{"token", "task_id", "action_id", "action"}`) runs one action: `{"kind": "tool",
|
|
718
|
+
"server", "name", "arguments"}` calls an env server through `ToolSession`, with the URL
|
|
719
|
+
and headers registered for it, and `{"kind": "bash", "command"}` runs `bash -c` in the
|
|
720
|
+
proxy's folder, as its user, in a process group of its own; either takes
|
|
721
|
+
`"timeout_seconds"` (3600 for `bash`, 600 for a tool by default). The driver picks
|
|
722
|
+
`action_id`, so a retried `act` returns the action it already started. It answers
|
|
723
|
+
`{"done", "result"}` at once when the action ends within the long-poll; `result`
|
|
724
|
+
(`{"token", "action_id"}`) long-polls one that doesn't. A `bash` result is
|
|
725
|
+
`{"exit_code", "stdout", "stderr", "timed_out"}`, a tool's `{"content", "is_error"}`.
|
|
726
|
+
- `record` (`{"token", "task_id", "message", "model", "usage"}`) records one model call: an
|
|
727
|
+
OpenAI chat message, an Anthropic message or a string; anything else gets 400. Once a turn
|
|
728
|
+
records a call, each action must be one the last recorded call made, by name and
|
|
729
|
+
arguments, and all of them must run before the next `record` or the `reply`, else 409.
|
|
730
|
+
Calls to tools the turn doesn't offer aren't checked.
|
|
731
|
+
- `reply` (`{"token", "task_id", "parts"}`, or `"error"` in place of `"parts"`) ends the
|
|
732
|
+
turn: parts complete the task, and an error fails it as `proxy.driver_error`.
|
|
733
|
+
|
|
734
|
+
A turn's actions belong to it: when it closes, by reply or withdrawal, those still running
|
|
735
|
+
are stopped, their whole process groups killed, and its results forgotten; the app's
|
|
736
|
+
lifespan stops what runs when it shuts down. The proxy writes the turn's trajectory as it
|
|
737
|
+
goes, readable live, as OTel GenAI spans in format `agentenv-proxy/v1`: `chat` for each
|
|
738
|
+
recorded call, `execute_tool` for each action, and `chain` for the turn. Skills it is given
|
|
739
|
+
are written to `~/skills/<name>/`, once each, and a turn names each one's folder.
|
|
740
|
+
`ProxyAgent(shell=False)` is a proxy for an agent that works in no sandbox: it offers no
|
|
741
|
+
`bash` and keeps no skills. It holds the env servers' headers as any agent does, so run it
|
|
742
|
+
with no credentials in its environment and expose it only to the control plane that drives
|
|
743
|
+
it.
|
|
744
|
+
|
|
700
745
|
Runnable, self-contained reference agents live in [`examples/`](https://github.com/scaleapi/agentenv-framework/tree/main/packages/agentenv-protocol/examples):
|
|
701
746
|
the normal, streaming, and multimodal `run(request)` paths, single- and
|
|
702
747
|
multi-operation custom extensions, and advanced ASGI-lifespan plus
|
|
@@ -5,6 +5,7 @@ from a2a.types import AgentCapabilities
|
|
|
5
5
|
from .extensions import (
|
|
6
6
|
AGENT_CONFIG_V1,
|
|
7
7
|
ATTRIBUTION_PROBE_V1,
|
|
8
|
+
PROXY_V1,
|
|
8
9
|
INSTALL_V1,
|
|
9
10
|
MCP_CONFIG_V1,
|
|
10
11
|
PEER_AGENTS_V1,
|
|
@@ -19,6 +20,24 @@ from .extensions import (
|
|
|
19
20
|
ContextEventsTrajectoryRequest,
|
|
20
21
|
ContextObjectTrajectoryRequest,
|
|
21
22
|
ContextTrajectoryRequest,
|
|
23
|
+
ProxyActRequest,
|
|
24
|
+
ProxyActionState,
|
|
25
|
+
ProxyBashAction,
|
|
26
|
+
ProxyBashResult,
|
|
27
|
+
ProxyClaimRequest,
|
|
28
|
+
ProxyNextRequest,
|
|
29
|
+
ProxyNextResponse,
|
|
30
|
+
ProxyRecordRequest,
|
|
31
|
+
ProxyReplyRequest,
|
|
32
|
+
ProxyResultRequest,
|
|
33
|
+
ProxySkill,
|
|
34
|
+
ProxyTextPart,
|
|
35
|
+
ProxyTool,
|
|
36
|
+
ProxyToolAction,
|
|
37
|
+
ProxyToolResult,
|
|
38
|
+
ProxyToolsRequest,
|
|
39
|
+
ProxyToolsResponse,
|
|
40
|
+
ProxyTurn,
|
|
22
41
|
ExtensionActivation,
|
|
23
42
|
ExtensionConfiguration,
|
|
24
43
|
ExtensionDefinition,
|
|
@@ -117,6 +136,7 @@ from ..transfers import (
|
|
|
117
136
|
__all__ = [
|
|
118
137
|
"AGENT_CONFIG_V1",
|
|
119
138
|
"ATTRIBUTION_PROBE_V1",
|
|
139
|
+
"PROXY_V1",
|
|
120
140
|
"INSTALL_V1",
|
|
121
141
|
"MCP_CONFIG_V1",
|
|
122
142
|
"PEER_AGENTS_V1",
|
|
@@ -139,6 +159,24 @@ __all__ = [
|
|
|
139
159
|
"ContextEventsTrajectoryRequest",
|
|
140
160
|
"ContextObjectTrajectoryRequest",
|
|
141
161
|
"ContextTrajectoryRequest",
|
|
162
|
+
"ProxyActRequest",
|
|
163
|
+
"ProxyActionState",
|
|
164
|
+
"ProxyBashAction",
|
|
165
|
+
"ProxyBashResult",
|
|
166
|
+
"ProxyClaimRequest",
|
|
167
|
+
"ProxyNextRequest",
|
|
168
|
+
"ProxyNextResponse",
|
|
169
|
+
"ProxyRecordRequest",
|
|
170
|
+
"ProxyReplyRequest",
|
|
171
|
+
"ProxyResultRequest",
|
|
172
|
+
"ProxySkill",
|
|
173
|
+
"ProxyTextPart",
|
|
174
|
+
"ProxyTool",
|
|
175
|
+
"ProxyToolAction",
|
|
176
|
+
"ProxyToolResult",
|
|
177
|
+
"ProxyToolsRequest",
|
|
178
|
+
"ProxyToolsResponse",
|
|
179
|
+
"ProxyTurn",
|
|
142
180
|
"DataPart",
|
|
143
181
|
"DefaultExtensionHandlers",
|
|
144
182
|
"ExtensionActivation",
|
|
@@ -10,7 +10,7 @@ from collections.abc import Callable, Iterable, Mapping
|
|
|
10
10
|
from dataclasses import dataclass, field
|
|
11
11
|
from enum import Enum
|
|
12
12
|
from types import MappingProxyType
|
|
13
|
-
from typing import Annotated, Any
|
|
13
|
+
from typing import Annotated, Any, Literal
|
|
14
14
|
|
|
15
15
|
from pydantic import (
|
|
16
16
|
BaseModel,
|
|
@@ -287,6 +287,124 @@ class TriggerDecideRequest(ExtensionRequest):
|
|
|
287
287
|
env_triggers: dict[str, dict[str, Any]] | None = None
|
|
288
288
|
|
|
289
289
|
|
|
290
|
+
PROXY_TOKEN_MIN_LENGTH = 22
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class ProxyClaimRequest(ExtensionRequest):
|
|
294
|
+
token: str = Field(min_length=PROXY_TOKEN_MIN_LENGTH)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class ProxyNextRequest(ExtensionRequest):
|
|
298
|
+
token: str
|
|
299
|
+
after: int = Field(default=0, ge=0)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class ProxyTextPart(ExtensionRequest):
|
|
303
|
+
kind: Literal["text"] = "text"
|
|
304
|
+
text: str
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class ProxySkill(ExtensionResponse):
|
|
308
|
+
name: str
|
|
309
|
+
description: str
|
|
310
|
+
path: str
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class ProxyTurn(ExtensionResponse):
|
|
314
|
+
task_id: str
|
|
315
|
+
context_id: str
|
|
316
|
+
parts: list[ProxyTextPart]
|
|
317
|
+
system_prompt: str | None = None
|
|
318
|
+
skills: list[ProxySkill] = Field(default_factory=list)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class ProxyNextResponse(ExtensionResponse):
|
|
322
|
+
version: int
|
|
323
|
+
turns: list[ProxyTurn]
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class ProxyToolsRequest(ExtensionRequest):
|
|
327
|
+
token: str
|
|
328
|
+
task_id: str
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class ProxyTool(ExtensionResponse):
|
|
332
|
+
name: str
|
|
333
|
+
description: str | None = None
|
|
334
|
+
input_schema: dict[str, Any] | None = None
|
|
335
|
+
server: str | None = None # the env server that offers it; none for bash
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class ProxyToolsResponse(ExtensionResponse):
|
|
339
|
+
tools: list[ProxyTool]
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class ProxyToolAction(ExtensionRequest):
|
|
343
|
+
kind: Literal["tool"]
|
|
344
|
+
server: str
|
|
345
|
+
name: str
|
|
346
|
+
arguments: dict[str, Any] = Field(default_factory=dict)
|
|
347
|
+
timeout_seconds: float | None = Field(default=None, gt=0)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class ProxyBashAction(ExtensionRequest):
|
|
351
|
+
kind: Literal["bash"]
|
|
352
|
+
command: str
|
|
353
|
+
timeout_seconds: float | None = Field(default=None, gt=0)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class ProxyActRequest(ExtensionRequest):
|
|
357
|
+
token: str
|
|
358
|
+
task_id: str
|
|
359
|
+
action_id: str = Field(min_length=1, max_length=128)
|
|
360
|
+
action: Annotated[ProxyToolAction | ProxyBashAction, Field(discriminator="kind")]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class ProxyResultRequest(ExtensionRequest):
|
|
364
|
+
token: str
|
|
365
|
+
action_id: str
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class ProxyBashResult(ExtensionResponse):
|
|
369
|
+
kind: Literal["bash"] = "bash"
|
|
370
|
+
exit_code: int | None # none when the command was stopped
|
|
371
|
+
stdout: str
|
|
372
|
+
stderr: str
|
|
373
|
+
timed_out: bool
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class ProxyToolResult(ExtensionResponse):
|
|
377
|
+
kind: Literal["tool"] = "tool"
|
|
378
|
+
content: list[dict[str, Any]]
|
|
379
|
+
is_error: bool
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class ProxyActionState(ExtensionResponse):
|
|
383
|
+
done: bool
|
|
384
|
+
result: Annotated[ProxyBashResult | ProxyToolResult, Field(discriminator="kind")] | None = None
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class ProxyRecordRequest(ExtensionRequest):
|
|
388
|
+
token: str
|
|
389
|
+
task_id: str
|
|
390
|
+
message: str | dict[str, Any]
|
|
391
|
+
model: str | None = None
|
|
392
|
+
usage: dict[str, Any] | None = None
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class ProxyReplyRequest(ExtensionRequest):
|
|
396
|
+
token: str
|
|
397
|
+
task_id: str
|
|
398
|
+
parts: list[ProxyTextPart] | None = Field(default=None, min_length=1)
|
|
399
|
+
error: str | None = None
|
|
400
|
+
|
|
401
|
+
@model_validator(mode="after")
|
|
402
|
+
def _parts_or_error(self) -> ProxyReplyRequest:
|
|
403
|
+
if (self.parts is None) == (self.error is None):
|
|
404
|
+
raise ValueError("a reply carries exactly one of parts and error")
|
|
405
|
+
return self
|
|
406
|
+
|
|
407
|
+
|
|
290
408
|
def _tuple(values: Iterable[str]) -> tuple[str, ...]:
|
|
291
409
|
return tuple(dict.fromkeys(values))
|
|
292
410
|
|
|
@@ -1167,6 +1285,33 @@ INSTALL_V1 = ExtensionDefinition(
|
|
|
1167
1285
|
configuration_validator=_configure_install,
|
|
1168
1286
|
)
|
|
1169
1287
|
|
|
1288
|
+
PROXY_V1 = ExtensionDefinition(
|
|
1289
|
+
uri="urn:agentenv:proxy/v1",
|
|
1290
|
+
description="Hand the agent's turns to a driver outside its sandbox, and run the driver's actions where the agent works.",
|
|
1291
|
+
endpoint="/ext/proxy",
|
|
1292
|
+
core_operations={
|
|
1293
|
+
"claim": _op("claim", "POST", "/ext/proxy/claim", RUNTIME, request_model=ProxyClaimRequest),
|
|
1294
|
+
"next": _op(
|
|
1295
|
+
"next", "POST", "/ext/proxy/next", RUNTIME,
|
|
1296
|
+
request_model=ProxyNextRequest, response_model=ProxyNextResponse,
|
|
1297
|
+
),
|
|
1298
|
+
"tools": _op(
|
|
1299
|
+
"tools", "POST", "/ext/proxy/tools", RUNTIME,
|
|
1300
|
+
request_model=ProxyToolsRequest, response_model=ProxyToolsResponse,
|
|
1301
|
+
),
|
|
1302
|
+
"act": _op(
|
|
1303
|
+
"act", "POST", "/ext/proxy/act", RUNTIME,
|
|
1304
|
+
request_model=ProxyActRequest, response_model=ProxyActionState,
|
|
1305
|
+
),
|
|
1306
|
+
"result": _op(
|
|
1307
|
+
"result", "POST", "/ext/proxy/result", RUNTIME,
|
|
1308
|
+
request_model=ProxyResultRequest, response_model=ProxyActionState,
|
|
1309
|
+
),
|
|
1310
|
+
"record": _op("record", "POST", "/ext/proxy/record", RUNTIME, request_model=ProxyRecordRequest),
|
|
1311
|
+
"reply": _op("reply", "POST", "/ext/proxy/reply", RUNTIME, request_model=ProxyReplyRequest),
|
|
1312
|
+
},
|
|
1313
|
+
)
|
|
1314
|
+
|
|
1170
1315
|
STANDARD_EXTENSIONS: Mapping[str, ExtensionDefinition] = MappingProxyType(
|
|
1171
1316
|
{
|
|
1172
1317
|
extension.uri: extension
|
|
@@ -1180,6 +1325,7 @@ STANDARD_EXTENSIONS: Mapping[str, ExtensionDefinition] = MappingProxyType(
|
|
|
1180
1325
|
ATTRIBUTION_PROBE_V1,
|
|
1181
1326
|
TRIGGERS_V1,
|
|
1182
1327
|
INSTALL_V1,
|
|
1328
|
+
PROXY_V1,
|
|
1183
1329
|
)
|
|
1184
1330
|
}
|
|
1185
1331
|
)
|