Skip to content

refactor(acp-adapter): boundary-inject AcpKaos and fix FS bridge semantics#395

Open
sailist wants to merge 1 commit into
MoonshotAI:mainfrom
sailist:fix/acp
Open

refactor(acp-adapter): boundary-inject AcpKaos and fix FS bridge semantics#395
sailist wants to merge 1 commit into
MoonshotAI:mainfrom
sailist:fix/acp

Conversation

@sailist
Copy link
Copy Markdown
Collaborator

@sailist sailist commented Jun 4, 2026

Summary

This PR refactors the ACP adapter to inject AcpKaos at session boundaries instead of wrapping every prompt execution with runWithKaos. It also fixes several FS bridge semantics issues to align binary/text behavior with LocalKaos, and updates the SDK session APIs to support optional id and kaos parameters.


1. Boundary-inject AcpKaos instead of per-prompt wrapping

Problem:
Previously, AcpKaos was instantiated and injected via runWithKaos on every prompt turn. This added unnecessary overhead and complexity to the prompt lifecycle.

What was done:

  • Replace per-prompt runWithKaos with boundary-injection at session creation.
  • Simplify activation and session setup in acp-adapter.

2. Fix FS bridge semantics for binary-safe reads and text consistency

Problem:
Several FS operations in the ACP bridge diverged from LocalKaos behavior:

  • readBytes went through ACP text RPC, which is unsafe for binary content.
  • readLines did not preserve \n terminators, causing mismatches with LocalKaos.
  • writeText in append mode swallowed errors too broadly.

What was done:

  • Fix readBytes to bypass ACP text RPC and read directly via the inner implementation for binary safety.
  • Fix readLines to preserve \n terminators, matching LocalKaos output.
  • Fix writeText append mode to only swallow not-found errors.

3. Filter subagent events to prevent parent prompt settlement

Problem:
Child agent events (e.g., subagent completion) could incorrectly settle the parent agent's prompt, causing premature or incorrect state transitions.

What was done:

  • Filter subagent events so child agents don't settle the parent prompt.

4. Update SDK session APIs

Problem:
The SDK's createSession and resumeSession did not allow callers to pass an existing id or a custom kaos instance.

What was done:

  • Update createSession / resumeSession signatures to accept optional id and kaos.
  • Add corresponding RPC types and client support.

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
Copy link
Copy Markdown

changeset-bot Bot commented Jun 4, 2026

🦋 Changeset detected

Latest commit: a78c954

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

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
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 4, 2026

pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@a78c954
npx https://pkg.pr.new/@moonshot-ai/kimi-code@a78c954

commit: a78c954

Copy link
Copy Markdown

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

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: f529a0735a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const session = await this.harness.createSession({
id: sessionId,
workDir: params.cwd,
...(acpKaos !== undefined ? { kaos: acpKaos } : {}),
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 Keep ACP Kaos out of session persistence

When a client advertises fs.readTextFile or fs.writeTextFile, this passes AcpKaos into the core session, so the session’s own persistence starts using the editor reverse-RPC instead of the local store. Session.writeMetadata()/readMetadata() call this.options.kaos.writeText/readText for $KIMI_HOME/.../state.json, so clients that only serve workspace/unsaved-buffer paths will reject session creation or resume, and even permissive clients are asked to edit Kimi’s internal state through ACP. The ACP bridge should be scoped to tool/workspace file access, not the session metadata Kaos.

Useful? React with 👍 / 👎.

Comment on lines +140 to +141
readBytes(path: string, n?: number): Promise<Buffer> {
return this.inner.readBytes(path, n);
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 Avoid local sniffing for ACP text reads

With ACP-backed sessions, ordinary Read calls still invoke ReadTool's this.kaos.readBytes(..., MEDIA_SNIFF_BYTES) before reading text, so this delegation sniffs the saved local file instead of the editor buffer. When the client has unsaved text for a file whose on-disk header is binary/media or otherwise different, the tool can reject or classify the file from stale disk content before the ACP readTextFile path ever gets used. Keep the text-read sniff consistent with the ACP text surface, or restrict the local byte path to media-only callers.

Useful? React with 👍 / 👎.

}
if (event.type === 'turn.ended') {
if (settled) return;
if (!isFromMainAgent(event)) return;
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 Filter child-agent updates before forwarding

This only ignores child turn.ended events; the earlier assistant.delta and tool.* branches still forward every subagent event as parent ACP session/update notifications. In a foreground Agent tool call, the child has a generated agentId and returns its result through the parent tool result, so leaving these branches unfiltered streams the subagent's private assistant text/tool cards into the user's main prompt while the parent turn is still running. Apply the same main-agent guard to user-visible update branches, not just to settlement.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

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

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: a558b64756

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +269 to +270
const msg = cur.message.toLowerCase();
if (msg.includes('not found') || msg.includes('no such file')) return true;
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 Avoid substring-matching wrapper messages for ENOENT

In append mode, readText() wraps every ACP failure with a message that includes the file path before isNotFoundError() sees it. If a non-ENOENT failure occurs for a path containing not found or no such file (or a transport/permission message uses those words), this check treats it as a missing file and proceeds to write existing = '' + data, which can mask the real failure and overwrite content instead of surfacing the error. Prefer checking the structured RequestError code or unwrapping the cause before applying any loose message fallback.

Useful? React with 👍 / 👎.

Comment on lines +711 to +715
if (event.type === 'error') {
if (settled) return;
if (!isFromMainAgent(event)) return;
if (event.code !== ErrorCodes.TURN_AGENT_BUSY) return;
settled = true;
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 Do not reject the active prompt on busy events

When a second session/prompt arrives while a turn is already running, the SDK emits this turn.agent_busy error to every listener for the session, including the listener owned by the already-active prompt. Because this branch rejects any unsettled main-agent prompt, the original in-flight prompt is rejected and unsubscribed even though its turn should continue streaming; only the new overlapping request should fail. Distinguish the prompt that caused the busy event (for example using the active turnId in event.details) before rejecting.

Useful? React with 👍 / 👎.

Comment on lines +115 to +119
const { kaos, ...resumeInput } = input;
const summary =
kaos === undefined
? await this.rpc.resumeSession({ ...resumeInput, id })
: await this.rpc.resumeSessionWithKaos({ ...resumeInput, id }, kaos);
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 Apply ACP Kaos when resuming cached sessions

The new ACP Kaos is only forwarded after the active-session cache check, so session/load or session/resume with FS capabilities cannot retrofit the ACP file bridge onto a session that is already active in this harness (for example a session created earlier without ACP FS support or by another in-process surface). In that case this method returns the cached Session before resumeSessionWithKaos is called, and all subsequent file tools keep using the old local Kaos; the previous per-prompt wrapping path did not have this active-session gap.

Useful? React with 👍 / 👎.

…ntics

- Replace per-prompt runWithKaos with boundary-injection at session creation\n- Fix readBytes to bypass ACP text RPC for binary-safe reads via inner\n- Fix readLines to preserve \n terminators matching LocalKaos\n- Fix writeText append mode to only swallow not-found errors\n- Filter subagent events so child agents don't settle the parent prompt\n- Update SDK createSession/resumeSession to accept optional id and kaos
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.

1 participant