coding-task.e2e.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { spawnSync } from 'node:child_process'
  2. import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
  3. import { tmpdir } from 'node:os'
  4. import { join } from 'node:path'
  5. import { afterEach, describe, expect, it } from 'vitest'
  6. import type { Context } from 'cordis'
  7. import { AgentId } from '@deepseek-ai/dsh-agent'
  8. import { codingHarness, finalText, SYSTEM_PROMPT, waitForIdle } from './harness.ts'
  9. /**
  10. * The swebench-style smoke test: a real model fixes a real bug in a temp
  11. * directory using only the bash tool, and the fix is verified OUTSIDE the
  12. * agent by re-running the test script. Key-gated.
  13. */
  14. const TEST_FILE = [
  15. "const assert = require('node:assert');",
  16. "const { add } = require('./add.js');",
  17. 'assert.strictEqual(add(2, 3), 5);',
  18. 'assert.strictEqual(add(-1, 1), 0);',
  19. "console.log('PASS');",
  20. '',
  21. ].join('\n')
  22. const BUGGY_ADD = [
  23. '// A tiny module with an obvious bug.',
  24. 'function add(a, b) {',
  25. ' return a - b;',
  26. '}',
  27. 'module.exports = { add };',
  28. '',
  29. ].join('\n')
  30. let workdir: string | undefined
  31. let ctx: Context | undefined
  32. afterEach(async () => {
  33. // Dispose the harness even on failure/retry: agent-loop teardown stops the
  34. // loop and LocalBashExecutor teardown kills anything the model left running.
  35. await ctx?.fiber.dispose()
  36. ctx = undefined
  37. if (workdir !== undefined) await rm(workdir, { recursive: true, force: true })
  38. workdir = undefined
  39. })
  40. describe.skipIf(!process.env.DEEPSEEK_API_KEY)('coding task: fix a failing test via bash', () => {
  41. it('repairs add.js so node add.test.js passes', async () => {
  42. workdir = await mkdtemp(join(tmpdir(), 'dsh-coding-task-'))
  43. await writeFile(join(workdir, 'add.js'), BUGGY_ADD)
  44. await writeFile(join(workdir, 'add.test.js'), TEST_FILE)
  45. // Confirm the fixture actually fails before the agent touches it.
  46. const before = spawnSync('node', ['add.test.js'], { cwd: workdir })
  47. expect(before.status).not.toBe(0)
  48. ctx = await codingHarness(workdir, { persona: SYSTEM_PROMPT })
  49. const agent = ctx.agentLoop.create(AgentId('e2e-task'), { model: 'deepseek-v4-flash' })
  50. agent.send([{
  51. type: 'text',
  52. text: 'In the current directory, `node add.test.js` fails because add.js has a bug. '
  53. + 'Fix add.js so the test passes, run `node add.test.js` to verify, and report the result. '
  54. + 'Do not modify add.test.js.',
  55. }])
  56. await waitForIdle(ctx, agent)
  57. // The agent claims success…
  58. const summary = finalText([...agent.session.events]).toLowerCase()
  59. expect(summary.length).toBeGreaterThan(0)
  60. // …and the world agrees: the test passes when WE run it, and the test
  61. // file is byte-identical (an agent that neutered the test instead of
  62. // fixing the bug fails here, not just on a keyword probe).
  63. const untouchedTest = await readFile(join(workdir, 'add.test.js'), 'utf8')
  64. expect(untouchedTest).toBe(TEST_FILE)
  65. const after = spawnSync('node', ['add.test.js'], { cwd: workdir, encoding: 'utf8' })
  66. expect(after.stdout).toContain('PASS')
  67. expect(after.status).toBe(0)
  68. const fixed = await readFile(join(workdir, 'add.js'), 'utf8')
  69. expect(fixed).not.toMatch(/a\s*-\s*b/)
  70. }, 180_000)
  71. })