Files
memind/mindspace-agent-runner.test.mjs
T
John 6ee6fd64dd Add MindSpace page live edit, chat skills, and H5 deploy tooling.
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>
2026-06-15 22:09:38 -07:00

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, '项目周报');
});