允许Agent创建项目快照
后台 Runtime 工具箱新增 project.checkpoint Agent 可在写入前按权限策略创建本地 checkpoint 补充 checkpoint 成功和确认阻断测试 同步 Runtime V1 文档和项目记忆
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -5175,6 +5175,195 @@ async fn background_agent_runtime_file_list_respects_project_policy() {
|
||||
fs::remove_dir_all(root).ok();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn background_agent_runtime_can_create_checkpoint_before_file_write() {
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(&root, "project-1", "月光厨房").expect("project init");
|
||||
write_local_project_file_at(&root, "game/notes.txt", "v1").expect("write notes");
|
||||
write_project_permission_policy_at(
|
||||
&root,
|
||||
ProjectPermissionPolicy {
|
||||
denied_commands: Vec::new(),
|
||||
confirm_commands: Vec::new(),
|
||||
agent_policies: BTreeMap::new(),
|
||||
},
|
||||
)
|
||||
.expect("allow project checkpoint");
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let plan_json = serde_json::json!({
|
||||
"thinkingSummary": "写文件前先创建 checkpoint",
|
||||
"plan": ["创建 checkpoint", "修改项目说明文件"],
|
||||
"actions": [
|
||||
{
|
||||
"tool": "project.checkpoint",
|
||||
"reason": "保存写入前的项目状态",
|
||||
"input": {}
|
||||
},
|
||||
{
|
||||
"tool": "file.write",
|
||||
"reason": "更新设计说明",
|
||||
"input": { "path": "game/notes.txt", "content": "v2" }
|
||||
}
|
||||
],
|
||||
"response": ""
|
||||
})
|
||||
.to_string();
|
||||
let base_url = spawn_mock_llm_server_responses_with_capture(
|
||||
vec![plan_json, "checkpoint 已创建,文件也已更新。".to_string()],
|
||||
Some(sender),
|
||||
);
|
||||
let _config_guard = write_test_local_config(format!(
|
||||
r#"{{
|
||||
"agentLlm": {{
|
||||
"design-director": {{
|
||||
"apiKey": "design-key",
|
||||
"baseUrl": {base_url:?},
|
||||
"model": "design-runtime-model",
|
||||
"apiKind": "openai_responses"
|
||||
}}
|
||||
}}
|
||||
}}"#
|
||||
));
|
||||
|
||||
start_game_creator_agent_background_task_at(
|
||||
&root,
|
||||
"design-director",
|
||||
"后台修改说明前保存 checkpoint",
|
||||
"design-checkpoint-write-run",
|
||||
)
|
||||
.expect("start background task");
|
||||
|
||||
let plan_request = receiver
|
||||
.recv_timeout(Duration::from_secs(2))
|
||||
.expect("plan llm request");
|
||||
assert!(plan_request.contains("project.checkpoint"));
|
||||
assert!(plan_request.contains("file.write"));
|
||||
let final_request = receiver
|
||||
.recv_timeout(Duration::from_secs(2))
|
||||
.expect("final reply llm request");
|
||||
assert!(final_request.contains("project.checkpoint"));
|
||||
assert!(final_request.contains("已创建 checkpoint checkpoint-"));
|
||||
assert!(final_request.contains("file.write"));
|
||||
|
||||
let runtime = wait_for_agent_runtime_idle(&root, "design-director");
|
||||
assert_eq!(runtime.status, "idle");
|
||||
assert!(runtime
|
||||
.tool_policy
|
||||
.auto_tools
|
||||
.contains(&"project.checkpoint".to_string()));
|
||||
assert!(runtime
|
||||
.observations
|
||||
.iter()
|
||||
.any(|item| item.contains("project.checkpoint:ok · 已创建 checkpoint")));
|
||||
assert!(runtime
|
||||
.observations
|
||||
.iter()
|
||||
.any(|item| item.contains("file.write:ok · 已写入 game/notes.txt")));
|
||||
|
||||
let checkpoints_root = root.join(".agent/checkpoints");
|
||||
let mut checkpoint_dirs = fs::read_dir(&checkpoints_root)
|
||||
.expect("checkpoint dir")
|
||||
.filter_map(Result::ok)
|
||||
.map(|entry| entry.path())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(checkpoint_dirs.len(), 1);
|
||||
let checkpoint_path = checkpoint_dirs.pop().expect("checkpoint path");
|
||||
assert_eq!(
|
||||
fs::read_to_string(checkpoint_path.join("files/game/notes.txt")).expect("checkpoint notes"),
|
||||
"v1"
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read_to_string(root.join("game/notes.txt")).expect("updated notes"),
|
||||
"v2"
|
||||
);
|
||||
let agent_db = fs::read_to_string(root.join(".agent/agent.db")).expect("agent db");
|
||||
assert!(agent_db.contains("\"recordType\":\"project.checkpoint\""));
|
||||
assert!(agent_db.contains("\"tool\":\"project.checkpoint\""));
|
||||
assert!(agent_db.contains("\"tool\":\"file.write\""));
|
||||
|
||||
fs::remove_dir_all(root).ok();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn background_agent_runtime_checkpoint_respects_project_policy() {
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(&root, "project-1", "月光厨房").expect("project init");
|
||||
write_local_project_file_at(&root, "game/blocked-notes.txt", "v1").expect("write notes");
|
||||
write_project_permission_policy_at(
|
||||
&root,
|
||||
ProjectPermissionPolicy {
|
||||
denied_commands: Vec::new(),
|
||||
confirm_commands: vec!["project.checkpoint".to_string()],
|
||||
agent_policies: BTreeMap::new(),
|
||||
},
|
||||
)
|
||||
.expect("write policy");
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let plan_json = serde_json::json!({
|
||||
"thinkingSummary": "尝试在写文件前创建 checkpoint",
|
||||
"plan": ["创建 checkpoint", "等待权限结果"],
|
||||
"actions": [
|
||||
{
|
||||
"tool": "project.checkpoint",
|
||||
"reason": "保存写入前的项目状态",
|
||||
"input": {}
|
||||
}
|
||||
],
|
||||
"response": ""
|
||||
})
|
||||
.to_string();
|
||||
let base_url = spawn_mock_llm_server_responses_with_capture(vec![plan_json], Some(sender));
|
||||
let _config_guard = write_test_local_config(format!(
|
||||
r#"{{
|
||||
"agentLlm": {{
|
||||
"design-director": {{
|
||||
"apiKey": "design-key",
|
||||
"baseUrl": {base_url:?},
|
||||
"model": "design-runtime-model",
|
||||
"apiKind": "openai_responses"
|
||||
}}
|
||||
}}
|
||||
}}"#
|
||||
));
|
||||
|
||||
start_game_creator_agent_background_task_at(
|
||||
&root,
|
||||
"design-director",
|
||||
"后台尝试创建 checkpoint",
|
||||
"design-checkpoint-policy-run",
|
||||
)
|
||||
.expect("start background task");
|
||||
|
||||
let plan_request = receiver
|
||||
.recv_timeout(Duration::from_secs(2))
|
||||
.expect("plan llm request");
|
||||
assert!(plan_request.contains("project.checkpoint"));
|
||||
assert!(receiver.recv_timeout(Duration::from_millis(200)).is_err());
|
||||
|
||||
let runtime = wait_for_agent_runtime_confirmation(&root, "design-director");
|
||||
assert_eq!(runtime.status, "waiting-for-confirmation");
|
||||
assert_eq!(runtime.task_queue.waiting_for_confirmation, 1);
|
||||
assert!(runtime
|
||||
.tool_policy
|
||||
.confirm_tools
|
||||
.contains(&"project.checkpoint".to_string()));
|
||||
assert!(runtime.observations.iter().any(|item| {
|
||||
item.contains(
|
||||
"project.checkpoint:waiting-for-confirmation · 项目权限策略要求用户确认:project.checkpoint",
|
||||
)
|
||||
}));
|
||||
assert!(runtime.recent_tool_calls.iter().any(|call| {
|
||||
call.tool == "project.checkpoint" && call.status == "waiting-for-confirmation"
|
||||
}));
|
||||
assert!(!root.join(".agent/checkpoints").exists());
|
||||
let agent_db = fs::read_to_string(root.join(".agent/agent.db")).expect("agent db");
|
||||
assert!(!agent_db.contains("\"recordType\":\"project.checkpoint\""));
|
||||
|
||||
fs::remove_dir_all(root).ok();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn background_agent_runtime_can_diff_checkpoint() {
|
||||
let root = unique_project_path();
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
- 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `agent.delegate`,让 Agent 可把明确子任务投递到另一个 Agent 的独立后台队列。该工具复用目标 Agent 既有锁和 pending drain 语义,不创建平行 runtime;同一目标 Agent 串行,不同 Agent 可并行。该工具使用独立 `agent.delegate` 权限策略,策略要求确认或拒绝时不会写目标对话、不会启动目标后台任务,也不会写 `agent.runtime.agent.delegate` 审计记录。
|
||||
- 2026-07-10 补充:主窗口 Agent 状态栏在开发模式新增“调度 Ready”控制,用来显式调用 `schedule_game_creator_agent_ready_tasks`。该控制先读取项目策略,命中 `agent.schedule_ready` confirm 时走现有确认弹窗,确认后才把 manifest ready task 投递到对应 Agent Runtime;普通用户窗口不展示这个开发控制。
|
||||
- 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `agent.schedule_ready`,让 Agent 在完成或更新 manifest task 后可按项目权限策略自行调度新 ready task。该工具复用同一个 scheduler:扫描依赖已完成且仍为 pending 的 manifest task,先标为 running,再按 taskId 投递到对应 Agent 的既有后台队列;策略要求确认时当前 Agent 停在 `waiting-for-confirmation`,不会静默启动下游 Agent。
|
||||
- 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `project.checkpoint`,让 Agent 在 `file.write`、`task.update` 或批量修改前自行创建本地 checkpoint。该工具复用 `project.checkpoint` 策略和项目写锁,observation 只返回 checkpoint id、文件数和总字节数,不返回本机绝对路径;策略要求确认时不创建 checkpoint。
|
||||
- 影响范围:`apps/ai-game-creator-shell` 的 Tauri command、Agent Runtime state/event、开发窗口单 Agent 聊天、项目内 Agent 对话弹窗、`appSurface.test.ts` 和 AI 游戏创作 App 实施计划。
|
||||
- 验证方式:运行 Tauri Rust 后台 Agent 并行测试、壳前端 appSurface 测试、壳 typecheck、编码检查和 `git diff --check`。
|
||||
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user