完善自主构建运行时并提高 Runner 启动容错
完善 Agent Swarm 自主游戏构建、委派协作、验证门和试玩闭环 补齐浏览器验证、CLI 入口、项目状态与运行时测试覆盖 将外部 Agent Runner 冷启动超时提高到 30 秒并增加回归测试 同步真实 E2E 自测脚本、配置检查和项目决策记录
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
"agent-runtime:collaboration-policy-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-collaboration-policy-mixed-recovery",
|
||||
"agent-runtime:mixed-swarm-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-static-isolated-autonomous-chat",
|
||||
"agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-autonomous-chat",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-autonomous-playable-lane-defense",
|
||||
"agent-runtime:supervisor-swarm-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-transient-retry",
|
||||
"agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-final-reply-transient-retry",
|
||||
"agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-tool-plan-handoff-runner-kill",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -426,7 +426,13 @@ const eventCapability = JSON.parse(
|
||||
);
|
||||
const eventCapabilityWindows = new Set(eventCapability.windows ?? []);
|
||||
const eventCapabilityPermissions = new Set(eventCapability.permissions ?? []);
|
||||
for (const windowLabel of ['client', 'developer', 'main', 'launcher']) {
|
||||
for (const windowLabel of [
|
||||
'client',
|
||||
'developer',
|
||||
'main',
|
||||
'launcher',
|
||||
'supervisor-chat',
|
||||
]) {
|
||||
if (!eventCapabilityWindows.has(windowLabel)) {
|
||||
throw new Error(
|
||||
`AI game creator shell event capability missing window: ${windowLabel}`,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -897,7 +897,8 @@ fn runtime_tool_input_schema(tool: &str) -> Value {
|
||||
"viewports": { "type": "array", "minItems": 1, "maxItems": 2, "items": { "type": "string", "enum": ["desktop", "mobile"] } },
|
||||
"expectedText": string_array_schema(16),
|
||||
"settleMs": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
||||
"failOnConsoleError": { "type": "boolean" }
|
||||
"failOnConsoleError": { "type": "boolean" },
|
||||
"playtestScenario": { "type": ["string", "null"], "enum": ["generic-v1", "lane-defense-v1", null] }
|
||||
}
|
||||
}),
|
||||
"image.inspect" => json!({
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ pub(crate) enum CliCommand {
|
||||
project_path: PathBuf,
|
||||
parent_agent_id: String,
|
||||
initialize: bool,
|
||||
run_profile: String,
|
||||
},
|
||||
AgentEnqueue {
|
||||
project_path: PathBuf,
|
||||
@@ -644,6 +645,7 @@ pub(crate) fn parse_cli_command(args: &[String]) -> Result<Option<CliCommand>, S
|
||||
}));
|
||||
}
|
||||
if args.first().map(String::as_str) == Some("--swarm-chat") {
|
||||
const USAGE: &str = "用法:--swarm-chat [--init] [--autonomous-game-build] <本地项目绝对路径> [parentAgentId]";
|
||||
let mut rest = args[1..].to_vec();
|
||||
let initialize = if let Some(index) = rest.iter().position(|arg| arg == "--init") {
|
||||
rest.remove(index);
|
||||
@@ -651,10 +653,24 @@ pub(crate) fn parse_cli_command(args: &[String]) -> Result<Option<CliCommand>, S
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let autonomous_game_build = match rest
|
||||
.iter()
|
||||
.filter(|arg| arg.as_str() == "--autonomous-game-build")
|
||||
.count()
|
||||
{
|
||||
0 => false,
|
||||
1 => {
|
||||
let index = rest
|
||||
.iter()
|
||||
.position(|arg| arg == "--autonomous-game-build")
|
||||
.expect("counted autonomous game build flag");
|
||||
rest.remove(index);
|
||||
true
|
||||
}
|
||||
_ => return Err(USAGE.to_string()),
|
||||
};
|
||||
if !(1..=2).contains(&rest.len()) || rest.iter().any(|value| value.trim().is_empty()) {
|
||||
return Err(
|
||||
"用法:--swarm-chat [--init] <本地项目绝对路径> [parentAgentId]".to_string(),
|
||||
);
|
||||
return Err(USAGE.to_string());
|
||||
}
|
||||
return Ok(Some(CliCommand::SwarmChat {
|
||||
project_path: PathBuf::from(&rest[0]),
|
||||
@@ -663,6 +679,11 @@ pub(crate) fn parse_cli_command(args: &[String]) -> Result<Option<CliCommand>, S
|
||||
.map(|value| value.trim().to_string())
|
||||
.unwrap_or_else(|| GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID.to_string()),
|
||||
initialize,
|
||||
run_profile: if autonomous_game_build {
|
||||
AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD.to_string()
|
||||
} else {
|
||||
AGENT_RUNTIME_RUN_PROFILE_STANDARD.to_string()
|
||||
},
|
||||
}));
|
||||
}
|
||||
if args.first().map(String::as_str) == Some("--agent-task") {
|
||||
@@ -876,11 +897,12 @@ pub(crate) fn run_cli_command(command: CliCommand) -> Result<(), String> {
|
||||
project_path,
|
||||
parent_agent_id,
|
||||
initialize,
|
||||
run_profile,
|
||||
} => {
|
||||
let project_path = canonicalize_cli_path(&project_path, "本地项目路径", initialize)?;
|
||||
require_external_agent_runner_for_cli_runtime_write(&project_path)?;
|
||||
initialize_cli_agent_project(&project_path, initialize)?;
|
||||
run_game_creator_swarm_chat_at(&project_path, &parent_agent_id)
|
||||
run_game_creator_swarm_chat_at(&project_path, &parent_agent_id, &run_profile)
|
||||
}
|
||||
CliCommand::AgentEnqueue {
|
||||
project_path,
|
||||
@@ -1704,6 +1726,7 @@ mod tests {
|
||||
project_path,
|
||||
parent_agent_id: "code-prototype".to_string(),
|
||||
initialize: true,
|
||||
run_profile: AGENT_RUNTIME_RUN_PROFILE_STANDARD.to_string(),
|
||||
}
|
||||
);
|
||||
assert!(command.requires_external_agent_runner());
|
||||
@@ -1764,6 +1787,29 @@ mod tests {
|
||||
project_path,
|
||||
parent_agent_id: GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID.to_string(),
|
||||
initialize: false,
|
||||
run_profile: AGENT_RUNTIME_RUN_PROFILE_STANDARD.to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swarm_chat_autonomous_game_build_flag_selects_autonomous_profile() {
|
||||
let project_path = std::env::current_dir().expect("current directory");
|
||||
let command = parse_cli_command(&[
|
||||
"--swarm-chat".to_string(),
|
||||
project_path.display().to_string(),
|
||||
"--autonomous-game-build".to_string(),
|
||||
])
|
||||
.expect("parse autonomous supervisor chat")
|
||||
.expect("autonomous supervisor chat command");
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
CliCommand::SwarmChat {
|
||||
project_path,
|
||||
parent_agent_id: GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID.to_string(),
|
||||
initialize: false,
|
||||
run_profile: AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD.to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1778,5 +1824,12 @@ mod tests {
|
||||
"extra".to_string(),
|
||||
])
|
||||
.is_err());
|
||||
assert!(parse_cli_command(&[
|
||||
"--swarm-chat".to_string(),
|
||||
"--autonomous-game-build".to_string(),
|
||||
"--autonomous-game-build".to_string(),
|
||||
"/tmp/game-project".to_string(),
|
||||
])
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ const SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_CURRENT: &str =
|
||||
const SUPERVISOR_COLLABORATION_MAX_STATIC_DELEGATES: usize = 3;
|
||||
const SUPERVISOR_COLLABORATION_MAX_ISOLATED_CHILDREN: usize = 3;
|
||||
const SUPERVISOR_COLLABORATION_MAX_ISOLATED_GROUPS_BEFORE_CLAIM: usize = 16;
|
||||
const AUTONOMOUS_GAME_BUILD_REQUIRED_STATIC_AGENT_IDS: [&str; 2] =
|
||||
["code-prototype", "quality-review"];
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
@@ -65,6 +67,70 @@ impl Default for SupervisorCollaborationPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
fn autonomous_game_build_supervisor_collaboration_policy() -> SupervisorCollaborationPolicy {
|
||||
SupervisorCollaborationPolicy {
|
||||
required_initial_wave: SupervisorInitialCollaborationWave::Static,
|
||||
min_static_delegates: AUTONOMOUS_GAME_BUILD_REQUIRED_STATIC_AGENT_IDS.len(),
|
||||
required_static_agent_ids: AUTONOMOUS_GAME_BUILD_REQUIRED_STATIC_AGENT_IDS
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
..SupervisorCollaborationPolicy::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SupervisorCollaborationUnboundPolicy {
|
||||
policy: SupervisorCollaborationPolicy,
|
||||
source: &'static str,
|
||||
project_policy_status: &'static str,
|
||||
project_policy_present: bool,
|
||||
}
|
||||
|
||||
fn read_supervisor_collaboration_unbound_policy_for_run_at(
|
||||
root: &Path,
|
||||
parent_agent_id: &str,
|
||||
parent_run_id: &str,
|
||||
) -> Result<SupervisorCollaborationUnboundPolicy, String> {
|
||||
let policy_path = root.join(SUPERVISOR_COLLABORATION_POLICY_RELATIVE_PATH);
|
||||
match fs::symlink_metadata(&policy_path) {
|
||||
Ok(_) => {
|
||||
return Ok(SupervisorCollaborationUnboundPolicy {
|
||||
policy: read_supervisor_collaboration_policy_at(root)?,
|
||||
source: "project-policy-unbound",
|
||||
project_policy_status: "current",
|
||||
project_policy_present: true,
|
||||
});
|
||||
}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(error) => {
|
||||
return Err(format!(
|
||||
"读取 Project Supervisor 协作策略文件状态失败:{error}"
|
||||
));
|
||||
}
|
||||
}
|
||||
let (run_profile, _) =
|
||||
agent_runtime_run_profile_identity_at(root, parent_agent_id, parent_run_id, None, None)?;
|
||||
if parent_agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|
||||
&& run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
{
|
||||
return Ok(SupervisorCollaborationUnboundPolicy {
|
||||
policy: normalize_supervisor_collaboration_policy(
|
||||
autonomous_game_build_supervisor_collaboration_policy(),
|
||||
)?,
|
||||
source: "autonomous-run-default",
|
||||
project_policy_status: "absent",
|
||||
project_policy_present: false,
|
||||
});
|
||||
}
|
||||
Ok(SupervisorCollaborationUnboundPolicy {
|
||||
policy: SupervisorCollaborationPolicy::default(),
|
||||
source: "project-policy-unbound",
|
||||
project_policy_status: "current",
|
||||
project_policy_present: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub(crate) struct SupervisorCollaborationState {
|
||||
pub(crate) initial_static_agent_ids: Vec<String>,
|
||||
@@ -656,7 +722,12 @@ pub(crate) fn resolve_supervisor_collaboration_policy_for_run_at(
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
let policy = read_supervisor_collaboration_policy_at(root)?;
|
||||
let unbound_policy = read_supervisor_collaboration_unbound_policy_for_run_at(
|
||||
root,
|
||||
parent_agent_id,
|
||||
parent_run_id,
|
||||
)?;
|
||||
let policy = unbound_policy.policy.clone();
|
||||
let state = read_supervisor_collaboration_state_at(root, parent_agent_id, parent_run_id)?;
|
||||
if state.has_collaboration() {
|
||||
if !game_creator_agent_runtime_run_is_non_terminal_for_collaboration_migration_at(
|
||||
@@ -693,8 +764,8 @@ pub(crate) fn resolve_supervisor_collaboration_policy_for_run_at(
|
||||
policy,
|
||||
snapshot_fingerprint: None,
|
||||
binding_source: None,
|
||||
source: "project-policy-unbound",
|
||||
project_policy_status: "current",
|
||||
source: unbound_policy.source,
|
||||
project_policy_status: unbound_policy.project_policy_status,
|
||||
current_project_policy_fingerprint: None,
|
||||
})
|
||||
}
|
||||
@@ -703,16 +774,23 @@ fn supervisor_collaboration_policy_resolution_from_snapshot(
|
||||
root: &Path,
|
||||
snapshot: SupervisorCollaborationPolicySnapshot,
|
||||
) -> Result<SupervisorCollaborationPolicyResolution, String> {
|
||||
let current_project_policy = read_supervisor_collaboration_policy_at(root);
|
||||
let (project_policy_status, current_project_policy_fingerprint) = match current_project_policy {
|
||||
let current_policy = read_supervisor_collaboration_unbound_policy_for_run_at(
|
||||
root,
|
||||
&snapshot.parent_agent_id,
|
||||
&snapshot.parent_run_id,
|
||||
);
|
||||
let (project_policy_status, current_project_policy_fingerprint) = match current_policy {
|
||||
Ok(current) => {
|
||||
let fingerprint = supervisor_collaboration_policy_fingerprint(¤t)?;
|
||||
let status = if current == snapshot.policy {
|
||||
let fingerprint = current
|
||||
.project_policy_present
|
||||
.then(|| supervisor_collaboration_policy_fingerprint(¤t.policy))
|
||||
.transpose()?;
|
||||
let status = if current.policy == snapshot.policy {
|
||||
"matched"
|
||||
} else {
|
||||
"drifted"
|
||||
};
|
||||
(status, Some(fingerprint))
|
||||
(status, fingerprint)
|
||||
}
|
||||
Err(_) => ("unreadable", None),
|
||||
};
|
||||
|
||||
@@ -457,6 +457,33 @@ pub(crate) fn start_game_creator_agent_runtime_task(
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) fn start_game_creator_supervisor_runtime_task(
|
||||
project_path: String,
|
||||
session_id: Option<String>,
|
||||
task: String,
|
||||
run_id: String,
|
||||
run_profile: Option<String>,
|
||||
) -> Result<AgentRuntimeResult, String> {
|
||||
let root = Path::new(project_path.trim());
|
||||
enforce_project_permission_policy(root, "conversation.read")?;
|
||||
enforce_project_permission_policy(root, "conversation.write")?;
|
||||
enforce_project_permission_policy(root, "agent.run_status")?;
|
||||
let run_profile = run_profile
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or(AGENT_RUNTIME_RUN_PROFILE_STANDARD);
|
||||
start_game_creator_supervisor_background_task_for_session_at(
|
||||
root,
|
||||
session_id.as_deref(),
|
||||
task.trim(),
|
||||
run_id.trim(),
|
||||
AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE,
|
||||
run_profile,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn compact_game_creator_agent_runtime_context(
|
||||
project_path: String,
|
||||
|
||||
@@ -190,6 +190,10 @@ struct AgentRuntimeState {
|
||||
run_id: String,
|
||||
#[serde(default)]
|
||||
source: String,
|
||||
#[serde(default = "default_agent_runtime_run_profile")]
|
||||
run_profile: String,
|
||||
#[serde(default)]
|
||||
run_profile_binding_fingerprint: String,
|
||||
#[serde(default)]
|
||||
parent_agent_id: Option<String>,
|
||||
#[serde(default)]
|
||||
@@ -307,6 +311,10 @@ struct AgentRuntimeSteerRef {
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct AgentRuntimeToolPolicySnapshot {
|
||||
#[serde(default = "default_agent_runtime_run_profile")]
|
||||
run_profile: String,
|
||||
#[serde(default)]
|
||||
run_profile_binding_fingerprint: String,
|
||||
#[serde(default)]
|
||||
allowed_tools: Vec<String>,
|
||||
#[serde(default)]
|
||||
@@ -322,6 +330,8 @@ struct AgentRuntimeToolPolicySnapshot {
|
||||
impl Default for AgentRuntimeToolPolicySnapshot {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
run_profile: default_agent_runtime_run_profile(),
|
||||
run_profile_binding_fingerprint: String::new(),
|
||||
allowed_tools: Vec::new(),
|
||||
auto_tools: Vec::new(),
|
||||
confirm_tools: Vec::new(),
|
||||
@@ -497,6 +507,10 @@ struct AgentRuntimeTaskRecord {
|
||||
run_id: String,
|
||||
#[serde(default)]
|
||||
source: String,
|
||||
#[serde(default = "default_agent_runtime_run_profile")]
|
||||
run_profile: String,
|
||||
#[serde(default)]
|
||||
run_profile_binding_fingerprint: String,
|
||||
#[serde(default)]
|
||||
parent_agent_id: Option<String>,
|
||||
#[serde(default)]
|
||||
@@ -1166,9 +1180,17 @@ const PROJECT_WRITE_LOCK_PATH: &str = ".agent/project.lock";
|
||||
const LOCAL_CONVERSATION_SCHEMA_VERSION: &str = "game-creator-conversation.v1";
|
||||
const AGENT_CONVERSATION_SESSION_SCHEMA_VERSION: &str = "game-creator-agent-sessions.v1";
|
||||
const AGENT_RUNTIME_SCHEMA_VERSION: &str = "game-creator-agent-runtime.v1";
|
||||
const AGENT_RUNTIME_RUN_PROFILE_STANDARD: &str = "standard";
|
||||
const AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD: &str = "autonomous-game-build";
|
||||
const AGENT_RUNTIME_AUTONOMOUS_SOURCE_FIELD_MAX_CHARS: usize = 8_000;
|
||||
const AGENT_RUNTIME_AUTONOMOUS_SOURCE_TOTAL_MAX_CHARS: usize = 10_000;
|
||||
const AGENT_RUNTIME_RECENT_EVENT_LIMIT: usize = 20;
|
||||
const AGENT_RUNTIME_RECENT_TASK_LIMIT: usize = 12;
|
||||
const GAME_CREATOR_CONVERSATION_CONTEXT_MAX_MESSAGES: usize = 12;
|
||||
|
||||
fn default_agent_runtime_run_profile() -> String {
|
||||
AGENT_RUNTIME_RUN_PROFILE_STANDARD.to_string()
|
||||
}
|
||||
const MAX_CANVAS_EXPORT_FILES: usize = 500;
|
||||
const MAX_CANVAS_EXPORT_BYTES: u64 = 512 * 1024 * 1024;
|
||||
const MAX_PROJECT_EXPORT_PACKAGE_FILES: usize = 1200;
|
||||
@@ -1695,6 +1717,7 @@ fn main() {
|
||||
chat_with_game_creator_role_agent,
|
||||
chat_with_game_creator_role_agent_stream,
|
||||
start_game_creator_agent_runtime_task,
|
||||
start_game_creator_supervisor_runtime_task,
|
||||
compact_game_creator_agent_runtime_context,
|
||||
read_game_creator_agent_goal,
|
||||
start_game_creator_agent_goal,
|
||||
|
||||
@@ -1906,6 +1906,8 @@ mod tests {
|
||||
session_id: state.session_id.clone(),
|
||||
run_id: state.run_id.clone(),
|
||||
source: state.source.clone(),
|
||||
run_profile: default_agent_runtime_run_profile(),
|
||||
run_profile_binding_fingerprint: String::new(),
|
||||
task: state.current_task.clone(),
|
||||
goal_id: None,
|
||||
goal_revision: 0,
|
||||
|
||||
@@ -1696,6 +1696,10 @@ pub(crate) fn append_agent_db_tool_plan_audit_idempotent(
|
||||
"normalizedTextSha256",
|
||||
"responseIdSha256",
|
||||
"responseIdChars",
|
||||
"autonomousSourcePayloadValidated",
|
||||
"autonomousSourceMutationActionCount",
|
||||
"autonomousSourceMaxFieldChars",
|
||||
"autonomousSourceTotalChars",
|
||||
];
|
||||
const REPAIR_FIELDS: &[&str] = &[
|
||||
"recordType",
|
||||
@@ -1801,6 +1805,49 @@ pub(crate) fn append_agent_db_tool_plan_audit_idempotent(
|
||||
return Err(format!("Agent DB tool-plan 幂等审计字段无效:{field}"));
|
||||
}
|
||||
}
|
||||
let autonomous_source_payload_validated = record
|
||||
.get("autonomousSourcePayloadValidated")
|
||||
.and_then(serde_json::Value::as_bool)
|
||||
.ok_or_else(|| {
|
||||
"Agent DB tool-plan 幂等审计字段无效:autonomousSourcePayloadValidated"
|
||||
.to_string()
|
||||
})?;
|
||||
let autonomous_source_values = [
|
||||
"autonomousSourceMutationActionCount",
|
||||
"autonomousSourceMaxFieldChars",
|
||||
"autonomousSourceTotalChars",
|
||||
]
|
||||
.map(|field| record.get(field));
|
||||
if autonomous_source_payload_validated {
|
||||
let [Some(mutation_count), Some(max_field_chars), Some(total_chars)] =
|
||||
autonomous_source_values
|
||||
else {
|
||||
return Err("Agent DB tool-plan 自主源码载荷审计缺少数值".to_string());
|
||||
};
|
||||
let Some(mutation_count) = mutation_count.as_u64() else {
|
||||
return Err("Agent DB tool-plan 自主源码动作数量无效".to_string());
|
||||
};
|
||||
let Some(max_field_chars) = max_field_chars.as_u64() else {
|
||||
return Err("Agent DB tool-plan 自主源码字段长度无效".to_string());
|
||||
};
|
||||
let Some(total_chars) = total_chars.as_u64() else {
|
||||
return Err("Agent DB tool-plan 自主源码总长度无效".to_string());
|
||||
};
|
||||
if mutation_count > 1
|
||||
|| max_field_chars
|
||||
> AGENT_RUNTIME_AUTONOMOUS_SOURCE_FIELD_MAX_CHARS as u64
|
||||
|| total_chars
|
||||
> AGENT_RUNTIME_AUTONOMOUS_SOURCE_TOTAL_MAX_CHARS as u64
|
||||
|| total_chars < max_field_chars
|
||||
{
|
||||
return Err("Agent DB tool-plan 自主源码载荷审计越界".to_string());
|
||||
}
|
||||
} else if autonomous_source_values
|
||||
.iter()
|
||||
.any(|value| !matches!(value, Some(serde_json::Value::Null)))
|
||||
{
|
||||
return Err("非自主 tool-plan 不能携带源码载荷数值".to_string());
|
||||
}
|
||||
}
|
||||
"agent.runtime.tool_plan.repair" => {
|
||||
for field in ["protocolErrorSha256", "responsePreviewSha256"] {
|
||||
@@ -5748,7 +5795,7 @@ pub(crate) fn run_limited_local_command_at(
|
||||
}
|
||||
validate_game_html_smoke(&html)?;
|
||||
|
||||
let output = format!("通过:{},{} 字节", game_index_path.display(), html.len());
|
||||
let output = format!("通过:game/index.html,{} 字节", html.len());
|
||||
let log_path = root.join(".agent/logs/command.log");
|
||||
if let Some(parent) = log_path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
@@ -5769,7 +5816,7 @@ pub(crate) fn run_limited_local_command_at(
|
||||
command_id: command_id.to_string(),
|
||||
status: GameCreationAppCommandRunStatus::Completed,
|
||||
output: output.clone(),
|
||||
log_path: log_path.to_string_lossy().into_owned(),
|
||||
log_path: ".agent/logs/command.log".to_string(),
|
||||
updated_at,
|
||||
},
|
||||
)?;
|
||||
@@ -5778,7 +5825,7 @@ pub(crate) fn run_limited_local_command_at(
|
||||
command_id: command_id.to_string(),
|
||||
status: "completed".to_string(),
|
||||
output,
|
||||
log_path: log_path.to_string_lossy().into_owned(),
|
||||
log_path: ".agent/logs/command.log".to_string(),
|
||||
updated_at,
|
||||
})
|
||||
}
|
||||
@@ -8824,6 +8871,10 @@ mod agent_db_security_tests {
|
||||
"normalizedTextSha256": null,
|
||||
"responseIdSha256": null,
|
||||
"responseIdChars": 0,
|
||||
"autonomousSourcePayloadValidated": false,
|
||||
"autonomousSourceMutationActionCount": null,
|
||||
"autonomousSourceMaxFieldChars": null,
|
||||
"autonomousSourceTotalChars": null,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9725,6 +9776,37 @@ mod agent_db_security_tests {
|
||||
fs::remove_dir_all(root).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_plan_audit_accepts_current_autonomous_source_limits() {
|
||||
let root = unique_agent_db_test_root("tool-plan-audit-autonomous-source-limits");
|
||||
fs::create_dir_all(&root).expect("create autonomous source audit project");
|
||||
let mut record = tool_plan_protocol_audit_record(
|
||||
"code-prototype",
|
||||
"autonomous-source-limit-run",
|
||||
"loop-0-repair-0",
|
||||
);
|
||||
record["autonomousSourcePayloadValidated"] = serde_json::json!(true);
|
||||
record["autonomousSourceMutationActionCount"] = serde_json::json!(1);
|
||||
record["autonomousSourceMaxFieldChars"] =
|
||||
serde_json::json!(AGENT_RUNTIME_AUTONOMOUS_SOURCE_FIELD_MAX_CHARS);
|
||||
record["autonomousSourceTotalChars"] =
|
||||
serde_json::json!(AGENT_RUNTIME_AUTONOMOUS_SOURCE_TOTAL_MAX_CHARS);
|
||||
|
||||
assert!(
|
||||
append_agent_db_tool_plan_audit_idempotent(&root, record.clone())
|
||||
.expect("append current autonomous source limits")
|
||||
);
|
||||
|
||||
record["requestSlot"] = serde_json::json!("loop-1-repair-0");
|
||||
record["autonomousSourceMaxFieldChars"] =
|
||||
serde_json::json!(AGENT_RUNTIME_AUTONOMOUS_SOURCE_FIELD_MAX_CHARS + 1);
|
||||
let error = append_agent_db_tool_plan_audit_idempotent(&root, record)
|
||||
.expect_err("reject autonomous source field above current limit");
|
||||
assert_eq!(error, "Agent DB tool-plan 自主源码载荷审计越界");
|
||||
|
||||
fs::remove_dir_all(root).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn action_group_append_repairs_torn_tail_and_scans_past_bounded_history() {
|
||||
const RECORD_TYPE: &str = "agent.runtime.agent.isolated_join.claimed_by_parent";
|
||||
|
||||
@@ -33,7 +33,7 @@ const EXTERNAL_AGENT_RUNNER_CONNECT_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
const EXTERNAL_AGENT_RUNNER_IO_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const EXTERNAL_AGENT_RUNNER_CONTEXT_COMPACTION_IO_TIMEOUT: Duration = Duration::from_secs(6 * 60);
|
||||
const EXTERNAL_AGENT_RUNNER_MCP_STATUS_IO_TIMEOUT: Duration = Duration::from_secs(6 * 60);
|
||||
const EXTERNAL_AGENT_RUNNER_START_TIMEOUT: Duration = Duration::from_secs(6);
|
||||
const EXTERNAL_AGENT_RUNNER_START_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const EXTERNAL_AGENT_RUNNER_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(2);
|
||||
const EXTERNAL_AGENT_RUNNER_LOOP_INTERVAL: Duration = Duration::from_millis(25);
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -3957,6 +3957,11 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runner_start_timeout_covers_cold_debug_binary_fingerprinting() {
|
||||
assert!(EXTERNAL_AGENT_RUNNER_START_TIMEOUT >= Duration::from_secs(30));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_reuse_requires_current_protocol_and_executable_identity() {
|
||||
let current_fingerprint = "b".repeat(64);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -103,7 +103,24 @@ pub(crate) fn open_project_supervisor_chat_window(
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if let Some(existing) = app.get_webview_window("supervisor-chat") {
|
||||
existing.close().map_err(|error| error.to_string())?;
|
||||
let mut current_url = existing.url().map_err(|error| error.to_string())?;
|
||||
let current_project_path = current_url
|
||||
.query_pairs()
|
||||
.find_map(|(key, value)| (key == "projectPath").then(|| value.into_owned()));
|
||||
if current_project_path.as_deref() != Some(project_path) {
|
||||
current_url.set_query(Some(&format!(
|
||||
"supervisor-chat&projectPath={}",
|
||||
percent_encode_query_value(project_path)
|
||||
)));
|
||||
current_url.set_fragment(None);
|
||||
existing
|
||||
.navigate(current_url)
|
||||
.map_err(|error| error.to_string())?;
|
||||
}
|
||||
existing.show().map_err(|error| error.to_string())?;
|
||||
existing.unminimize().map_err(|error| error.to_string())?;
|
||||
existing.set_focus().map_err(|error| error.to_string())?;
|
||||
return Ok(());
|
||||
}
|
||||
tauri::WebviewWindowBuilder::new(
|
||||
&app,
|
||||
|
||||
@@ -264,6 +264,8 @@ interface AgentRuntimeState {
|
||||
sessionId: string;
|
||||
runId: string;
|
||||
source: string;
|
||||
runProfile?: 'standard' | 'autonomous-game-build';
|
||||
runProfileBindingFingerprint?: string;
|
||||
parentAgentId?: string | null;
|
||||
parentRunId?: string | null;
|
||||
delegationId?: string | null;
|
||||
@@ -336,6 +338,8 @@ interface AgentRuntimeContextCompactionResult {
|
||||
}
|
||||
|
||||
interface AgentRuntimeToolPolicySnapshot {
|
||||
runProfile?: 'standard' | 'autonomous-game-build';
|
||||
runProfileBindingFingerprint?: string;
|
||||
allowedTools: string[];
|
||||
autoTools: string[];
|
||||
confirmTools: string[];
|
||||
@@ -421,6 +425,8 @@ interface AgentRuntimeEventRecord {
|
||||
sessionId: string;
|
||||
runId: string;
|
||||
source: string;
|
||||
runProfile?: 'standard' | 'autonomous-game-build';
|
||||
runProfileBindingFingerprint?: string;
|
||||
eventType: string;
|
||||
status: string;
|
||||
phase: string;
|
||||
@@ -1562,12 +1568,14 @@ async function submitProjectSupervisorRuntimeTask({
|
||||
sessionId,
|
||||
prompt,
|
||||
runtime,
|
||||
runProfile,
|
||||
}: {
|
||||
invoke: TauriInvoke;
|
||||
projectPath: string;
|
||||
sessionId: string;
|
||||
prompt: string;
|
||||
runtime: AgentRuntimeState | null;
|
||||
runProfile: 'standard' | 'autonomous-game-build';
|
||||
}) {
|
||||
const steerRuntime = matchingAgentRuntimeForSteer(
|
||||
[runtime],
|
||||
@@ -1589,13 +1597,13 @@ async function submitProjectSupervisorRuntimeTask({
|
||||
return { mode: 'steer' as const, runtimeResult: steer.runtime };
|
||||
}
|
||||
const runtimeResult = await invoke<AgentRuntimeResult>(
|
||||
'start_game_creator_agent_runtime_task',
|
||||
'start_game_creator_supervisor_runtime_task',
|
||||
{
|
||||
projectPath,
|
||||
agentId: PROJECT_SUPERVISOR_AGENT_ID,
|
||||
sessionId,
|
||||
task: prompt,
|
||||
runId: createAgentChatRunId('project-supervisor-task'),
|
||||
runProfile,
|
||||
},
|
||||
);
|
||||
return { mode: 'start' as const, runtimeResult };
|
||||
@@ -6833,6 +6841,7 @@ export function WorkspaceLauncher({
|
||||
sessionId,
|
||||
prompt: buildHomeConversationContent(mode, prompt, attachments),
|
||||
runtime: null,
|
||||
runProfile: 'autonomous-game-build',
|
||||
});
|
||||
setStatus('已创建项目并交给项目总控 Agent');
|
||||
} catch (error) {
|
||||
@@ -23215,6 +23224,7 @@ export function App({
|
||||
sessionId,
|
||||
prompt,
|
||||
runtime: projectSupervisorRuntimeRef.current,
|
||||
runProfile: supervisorChatOnly ? 'standard' : 'autonomous-game-build',
|
||||
});
|
||||
const runtimeResult = submission.runtimeResult;
|
||||
setCommandLog((current) => [
|
||||
|
||||
@@ -253,6 +253,7 @@ function createProjectSupervisorRuntimeHarness({
|
||||
initialRuntime,
|
||||
initialResponseStream = null,
|
||||
runtimeMapLoader,
|
||||
expectedRunProfile = 'autonomous-game-build',
|
||||
}: {
|
||||
projectPath?: string;
|
||||
sessionId?: string;
|
||||
@@ -262,6 +263,7 @@ function createProjectSupervisorRuntimeHarness({
|
||||
initialRuntime?: Record<string, unknown>;
|
||||
initialResponseStream?: Record<string, unknown> | null;
|
||||
runtimeMapLoader?: () => Promise<Array<Record<string, unknown>>>;
|
||||
expectedRunProfile?: 'standard' | 'autonomous-game-build';
|
||||
} = {}) {
|
||||
const manifest = createGameCreationAppManifest(
|
||||
'local-project-draft',
|
||||
@@ -470,7 +472,10 @@ function createProjectSupervisorRuntimeHarness({
|
||||
const states = runtimeMapLoader ? await runtimeMapLoader() : [];
|
||||
return states.map((state) => runtimeResult(state));
|
||||
}
|
||||
if (command === 'start_game_creator_agent_runtime_task') {
|
||||
if (command === 'start_game_creator_supervisor_runtime_task') {
|
||||
if (args?.runProfile !== expectedRunProfile) {
|
||||
throw new Error('unexpected Project Supervisor run profile');
|
||||
}
|
||||
const runId = String(args?.runId ?? '');
|
||||
currentSupervisorMessages.push(
|
||||
conversationRecord(
|
||||
@@ -6859,15 +6864,15 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
title: '项目总控',
|
||||
});
|
||||
const startCalls = invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
);
|
||||
expect(startCalls).toHaveLength(1);
|
||||
expect(startCalls[0]?.[1]).toMatchObject({
|
||||
projectPath: '/tmp/home-created-game',
|
||||
agentId: 'project-supervisor',
|
||||
sessionId: supervisorHarness.sessionId,
|
||||
task: expect.stringContaining('初始意图:art / 做素材'),
|
||||
runId: expect.stringMatching(/^project-supervisor-task-/),
|
||||
runProfile: 'autonomous-game-build',
|
||||
});
|
||||
expect(startCalls[0]?.[1]).toMatchObject({
|
||||
task: expect.stringContaining(
|
||||
@@ -6914,6 +6919,7 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
const historyMessage = '已持久化的项目总控历史';
|
||||
const harness = createProjectSupervisorRuntimeHarness({
|
||||
projectPath,
|
||||
expectedRunProfile: 'standard',
|
||||
supervisorMessages: [
|
||||
{
|
||||
schemaVersion: 'game-creator-conversation.v1',
|
||||
@@ -6964,13 +6970,13 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(harness.invoke).toHaveBeenCalledWith(
|
||||
'start_game_creator_agent_runtime_task',
|
||||
'start_game_creator_supervisor_runtime_task',
|
||||
{
|
||||
projectPath,
|
||||
agentId: 'project-supervisor',
|
||||
sessionId: harness.sessionId,
|
||||
task: '继续完成可玩原型',
|
||||
runId: expect.stringMatching(/^project-supervisor-task-/),
|
||||
runProfile: 'standard',
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -7320,18 +7326,18 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(invoke).toHaveBeenCalledWith(
|
||||
'start_game_creator_agent_runtime_task',
|
||||
'start_game_creator_supervisor_runtime_task',
|
||||
{
|
||||
projectPath,
|
||||
agentId: 'project-supervisor',
|
||||
sessionId: supervisorHarness.sessionId,
|
||||
task: '先完成正式客户端玩法拆解',
|
||||
runId: expect.stringMatching(/^project-supervisor-task-/),
|
||||
runProfile: 'autonomous-game-build',
|
||||
},
|
||||
);
|
||||
});
|
||||
const startCall = invoke.mock.calls.find(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
);
|
||||
const runId = String(startCall?.[1]?.runId ?? '');
|
||||
await waitFor(() => {
|
||||
@@ -7365,7 +7371,7 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
});
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
expect(
|
||||
@@ -27653,13 +27659,13 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(harness.invoke).toHaveBeenCalledWith(
|
||||
'start_game_creator_agent_runtime_task',
|
||||
'start_game_creator_supervisor_runtime_task',
|
||||
{
|
||||
projectPath: harness.projectPath,
|
||||
agentId: 'project-supervisor',
|
||||
sessionId: harness.sessionId,
|
||||
task: '做一个反弹弹幕厨房游戏',
|
||||
runId: expect.stringMatching(/^project-supervisor-task-/),
|
||||
runProfile: 'autonomous-game-build',
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -27896,12 +27902,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
harness.invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
const startCall = harness.invoke.mock.calls.find(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
);
|
||||
const newRunId = String(startCall?.[1]?.runId ?? '');
|
||||
expect(newRunId).not.toBe('');
|
||||
@@ -28033,12 +28039,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
harness.invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
const startCall = harness.invoke.mock.calls.find(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
);
|
||||
const startedRunId = String(startCall?.[1]?.runId ?? '');
|
||||
await waitFor(() => {
|
||||
@@ -28065,7 +28071,7 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
});
|
||||
expect(
|
||||
harness.invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
@@ -28635,12 +28641,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
harness.invoke.mock.calls.filter(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
const startCall = harness.invoke.mock.calls.find(
|
||||
([command]) => command === 'start_game_creator_agent_runtime_task',
|
||||
([command]) => command === 'start_game_creator_supervisor_runtime_task',
|
||||
);
|
||||
const runId = String(startCall?.[1]?.runId ?? '');
|
||||
const respondingRuntime = harness.runtimeState({
|
||||
|
||||
@@ -5132,9 +5132,9 @@
|
||||
## 2026-07-20 开发态 Project Supervisor 纯聊天独立窗口
|
||||
|
||||
- 背景:开发人员需要一个不依赖正式产品布局的最小 GUI,用于直接验证 `project-supervisor` 的持久多轮对话与 Runtime 行为。
|
||||
- 入口决策:仅 debug 构建提供独立 Tauri 窗口,使用 `index.html?supervisor-chat&projectPath=...` 路由,并从现有 `index.html?agent-chat` 开发入口打开;`projectPath` 是 URL 编码后的项目绝对路径。
|
||||
- 复用边界:窗口固定使用 `project-supervisor`,继续复用现有 active Session、External Runner、Agent Runtime、AppData 配置与持久 conversation;不创建新聊天后端、本地 HTTP 服务、数据库或平行配置体系。
|
||||
- UI 边界:只显示持久消息区、输入框、必要的等待 / 错误状态和设置;不显示 Agent picker、Session / Goal / Runtime 面板或专业 Agent 协作栏。Session 控制面可隐藏,但对话仍按 `project-supervisor` active Session 持久化。
|
||||
- 入口决策:当前仅 Tauri dev 提供独立窗口,使用 `index.html?supervisor-chat&projectPath=...` 路由,并从现有 `index.html?agent-chat` 开发入口打开;`projectPath` 是 URL 编码后的项目绝对路径。重复打开同一项目只恢复并聚焦原窗口,切换项目时在同一窗口导航,不销毁未发送输入。
|
||||
- 复用边界:窗口固定使用 `project-supervisor`,新 Run 固定选择 `standard` profile,继续复用现有 active Session、External Runner、Agent Runtime、AppData 配置与持久 conversation;不创建新聊天后端、本地 HTTP 服务、数据库或平行配置体系,也不继承正式构建流程的自主交付 profile。
|
||||
- UI 边界:只显示持久消息区、输入框、必要的等待 / 错误状态、工具确认 / 用户追问卡片和设置;不显示 Agent picker、Session / Goal / 完整 Runtime 面板或专业 Agent 协作栏。Session 控制面可隐藏,但对话仍按 `project-supervisor` active Session 持久化;`supervisor-chat` 纳入只读 Tauri event capability,保证同一 Runtime 的状态更新可实时到达窗口。
|
||||
- 产品边界:该窗口只是开发验证入口,不替换、不修改正式用户 `client` 窗口及其登录、首页和项目开发流程。
|
||||
|
||||
## 2026-07-18 AI 游戏创作项目工作台 Runtime 状态投影
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
## 开发态 Project Supervisor 纯聊天独立窗口
|
||||
|
||||
- 入口边界:仅 debug 构建提供独立 Tauri 窗口,路由为 `index.html?supervisor-chat&projectPath=...`,其中 `projectPath` 传入 URL 编码后的项目绝对路径。窗口从现有 `index.html?agent-chat` 开发入口打开,不增加正式用户入口。
|
||||
- Runtime 边界:窗口固定对话 Agent 为 `project-supervisor`,复用现有 active Session、External Runner、Agent Runtime、AppData 配置和持久 conversation;不新建平行会话库、Runner 或配置存储。
|
||||
- 界面边界:只显示持久消息区、输入框、必要的等待 / 错误状态和设置入口;不显示 Agent picker、Session 面板、Goal 面板、Runtime 面板或专业 Agent 协作栏。会话历史仍绑定 `project-supervisor` 的 active Session 持久化,不因隐藏 Session 控制面而变为临时聊天。
|
||||
- 入口边界:当前仅 Tauri dev 提供独立窗口,路由为 `index.html?supervisor-chat&projectPath=...`,其中 `projectPath` 传入 URL 编码后的项目绝对路径。窗口从现有 `index.html?agent-chat` 开发入口打开,不增加正式用户入口;重复打开同一项目只恢复并聚焦原窗口,切换项目时在同一窗口导航。
|
||||
- Runtime 边界:窗口固定对话 Agent 为 `project-supervisor`,新 Run 使用 `standard` profile,复用现有 active Session、External Runner、Agent Runtime、AppData 配置和持久 conversation;不新建平行会话库、Runner 或配置存储,也不把该测试入口隐式切成自动修改项目的自主构建模式。
|
||||
- 界面边界:只显示持久消息区、输入框、必要的等待 / 错误状态、工具确认 / 用户追问卡片和设置入口;不显示 Agent picker、Session 面板、Goal 面板、完整 Runtime 面板或专业 Agent 协作栏。会话历史仍绑定 `project-supervisor` 的 active Session 持久化,不因隐藏 Session 控制面而变为临时聊天;`supervisor-chat` 窗口必须具备只读 Tauri event listen / unlisten capability,以实时接收 Runtime 更新。
|
||||
- 产品边界:正式用户 `client` 窗口、登录后首页和项目开发流程保持不变,不暴露该开发验证面。
|
||||
|
||||
## Runtime 边界
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
"ai-game-creator-shell:agent-run:smoke": "npm --prefix apps/ai-game-creator-shell run agent-run:smoke",
|
||||
"ai-game-creator-shell:agent-runtime:real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:real-e2e --",
|
||||
"ai-game-creator-shell:agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-autonomous-chat-real-e2e --",
|
||||
"ai-game-creator-shell:agent-runtime:supervisor-autonomous-playable-lane-defense-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-autonomous-playable-lane-defense-real-e2e --",
|
||||
"ai-game-creator-shell:agent-runtime:supervisor-swarm-transient-retry-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-transient-retry-real-e2e --",
|
||||
"ai-game-creator-shell:agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e --",
|
||||
"ai-game-creator-shell:agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e --",
|
||||
|
||||
Reference in New Issue
Block a user