修复 DirectProject 对话权限与原生命令门禁
补充 DirectProject 原生对话读写命令的 native-only 白名单 复用项目级 DirectProject 写权限检查,避免连续回合重复读取策略 更新 AppSurface 测试以覆盖 Rust 负责历史持久化的直连语义
This commit is contained in:
@@ -107,11 +107,13 @@ const rustSharedContractSource = fs.readFileSync(
|
||||
'utf8',
|
||||
);
|
||||
const allowedUncalledTauriCommands = [
|
||||
'append_direct_project_conversation_message',
|
||||
'chat_with_game_creator_agent',
|
||||
'check_ui_editor_font_glyph_coverage',
|
||||
'create_ui_design_resource',
|
||||
'open_game_creator_launcher_window',
|
||||
'open_game_creator_workspace_window',
|
||||
'read_direct_project_conversation',
|
||||
'stop_local_game_preview_if_matches',
|
||||
];
|
||||
const sourceExtensions = new Set([
|
||||
|
||||
@@ -5307,7 +5307,10 @@ export function App({
|
||||
if (directProjectPath && directInvoke) {
|
||||
const clientTurnId =
|
||||
directConversationTurnId ?? createDirectCodexConversationTurnId();
|
||||
if (!directPolicyChecked) {
|
||||
if (
|
||||
!directPolicyChecked &&
|
||||
projectConversationWriteConfirmedRef.current !== directProjectPath
|
||||
) {
|
||||
try {
|
||||
const policyPaused = await queueProjectPolicyConfirmationIfNeeded(
|
||||
directInvoke,
|
||||
@@ -5316,6 +5319,8 @@ export function App({
|
||||
'写入 DirectProject 对话历史',
|
||||
'DirectProject 对话写入需要确认。',
|
||||
() => {
|
||||
projectConversationWriteConfirmedRef.current =
|
||||
directProjectPath;
|
||||
void executeChatAgentReply({
|
||||
prompt,
|
||||
clientTurnId,
|
||||
@@ -5328,6 +5333,7 @@ export function App({
|
||||
if (policyPaused) {
|
||||
return;
|
||||
}
|
||||
projectConversationWriteConfirmedRef.current = directProjectPath;
|
||||
} catch (error) {
|
||||
if (localProjectPathRef.current === directProjectPath) {
|
||||
setProjectSupervisorRuntimeError(
|
||||
|
||||
@@ -2122,6 +2122,7 @@ export function registerHomeProjectCreationTests() {
|
||||
'existing-project',
|
||||
'已有项目',
|
||||
);
|
||||
const persistedMessages: Array<Record<string, unknown>> = [];
|
||||
const invoke = vi.fn(
|
||||
async (command: string, args?: Record<string, unknown>) => {
|
||||
if (command === 'get_local_game_manifest') {
|
||||
@@ -2131,14 +2132,45 @@ export function registerHomeProjectCreationTests() {
|
||||
if (command === 'append_local_permission_log') {
|
||||
return {};
|
||||
}
|
||||
if (command === 'append_local_conversation_message') {
|
||||
if (command === 'hydrate_game_creator_plan_gdd_state') {
|
||||
return null;
|
||||
}
|
||||
if (command === 'read_project_permission_policy') {
|
||||
return {
|
||||
path: '.agent/policy.json',
|
||||
policy: { deniedCommands: [], confirmCommands: [] },
|
||||
};
|
||||
}
|
||||
if (
|
||||
command === 'read_local_conversation' ||
|
||||
command === 'read_direct_project_conversation'
|
||||
) {
|
||||
return {
|
||||
path: `${projectPath}/.agent/conversations/project.jsonl`,
|
||||
agentId: null,
|
||||
messages: [],
|
||||
sessionId: null,
|
||||
messages: [...persistedMessages],
|
||||
};
|
||||
}
|
||||
if (command === 'append_local_conversation_message') {
|
||||
throw new Error(
|
||||
'DirectProject must not use browser conversation writer',
|
||||
);
|
||||
}
|
||||
if (command === 'chat_with_game_creator_direct_codex') {
|
||||
const clientTurnId = String(args?.clientTurnId ?? '');
|
||||
persistedMessages.push(
|
||||
{
|
||||
role: 'user',
|
||||
content: String(args?.prompt ?? ''),
|
||||
messageId: `direct-codex:${clientTurnId}:user`,
|
||||
},
|
||||
{
|
||||
role: 'assistant',
|
||||
content: 'DIRECT_EXISTING_PROJECT_OK',
|
||||
messageId: `direct-codex:${clientTurnId}:assistant`,
|
||||
},
|
||||
);
|
||||
return 'DIRECT_EXISTING_PROJECT_OK';
|
||||
}
|
||||
throw new Error(`unexpected invoke ${command}`);
|
||||
@@ -2165,32 +2197,18 @@ export function registerHomeProjectCreationTests() {
|
||||
},
|
||||
);
|
||||
});
|
||||
const persistedTurnCall = invoke.mock.calls.findIndex(
|
||||
([command, args]) =>
|
||||
command === 'append_local_conversation_message' &&
|
||||
(args as Record<string, unknown> | undefined)?.messageId !== undefined,
|
||||
);
|
||||
const directTurnCall = invoke.mock.calls.findIndex(
|
||||
([command]) => command === 'chat_with_game_creator_direct_codex',
|
||||
);
|
||||
expect(persistedTurnCall).toBeGreaterThanOrEqual(0);
|
||||
expect(persistedTurnCall).toBeLessThan(directTurnCall);
|
||||
const persistedTurnArgs = invoke.mock.calls[persistedTurnCall]?.[1] as
|
||||
| Record<string, unknown>
|
||||
| undefined;
|
||||
const directTurnArgs = invoke.mock.calls[directTurnCall]?.[1] as
|
||||
| Record<string, unknown>
|
||||
| undefined;
|
||||
expect(persistedTurnArgs).toEqual({
|
||||
projectPath,
|
||||
agentId: null,
|
||||
messageId: `direct-codex:${String(directTurnArgs?.clientTurnId ?? '')}:user`,
|
||||
message: {
|
||||
role: 'user',
|
||||
content: '继续修改已有项目',
|
||||
agentId: null,
|
||||
},
|
||||
});
|
||||
expect(directTurnCall).toBeGreaterThanOrEqual(0);
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
'append_local_conversation_message',
|
||||
expect.anything(),
|
||||
);
|
||||
expect(directTurnArgs?.clientTurnId).toEqual(expect.any(String));
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
'create_automatic_local_game_project',
|
||||
);
|
||||
@@ -2214,7 +2232,10 @@ export function registerHomeProjectCreationTests() {
|
||||
expect(args).toEqual({ projectPath });
|
||||
return manifest;
|
||||
}
|
||||
if (command === 'read_local_conversation') {
|
||||
if (
|
||||
command === 'read_local_conversation' ||
|
||||
command === 'read_direct_project_conversation'
|
||||
) {
|
||||
return {
|
||||
path: `${projectPath}/.agent/conversations/project.jsonl`,
|
||||
agentId: null,
|
||||
@@ -2257,6 +2278,23 @@ export function registerHomeProjectCreationTests() {
|
||||
};
|
||||
}
|
||||
if (command === 'chat_with_game_creator_direct_codex') {
|
||||
const clientTurnId = String(args?.clientTurnId ?? '');
|
||||
persistedMessages.push(
|
||||
{
|
||||
schemaVersion: 'game-creator-conversation.v1',
|
||||
role: 'user',
|
||||
content: String(args?.prompt ?? ''),
|
||||
messageId: `direct-codex:${clientTurnId}:user`,
|
||||
updatedAt: 1,
|
||||
},
|
||||
{
|
||||
schemaVersion: 'game-creator-conversation.v1',
|
||||
role: 'assistant',
|
||||
content: '陶泥儿智能创作 鉴权失败,请检查 API Key 或登录态',
|
||||
messageId: `direct-codex:${clientTurnId}:assistant`,
|
||||
updatedAt: 2,
|
||||
},
|
||||
);
|
||||
throw new Error('codex-app-server-error:unauthorized');
|
||||
}
|
||||
throw new Error(`unexpected invoke ${command}`);
|
||||
|
||||
Reference in New Issue
Block a user