Skip to content

[bug] local agent: agent hooks go to files the tool does not read, and the ZCode one makes ZCode reject its user config #1220

Closed

Description

@SaulMoro

Problem

The HTTP local agent installs a server-pushed hook (install_hook_rule) through runHookRuleCommand (src/local-agent.ts:3672). For every tool without its own adapter it writes to resolveToolSettingsPath (src/local-agent.ts:557), which joins $HOME with the tool's project settings path. It ignores the user-scope paths that pull uses (scopedToolPaths, src/types.ts:2691). Why the paths differ:

install_hook_rule (from the server)
  runHookRuleCommand                     local-agent.ts:3672
    resolveToolSettingsPath              local-agent.ts:557
      applyToolRoots(team.toolPaths)     ← the project-scope paths
      path.join(getUserHome(), settings)
    applyAgentHook                       ← Claude-shaped hooks.<Event>

pull, user scope
  scopedToolPaths(team, { scope: 'user' })   ← the user paths each tool reads

The result, per tool:

Tool The local agent writes The tool reads Effect
ZCode a Claude-shaped hooks.SessionStart key in ~/.zcode/cli/config.json hooks.events.*, under a strict schema ZCode rejects the whole file and loads none of it. teamai's built-in hooks and the member's MCP servers in that file stop working, though they stay on disk. The next teamai reconcile strips the key (src/hooks.ts:2209), so the server still records a hook that no longer exists
Muse ~/.muse/hooks.json ~/.config/muse/settings.json the hook never runs
Qoder CN ~/.qoder/settings.json ~/.qoder-cn/settings.json the hook never runs
Codex ~/.codex/hooks.json the same file, only trusted hooks the local agent runs no trust pass, so Codex skips the hook
Hermes hooks.SessionStart, hooks.UserPromptSubmit on_session_start, pre_llm_call Hermes logs "unknown hook event" and parses none
OpenCode a plugin for the mapped events PreToolUse is skipped with a warning, but the install returns success to the server

What happens to ZCode, step by step:

step                          ~/.zcode/cli/config.json             ZCode loads             expected
pull                          hooks.events (teamai), mcp           hooks + MCP             hooks + MCP
server pushes SessionStart    + hooks.SessionStart (Claude shape)  nothing: schema error   hooks + MCP, install refused   ← first divergence
next pull                     hooks.SessionStart stripped          hooks + MCP             hooks + MCP
server's view                 the hook is installed                -                       the install failed

Evidence

  • ZCode: teamai's file before and after applyAgentHook, parsed with ZCode v3.15.1's own config schema (hooksSchema, zod 4.6.5). Before, it parses. After, it throws unrecognized_keys ["SessionStart"] at ["hooks"]. loadFileConfig catches that and returns config: {}.
  • Muse and Qoder CN: resolveToolSettingsPath against the default toolPaths, compared with each tool's user directory (Muse resolves $XDG_CONFIG_HOME/muse or ~/.config/muse; Qoder CN's brand directory is .qoder-cn).
  • Codex 0.163: an untrusted user hook does not run (the trust key is <source_path>:<event>:<group>:<handler>).
  • Hermes 7e9eb8d9: agent.shell_hooks._parse_hooks on teamai's output.

Fix

  1. Resolve the settings path with scopedToolPaths(teamConfig, { scope: 'user', toolRoots }), as HOME delivery does. Clean up hooks already written to the old paths, because removal (src/local-agent.ts:3752) uses the same resolver.
  2. Refuse ZCode until applyAgentHook writes ZCode's hooks.events shape. Then write that shape.
  3. Run the Codex trust pass after writing a Codex agent hook.
  4. Map Claude event names to Hermes's (on_session_start, pre_llm_call, post_tool_call, on_session_end).
  5. Return a failure to the server for an event a tool cannot take.

Done when

  • An agent hook pushed for ZCode leaves ~/.zcode/cli/config.json valid under ZCode's schema.
  • Agent hooks for Muse, Qoder CN, Codex and Hermes run in the tool.
  • The server's install ack is false for an event the tool cannot take.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions