const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const vm = require('node:vm') const ts = require('typescript') const contextApi = {} vm.runInNewContext(ts.transpileModule(fs.readFileSync(require.resolve('../src/renderer/src/features/workspace/agentContext.ts'), 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText, { exports: contextApi, DOMException }) const source = ts.createSourceFile('useAiAgent.ts', fs.readFileSync(require.resolve('../src/renderer/src/features/workspace/useAiAgent.ts'), 'utf8'), ts.ScriptTarget.Latest, true) let payloadExpression function visit(node) { if (ts.isCallExpression(node) && node.expression.getText(source) === 'JSON.stringify' && node.arguments[0]?.getText(source).includes('options.attachments')) { payloadExpression = node.getText(source) } ts.forEachChild(node, visit) } visit(source) assert.ok(payloadExpression, 'Agent request JSON serialization must exist') function serialize(attachments, finalize = false, request = '설정 위치 알려줘') { return JSON.parse(vm.runInNewContext(payloadExpression, { courseId: 'course-1', weekNumber: 3, request, options: { attachments }, finalize, agentPlanRequestText: contextApi.agentPlanRequestText, context: { modules: [{ name: 'Control', files: [] }], runtimeLogs: [] } })) } for (const attachments of [undefined, []]) { test(`omits attachments when ${attachments ? 'empty' : 'undefined'}`, () => { const body = serialize(attachments) assert.equal(Object.hasOwn(body, 'attachments'), false) assert.equal(body.request, '설정 위치 알려줘') assert.equal(body.modules[0].name, 'Control') assert.equal(body.week, 3) }) } test('preserves actual attachment contents rather than silently dropping them', () => { const attachments = [{ name: 'config.yaml', content: 'steering: ackermann' }] assert.deepEqual(serialize(attachments).attachments, attachments) }) test('finalization instruction reaches the API without sending unsupported DTO fields', () => { const body = serialize(undefined, true) assert.match(body.request, /자료 조회는 종료/) assert.match(body.request, /설정 위치 알려줘/) assert.match(body.request, /최종 답변/) assert.equal(Object.hasOwn(body, 'finalize'), false) assert.deepEqual(Object.keys(body).sort(), ['courseId', 'modules', 'request', 'runtimeLogs', 'week']) }) test('finalization keeps the request within the backend 4000-character limit', () => { const body = serialize(undefined, true, 'x'.repeat(4000)) assert.equal(body.request.length, 4000) assert.match(body.request, /원래 학생 질문/) })