somnia 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.
- openagent/__init__.py +5 -0
- openagent/__main__.py +10 -0
- openagent/cli/__init__.py +1 -0
- openagent/cli/commands.py +202 -0
- openagent/cli/main.py +103 -0
- openagent/cli/prompting.py +474 -0
- openagent/cli/repl.py +996 -0
- openagent/collaboration/__init__.py +1 -0
- openagent/collaboration/bus.py +40 -0
- openagent/collaboration/protocols.py +67 -0
- openagent/config/__init__.py +1 -0
- openagent/config/models.py +97 -0
- openagent/config/settings.py +417 -0
- openagent/mcp/__init__.py +1 -0
- openagent/mcp/client.py +67 -0
- openagent/mcp/registry.py +98 -0
- openagent/mcp/transport_http.py +138 -0
- openagent/mcp/transport_stdio.py +120 -0
- openagent/providers/__init__.py +1 -0
- openagent/providers/anthropic_provider.py +120 -0
- openagent/providers/base.py +53 -0
- openagent/providers/openai_provider.py +295 -0
- openagent/runtime/__init__.py +1 -0
- openagent/runtime/agent.py +826 -0
- openagent/runtime/compact.py +265 -0
- openagent/runtime/events.py +12 -0
- openagent/runtime/execution_mode.py +191 -0
- openagent/runtime/interrupts.py +5 -0
- openagent/runtime/messages.py +281 -0
- openagent/runtime/permissions.py +179 -0
- openagent/runtime/session.py +81 -0
- openagent/runtime/subagent_runner.py +159 -0
- openagent/runtime/system_prompt.py +99 -0
- openagent/runtime/teammate.py +615 -0
- openagent/runtime/tool_events.py +402 -0
- openagent/skills/__init__.py +1 -0
- openagent/skills/loader.py +114 -0
- openagent/storage/__init__.py +1 -0
- openagent/storage/common.py +58 -0
- openagent/storage/inbox.py +27 -0
- openagent/storage/jobs.py +39 -0
- openagent/storage/sessions.py +74 -0
- openagent/storage/tasks.py +172 -0
- openagent/storage/team.py +40 -0
- openagent/storage/tool_logs.py +62 -0
- openagent/storage/transcripts.py +34 -0
- openagent/tools/__init__.py +1 -0
- openagent/tools/background.py +113 -0
- openagent/tools/filesystem.py +428 -0
- openagent/tools/mcp.py +5 -0
- openagent/tools/process.py +75 -0
- openagent/tools/registry.py +48 -0
- openagent/tools/shell.py +118 -0
- openagent/tools/subagent.py +29 -0
- openagent/tools/tasks.py +129 -0
- openagent/tools/team.py +160 -0
- openagent/tools/todo.py +124 -0
- somnia-0.1.0.dist-info/METADATA +580 -0
- somnia-0.1.0.dist-info/RECORD +63 -0
- somnia-0.1.0.dist-info/WHEEL +5 -0
- somnia-0.1.0.dist-info/entry_points.txt +2 -0
- somnia-0.1.0.dist-info/licenses/LICENSE +21 -0
- somnia-0.1.0.dist-info/top_level.txt +1 -0
openagent/__init__.py
ADDED
openagent/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI entrypoints for OpenAgent."""
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from openagent.runtime.agent import OpenAgentRuntime
|
|
8
|
+
from openagent.runtime.messages import MarkdownStreamRenderer, render_markdown_text, render_message_content, render_text_content
|
|
9
|
+
from openagent.cli.prompting import choose_session_interactively, format_session_timestamp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ASSISTANT_BULLET = "\u25cf"
|
|
13
|
+
USER_BULLET = "\u276f"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _prefix_first_line(text: str, prefix: str) -> str:
|
|
17
|
+
if not text:
|
|
18
|
+
return prefix.rstrip()
|
|
19
|
+
lines = text.splitlines()
|
|
20
|
+
if not lines:
|
|
21
|
+
return prefix.rstrip()
|
|
22
|
+
lines[0] = f"{prefix}{lines[0]}"
|
|
23
|
+
return "\n".join(lines)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _assistant_prefix(*, ansi: bool) -> str:
|
|
27
|
+
if ansi:
|
|
28
|
+
return "\x1b[37m\u25cf\x1b[0m "
|
|
29
|
+
return f"{ASSISTANT_BULLET} "
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _user_prefix(*, ansi: bool) -> str:
|
|
33
|
+
if ansi:
|
|
34
|
+
return "\x1b[38;5;45m\u276f\x1b[0m "
|
|
35
|
+
return f"{USER_BULLET} "
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def print_user_message(text: str, *, ansi: bool | None = None) -> None:
|
|
39
|
+
ansi_enabled = sys.stdout.isatty() if ansi is None else ansi
|
|
40
|
+
lines = text.splitlines() or [""]
|
|
41
|
+
first = f"{_user_prefix(ansi=ansi_enabled)}{lines[0]}"
|
|
42
|
+
remainder = [f" {line}" if line else " " for line in lines[1:]]
|
|
43
|
+
print()
|
|
44
|
+
print(first)
|
|
45
|
+
for line in remainder:
|
|
46
|
+
print(line)
|
|
47
|
+
print()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ConsoleStreamer:
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
start_on_new_line: bool = False,
|
|
54
|
+
line_buffered: bool = False,
|
|
55
|
+
on_first_output=None,
|
|
56
|
+
) -> None:
|
|
57
|
+
self.has_output = False
|
|
58
|
+
self.start_on_new_line = start_on_new_line
|
|
59
|
+
self.line_buffered = line_buffered
|
|
60
|
+
self.on_first_output = on_first_output
|
|
61
|
+
self._renderer: MarkdownStreamRenderer | None = None
|
|
62
|
+
self._started_printing = False
|
|
63
|
+
|
|
64
|
+
def __call__(self, text: str) -> None:
|
|
65
|
+
if not text:
|
|
66
|
+
return
|
|
67
|
+
if self._renderer is None:
|
|
68
|
+
self._renderer = MarkdownStreamRenderer(ansi=sys.stdout.isatty())
|
|
69
|
+
if not self.has_output and self.on_first_output is not None:
|
|
70
|
+
self.on_first_output()
|
|
71
|
+
self._print_rendered(self._renderer.feed(text))
|
|
72
|
+
self.has_output = True
|
|
73
|
+
|
|
74
|
+
def finish(self) -> None:
|
|
75
|
+
if not self.has_output:
|
|
76
|
+
return
|
|
77
|
+
if self._renderer is None:
|
|
78
|
+
return
|
|
79
|
+
self._print_rendered(self._renderer.finish())
|
|
80
|
+
|
|
81
|
+
def _print_rendered(self, rendered: str) -> None:
|
|
82
|
+
if not rendered:
|
|
83
|
+
return
|
|
84
|
+
if self.start_on_new_line and not self._started_printing:
|
|
85
|
+
print()
|
|
86
|
+
if not self._started_printing:
|
|
87
|
+
rendered = _prefix_first_line(rendered, _assistant_prefix(ansi=sys.stdout.isatty()))
|
|
88
|
+
print(rendered, end="" if rendered.endswith("\n") else "\n", flush=True)
|
|
89
|
+
self._started_printing = True
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(slots=True)
|
|
93
|
+
class SessionChoice:
|
|
94
|
+
session_id: str
|
|
95
|
+
label: str
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _has_visible_exchange(session) -> bool:
|
|
99
|
+
has_user = False
|
|
100
|
+
has_assistant = False
|
|
101
|
+
for message in session.messages:
|
|
102
|
+
role = message.get("role")
|
|
103
|
+
content = message.get("content")
|
|
104
|
+
if role == "user" and isinstance(content, str):
|
|
105
|
+
if content.startswith("<background-results>") or content.startswith("<inbox>"):
|
|
106
|
+
continue
|
|
107
|
+
if content.strip():
|
|
108
|
+
has_user = True
|
|
109
|
+
elif role == "assistant":
|
|
110
|
+
text = render_text_content(content).strip()
|
|
111
|
+
if text:
|
|
112
|
+
has_assistant = True
|
|
113
|
+
if has_user and has_assistant:
|
|
114
|
+
return True
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _session_preview(session) -> str:
|
|
119
|
+
for message in reversed(session.messages):
|
|
120
|
+
role = message.get("role")
|
|
121
|
+
content = message.get("content")
|
|
122
|
+
if role == "assistant":
|
|
123
|
+
text = render_message_content(content, ansi=False).strip()
|
|
124
|
+
elif role == "user" and isinstance(content, str):
|
|
125
|
+
if content.startswith("<background-results>") or content.startswith("<inbox>"):
|
|
126
|
+
continue
|
|
127
|
+
text = content.strip()
|
|
128
|
+
else:
|
|
129
|
+
continue
|
|
130
|
+
if text:
|
|
131
|
+
return " ".join(text.split())[:80]
|
|
132
|
+
return "[no visible messages]"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _build_session_choices(runtime: OpenAgentRuntime) -> list[SessionChoice]:
|
|
136
|
+
choices: list[SessionChoice] = []
|
|
137
|
+
for session in runtime.list_sessions():
|
|
138
|
+
if not _has_visible_exchange(session):
|
|
139
|
+
continue
|
|
140
|
+
stamp = format_session_timestamp(session.updated_at or session.created_at)
|
|
141
|
+
preview = _session_preview(session)
|
|
142
|
+
label = f"{session.id} | {stamp} | {preview}"
|
|
143
|
+
choices.append(SessionChoice(session_id=session.id, label=label))
|
|
144
|
+
return choices
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _select_session(runtime: OpenAgentRuntime):
|
|
148
|
+
choices = _build_session_choices(runtime)
|
|
149
|
+
if not choices:
|
|
150
|
+
print("No saved sessions. Starting a new chat.")
|
|
151
|
+
return runtime.create_session(), False
|
|
152
|
+
|
|
153
|
+
selected_id = choose_session_interactively([(item.session_id, item.label) for item in choices])
|
|
154
|
+
if not selected_id:
|
|
155
|
+
print("Session selection cancelled. Starting a new chat.")
|
|
156
|
+
return runtime.create_session(), False
|
|
157
|
+
return runtime.load_session(selected_id), True
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def cmd_chat(runtime: OpenAgentRuntime, resume: bool = False) -> int:
|
|
161
|
+
from openagent.cli.repl import run_repl
|
|
162
|
+
|
|
163
|
+
session, resumed = _select_session(runtime) if resume else (runtime.create_session(), False)
|
|
164
|
+
return run_repl(runtime, session, resumed=resumed)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def cmd_run(runtime: OpenAgentRuntime, prompt: str) -> int:
|
|
168
|
+
session = runtime.create_session()
|
|
169
|
+
streamer = ConsoleStreamer()
|
|
170
|
+
result = runtime.run_turn(session, prompt, text_callback=streamer)
|
|
171
|
+
if streamer.has_output:
|
|
172
|
+
streamer.finish()
|
|
173
|
+
elif result:
|
|
174
|
+
print(_prefix_first_line(render_markdown_text(result, ansi=sys.stdout.isatty()), _assistant_prefix(ansi=sys.stdout.isatty())))
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def cmd_tasks_list(runtime: OpenAgentRuntime) -> int:
|
|
179
|
+
tasks = runtime.task_store.list_all()
|
|
180
|
+
if not tasks:
|
|
181
|
+
print("No tasks.")
|
|
182
|
+
else:
|
|
183
|
+
for task in tasks:
|
|
184
|
+
print(json.dumps(task, ensure_ascii=False, indent=2))
|
|
185
|
+
return 0
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def cmd_tasks_get(runtime: OpenAgentRuntime, task_id: int) -> int:
|
|
189
|
+
print(json.dumps(runtime.task_store.get(task_id), ensure_ascii=False, indent=2))
|
|
190
|
+
return 0
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def cmd_compact(runtime: OpenAgentRuntime) -> int:
|
|
194
|
+
session = runtime.latest_session()
|
|
195
|
+
runtime.compact_session(session)
|
|
196
|
+
print(f"Compacted session {session.id}")
|
|
197
|
+
return 0
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def cmd_doctor(runtime: OpenAgentRuntime) -> int:
|
|
201
|
+
print(runtime.doctor())
|
|
202
|
+
return 0
|
openagent/cli/main.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from openagent.config.settings import load_settings
|
|
6
|
+
from openagent.runtime.agent import OpenAgentRuntime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _add_provider_overrides(parser: argparse.ArgumentParser) -> None:
|
|
10
|
+
parser.add_argument(
|
|
11
|
+
"--provider",
|
|
12
|
+
default=argparse.SUPPRESS,
|
|
13
|
+
help="Override the configured provider for this invocation.",
|
|
14
|
+
)
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"--model",
|
|
17
|
+
default=argparse.SUPPRESS,
|
|
18
|
+
help="Override the configured model for this invocation.",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
23
|
+
parser = argparse.ArgumentParser(prog="openagent")
|
|
24
|
+
parser.add_argument("--workspace", default=".", help="Workspace root for the agent.")
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"-r",
|
|
27
|
+
"-resume",
|
|
28
|
+
"--resume",
|
|
29
|
+
dest="resume",
|
|
30
|
+
action="store_true",
|
|
31
|
+
help="Open the interactive session picker and resume a saved chat.",
|
|
32
|
+
)
|
|
33
|
+
_add_provider_overrides(parser)
|
|
34
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
35
|
+
|
|
36
|
+
chat_parser = subparsers.add_parser("chat", help="Start interactive chat mode.")
|
|
37
|
+
chat_parser.add_argument(
|
|
38
|
+
"-r",
|
|
39
|
+
"-resume",
|
|
40
|
+
"--resume",
|
|
41
|
+
dest="resume",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help="Open the interactive session picker and resume a saved chat.",
|
|
44
|
+
)
|
|
45
|
+
_add_provider_overrides(chat_parser)
|
|
46
|
+
|
|
47
|
+
run_parser = subparsers.add_parser("run", help="Run a single prompt.")
|
|
48
|
+
run_parser.add_argument("prompt", help="Prompt to execute.")
|
|
49
|
+
_add_provider_overrides(run_parser)
|
|
50
|
+
|
|
51
|
+
tasks_parser = subparsers.add_parser("tasks", help="Inspect persistent tasks.")
|
|
52
|
+
_add_provider_overrides(tasks_parser)
|
|
53
|
+
tasks_subparsers = tasks_parser.add_subparsers(dest="tasks_command", required=True)
|
|
54
|
+
tasks_subparsers.add_parser("list", help="List tasks.")
|
|
55
|
+
get_parser = tasks_subparsers.add_parser("get", help="Get a task by ID.")
|
|
56
|
+
get_parser.add_argument("task_id", type=int)
|
|
57
|
+
|
|
58
|
+
compact_parser = subparsers.add_parser("compact", help="Compact the latest session.")
|
|
59
|
+
_add_provider_overrides(compact_parser)
|
|
60
|
+
doctor_parser = subparsers.add_parser("doctor", help="Validate runtime configuration.")
|
|
61
|
+
_add_provider_overrides(doctor_parser)
|
|
62
|
+
return parser
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def main(argv: list[str] | None = None) -> int:
|
|
66
|
+
parser = build_parser()
|
|
67
|
+
args = parser.parse_args(argv)
|
|
68
|
+
settings = load_settings(
|
|
69
|
+
args.workspace,
|
|
70
|
+
provider_override=getattr(args, "provider", None),
|
|
71
|
+
model_override=getattr(args, "model", None),
|
|
72
|
+
)
|
|
73
|
+
runtime = OpenAgentRuntime(settings)
|
|
74
|
+
try:
|
|
75
|
+
from openagent.cli.commands import (
|
|
76
|
+
cmd_chat,
|
|
77
|
+
cmd_compact,
|
|
78
|
+
cmd_doctor,
|
|
79
|
+
cmd_run,
|
|
80
|
+
cmd_tasks_get,
|
|
81
|
+
cmd_tasks_list,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if args.command in {None, "chat"}:
|
|
85
|
+
return cmd_chat(runtime, resume=getattr(args, "resume", False))
|
|
86
|
+
if args.command == "run":
|
|
87
|
+
return cmd_run(runtime, args.prompt)
|
|
88
|
+
if args.command == "tasks" and args.tasks_command == "list":
|
|
89
|
+
return cmd_tasks_list(runtime)
|
|
90
|
+
if args.command == "tasks" and args.tasks_command == "get":
|
|
91
|
+
return cmd_tasks_get(runtime, args.task_id)
|
|
92
|
+
if args.command == "compact":
|
|
93
|
+
return cmd_compact(runtime)
|
|
94
|
+
if args.command == "doctor":
|
|
95
|
+
return cmd_doctor(runtime)
|
|
96
|
+
parser.error("Unsupported command")
|
|
97
|
+
finally:
|
|
98
|
+
runtime.close()
|
|
99
|
+
return 1
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
raise SystemExit(main())
|