opencode-agent-hub 1.4.1__tar.gz → 1.4.2__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.
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/.gitignore +2 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/PKG-INFO +5 -4
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/pyproject.toml +5 -4
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/src/opencode_agent_hub/__init__.py +1 -1
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/config.py +330 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/coordinator.py +745 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/daemon.py +379 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/garbage_collector.py +269 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/hub_server.py +214 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/messaging.py +628 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/metrics.py +164 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/models.py +41 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/persistence.py +316 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/preflight.py +123 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/rate_limiting.py +63 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/service.py +159 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/sessions.py +742 -0
- opencode_agent_hub-1.4.2/src/opencode_agent_hub/utils.py +71 -0
- opencode_agent_hub-1.4.2/tests/test_agent_id_generation.py +49 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/tests/test_config.py +41 -39
- opencode_agent_hub-1.4.2/tests/test_coordinator.py +705 -0
- opencode_agent_hub-1.4.2/tests/test_coordinator_cost.py +72 -0
- opencode_agent_hub-1.4.2/tests/test_coordinator_model_logging.py +105 -0
- opencode_agent_hub-1.4.2/tests/test_daemon_integration.py +309 -0
- opencode_agent_hub-1.4.2/tests/test_orientation_retry.py +298 -0
- opencode_agent_hub-1.4.2/tests/test_rate_limiting.py +83 -0
- opencode_agent_hub-1.4.2/tests/test_session_agents.py +427 -0
- opencode_agent_hub-1.4.2/tests/test_sqlite_schema.py +84 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/tests/test_watch.py +1 -1
- opencode_agent_hub-1.4.1/src/opencode_agent_hub/daemon.py +0 -3373
- opencode_agent_hub-1.4.1/tests/test_agent_detection.py +0 -426
- opencode_agent_hub-1.4.1/tests/test_coordinator.py +0 -1282
- opencode_agent_hub-1.4.1/tests/test_coordinator_cost.py +0 -430
- opencode_agent_hub-1.4.1/tests/test_integration.py +0 -338
- opencode_agent_hub-1.4.1/tests/test_message_injection.py +0 -285
- opencode_agent_hub-1.4.1/tests/test_orientation_retry.py +0 -466
- opencode_agent_hub-1.4.1/tests/test_rate_limiting.py +0 -152
- opencode_agent_hub-1.4.1/tests/test_session_agents.py +0 -342
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/LICENSE +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/README.md +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/coordinator/AGENTS.md +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/coordinator/opencode.json +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/launchd/com.xnoto.agent-hub-daemon.plist +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/aur/PKGBUILD +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/changelog +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/control +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/copyright +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/docs +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/install +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/postinst +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/rules +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/debian/source/format +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/packaging/rpm/opencode-agent-hub.spec +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/contrib/systemd/agent-hub-daemon.service +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/src/opencode_agent_hub/py.typed +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/src/opencode_agent_hub/watch.py +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/tests/__init__.py +0 -0
- {opencode_agent_hub-1.4.1 → opencode_agent_hub-1.4.2}/tests/test_placeholder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencode-agent-hub
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: Multi-agent coordination daemon and tools for OpenCode
|
|
5
5
|
Project-URL: Homepage, https://github.com/xnoto/opencode-agent-hub
|
|
6
6
|
Project-URL: Repository, https://github.com/xnoto/opencode-agent-hub
|
|
@@ -22,15 +22,16 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Classifier: Topic :: System :: Distributed Computing
|
|
24
24
|
Requires-Python: >=3.11
|
|
25
|
-
Requires-Dist: requests>=2.
|
|
25
|
+
Requires-Dist: requests>=2.31.0
|
|
26
26
|
Requires-Dist: watchdog>=3.0.0
|
|
27
27
|
Provides-Extra: dev
|
|
28
28
|
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
29
29
|
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
30
30
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
31
31
|
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
32
|
-
Requires-Dist: ruff>=0.
|
|
33
|
-
Requires-Dist: types-requests>=2.
|
|
32
|
+
Requires-Dist: ruff>=0.15.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: types-requests>=2.31.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: vulture>=2.14; extra == 'dev'
|
|
34
35
|
Description-Content-Type: text/markdown
|
|
35
36
|
|
|
36
37
|
# opencode-agent-hub
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opencode-agent-hub"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.2"
|
|
8
8
|
description = "Multi-agent coordination daemon and tools for OpenCode"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -35,7 +35,7 @@ classifiers = [
|
|
|
35
35
|
"Topic :: System :: Distributed Computing",
|
|
36
36
|
]
|
|
37
37
|
dependencies = [
|
|
38
|
-
"requests>=2.
|
|
38
|
+
"requests>=2.31.0",
|
|
39
39
|
"watchdog>=3.0.0",
|
|
40
40
|
]
|
|
41
41
|
|
|
@@ -44,9 +44,10 @@ dev = [
|
|
|
44
44
|
"pre-commit>=3.0.0",
|
|
45
45
|
"pytest>=7.0.0",
|
|
46
46
|
"pytest-cov>=4.0.0",
|
|
47
|
-
"ruff>=0.
|
|
47
|
+
"ruff>=0.15.0",
|
|
48
|
+
"vulture>=2.14",
|
|
48
49
|
"mypy>=1.0.0",
|
|
49
|
-
"types-requests>=2.
|
|
50
|
+
"types-requests>=2.31.0",
|
|
50
51
|
]
|
|
51
52
|
|
|
52
53
|
[project.scripts]
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"""Configuration management for the agent hub daemon.
|
|
2
|
+
|
|
3
|
+
This module handles all configuration loading with the precedence:
|
|
4
|
+
environment variables > config file > defaults
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import threading
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
# For accessing package data files reliably across install methods (pip, deb, rpm, aur)
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, cast
|
|
16
|
+
|
|
17
|
+
# =============================================================================
|
|
18
|
+
# Static Paths (not configurable)
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
AGENT_HUB_DIR = Path.home() / ".agent-hub"
|
|
22
|
+
MESSAGES_DIR = AGENT_HUB_DIR / "messages"
|
|
23
|
+
ARCHIVE_DIR = MESSAGES_DIR / "archive"
|
|
24
|
+
THREADS_DIR = AGENT_HUB_DIR / "threads"
|
|
25
|
+
AGENTS_DIR = AGENT_HUB_DIR / "agents"
|
|
26
|
+
ORIENTED_SESSIONS_FILE = AGENT_HUB_DIR / "oriented_sessions.json"
|
|
27
|
+
SESSION_AGENTS_FILE = AGENT_HUB_DIR / "session_agents.json"
|
|
28
|
+
OPENCODE_DATA_DIR = Path.home() / ".local/share/opencode"
|
|
29
|
+
OPENCODE_DB_PATH = OPENCODE_DATA_DIR / "opencode.db"
|
|
30
|
+
OPENCODE_STORAGE_DIR = OPENCODE_DATA_DIR / "storage" # Watch all project subdirs, not just global
|
|
31
|
+
CONFIG_DIR = Path.home() / ".config" / "agent-hub-daemon"
|
|
32
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
33
|
+
|
|
34
|
+
# Coordinator directory
|
|
35
|
+
COORDINATOR_DIR = AGENT_HUB_DIR / "coordinator"
|
|
36
|
+
|
|
37
|
+
# Metrics and logging
|
|
38
|
+
METRICS_FILE = AGENT_HUB_DIR / "metrics.prom"
|
|
39
|
+
HUB_SERVER_PID_FILE = AGENT_HUB_DIR / "hub-server.pid"
|
|
40
|
+
DAEMON_LOG_DIR = Path.home() / ".local/share/agent-hub-daemon"
|
|
41
|
+
HUB_STDERR_LOG_FILE = DAEMON_LOG_DIR / "hub-stderr.log"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# =============================================================================
|
|
45
|
+
# Configuration Loading
|
|
46
|
+
# =============================================================================
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _load_config_file() -> dict[str, Any]:
|
|
50
|
+
"""Load configuration from JSON file if it exists."""
|
|
51
|
+
if not CONFIG_FILE.exists():
|
|
52
|
+
return {}
|
|
53
|
+
try:
|
|
54
|
+
return cast(dict[str, Any], json.loads(CONFIG_FILE.read_text()))
|
|
55
|
+
except (json.JSONDecodeError, OSError):
|
|
56
|
+
# Log warning later after logging is set up
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _get_config_value(
|
|
61
|
+
env_var: str,
|
|
62
|
+
config_path: list[str],
|
|
63
|
+
default: str | int | bool | float | None,
|
|
64
|
+
config: dict[str, Any],
|
|
65
|
+
type_: type = str,
|
|
66
|
+
) -> Any:
|
|
67
|
+
"""Get config value with precedence: env var > config file > default.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
env_var: Environment variable name
|
|
71
|
+
config_path: Path in config dict (e.g., ["rate_limit", "enabled"])
|
|
72
|
+
default: Default value
|
|
73
|
+
config: Loaded config dict
|
|
74
|
+
type_: Expected type (str, int, or bool)
|
|
75
|
+
"""
|
|
76
|
+
# Check environment variable first
|
|
77
|
+
env_value = os.environ.get(env_var)
|
|
78
|
+
if env_value is not None:
|
|
79
|
+
if type_ is bool:
|
|
80
|
+
return env_value.lower() in ("1", "true", "yes")
|
|
81
|
+
elif type_ is int:
|
|
82
|
+
return int(env_value)
|
|
83
|
+
return env_value
|
|
84
|
+
|
|
85
|
+
# Traverse config dict path
|
|
86
|
+
value = config
|
|
87
|
+
for key in config_path:
|
|
88
|
+
if isinstance(value, dict) and key in value:
|
|
89
|
+
value = value[key]
|
|
90
|
+
else:
|
|
91
|
+
return default
|
|
92
|
+
|
|
93
|
+
# Validate type
|
|
94
|
+
if type_ is bool and isinstance(value, bool):
|
|
95
|
+
return value
|
|
96
|
+
if type_ is int and isinstance(value, int):
|
|
97
|
+
return value
|
|
98
|
+
if type_ is str and isinstance(value, str):
|
|
99
|
+
return value
|
|
100
|
+
|
|
101
|
+
return default
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Load config file once at module load
|
|
105
|
+
_CONFIG = _load_config_file()
|
|
106
|
+
|
|
107
|
+
# =============================================================================
|
|
108
|
+
# Hub Server Configuration
|
|
109
|
+
# =============================================================================
|
|
110
|
+
|
|
111
|
+
OPENCODE_PORT = _get_config_value("OPENCODE_PORT", ["hub", "port"], 4096, _CONFIG, int)
|
|
112
|
+
OPENCODE_URL = f"http://127.0.0.1:{OPENCODE_PORT}"
|
|
113
|
+
|
|
114
|
+
# =============================================================================
|
|
115
|
+
# Coordinator Configuration
|
|
116
|
+
# =============================================================================
|
|
117
|
+
|
|
118
|
+
COORDINATOR_ENABLED = _get_config_value(
|
|
119
|
+
"AGENT_HUB_COORDINATOR", ["coordinator", "enabled"], True, _CONFIG, bool
|
|
120
|
+
)
|
|
121
|
+
COORDINATOR_AGENTS_MD = _get_config_value(
|
|
122
|
+
"AGENT_HUB_COORDINATOR_AGENTS_MD",
|
|
123
|
+
["coordinator", "agents_md_path"],
|
|
124
|
+
None,
|
|
125
|
+
_CONFIG,
|
|
126
|
+
str,
|
|
127
|
+
)
|
|
128
|
+
COORDINATOR_PRESERVE_LOCAL_AGENTS_MD = _get_config_value(
|
|
129
|
+
"AGENT_HUB_COORDINATOR_PRESERVE_LOCAL_AGENTS_MD",
|
|
130
|
+
["coordinator", "preserve_local_agents_md"],
|
|
131
|
+
False,
|
|
132
|
+
_CONFIG,
|
|
133
|
+
bool,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Convert COORDINATOR_AGENTS_MD to Path if set
|
|
137
|
+
if COORDINATOR_AGENTS_MD:
|
|
138
|
+
COORDINATOR_AGENTS_MD = Path(COORDINATOR_AGENTS_MD)
|
|
139
|
+
|
|
140
|
+
# Coordinator settings
|
|
141
|
+
COORDINATOR_READY_TIMEOUT_SECONDS = _get_config_value(
|
|
142
|
+
"AGENT_HUB_COORDINATOR_READY_TIMEOUT",
|
|
143
|
+
["coordinator", "ready_timeout_seconds"],
|
|
144
|
+
60,
|
|
145
|
+
_CONFIG,
|
|
146
|
+
int,
|
|
147
|
+
)
|
|
148
|
+
COORDINATOR_BOOTSTRAP_REQUIRED = _get_config_value(
|
|
149
|
+
"AGENT_HUB_COORDINATOR_BOOTSTRAP_REQUIRED",
|
|
150
|
+
["coordinator", "bootstrap_required"],
|
|
151
|
+
False, # Allow daemon to run without coordinator READY for headless operation
|
|
152
|
+
_CONFIG,
|
|
153
|
+
bool,
|
|
154
|
+
)
|
|
155
|
+
COORDINATOR_STRICT_READY = _get_config_value(
|
|
156
|
+
"AGENT_HUB_COORDINATOR_STRICT_READY",
|
|
157
|
+
["coordinator", "strict_ready"],
|
|
158
|
+
False, # Allow any activity, not just exact "READY" text
|
|
159
|
+
_CONFIG,
|
|
160
|
+
bool,
|
|
161
|
+
)
|
|
162
|
+
COORDINATOR_BOOTSTRAP_PROMPT = _get_config_value(
|
|
163
|
+
"AGENT_HUB_COORDINATOR_BOOTSTRAP_PROMPT",
|
|
164
|
+
["coordinator", "bootstrap_prompt"],
|
|
165
|
+
"READY",
|
|
166
|
+
_CONFIG,
|
|
167
|
+
str,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Coordinator pricing (USD per token)
|
|
171
|
+
PRICING_INPUT = _get_config_value(
|
|
172
|
+
"AGENT_HUB_PRICING_INPUT", ["pricing", "input_per_1m"], 3.00, _CONFIG, float
|
|
173
|
+
)
|
|
174
|
+
PRICING_OUTPUT = _get_config_value(
|
|
175
|
+
"AGENT_HUB_PRICING_OUTPUT", ["pricing", "output_per_1m"], 15.00, _CONFIG, float
|
|
176
|
+
)
|
|
177
|
+
PRICING_CACHE_READ = _get_config_value(
|
|
178
|
+
"AGENT_HUB_PRICING_CACHE_READ", ["pricing", "cache_read_per_1m"], 0.30, _CONFIG, float
|
|
179
|
+
)
|
|
180
|
+
PRICING_CACHE_WRITE = _get_config_value(
|
|
181
|
+
"AGENT_HUB_PRICING_CACHE_WRITE", ["pricing", "cache_write_per_1m"], 3.75, _CONFIG, float
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Convert per-1M rates to per-token
|
|
185
|
+
PRICING_INPUT = PRICING_INPUT / 1_000_000
|
|
186
|
+
PRICING_OUTPUT = PRICING_OUTPUT / 1_000_000
|
|
187
|
+
PRICING_CACHE_READ = PRICING_CACHE_READ / 1_000_000
|
|
188
|
+
PRICING_CACHE_WRITE = PRICING_CACHE_WRITE / 1_000_000
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# =============================================================================
|
|
192
|
+
# Rate Limiting Configuration
|
|
193
|
+
# =============================================================================
|
|
194
|
+
|
|
195
|
+
RATE_LIMIT_ENABLED = _get_config_value(
|
|
196
|
+
"AGENT_HUB_RATE_LIMIT_ENABLED", ["rate_limit", "enabled"], False, _CONFIG, bool
|
|
197
|
+
)
|
|
198
|
+
RATE_LIMIT_MAX_MESSAGES = _get_config_value(
|
|
199
|
+
"AGENT_HUB_RATE_LIMIT_MAX_MESSAGES",
|
|
200
|
+
["rate_limit", "max_messages_per_window"],
|
|
201
|
+
100,
|
|
202
|
+
_CONFIG,
|
|
203
|
+
int,
|
|
204
|
+
)
|
|
205
|
+
RATE_LIMIT_WINDOW_SECONDS = _get_config_value(
|
|
206
|
+
"AGENT_HUB_RATE_LIMIT_WINDOW_SECONDS", ["rate_limit", "window_seconds"], 3600, _CONFIG, int
|
|
207
|
+
)
|
|
208
|
+
RATE_LIMIT_COOLDOWN_SECONDS = _get_config_value(
|
|
209
|
+
"AGENT_HUB_RATE_LIMIT_COOLDOWN_SECONDS",
|
|
210
|
+
["rate_limit", "cooldown_seconds"],
|
|
211
|
+
5,
|
|
212
|
+
_CONFIG,
|
|
213
|
+
int,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# =============================================================================
|
|
217
|
+
# Agent & Session Configuration
|
|
218
|
+
# =============================================================================
|
|
219
|
+
|
|
220
|
+
AGENT_STALE_SECONDS = _get_config_value(
|
|
221
|
+
"AGENT_HUB_AGENT_STALE_SECONDS", ["agent", "stale_seconds"], 600, _CONFIG, int
|
|
222
|
+
)
|
|
223
|
+
MESSAGE_TTL_SECONDS = _get_config_value(
|
|
224
|
+
"AGENT_HUB_MESSAGE_TTL_SECONDS", ["message", "ttl_seconds"], 3600, _CONFIG, int
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# =============================================================================
|
|
228
|
+
# Polling Intervals
|
|
229
|
+
# =============================================================================
|
|
230
|
+
|
|
231
|
+
SESSION_POLL_SECONDS = _get_config_value(
|
|
232
|
+
"AGENT_HUB_SESSION_POLL_SECONDS", ["poll", "session_seconds"], 5, _CONFIG, int
|
|
233
|
+
)
|
|
234
|
+
GC_INTERVAL_SECONDS = _get_config_value(
|
|
235
|
+
"AGENT_HUB_GC_INTERVAL_SECONDS", ["poll", "gc_seconds"], 60, _CONFIG, int
|
|
236
|
+
)
|
|
237
|
+
METRICS_INTERVAL = _get_config_value(
|
|
238
|
+
"AGENT_HUB_METRICS_INTERVAL", ["poll", "metrics_seconds"], 60, _CONFIG, int
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# =============================================================================
|
|
242
|
+
# Injection Configuration
|
|
243
|
+
# =============================================================================
|
|
244
|
+
|
|
245
|
+
INJECTION_TIMEOUT = _get_config_value(
|
|
246
|
+
"AGENT_HUB_INJECTION_TIMEOUT", ["injection", "timeout_seconds"], 30, _CONFIG, int
|
|
247
|
+
)
|
|
248
|
+
INJECTION_RETRIES = _get_config_value(
|
|
249
|
+
"AGENT_HUB_INJECTION_RETRIES", ["injection", "retries"], 3, _CONFIG, int
|
|
250
|
+
)
|
|
251
|
+
INJECTION_WORKERS = _get_config_value(
|
|
252
|
+
"AGENT_HUB_INJECTION_WORKERS", ["injection", "workers"], 3, _CONFIG, int
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# =============================================================================
|
|
256
|
+
# Caching Configuration
|
|
257
|
+
# =============================================================================
|
|
258
|
+
|
|
259
|
+
SESSION_CACHE_TTL = _get_config_value(
|
|
260
|
+
"AGENT_HUB_SESSION_CACHE_TTL", ["cache", "session_ttl_seconds"], 5, _CONFIG, int
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# =============================================================================
|
|
264
|
+
# Orientation Configuration
|
|
265
|
+
# =============================================================================
|
|
266
|
+
|
|
267
|
+
ORIENTATION_RETRY_DELAY = _get_config_value(
|
|
268
|
+
"AGENT_HUB_ORIENTATION_RETRY_DELAY", ["orientation", "retry_delay_seconds"], 30, _CONFIG, int
|
|
269
|
+
)
|
|
270
|
+
ORIENTATION_RETRY_MAX = _get_config_value(
|
|
271
|
+
"AGENT_HUB_ORIENTATION_RETRY_MAX", ["orientation", "retry_max"], 5, _CONFIG, int
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# =============================================================================
|
|
275
|
+
# Logging Configuration
|
|
276
|
+
# =============================================================================
|
|
277
|
+
|
|
278
|
+
LOG_LEVEL = _get_config_value(
|
|
279
|
+
"AGENT_HUB_DAEMON_LOG_LEVEL", ["logging", "level"], "INFO", _CONFIG, str
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# =============================================================================
|
|
283
|
+
# Internal State (global mutable state)
|
|
284
|
+
# =============================================================================
|
|
285
|
+
|
|
286
|
+
# Track which sessions have been oriented
|
|
287
|
+
ORIENTED_SESSIONS: set[str] = set()
|
|
288
|
+
|
|
289
|
+
# Threading lock for ORIENTED_SESSIONS access
|
|
290
|
+
ORIENTED_SESSIONS_LOCK = threading.Lock()
|
|
291
|
+
|
|
292
|
+
# Track session-to-agent mappings
|
|
293
|
+
SESSION_AGENTS: dict[str, dict[str, Any]] = {}
|
|
294
|
+
|
|
295
|
+
# Track pending orientation retries
|
|
296
|
+
ORIENTATION_PENDING: dict[str, dict] = {}
|
|
297
|
+
|
|
298
|
+
# Session cache (avoids repeated API calls)
|
|
299
|
+
_sessions_cache: list[dict] = []
|
|
300
|
+
_sessions_cache_time: float = 0
|
|
301
|
+
|
|
302
|
+
# Coordinator session ID
|
|
303
|
+
COORDINATOR_SESSION_ID: str | None = None
|
|
304
|
+
|
|
305
|
+
# Daemon start time - only orient sessions created after this
|
|
306
|
+
DAEMON_START_TIME_MS: int = int(time.time() * 1000)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _is_running_from_source() -> bool:
|
|
310
|
+
"""Check if running from source (development) or installed package."""
|
|
311
|
+
this_file = Path(__file__)
|
|
312
|
+
# If we're in a site-packages or dist-packages, we're installed
|
|
313
|
+
if "site-packages" in str(this_file) or "dist-packages" in str(this_file):
|
|
314
|
+
return False
|
|
315
|
+
# If parent of parent has pyproject.toml, we're likely in dev
|
|
316
|
+
return (this_file.parent.parent.parent / "pyproject.toml").exists()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _get_coordinator_title() -> str:
|
|
320
|
+
"""Get the coordinator session title from environment or default."""
|
|
321
|
+
return os.environ.get("AGENT_HUB_COORDINATOR_TITLE", "Agent Hub Coordinator")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# Initialize logging
|
|
325
|
+
logging.basicConfig(
|
|
326
|
+
level=getattr(logging, LOG_LEVEL),
|
|
327
|
+
format="%(asctime)s [%(levelname)s] %(message)s",
|
|
328
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
329
|
+
)
|
|
330
|
+
log = logging.getLogger(__name__)
|