Skip to content

feat(tui): ask for workspace trust on startup with the v2 engine - #2453

Merged
liruifengv merged 2 commits into
mainfrom
feat/workspace-trust-prompt
Jul 31, 2026
Merged

feat(tui): ask for workspace trust on startup with the v2 engine#2453
liruifengv merged 2 commits into
mainfrom
feat/workspace-trust-prompt

Conversation

@liruifengv

Copy link
Copy Markdown
Collaborator

Related Issue

Related: #373 — this PR delivers the mechanism on the experimental v2 engine (KIMI_CODE_EXPERIMENTAL_FLAG); the default v1 engine has no trust concept, so the issue may be worth keeping open until trust reaches the default surface.

Problem

agent-core-v2 already enforces workspace trust: project-level MCP servers (.mcp.json, .kimi-code/mcp.json) only load in trusted workspaces, with the marker persisted under the kimi home (file presence = trusted). But the engine deliberately ships no interactive prompt, and the TUI — which bootstraps the v2 engine in-process — had no trust surface at all. On the v2 engine, a TUI session in an unfamiliar folder therefore ran silently with project MCP servers disabled, with no way to grant trust from the CLI (the only existing surface is kap-server's REST routes, consumed by kimi web / kimi-inspect).

What changed

node-sdk — new trust surface on the SDK facade:

  • getWorkspaceTrustInfo(workDir) returns { trusted, gatedMcpServers }: the trust state plus the names of project-level MCP servers that trusting would enable, diffed through the engine's own workspaceMcpConfig loader. Best-effort: an unreadable/invalid project file degrades to an empty list instead of failing the caller.
  • trustWorkspace(workDir) grants trust. The flip fires IWorkspaceTrust.onDidChange, which makes the engine's workspaceMcpConfig reload with project files included — project MCP servers connect live, no restart needed.
  • The v1 client inherits base-class defaults (always trusted; trust is a no-op) since the v1 engine has no trust concept. The v2 client composes both methods from the engine via the engineAccessor escape hatch (IWorkspaceLifecycleService.handlerFor({ root })IWorkspaceTrust), the same path createSession already takes.

TUI — on startup with the v2 engine, before any session is created:

  • If the cwd workspace is untrusted, a "Trust this folder?" dialog appears, listing the project MCP servers that trusting would enable (when any) — the informed-consent detail folder-trust dialogs typically omit.
  • Trust this folder (default) grants trust and continues startup. Don't trust / Esc exits the program — Claude Code's "No, exit" semantics. The engine's untrusted state is indistinguishable from never-asked, so the prompt reappears on the next launch.
  • Never shown on the v1 engine, in print mode (kimi -p never reaches the TUI), in the migration branch, or when the workspace is already trusted. The trust check itself is best-effort and never blocks startup.

Also fixed along the way: the prompt path initially started the pi-tui event loop twice, double-pushing the terminal's Kitty keyboard-protocol flags (one stack entry survived exit and the user's shell then received CSI-u sequences as 9;1:3u… garbage); the loop now starts exactly once, and the decline path skips the editor restore so no orphaned input box lingers above the exit message.

Tests: v2-client trust tests in node-sdk (untrusted + gated-server enumeration across both project files, invalid .mcp.json degradation, marker persisted in the kimi home); TrustPromptComponent render/keyboard tests. Verified end-to-end under a PTY: the prompt renders with the server list → choosing trust persists the marker and the project MCP server connects in the same session → relaunch shows no prompt → Esc exits cleanly before any session is created.

Checklist

@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0fe3f7d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 31, 2026

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

commit: 0fe3f7d

@liruifengv
liruifengv merged commit 32d693f into main Jul 31, 2026
15 checks passed
@liruifengv
liruifengv deleted the feat/workspace-trust-prompt branch July 31, 2026 06:21

@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: 0fe3f7d020

ℹ️ 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 +601 to +602
const gatedMcpServers = Object.keys(withProject)
.filter((name) => !(name in userOnly))

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 Disclose same-name project MCP overrides

When a project-level .mcp.json defines a server with the same name as a user-global MCP server, loadMcpServers(includeProject: true) uses the project definition because the loader merges user < project, but this filter suppresses that name simply because it exists in userOnly. In that case the trust prompt can show no gated server even though choosing trust replaces a trusted/global server with a repo-provided command and connects it, so the consent prompt misses exactly the project code being enabled. Compare the effective configs or track project-origin names instead of filtering only by key presence.

AGENTS.md reference: AGENTS.md:L22-L22

Useful? React with 👍 / 👎.

Comment on lines +81 to +83
const notice =
this.opts.gatedMcpServers.length > 0
? `Kimi Code loads project-level MCP servers (.mcp.json, .kimi-code/mcp.json) only in trusted folders. They run as local processes on your machine. This folder defines: ${this.opts.gatedMcpServers.join(', ')}.`

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 Sanitize MCP names before rendering trust prompt

For an untrusted repo, the server names come directly from project-controlled .mcp.json files and are interpolated into the terminal output before the user has granted trust. wrapTextWithAnsi/theme coloring preserves embedded ESC/OSC bytes, and this codebase already notes that pi-tui renders strings straight to the terminal, so a malicious server name can emit terminal control sequences from the trust dialog itself. Strip control sequences (or otherwise render names as inert text) before joining them into this notice.

AGENTS.md reference: AGENTS.md:L22-L22

Useful? React with 👍 / 👎.

7723qqq added a commit to 7723qqq/kimi-code that referenced this pull request Aug 3, 2026
… secondary model, workspace trust

- A6 (MoonshotAI#2210): TurnResult.hit_step_cap + render_step_capped_continuation; goal
  pursuit continues with a step-capped prompt when a turn hits the per-turn
  step limit (2 tests)
- A12 (MoonshotAI#2232): secondary-model config surface — [secondary_model] section +
  KIMI_SECONDARY_MODEL/EFFORT behind the experimental gate; Task/AgentSwarm
  subagents bind the resolved model (4 tests)
- A15 (MoonshotAI#2379): TaskOutput non-blocking — native interceptor ignores
  block/timeout; prompt text no longer advertises block=false
- C6 (MoonshotAI#2453): workspace trust — McpConnectionState.workspace_trusted drives
  the .mcp.json stdio approval gate; session/create workspace_trusted RPC +
  rust-loop wiring (2 tests)
- gen:wire regenerated (workspace_trusted landed in wire.gen.ts)
- 2011 lib + 51 integration tests green, 0 warnings
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