built-errors.e2e.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. /** Built tool and runtime bundles must preserve their shared structured error classes. */
  2. import { execFile } from 'node:child_process'
  3. import { existsSync } from 'node:fs'
  4. import { fileURLToPath } from 'node:url'
  5. import { promisify } from 'node:util'
  6. import { expect, it } from 'vitest'
  7. const repoRoot = fileURLToPath(new URL('../../../../', import.meta.url))
  8. const bundle = fileURLToPath(new URL('../lib/index.js', import.meta.url))
  9. const execFileAsync = promisify(execFile)
  10. const probe = String.raw`
  11. import assert from 'node:assert/strict'
  12. import { Context } from './vendor/cordis/lib/index.js'
  13. import AgentRegistry from './packages/core/agent/lib/index.js'
  14. import LocalFileSystem from './packages/fs/fs-local/lib/index.js'
  15. import SystemPrompt from './packages/core/system-prompt/lib/index.js'
  16. import ToolRuntime from './packages/core/tools/lib/index.js'
  17. import { Session, SESSION_FORMAT_VERSION } from './packages/core/session/lib/index.js'
  18. import * as Present from './packages/fs/tool-present/lib/index.js'
  19. import { mkdtemp, rm } from 'node:fs/promises'
  20. import { tmpdir } from 'node:os'
  21. import { join } from 'node:path'
  22. const root = await mkdtemp(join(tmpdir(), 'present-built-'))
  23. const ctx = new Context()
  24. try {
  25. await ctx.plugin(SystemPrompt)
  26. await ctx.plugin(ToolRuntime)
  27. await ctx.plugin(AgentRegistry)
  28. await ctx.plugin(LocalFileSystem, { cwd: root })
  29. ctx.provide('sessionProjections', { stateOf() { return { openTurnStartSeq: 1, lastTurn: 1 } } })
  30. await ctx.plugin(Present, { maxFiles: 2 })
  31. const scope = ctx.plugin(() => {})
  32. const session = Session.create('built-present', [], { version: SESSION_FORMAT_VERSION, id: 'built-present', createdAt: 0, cwd: root, isSeeded: false })
  33. const owner = { id: 'built-present', session, ctx: scope.ctx, options: {}, status: 'idle' }
  34. ctx.agents.register(owner)
  35. const events = []
  36. ctx.on('tools/result', (_exec, result) => events.push(result))
  37. let n = 0
  38. for (const [files, code] of [[[{ path: 'missing' }], 'FS_NOT_FOUND'], [[{ path: 42 }], 'INVALID_ARGS']]) {
  39. const result = await ctx.tools.execute({ signal: new AbortController().signal, name: 'present', callId: 'call-' + (++n), arguments: { files }, agent: owner })
  40. assert.equal(result.isError, true)
  41. assert.equal(result.error.info.code, code)
  42. assert.equal(events.at(-1).error.info.code, code)
  43. console.log('built ToolRuntime result preserved ' + code)
  44. }
  45. } finally {
  46. try { await ctx.fiber.dispose() }
  47. finally { await rm(root, { recursive: true, force: true }) }
  48. }
  49. `
  50. it.skipIf(!existsSync(bundle))('preserves present error codes through built ToolRuntime results', { retry: 0 }, async ({ signal }) => {
  51. const { stdout } = await execFileAsync(process.execPath, ['--input-type=module', '-e', probe], { cwd: repoRoot, signal })
  52. expect(stdout.trim().split('\n')).toEqual([
  53. 'built ToolRuntime result preserved FS_NOT_FOUND',
  54. 'built ToolRuntime result preserved INVALID_ARGS',
  55. ])
  56. })