Repository navigation
[bug] local agent: agent hooks go to files the tool does not read, and the ZCode one makes ZCode reject its user config #1220
Copy link
Copy link
Closed
Description
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 throwsunrecognized_keys ["SessionStart"] at ["hooks"].loadFileConfigcatches that and returnsconfig: {}. - Muse and Qoder CN:
resolveToolSettingsPathagainst the defaulttoolPaths, compared with each tool's user directory (Muse resolves$XDG_CONFIG_HOME/museor~/.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_hookson teamai's output.
Fix
- 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. - Refuse ZCode until
applyAgentHookwrites ZCode'shooks.eventsshape. Then write that shape. - Run the Codex trust pass after writing a Codex agent hook.
- Map Claude event names to Hermes's (
on_session_start,pre_llm_call,post_tool_call,on_session_end). - 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.jsonvalid 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.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
fix(codex): clear inherited approval for untrusted surviving hooks (T…
fix(local-agent): write agent hooks where each tool reads them (#1224)