feat: complete Aider Page Data review workflow
This commit is contained in:
@@ -1088,6 +1088,114 @@ test('Page Data run succeeds only after a generated session page is detected', a
|
||||
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
|
||||
});
|
||||
|
||||
test('Page Data plus Aider workflow builds with Agent and then performs mandatory Aider review', async () => {
|
||||
const workdir = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-page-data-aider-'));
|
||||
const pool = createFakePool({
|
||||
sessionDeliverables: {
|
||||
'user-1:session-order-system': [{
|
||||
page_id: 'page-order',
|
||||
title: '下单系统',
|
||||
publication_id: 'pub-order',
|
||||
publication_status: 'online',
|
||||
public_url: 'http://127.0.0.1:5173/u/john/pages/page-order',
|
||||
workspace_relative_path: 'public/order.html',
|
||||
}],
|
||||
},
|
||||
});
|
||||
const reviewJobs = [];
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {
|
||||
async resolveWorkingDir() {
|
||||
return workdir;
|
||||
},
|
||||
},
|
||||
tkmindProxy: {
|
||||
async startSessionForUser() {
|
||||
return { id: 'session-order-system' };
|
||||
},
|
||||
async submitSessionReplyAndAwaitFinishForUser(_userId, _sessionId, _requestId, _message, options) {
|
||||
assert.equal(options.toolMode, 'chat');
|
||||
await fs.mkdir(path.join(workdir, 'public'), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(workdir, 'public', 'order.html'),
|
||||
'<!doctype html><script src="/assets/page-data-client.js"></script>',
|
||||
);
|
||||
return {
|
||||
ok: true,
|
||||
finishEvent: { type: 'Finish' },
|
||||
toolEvidence: { calls: ['private_data_execute', 'private_data_bind_workspace_page'] },
|
||||
};
|
||||
},
|
||||
},
|
||||
toolGateway: {
|
||||
getStatus() {
|
||||
return {
|
||||
enabled: true,
|
||||
protocol: 'agent-run-v1',
|
||||
executors: ['aider', 'openhands'],
|
||||
};
|
||||
},
|
||||
async executeJob(job) {
|
||||
reviewJobs.push(job);
|
||||
await fs.mkdir(path.join(workdir, '.memind', 'agent-runs'), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(workdir, '.memind', 'agent-runs', 'req-page-data-aider.json'),
|
||||
JSON.stringify({ requestId: 'req-page-data-aider', review: 'passed' }),
|
||||
);
|
||||
return {
|
||||
ok: true,
|
||||
executor: 'aider',
|
||||
exitCode: 0,
|
||||
cwd: workdir,
|
||||
stdout: 'Reviewed public/order.html and the Page Data client usage.',
|
||||
};
|
||||
},
|
||||
},
|
||||
syncUserPagesOnSuccess: async () => ({
|
||||
pageDataBind: { errors: [] },
|
||||
pageDataRelativePaths: ['public/order.html'],
|
||||
}),
|
||||
validateRunDeliverables: async () => ({ errors: [] }),
|
||||
retryDelaysMs: [],
|
||||
});
|
||||
|
||||
const run = await gateway.createRun('user-1', {
|
||||
requestId: 'req-page-data-aider',
|
||||
userMessage: {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: '创建下单系统并在后台管理订单' }],
|
||||
metadata: {
|
||||
displayText: '创建下单系统并在后台管理订单',
|
||||
memindRun: {
|
||||
reviewExecutor: 'aider',
|
||||
pageDataAiderWorkflow: true,
|
||||
validation: {
|
||||
expectedFile: {
|
||||
path: '.memind/agent-runs/req-page-data-aider.json',
|
||||
contains: 'req-page-data-aider',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
toolMode: 'chat',
|
||||
});
|
||||
|
||||
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
|
||||
assert.equal(reviewJobs.length, 1);
|
||||
assert.equal(reviewJobs[0].taskType, 'page_data_dev');
|
||||
assert.deepEqual(
|
||||
reviewJobs[0].userMessage.metadata.memindRun.aiderContextFiles,
|
||||
['public/order.html'],
|
||||
);
|
||||
assert.ok(
|
||||
pool.events.some(
|
||||
(event) => event.runId === run.id && event.eventType === 'required_code_review_validation',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('agent run fails closed when a generated page violates browser storage policy', async () => {
|
||||
const pool = createFakePool({
|
||||
sessionDeliverables: {
|
||||
@@ -2074,6 +2182,88 @@ test('agent run validates expected tool gateway artifacts before succeeding', as
|
||||
assert.equal(JSON.parse(validationEvent.dataJson).expectedFiles[0].path, 'RESULT.md');
|
||||
});
|
||||
|
||||
test('required Aider run persists a validated result into a chat session', async () => {
|
||||
const pool = createFakePool();
|
||||
const workdir = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-aider-delivery-'));
|
||||
const deliveries = [];
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {
|
||||
async resolveWorkingDir() {
|
||||
return workdir;
|
||||
},
|
||||
},
|
||||
tkmindProxy: {},
|
||||
directChatService: {
|
||||
async respondDeterministically(options) {
|
||||
deliveries.push(options);
|
||||
await options.onSessionReady('h5direct_aider_result');
|
||||
return { sessionId: 'h5direct_aider_result' };
|
||||
},
|
||||
},
|
||||
toolGateway: {
|
||||
getStatus() {
|
||||
return {
|
||||
enabled: true,
|
||||
protocol: 'agent-run-v1',
|
||||
executors: ['aider', 'openhands'],
|
||||
};
|
||||
},
|
||||
async executeJob() {
|
||||
await fs.mkdir(path.join(workdir, '.memind', 'agent-runs'), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(workdir, '.memind', 'agent-runs', 'req-aider-delivery.json'),
|
||||
JSON.stringify({ requestId: 'req-aider-delivery', tests: 'passed' }),
|
||||
);
|
||||
return {
|
||||
ok: true,
|
||||
dryRun: false,
|
||||
executor: 'aider',
|
||||
exitCode: 0,
|
||||
cwd: workdir,
|
||||
stdout: 'Implemented the requested page and ran its checks.',
|
||||
stderr: '',
|
||||
};
|
||||
},
|
||||
},
|
||||
retryDelaysMs: [],
|
||||
});
|
||||
|
||||
const run = await gateway.createRun('user-1', {
|
||||
requestId: 'req-aider-delivery',
|
||||
userMessage: {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: 'build the page' }],
|
||||
metadata: {
|
||||
displayText: 'build the page',
|
||||
memindRun: {
|
||||
executor: 'aider',
|
||||
validation: {
|
||||
expectedFile: {
|
||||
path: '.memind/agent-runs/req-aider-delivery.json',
|
||||
contains: 'req-aider-delivery',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
toolMode: 'code',
|
||||
taskType: 'h5_chat_code_task',
|
||||
});
|
||||
|
||||
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
|
||||
assert.equal(pool.runs.get(run.id).agent_session_id, 'h5direct_aider_result');
|
||||
assert.equal(deliveries.length, 1);
|
||||
assert.match(deliveries[0].reply, /Aider/i);
|
||||
assert.match(deliveries[0].reply, /通过平台文件验收/);
|
||||
assert.equal(
|
||||
pool.events.some(
|
||||
(event) => event.runId === run.id && event.eventType === 'tool_gateway_result_delivered',
|
||||
),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test('agent run fails non-retryably when tool gateway artifact validation fails', async () => {
|
||||
const pool = createFakePool();
|
||||
const workdir = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-tool-validation-missing-'));
|
||||
|
||||
Reference in New Issue
Block a user