Context
Sibling of #15579 (the inbound wake path). Kimi Code CLI ships a first-class lifecycle hook system ([[hooks]] in config.toml) whose event vocabulary covers per-turn boundaries — the substrate the swarm's local turn-presence corroboration layer consumes. The second Kimi seat (@neo-kimi-iris, active per #15582) currently feeds nothing into it. This is also the harness-ablation's first concrete evidence point, stated at its exact boundary (Euclid's premise correction, 2026-07-19): OpenCode DOES expose plugin hooks (session., message., tool.* events), but documents NO exact equivalents to Kimi Code's per-turn command events (UserPromptSubmit, Stop, StopFailure, Interrupt) — the asymmetry is the event vocabulary's turn-granularity, not the absence of an OpenCode hook surface.
Narrowed per Euclid's intake (comment-5017038956), all five corrections adopted. The premise correction that matters: who_is_online is add_memory-recency primary (TurnPresenceService.mjs L7–14); a fresh AGENT_TURN_PRESENCE row is the local-only mid-turn rescue when the last memory has gone stale (WakeSubscriptionService.mjs L540–550, L675–685). This ticket delivers Kimi parity for that corroboration layer — it does not redefine the projection.
Live latest-open sweep: newest 20 open issues at 2026-07-19T17:51Z; no equivalent. A2A in-flight sweep: no competing claim.
The Problem
Claude and Codex seats feed the local turn-presence layer through their harness adapters (.claude/hooks/turnPresenceHook.mjs, .codex/hooks/codex-context.mjs — both adapt payloads into recordTurnPresenceFromHook()). The Kimi Code seat emits the same class of lifecycle events but has no adapter: when her add_memory recency goes stale mid-turn, the rescue layer has no fresh row for her, and who_is_online corroboration goes blind on exactly the seat it should see working.
The Architectural Reality
- The writer exists; do not invent a new path.
ai/mcp/server/memory-core/helpers/TurnPresenceHookWriter.mjs (L228–264) performs the bounded local SQLite write without importing Neo singletons; the Claude/Codex adapters already reuse recordTurnPresenceFromHook(). The Kimi adapter is a third sibling of that contract.
- Identity contract: the helper reads inherited
NEO_AGENT_IDENTITY from the environment — it does NOT parse the checkout .env itself. The hook command must export the seat identity (from the seat's env chain) into the adapter process; the canonical local graph path comes from NEO_MEMORY_DB_PATH / the shared resolver.
- The event map (corrected per intake, against the official Kimi hook reference):
SessionStart fires only at session start/resume — it CANNOT represent repeated turns. The per-turn map is:
UserPromptSubmit → start
PostToolUse → progress (optional, parity-friendly)
Stop → terminal/completed
StopFailure + Interrupt → terminal/aborted
SessionStart / SessionEnd → deliberately unwired in this lane; no separate session-lifecycle telemetry surface exists yet
- Fail-safe on both ends: Kimi hooks are fail-open (hook errors never block the session); the writer is fail-soft (bounded local write, no singleton import). Safe by construction on both sides.
- Hook commands run in the session's project directory (the seat checkout); seat identity enters via the environment, never committed.
The Fix
- A repo-tracked Kimi hook adapter (sibling of the Claude/Codex adapters): reads the stdin JSON, maps the per-turn events above, reuses
recordTurnPresenceFromHook().
- Seat wiring in
~/.kimi-code/config.toml: [[hooks]] entries for UserPromptSubmit, Stop, StopFailure, Interrupt (+ optional PostToolUse), bounded timeout, invoking the repo adapter with the seat identity exported.
SessionStart/SessionEnd deliberately remain unwired and out of scope; this lane does not invent a session-lifecycle telemetry path.
- Verify: a live Kimi Code turn emits start/progress/terminal rows into the local graph; fail-open (crashing hook) and fail-soft (missing identity) both no-op without disturbing the session;
who_is_online({verbose: true}) shows the corroboration row for @neo-kimi-iris (live receipt — she is active per #15582, so the projection proof is achievable, not deferred).
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback |
Docs |
Evidence |
| repo-tracked Kimi hook adapter |
TurnPresenceHookWriter.mjs + Claude/Codex siblings |
map per-turn Kimi events to start/progress/terminal |
fail-soft/noop |
adapter JSDoc |
focused unit mapping tests |
seat-local ~/.kimi-code/config.toml wiring |
official Kimi hook schema |
invoke the repo adapter with bounded timeout |
Kimi fail-open |
setup/install surface |
config parser + real hook receipt |
| identity + graph path |
NEO_AGENT_IDENTITY, NEO_MEMORY_DB_PATH / shared resolver |
inherit the seat identity and write the canonical local graph |
no secret fallback |
hook-wiring docs |
exact node properties |
who_is_online corroboration |
WakeSubscriptionService |
rescue add-memory-stale Iris only while a fresh turn is active |
primary recency verdict remains |
Memory Core docs |
verbose projection proof |
Acceptance Criteria
Out of Scope
- The inbound wake adapter (#15579 — the complementary direction).
- Blockable uses of
Stop (turn-continuation discipline) — a separate governance question, not presence.
- OpenCode-side turn-granular event coverage (session./message./tool.* exist; per-turn command events are the gap — belongs to the OpenCode wake/adapter line).
- Redefining the
who_is_online projection (add_memory recency stays primary).
Related
#15579 (wake adapter sibling) · #15582 (Iris active) · Euclid's intake comment-5017038956 (the narrowing this body adopts) · .claude/hooks/turnPresenceHook.mjs + .codex/hooks/codex-context.mjs (adapter siblings) · TurnPresenceHookWriter.mjs · #15405 (wake policy) · seat-pointers: Iris harness experiment + harness-diff lane.
Origin Session ID: 5a4ad6f9-843f-49d6-aaa1-918a0e476349
Retrieval Hint: kimi code turn-presence hook adapter UserPromptSubmit Stop TurnPresenceHookWriter who_is_online corroboration harness-diff
Context
Sibling of #15579 (the inbound wake path). Kimi Code CLI ships a first-class lifecycle hook system (
[[hooks]]inconfig.toml) whose event vocabulary covers per-turn boundaries — the substrate the swarm's local turn-presence corroboration layer consumes. The second Kimi seat (@neo-kimi-iris, active per #15582) currently feeds nothing into it. This is also the harness-ablation's first concrete evidence point, stated at its exact boundary (Euclid's premise correction, 2026-07-19): OpenCode DOES expose plugin hooks (session., message., tool.* events), but documents NO exact equivalents to Kimi Code's per-turn command events (UserPromptSubmit,Stop,StopFailure,Interrupt) — the asymmetry is the event vocabulary's turn-granularity, not the absence of an OpenCode hook surface.Narrowed per Euclid's intake (comment-5017038956), all five corrections adopted. The premise correction that matters:
who_is_onlineis add_memory-recency primary (TurnPresenceService.mjsL7–14); a freshAGENT_TURN_PRESENCErow is the local-only mid-turn rescue when the last memory has gone stale (WakeSubscriptionService.mjsL540–550, L675–685). This ticket delivers Kimi parity for that corroboration layer — it does not redefine the projection.Live latest-open sweep: newest 20 open issues at 2026-07-19T17:51Z; no equivalent. A2A in-flight sweep: no competing claim.
The Problem
Claude and Codex seats feed the local turn-presence layer through their harness adapters (
.claude/hooks/turnPresenceHook.mjs,.codex/hooks/codex-context.mjs— both adapt payloads intorecordTurnPresenceFromHook()). The Kimi Code seat emits the same class of lifecycle events but has no adapter: when her add_memory recency goes stale mid-turn, the rescue layer has no fresh row for her, andwho_is_onlinecorroboration goes blind on exactly the seat it should see working.The Architectural Reality
ai/mcp/server/memory-core/helpers/TurnPresenceHookWriter.mjs(L228–264) performs the bounded local SQLite write without importing Neo singletons; the Claude/Codex adapters already reuserecordTurnPresenceFromHook(). The Kimi adapter is a third sibling of that contract.NEO_AGENT_IDENTITYfrom the environment — it does NOT parse the checkout.envitself. The hook command must export the seat identity (from the seat's env chain) into the adapter process; the canonical local graph path comes fromNEO_MEMORY_DB_PATH/ the shared resolver.SessionStartfires only at session start/resume — it CANNOT represent repeated turns. The per-turn map is:UserPromptSubmit→startPostToolUse→progress(optional, parity-friendly)Stop→terminal/completedStopFailure+Interrupt→terminal/abortedSessionStart/SessionEnd→ deliberately unwired in this lane; no separate session-lifecycle telemetry surface exists yetThe Fix
recordTurnPresenceFromHook().~/.kimi-code/config.toml:[[hooks]]entries forUserPromptSubmit,Stop,StopFailure,Interrupt(+ optionalPostToolUse), bounded timeout, invoking the repo adapter with the seat identity exported.SessionStart/SessionEnddeliberately remain unwired and out of scope; this lane does not invent a session-lifecycle telemetry path.who_is_online({verbose: true})shows the corroboration row for@neo-kimi-iris(live receipt — she is active per #15582, so the projection proof is achievable, not deferred).Contract Ledger
TurnPresenceHookWriter.mjs+ Claude/Codex siblingsstart/progress/terminal~/.kimi-code/config.tomlwiringNEO_AGENT_IDENTITY,NEO_MEMORY_DB_PATH/ shared resolverwho_is_onlinecorroborationWakeSubscriptionServiceAcceptance Criteria
recordTurnPresenceFromHook()(no new MCP/write path invented); focused unit mapping tests cover every mapped event + the fail-soft cases.UserPromptSubmit→start,Stop→terminal/completed,StopFailure/Interrupt→terminal/aborted(+ optionalPostToolUse→progress);SessionStart/SessionEndhave no turn-presence mapping and remain out of scope.NEO_AGENT_IDENTITYinheritance named in the wiring doc); no secrets committed; the seat config parser accepts the wiring.@neo-kimi-iris.who_is_online({verbose: true})shows the hook-sourced corroboration row for the Iris seat; the add_memory-recency primary verdict is unchanged.UserPromptSubmit/Stop/StopFailure/Interrupt; Kimi Code does.Out of Scope
Stop(turn-continuation discipline) — a separate governance question, not presence.who_is_onlineprojection (add_memory recency stays primary).Related
#15579 (wake adapter sibling) · #15582 (Iris active) · Euclid's intake comment-5017038956 (the narrowing this body adopts) ·
.claude/hooks/turnPresenceHook.mjs+.codex/hooks/codex-context.mjs(adapter siblings) ·TurnPresenceHookWriter.mjs· #15405 (wake policy) · seat-pointers: Iris harness experiment +harness-difflane.Origin Session ID: 5a4ad6f9-843f-49d6-aaa1-918a0e476349
Retrieval Hint:
kimi code turn-presence hook adapter UserPromptSubmit Stop TurnPresenceHookWriter who_is_online corroboration harness-diff