Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0847b1bb83 | |||
| 8f691d4c8c | |||
| d3b9f326f0 | |||
| 7eb2326a76 | |||
| 9fc28790a4 | |||
| cea45a608d | |||
| ee03ade156 | |||
| 4933c6c380 | |||
| 931f3eae0a | |||
| c5f8fa1ef6 |
@@ -957,9 +957,6 @@ async function runInteractiveCargo(cliArguments, setActiveChild) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立项策划跑 standard 档,`agent.delegate` 这类动作按项目权限策略必须逐个确认,
|
|
||||||
// 而确认和问询都只从 CLI 的 stdin 读。自主构建档没有这一步,所以只有 --plan 需要
|
|
||||||
// 一个把「人坐在终端前敲 approve」自动化掉的应答器;判据本身仍然走后端确认命令。
|
|
||||||
const swarmConfirmationPromptPattern = /输入 approve 或 reject:$/u;
|
const swarmConfirmationPromptPattern = /输入 approve 或 reject:$/u;
|
||||||
const swarmUserInputPromptPattern = /请选择 1-\d+,或直接输入其他答案:$/u;
|
const swarmUserInputPromptPattern = /请选择 1-\d+,或直接输入其他答案:$/u;
|
||||||
|
|
||||||
|
|||||||
@@ -795,15 +795,10 @@ pub(crate) async fn continue_design_agent_at(
|
|||||||
.ok_or("策划 Agent 当前正在工作")?;
|
.ok_or("策划 Agent 当前正在工作")?;
|
||||||
let mut session = match read_design_session(root)? {
|
let mut session = match read_design_session(root)? {
|
||||||
Some(session) => session,
|
Some(session) => session,
|
||||||
None => {
|
None => new_design_session(
|
||||||
if read_planning_session_v2(root)?.is_some() {
|
&project_id,
|
||||||
return Err("此项目包含旧策划会话,请查看原有记录或在新项目开始五阶段策划".into());
|
&load_game_creator_app_config()?.selected_model_id,
|
||||||
}
|
),
|
||||||
new_design_session(
|
|
||||||
&project_id,
|
|
||||||
&load_game_creator_app_config()?.selected_model_id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if session.project_id != project_id {
|
if session.project_id != project_id {
|
||||||
return Err("策划会话与当前项目不匹配".into());
|
return Err("策划会话与当前项目不匹配".into());
|
||||||
|
|||||||
@@ -78,12 +78,6 @@ pub(super) fn game_creator_agent_final_reply_error_allows_fallback(error: &str)
|
|||||||
matches!(kind.as_str(), "empty-response" | "deserialize")
|
matches!(kind.as_str(), "empty-response" | "deserialize")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 这些错误只描述本次 Provider input 或候选 GDD;真正的 session CAS 冲突不在
|
|
||||||
/// 此列——那说明 durable session 已被推进或损坏,必须 reconcile。
|
|
||||||
fn plan_submit_error_is_business_rejection(error: &PlanningStorageError) -> bool {
|
|
||||||
matches!(error.code(), "PLAN_INVALID_REQUEST" | "PLAN_SIZE_LIMIT")
|
|
||||||
}
|
|
||||||
|
|
||||||
const AGENT_RUNTIME_PLAN_UPDATE_IDLE_LIMIT: u32 = 4;
|
const AGENT_RUNTIME_PLAN_UPDATE_IDLE_LIMIT: u32 = 4;
|
||||||
|
|
||||||
/// 最终回复被收束门禁拦下后 run 会原地续跑重试。多数 blocker 是模型自己能解的
|
/// 最终回复被收束门禁拦下后 run 会原地续跑重试。多数 blocker 是模型自己能解的
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ mod design_session;
|
|||||||
mod finalization;
|
mod finalization;
|
||||||
mod json_sidecar;
|
mod json_sidecar;
|
||||||
mod models;
|
mod models;
|
||||||
mod planning_gdd_model;
|
|
||||||
mod planning_policy_v2;
|
|
||||||
mod planning_session_v2;
|
|
||||||
mod provider_control;
|
mod provider_control;
|
||||||
mod provider_retry;
|
mod provider_retry;
|
||||||
mod real_e2e_checkpoint;
|
mod real_e2e_checkpoint;
|
||||||
@@ -24,9 +21,6 @@ pub(crate) use design_session::*;
|
|||||||
pub(in crate::agent) use finalization::*;
|
pub(in crate::agent) use finalization::*;
|
||||||
pub(in crate::agent) use json_sidecar::*;
|
pub(in crate::agent) use json_sidecar::*;
|
||||||
pub(in crate::agent) use models::*;
|
pub(in crate::agent) use models::*;
|
||||||
pub(crate) use planning_gdd_model::*;
|
|
||||||
pub(crate) use planning_policy_v2::*;
|
|
||||||
pub(crate) use planning_session_v2::*;
|
|
||||||
pub(in crate::agent) use provider_control::*;
|
pub(in crate::agent) use provider_control::*;
|
||||||
pub(in crate::agent) use provider_retry::*;
|
pub(in crate::agent) use provider_retry::*;
|
||||||
pub(in crate::agent) use real_e2e_checkpoint::*;
|
pub(in crate::agent) use real_e2e_checkpoint::*;
|
||||||
|
|||||||
-1082
File diff suppressed because it is too large
Load Diff
-2021
File diff suppressed because it is too large
Load Diff
-1756
File diff suppressed because it is too large
Load Diff
@@ -2628,10 +2628,6 @@ fn main() {
|
|||||||
chat_with_game_creator_role_agent,
|
chat_with_game_creator_role_agent,
|
||||||
chat_with_game_creator_role_agent_stream,
|
chat_with_game_creator_role_agent_stream,
|
||||||
chat_with_game_creator_direct_codex,
|
chat_with_game_creator_direct_codex,
|
||||||
start_planning_session_v2,
|
|
||||||
continue_planning_session_v2,
|
|
||||||
decide_planning_artifact_v2,
|
|
||||||
hydrate_planning_session_v2,
|
|
||||||
hydrate_design_agent_session,
|
hydrate_design_agent_session,
|
||||||
reset_design_agent_session,
|
reset_design_agent_session,
|
||||||
get_design_agent_runtime_mode,
|
get_design_agent_runtime_mode,
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ pub(crate) fn project_write_lock_reclaim(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `.agent/project.lock` 的争用错误前缀。`project_gates.rs`、`provider_recovery.rs`、
|
/// `.agent/project.lock` 的争用错误前缀。`project_gates.rs`、`provider_recovery.rs`、
|
||||||
/// `planning_session_v2.rs`、`direct_runtime.rs` 和前端 `App.tsx` 都按这个前缀把争用
|
/// `direct_runtime.rs` 和前端 `App.tsx` 都按这个前缀把争用
|
||||||
/// 识别成"可以等一下"的瞬时状态;文案扩展时要保持前缀逐字不变。
|
/// 识别成"可以等一下"的瞬时状态;文案扩展时要保持前缀逐字不变。
|
||||||
pub(crate) const PROJECT_WRITE_LOCK_CONTENTION_PREFIX: &str = "项目正在被其他写操作占用:";
|
pub(crate) const PROJECT_WRITE_LOCK_CONTENTION_PREFIX: &str = "项目正在被其他写操作占用:";
|
||||||
|
|
||||||
@@ -476,7 +476,7 @@ impl ProjectWriteLockFailure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 零等待入口的文案。可重试的失败保持争用前缀逐字不变:`provider_recovery.rs`、
|
/// 零等待入口的文案。可重试的失败保持争用前缀逐字不变:`provider_recovery.rs`、
|
||||||
/// `planning_session_v2.rs`、`direct_runtime.rs` 和前端 `App.tsx` 都按这个前缀把错误
|
/// `direct_runtime.rs` 和前端 `App.tsx` 都按这个前缀把错误
|
||||||
/// 当成可等待的瞬时状态,改前缀等于顺手改掉它们的重试语义。
|
/// 当成可等待的瞬时状态,改前缀等于顺手改掉它们的重试语义。
|
||||||
pub(crate) fn message(&self) -> String {
|
pub(crate) fn message(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -229,7 +229,6 @@ import {
|
|||||||
parseRememberInput,
|
parseRememberInput,
|
||||||
} from './features/project-workspace/memoryCommands';
|
} from './features/project-workspace/memoryCommands';
|
||||||
import { pendingCommandDetail } from './features/project-workspace/pendingCommandPresentation';
|
import { pendingCommandDetail } from './features/project-workspace/pendingCommandPresentation';
|
||||||
import { planningStateNeedsRuntimeRefresh } from './features/project-workspace/planningLane';
|
|
||||||
import {
|
import {
|
||||||
type PlanningApprovalCommandResultV2,
|
type PlanningApprovalCommandResultV2,
|
||||||
planningMessagesToChatMessages,
|
planningMessagesToChatMessages,
|
||||||
@@ -1202,39 +1201,6 @@ export function App({
|
|||||||
void hydratePlanGddState(targetProjectPath);
|
void hydratePlanGddState(targetProjectPath);
|
||||||
}, [hydratePlanGddState, localProject?.projectPath]);
|
}, [hydratePlanGddState, localProject?.projectPath]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 存在性判据故意走 `status`(必选字段,为 `undefined` 当且仅当 runtime 为 null)而不是整个
|
|
||||||
// 对象:依赖里只挖 phase/status/updatedAt 三个标量,是为了只在监工状态真的动了时
|
|
||||||
// 重灌。把 `projectSupervisorRuntime` 本体写进依赖会让每一轮轮询新建的对象身份都触发一次
|
|
||||||
// hydrate,白烧 IPC。
|
|
||||||
if (
|
|
||||||
!localProject?.projectPath ||
|
|
||||||
projectSupervisorRuntime?.status === undefined
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 后端 hydrate 会抢项目写锁并扫 authority,不是纯内存读。没有这道门,做游戏和做
|
|
||||||
// 素材链路的每一拍监工心跳都会去抢一次项目写锁——而那两条链路根本不产生策划状态。
|
|
||||||
// 策划状态读 ref 而不进依赖:hydrate 成功就会换一个 `planGddState` 对象身份,写进
|
|
||||||
// 依赖等于 hydrate 触发 hydrate。
|
|
||||||
if (
|
|
||||||
!planningStateNeedsRuntimeRefresh(
|
|
||||||
projectSupervisorRuntime?.source,
|
|
||||||
planGddStateRef.current,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void hydratePlanGddState(localProject.projectPath);
|
|
||||||
}, [
|
|
||||||
hydratePlanGddState,
|
|
||||||
localProject?.projectPath,
|
|
||||||
projectSupervisorRuntime?.phase,
|
|
||||||
projectSupervisorRuntime?.source,
|
|
||||||
projectSupervisorRuntime?.status,
|
|
||||||
projectSupervisorRuntime?.updatedAt,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hydrateOnResume = () => {
|
const hydrateOnResume = () => {
|
||||||
if (document.visibilityState === 'hidden' || !localProject?.projectPath) {
|
if (document.visibilityState === 'hidden' || !localProject?.projectPath) {
|
||||||
|
|||||||
@@ -676,7 +676,6 @@ export function ProjectSupervisorRuntimePanel({
|
|||||||
error: string;
|
error: string;
|
||||||
runtimeByAgentId: Record<string, AgentRuntimeState | undefined>;
|
runtimeByAgentId: Record<string, AgentRuntimeState | undefined>;
|
||||||
controlBusy: boolean;
|
controlBusy: boolean;
|
||||||
planGddAwaitingDecision?: boolean;
|
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
professionalResultsByAgentId: Record<
|
professionalResultsByAgentId: Record<
|
||||||
string,
|
string,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
-171
@@ -1,171 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
AgentRuntimeState,
|
|
||||||
AgentRuntimeUserInputRequest,
|
|
||||||
} from '../../app/types';
|
|
||||||
import {
|
|
||||||
AgentRuntimeUserInputCard,
|
|
||||||
projectRuntimeVisibleError,
|
|
||||||
} from '../agent-runtime';
|
|
||||||
|
|
||||||
type PlanningLaneRuntimeStripProps = {
|
|
||||||
runtime: AgentRuntimeState | null;
|
|
||||||
error: string;
|
|
||||||
controlBusy: boolean;
|
|
||||||
readOnly?: boolean;
|
|
||||||
onSupervisorRetry: (runtime: AgentRuntimeState) => Promise<string>;
|
|
||||||
onUserInput: (
|
|
||||||
request: AgentRuntimeUserInputRequest,
|
|
||||||
responseId: string,
|
|
||||||
answers: Record<string, string>,
|
|
||||||
) => void | Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 立项策划链路下替代 `ProjectSupervisorRuntimePanel` 的窄条。
|
|
||||||
*
|
|
||||||
* 完整面板是为做游戏链路设计的:十几个专业 Agent、多步计划、逐 Agent 重试。套到
|
|
||||||
* 策划链路上,子 Agent 永远只有 `project-planning` 一个,计划永远一两步,「专业
|
|
||||||
* Agent 协作:1」永远是 1——它把 D11 的「总控 + 委派子 Run」拓扑整个漏给了用户,而
|
|
||||||
* 用户的心智模型是在跟一个策划聊天。状态本身由顶部的 `PlanGddStageProgress` 承担。
|
|
||||||
*
|
|
||||||
* 这里只画真正需要用户动手的两样:澄清问答卡,以及失败后的恢复入口。其余时候
|
|
||||||
* 返回 null,不占一行。
|
|
||||||
*
|
|
||||||
* 完整面板在 `waiting-for-user-input` 却读不到 `userInputRequest` 时会画一句
|
|
||||||
* 「待回答问题未能读取」。策划链路里这个组合出现在子 Run 退出到父 Run 醒来之间的
|
|
||||||
* 瞬时窗口,以及审批等待(交互面是审批卡)——两种都不是读取失败,所以这里不画。
|
|
||||||
*/
|
|
||||||
export function PlanningLaneRuntimeStrip({
|
|
||||||
runtime,
|
|
||||||
error,
|
|
||||||
controlBusy,
|
|
||||||
readOnly = false,
|
|
||||||
onSupervisorRetry,
|
|
||||||
onUserInput,
|
|
||||||
}: PlanningLaneRuntimeStripProps) {
|
|
||||||
const [retrySubmitting, setRetrySubmitting] = useState(false);
|
|
||||||
const [retryAccepted, setRetryAccepted] = useState(false);
|
|
||||||
const [retryFeedback, setRetryFeedback] = useState('');
|
|
||||||
useEffect(() => {
|
|
||||||
setRetrySubmitting(false);
|
|
||||||
setRetryAccepted(false);
|
|
||||||
if (runtime?.status === 'failed' || runtime?.phase === 'failed') {
|
|
||||||
setRetryFeedback('');
|
|
||||||
}
|
|
||||||
}, [runtime?.phase, runtime?.runId, runtime?.status]);
|
|
||||||
|
|
||||||
const userInputRequest = readOnly
|
|
||||||
? null
|
|
||||||
: (runtime?.userInputRequest ?? null);
|
|
||||||
const needsReconciliation = Boolean(
|
|
||||||
runtime &&
|
|
||||||
(runtime.status === 'needs-reconciliation' ||
|
|
||||||
runtime.phase === 'needs-reconciliation'),
|
|
||||||
);
|
|
||||||
// Planning V2 has no supported manual retry path. Its Provider failure is
|
|
||||||
// terminal for the current session; exposing the generic Supervisor retry
|
|
||||||
// would incorrectly enter the retired V1 Runtime and report a busy service.
|
|
||||||
const showRecovery = false;
|
|
||||||
// 与完整面板同源:App 层的操作错误(如「请先回答当前的澄清问题」)优先,其次是
|
|
||||||
// run 自己记下的失败原因。这是原面板里唯一真正面向用户的一行文字,照搬。
|
|
||||||
const rawErrorDetail = error || runtime?.error || '';
|
|
||||||
const errorDetail = rawErrorDetail
|
|
||||||
? projectRuntimeVisibleError(rawErrorDetail, '项目总控 Agent', true)
|
|
||||||
: '';
|
|
||||||
|
|
||||||
if (!userInputRequest && !showRecovery && !errorDetail) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className="agent-runtime-status planning-lane-runtime-strip"
|
|
||||||
aria-label="立项策划运行状态"
|
|
||||||
>
|
|
||||||
{errorDetail ? (
|
|
||||||
<small className="project-runtime-error" role="alert">
|
|
||||||
{errorDetail}
|
|
||||||
</small>
|
|
||||||
) : null}
|
|
||||||
{showRecovery && runtime ? (
|
|
||||||
<div
|
|
||||||
className="project-runtime-recovery"
|
|
||||||
aria-label={
|
|
||||||
needsReconciliation ? '立项策划待核对恢复' : '立项策划失败恢复'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{needsReconciliation ? (
|
|
||||||
<>
|
|
||||||
本轮工具动作的结果不确定,需要先结束旧任务。
|
|
||||||
<small>不会直接重试,避免重复执行未核对的动作。</small>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
本轮策划已停止。
|
|
||||||
<small>在当前项目重新启动策划,不会新建项目。</small>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={controlBusy || retrySubmitting || retryAccepted}
|
|
||||||
onClick={() => {
|
|
||||||
setRetryFeedback(
|
|
||||||
needsReconciliation
|
|
||||||
? '正在结束待核对的旧任务…'
|
|
||||||
: '正在重新启动策划…',
|
|
||||||
);
|
|
||||||
setRetrySubmitting(true);
|
|
||||||
void onSupervisorRetry(runtime)
|
|
||||||
.then((message) => {
|
|
||||||
setRetryAccepted(true);
|
|
||||||
setRetryFeedback(message);
|
|
||||||
})
|
|
||||||
.catch((retryError) => {
|
|
||||||
setRetryAccepted(false);
|
|
||||||
setRetryFeedback(
|
|
||||||
projectRuntimeVisibleError(
|
|
||||||
retryError instanceof Error
|
|
||||||
? retryError.message
|
|
||||||
: String(retryError),
|
|
||||||
'项目总控 Agent',
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => setRetrySubmitting(false));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{retrySubmitting
|
|
||||||
? needsReconciliation
|
|
||||||
? '正在结束旧任务…'
|
|
||||||
: '正在重新启动…'
|
|
||||||
: retryAccepted
|
|
||||||
? needsReconciliation
|
|
||||||
? '旧任务结束请求已受理'
|
|
||||||
: '重试已受理'
|
|
||||||
: needsReconciliation
|
|
||||||
? '已核对,结束旧任务'
|
|
||||||
: '重新启动策划'}
|
|
||||||
</button>
|
|
||||||
{retryFeedback ? (
|
|
||||||
<small className="project-runtime-retry-feedback" role="status">
|
|
||||||
{retryFeedback}
|
|
||||||
</small>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{userInputRequest && !controlBusy ? (
|
|
||||||
<AgentRuntimeUserInputCard
|
|
||||||
key={`${userInputRequest.requestId}:${userInputRequest.responseId ?? 'pending'}`}
|
|
||||||
request={userInputRequest}
|
|
||||||
controlBusy={controlBusy}
|
|
||||||
onSubmit={onUserInput}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
+7
-57
@@ -13,8 +13,6 @@ import type {
|
|||||||
GameCreatorDirectTurnUpdateStatus,
|
GameCreatorDirectTurnUpdateStatus,
|
||||||
PendingCommand,
|
PendingCommand,
|
||||||
PendingUiConfirmation,
|
PendingUiConfirmation,
|
||||||
PlanGddDecisionAction,
|
|
||||||
PlanGddStateViewV1,
|
|
||||||
} from '../../app/types';
|
} from '../../app/types';
|
||||||
import type { DesignClarificationRequest, DesignView } from '../../app/types';
|
import type { DesignClarificationRequest, DesignView } from '../../app/types';
|
||||||
import { ChatMarkdownMessage } from '../../components/ChatMarkdownMessage';
|
import { ChatMarkdownMessage } from '../../components/ChatMarkdownMessage';
|
||||||
@@ -35,13 +33,10 @@ import {
|
|||||||
DesignAgentPendingActions,
|
DesignAgentPendingActions,
|
||||||
DesignAgentPhaseStatus,
|
DesignAgentPhaseStatus,
|
||||||
} from './DesignAgentSurface';
|
} from './DesignAgentSurface';
|
||||||
import { PlanGddSurface } from './GddApprovalCard';
|
|
||||||
import {
|
import {
|
||||||
pendingCommandDetail,
|
pendingCommandDetail,
|
||||||
pendingCommandTitle,
|
pendingCommandTitle,
|
||||||
} from './pendingCommandPresentation';
|
} from './pendingCommandPresentation';
|
||||||
import { isPlanningLaneRuntime } from './planningLane';
|
|
||||||
import { PlanningLaneRuntimeStrip } from './PlanningLaneRuntimeStrip';
|
|
||||||
import { resolvePendingCommandProjectPath } from './projectCommandPolicy';
|
import { resolvePendingCommandProjectPath } from './projectCommandPolicy';
|
||||||
import {
|
import {
|
||||||
ResourceReferenceInput,
|
ResourceReferenceInput,
|
||||||
@@ -100,17 +95,6 @@ type ProjectSupervisorViewProps = RuntimePanelProps & {
|
|||||||
visibleMessages: ChatMessage[];
|
visibleMessages: ChatMessage[];
|
||||||
visibleProfessionalAgentCards: AgentStatusCard[];
|
visibleProfessionalAgentCards: AgentStatusCard[];
|
||||||
workspaceStatus: string;
|
workspaceStatus: string;
|
||||||
planGddState: PlanGddStateViewV1 | null;
|
|
||||||
planGddHydrateBusy: boolean;
|
|
||||||
planGddDecisionBusy: boolean;
|
|
||||||
planGddError: string | null;
|
|
||||||
planningLane?: boolean;
|
|
||||||
onPlanGddRefresh: () => void;
|
|
||||||
onPlanGddDecision: (
|
|
||||||
action: PlanGddDecisionAction,
|
|
||||||
comment: string | null,
|
|
||||||
) => Promise<void>;
|
|
||||||
onMakeGameFromApprovedGdd?: () => Promise<void>;
|
|
||||||
versions?: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameIterationVersion[];
|
versions?: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameIterationVersion[];
|
||||||
designView?: DesignView | null;
|
designView?: DesignView | null;
|
||||||
onDesignApprove?: (requestId: string, approved: boolean) => void;
|
onDesignApprove?: (requestId: string, approved: boolean) => void;
|
||||||
@@ -152,14 +136,6 @@ export function ProjectSupervisorView({
|
|||||||
visibleMessages,
|
visibleMessages,
|
||||||
visibleProfessionalAgentCards,
|
visibleProfessionalAgentCards,
|
||||||
workspaceStatus,
|
workspaceStatus,
|
||||||
planGddState,
|
|
||||||
planGddHydrateBusy,
|
|
||||||
planGddDecisionBusy,
|
|
||||||
planGddError,
|
|
||||||
planningLane = false,
|
|
||||||
onPlanGddRefresh,
|
|
||||||
onPlanGddDecision,
|
|
||||||
onMakeGameFromApprovedGdd,
|
|
||||||
versions,
|
versions,
|
||||||
designView = null,
|
designView = null,
|
||||||
onDesignApprove,
|
onDesignApprove,
|
||||||
@@ -167,8 +143,6 @@ export function ProjectSupervisorView({
|
|||||||
onDesignRetry,
|
onDesignRetry,
|
||||||
...runtimePanelProps
|
...runtimePanelProps
|
||||||
}: ProjectSupervisorViewProps) {
|
}: ProjectSupervisorViewProps) {
|
||||||
const planningSurfaceActive =
|
|
||||||
planningLane || isPlanningLaneRuntime(runtimePanelProps.runtime);
|
|
||||||
const [expandedProcessKey, setExpandedProcessKey] = useState<string | null>(
|
const [expandedProcessKey, setExpandedProcessKey] = useState<string | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
@@ -217,25 +191,13 @@ export function ProjectSupervisorView({
|
|||||||
{designView || onDesignApprove ? (
|
{designView || onDesignApprove ? (
|
||||||
<DesignAgentPhaseStatus
|
<DesignAgentPhaseStatus
|
||||||
view={designView}
|
view={designView}
|
||||||
busy={runtimePanelProps.controlBusy || planGddDecisionBusy}
|
busy={runtimePanelProps.controlBusy}
|
||||||
error={planGddError}
|
error={runtimePanelProps.error}
|
||||||
onApprove={onDesignApprove ?? (() => undefined)}
|
onApprove={onDesignApprove ?? (() => undefined)}
|
||||||
onClarify={onDesignClarify ?? (() => undefined)}
|
onClarify={onDesignClarify ?? (() => undefined)}
|
||||||
onRetry={onDesignRetry ?? (() => undefined)}
|
onRetry={onDesignRetry ?? (() => undefined)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : null}
|
||||||
<PlanGddSurface
|
|
||||||
state={planGddState}
|
|
||||||
active={planningSurfaceActive}
|
|
||||||
projectPath={projectPath}
|
|
||||||
hydrateBusy={planGddHydrateBusy}
|
|
||||||
decisionBusy={planGddDecisionBusy}
|
|
||||||
error={planGddError}
|
|
||||||
onRefresh={onPlanGddRefresh}
|
|
||||||
onDecision={onPlanGddDecision}
|
|
||||||
onMakeGame={onMakeGameFromApprovedGdd}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div
|
<div
|
||||||
ref={messagesRef}
|
ref={messagesRef}
|
||||||
className="message-list project-supervisor-message-list"
|
className="message-list project-supervisor-message-list"
|
||||||
@@ -343,20 +305,8 @@ export function ProjectSupervisorView({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{directCodex ? null : planningSurfaceActive ? (
|
{directCodex ? null : (
|
||||||
<PlanningLaneRuntimeStrip
|
<ProjectSupervisorRuntimePanel {...runtimePanelProps} />
|
||||||
runtime={runtimePanelProps.runtime}
|
|
||||||
error={runtimePanelProps.error}
|
|
||||||
controlBusy={runtimePanelProps.controlBusy}
|
|
||||||
readOnly={runtimePanelProps.readOnly}
|
|
||||||
onSupervisorRetry={runtimePanelProps.onSupervisorRetry}
|
|
||||||
onUserInput={runtimePanelProps.onUserInput}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ProjectSupervisorRuntimePanel
|
|
||||||
{...runtimePanelProps}
|
|
||||||
planGddAwaitingDecision={Boolean(planGddState?.pendingApproval)}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{pendingCommand ? (
|
{pendingCommand ? (
|
||||||
<div className="pending-command">
|
<div className="pending-command">
|
||||||
@@ -399,8 +349,8 @@ export function ProjectSupervisorView({
|
|||||||
{designView || onDesignApprove ? (
|
{designView || onDesignApprove ? (
|
||||||
<DesignAgentPendingActions
|
<DesignAgentPendingActions
|
||||||
view={designView}
|
view={designView}
|
||||||
busy={runtimePanelProps.controlBusy || planGddDecisionBusy}
|
busy={runtimePanelProps.controlBusy}
|
||||||
error={planGddError}
|
error={runtimePanelProps.error}
|
||||||
onApprove={onDesignApprove ?? (() => undefined)}
|
onApprove={onDesignApprove ?? (() => undefined)}
|
||||||
onClarify={onDesignClarify ?? (() => undefined)}
|
onClarify={onDesignClarify ?? (() => undefined)}
|
||||||
onRetry={onDesignRetry ?? (() => undefined)}
|
onRetry={onDesignRetry ?? (() => undefined)}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import { PROJECT_SUPERVISOR_PLAN_SOURCE } from '../../app/constants';
|
|
||||||
import type { AgentRuntimeState, PlanGddStateViewV1 } from '../../app/types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前总控 run 是否属于立项策划链路。
|
|
||||||
*
|
|
||||||
* 判据是 run 的 `source`,不是 `planGddState`:做游戏链路在策划批准之后照样带着
|
|
||||||
* 一份 approved 的策划状态,但它的总控 run 是 autonomous 源,必须继续拿完整面板。
|
|
||||||
*/
|
|
||||||
export function isPlanningLaneRuntime(
|
|
||||||
runtime: AgentRuntimeState | null | undefined,
|
|
||||||
) {
|
|
||||||
return isPlanningLaneSource(runtime?.source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同一个判据的标量入口。
|
|
||||||
*
|
|
||||||
* `App.tsx` 里那条按监工状态重灌策划状态的 effect,依赖里只放 phase/status/updatedAt
|
|
||||||
* 这类标量——轮询每拍都会新建 runtime 对象,把本体写进依赖会让每一拍都重跑。要在那
|
|
||||||
* 条 effect 里用上链路判据,就只能拿 `source` 这一个标量进去。
|
|
||||||
*/
|
|
||||||
export function isPlanningLaneSource(source: string | null | undefined) {
|
|
||||||
return source === PROJECT_SUPERVISOR_PLAN_SOURCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 策划状态里还会继续变的那几个态。`approved` / `rejected` 是终态。 */
|
|
||||||
const PLAN_GDD_LIVE_STATES: ReadonlySet<PlanGddStateViewV1['state']> = new Set([
|
|
||||||
'draft',
|
|
||||||
'ready_for_approval',
|
|
||||||
'revision_requested',
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监工状态每次变动时,要不要重新 hydrate 策划状态。
|
|
||||||
*
|
|
||||||
* hydrate 不是纯内存读:后端会抢项目写锁、扫 authority、必要时修投影。把它挂在
|
|
||||||
* 「任意 run 的任意一次更新」上,等于让做游戏和做素材链路的每一拍心跳都去抢一次
|
|
||||||
* 项目写锁。
|
|
||||||
*
|
|
||||||
* 但也不能简单地只看 `isPlanningLaneRuntime`:审批卡的可见性判据是
|
|
||||||
* `displayGdd && (pendingApproval || recoveryPending)`,跟当前 run 的 source 无关,
|
|
||||||
* 而非策划分支的监工面板还要靠 `planGddState.pendingApproval` 点亮等待审批位。所以
|
|
||||||
* 策划状态自身还没落定时,即便当前 run 不是策划链路也必须继续跟。
|
|
||||||
*/
|
|
||||||
export function planningStateNeedsRuntimeRefresh(
|
|
||||||
runtimeSource: string | null | undefined,
|
|
||||||
planGddState: PlanGddStateViewV1 | null | undefined,
|
|
||||||
) {
|
|
||||||
if (isPlanningLaneSource(runtimeSource)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!planGddState) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Boolean(
|
|
||||||
planGddState.pendingApproval ||
|
|
||||||
planGddState.recoveryPending ||
|
|
||||||
PLAN_GDD_LIVE_STATES.has(planGddState.state),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
registerHomeProjectCreationTests,
|
registerHomeProjectCreationTests,
|
||||||
registerRecentProjectsTests,
|
registerRecentProjectsTests,
|
||||||
} from './appSurface/home.suite';
|
} from './appSurface/home.suite';
|
||||||
import { registerPlanGddApprovalTests } from './appSurface/plan-gdd.suite';
|
|
||||||
import {
|
import {
|
||||||
registerCanvasAssetTests,
|
registerCanvasAssetTests,
|
||||||
registerProjectAssetTests,
|
registerProjectAssetTests,
|
||||||
@@ -72,6 +71,5 @@ describe('AI 游戏创作 App 界面边界', () => {
|
|||||||
registerProjectAssetTests();
|
registerProjectAssetTests();
|
||||||
registerAgentRuntimeCommandTests();
|
registerAgentRuntimeCommandTests();
|
||||||
registerCanvasAssetTests();
|
registerCanvasAssetTests();
|
||||||
registerPlanGddApprovalTests();
|
|
||||||
registerDesignAgentSurfaceTests();
|
registerDesignAgentSurfaceTests();
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
# 关联里程碑
|
||||||
|
|
||||||
|
`【里程碑】退役策划V2 Rust Runtime清理-2026-09-14.md`
|
||||||
|
|
||||||
|
# 修改顺序
|
||||||
|
|
||||||
|
1. 从 `runtime_protocol.rs` 移除 V2 模块声明与导出。
|
||||||
|
2. 从 `main.rs` / `commands.rs` 移除 V2 command 注册和仅供 V2 的导入。
|
||||||
|
3. 删除 V2 Rust 模块及其专属单元测试;保留共享 GDD 模型或新版设计会话仍使用的类型。
|
||||||
|
4. 用 `rg` 检查 V2 Rust 符号残留,修复编译引用。
|
||||||
|
|
||||||
|
# 验证命令
|
||||||
|
|
||||||
|
- `cargo check --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`
|
||||||
|
- `npm run check:encoding`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
# 风险与回滚
|
||||||
|
|
||||||
|
- 风险:V2 类型可能被共享测试或前端桥接代码引用。处理方式是按编译错误逐项判断,保留真正共享类型。
|
||||||
|
- 回滚:按提交粒度回退本里程碑提交,不触碰前序 V1 清理提交。
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Version
|
||||||
|
|
||||||
|
V2-RUST-RETIRE-1
|
||||||
|
|
||||||
|
# Status
|
||||||
|
|
||||||
|
in-progress
|
||||||
|
|
||||||
|
# Date
|
||||||
|
|
||||||
|
2026-09-14
|
||||||
|
|
||||||
|
# Parent Spec
|
||||||
|
|
||||||
|
`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`
|
||||||
|
|
||||||
|
# 目标
|
||||||
|
|
||||||
|
删除已经被独立 Design Agent 取代的旧策划 V2 Rust Runtime、Tauri 命令注册和仅服务 V2 的模块导出,使桌面壳继续编译并保留做游戏 Agent 与新版 Design Agent。
|
||||||
|
|
||||||
|
# 边界
|
||||||
|
|
||||||
|
- 删除 `planning_policy_v2`、`planning_session_v2` 及仅供这两者使用的 V2 注册和调用。
|
||||||
|
- 删除 V2 专属的 Tauri command 注册、模块导出和测试入口。
|
||||||
|
- 保留 `design_runtime`、`design_tools`、`design_session`、通用 runtime、DirectProject 和做游戏 Agent。
|
||||||
|
- 本里程碑不处理前端 V2 数据层、UI、文档索引和共享运行时中的可选清理。
|
||||||
|
|
||||||
|
# 验收标准
|
||||||
|
|
||||||
|
1. Rust 源码不再编译 `planning_policy_v2.rs` 或 `planning_session_v2.rs`。
|
||||||
|
2. `main.rs`、`commands.rs` 和 runtime protocol 不再注册或导出 V2 命令。
|
||||||
|
3. 新版 Design Agent 与做游戏 Agent 的 Rust 编译路径保持可用。
|
||||||
|
4. 相关定向 Rust 测试和 `cargo check` 通过。
|
||||||
|
|
||||||
|
# 依赖
|
||||||
|
|
||||||
|
- 当前分支已包含 PR159 的 V1 清理。
|
||||||
|
- 前端 V2 调用暂时保留,待后续里程碑同步删除。
|
||||||
Reference in New Issue
Block a user