Zentty exports these environment variables into every pane:
ZENTTY_CLI_BINZENTTY_AGENT_EVENT_COMMANDZENTTY_INSTANCE_SOCKETZENTTY_PANE_TOKENZENTTY_WINDOW_IDZENTTY_WORKLANE_IDZENTTY_PANE_ID
ZENTTY_AGENT_EVENT_COMMAND expands to the bundled Zentty helper for canonical agent events:
$ZENTTY_CLI_BIN ipc agent-eventRegister the same command for these Claude hook events:
NotificationPermissionRequestUserPromptSubmitSessionStartStopSessionEndPreToolUsewith matcherAskUserQuestionTaskCreatedTaskCompleted
Example config snippet:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"PreToolUse": [
{
"matcher": "AskUserQuestion",
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"TaskCreated": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
],
"TaskCompleted": [
{
"hooks": [
{
"type": "command",
"command": "$ZENTTY_AGENT_EVENT_COMMAND --adapter=claude"
}
]
}
]
}
}SessionStart-> session/PID attach onlyNotification,PermissionRequest,PreToolUse(AskUserQuestion)->needs-inputUserPromptSubmit->runningStop->idleSessionEnd-> clear session + PID mapping
This keeps Zentty’s sidebar and alerts aligned with Claude’s own lifecycle instead of terminal heuristics.
Claude Code task hooks are used to maintain a per-session task registry. When a top-level session emits TaskCreated / TaskCompleted, Zentty can render running status as Running (<done>/<total>).
Counts are intentionally scoped to the main session only. Subagent or nested task lists are ignored so the suffix stays stable.
Claude hook execution is best effort. If the Claude adapter fails internally, Zentty returns success to Claude and suppresses stderr so users do not see hook error banners.
Zentty injects Copilot hooks via a temporary COPILOT_HOME overlay so the user's real ~/.copilot config stays untouched. Official schema: https://docs.github.com/en/copilot/reference/hooks-configuration.
The overlay:
- preserves existing Copilot config/state entries (deep-merged)
- ensures
"version": 1at the top level (required by Copilot) - appends Zentty hook commands without removing user hook commands
Copilot CLI exposes six hook events (all camelCase). Zentty registers the bundled helper for each:
sessionStartsessionEnduserPromptSubmittedpreToolUsepostToolUseerrorOccurred
Entry format: {"type": "command", "bash": "<command>", "timeoutSec": N}.
sessionStart-> PID attach + seed agentStatus atidlesessionEnd-> clear session (lifecycle payload withstate: null)userPromptSubmitted-> no-op; OSC 9;4 (libghostty progress) drives RunningpreToolUsewithtoolNamematchingaskuserquestion*->needs-inputwith the parsed question textpostToolUsewith the matching tool -> revert toidleso OSC drives Running againerrorOccurred-> no-op for now
Unlike Claude Code (which has a Stop hook), Copilot has no "turn complete" event. Running detection relies on libghostty's OSC 9;4 progress state (TerminalProgressReport.indicatesActivity): when Copilot emits SET/INDETERMINATE, the normalizer promotes the pane from idle to running; when Copilot emits REMOVE, it drops back to idle. The copilot special case in PanePresentationNormalizer.normalizedRuntimePhase implements this.
Zentty injects Gemini hooks automatically for wrapped gemini launches by generating a per-pane system-settings overlay and pointing GEMINI_CLI_SYSTEM_SETTINGS_PATH at it. The user's real Gemini config is not modified.
The overlay:
- preserves any existing system settings that Gemini would have loaded
- forces
general.enableNotifications = truefor wrapped sessions - appends Zentty hook commands without removing existing hook commands
Gemini hook commands use the hidden bundled CLI helper:
"$ZENTTY_CLI_BIN" gemini-hookZentty registers the helper for these Gemini hook events:
SessionStartSessionEndBeforeAgentAfterAgentNotificationBeforeTool
SessionStart-> PID attach +startingBeforeAgent->runningBeforeTool->runningas a blocked-session recovery signal after approvalAfterAgent->idleSessionEnd-> clear session + PID mappingNotificationwithnotification_type = ToolPermission->needs-inputwithapproval
Gemini's built-in terminal notifications still matter for wrapped sessions. Zentty treats:
Action requiredas approval-needed attentionSession completeas a ready/completion signal
This gives Gemini first-class sidebar and notification behavior even when the hook payload is minimal.
Wrapped kimi launches use a per-launch overlay config under Zentty's runtime directory. Zentty reads the active Kimi config source, merges in its hook block for that session, and launches Kimi with --config-file <overlay>. The user's ~/.kimi/config.toml is left untouched during normal wrapped launches.
Kimi's own setup commands are stricter than normal chat turns:
kimi loginmust run against Kimi's default config location. Zentty now passthroughskimi loginand the other Kimi management commands directly to the real Kimi binary so they keep using the default config./loginand/modelinside a wrappedkimisession are not reliable because Kimi rejects those flows when launched with--configor--config-file.- For model selection in wrapped sessions, prefer
kimi --model <model-id>or update~/.kimi/config.tomldirectly.
Manual fallback commands:
zentty install kimi-hooks
zentty uninstall kimi-hookszentty install kimi-hooks remains available if you explicitly want a persistent global hook install for debugging or recovery. Set ZENTTY_KIMI_HOOKS_DISABLED=1 to bypass Zentty's Kimi hook overlay and launch Kimi directly.
Zentty registers these Kimi hooks:
SessionStartSessionEndUserPromptSubmitStopNotificationwithmatcher = "permission_prompt"PreToolUsewithmatcher = "AskUserQuestion"PostToolUsewithmatcher = "AskUserQuestion"
Each hook calls:
"$ZENTTY_CLI_BIN" ipc agent-event --adapter=kimiSessionStart-> PID attach +startingUserPromptSubmit->runningStop->idleSessionEnd-> clear session + PID mappingNotification(permission_prompt)->needs-inputwithapprovalPreToolUse(AskUserQuestion)->needs-inputwithquestionPostToolUse(AskUserQuestion)->running
Zentty injects a local OpenCode plugin overlay via the shared agent wrapper. The plugin forwards session.status, session.idle, permission/question events, and todo.updated.
todo.updated is normalized inside the plugin into taskProgressDoneCount / taskProgressTotalCount. The Swift bridge treats those as the authoritative OpenCode task counts and uses them only for the main session's running label.