loader-composition.e2e.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { readFileSync } from 'node:fs'
  2. import { join } from 'node:path'
  3. import { fileURLToPath } from 'node:url'
  4. import { describe, expect, it } from 'vitest'
  5. import {
  6. LOADER_SMOKE_TEST_TIMEOUT_MS,
  7. runLoaderSmoke,
  8. } from '@deepseek-ai/dsh-loader-smoke'
  9. const fixtureDir = fileURLToPath(new URL(
  10. '../../../../examples/acp-agent/tests/fixtures/subagent/subagent-claude-code/',
  11. import.meta.url,
  12. ))
  13. const driver = join(fixtureDir, 'driver.ts')
  14. const configPath = join(fixtureDir, 'cordis.yml')
  15. const packageDir = fileURLToPath(new URL('..', import.meta.url))
  16. const manifest = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')) as {
  17. dsh?: { bundle?: { patch?: string } }
  18. }
  19. const bundlePatch = manifest.dsh?.bundle?.patch
  20. if (bundlePatch === undefined) throw new Error('Claude Code package must declare a Bundle patch')
  21. const bundlePatchPath = join(packageDir, bundlePatch)
  22. const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url))
  23. describe('product-provider public Loader composition', () => {
  24. it('loads the Bundle default, two named Claude instances, their tools, and Codex without starting either product', async () => {
  25. const { stdout, stderr } = await runLoaderSmoke({
  26. label: 'product-provider Loader composition',
  27. tempDirPrefix: 'dsh-product-provider-loader-',
  28. binScript: driver,
  29. libBinScript: driver,
  30. configPath,
  31. binArgs: [configPath, bundlePatchPath],
  32. tsconfigPath: repoTsconfig,
  33. env: {
  34. // Loading the optional package must not probe or start a Claude binary.
  35. PATH: '',
  36. },
  37. })
  38. expect(stderr).toBe('')
  39. expect(JSON.parse(stdout)).toEqual({
  40. registeredProviders: ['codex', 'claude-primary', 'claude-secondary', 'claude-code'],
  41. providers: [
  42. {
  43. name: 'codex',
  44. capabilities: {
  45. outputSchema: false,
  46. depthLimit: false,
  47. toolFilter: false,
  48. persona: false,
  49. },
  50. inheritsParentContext: false,
  51. },
  52. {
  53. name: 'claude-code',
  54. capabilities: {
  55. outputSchema: false,
  56. depthLimit: false,
  57. toolFilter: false,
  58. persona: false,
  59. },
  60. inheritsParentContext: false,
  61. },
  62. {
  63. name: 'claude-primary',
  64. capabilities: {
  65. outputSchema: false,
  66. depthLimit: false,
  67. toolFilter: false,
  68. persona: false,
  69. },
  70. inheritsParentContext: false,
  71. },
  72. {
  73. name: 'claude-secondary',
  74. capabilities: {
  75. outputSchema: false,
  76. depthLimit: false,
  77. toolFilter: false,
  78. persona: false,
  79. },
  80. inheritsParentContext: false,
  81. },
  82. ],
  83. tools: [
  84. {
  85. name: 'subagent_codex',
  86. parameterNames: ['description', 'prompt', 'run_in_background'],
  87. required: ['description', 'prompt'],
  88. },
  89. {
  90. name: 'subagent_claude_code',
  91. parameterNames: ['description', 'prompt', 'run_in_background'],
  92. required: ['description', 'prompt'],
  93. },
  94. {
  95. name: 'subagent_claude_primary',
  96. parameterNames: ['description', 'prompt', 'run_in_background'],
  97. required: ['description', 'prompt'],
  98. },
  99. {
  100. name: 'subagent_claude_secondary',
  101. parameterNames: ['description', 'prompt', 'run_in_background'],
  102. required: ['description', 'prompt'],
  103. },
  104. ],
  105. jobTools: ['job_kill', 'job_list', 'job_output'],
  106. starts: 0,
  107. })
  108. }, LOADER_SMOKE_TEST_TIMEOUT_MS)
  109. })