# How to Write Lifecycle Hooks for Desktop Notifications in Kimi Code

> Learn how to write lifecycle hooks for desktop notifications in Kimi Code. Trigger OS-native alerts and customize plugin behavior at specific execution points.

- Repository: [Moonshot AI/kimi-code](https://github.com/MoonshotAI/kimi-code)
- Tags: how-to-guide
- Published: 2026-07-25

---

**Kimi Code provides a lifecycle-hook system that executes arbitrary commands at specific execution points, allowing plugins to trigger desktop notifications by reading environment variables like `KIMI_TURN_ID` and dispatching OS-native alerts or OSC 9 terminal sequences.**

Kimi Code, the open-source agent framework from MoonshotAI, exposes a robust lifecycle-hook system that enables plugins to run commands when key events occur. By writing lifecycle hooks for desktop notifications in Kimi Code, you can alert users when agents finish turns, complete tool calls, or reach specific milestones. This guide walks through the architecture, implementation, and configuration required to build notification plugins using the actual source code from the `MoonshotAI/kimi-code` repository.

## Understanding the Lifecycle Hook Architecture

The hook system relies on four core components that work together to bridge agent events and external notifications:

- **Plugin manifest** ([`kimi.plugin.json`](https://github.com/MoonshotAI/kimi-code/blob/main/kimi.plugin.json)): Declares which lifecycle hooks a plugin consumes and the command to run for each event. The schema is documented in [`apps/kimi-code/README.md`](https://github.com/MoonshotAI/kimi-code/blob/main/apps/kimi-code/README.md).
- **Lifecycle-hook service** ([`sessionLifecycleService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/sessionLifecycleService.ts)): Located at [`packages/agent-core-v2/src/app/sessionLifecycle/sessionLifecycleService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/app/sessionLifecycle/sessionLifecycleService.ts), this service registers hook callbacks, invokes the configured command as a child process, and captures its output.
- **Desktop-notification helper** ([`terminal-notification.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/terminal-notification.ts)): Found in [`apps/kimi-code/src/tui/utils/terminal-notification.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/apps/kimi-code/src/tui/utils/terminal-notification.ts), this utility emits an OSC 9 escape sequence that iTerm2, WezTerm, Kitty, and other terminals treat as a native desktop notification.
- **Configuration** ([`config-files.md`](https://github.com/MoonshotAI/kimi-code/blob/main/config-files.md)): The global `[notifications].enabled` flag in [`docs/en/configuration/config-files.md`](https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/configuration/config-files.md) toggles whether desktop notifications are delivered system-wide.

When a hook fires, the service injects context via environment variables—`KIMI_TURN_ID`, `KIMI_SESSION_ID`, `KIMI_EVENT`, and `KIMI_TURN_OUTPUT`—allowing the command to customize the notification based on the current execution state.

## Creating a Desktop Notification Plugin

To build a plugin that alerts users when a turn finishes, you need three pieces: a manifest that registers the hook, a script that handles the event, and optionally a configuration override.

### Step 1: Define the Plugin Manifest

Create a [`kimi.plugin.json`](https://github.com/MoonshotAI/kimi-code/blob/main/kimi.plugin.json) file that binds the `TurnFinished` event to your notification script:

```json
{
  "name": "my-notify-plugin",
  "version": "0.1.0",
  "hooks": [
    {
      "event": "TurnFinished",
      "command": "node ./scripts/notify.js"
    }
  ]
}

```

The `event` field must match the enum values used by the session-lifecycle service, such as `TurnFinished`, `ToolInvoked`, or `SessionCreated`.

### Step 2: Write the Notification Script

The script receives context through environment variables and should handle three execution paths: OSC 9 for compatible terminals, OS-native tools for standard desktops, and graceful fallbacks. Save this as [`scripts/notify.js`](https://github.com/MoonshotAI/kimi-code/blob/main/scripts/notify.js):

```javascript
#!/usr/bin/env node
// Simple desktop notification for a completed turn.
// Env vars: KIMI_TURN_ID, KIMI_SESSION_ID, KIMI_EVENT, KIMI_TURN_OUTPUT (JSON)

const { execSync } = require('child_process');

// Pull relevant data from the environment.
const turnId   = process.env.KIMI_TURN_ID   ?? 'unknown';
const sessionId = process.env.KIMI_SESSION_ID ?? 'unknown';
const output   = process.env.KIMI_TURN_OUTPUT ?? '{}';

// Build a human‑readable title and body.
const title = `Kimi Code – Turn ${turnId} finished`;
const body = `Session ${sessionId} completed a turn.`;
const payload = JSON.parse(output);

// If the terminal supports OSC 9, delegate to the helper utility.
// (The helper lives in the main repo; we invoke it via node.)
try {
  const helperPath = require('path')
    .join(__dirname, '../../apps/kimi-code/src/tui/utils/terminal-notification.js');
  execSync(`node ${helperPath} "${title}" "${body}"`);
} catch (_) {
  // Fallback to a system‑specific notifier.
  if (process.platform === 'darwin') {
    // macOS: use AppleScript.
    execSync(`osascript -e 'display notification "${body}" with title "${title}"'`);
  } else if (process.platform === 'linux') {
    // Linux: use notify‑send.
    execSync(`notify-send "${title}" "${body}"`);
  } else {
    // Windows (PowerShell):
    execSync(`powershell -Command "New-BurntToastNotification -Text '${title}', '${body}'"`);
  }
}

```

This script first attempts to use the **terminal-notification helper** for OSC 9 compatibility. If that fails, it falls back to `osascript` on macOS, `notify-send` on Linux, or PowerShell's `New-BurntToastNotification` on Windows.

### Step 3: Configure Global Notification Settings

Ensure notifications are enabled in your Kimi Code configuration. According to [`docs/en/configuration/config-files.md`](https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/configuration/config-files.md), set the following in your config file:

```toml
[notifications]
enabled = true

```

When `enabled` is false, the engine suppresses all desktop notification delivery regardless of hook configuration.

## How the Hook Execution Flow Works

The [`sessionLifecycleService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/sessionLifecycleService.ts) file in `packages/agent-core-v2/src/app/sessionLifecycle/` orchestrates the entire process. When the agent engine finishes a turn, it emits the `TurnFinished` event. The service then:

1. **Resolves the command** mapped to that event in the plugin manifest.
2. **Spawns a child process** executing that command with the injected environment variables (`KIMI_TURN_ID`, `KIMI_SESSION_ID`, etc.).
3. **Captures stdout and stderr** for logging and debugging purposes.
4. **Evaluates the exit code**; if the command returns non-zero, the hook is considered **blocked**. As implemented in [`packages/agent-core/test/session/lifecycle-hooks.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/test/session/lifecycle-hooks.test.ts), the engine can optionally retry the action or abort the session based on this status.

This design allows hooks to act as **gating mechanisms**—for example, requiring user approval via a notification click before proceeding—or as simple fire-and-forget alerts.

## Terminal-Specific Notifications with OSC 9

For users running modern terminal emulators, the [`terminal-notification.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/terminal-notification.ts) helper in [`apps/kimi-code/src/tui/utils/terminal-notification.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/apps/kimi-code/src/tui/utils/terminal-notification.ts) provides a lightweight alternative to OS-native binaries. It constructs an OSC 9 escape sequence that terminals like iTerm2, WezTerm, and Kitty render as a desktop notification without spawning external processes.

You can invoke this helper directly from any hook command:

```bash
node apps/kimi-code/src/tui/utils/terminal-notification.js "Kimi Code" "Turn completed"

```

The helper automatically truncates payloads to stay within terminal emulator limits, ensuring the notification always renders even with long titles or bodies.

## Summary

- **Lifecycle hooks** in Kimi Code are declared in [`kimi.plugin.json`](https://github.com/MoonshotAI/kimi-code/blob/main/kimi.plugin.json) and executed by [`sessionLifecycleService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/sessionLifecycleService.ts) at specific agent events like `TurnFinished`.
- **Environment variables** (`KIMI_TURN_ID`, `KIMI_SESSION_ID`, `KIMI_EVENT`, `KIMI_TURN_OUTPUT`) provide context to hook scripts, enabling dynamic notification content.
- **OSC 9 escape sequences** via [`terminal-notification.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/terminal-notification.ts) offer a fast, dependency-free notification path for compatible terminals, while fallbacks to `notify-send`, `osascript`, or PowerShell cover standard desktops.
- **Error handling** treats non-zero exit codes as blocked hooks, allowing the engine to retry or abort according to logic tested in [`packages/agent-core/test/session/lifecycle-hooks.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/test/session/lifecycle-hooks.test.ts).
- **Global toggles** in [`docs/en/configuration/config-files.md`](https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/configuration/config-files.md) let users disable all notifications via the `[notifications].enabled` setting.

## Frequently Asked Questions

### What events can trigger lifecycle hooks in Kimi Code?

The system supports events such as `TurnFinished`, `ToolInvoked`, and `SessionCreated`, as defined in the lifecycle service enum. You bind to these events in your plugin's [`kimi.plugin.json`](https://github.com/MoonshotAI/kimi-code/blob/main/kimi.plugin.json) manifest by specifying the exact event name in the `event` field of the hooks array.

### How do I disable desktop notifications globally without removing my plugin?

Set `enabled = false` under the `[notifications]` section in your Kimi Code configuration file. This setting, documented in [`docs/en/configuration/config-files.md`](https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/configuration/config-files.md), suppresses all desktop notification delivery system-wide while keeping your lifecycle hooks registered for other side effects.

### What happens if my notification script exits with an error?

If your hook command returns a non-zero exit status, the lifecycle service marks the hook as **blocked**. According to the test suite in [`packages/agent-core/test/session/lifecycle-hooks.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/test/session/lifecycle-hooks.test.ts), the engine can be configured to either retry the blocked action or abort the session entirely, making hooks suitable for approval gates as well as notifications.

### Can I use the terminal notification helper outside of lifecycle hooks?

Yes. The [`terminal-notification.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/terminal-notification.ts) utility is a standalone script that emits OSC 9 escape sequences. You can invoke it directly from the command line or from any Node.js script to send notifications to iTerm2, WezTerm, or Kitty, regardless of whether it is triggered by the Kimi Code hook system.