Merge branch 'master' into codex/admin-database-table-query
This commit is contained in:
+82
-72
@@ -715,36 +715,6 @@ fn build_game_creator_agent_background_tool_plan_request_at(
|
||||
))
|
||||
}
|
||||
|
||||
pub(in crate::agent) fn build_game_creator_agent_background_tool_plan_request(
|
||||
root: &Path,
|
||||
agent_id: &str,
|
||||
session_id: &str,
|
||||
run_id: &str,
|
||||
task: &str,
|
||||
observations: &[AgentRuntimeToolObservation],
|
||||
loop_index: usize,
|
||||
) -> Result<
|
||||
(
|
||||
GameCreatorLlmConfig,
|
||||
String,
|
||||
LlmRunRequest,
|
||||
String,
|
||||
AgentRuntimeToolPlanRequestSnapshot,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
build_game_creator_agent_background_tool_plan_request_at(
|
||||
root,
|
||||
None,
|
||||
agent_id,
|
||||
session_id,
|
||||
run_id,
|
||||
task,
|
||||
observations,
|
||||
loop_index,
|
||||
)
|
||||
}
|
||||
|
||||
pub(in crate::agent) fn build_game_creator_agent_background_tool_plan_request_locked(
|
||||
root: &Path,
|
||||
project_lock: &ProjectWriteLock,
|
||||
@@ -972,17 +942,20 @@ mod tests {
|
||||
use crate::{update_manifest_task_status_at, GameCreationAppTaskStatus};
|
||||
|
||||
use super::{
|
||||
acquire_game_creator_agent_provider_plan_project_write_lock_with_wait,
|
||||
agent_runtime_root_source_at, bind_game_creator_agent_runtime_run_profile_at,
|
||||
build_game_creator_agent_background_final_reply_request,
|
||||
build_game_creator_agent_background_tool_plan_request,
|
||||
build_game_creator_agent_background_tool_plan_request_locked,
|
||||
game_creator_agent_context_preload_notice,
|
||||
game_creator_agent_runtime_run_profile_binding_path,
|
||||
game_creator_project_supervisor_chat_system_prompt, init_local_game_project_at,
|
||||
new_game_creation_app_seed_tasks, provider_command_exec_contract,
|
||||
provider_command_start_contract, render_relaxed_autonomous_manifest_ready_task_background_prompt,
|
||||
provider_command_start_contract,
|
||||
render_relaxed_autonomous_manifest_ready_task_background_prompt,
|
||||
required_runtime_prompt_section, start_game_creator_agent_runtime_task_at,
|
||||
AgentRuntimeGoalContractAcceptanceNodeDraft, AgentRuntimeGoalContractDraft,
|
||||
AgentRuntimeTaskLink, AgentRuntimeToolObservation, AgentRuntimeToolPlan,
|
||||
AgentRuntimeToolPlanRequestSnapshot, GameCreatorLlmConfig,
|
||||
AGENT_RUNTIME_AUTONOMOUS_PRE_MUTATION_LOOP_LIMIT,
|
||||
AGENT_RUNTIME_COMPLETION_BLOCKER_TOOL_PLAN_PROTOCOL,
|
||||
AGENT_RUNTIME_PLAN_AUTONOMOUS_PROFILE_UNSUPPORTED_KIND,
|
||||
@@ -996,6 +969,40 @@ mod tests {
|
||||
RUNTIME_PROMPT_SUPERVISOR_CHAT_COMPOSITION,
|
||||
};
|
||||
|
||||
fn build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
root: &std::path::Path,
|
||||
agent_id: &str,
|
||||
session_id: &str,
|
||||
run_id: &str,
|
||||
task: &str,
|
||||
observations: &[AgentRuntimeToolObservation],
|
||||
loop_index: usize,
|
||||
) -> Result<
|
||||
(
|
||||
GameCreatorLlmConfig,
|
||||
String,
|
||||
platform_llm::LlmRunRequest,
|
||||
String,
|
||||
AgentRuntimeToolPlanRequestSnapshot,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
let lock = acquire_game_creator_agent_provider_plan_project_write_lock_with_wait(
|
||||
root,
|
||||
"test.provider_request.build.tool_plan",
|
||||
)?;
|
||||
build_game_creator_agent_background_tool_plan_request_locked(
|
||||
root,
|
||||
&lock,
|
||||
agent_id,
|
||||
session_id,
|
||||
run_id,
|
||||
task,
|
||||
observations,
|
||||
loop_index,
|
||||
)
|
||||
}
|
||||
|
||||
fn native_input_required_fields(
|
||||
request: &platform_llm::LlmRunRequest,
|
||||
tool: &str,
|
||||
@@ -1066,7 +1073,7 @@ mod tests {
|
||||
summary: "结构化计划更新被 Runtime 拒绝".to_string(),
|
||||
detail: Some("计划状态回退".to_string()),
|
||||
};
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
@@ -1155,16 +1162,17 @@ mod tests {
|
||||
// Relaxed orchestration does not convert an idle planning counter into
|
||||
// a tool-removal gate; the Provider remains free to choose its next
|
||||
// action.
|
||||
let (_, _, baseline, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
&state.run_id,
|
||||
&state.current_task,
|
||||
&[],
|
||||
1,
|
||||
)
|
||||
.expect("build baseline request");
|
||||
let (_, _, baseline, _, _) =
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
&state.run_id,
|
||||
&state.current_task,
|
||||
&[],
|
||||
1,
|
||||
)
|
||||
.expect("build baseline request");
|
||||
assert!(baseline
|
||||
.function_tools
|
||||
.iter()
|
||||
@@ -1175,7 +1183,7 @@ mod tests {
|
||||
crate::agent::write_game_creator_agent_runtime_state(&root, &idle_state)
|
||||
.expect("persist idle rounds");
|
||||
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
@@ -1256,7 +1264,7 @@ mod tests {
|
||||
vec!["交付当前 manifest task".to_string()],
|
||||
)
|
||||
.expect("start autonomous ready child task");
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
agent_id,
|
||||
&state.session_id,
|
||||
@@ -1506,7 +1514,7 @@ mod tests {
|
||||
vec!["冻结 Goal Contract".to_string()],
|
||||
)
|
||||
.expect("start trusted root");
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
let (_, _, request, _, _) = build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
@@ -1566,7 +1574,7 @@ mod tests {
|
||||
)
|
||||
.expect("start plan root");
|
||||
|
||||
let (_, _, first, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
let (_, _, first, _, _) = build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
@@ -1633,7 +1641,7 @@ mod tests {
|
||||
},
|
||||
)
|
||||
.expect("freeze plan contract");
|
||||
let (_, _, later, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
let (_, _, later, _, _) = build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
&state.agent_id,
|
||||
&state.session_id,
|
||||
@@ -1719,7 +1727,7 @@ mod tests {
|
||||
)
|
||||
.expect("start supervisor runtime state");
|
||||
let (_, _, supervisor_request, _, _) =
|
||||
build_game_creator_agent_background_tool_plan_request(
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&supervisor_state.session_id,
|
||||
@@ -1865,16 +1873,17 @@ mod tests {
|
||||
vec!["核对普通说明".to_string()],
|
||||
)
|
||||
.expect("start ordinary runtime state");
|
||||
let (_, _, ordinary_request, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
&root,
|
||||
"code-prototype",
|
||||
&ordinary_state.session_id,
|
||||
&ordinary_state.run_id,
|
||||
&ordinary_state.current_task,
|
||||
&[],
|
||||
0,
|
||||
)
|
||||
.expect("build ordinary planning request");
|
||||
let (_, _, ordinary_request, _, _) =
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
"code-prototype",
|
||||
&ordinary_state.session_id,
|
||||
&ordinary_state.run_id,
|
||||
&ordinary_state.current_task,
|
||||
&[],
|
||||
0,
|
||||
)
|
||||
.expect("build ordinary planning request");
|
||||
assert!(ordinary_request.messages[0]
|
||||
.content
|
||||
.contains("你正在使用 Genarrative AI 游戏创作多智能体 Runtime"));
|
||||
@@ -1989,16 +1998,17 @@ mod tests {
|
||||
vec!["读取需求并准备澄清".to_string()],
|
||||
)
|
||||
.expect("start planning child");
|
||||
let (_, _, planning_request, _, _) = build_game_creator_agent_background_tool_plan_request(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
|
||||
&planning_state.session_id,
|
||||
&planning_state.run_id,
|
||||
&planning_state.current_task,
|
||||
&[],
|
||||
0,
|
||||
)
|
||||
.expect("build planning request");
|
||||
let (_, _, planning_request, _, _) =
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
|
||||
&planning_state.session_id,
|
||||
&planning_state.run_id,
|
||||
&planning_state.current_task,
|
||||
&[],
|
||||
0,
|
||||
)
|
||||
.expect("build planning request");
|
||||
let planning_system_prompt = &planning_request.messages[0].content;
|
||||
let planning_brief_marker = "你是“立项策划 Agent”(`agentId=project-planning`)";
|
||||
assert!(planning_system_prompt.contains(planning_brief_marker));
|
||||
@@ -2074,7 +2084,7 @@ mod tests {
|
||||
)
|
||||
.expect("start supervisor");
|
||||
let (_, _, supervisor_request, _, _) =
|
||||
build_game_creator_agent_background_tool_plan_request(
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&supervisor_state.session_id,
|
||||
@@ -2183,7 +2193,7 @@ mod tests {
|
||||
},
|
||||
];
|
||||
let (_, _, request, _, request_snapshot) =
|
||||
build_game_creator_agent_background_tool_plan_request(
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&state.session_id,
|
||||
@@ -2209,7 +2219,7 @@ mod tests {
|
||||
),
|
||||
});
|
||||
let (_, _, _, _, settled_request_snapshot) =
|
||||
build_game_creator_agent_background_tool_plan_request(
|
||||
build_game_creator_agent_background_tool_plan_request_for_test(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
&state.session_id,
|
||||
|
||||
@@ -216,23 +216,6 @@ pub(crate) fn pending_matches_receipt(
|
||||
/// Construct the independent planning pending projection after an external
|
||||
/// acceptance gate has succeeded. M1C-1 does not decide whether the gate
|
||||
/// passed; the caller must supply that fact and the exact GDD identity.
|
||||
pub(crate) fn create_plan_gdd_approval_pending_at(
|
||||
root: &Path,
|
||||
gdd: &PlanGddV1,
|
||||
) -> Result<(), PlanningStorageError> {
|
||||
if !crate::config::game_creator_planning_capability_enabled()
|
||||
.map_err(|error| approval_error("PLAN_CAPABILITY_DISABLED", error))?
|
||||
{
|
||||
return Err(approval_error(
|
||||
"PLAN_CAPABILITY_DISABLED",
|
||||
"立项策划能力当前已停用",
|
||||
));
|
||||
}
|
||||
let _lock = acquire_project_write_lock(root, "planning.approval-pending.create")
|
||||
.map_err(|error| approval_error("PLAN_DURABILITY_FAILED", error))?;
|
||||
create_plan_gdd_approval_pending_locked(root, gdd)
|
||||
}
|
||||
|
||||
pub(crate) fn create_plan_gdd_approval_pending_locked(
|
||||
root: &Path,
|
||||
gdd: &PlanGddV1,
|
||||
@@ -2357,13 +2340,6 @@ pub(crate) fn plan_gdd_typed_completion_blocker_at_locked(
|
||||
),
|
||||
));
|
||||
}
|
||||
if session.phase == "recovery_required" {
|
||||
return Some(plan_gdd_completion_blocker(
|
||||
"needs-reconciliation",
|
||||
"planning session 仍处于 recovery_required,不能收束任务",
|
||||
format!("gddVersion={}", latest.version),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
+13
-25
@@ -2408,8 +2408,7 @@ pub(crate) fn validate_plan_session_successor(
|
||||
&& next.active_run_id.is_some()
|
||||
&& next.last_run_id == next.active_run_id.clone().unwrap_or_default();
|
||||
if next.run_profile_binding_fingerprint != previous.run_profile_binding_fingerprint
|
||||
&& (!active_run_changed
|
||||
|| !matches!(next.phase.as_str(), "collecting" | "revision_requested"))
|
||||
&& !active_run_changed
|
||||
{
|
||||
return Err(conflict(
|
||||
"session 只有在绑定新的 active planning child 时才能更换 Run Profile binding fingerprint",
|
||||
@@ -2712,18 +2711,6 @@ pub(crate) fn canonical_plan_submit_gdd_input_bytes(
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_plan_submit_gdd_input_bytes(
|
||||
bytes: &[u8],
|
||||
) -> Result<PlanSubmitGddInputV1, PlanningStorageError> {
|
||||
let value = parse_strict_canonical::<PlanSubmitGddInputV1>(
|
||||
bytes,
|
||||
"plan.submit_gdd input",
|
||||
PLAN_GDD_MAX_BYTES,
|
||||
)?;
|
||||
validate_plan_submit_gdd_input(&value)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub(crate) fn validate_plan_gdd_chain(values: &[PlanGddV1]) -> Result<(), PlanningStorageError> {
|
||||
if values.len() > PLAN_MAX_VERSIONS as usize {
|
||||
return Err(PlanningStorageError::new(
|
||||
@@ -5067,22 +5054,23 @@ mod tests {
|
||||
fn submit_input_has_strict_canonical_parser_and_runtime_field_boundary() {
|
||||
let value = golden_submit_input();
|
||||
let bytes = canonical_plan_submit_gdd_input_bytes(&value).expect("submit input bytes");
|
||||
assert_eq!(
|
||||
parse_plan_submit_gdd_input_bytes(&bytes).expect("parse input"),
|
||||
value
|
||||
);
|
||||
let parse = |bytes: &[u8]| -> Result<PlanSubmitGddInputV1, PlanningStorageError> {
|
||||
let value = parse_strict_canonical::<PlanSubmitGddInputV1>(
|
||||
bytes,
|
||||
"plan.submit_gdd input",
|
||||
PLAN_GDD_MAX_BYTES,
|
||||
)?;
|
||||
validate_plan_submit_gdd_input(&value)?;
|
||||
Ok(value)
|
||||
};
|
||||
assert_eq!(parse(&bytes).expect("parse input"), value);
|
||||
let mut newline = bytes.clone();
|
||||
newline.push(b'\n');
|
||||
assert_eq!(
|
||||
parse_plan_submit_gdd_input_bytes(&newline)
|
||||
.unwrap_err()
|
||||
.code(),
|
||||
"PLAN_NON_CANONICAL_BYTES"
|
||||
);
|
||||
assert_eq!(parse(&newline).unwrap_err().code(), "PLAN_NON_CANONICAL_BYTES");
|
||||
let mut object = serde_json::from_slice::<serde_json::Value>(&bytes).expect("input json");
|
||||
object["projectId"] = serde_json::Value::String("forged-project".to_string());
|
||||
let forged = serde_json::to_vec(&object).expect("forged input");
|
||||
assert!(parse_plan_submit_gdd_input_bytes(&forged).is_err());
|
||||
assert!(parse(&forged).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+25
-27
@@ -1166,15 +1166,7 @@ fn validate_current_session_cas(
|
||||
"plan.submit_gdd 必须绑定当前活跃策划子 Run",
|
||||
));
|
||||
}
|
||||
// 这条 phase 判据实际只可能看到 `collecting`:上面的 activeRunId 判据要求
|
||||
// session 绑着当前策划子 run,而 schema 不变量禁止 `awaiting_user_input`、
|
||||
// `awaiting_gdd_approval`、`revision_requested`、`approved`、`rejected`、
|
||||
// `recovery_required` 保留 activeRunId(planning_storage.rs 的
|
||||
// 「session 进入审批/终态/recovery_required 后不得保留 activeRunId」)。
|
||||
// 因此 revise/reject 之后能不能重做,不由这条门决定,而由 M1C-2b 的 continuation
|
||||
// 起点 writer 决定——它必须以新 activeRunId 写 revision+1 successor,phase 只能落回
|
||||
// `collecting`。这里保留 `revision_requested` 作为既有冗余,不再新增更多不可达分支。
|
||||
if !matches!(session.phase.as_str(), "collecting" | "revision_requested") {
|
||||
if session.phase != "collecting" {
|
||||
return Err(submit_error(
|
||||
"PLAN_PENDING_GDD_EXISTS",
|
||||
"当前 planning session 仍有未决 GDD",
|
||||
@@ -1641,11 +1633,7 @@ fn project_submit_successors_locked(
|
||||
let source_session_matches_gdd = session_identity_matches_gdd
|
||||
&& previous_session.session_revision == gdd.source_session_revision
|
||||
&& previous_session.session_fingerprint == gdd.source_session_fingerprint
|
||||
&& previous_session.active_run_id.as_deref() == Some(gdd.created_by_run_id.as_str())
|
||||
&& matches!(
|
||||
previous_session.phase.as_str(),
|
||||
"collecting" | "revision_requested"
|
||||
);
|
||||
&& previous_session.active_run_id.as_deref() == Some(gdd.created_by_run_id.as_str());
|
||||
if !session_identity_matches_gdd {
|
||||
recovery_pending = true;
|
||||
} else if same_ref {
|
||||
@@ -1896,6 +1884,15 @@ mod tests {
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn create_plan_gdd_approval_pending_for_test(
|
||||
root: &std::path::Path,
|
||||
gdd: &PlanGddV1,
|
||||
) -> Result<(), PlanningStorageError> {
|
||||
let _lock = acquire_project_write_lock(root, "test.planning.approval-pending.create")
|
||||
.map_err(|error| PlanningStorageError::new("PLAN_DURABILITY_FAILED", error))?;
|
||||
create_plan_gdd_approval_pending_locked(root, gdd)
|
||||
}
|
||||
|
||||
fn valid_input() -> PlanSubmitGddInputV1 {
|
||||
PlanSubmitGddInputV1 {
|
||||
schema_version: PLAN_SUBMIT_INPUT_SCHEMA.to_string(),
|
||||
@@ -3628,7 +3625,7 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
let decision_input = approval_input(
|
||||
&gdd,
|
||||
"approve",
|
||||
@@ -3654,13 +3651,13 @@ mod tests {
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
let pending = read_plan_gdd_approval_pending_locked(&root)
|
||||
.expect("read approval pending")
|
||||
.expect("pending exists");
|
||||
assert_eq!(pending.status, "awaiting_decision");
|
||||
// Recreating the exact card is an idempotent replay.
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("replay approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("replay approval pending");
|
||||
|
||||
let mut forged_next = gdd.clone();
|
||||
forged_next.version = 2;
|
||||
@@ -3669,7 +3666,7 @@ mod tests {
|
||||
"gdd-approval-00000000-0000-4000-8000-000000000003".to_string();
|
||||
forged_next.action_fingerprint = "4".repeat(64);
|
||||
forged_next.fingerprint = plan_gdd_fingerprint(&forged_next).expect("next fingerprint");
|
||||
let stale = create_plan_gdd_approval_pending_at(&root, &forged_next)
|
||||
let stale = create_plan_gdd_approval_pending_for_test(&root, &forged_next)
|
||||
.expect_err("a non-latest GDD cannot receive an approval card");
|
||||
assert_eq!(stale.code(), "PLAN_STALE_APPROVAL");
|
||||
|
||||
@@ -3684,7 +3681,7 @@ mod tests {
|
||||
)
|
||||
.expect("commit approval receipt");
|
||||
assert_eq!(decision.outcome, "committed");
|
||||
let after_receipt = create_plan_gdd_approval_pending_at(&root, &gdd)
|
||||
let after_receipt = create_plan_gdd_approval_pending_for_test(&root, &gdd)
|
||||
.expect_err("a receipt must close awaiting_decision recreation");
|
||||
assert_eq!(after_receipt.code(), "PLAN_STALE_APPROVAL");
|
||||
cleanup_fixture(root);
|
||||
@@ -3698,7 +3695,7 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
|
||||
let task_path =
|
||||
game_creator_agent_runtime_task_path(&root, GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID);
|
||||
@@ -3738,7 +3735,7 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
let _config_guard = crate::tests::write_test_local_config(
|
||||
r#"{"planning":{"capabilityEnabled":false}}"#.to_string(),
|
||||
);
|
||||
@@ -3802,7 +3799,7 @@ mod tests {
|
||||
assert_eq!(blocker.status, "needs-reconciliation");
|
||||
assert!(blocker.summary.contains("审批 pending"));
|
||||
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
let blocker = plan_gdd_completion_blocker_at_locked(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
@@ -3955,7 +3952,7 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
decide_plan_gdd_at(
|
||||
&root,
|
||||
&approval_input(
|
||||
@@ -4089,7 +4086,7 @@ mod tests {
|
||||
#[test]
|
||||
fn a_revision_comment_reaches_the_supervisor_conversation_once() {
|
||||
let (root, gdd, _root_runtime) = acceptance_gate_fixture(true);
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
decide_plan_gdd_at(
|
||||
&root,
|
||||
&approval_input(
|
||||
@@ -4143,7 +4140,7 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
decide_plan_gdd_at(
|
||||
&root,
|
||||
&approval_input(
|
||||
@@ -4230,7 +4227,8 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd)
|
||||
.expect("create approval pending");
|
||||
let first_input = approval_input(
|
||||
&gdd,
|
||||
action,
|
||||
@@ -4461,7 +4459,7 @@ mod tests {
|
||||
.expect("read submitted GDD")
|
||||
.pop()
|
||||
.expect("GDD exists");
|
||||
create_plan_gdd_approval_pending_at(&root, &gdd).expect("create approval pending");
|
||||
create_plan_gdd_approval_pending_for_test(&root, &gdd).expect("create approval pending");
|
||||
let decision_input = approval_input(
|
||||
&gdd,
|
||||
"approve",
|
||||
|
||||
@@ -11037,20 +11037,6 @@ export function App({
|
||||
projectSupervisorRuntime={projectSupervisorRuntime}
|
||||
projectSupervisorRuntimeError={projectSupervisorRuntimeError}
|
||||
projectSupervisorTransientReply={projectSupervisorTransientReply}
|
||||
planGddState={planGddState}
|
||||
planGddHydrateBusy={planGddHydrateBusy}
|
||||
planGddDecisionBusy={planGddDecisionBusy}
|
||||
planGddError={planGddError}
|
||||
onPlanGddRefresh={() => void hydratePlanGddState()}
|
||||
onPlanGddDecision={decidePlanGdd}
|
||||
onMakeGameFromApprovedGdd={
|
||||
onMakeGameFromApprovedGdd
|
||||
? () =>
|
||||
onMakeGameFromApprovedGdd(
|
||||
localProject?.projectPath ?? projectPath,
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
queueAgentRunControlFromPanel={queueAgentRunControlFromPanel}
|
||||
queueOrExecuteProjectIndex={queueOrExecuteProjectIndex}
|
||||
queuePendingCommand={queuePendingCommand}
|
||||
|
||||
+12
-56
@@ -25,8 +25,6 @@ import type {
|
||||
MemoryScope,
|
||||
PendingCommand,
|
||||
PendingUiConfirmation,
|
||||
PlanGddDecisionAction,
|
||||
PlanGddStateViewV1,
|
||||
} from '../../app/types';
|
||||
import type { ProjectAgentResultSummary } from '../../view/project-development';
|
||||
import {
|
||||
@@ -47,13 +45,10 @@ import {
|
||||
taskGroupLabels,
|
||||
taskStatusLabels,
|
||||
} from '../project-summary/projectSummary';
|
||||
import { PlanGddSurface } from './GddApprovalCard';
|
||||
import {
|
||||
pendingCommandDetail,
|
||||
pendingCommandTitle,
|
||||
} from './pendingCommandPresentation';
|
||||
import { isPlanningLaneRuntime } from './planningLane';
|
||||
import { PlanningLaneRuntimeStrip } from './PlanningLaneRuntimeStrip';
|
||||
import { resolvePendingCommandProjectPath } from './projectCommandPolicy';
|
||||
|
||||
type ProjectWorkspaceChatPaneProps = {
|
||||
@@ -180,16 +175,6 @@ type ProjectWorkspaceChatPaneProps = {
|
||||
projectSupervisorRuntime: AgentRuntimeState | null;
|
||||
projectSupervisorRuntimeError: string;
|
||||
projectSupervisorTransientReply: string;
|
||||
planGddState: PlanGddStateViewV1 | null;
|
||||
planGddHydrateBusy: boolean;
|
||||
planGddDecisionBusy: boolean;
|
||||
planGddError: string | null;
|
||||
onPlanGddRefresh: () => void;
|
||||
onPlanGddDecision: (
|
||||
action: PlanGddDecisionAction,
|
||||
comment: string | null,
|
||||
) => Promise<void>;
|
||||
onMakeGameFromApprovedGdd?: () => Promise<void>;
|
||||
queueAgentRunControlFromPanel: (action: 'kill' | 'retry' | 'resume') => void;
|
||||
queueOrExecuteProjectIndex: () => Promise<void>;
|
||||
queuePendingCommand: (command: PendingCommand) => void;
|
||||
@@ -273,13 +258,6 @@ export function ProjectWorkspaceChatPane({
|
||||
projectSupervisorRuntime,
|
||||
projectSupervisorRuntimeError,
|
||||
projectSupervisorTransientReply,
|
||||
planGddState,
|
||||
planGddHydrateBusy,
|
||||
planGddDecisionBusy,
|
||||
planGddError,
|
||||
onPlanGddRefresh,
|
||||
onPlanGddDecision,
|
||||
onMakeGameFromApprovedGdd,
|
||||
queueAgentRunControlFromPanel,
|
||||
queueOrExecuteProjectIndex,
|
||||
queuePendingCommand,
|
||||
@@ -369,17 +347,6 @@ export function ProjectWorkspaceChatPane({
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<PlanGddSurface
|
||||
state={planGddState}
|
||||
active={isPlanningLaneRuntime(projectSupervisorRuntime)}
|
||||
projectPath={projectPath}
|
||||
hydrateBusy={planGddHydrateBusy}
|
||||
decisionBusy={planGddDecisionBusy}
|
||||
error={planGddError}
|
||||
onRefresh={onPlanGddRefresh}
|
||||
onDecision={onPlanGddDecision}
|
||||
onMakeGame={onMakeGameFromApprovedGdd}
|
||||
/>
|
||||
<div className="chat-quick-actions" aria-label="预览快捷操作">
|
||||
<button
|
||||
type="button"
|
||||
@@ -865,29 +832,18 @@ export function ProjectWorkspaceChatPane({
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
{isPlanningLaneRuntime(projectSupervisorRuntime) ? (
|
||||
<PlanningLaneRuntimeStrip
|
||||
runtime={projectSupervisorRuntime}
|
||||
error={projectSupervisorRuntimeError}
|
||||
controlBusy={chatAgentBusy}
|
||||
onSupervisorRetry={handleProjectSupervisorRetry}
|
||||
onUserInput={handleProjectSupervisorUserInput}
|
||||
/>
|
||||
) : (
|
||||
<ProjectSupervisorRuntimePanel
|
||||
runtime={projectSupervisorRuntime}
|
||||
error={projectSupervisorRuntimeError}
|
||||
runtimeByAgentId={agentRuntimeById}
|
||||
controlBusy={chatAgentBusy}
|
||||
planGddAwaitingDecision={Boolean(planGddState?.pendingApproval)}
|
||||
professionalResultsByAgentId={professionalAgentResultsById}
|
||||
onToolAction={handleProjectSupervisorToolAction}
|
||||
onSupervisorRetry={handleProjectSupervisorRetry}
|
||||
onProfessionalToolAction={handleProjectProfessionalAgentToolAction}
|
||||
onProfessionalRetry={handleProjectProfessionalAgentRetry}
|
||||
onUserInput={handleProjectSupervisorUserInput}
|
||||
/>
|
||||
)}
|
||||
<ProjectSupervisorRuntimePanel
|
||||
runtime={projectSupervisorRuntime}
|
||||
error={projectSupervisorRuntimeError}
|
||||
runtimeByAgentId={agentRuntimeById}
|
||||
controlBusy={chatAgentBusy}
|
||||
professionalResultsByAgentId={professionalAgentResultsById}
|
||||
onToolAction={handleProjectSupervisorToolAction}
|
||||
onSupervisorRetry={handleProjectSupervisorRetry}
|
||||
onProfessionalToolAction={handleProjectProfessionalAgentToolAction}
|
||||
onProfessionalRetry={handleProjectProfessionalAgentRetry}
|
||||
onUserInput={handleProjectSupervisorUserInput}
|
||||
/>
|
||||
{pendingCommand ? (
|
||||
<div className="pending-command">
|
||||
<span>
|
||||
|
||||
@@ -4,21 +4,34 @@ import { resolve } from 'node:path';
|
||||
import { PlanGddStageProgress } from '../../src/features/project-workspace/GddApprovalCard';
|
||||
import {
|
||||
agentRuntimeUserInputRequest,
|
||||
App,
|
||||
createPlanGddStateView,
|
||||
createProjectSupervisorRuntimeHarness,
|
||||
expect,
|
||||
fireEvent,
|
||||
it,
|
||||
openMainProject,
|
||||
React,
|
||||
render,
|
||||
renderAppAt,
|
||||
screen,
|
||||
vi,
|
||||
waitFor,
|
||||
within,
|
||||
} from './harness';
|
||||
|
||||
function mountFormalSupervisor(
|
||||
harness: ReturnType<typeof createProjectSupervisorRuntimeHarness>,
|
||||
) {
|
||||
window.history.pushState({}, '', '/');
|
||||
render(
|
||||
React.createElement(App, {
|
||||
initialProjectPath: harness.projectPath,
|
||||
orchestrationMode: 'single-supervisor',
|
||||
planningStartMode: true,
|
||||
projectSupervisorOnly: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function mountApprovalCard(
|
||||
harness: ReturnType<typeof createProjectSupervisorRuntimeHarness>,
|
||||
) {
|
||||
@@ -26,8 +39,7 @@ async function mountApprovalCard(
|
||||
core: { invoke: harness.invoke },
|
||||
event: { listen: harness.listen },
|
||||
};
|
||||
renderAppAt('/');
|
||||
await openMainProject(harness.projectPath);
|
||||
mountFormalSupervisor(harness);
|
||||
return await screen.findByLabelText('GDD 审批卡');
|
||||
}
|
||||
|
||||
@@ -38,8 +50,7 @@ async function mountPlanningSurface(
|
||||
core: { invoke: harness.invoke },
|
||||
event: { listen: harness.listen },
|
||||
};
|
||||
renderAppAt('/');
|
||||
await openMainProject(harness.projectPath);
|
||||
mountFormalSupervisor(harness);
|
||||
return await screen.findByLabelText('立项策划阶段进度');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user