6ee6fd64dd
Introduce page edit sessions with draft preview and patch API, chat skill picker, user memory profile, h5ApiBase resolution, voice WAV transport, and scripts for 105/g2 deployment and Plaza local dev. Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
5.4 KiB
JavaScript
157 lines
5.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import { agentRunnerInternals, createMindSpaceAgentRunner } from './mindspace-agent-runner.mjs';
|
|
|
|
test('buildAgentJobPrompt includes asset excerpts and strict JSON instructions', () => {
|
|
const prompt = agentRunnerInternals.buildAgentJobPrompt(
|
|
{ instruction: '根据资料生成周报' },
|
|
[
|
|
{
|
|
displayName: 'weekly.md',
|
|
mimeType: 'text/markdown',
|
|
excerpt: '# Weekly\nDone.',
|
|
},
|
|
],
|
|
);
|
|
|
|
assert.match(prompt, /合法 JSON 对象/);
|
|
assert.match(prompt, /weekly\.md/);
|
|
assert.match(prompt, /# Weekly/);
|
|
});
|
|
|
|
test('extractJsonObject and normalizeStructuredResult parse structured agent output', () => {
|
|
const payload = agentRunnerInternals.extractJsonObject(`
|
|
这里是结果
|
|
\`\`\`json
|
|
{"title":"项目周报","summary":"本周进展","content":"# 周报\\n\\n完成了任务","content_format":"markdown"}
|
|
\`\`\`
|
|
`);
|
|
const result = agentRunnerInternals.normalizeStructuredResult(payload);
|
|
assert.equal(result.title, '项目周报');
|
|
assert.equal(result.contentFormat, 'markdown');
|
|
});
|
|
|
|
test('extractJsonObject repairs multiline content and trailing commas', () => {
|
|
const multiline = agentRunnerInternals.extractJsonObject(`{
|
|
"title":"项目周报",
|
|
"summary":"本周进展",
|
|
"content":"# 周报
|
|
|
|
完成了任务",
|
|
"content_format":"markdown"
|
|
}`);
|
|
assert.equal(multiline.title, '项目周报');
|
|
assert.match(multiline.content, /完成了任务/);
|
|
|
|
const trailingComma = agentRunnerInternals.extractJsonObject(
|
|
'{"title":"页面","summary":"摘要","content":"正文","content_format":"markdown",}',
|
|
);
|
|
assert.equal(trailingComma.title, '页面');
|
|
});
|
|
|
|
test('extractJsonObject keeps braces inside content strings', () => {
|
|
const payload = agentRunnerInternals.extractJsonObject(
|
|
'{"title":"页面","summary":"摘要","content":"function demo() { return 1; }","content_format":"markdown"}',
|
|
);
|
|
assert.match(payload.content, /return 1/);
|
|
});
|
|
|
|
test('readAssetContext reads and truncates text assets', async () => {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-agent-runner-'));
|
|
const file = path.join(dir, 'note.md');
|
|
await fs.writeFile(file, 'A'.repeat(64), 'utf8');
|
|
|
|
const context = await agentRunnerInternals.readAssetContext(
|
|
{ assetId: 'a1', displayName: 'note.md', mimeType: 'text/markdown', path: file },
|
|
16,
|
|
);
|
|
assert.equal(context.excerpt.length, 16);
|
|
assert.equal(context.truncated, true);
|
|
});
|
|
|
|
test('runner claims job, executes reply, bills usage, and completes job', async () => {
|
|
const calls = [];
|
|
const runner = createMindSpaceAgentRunner({
|
|
apiTarget: 'http://example.test',
|
|
apiSecret: 'secret',
|
|
userAuth: {
|
|
async canUseChat() {
|
|
return { ok: true };
|
|
},
|
|
async resolveWorkingDir() {
|
|
return '/tmp/user-space';
|
|
},
|
|
async getAgentSessionPolicy() {
|
|
return { enableContextMemory: false, unrestricted: true };
|
|
},
|
|
async getUserPublishLayout() {
|
|
return null;
|
|
},
|
|
async registerAgentSession(userId, sessionId) {
|
|
calls.push(['registerAgentSession', userId, sessionId]);
|
|
},
|
|
async billSessionUsage(userId, sessionId, tokenState, requestId) {
|
|
calls.push(['billSessionUsage', userId, sessionId, tokenState, requestId]);
|
|
return { ok: true, costCents: 1 };
|
|
},
|
|
},
|
|
agentJobService: {
|
|
async claimJob(jobId) {
|
|
calls.push(['claimJob', jobId]);
|
|
return {
|
|
jobId,
|
|
userId: 'user-1',
|
|
jobToken: 'job-token',
|
|
instruction: '生成周报',
|
|
allowedAssets: [{ assetId: 'asset-1', displayName: 'weekly.md', mimeType: 'text/markdown' }],
|
|
};
|
|
},
|
|
async getAssetForJob(jobId, token, assetId) {
|
|
calls.push(['getAssetForJob', jobId, token, assetId]);
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-agent-job-'));
|
|
const file = path.join(dir, 'weekly.md');
|
|
await fs.writeFile(file, '# Weekly\nAll good.', 'utf8');
|
|
return {
|
|
assetId,
|
|
displayName: 'weekly.md',
|
|
mimeType: 'text/markdown',
|
|
path: file,
|
|
};
|
|
},
|
|
async completeJob(jobId, token, payload) {
|
|
calls.push(['completeJob', jobId, token, payload]);
|
|
return { id: jobId, status: payload.status ?? 'completed', resultPageId: 'page-1' };
|
|
},
|
|
},
|
|
executeSessionReply: async (_apiFetch, sessionId, requestId, prompt) => {
|
|
calls.push(['executeSessionReply', sessionId, requestId, prompt]);
|
|
return {
|
|
text: '{"title":"项目周报","summary":"本周完成情况","content":"# 周报\\n\\n完成了全部任务","content_format":"markdown"}',
|
|
tokenState: {
|
|
inputTokens: 10,
|
|
outputTokens: 20,
|
|
totalTokens: 30,
|
|
accumulatedInputTokens: 10,
|
|
accumulatedOutputTokens: 20,
|
|
accumulatedTotalTokens: 30,
|
|
},
|
|
};
|
|
},
|
|
apiFetchImpl: async (_pathname, _init) => ({
|
|
ok: true,
|
|
async text() {
|
|
return JSON.stringify({ id: 'session-1' });
|
|
},
|
|
}),
|
|
});
|
|
|
|
const result = await runner.runJob('job-1');
|
|
assert.equal(result.status, 'completed');
|
|
assert.equal(calls.some((item) => item[0] === 'billSessionUsage'), true);
|
|
const completeCall = calls.find((item) => item[0] === 'completeJob');
|
|
assert.equal(completeCall[3].title, '项目周报');
|
|
});
|