Dev/knowledge plane - #2606
Conversation
| else: | ||
| project_env_file = Path(str(work_dir)) / ".env" | ||
| project_env = load_dotenv_values(project_env_file if project_env_file.is_file() else None) | ||
| if project_config := project_env.get("KIMI_CONFIG_FILE"): | ||
| config_path = Path(project_config).expanduser() | ||
| if not config_path.is_absolute(): | ||
| config_path = Path(str(work_dir)) / config_path | ||
| config_path = config_path.resolve(strict=False) | ||
| if not config_path.is_file(): | ||
| raise typer.BadParameter( | ||
| f"Project config file not found: {config_path}", param_hint="KIMI_CONFIG_FILE" | ||
| ) | ||
| config = config_path |
There was a problem hiding this comment.
🟡 Custom env file passed on the command line is ignored when it points at a project config file
The config-file setting from a user-supplied env file is never read (load_dotenv_values(project_env_file ...) at src/kimi_cli/cli/__init__.py:566-567), because only the working directory's .env is consulted, so a project chosen with the command-line env option silently starts with the wrong configuration file.
Impact: Users who point the CLI at a non-default env file get the default (or another project's) configuration instead of the one they asked for, with no warning.
Why the option is skipped during config resolution
--env-file is accepted by the callback (src/kimi_cli/cli/__init__.py:199-209) and forwarded to KimiCLI.create (src/kimi_cli/cli/__init__.py:691), where it drives LLM env overrides (src/kimi_cli/app.py:263-268). However the KIMI_CONFIG_FILE lookup added at src/kimi_cli/cli/__init__.py:565-577 hard-codes Path(str(work_dir)) / ".env" and never considers env_file. Consequently, with --env-file custom.env, LLM settings come from custom.env while KIMI_CONFIG_FILE is read from <work-dir>/.env (or nowhere), which contradicts the documented behavior in docs/zh/configuration/env-vars.md:5-13 ("也可通过 --env-file PATH 指定其他文件").
| else: | |
| project_env_file = Path(str(work_dir)) / ".env" | |
| project_env = load_dotenv_values(project_env_file if project_env_file.is_file() else None) | |
| if project_config := project_env.get("KIMI_CONFIG_FILE"): | |
| config_path = Path(project_config).expanduser() | |
| if not config_path.is_absolute(): | |
| config_path = Path(str(work_dir)) / config_path | |
| config_path = config_path.resolve(strict=False) | |
| if not config_path.is_file(): | |
| raise typer.BadParameter( | |
| f"Project config file not found: {config_path}", param_hint="KIMI_CONFIG_FILE" | |
| ) | |
| config = config_path | |
| else: | |
| default_env_file = Path(str(work_dir)) / ".env" | |
| selected_env_file = env_file or (default_env_file if default_env_file.is_file() else None) | |
| project_env = load_dotenv_values(selected_env_file) | |
| if project_config := project_env.get("KIMI_CONFIG_FILE"): | |
| config_path = Path(project_config).expanduser() | |
| if not config_path.is_absolute(): | |
| config_path = Path(str(work_dir)) / config_path | |
| config_path = config_path.resolve(strict=False) | |
| if not config_path.is_file(): | |
| raise typer.BadParameter( | |
| f"Project config file not found: {config_path}", param_hint="KIMI_CONFIG_FILE" | |
| ) | |
| config = config_path |
Was this helpful? React with 👍 or 👎 to provide feedback.
| env: Mapping[str, str] | None = None, | ||
| ) -> LLM | None: | ||
| env = os.environ if env is None else env |
There was a problem hiding this comment.
🟡 Model tuning values from the project environment file are dropped for helper agents and model switches
Model tuning values are read only from the environment map handed to the main model builder (create_llm(..., env=...) at src/kimi_cli/llm.py:336-338), while other places that build a model do not pass it, so settings from a project environment file stop applying as soon as a helper agent or a model switch creates a new model.
Impact: Temperature, top-p, max-token and thinking-keep settings defined per project silently revert to whatever the shell environment has for subagents and after switching models.
Call sites that build an LLM without the project env map
create_llm reads KIMI_MODEL_TEMPERATURE, KIMI_MODEL_TOP_P, KIMI_MODEL_MAX_COMPLETION_TOKENS / KIMI_MODEL_MAX_TOKENS (src/kimi_cli/llm.py:368-388) and KIMI_MODEL_THINKING_KEEP (src/kimi_cli/llm.py:496) from the env mapping, defaulting to os.environ. Only KimiCLI.create passes the dotenv-overlaid mapping (src/kimi_cli/app.py:285-292). Other builders — clone_llm_with_model_alias used for subagent model overrides (src/kimi_cli/llm.py:528-534, src/kimi_cli/subagents/builder.py:20-26) and the ACP model-switch handler (src/kimi_cli/acp/server.py:357-363) — omit env, so they fall back to os.environ, which by design never contains the project .env values. Provider base_url/api_key survive because augment_provider_with_env_vars mutates the shared config objects in place, but the generation parameters do not.
Prompt for agents
Project-local dotenv values are only visible to the single create_llm() call in KimiCLI.create (src/kimi_cli/app.py). Other LLM construction paths — clone_llm_with_model_alias() (src/kimi_cli/llm.py), used by SubagentBuilder for per-subagent model overrides, and the ACP set-model handler in src/kimi_cli/acp/server.py — call create_llm() without the env mapping and therefore fall back to os.environ, losing KIMI_MODEL_TEMPERATURE / TOP_P / MAX_COMPLETION_TOKENS / THINKING_KEEP overrides from the project .env. Consider storing the resolved llm_env mapping on the Runtime (or another long-lived object) when KimiCLI.create builds it, and threading it through clone_llm_with_model_alias and the ACP model-switch path so all LLM instances in the process observe the same environment.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
|
||
| **生产影响:** 长期项目中,Agent 会重复探索仓库、重复询问约束,并可能做出与历史决策冲突的修改。直接把全部历史会话塞入上下文又会增加 Token 成本、隐私暴露和错误召回风险,因此生产实现必须包含作用域、来源、过期、删除和可解释引用,而不只是增加一个数据库。 | ||
|
|
||
| **代码证据:** [`Session.dir`](../../src/kimi_cli/session.py#L48) 将数据限定在当前会话目录,[`Session.find`](../../src/kimi_cli/session.py#L183) 也要求使用明确的 `session_id` 恢复;[`SubagentStore.root`](../../src/kimi_cli/subagents/store.py#L68) 位于 `session.dir/subagents`。这些代码证明已有持久化,但其边界仍是单个会话。 |
There was a problem hiding this comment.
🟡 Internal planning and résumé documents dropped into the docs site directory break the documentation build
Ten Chinese planning/résumé documents were added inside the documentation site tree (docs/mk/Kimi Code CLI核心短板与秋招项目评估.md:21 and siblings) instead of the required docs/zh / docs/en structure, and they link to source files outside the site, so the documentation build fails and unrelated personal content would be published.
Impact: The docs site no longer builds, and if it did it would ship internal career-planning material to end users.
Why VitePress rejects these files
docs/AGENTS.md requires every page to live under docs/en/ or docs/zh/ with mirrored paths and to be wired into nav/sidebar in docs/.vitepress/config.ts. The new files live in docs/mk/ and docs/dev_note/ (plus a raw docs/mk/resume-kimi-code-cli.html) and are not registered anywhere. docs/.vitepress/config.ts sets no srcExclude, so VitePress picks up every .md under docs/ as a page. Those pages contain relative links to repository source such as [\Session.dir`](../../src/kimi_cli/session.py#L48)and`collect_git_context`; VitePress dead-link checking treats these as internal links to non-existent pages and fails npm run build` by default.
Prompt for agents
The PR adds planning/résumé documents under docs/mk/ and docs/dev_note/ (including a raw HTML file). docs/AGENTS.md mandates that documentation pages live under docs/en/ and docs/zh/ with mirrored slugs and be registered in docs/.vitepress/config.ts. Because docs/.vitepress/config.ts defines no srcExclude, every markdown file under docs/ becomes a site page, and these files link to repository sources with relative paths such as ../../src/kimi_cli/session.py#L48, which VitePress dead-link checking rejects, failing npm run build. Move this material out of the docs site (for example to a non-published directory or drop it from the PR), or, if it must stay, exclude it from the VitePress build and remove the links to non-page targets.
Was this helpful? React with 👍 or 👎 to provide feedback.
| @staticmethod | ||
| async def create( # Agent启动的入口 |
There was a problem hiding this comment.
🟡 Stray whitespace in the application startup file makes the repository's formatting check fail
A trailing space was left on the decorator line above the startup entry point (@staticmethod at src/kimi_cli/app.py:139), so the mandatory formatting check reports the file as unformatted and the commit hooks/CI stop.
Impact: Contributors running the standard check or committing with hooks enabled get a failure unrelated to their own work.
Formatting rules involved
make check-kimi-cli runs uv run ruff format --check (see Makefile:67-71), and prek hooks run make format-kimi-cli / make check-kimi-cli per AGENTS.md and CONTRIBUTING.md. ruff format strips trailing whitespace, so src/kimi_cli/app.py:139 ( @staticmethod with a trailing space) plus the inline comment placement on line 140 make the file differ from formatted output. Similar single-blank-line separations before module-level decorators were introduced in src/kimi_cli/cli/__init__.py (e.g. the comment banner directly above @cli.command() around line 978), which the formatter also rewrites. Running make format resolves all of these.
| @staticmethod | |
| async def create( # Agent启动的入口 | |
| @staticmethod | |
| async def create( # Agent启动的入口 |
Was this helpful? React with 👍 or 👎 to provide feedback.
| default_env_file = Path(str(session.work_dir)) / ".env" | ||
| selected_env_file = env_file or (default_env_file if default_env_file.is_file() else None) | ||
| llm_env: Mapping[str, str] = load_llm_env(selected_env_file) | ||
| if selected_env_file is not None: | ||
| logger.info("Loaded local LLM environment from: {file}", file=selected_env_file) | ||
| env_overrides = augment_provider_with_env_vars(provider, model, llm_env) |
There was a problem hiding this comment.
🟨 Project-local .env credentials are auto-loaded from the working directory without opt-in
KimiCLI.create now silently discovers and applies <work-dir>/.env (src/kimi_cli/app.py:263-268), letting any repository the user opens override KIMI_BASE_URL, KIMI_API_KEY and the config file used (KIMI_CONFIG_FILE handling at src/kimi_cli/cli/__init__.py:565-577). Simply running kimi inside an untrusted clone that ships a .env redirects model traffic (including prompts and file contents) to an attacker-controlled base_url, or replaces the whole configuration with an attacker-supplied TOML/JSON file. The resolved API key is additionally propagated into globally persisted plugin config files via collect_host_values / refresh_plugin_configs (src/kimi_cli/app.py:332-334), so a project-scoped credential can be written outside the project.
Was this helpful? React with 👍 or 👎 to provide feedback.
Related Issue
Resolve #(issue_number)
Description
Checklist
make gen-changelogto update the changelog.make gen-docsto update the user documentation.