MoonshotAI / MoonshotAI/kimi-code
ACP: internally-triggered turns (cron / background task notifications) never emit session/update notifications
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
ACP: internally-triggered turns (cron / background task notifications) never emit session/update notifications
Summary
When a turn is triggered internally by kimi-code itself — a cron task firing, or a background task completion notification — the turn executes fully inside the agent (and is persisted to the session wire records), but the connected ACP client receives no session/update notifications at all. The client only learns about these turns later, when it reconnects and session/load replays the persisted history.
User-initiated turns (via session/prompt) stream correctly and completely.
Environment
- kimi-code CLI: 0.34.0 (
kimi acp, native ACP server on the agent-core-v2 engine) - ACP client: Paseo 0.3.1 (desktop + daemon), but the behavior is client-independent — the adapter simply never sends the notifications.
- OS: macOS
Steps to reproduce
- Connect an ACP client to
kimi acpand create a session (session/new). - In that session, have the agent schedule a near-term cron task (e.g. via the
CronCreatetool), or start a background task that will finish on its own (e.g. aBash(run_in_background=true)command, or a background subagent). - Keep the ACP connection open and idle. Wait for the cron task to fire / the background task to complete.
- Observe the ACP wire: zero
session/updatenotifications during the internally-triggered turn. The agent runs the whole turn silently. - Disconnect and reconnect with
session/load: the missed turns now appear via history replay, confirming they executed and were persisted.
Evidence
- The client's daemon keeps 30-second aggregated logs of ACP stream activity. During the windows where cron/background-task turns ran (later confirmed present in the session's
wire.jsonl), the logs show zero stream activity and the agent status staysidlefor the entire period. - In the same session, user-initiated turns produce a complete notification stream (
agent_thought_chunk,agent_message_chunk,tool_call/tool_call_update, and the prompt resolves withturn_completedsemantics), so the transport and client rendering are fine. - After reconnect,
session/loadhistory replay (which per the ACP spec re-sends the session's previous turns assession/updatenotifications) displays the internally-triggered turns — proving they executed and were recorded, just never pushed live.
Root cause (code pointers)
The ACP adapter only forwards engine events while a client-initiated prompt is in flight:
AcpSession.runTurnBody(packages/acp-adapter/src/session.ts:987) creates the only live event subscription,this.session.onEvent(...)atpackages/acp-adapter/src/session.ts:1024, and unsubscribes as soon as that turn settles (unsub()onturn.endedatpackages/acp-adapter/src/session.ts:1247,:1270,:1279). There is no session-lifetime subscription.
Internally-triggered turns never pass through runTurnBody. They enqueue turns directly onto the agent loop:
- Cron fire:
SessionCronServiceImpl.deliverFire→promptService.inject(message)atpackages/agent-core-v2/src/session/cron/sessionCronServiceImpl.ts:445(missed-fire catch-up path:handleMissed→injectat:383). - Background task terminal notification:
TaskService.notifyAgentTaskenqueues aTaskNotificationStepRequestonto the loop atpackages/agent-core-v2/src/agent/task/taskService.ts:1001-1010. - Both go through
IAgentPromptService.inject(packages/agent-core-v2/src/agent/prompt/promptService.ts:150), which runs the turn and emits the usual session events — but with no ACP subscriber attached, every event is dropped on the floor as far as the client is concerned.
Catch-up path (working as designed, but the only thing that saves the client today):
AcpServer.loadSession(packages/acp-adapter/src/server.ts:448) →AcpSession.replayHistory()(packages/acp-adapter/src/session.ts:593) replays persisted wire history assession/updatenotifications, which matches the ACPsession/loadsemantics ("replay the session's previous turns").
Expected behavior
When an ACP client is connected to a session, internally-triggered turns should stream their events as session/update notifications just like client-initiated turns do — i.e. the adapter should hold a session-lifetime event subscription (or otherwise forward turn activity) independent of an in-flight session/prompt request. At minimum, the client should be notified that a new turn started/finished so it can refresh, instead of observing a permanently idle agent while work is happening.
Impact
Any ACP client that supervises long-running kimi sessions (dashboards, mobile companions, multi-agent orchestrators like Paseo) shows a stale, idle session while the agent is in fact working on scheduled or delegated work. Users only discover the activity after a manual reconnect.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/acp-adapter/src/session.ts at AcpSession.runTurnBody and its onEvent subscription, then trace the cron and task paths through sessionCronServiceImpl.ts, taskService.ts, and promptService.ts. Verify behavior with an open ACP session: internally triggered turns should emit live session/update notifications, while session/load history replay remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 66/100