operator-agent 0.2.0__tar.gz → 0.4.0__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.
Files changed (23) hide show
  1. {operator_agent-0.2.0/src/operator_agent.egg-info → operator_agent-0.4.0}/PKG-INFO +1 -1
  2. {operator_agent-0.2.0 → operator_agent-0.4.0}/pyproject.toml +4 -1
  3. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/cli.py +1 -0
  4. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/core.py +23 -0
  5. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/providers/codex.py +3 -0
  6. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/providers/gemini.py +3 -0
  7. operator_agent-0.4.0/src/operator_agent/system_prompt.md +19 -0
  8. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/transports/telegram.py +28 -0
  9. {operator_agent-0.2.0 → operator_agent-0.4.0/src/operator_agent.egg-info}/PKG-INFO +1 -1
  10. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent.egg-info/SOURCES.txt +1 -0
  11. {operator_agent-0.2.0 → operator_agent-0.4.0}/LICENSE +0 -0
  12. {operator_agent-0.2.0 → operator_agent-0.4.0}/README.md +0 -0
  13. {operator_agent-0.2.0 → operator_agent-0.4.0}/setup.cfg +0 -0
  14. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/__init__.py +0 -0
  15. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/config.py +0 -0
  16. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/providers/__init__.py +0 -0
  17. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/providers/claude.py +0 -0
  18. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent/transports/__init__.py +0 -0
  19. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent.egg-info/dependency_links.txt +0 -0
  20. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent.egg-info/entry_points.txt +0 -0
  21. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent.egg-info/requires.txt +0 -0
  22. {operator_agent-0.2.0 → operator_agent-0.4.0}/src/operator_agent.egg-info/top_level.txt +0 -0
  23. {operator_agent-0.2.0 → operator_agent-0.4.0}/tests/test_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: operator-agent
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Personal AI agent that bridges Telegram to CLI agents (Claude, Codex, Gemini) running on your machine.
5
5
  Author-email: Gavin Vickery <gavin@geekforbrains.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "operator-agent"
3
- version = "0.2.0"
3
+ version = "0.4.0"
4
4
  description = "Personal AI agent that bridges Telegram to CLI agents (Claude, Codex, Gemini) running on your machine."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -40,6 +40,9 @@ build-backend = "setuptools.build_meta"
40
40
  [tool.setuptools.packages.find]
41
41
  where = ["src"]
42
42
 
43
+ [tool.setuptools.package-data]
44
+ operator_agent = ["system_prompt.md"]
45
+
43
46
  [tool.ruff]
44
47
  target-version = "py310"
45
48
  line-length = 100
@@ -540,6 +540,7 @@ def serve(
540
540
 
541
541
  runtime = Runtime(config)
542
542
  runtime.init_config_dir()
543
+ runtime.install_system_prompts()
543
544
  runtime.load_state()
544
545
 
545
546
  log = logging.getLogger("operator_agent")
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import contextlib
7
+ import importlib.resources
7
8
  import json
8
9
  import logging
9
10
  import os
@@ -65,6 +66,28 @@ class Runtime:
65
66
  """Ensure config directory exists."""
66
67
  os.makedirs(CONFIG_DIR, exist_ok=True)
67
68
 
69
+ def install_system_prompts(self):
70
+ """Write CLAUDE.md / AGENTS.md / GEMINI.md into the working directory.
71
+
72
+ Each CLI agent reads its own convention file from the cwd to understand
73
+ its role and behaviour. We ship the canonical prompt as package data
74
+ and materialise it on every ``serve`` so it stays current across
75
+ package upgrades.
76
+ """
77
+ source = importlib.resources.files("operator_agent").joinpath("system_prompt.md")
78
+ content = source.read_text(encoding="utf-8")
79
+
80
+ for name in ("CLAUDE.md", "AGENTS.md", "GEMINI.md"):
81
+ target = os.path.join(self.working_dir, name)
82
+ if os.path.exists(target):
83
+ continue
84
+ try:
85
+ with open(target, "w") as f:
86
+ f.write(content)
87
+ log.info("Wrote %s to %s", name, self.working_dir)
88
+ except OSError:
89
+ log.warning("Could not write %s to %s", name, self.working_dir, exc_info=True)
90
+
68
91
  # --- State persistence ---
69
92
 
70
93
  def save_state(self):
@@ -111,5 +111,8 @@ class CodexProvider(BaseProvider):
111
111
 
112
112
  return events
113
113
 
114
+ def stdout_limit(self):
115
+ return 10 * 1024 * 1024
116
+
114
117
  def stderr_to_stdout(self):
115
118
  return True
@@ -49,6 +49,9 @@ def _format_status(event: dict) -> str | None:
49
49
  class GeminiProvider(BaseProvider):
50
50
  name = "gemini"
51
51
 
52
+ def stdout_limit(self):
53
+ return 10 * 1024 * 1024
54
+
52
55
  def build_command(self, prompt, model, session_id=None):
53
56
  cmd = [
54
57
  self.path,
@@ -0,0 +1,19 @@
1
+ # Operator
2
+
3
+ Operator proxies messages from a chat platform (Telegram, Slack, etc.)
4
+ to a CLI agent (Claude, Codex, Gemini) running on the user's machine.
5
+ The user sends a message, Operator invokes your CLI, and your response
6
+ is sent back to the chat. You have full access to the machine you're
7
+ running on — act accordingly.
8
+
9
+ ## Behaviour
10
+
11
+ - **Bias to action.** Attempt the task first; only ask questions when
12
+ you genuinely cannot proceed or when the action is destructive.
13
+ - **Confirm before destructive/irreversible actions only:** deleting
14
+ files or data, security-sensitive changes (credentials, permissions,
15
+ keys), force-pushing, or anything that affects shared/remote state.
16
+ - **Everything else:** just do it. Don't ask for permission to read
17
+ files, run commands, install tools, or explore the system.
18
+ - Get creative with shell commands or install new tools as needed.
19
+ - Control the desktop, keyboard and mouse if you have to. Take screenshots.
@@ -150,6 +150,7 @@ class TelegramTransport:
150
150
  "!clear - Clear current provider session\n"
151
151
  "!clear all - Clear all provider sessions\n"
152
152
  "!restart - Restart the bot\n"
153
+ "!logs - Show last 25 log entries\n"
153
154
  "!help - Show this message"
154
155
  )
155
156
  return
@@ -159,6 +160,10 @@ class TelegramTransport:
159
160
  asyncio.get_event_loop().call_later(1, _restart)
160
161
  return
161
162
 
163
+ if text == "!logs":
164
+ await self._handle_logs(update)
165
+ return
166
+
162
167
  if text.startswith("!"):
163
168
  await update.message.reply_text("Unknown command. Try !help")
164
169
  return
@@ -414,3 +419,26 @@ class TelegramTransport:
414
419
  rt.save_state()
415
420
  msg = "Cleared all providers!" if clear_all else "Cleared current provider!"
416
421
  await update.message.reply_text(f"{msg}\n" + "\n".join(parts))
422
+
423
+ async def _handle_logs(self, update: Update):
424
+ log_path = os.path.join(os.path.expanduser("~/.operator"), "operator.log")
425
+ if not os.path.exists(log_path):
426
+ await update.message.reply_text("No log file found.")
427
+ return
428
+
429
+ try:
430
+ with open(log_path, "rb") as f:
431
+ # Read last ~32KB to find the last 25 lines
432
+ f.seek(0, 2)
433
+ size = f.tell()
434
+ f.seek(max(0, size - 32768))
435
+ tail = f.read().decode(errors="replace")
436
+
437
+ lines = tail.splitlines()[-25:]
438
+ text = "\n".join(lines) if lines else "(empty)"
439
+ # Telegram message limit is 4096 chars
440
+ if len(text) > 4000:
441
+ text = text[-4000:]
442
+ await update.message.reply_text(text)
443
+ except Exception as exc:
444
+ await update.message.reply_text(f"Error reading logs: {exc}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: operator-agent
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Personal AI agent that bridges Telegram to CLI agents (Claude, Codex, Gemini) running on your machine.
5
5
  Author-email: Gavin Vickery <gavin@geekforbrains.com>
6
6
  License-Expression: MIT
@@ -5,6 +5,7 @@ src/operator_agent/__init__.py
5
5
  src/operator_agent/cli.py
6
6
  src/operator_agent/config.py
7
7
  src/operator_agent/core.py
8
+ src/operator_agent/system_prompt.md
8
9
  src/operator_agent.egg-info/PKG-INFO
9
10
  src/operator_agent.egg-info/SOURCES.txt
10
11
  src/operator_agent.egg-info/dependency_links.txt
File without changes
File without changes
File without changes