core-web-profile.snapshot.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { writeFile } from 'node:fs/promises'
  2. import { join } from 'node:path'
  3. import { fileURLToPath } from 'node:url'
  4. import { afterAll, beforeAll, describe, expect, it } from 'vitest'
  5. import type { AgentHandle } from '@deepseek-ai/dsh-agent'
  6. import { CallId, createUserMessage } from '@deepseek-ai/dsh-llm'
  7. import { SessionId } from '@deepseek-ai/dsh-session'
  8. import { assertFixtureInventory, launchWebScaffold, type WebScaffold } from './scaffold.ts'
  9. const CORE_WEB_OVERLAY = fileURLToPath(new URL('../../cli/config/core-web.cordis.yml', import.meta.url))
  10. const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/core-web-profile', import.meta.url))
  11. const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
  12. const PROMPT = 'Reply exactly CORE_WEB_REQUEST_OK and stop.'
  13. describe('core Web profile', () => {
  14. let scaffold: WebScaffold
  15. let agentHandle: AgentHandle
  16. beforeAll(async () => {
  17. const systemPrompt = process.env.DSH_SYSTEM_PROMPT
  18. Reflect.deleteProperty(process.env, 'DSH_SYSTEM_PROMPT')
  19. try {
  20. scaffold = await launchWebScaffold({ extraOverlayPath: CORE_WEB_OVERLAY, replayFixture: FIXTURE })
  21. } finally {
  22. if (systemPrompt !== undefined) process.env.DSH_SYSTEM_PROMPT = systemPrompt
  23. }
  24. agentHandle = await scaffold.ctx.agents.create({
  25. sessionId: SessionId('core-web-profile-smoke'),
  26. meta: { cwd: scaffold.workspaceCwd },
  27. agentOptions: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },
  28. })
  29. })
  30. afterAll(async () => {
  31. const failures: unknown[] = []
  32. await agentHandle?.dispose().catch((error: unknown) => failures.push(error))
  33. await scaffold?.close().catch((error: unknown) => failures.push(error))
  34. if (failures.length === 1) throw failures[0]
  35. if (failures.length > 1) throw new AggregateError(failures, 'core Web profile smoke teardown failed')
  36. })
  37. it('sends the RL prompt and tool schemas through a real request, then executes both tools', async () => {
  38. agentHandle.agent.followup(createUserMessage({
  39. content: [{ type: 'text', text: PROMPT }],
  40. source: { kind: 'user' },
  41. }))
  42. await agentHandle.agent.whenIdle()
  43. const requestHeader = agentHandle.agent.session.requestHeader()
  44. if (requestHeader === undefined) throw new Error('the core Web agent issued no model request')
  45. const seedPath = join(scaffold.workspaceCwd, 'profile-smoke.txt')
  46. await writeFile(seedPath, 'CORE_WEB_EDITOR_OK\n')
  47. const signal = new AbortController().signal
  48. const bash = await scaffold.ctx.tools.execute({
  49. signal,
  50. callId: CallId('core-web-bash-smoke'),
  51. name: 'bash',
  52. arguments: { command: "printf 'CORE_WEB_BASH_OK\\n'" },
  53. agent: agentHandle.agent,
  54. })
  55. const editor = await scaffold.ctx.tools.execute({
  56. signal,
  57. callId: CallId('core-web-editor-smoke'),
  58. name: 'str_replace_editor',
  59. arguments: { command: 'view', path: seedPath },
  60. agent: agentHandle.agent,
  61. })
  62. const text = (result: typeof bash): string => result.content
  63. .filter(block => block.type === 'text')
  64. .map(block => block.text)
  65. .join('')
  66. .replaceAll(scaffold.workspaceCwd, '{{cwd}}')
  67. .trimEnd()
  68. expect({
  69. prompt: requestHeader.system,
  70. tools: requestHeader.tools?.map(tool => tool.name),
  71. bash: text(bash),
  72. editor: text(editor),
  73. }).toMatchInlineSnapshot(`
  74. {
  75. "bash": "CORE_WEB_BASH_OK",
  76. "editor": "Here's the content of {{cwd}}/profile-smoke.txt with line numbers (which has a total of 2 lines):
  77. 1 CORE_WEB_EDITOR_OK
  78. 2",
  79. "prompt": "You are a helpful software engineer assistant.",
  80. "tools": [
  81. "bash",
  82. "str_replace_editor",
  83. ],
  84. }
  85. `)
  86. expect(requestHeader.tools).toEqual(scaffold.ctx.tools.schemas(agentHandle.agent))
  87. const entries = [...scaffold.ctx.loader.entries()]
  88. expect(entries.find(entry => entry.options.id === 'persistent-bash')?.fiber).toBeDefined()
  89. expect(entries.find(entry => entry.options.id === 'pty-local')?.fiber).toBeDefined()
  90. expect(entries.find(entry => entry.options.id === 'str-replace-editor')?.fiber).toBeDefined()
  91. expect(entries.find(entry => entry.options.id === 'web-runtime')?.fiber).toBeDefined()
  92. expect(entries.find(entry => entry.options.id === 'workspace-context')?.fiber).toBeUndefined()
  93. await assertFixtureInventory(SNAPSHOT_DIR, ['session.jsonl'])
  94. })
  95. it('uses DSH_SYSTEM_PROMPT as the complete prompt when configured', async () => {
  96. const previous = process.env.DSH_SYSTEM_PROMPT
  97. process.env.DSH_SYSTEM_PROMPT = 'RL prompt override'
  98. let overrideScaffold: WebScaffold | undefined
  99. let overrideAgent: AgentHandle | undefined
  100. try {
  101. overrideScaffold = await launchWebScaffold({ extraOverlayPath: CORE_WEB_OVERLAY, replayFixture: FIXTURE })
  102. overrideAgent = await overrideScaffold.ctx.agents.create({
  103. sessionId: SessionId('core-web-profile-override'),
  104. meta: { cwd: overrideScaffold.workspaceCwd },
  105. agentOptions: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },
  106. })
  107. overrideAgent.agent.followup(createUserMessage({
  108. content: [{ type: 'text', text: PROMPT }],
  109. source: { kind: 'user' },
  110. }))
  111. await overrideAgent.agent.whenIdle()
  112. expect(overrideAgent.agent.session.requestHeader()?.system).toBe('RL prompt override')
  113. } finally {
  114. try {
  115. await overrideAgent?.dispose()
  116. } finally {
  117. try {
  118. await overrideScaffold?.close()
  119. } finally {
  120. if (previous === undefined) Reflect.deleteProperty(process.env, 'DSH_SYSTEM_PROMPT')
  121. else process.env.DSH_SYSTEM_PROMPT = previous
  122. }
  123. }
  124. }
  125. })
  126. })