Skip to content

Commit 00964cd

Browse files
authored
chore: rebrand HERMES_→COLLAR_ env vars + dead file cleanup
- All HERMES_ env vars renamed to COLLAR_ with collar_env() fallback - collar_env() reads COLLAR_* first, falls back to HERMES_* - Renamed skills/deepsuck-agent → skills/dag-agent - Updated hermes_cli → dag_cli references - Updated hermes-gateway → collar-gateway - Cleaned ADDING_A_PLATFORM.dog hermes references - Removed self-refine-and-hermes-evolution.md
1 parent 1077cb1 commit 00964cd

23 files changed

Lines changed: 142 additions & 205 deletions

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Entity→Target:abbreviated_verb(card)>Target:verb(card)
5252
```
5353

5454
**Format rules:**
55-
- DAG names abbreviated to 2 chars: `deepsuck-harness``[dh]`
55+
- DAG names abbreviated to 2 chars: `collar-harness``[ch]`
5656
- Verbs abbreviated to 5 chars: `references``refer`
5757
- `` between entity and its edges, `>` between edges
5858
- Cards compacted: `1:1``11`, `1:many``1m`, `many:many``mm`

cli.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def realign_markdown_tables(*args, **kwargs):
167167

168168
# Load .env from ~/.dag/.env first, then project root as dev fallback.
169169
# User-managed env files should override stale shell exports on restart.
170-
from dag_constants import get_dag_home, display_dag_home
170+
from dag_constants import collar_env, get_dag_home, display_dag_home
171171
from dag_cli.browser_connect import (
172172
DEFAULT_BROWSER_CDP_URL,
173173
is_browser_debug_ready,
@@ -322,7 +322,7 @@ def _resolve_prefill_messages_file(config: Dict[str, Any]) -> str:
322322
``agent.prefill_messages_file`` remains a legacy fallback for older CLI and
323323
godmode-generated configs.
324324
"""
325-
env_path = os.getenv("COLLAR_PREFILL_MESSAGES_FILE", "").strip()
325+
env_path = collar_env("PREFILL_MESSAGES_FILE", "").strip()
326326
if env_path:
327327
return env_path
328328
top_level = str(config.get("prefill_messages_file", "") or "").strip()
@@ -376,7 +376,7 @@ def load_cli_config() -> Dict[str, Any]:
376376

377377
# --ignore-user-config: force-skip the user config.yaml (still honor project
378378
# config as a fallback so defaults stay sensible).
379-
ignore_user_config = os.environ.get("COLLAR_IGNORE_USER_CONFIG") == "1"
379+
ignore_user_config = collar_env("IGNORE_USER_CONFIG") == "1"
380380

381381
# Use user config if it exists, otherwise project config
382382
if user_config_path.exists() and not ignore_user_config:
@@ -919,10 +919,10 @@ def _prepare_deferred_agent_startup() -> None:
919919
global _deferred_agent_startup_done
920920
if _deferred_agent_startup_done:
921921
return
922-
if os.environ.get("COLLAR_DEFER_AGENT_STARTUP") != "1":
922+
if collar_env("DEFER_AGENT_STARTUP") != "1":
923923
return
924924
_deferred_agent_startup_done = True
925-
_accept_hooks = os.environ.get("COLLAR_ACCEPT_HOOKS", "").lower() in {
925+
_accept_hooks = collar_env("ACCEPT_HOOKS", "").lower() in {
926926
"1",
927927
"true",
928928
"yes",
@@ -1811,7 +1811,7 @@ def _detect_light_mode() -> bool:
18111811
_LIGHT_MODE_CACHE = result
18121812
return result
18131813
# 2. Theme hint
1814-
theme = (os.environ.get("COLLAR_TUI_THEME") or "").strip().lower()
1814+
theme = (collar_env("TUI_THEME") or "").strip().lower()
18151815
if theme == "light":
18161816
result = True
18171817
_LIGHT_MODE_CACHE = result
@@ -1820,7 +1820,7 @@ def _detect_light_mode() -> bool:
18201820
_LIGHT_MODE_CACHE = result
18211821
return result
18221822
# 3. Explicit bg hex
1823-
bg_hint = os.environ.get("COLLAR_TUI_BACKGROUND") or ""
1823+
bg_hint = collar_env("TUI_BACKGROUND") or ""
18241824
bg_lum = _luminance_from_hex(bg_hint)
18251825
if bg_lum is not None:
18261826
result = bg_lum >= 0.5
@@ -3006,7 +3006,7 @@ def _build_compact_banner() -> str:
30063006
line1 = f"{agent_name} - AI Agent Framework"
30073007
tiny_line = agent_name
30083008

3009-
if os.environ.get("COLLAR_FAST_STARTUP_BANNER") == "1":
3009+
if collar_env("FAST_STARTUP_BANNER") == "1":
30103010
from dag_cli import __release_date__ as _release_date
30113011
from dag_cli import __version__ as _version
30123012

@@ -3322,7 +3322,7 @@ def __init__(
33223322
_DEFAULT_CONFIG_MODEL = ""
33233323
self.model = model or _config_model or _DEFAULT_CONFIG_MODEL
33243324
# Read max_tokens from config (env var override: COLLAR_MAX_TOKENS)
3325-
_env_mt = os.environ.get("COLLAR_MAX_TOKENS")
3325+
_env_mt = collar_env("MAX_TOKENS")
33263326
if _env_mt:
33273327
try:
33283328
self.max_tokens = int(_env_mt)
@@ -3358,7 +3358,7 @@ def __init__(
33583358
self.requested_provider = (
33593359
provider
33603360
or CLI_CONFIG["model"].get("provider")
3361-
or os.getenv("COLLAR_INFERENCE_PROVIDER")
3361+
or collar_env("INFERENCE_PROVIDER")
33623362
or "auto"
33633363
)
33643364
self._provider_source: Optional[str] = None
@@ -3385,9 +3385,9 @@ def __init__(
33853385
self.max_turns = CLI_CONFIG["agent"]["max_turns"]
33863386
elif CLI_CONFIG.get("max_turns"): # Backwards compat: root-level max_turns
33873387
self.max_turns = CLI_CONFIG["max_turns"]
3388-
elif os.getenv("COLLAR_MAX_ITERATIONS"):
3388+
elif collar_env("MAX_ITERATIONS"):
33893389
try:
3390-
self.max_turns = int(os.getenv("COLLAR_MAX_ITERATIONS", ""))
3390+
self.max_turns = int(collar_env("MAX_ITERATIONS", ""))
33913391
except (TypeError, ValueError):
33923392
self.max_turns = 90
33933393
else:
@@ -3419,11 +3419,11 @@ def __init__(
34193419
# by `dag chat --ignore-rules` in dag_cli/main.py. When true we
34203420
# pass skip_context_files=True and skip_memory=True to AIAgent so
34213421
# AGENTS.md/SOUL.md/.cursorrules and persistent memory are not loaded.
3422-
self.ignore_rules = ignore_rules or os.environ.get("COLLAR_IGNORE_RULES") == "1"
3422+
self.ignore_rules = ignore_rules or collar_env("IGNORE_RULES") == "1"
34233423

34243424
# Ephemeral system prompt: env var takes precedence, then config
34253425
self.system_prompt = (
3426-
os.getenv("COLLAR_EPHEMERAL_SYSTEM_PROMPT", "")
3426+
collar_env("EPHEMERAL_SYSTEM_PROMPT", "")
34273427
or CLI_CONFIG["agent"].get("system_prompt", "")
34283428
)
34293429
self.personalities = CLI_CONFIG["agent"].get("personalities", {})
@@ -5189,7 +5189,7 @@ def show_banner(self):
51895189

51905190
# Tool discovery is intentionally deferred on the Termux bare prompt
51915191
# path; availability warnings are shown once tools are initialized.
5192-
if os.environ.get("COLLAR_DEFER_AGENT_STARTUP") != "1":
5192+
if collar_env("DEFER_AGENT_STARTUP") != "1":
51935193
self._show_tool_availability_warnings()
51945194

51955195
# Warn about low context lengths (common with local servers). Keep
@@ -5488,7 +5488,7 @@ def _show_tool_availability_warnings(self):
54885488
def _show_status(self):
54895489
"""Show compact startup status line."""
54905490
# Avoid pulling the full tool registry into the bare Termux prompt path.
5491-
if os.environ.get("COLLAR_DEFER_AGENT_STARTUP") == "1":
5491+
if collar_env("DEFER_AGENT_STARTUP") == "1":
54925492
tool_status = "tools deferred"
54935493
else:
54945494
tools = get_tool_definitions(enabled_toolsets=self.enabled_toolsets, quiet_mode=True)
@@ -6000,7 +6000,7 @@ def new_session(self, silent=False, title=None):
60006000
self.agent._session_db_created = False
60016001
self._session_db.create_session(
60026002
session_id=self.session_id,
6003-
source=os.environ.get("COLLAR_SESSION_SOURCE", "cli"),
6003+
source=collar_env("SESSION_SOURCE", "cli"),
60046004
model=self.model,
60056005
model_config={
60066006
"max_iterations": self.max_turns,
@@ -11016,7 +11016,7 @@ def run(self):
1101611016
# won't affect the running process — we just want the operator to
1101711017
# see that they're running without the safety net.
1101811018
try:
11019-
_redact_raw = os.getenv("COLLAR_REDACT_SECRETS", "true")
11019+
_redact_raw = collar_env("REDACT_SECRETS", "true")
1102011020
if _redact_raw.lower() not in {"1", "true", "yes", "on"}:
1102111021
self._console_print(
1102211022
"[bold red]⚠ Secret redaction is DISABLED[/] "
@@ -11154,10 +11154,10 @@ def run(self):
1115411154
self._voice_tts_done = threading.Event() # Signals TTS playback finished
1115511155
self._voice_tts_done.set() # Initially "done" (no TTS pending)
1115611156

11157-
if os.environ.get("COLLAR_DEFER_AGENT_STARTUP") != "1":
11157+
if collar_env("DEFER_AGENT_STARTUP") != "1":
1115811158
self._install_tool_callbacks()
1115911159

11160-
if os.environ.get("COLLAR_DEFER_AGENT_STARTUP") != "1":
11160+
if collar_env("DEFER_AGENT_STARTUP") != "1":
1116111161
self._ensure_tirith_security()
1116211162

1116311163
# Key bindings for the input area
@@ -13156,7 +13156,7 @@ def _signal_handler(signum, frame):
1315613156
if getattr(self, "agent", None) and getattr(self, "_agent_running", False):
1315713157
self.agent.interrupt(f"received signal {signum}")
1315813158
try:
13159-
_grace = float(os.getenv("COLLAR_SIGTERM_GRACE", "1.5"))
13159+
_grace = float(collar_env("SIGTERM_GRACE", "1.5"))
1316013160
except (TypeError, ValueError):
1316113161
_grace = 1.5
1316213162
if _grace > 0:
@@ -13429,7 +13429,7 @@ def _run_kanban_goal_loop_q(cli: "DagCLI", first_response: str) -> None:
1342913429
"""
1343013430
import os as _os
1343113431

13432-
task_id = (_os.environ.get("COLLAR_KANBAN_TASK") or "").strip()
13432+
task_id = (_collar_env("KANBAN_TASK") or "").strip()
1343313433
if not task_id:
1343413434
return
1343513435

@@ -13725,7 +13725,7 @@ def _signal_handler_q(signum, frame):
1372513725
if _agent is not None:
1372613726
_agent.interrupt(f"received signal {signum}")
1372713727
try:
13728-
_grace = float(os.getenv("COLLAR_SIGTERM_GRACE", "1.5"))
13728+
_grace = float(collar_env("SIGTERM_GRACE", "1.5"))
1372913729
except (TypeError, ValueError):
1373013730
_grace = 1.5
1373113731
if _grace > 0:
@@ -13744,7 +13744,7 @@ def _signal_handler_q(signum, frame):
1374413744
# first so the final debug trace isn't lost; SIGALRM deadman guards
1374513745
# the flush against any rare blocking-I/O case (the reporter measured
1374613746
# flush in <1ms; the alarm is a failsafe, not the common path).
13747-
if os.environ.get("COLLAR_KANBAN_TASK"):
13747+
if collar_env("KANBAN_TASK"):
1374813748
try:
1374913749
import signal as _sig_mod
1375013750
if hasattr(_sig_mod, "SIGALRM"):
@@ -13789,7 +13789,7 @@ def _signal_handler_q(signum, frame):
1378913789
# path or URL into a kanban task body never get it routed to the
1379013790
# model's vision input.
1379113791
single_query_image_urls: list[str] = []
13792-
_kanban_task_id = os.environ.get("COLLAR_KANBAN_TASK", "").strip()
13792+
_kanban_task_id = collar_env("KANBAN_TASK", "").strip()
1379313793
if _kanban_task_id:
1379413794
try:
1379513795
from dag_cli import kanban_db as _kb
@@ -13933,7 +13933,7 @@ def _signal_handler_q(signum, frame):
1393313933
# out (→ sticky block). Gated on the env vars the
1393413934
# dispatcher sets in `_default_spawn`; a no-op for every
1393513935
# normal worker and every non-kanban `-q` run.
13936-
if os.environ.get("COLLAR_KANBAN_GOAL_MODE") == "1":
13936+
if collar_env("KANBAN_GOAL_MODE") == "1":
1393713937
try:
1393813938
_run_kanban_goal_loop_q(cli, response)
1393913939
except Exception as _goal_exc:
@@ -13957,7 +13957,7 @@ def _signal_handler_q(signum, frame):
1395713957
_exit_code = 0
1395813958
if isinstance(result, dict) and result.get("failed"):
1395913959
_exit_code = 1
13960-
if os.environ.get("COLLAR_KANBAN_TASK") and result.get(
13960+
if collar_env("KANBAN_TASK") and result.get(
1396113961
"failure_reason"
1396213962
) in ("rate_limit", "billing"):
1396313963
try:

cron/jobs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
msvcrt = None
3131
from datetime import datetime, timedelta
3232
from pathlib import Path
33-
from dag_constants import get_dag_home
33+
from dag_constants import collar_env, get_dag_home
3434
from typing import Optional, Dict, List, Any, Union
3535

3636
logger = logging.getLogger(__name__)
@@ -48,8 +48,8 @@
4848
# Configuration
4949
# =============================================================================
5050

51-
HERMES_DIR = get_dag_home().resolve()
52-
CRON_DIR = HERMES_DIR / "cron"
51+
COLLAR_DIR = get_dag_home().resolve()
52+
CRON_DIR = COLLAR_DIR / "cron"
5353
JOBS_FILE = CRON_DIR / "jobs.json"
5454

5555
# In-process lock protecting load_jobs→modify→save_jobs cycles.

cron/scheduler.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
# the module) fail with ModuleNotFoundError for hermes_time et al.
3838
sys.path.insert(0, str(Path(__file__).parent.parent))
3939

40-
from dag_constants import get_dag_home
40+
from dag_constants import collar_env, get_dag_home
4141
from dag_cli._subprocess_compat import windows_hide_flags
4242
from dag_cli.config import load_config, _expand_env_vars
4343
from dag_time import now as _dag_now
@@ -866,14 +866,14 @@ def _get_script_timeout() -> int:
866866
except Exception:
867867
logger.warning("Invalid patched _SCRIPT_TIMEOUT=%r; using env/config/default", _SCRIPT_TIMEOUT)
868868

869-
env_value = os.getenv("HERMES_CRON_SCRIPT_TIMEOUT", "").strip()
869+
env_value = collar_env("CRON_SCRIPT_TIMEOUT", "").strip()
870870
if env_value:
871871
try:
872872
timeout = int(float(env_value))
873873
if timeout > 0:
874874
return timeout
875875
except Exception:
876-
logger.warning("Invalid HERMES_CRON_SCRIPT_TIMEOUT=%r; using config/default", env_value)
876+
logger.warning("Invalid COLLAR_CRON_SCRIPT_TIMEOUT=%r; using config/default", env_value)
877877

878878
try:
879879
cfg = load_config() or {}
@@ -1494,7 +1494,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
14941494
# Mark this as a cron session so the approval system can apply cron_mode.
14951495
# This env var is process-wide and persists for the lifetime of the
14961496
# scheduler process — every job this process runs is a cron job.
1497-
os.environ["HERMES_CRON_SESSION"] = "1"
1497+
os.environ["COLLAR_CRON_SESSION"] = "1"
14981498

14991499
# Use ContextVars for per-job session/delivery state so parallel jobs
15001500
# don't clobber each other's targets (os.environ is process-global).
@@ -1510,7 +1510,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
15101510
# (notify_on_complete / watch_patterns) reads DAG_SESSION_PLATFORM
15111511
# and DAG_SESSION_CHAT_ID to populate watcher_platform / chat_id,
15121512
# which would route completion notifications to the origin chat
1513-
# instead of via HERMES_CRON_AUTO_DELIVER_* below.
1513+
# instead of via COLLAR_CRON_AUTO_DELIVER_* below.
15141514
# - tools/tts_tool.py: picks Opus vs MP3 based on
15151515
# DAG_SESSION_PLATFORM == "telegram".
15161516
# - tools/skills_tool.py + agent/prompt_builder.py: per-platform
@@ -1519,17 +1519,17 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
15191519
# - tools/send_message_tool.py: mirror source labelling and the
15201520
# send_message gate read DAG_SESSION_PLATFORM.
15211521
# Cron output delivery itself reads job["origin"] directly via
1522-
# _resolve_origin(job) and the HERMES_CRON_AUTO_DELIVER_* vars set
1522+
# _resolve_origin(job) and the COLLAR_CRON_AUTO_DELIVER_* vars set
15231523
# below, so clearing DAG_SESSION_* here does not affect delivery.
15241524
_ctx_tokens = set_session_vars(
15251525
platform="",
15261526
chat_id="",
15271527
chat_name="",
15281528
)
15291529
_cron_delivery_vars = (
1530-
"HERMES_CRON_AUTO_DELIVER_PLATFORM",
1531-
"HERMES_CRON_AUTO_DELIVER_CHAT_ID",
1532-
"HERMES_CRON_AUTO_DELIVER_THREAD_ID",
1530+
"COLLAR_CRON_AUTO_DELIVER_PLATFORM",
1531+
"COLLAR_CRON_AUTO_DELIVER_CHAT_ID",
1532+
"COLLAR_CRON_AUTO_DELIVER_THREAD_ID",
15331533
)
15341534
for _var_name in _cron_delivery_vars:
15351535
_VAR_MAP[_var_name].set("")
@@ -1569,15 +1569,15 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
15691569

15701570
delivery_target = _resolve_delivery_target(job)
15711571
if delivery_target:
1572-
_VAR_MAP["HERMES_CRON_AUTO_DELIVER_PLATFORM"].set(delivery_target["platform"])
1573-
_VAR_MAP["HERMES_CRON_AUTO_DELIVER_CHAT_ID"].set(str(delivery_target["chat_id"]))
1574-
_VAR_MAP["HERMES_CRON_AUTO_DELIVER_THREAD_ID"].set(
1572+
_VAR_MAP["COLLAR_CRON_AUTO_DELIVER_PLATFORM"].set(delivery_target["platform"])
1573+
_VAR_MAP["COLLAR_CRON_AUTO_DELIVER_CHAT_ID"].set(str(delivery_target["chat_id"]))
1574+
_VAR_MAP["COLLAR_CRON_AUTO_DELIVER_THREAD_ID"].set(
15751575
""
15761576
if delivery_target.get("thread_id") is None
15771577
else str(delivery_target["thread_id"])
15781578
)
15791579

1580-
model = job.get("model") or os.getenv("HERMES_MODEL") or ""
1580+
model = job.get("model") or collar_env("MODEL") or ""
15811581

15821582
# Load config.yaml for model, reasoning, prefill, toolsets, provider routing
15831583
_cfg = {}
@@ -1617,7 +1617,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
16171617
prefill_messages = None
16181618
agent_cfg = _cfg.get("agent", {}) if isinstance(_cfg.get("agent", {}), dict) else {}
16191619
prefill_file = (
1620-
os.getenv("HERMES_PREFILL_MESSAGES_FILE", "")
1620+
collar_env("PREFILL_MESSAGES_FILE", "")
16211621
or _cfg.get("prefill_messages_file", "")
16221622
or agent_cfg.get("prefill_messages_file", "")
16231623
)
@@ -1647,7 +1647,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
16471647
)
16481648
from dag_cli.auth import AuthError
16491649
try:
1650-
# Do not inject HERMES_INFERENCE_PROVIDER here. resolve_runtime_provider()
1650+
# Do not inject COLLAR_INFERENCE_PROVIDER here. resolve_runtime_provider()
16511651
# already prefers persisted config over stale shell/env overrides when
16521652
# no explicit provider is requested. Passing the env var here short-
16531653
# circuits that precedence and can resurrect old providers (for
@@ -1760,17 +1760,17 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
17601760
# for hours if it's actively calling tools / receiving stream tokens,
17611761
# but a hung API call or stuck tool with no activity for the configured
17621762
# duration is caught and killed. Default 600s (10 min inactivity);
1763-
# override via HERMES_CRON_TIMEOUT env var. 0 = unlimited.
1763+
# override via COLLAR_CRON_TIMEOUT env var. 0 = unlimited.
17641764
#
17651765
# Uses the agent's built-in activity tracker (updated by
17661766
# _touch_activity() on every tool call, API call, and stream delta).
1767-
_raw_cron_timeout = os.getenv("HERMES_CRON_TIMEOUT", "").strip()
1767+
_raw_cron_timeout = collar_env("CRON_TIMEOUT", "").strip()
17681768
if _raw_cron_timeout:
17691769
try:
17701770
_cron_timeout = float(_raw_cron_timeout)
17711771
except (ValueError, TypeError):
17721772
logger.warning(
1773-
"Invalid HERMES_CRON_TIMEOUT=%r; using default 600s",
1773+
"Invalid COLLAR_CRON_TIMEOUT=%r; using default 600s",
17741774
_raw_cron_timeout,
17751775
)
17761776
_cron_timeout = 600.0
@@ -2018,14 +2018,14 @@ def tick(verbose: bool = True, adapters=None, loop=None, sync: bool = True) -> i
20182018
advance_next_run(job["id"])
20192019

20202020
# Resolve max parallel workers: env var > config.yaml > unbounded.
2021-
# Set HERMES_CRON_MAX_PARALLEL=1 to restore old serial behaviour.
2021+
# Set COLLAR_CRON_MAX_PARALLEL=1 to restore old serial behaviour.
20222022
_max_workers: Optional[int] = None
20232023
try:
2024-
_env_par = os.getenv("HERMES_CRON_MAX_PARALLEL", "").strip()
2024+
_env_par = collar_env("CRON_MAX_PARALLEL", "").strip()
20252025
if _env_par:
20262026
_max_workers = int(_env_par) or None
20272027
except (ValueError, TypeError):
2028-
logger.warning("Invalid HERMES_CRON_MAX_PARALLEL value; defaulting to unbounded")
2028+
logger.warning("Invalid COLLAR_CRON_MAX_PARALLEL value; defaulting to unbounded")
20292029
if _max_workers is None:
20302030
try:
20312031
_ucfg = load_config() or {}

0 commit comments

Comments
 (0)