diff --git a/.project-docs/30-worklog/current-state.md b/.project-docs/30-worklog/current-state.md
index ba84782..1cd7092 100644
--- a/.project-docs/30-worklog/current-state.md
+++ b/.project-docs/30-worklog/current-state.md
@@ -4,6 +4,23 @@ This file is the integrated default-branch snapshot. Feature tasks record progre
## Integrated Through
+- Coding project setup UX source `8042d2be265e40288e628fcdc755e9f29151306b`
+ from task `20260906-project-setup-ux-90fe6cf2` is integrated onto local `main`
+ by task `20260906-integrate-project-setup-ux-4b7d2e91`. A Coding Conversation is
+ ready only when the project is persisted as initialized and has at least one enabled,
+ unarchived project Agent with a non-empty name, avatar, role, responsibility, and
+ resolved model. Project-list entry, direct route resolution, the module gate, and the
+ Coding chat recovery surface all reuse that predicate. Project Configuration presents
+ the missing first Agent as a required step, sends users to model settings when no model
+ exists, persists the first valid Agent before entering chat, offers creation instead of
+ an unrecoverable edit for disabled-only projects, and uses labelled deterministic exit
+ actions with unsaved-change confirmation. A direct `/chat` visit without a ready Agent
+ renders a configuration recovery action without creating a Conversation/runtime or
+ exposing the Composer. Source verification passed 63 focused tests, the 1,928-test full
+ unit suite with 2 skips plus pressure, typecheck, lint with 0 errors/5 unchanged warnings,
+ all Vite targets, the targeted Electron flow, and independent review. Persistence,
+ Pi/runtime, Provider, Plugin, package, deployment, publication, and remote state are
+ unchanged.
- Token Point V2 account-usage source `6348e402a4e8dde712e5cba4620bb883ffe24e0b`
from task `20260906-trace-makelore-usage-3d7a5c1e` is integrated onto local
`main` by task `20260906-integrate-token-points-v2-7b4e1c92`. The account menu
@@ -714,7 +731,7 @@ Makelore 在会话恢复、登录和刷新后由 Electron Main 请求 Works `/ap
AI 学习现在是已启用的运营精选项目目录,并继续受登录和 `module_access.learning` 控制。Renderer 通过 Main-owned Host API 获取分页项目卡片和 README 详情;Markdown 支持 GFM、禁用原始 HTML。服务端发布时只校验图片 URL 为无凭据、默认端口、无 fragment 且当前 DNS 结果全部为公网地址的 HTTPS URL,保留地址而不下载、识别格式、转码或镜像;客户端仅为 README 图片节点启用直连,因此 SVG 和 Electron 支持的其他格式可直接显示,单图失败不阻断详情。封面和历史发布媒体继续走受控路径。详情页的下载按钮打开系统保存对话框;Main 将 ZIP 流式写入临时文件,只允许最多五跳同 Works origin 重定向,不校验 `Content-Length`、`archiveBytes`、实际流字节数或客户端大小上限,校验 SHA-256 和 ZIP 签名后原子保存,Renderer 只接收 `saved` 或 `cancelled`。课程生成、进度、本地课程库、OpenMAIC player、Agent、ASR、课堂 runtime、Learning IPC 和 player artifact 打包已删除且没有兼容读取路径;历史课程数据保留但不再读取。服务端和客户端源码契约已完成,不代表生产部署或真实账号安装包联调已经完成。
-AI 编程已经硬切到精确 pin 的 Pi `0.84.2`,不存在 OpenCode fallback 或双 runtime。project、Agent 与 Conversation 只在 `.makelore/project.json` 和 `.makelore/conversations.json` 使用本地 schema v2;当前客户端不从 `.niancode` 或 `.opencode` 读取、迁移或删除项目元数据。Electron Main 按需启动一个长驻父 Agent Server,每条 active/warm Conversation 在其中拥有独立 Runtime/Session/channel、credential store、extension context、generation/seq、Snapshot/Patch、model/thinking、队列、interaction 与错误状态,Composer 在 lazy prepare 期间仍可编辑;正式包从 staged `pi-runtime` manifest/root 定位并校验 Pi 包入口,不从脚本目录或应用 `node_modules` 回退。Renderer 只通过 `/api/coding/*` 和 Snapshot-first/`patch-batch` SSE 消费产品中立合同;gap/reconnect 只恢复目标 Conversation,accepted/uncertain mutation 不自动重放。`lifecycle:sleep` 会关闭旧事件流,视图挂载、项目上下文变化、页面重新可见或窗口 focus 会静默刷新已选 Snapshot,使后台 terminal 状态收敛且不重放 mutation。隐藏 Conversation 的红点只在新 pending interaction 或新 completed/failed/aborted terminal transition 出现,不由助手/thinking/工具过程或单个工具失败触发。Session hydration 沿完整 active branch 投影可见历史,Pi compaction 只改变模型上下文并保持 summary 私有;Renderer 首次挂载最近 120 个节点,向上滚动时按 100 个节点追加更早内容并补偿新增高度以保持阅读锚点。折叠的 thinking、助手过程说明与工具输出固定展示第一条可见内容和首个非空行,横向偏移保持为零,展开后仍显示完整内容。未解析 Conversation 第一次选模先 validate 并持久化 resolved metadata,再 prepare;同账号模型切换使用 target `set_model`,跨账号只重建目标逻辑线程。Web Search 只作为所选模型 capability 进入父 worker 并使用相同 model/provider/credential 与普通模型计费,不经过 Marketplace/Hosted client/Plugin Charge 或浏览器 fallback;child 不继承。Conversation 工具可检查并在独立确认后安装 npm、Git、本地 Plugin 目录或 loose `SKILL.md` 为 Main-owned immutable Device Package;不运行生命周期脚本,可执行 extension 具有桌面用户权限,新/idle parent 自动刷新,active parent 结算后刷新,child 为空。每个 Main-selected generation 都保留全部显式安装且当前启用的 Skill 路径;生成的 Makelore bridge 是必需的首个 extension,其余 extension 全部经 `additionalExtensionPaths` 加载,并继续关闭 ambient discovery。top-level 逻辑 turn 并发为 4,warm idle logical-thread LRU 为 8;independent child 进程并发为 4 并使用 FIFO 进程预算 8;coding child 与 parent 共用项目 write lease。同一助手工具批次内,内置 `bash`/`edit`/`write` 与声明需要该 write lease 的动态产品工具按顺序执行,避免 Pi 在批量 prepare 阶段形成租约自锁;纯只读工具批次仍可并行。prompt/compact confirmation timeout 后仍保留 run/Agent-Server-or-child-process/background ownership,迟到 success/failure/exit/abort 单调且 exactly-once 收敛,页面隐藏不会停止 active/uncertain run。线程级替换只使目标 generation 失效;整个 Agent Server 退出会统一使所有旧 channel 失效,但 Main/Renderer 存活且下次恢复只重启一个 Server。Pi `0.84.2` 手动 compact 不发 `agent_settled`,由 correlated compact RPC 结果终结。父 Provider credential 只进入选中逻辑线程的内存 credential store,child 凭据只进入该短命进程;确定性的 Works user-context 缺失会失效 gateway credential、fail fast、不重放并投影固定 Provider-auth 错误,不归类为 Pi crash。精确的 `403 token_point_balance_exhausted` / `词元点数余额不足` 上游响应会在 Main 内归类为不可重试的配额耗尽,Renderer 只收到稳定 `CODING_PROVIDER_QUOTA_EXHAUSTED` 与安全中文提示;重开会话仍保留该提示,不暴露上游 request id 或原始错误正文。真实 Provider 验证仍为用户明确接受的未验证风险;macOS arm64 仅有本地未签名 mounted-image initialize/shutdown 证据,签名/公证/完整 process-enumeration、macOS x64 与 native non-WSL Linux 仍未通过平台发布门禁。
+AI 编程已经硬切到精确 pin 的 Pi `0.84.2`,不存在 OpenCode fallback 或双 runtime。project、Agent 与 Conversation 只在 `.makelore/project.json` 和 `.makelore/conversations.json` 使用本地 schema v2;当前客户端不从 `.niancode` 或 `.opencode` 读取、迁移或删除项目元数据。Coding Project 只有在 `initialized` 且至少存在一个启用、未归档、名称/头像/角色/职责/解析模型完整的项目 Agent 时才可进入 Conversation;项目入口、路由门禁、配置完成和 Chat 恢复必须复用共享判定,不能单独信任 `initialized`。Electron Main 按需启动一个长驻父 Agent Server,每条 active/warm Conversation 在其中拥有独立 Runtime/Session/channel、credential store、extension context、generation/seq、Snapshot/Patch、model/thinking、队列、interaction 与错误状态,Composer 在 lazy prepare 期间仍可编辑;正式包从 staged `pi-runtime` manifest/root 定位并校验 Pi 包入口,不从脚本目录或应用 `node_modules` 回退。Renderer 只通过 `/api/coding/*` 和 Snapshot-first/`patch-batch` SSE 消费产品中立合同;gap/reconnect 只恢复目标 Conversation,accepted/uncertain mutation 不自动重放。`lifecycle:sleep` 会关闭旧事件流,视图挂载、项目上下文变化、页面重新可见或窗口 focus 会静默刷新已选 Snapshot,使后台 terminal 状态收敛且不重放 mutation。隐藏 Conversation 的红点只在新 pending interaction 或新 completed/failed/aborted terminal transition 出现,不由助手/thinking/工具过程或单个工具失败触发。Session hydration 沿完整 active branch 投影可见历史,Pi compaction 只改变模型上下文并保持 summary 私有;Renderer 首次挂载最近 120 个节点,向上滚动时按 100 个节点追加更早内容并补偿新增高度以保持阅读锚点。折叠的 thinking、助手过程说明与工具输出固定展示第一条可见内容和首个非空行,横向偏移保持为零,展开后仍显示完整内容。未解析 Conversation 第一次选模先 validate 并持久化 resolved metadata,再 prepare;同账号模型切换使用 target `set_model`,跨账号只重建目标逻辑线程。Web Search 只作为所选模型 capability 进入父 worker 并使用相同 model/provider/credential 与普通模型计费,不经过 Marketplace/Hosted client/Plugin Charge 或浏览器 fallback;child 不继承。Conversation 工具可检查并在独立确认后安装 npm、Git、本地 Plugin 目录或 loose `SKILL.md` 为 Main-owned immutable Device Package;不运行生命周期脚本,可执行 extension 具有桌面用户权限,新/idle parent 自动刷新,active parent 结算后刷新,child 为空。每个 Main-selected generation 都保留全部显式安装且当前启用的 Skill 路径;生成的 Makelore bridge 是必需的首个 extension,其余 extension 全部经 `additionalExtensionPaths` 加载,并继续关闭 ambient discovery。top-level 逻辑 turn 并发为 4,warm idle logical-thread LRU 为 8;independent child 进程并发为 4 并使用 FIFO 进程预算 8;coding child 与 parent 共用项目 write lease。同一助手工具批次内,内置 `bash`/`edit`/`write` 与声明需要该 write lease 的动态产品工具按顺序执行,避免 Pi 在批量 prepare 阶段形成租约自锁;纯只读工具批次仍可并行。prompt/compact confirmation timeout 后仍保留 run/Agent-Server-or-child-process/background ownership,迟到 success/failure/exit/abort 单调且 exactly-once 收敛,页面隐藏不会停止 active/uncertain run。线程级替换只使目标 generation 失效;整个 Agent Server 退出会统一使所有旧 channel 失效,但 Main/Renderer 存活且下次恢复只重启一个 Server。Pi `0.84.2` 手动 compact 不发 `agent_settled`,由 correlated compact RPC 结果终结。父 Provider credential 只进入选中逻辑线程的内存 credential store,child 凭据只进入该短命进程;确定性的 Works user-context 缺失会失效 gateway credential、fail fast、不重放并投影固定 Provider-auth 错误,不归类为 Pi crash。精确的 `403 token_point_balance_exhausted` / `词元点数余额不足` 上游响应会在 Main 内归类为不可重试的配额耗尽,Renderer 只收到稳定 `CODING_PROVIDER_QUOTA_EXHAUSTED` 与安全中文提示;重开会话仍保留该提示,不暴露上游 request id 或原始错误正文。真实 Provider 验证仍为用户明确接受的未验证风险;macOS arm64 仅有本地未签名 mounted-image initialize/shutdown 证据,签名/公证/完整 process-enumeration、macOS x64 与 native non-WSL Linux 仍未通过平台发布门禁。
Updater 仍由 Electron Main 选择目标 feed、记录原始诊断并保持失败语义。正式稳定源缺少对应平台 manifest 时,设置页只显示一条简洁中文提示并允许重试,不把缺包误报为已是最新版,也不向普通界面暴露堆栈、URL、路径或错误码;签名产物发布和真实升级安装仍属于外部 Release Gate。
@@ -855,4 +872,4 @@ Robot 绑定设备默认先显示“引导配网 / 已有激活码”路径选
## Last Updated
-2026-09-05
+2026-09-06
diff --git a/.project-docs/30-worklog/tasks/20260906-integrate-project-setup-ux-4b7d2e91.md b/.project-docs/30-worklog/tasks/20260906-integrate-project-setup-ux-4b7d2e91.md
new file mode 100644
index 0000000..5a082b4
--- /dev/null
+++ b/.project-docs/30-worklog/tasks/20260906-integrate-project-setup-ux-4b7d2e91.md
@@ -0,0 +1,65 @@
+# Task: Integrate project setup UX
+
+## Identity
+
+- Task ID: 20260906-integrate-project-setup-ux-4b7d2e91
+- Mode: Integration
+- Branch: main
+- Worktree: D:\Datas\OthersProjects\makelore
+- Base commit: 83ad10d95e607c10973fed543cc5fa09afdcb9f7
+- Owner: codex
+- Status: Ready for Integration
+
+## Scope
+
+- Integrate source task `20260906-project-setup-ux-90fe6cf2` at `8042d2be265e40288e628fcdc755e9f29151306b` onto `main` at `83ad10d95e607c10973fed543cc5fa09afdcb9f7`.
+- Preserve the Token Point V2 sidebar behavior already on `main` while adopting the shared Coding Project conversation-readiness gate and project-setup onboarding flow.
+- Promote the source task's accepted readiness invariant into `.project-docs/30-worklog/current-state.md`.
+- Verify the exact merged tree and leave the three pre-existing adopted task records unchanged and outside the integration commit.
+
+## Intent And Constraints
+
+- Use a normal non-history-rewriting merge; do not squash, rebase, force-push, package, deploy, or push remotely.
+- Treat the source task record as read-only source evidence until it is intentionally adopted as part of the merge commit.
+- Preserve the accepted `.makelore/project.json` authority, Pi runtime structure, Main-owned boundaries, and single light visual system.
+- Do not modify or commit the adopted records `20260901-package-122-c5e8.md`, `20260901-package-123-d7f3.md`, or `20260902-client-hang-diagnosis-a47c9e2b.md`.
+- Re-run focused onboarding/readiness tests, typecheck, lint, Renderer build, the relevant Electron E2E, and the full unit suite because the source and destination both modify `Sidebar.tsx`.
+
+## Outcome
+
+- Merged `codex/20260906-project-setup-ux-90fe6cf2-project-setup-ux` into the
+ current `main` with `--no-commit --no-ff`; Git reconciled the shared
+ `src/components/layout/Sidebar.tsx` change without conflicts.
+- Preserved the Token Point V2 version of Sidebar and added only the shared Coding
+ Project conversation-readiness import and navigation predicate from the source.
+- Integrated the shared readiness contract, required first-Agent setup card,
+ deterministic configuration exits, and Coding chat recovery state together with
+ their focused unit and Electron E2E coverage.
+- Registered the unchanged source task record as an adopted integration input and
+ promoted its accepted readiness invariant into canonical `current-state.md`.
+- Kept the three pre-existing adopted task records unstaged and unchanged.
+
+## Verification
+
+- Focused Vitest passed 5 files / 63 tests for readiness, App and MainLayout gates,
+ Project Configuration onboarding, and Coding chat recovery.
+- Full `pnpm test` passed 231 files / 1,936 tests with 2 skipped; the separately
+ serialized Coding chat pressure test passed 1/1.
+- `pnpm run typecheck` passed.
+- `pnpm run lint:check` passed with 0 errors and the 5 unchanged warnings in
+ `src/pages/Home/index.tsx` and `src/pages/Makelore/index.tsx`.
+- `pnpm run build:vite` passed for Renderer, Electron Main, preload, and utility
+ bundles; only the repository's existing Browserslist, dynamic-import, and chunk-size
+ advisories were reported.
+- Electron E2E `tests/e2e/project-configuration-skills.spec.ts` passed 1/1.
+- `check_project_docs.py`, task-aware `check_doc_drift.py`, the unchanged source-task
+ record comparison, and `git diff --check` all passed before the merge commit.
+
+## Follow-ups
+
+- None recorded.
+
+## Promotion Candidates
+
+- None. This Integration Gate applies the accepted source promotion directly to
+ `.project-docs/30-worklog/current-state.md`.
diff --git a/.project-docs/30-worklog/tasks/20260906-project-setup-ux-90fe6cf2.md b/.project-docs/30-worklog/tasks/20260906-project-setup-ux-90fe6cf2.md
new file mode 100644
index 0000000..bb30484
--- /dev/null
+++ b/.project-docs/30-worklog/tasks/20260906-project-setup-ux-90fe6cf2.md
@@ -0,0 +1,106 @@
+# Task: Review new project setup UX
+
+## Identity
+
+- Task ID: 20260906-project-setup-ux-90fe6cf2
+- Mode: Feature
+- Branch: codex/20260906-project-setup-ux-90fe6cf2-project-setup-ux
+- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260906-project-setup-ux-90fe6cf2
+- Base commit: 5c61110f465cc4172f712ad435c06041c0aed1ec
+- Owner: codex
+- Status: Ready for Integration
+
+## Scope
+
+- Review the new-project transition from Project Configuration to the first Coding
+ Conversation, with particular attention to the zero-Agent state and the discoverability
+ of the return/continue action.
+- Verify the persisted readiness rule, route behavior, empty states, model prerequisite,
+ and current product terminology before recommending an interaction change.
+- Implement the accepted interaction direction with one shared Conversation-readiness
+ rule, a strong first-Agent setup path, deterministic navigation, and a recoverable
+ zero-Agent Conversation state.
+- Add focused unit and Electron E2E coverage while keeping persistence, Pi runtime,
+ Provider, Plugin, and Main/Renderer contracts unchanged.
+
+## Intent And Constraints
+
+- Preserve the explicit product decision that new projects have no default project Agent
+ and that the user chooses the Agent identity, model, responsibility, and Skills.
+- Keep project-owned parent Agents distinct from short-lived runtime child Agents; do not
+ describe the Project Configuration entity as a child Agent.
+- Preserve Pi `0.84.2`, `.makelore` ownership, the single light visual system, and the
+ current Main/Renderer boundary.
+- Prefer one explicit first-run completion path over a forced tutorial, compatibility
+ layer, new state machine, or automatically fabricated default Agent.
+
+## Outcome
+
+- Confirmed that the screenshot's “项目智能体” is the persisted project-owned parent
+ Agent, not an ephemeral runtime child Agent, and retained that terminology throughout.
+- Added one shared Conversation-readiness rule: `initialized` must be true and the project
+ must contain at least one enabled, unarchived Agent with a non-empty name, avatar, role,
+ responsibility, and resolved model. Project list entry, direct `/chat` resolution,
+ the programming-module gate, and Coding chat now use the same rule instead of trusting
+ `initialized` alone.
+- Reworked the zero-ready-Agent configuration state into a prominent required-step card.
+ With no model it offers “先配置模型” and a direct model-settings action; otherwise it
+ creates the first Agent or resumes an enabled incomplete Agent. Disabled-only state
+ intentionally offers a new Agent because the edit dialog has no enable control.
+- The first Agent is persisted immediately and then navigates to `/chat`. Later Agent
+ additions and edits retain the existing page-draft plus explicit-save behavior.
+- Replaced history-relative back navigation with labelled, deterministic “返回对话” or
+ “稍后设置” destinations. Unsaved Agent changes are confirmed before any page exit,
+ including the model-settings prerequisite path.
+- Archiving the last ready Agent now explains that saving will return the project to a
+ pending state; saving that state clears `initialized` and leaves the unusable chat route.
+- Added a full Coding chat recovery state with a direct project-configuration action and no
+ Composer or Conversation/runtime creation when no usable Agent exists.
+- Updated README with the accepted readiness and onboarding behavior.
+
+## Verification
+
+- Focused Vitest: 5 files, 63 tests passed, covering readiness, App routing, MainLayout
+ gating, Project Configuration onboarding/navigation, and Coding chat recovery.
+- Full `pnpm test`: 230 files / 1928 tests passed with 2 skipped, followed by the isolated
+ pressure test passing 1/1.
+- `pnpm run typecheck`: passed after the final interaction changes.
+- `pnpm run lint:check`: passed with 0 errors; it retained 5 pre-existing warnings in
+ `src/pages/Home/index.tsx` and `src/pages/Makelore/index.tsx`.
+- `pnpm run build:vite`: passed after the final interaction changes; only the repository's
+ existing Browserslist, dynamic-import, and chunk-size warnings were emitted.
+- Electron E2E `project-configuration-skills.spec.ts`: passed 1/1, including zero-model
+ guidance, legacy `initialized: true` plus zero-Agent recovery, and labelled return to chat.
+- Playwright trace inspection showed an initial E2E failure was caused by that spec's later
+ restrictive Host API stub, not product navigation; the navigation assertion now runs
+ before the stub and passes.
+- Independent read-only review found the disabled-only recovery loop; after the targeted
+ correction and test, the reviewer returned PASS.
+- `git diff --check`: passed.
+- With explicit human approval, rebuilt the unpushed `main` merge from a safety
+ checkpoint and adopted the exact unchanged state of
+ `.project-docs/30-worklog/tasks/20260906-agent-browser-failure-a7c91e4d.md`; the rebuilt
+ feature tree matches the checkpoint tree exactly.
+- `check_project_docs.py` and `check_doc_drift.py --task-id
+ 20260906-project-setup-ux-90fe6cf2` both passed after recovery.
+
+## Follow-ups
+
+- No required implementation follow-up remains.
+
+## Promotion Candidates
+
+- Target: `.project-docs/30-worklog/current-state.md`.
+ Proposal: record that Coding Conversation readiness requires both persisted
+ initialization and at least one enabled, unarchived, fully configured project Agent;
+ all project-entry and chat-recovery surfaces share this invariant, and the first Agent
+ setup is the explicit completion path.
+ Evidence: shared readiness tests, Project Configuration onboarding tests, App/MainLayout
+ route tests, Coding chat recovery tests, Electron E2E, successful full unit suite,
+ typecheck, lint, production build, and independent review.
+ Future impact: future project-entry, archival, or Agent-edit work must reuse the shared
+ predicate rather than treating `initialized` as sufficient on its own.
+ Semantic conflicts: none known; the rule preserves the existing no-default-Agent and
+ project-owned Agent decisions.
+ Human confirmation required: no, unless integration discovers a conflicting accepted
+ product decision.
diff --git a/README.md b/README.md
index 2e441ef..d7c2327 100644
--- a/README.md
+++ b/README.md
@@ -126,7 +126,8 @@ Pi 正式包必须继续运行 `pnpm run verify:artifact:pi`、`pnpm run smoke:p
- 新项目默认没有智能体;用户在项目内创建智能体时必须填写名称、预设头像或本地头像、职责说明和精确的 Provider 账号与默认模型。该选择以 `{ accountId, modelId, thinkingLevel }` 保存,不依赖运行时私有 Provider id。本地头像会自动裁剪为 256×256,并优先压缩为 WebP 后随项目配置保存;系统指令与技能属于高级设置。
- Makelore 为每个项目智能体附加平台管理的中文语言规则:自然语言默认使用简体中文,包括可见的思考过程、进度说明、工具调用说明和最终回复;代码、命令、路径、日志、错误原文与必要引用保留原始语言。用户或项目智能体系统指令明确要求其他语言时按该要求执行;模型仍不遵循时视为 Provider 模型行为。
-- 项目配置页的新增与已有智能体编辑统一使用居中弹窗;编辑保存先更新当前页面草稿,点击底部“保存项目配置”后统一持久化。“可用模型”抽屉只展示已配置模型、提供方和文本/多模态能力,不提供项目级选择。智能体的默认模型只作为新对话的初始值;核心聊天页可为当前对话独立切换模型和思考强度,切换不会改写智能体默认值或其他对话。
+- 新项目可以暂时保留为待配置状态,但只有至少一个已启用、未归档且名称、头像、默认模型、角色与职责完整的项目智能体时,项目才可进入对话;旧配置即使错误保留了 `initialized: true`,也会回到项目配置完成这一步。配置页用顶部必需步骤和主操作强引导创建智能体,无模型时直接引导到模型设置;左上角提供带文字的确定性返回操作。聊天页遇到无可用智能体时显示可恢复空态,不展示不可用的输入框。
+- 项目配置页的新增与已有智能体编辑统一使用居中弹窗;首个智能体创建成功后会立即持久化并进入项目对话,后续新增与编辑先更新当前页面草稿,再点击底部“保存项目配置”统一持久化。“可用模型”抽屉只展示已配置模型、提供方和文本/多模态能力,不提供项目级选择。智能体的默认模型只作为新对话的初始值;核心聊天页可为当前对话独立切换模型和思考强度,切换不会改写智能体默认值或其他对话。
- 项目智能体只保存自身的精确默认模型;未完成模型选择的智能体保持待配置状态,不从项目级 `defaultModel` 或运行时私有配置回退。
- “可用技能”入口使用模块图标;点击技能后先展示目录结构,再展示主文件 `SKILL.md` 原文,并支持返回技能列表。
- 一个智能体可以拥有多条互相独立的 Pi Session;智能体和对话元数据分别由 `.makelore/project.json` 与 `.makelore/conversations.json` 保存,稳定的智能体与对话 id 保持本地历史连续。
diff --git a/shared/coding-project-contracts.ts b/shared/coding-project-contracts.ts
index b2f1fce..12e9c9a 100644
--- a/shared/coding-project-contracts.ts
+++ b/shared/coding-project-contracts.ts
@@ -56,3 +56,22 @@ export interface CodingProjectConfigSnapshot {
config: CodingProjectConfig;
knowledgeFiles: string[];
}
+
+export function isCodingProjectAgentReady(agent: CodingProjectAgent): boolean {
+ return agent.enabled
+ && agent.archivedAt === null
+ && agent.name.trim().length > 0
+ && agent.avatarId.trim().length > 0
+ && agent.roleName.trim().length > 0
+ && agent.responsibility.mission.trim().length > 0
+ && agent.modelResolution === 'resolved'
+ && agent.model !== null;
+}
+
+export function hasReadyCodingProjectAgent(config: CodingProjectConfig): boolean {
+ return config.agents.some(isCodingProjectAgentReady);
+}
+
+export function isCodingProjectConversationReady(config: CodingProjectConfig): boolean {
+ return config.initialized && hasReadyCodingProjectAgent(config);
+}
diff --git a/src/App.tsx b/src/App.tsx
index c97a8d8..921dda8 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -26,6 +26,7 @@ import { subscribeHostEvent } from '@/lib/host-events';
import { reportDesktopActivity, type DesktopActivityModule } from '@/lib/host-api';
import { installRendererPerformanceDiagnostics } from '@/lib/performance-diagnostics';
import { resolveSupportedLanguage } from '../shared/language';
+import { isCodingProjectConversationReady } from '../shared/coding-project-contracts';
import { LegacyPluginRedirect } from './pages/Plugins/legacy-plugin-redirect';
const Models = lazy(() => import('./pages/Models').then(({ Models: component }) => ({ default: component })));
@@ -229,7 +230,12 @@ function ProjectChatRoute() {
const configResult = await loadProjectConfig(project.id);
if (!cancelled) {
setResolvedProjectKey(project.id);
- setResolvedRoute(configResult.status === 'valid' && Boolean(configResult.config?.initialized) ? 'chat' : 'config');
+ setResolvedRoute(
+ configResult.status === 'valid'
+ && Boolean(configResult.config && isCodingProjectConversationReady(configResult.config))
+ ? 'chat'
+ : 'config',
+ );
}
} catch {
if (!cancelled) {
diff --git a/src/components/layout/MainLayout.tsx b/src/components/layout/MainLayout.tsx
index 06c2037..b103f45 100644
--- a/src/components/layout/MainLayout.tsx
+++ b/src/components/layout/MainLayout.tsx
@@ -14,6 +14,7 @@ import { useProjectConfigStore } from '@/stores/project-config';
import { useSettingsStore } from '@/stores/settings';
import { cn } from '@/lib/utils';
import type { SidebarPeekSource } from './sidebar-peek';
+import { isCodingProjectConversationReady } from '../../../shared/coding-project-contracts';
const SIDEBAR_PEEK_CLOSE_DELAY_MS = 180;
@@ -88,7 +89,11 @@ export function MainLayout() {
if (activeProject && isProgrammingModule) void load(activeProject.id).catch(() => undefined);
}, [activeProject, isProgrammingModule, load]);
- const initializationBlocked = Boolean(activeProject && (!config || !config.initialized) && !isInitializationSafeRoute);
+ const initializationBlocked = Boolean(
+ activeProject
+ && (!config || !isCodingProjectConversationReady(config))
+ && !isInitializationSafeRoute,
+ );
return (
{/* Title bar: drag region on macOS, icon + controls on Windows */}
@@ -121,8 +126,8 @@ export function MainLayout() {
-
项目尚未初始化
-
请先在项目配置中完成至少一个手动 Agent 的名称、模型和职责设置。
+
项目还差一步
+
请先在项目配置中创建或恢复一个项目智能体,并完成名称、模型和职责设置。
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index b720316..f2996bd 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -55,7 +55,10 @@ import { isCanonicalCodingProjectId } from '@/lib/coding-projects';
import type { CodingProjectSummary } from '@/types/coding-project';
import { useProjectConfigStore } from '@/stores/project-config';
import type { ProjectType } from '../../../shared/project-config';
-import type { ProjectIdentityChoice } from '../../../shared/coding-project-contracts';
+import {
+ isCodingProjectConversationReady,
+ type ProjectIdentityChoice,
+} from '../../../shared/coding-project-contracts';
import { toast } from 'sonner';
type OpenDialogResult = {
@@ -439,7 +442,7 @@ export function Sidebar({ workspaceLayout = false, sidebarPeekOpen = false, onSi
return;
}
await setActiveProject(project.id);
- navigate(result.config.initialized
+ navigate(isCodingProjectConversationReady(result.config)
? '/chat'
: projectConfigPath);
};
diff --git a/src/pages/Chat/CodingChatPanel.tsx b/src/pages/Chat/CodingChatPanel.tsx
index 52b1a01..85919c1 100644
--- a/src/pages/Chat/CodingChatPanel.tsx
+++ b/src/pages/Chat/CodingChatPanel.tsx
@@ -41,7 +41,12 @@ import { CodingConversationSidebar } from './CodingConversationSidebar';
import { CodingConversationHeader } from './CodingConversationHeader';
import { CodingConversationTimeline } from './CodingConversationTimeline';
import { CodingInteractionPanel } from './CodingInteractionPanel';
+import { CodingProjectSetupEmptyState } from './CodingProjectSetupEmptyState';
import { createLocalConversationSnapshot } from './coding-chat-snapshot';
+import {
+ isCodingProjectAgentReady,
+ isCodingProjectConversationReady,
+} from '../../../shared/coding-project-contracts';
export interface CodingChatPanelProps {
navigationDraft?: string;
@@ -158,8 +163,9 @@ export function CodingChatPanel({
const submissionFlightsRef = useRef(new Set());
const agents = useMemo(() => (
- config?.agents.filter((agent) => agent.enabled && !agent.archivedAt) ?? []
+ config?.agents.filter(isCodingProjectAgentReady) ?? []
), [config]);
+ const conversationReady = Boolean(config && isCodingProjectConversationReady(config));
const selectedAgent = agents.find((agent) => agent.id === selectedAgentId) ?? null;
const selectedConversation = conversations.find((conversation) => (
conversation.id === selectedConversationId
@@ -615,6 +621,10 @@ export function CodingChatPanel({
);
}
+ if (activeProject && config && !conversationReady && workspaceLoadState !== 'loading') {
+ return ;
+ }
+
return (
+
+
+
+
+
创建项目智能体后开始对话
+
+ 当前项目还没有可用的项目智能体。完成名称、模型和职责设置后,就可以发送消息。
+
+
+
+
+ );
+}
diff --git a/src/pages/ProjectConfiguration/index.tsx b/src/pages/ProjectConfiguration/index.tsx
index 1e86629..5459d14 100644
--- a/src/pages/ProjectConfiguration/index.tsx
+++ b/src/pages/ProjectConfiguration/index.tsx
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
-import { ArrowLeft, Blocks, Check, Cpu, FileText, Files, FolderOpen, Plus, Trash2, Upload, X } from 'lucide-react';
+import { ArrowLeft, Blocks, Bot, Check, Cpu, FileText, Files, FolderOpen, Plus, Trash2, Upload, X } from 'lucide-react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@@ -20,10 +20,16 @@ import { useCodingWorkspaceStore } from '@/stores/coding-workspace';
import { useProjectConfigStore } from '@/stores/project-config';
import { useProviderStore } from '@/stores/providers';
import type { CodingProjectAgent, CodingProjectConfig } from '@/types/coding-project';
-import type { ProjectIdentityChoice } from '../../../shared/coding-project-contracts';
+import {
+ hasReadyCodingProjectAgent,
+ isCodingProjectAgentReady,
+ isCodingProjectConversationReady,
+ type ProjectIdentityChoice,
+} from '../../../shared/coding-project-contracts';
const EMPTY_FILES: string[] = [];
type DrawerMode = 'models' | 'knowledge' | 'skills' | null;
+type ExitDestination = '/chat' | '/module-select' | '/models';
type ProjectIdentityKind = ProjectIdentityChoice['kind'];
type SkillStructureEntry = { path: string; type: 'file' | 'directory' };
type SkillInfo = {
@@ -93,7 +99,8 @@ function SkillDetail({ skill }: { skill: SkillInfo }) {
}
function AgentCard({ agent, onOpen }: { agent: CodingProjectAgent; onOpen: () => void }) {
- return ;
+ const ready = isCodingProjectAgentReady(agent);
+ return ;
}
function ModelCard({ model }: { model: CodingModelOption }) {
@@ -111,10 +118,20 @@ function ModelCard({ model }: { model: CodingModelOption }) {