Skip to content

feat(acp): add /undo builtin and kimi/session fork, close, steer extension methods#1686

Open
zhiyuan1i wants to merge 3 commits into
MoonshotAI:mainfrom
zhiyuan1i:lizhiyuan/acp-undo-btw
Open

feat(acp): add /undo builtin and kimi/session fork, close, steer extension methods#1686
zhiyuan1i wants to merge 3 commits into
MoonshotAI:mainfrom
zhiyuan1i:lizhiyuan/acp-undo-btw

Conversation

@zhiyuan1i

@zhiyuan1i zhiyuan1i commented Jul 14, 2026

Copy link
Copy Markdown

Related Issue

Resolve #1685

Problem

See linked issue. In short: ACP clients (and hosts embedding kimi acp) cannot undo history, cannot create an ephemeral fork for btw-style side conversations, and cannot steer a pending user message into a running turn — even though the SDK/kernel already support all three (undoHistory, forkSession, steer).

What changed

/undo [count] built-in slash command (acp-adapter)

  • Advertised like the other built-ins via available_commands_update; intercepted in AcpSession.prompt through the existing slash-intent routing.
  • Drives Session.undoHistory(count) — the same RPC the TUI's /undo uses. Default count 1; malformed counts get a usage message.
  • Refused while a turn is running: the kernel's context.undo has no active-turn guard, so the adapter checks its own currentTurnId before splicing history out from under an in-flight turn.
  • Availability is pre-checked against the live context (getContext + a canUndoHistory walk mirroring the web backend) and refused up front with the kernel's own message wording — the kernel's ContextMemory.undo deletes as it walks and only throws REQUEST_INVALID when it runs out of undoable prompts, so an over-large count would otherwise partially delete history before failing. A race fallback between pre-check and kernel call surfaces the kernel message verbatim.

kimi/session/fork extension method ({ sessionId } -> { sessionId })

  • Forks via KimiHarness.forkSession and registers the fork as a first-class ACP session, so clients drive it with the normal session/prompt streaming/tool surface — no new prompt path.
  • Inherits the source ACP session's adapter-side model/thinking state; the kernel-level fork copies context/history and keeps its SESSION_FORK_ACTIVE_TURN rejection.
  • Routes file I/O through the same AcpKaos/persistence pair as session/new when the client advertises fs.readTextFile/fs.writeTextFile: the kernel gains forkSessionWithOverrides and the SDK a matching ForkSessionInput.kaos + forkSessionWithKaos passthrough, so forked sessions keep talking to the client's filesystem instead of silently falling back to the kernel's LocalKaos.

kimi/session/close extension method ({ sessionId, archive? })

  • Closes and drops a session from the adapter table. With archive: true it also archives the on-disk directory — the cleanup path for btw forks, which would otherwise accumulate forever.

kimi/session/steer extension method ({ sessionId, prompt: ContentBlock[] } -> { steered: true } | { steered: false, reason: 'no_active_turn' })

  • Injects a pending user message into the session's active turn via Session.steer — the model consumes it at the next step boundary while in-flight tool calls and subagents run on undisturbed. The content pipeline (blocks → prompt parts → image compression) is identical to session/prompt.
  • With no active turn the method resolves { steered: false, reason: 'no_active_turn' } instead of erroring, so the client can fall back to session/prompt. The gate reads the adapter-tracked currentTurnId and is best-effort (re-checked after the compression await): the v1 kernel has no idle-rejecting steer variant — with no active turn its Turn.steer launches a fresh one — so a residual RPC-hop race is documented in code. Kernel-started turns (background/cron completion) are a known blind spot; closing it needs a kernel turn-state query (follow-up).
  • Params are validated element-by-element (invalidParams on malformed blocks), matching the sessionId checks of fork/close.

node-sdk

  • rpc.archiveSession passthrough + KimiHarness.archiveSession(id) (agent-core already implements archiveSession; it just was not wired through the SDK).
  • ForkSessionInput.kaos / persistenceKaos + forkSessionWithKaos passthrough (mirrors the create/resume kaos-override chain).

Docs & release

  • docs/en/reference/kimi-acp.md and docs/zh/reference/kimi-acp.md both carry the extension-methods table (fork/close/steer) and the built-in slash-commands table.
  • Changesets: @moonshot-ai/kimi-code minor (fork/close/undo, and steer+fork-kaos) + patch (undo pre-check); @moonshot-ai/kimi-code-sdk minor.

Tests (acp-adapter 313 tests, kimi-sdk 192 tests, all passing; typecheck + oxlint clean)

  • Slash parsing: /undo 3 detected as builtin.
  • /undo / /undo 3 call undoHistory(1) / undoHistory(3) without invoking Session.prompt; malformed count yields the usage message; /undo during an in-flight turn is refused; over-large counts and compaction boundaries are refused by the pre-check with the kernel's exact wording (undoHistory not called); injection messages are skipped by the walk; a racing kernel Cannot undo error surfaces verbatim instead of the /undo failed: wrapper.
  • kimi/session/fork roundtrip over the wire: forks via the harness and registers a promptable session; with fs capabilities advertised the fork carries an AcpKaos/persistence pair and a pre-minted forkId; unknown/missing sessionIdinvalidParams (-32602). SDK-level integration test drives harness.forkSession({ kaos, persistenceKaos }) against an in-process kernel and asserts fork.id === forkId with source content copied.
  • kimi/session/close: drops the session; archive: true routes to archiveSession, default routes to closeSession.
  • kimi/session/steer: happy path converts blocks and returns { steered: true }; no active turn → { steered: false, reason: 'no_active_turn' }; a prompt.not_found kernel rejection maps to the same; missing/empty/malformed promptinvalidParams.

Known limitation (documented in code + issue): ACP-supplied mcpServers are not carried into forks — resumeSession rebuilds MCP from the on-disk config only. Follow-up if needed.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9eaf140

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@moonshot-ai/kimi-code Minor
@moonshot-ai/kimi-code-sdk Minor
@moonshot-ai/acp-adapter Patch
kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 14, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@98e739e
npx https://pkg.pr.new/@moonshot-ai/kimi-code@98e739e

commit: 98e739e

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98e739e824

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/acp-adapter/src/session.ts Outdated
}
count = parsed;
}
await this.session.undoHistory(count);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate undo count before mutating history

When /undo asks for more turns than are undoable (or crosses a compaction boundary), the core ContextMemory.undo() removes messages as it walks history and only then throws REQUEST_INVALID; the TUI and web service avoid this by checking undo availability before calling undoHistory. This ACP path only syntax-checks the number, so /undo 99 on a shorter session can delete the available conversation context while reporting a failure. Please compute the available undo count from getContext() before calling the mutating RPC.

Useful? React with 👍 / 👎.

Comment on lines +800 to +803
const fork = await this.harness.forkSession({
id: sessionId,
title: `Fork of ${sessionId}`,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve ACP filesystem routing for forks

In clients that advertise fs.readTextFile/fs.writeTextFile, session/new, session/load, and session/resume build an AcpKaos so tool file reads and writes go through the ACP client. This fork path calls KimiHarness.forkSession(), whose implementation resumes the fork without any kaos override, so prompts in the fork fall back to the server's local filesystem and miss client buffers/sandboxing. Please make the fork registration use the same ACP kaos path before exposing it as a promptable ACP session.

Useful? React with 👍 / 👎.


All methods not listed above return `methodNotFound`.

## Extension methods (`kimi/*` namespace)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the Chinese ACP docs in sync

docs/AGENTS.md requires docs/en/ and docs/zh/ pages to stay mirrored for non-changelog edits. This change adds the new kimi/session/fork, kimi/session/close, and /undo documentation only to docs/en/reference/kimi-acp.md, leaving docs/zh/reference/kimi-acp.md without the new sections, so Chinese readers get stale ACP capabilities.

Useful? React with 👍 / 👎.

@zhiyuan1i zhiyuan1i changed the title feat(acp): add /undo builtin command and kimi/session fork+close extension methods feat(acp): add /undo builtin and kimi/session fork, close, steer extension methods Jul 15, 2026
lizhiyuan added 3 commits July 19, 2026 01:26
…ck /undo availability

- kimi/session/steer: new extension method injecting a pending user
  message into the active turn (consumed at the next step boundary);
  resolves { steered: false, reason: 'no_active_turn' } instead of
  erroring when no turn is running, gated adapter-side via the tracked
  currentTurnId because the v1 kernel's steer launches a fresh turn
  rather than rejecting when idle.
- kimi/session/fork: thread the AcpKaos/persistenceKaos pair through
  the fork path (core forkSessionWithOverrides, SDK forkSessionWithKaos,
  ForkSessionInput.kaos) so forked sessions keep routing file I/O to
  the client when fs capabilities are advertised.
- /undo: pre-check undoable prompt count against the live context
  (mirroring sessionService.canUndoHistory) and refuse up front with
  the kernel's message wording, instead of the kernel partially
  deleting history before throwing REQUEST_INVALID.
@zhiyuan1i
zhiyuan1i force-pushed the lizhiyuan/acp-undo-btw branch from 871ffb0 to 9eaf140 Compare July 18, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ACP: expose undo and session fork/close (btw support)

1 participant