dex-agents-deepagents-cli 0.6.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.
@@ -0,0 +1,112 @@
1
+ # Copyright (c) 2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Sustainable Use License 1.0.
4
+ # You may not use this file except in compliance with the License.
5
+ # See the LICENSE file in the repository root.
6
+ #
7
+ # SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
8
+
9
+ """The ``dex-agents-deepagents`` command: a Deep Agents agent in the terminal.
10
+
11
+ It builds the agent session Flow from ``create_deep_agent`` with the Dex
12
+ LangGraph adapter and runs it with the shared Dex Agents CLI core. See the
13
+ package README.
14
+ """
15
+
16
+ import dex_agents_cli_core # noqa: F401 # first: it configures gRPC before anything imports it
17
+
18
+ # isort: split
19
+
20
+ import sys
21
+
22
+ from dex_agents_cli_core import ConfigError, command_name, run_agent_cli
23
+ from dex_agents_deepagents_cli.connector_tool_names import (
24
+ COLLIDING_CONNECTOR_TOOL_PREFIX,
25
+ PrefixedConnectorToolSource,
26
+ )
27
+ from dex_agents_deepagents_cli.harness import (
28
+ DEEP_AGENTS_TOOL_RULES,
29
+ DEEPAGENTS_CLI_FLOW_TYPE,
30
+ DEFAULT_DEEPAGENTS_MODEL,
31
+ DEFAULT_INSTRUCTIONS,
32
+ EXECUTE_TOOL_NAME,
33
+ TASK_TOOL_NAME,
34
+ WRITE_TODOS_TOOL_NAME,
35
+ DeepAgentsAgentRunner,
36
+ DeepAgentsHarness,
37
+ DeepAgentsToolRule,
38
+ LocalToolSwitchesMiddleware,
39
+ TurnChatModelSelector,
40
+ TurnInstructionsMiddleware,
41
+ build_deep_agent,
42
+ deep_agents_chat_model,
43
+ deep_agents_tool_sources,
44
+ validate_deepagents_section,
45
+ )
46
+ from dex_agents_deepagents_cli.workspace_backend import (
47
+ DEEP_AGENTS_ARTIFACT_PREFIXES,
48
+ SESSION_ARTIFACTS_RETENTION,
49
+ SessionArtifactsBackend,
50
+ WorkspaceShellBackend,
51
+ bind_artifacts_session,
52
+ build_workspace_backend,
53
+ default_artifacts_directory,
54
+ ensure_artifacts_directory,
55
+ remove_stale_session_artifacts,
56
+ )
57
+
58
+ __all__ = [
59
+ "COLLIDING_CONNECTOR_TOOL_PREFIX",
60
+ "DEEPAGENTS_CLI_FLOW_TYPE",
61
+ "DEEP_AGENTS_ARTIFACT_PREFIXES",
62
+ "DEEP_AGENTS_TOOL_RULES",
63
+ "DEFAULT_DEEPAGENTS_MODEL",
64
+ "DEFAULT_INSTRUCTIONS",
65
+ "EXECUTE_TOOL_NAME",
66
+ "SESSION_ARTIFACTS_RETENTION",
67
+ "TASK_TOOL_NAME",
68
+ "WRITE_TODOS_TOOL_NAME",
69
+ "DeepAgentsAgentRunner",
70
+ "DeepAgentsHarness",
71
+ "DeepAgentsToolRule",
72
+ "LocalToolSwitchesMiddleware",
73
+ "PrefixedConnectorToolSource",
74
+ "SessionArtifactsBackend",
75
+ "TurnChatModelSelector",
76
+ "TurnInstructionsMiddleware",
77
+ "WorkspaceShellBackend",
78
+ "bind_artifacts_session",
79
+ "build_deep_agent",
80
+ "build_workspace_backend",
81
+ "deep_agents_chat_model",
82
+ "deep_agents_tool_sources",
83
+ "default_artifacts_directory",
84
+ "ensure_artifacts_directory",
85
+ "main",
86
+ "remove_stale_session_artifacts",
87
+ "run",
88
+ "validate_deepagents_section",
89
+ ]
90
+
91
+
92
+ def main(argv: list[str] | None = None) -> int:
93
+ """Run the command and return its exit code.
94
+
95
+ Args:
96
+ argv: The arguments without the program name; ``None`` reads ``sys.argv``.
97
+
98
+ Returns:
99
+ 0 on success, 1 for a failure at run time, and 2 for a usage or
100
+ configuration error, such as a key in the ``deepagents`` section.
101
+ """
102
+ harness = DeepAgentsHarness()
103
+ try:
104
+ return run_agent_cli(harness, argv)
105
+ except ConfigError as error:
106
+ print(f"{command_name(harness)}: {error}", file=sys.stderr)
107
+ return 2
108
+
109
+
110
+ def run() -> None:
111
+ """Run the command from its console script and exit with its code."""
112
+ raise SystemExit(main())
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Sustainable Use License 1.0.
4
+ # You may not use this file except in compliance with the License.
5
+ # See the LICENSE file in the repository root.
6
+ #
7
+ # SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
8
+
9
+ """``python -m dex_agents_deepagents_cli`` runs the ``dex-agents-deepagents`` command."""
10
+
11
+ from dex_agents_deepagents_cli import run
12
+
13
+ if __name__ == "__main__":
14
+ run()
@@ -0,0 +1,138 @@
1
+ # Copyright (c) 2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Sustainable Use License 1.0.
4
+ # You may not use this file except in compliance with the License.
5
+ # See the LICENSE file in the repository root.
6
+ #
7
+ # SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
8
+
9
+ """Connector tools next to Deep Agents' own: prefixed on a name collision, writes approved."""
10
+
11
+ from __future__ import annotations
12
+
13
+ import dataclasses
14
+ from collections.abc import Collection, Sequence
15
+
16
+ from dex_agents import (
17
+ AgentToolCall,
18
+ AgentTurnContext,
19
+ DeferredToolCall,
20
+ DeferredToolExecution,
21
+ DeferredToolOutcome,
22
+ ToolCallStepOptions,
23
+ ToolEffect,
24
+ ToolPolicy,
25
+ ToolTaskCancellation,
26
+ ToolTaskPoll,
27
+ ToolTaskPollOutcome,
28
+ )
29
+ from dex_agents.builtin_loop import SourceToolDefinition, ToolSource
30
+
31
+ #: The prefix of a connector tool whose name a Deep Agents tool already uses.
32
+ COLLIDING_CONNECTOR_TOOL_PREFIX = "connector_"
33
+
34
+
35
+ class PrefixedConnectorToolSource:
36
+ """Offer a connectors tool source's tools beside Deep Agents' tools.
37
+
38
+ A connector tool keeps its name unless a Deep Agents tool has it; then
39
+ the model sees it as ``connector_<name>``, and ``approval.always`` names
40
+ it that way. When the source also lists that name, the prefix repeats
41
+ until the name is free, so every offered name is unique. The source
42
+ still runs the connector operation it listed. Every call that is not a
43
+ read waits for approval.
44
+ """
45
+
46
+ def __init__(self, source: ToolSource, reserved_tool_names: Collection[str]) -> None:
47
+ """Wrap one connectors tool source.
48
+
49
+ Args:
50
+ source: The CLI core's connectors tool source.
51
+ reserved_tool_names: The Deep Agents tool names. They are fixed, so
52
+ every Worker prefixes the same tools.
53
+ """
54
+ self._source = source
55
+ self._reserved_tool_names = frozenset(reserved_tool_names)
56
+
57
+ @property
58
+ def source_name(self) -> str:
59
+ """Return the wrapped source's name, so stored calls reach it on any Worker."""
60
+ return self._source.source_name
61
+
62
+ async def list_tools(self) -> Sequence[SourceToolDefinition]:
63
+ """Return the source's listing, each colliding name prefixed."""
64
+ return self._agent_visible_listing(await self._source.list_tools())
65
+
66
+ def cached_tools(self) -> Sequence[SourceToolDefinition]:
67
+ """Return the source's last listing, each colliding name prefixed."""
68
+ read_cache = getattr(self._source, "cached_tools", None)
69
+ cached: Sequence[SourceToolDefinition] = read_cache() if callable(read_cache) else ()
70
+ return self._agent_visible_listing(cached)
71
+
72
+ def deferred_tool_call(
73
+ self,
74
+ turn: AgentTurnContext,
75
+ *,
76
+ tool_call_id: str,
77
+ tool_name: str,
78
+ arguments: str,
79
+ listed_tool: SourceToolDefinition,
80
+ ) -> DeferredToolCall:
81
+ """Build the source's call under the agent's name, with approval for every write."""
82
+ built = self._source.deferred_tool_call(
83
+ turn,
84
+ tool_call_id=tool_call_id,
85
+ tool_name=tool_name,
86
+ arguments=arguments,
87
+ listed_tool=listed_tool,
88
+ )
89
+ if built.policy.effect is ToolEffect.READ or built.policy.is_approval_required:
90
+ return built
91
+ return dataclasses.replace(
92
+ built, policy=ToolPolicy(built.policy.effect, requires_approval=True)
93
+ )
94
+
95
+ async def execute_deferred_tool_call(self, call: DeferredToolExecution) -> DeferredToolOutcome:
96
+ """Run one call through the source, which reads the operation from its own reference."""
97
+ return await self._source.execute_deferred_tool_call(call)
98
+
99
+ async def poll_tool_task(self, poll: ToolTaskPoll) -> ToolTaskPollOutcome:
100
+ """Poll one of the source's tool tasks."""
101
+ return await self._source.poll_tool_task(poll)
102
+
103
+ async def cancel_tool_task(self, cancellation: ToolTaskCancellation) -> None:
104
+ """Cancel one of the source's abandoned tool tasks, when the source can."""
105
+ cancel = getattr(self._source, "cancel_tool_task", None)
106
+ if callable(cancel):
107
+ await cancel(cancellation)
108
+
109
+ def tool_call_step_options(self, tool_call: AgentToolCall) -> ToolCallStepOptions | None:
110
+ """Return the source's options for one call's tool Step, or ``None`` for the defaults."""
111
+ provide = getattr(self._source, "tool_call_step_options", None)
112
+ options: ToolCallStepOptions | None = provide(tool_call) if callable(provide) else None
113
+ return options
114
+
115
+ def _agent_visible_listing(
116
+ self, tools: Sequence[SourceToolDefinition]
117
+ ) -> list[SourceToolDefinition]:
118
+ # Names the source lists and Deep Agents' names stay taken; renamed tools avoid them all.
119
+ taken = set(self._reserved_tool_names)
120
+ taken.update(tool.name for tool in tools if tool.name not in self._reserved_tool_names)
121
+ visible: list[SourceToolDefinition] = []
122
+ for tool in tools:
123
+ if tool.name not in self._reserved_tool_names:
124
+ visible.append(tool)
125
+ continue
126
+ name = f"{COLLIDING_CONNECTOR_TOOL_PREFIX}{tool.name}"
127
+ while name in taken:
128
+ name = f"{COLLIDING_CONNECTOR_TOOL_PREFIX}{name}"
129
+ taken.add(name)
130
+ visible.append(_renamed(tool, name))
131
+ return visible
132
+
133
+
134
+ def _renamed(tool: SourceToolDefinition, name: str) -> SourceToolDefinition:
135
+ if not dataclasses.is_dataclass(tool) or isinstance(tool, type):
136
+ raise TypeError(f"cannot rename the listed tool {tool.name}: it is not a dataclass")
137
+ renamed: SourceToolDefinition = dataclasses.replace(tool, name=name)
138
+ return renamed