Skip to content

[Proposal] Hooks drop-in directory (hooks.d) + config-dir env override for external tool integrations #3493

Description

@aheritier

Problem

External tools that integrate with docker-agent (terminal emulators, IDEs, audit/observability sidecars) need to install user-level lifecycle hooks (session_start / stop / permission_request / ...). Today the only user-level install point is the hooks: block in ~/.config/cagent/config.yaml, which means:

  • installers must parse and rewrite a user-owned YAML file in place (fragile: comments, formatting, and ordering are easily clobbered)
  • clean uninstall requires marker-block surgery
  • two tools editing the same block can conflict

Proposal

  1. Hooks drop-in directory: load <config-dir>/hooks.d/*.yaml, where each file is a standalone HooksConfig (same schema as the userconfig hooks: block).
    • Files loaded in lexicographic order
    • Merged additively after the userconfig hooks: block and before agent-config and CLI hooks (same merge path as runConfig.GlobalHooks)
    • Malformed files: log a warning and skip — a broken drop-in never breaks a run
    • Install = write one self-contained file; uninstall = delete it; no shared-file edits
  2. Config-dir env override: support DOCKER_AGENT_CONFIG_DIR (plus CAGENT_CONFIG_DIR alias, matching the existing dual-env convention used for the models gateway) as a fallback when --config-dir is not passed. Precedence: flag > env > default. External tools need this to locate hooks.d under non-default config dirs, mirroring conventions like CODEX_HOME / OPENCODE_CONFIG_DIR.

Motivating example

Terminal integrations à la cmux: show agent running/idle state in the sidebar, surface tool-permission approvals as actionable cards, notify when the model stops, and restore agent sessions across app relaunches. cmux integrates Codex/Gemini/Copilot/Hermes by writing hook entries into each agent's config file; a drop-in directory gives docker-agent the cleanest possible equivalent — one cmux-owned file, trivially uninstallable. The same mechanism serves any other integrator.

Non-goals

  • No agent-schema change (userconfig only; frozen config versions untouched)
  • No change to hook semantics or existing merge precedence

Happy to send a PR — the loading/merge change is small and additive, with tests pinning lexicographic order, additive merge, and warn-and-skip behavior.

Metadata

Metadata

Assignees

Labels

area/cliCLI commands, flags, output formattingarea/configFor configuration parsing, YAML, environment variablesarea/sessionsFor features/issues/fixes related to session lifecycle (resume, persistence, export)kind/featPR adds a new feature (maps to feat:). Use on PRs only.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions