source-runtime.compat.spec.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. /** The Node compatibility matrix runs this complete source-entry smoke. */
  2. import { readFile, writeFile } from 'node:fs/promises'
  3. import { join } from 'node:path'
  4. import { expect, it } from 'vitest'
  5. import { Context } from '@deepseek-ai/cordis'
  6. import SubagentRuntime from '@deepseek-ai/dsh-subagent'
  7. import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
  8. import PtcWorkflowEngine from '../src/index.ts'
  9. import { fakeParent, mountPtcRuntime } from './setup.ts'
  10. async function setup(mode: SandboxMode) {
  11. const ctx = new Context()
  12. const files = await mountPtcRuntime(ctx, mode)
  13. await ctx.plugin(SubagentRuntime)
  14. ctx.subagents.registerProvider({
  15. name: 'spawn',
  16. capabilities: { agentOptions: true, outputSchema: true, depthLimit: true, toolFilter: true, persona: true },
  17. inheritsParentContext: false,
  18. start: () => Promise.reject(new Error('source runtime smoke must not start a child')),
  19. })
  20. await ctx.plugin(PtcWorkflowEngine, {})
  21. return { ctx, parent: fakeParent(ctx), ...files }
  22. }
  23. it('runs the default workflow config through the source PTC runtime', async () => {
  24. const { ctx, parent } = await setup('read-only')
  25. const run = ctx.workflowEngine.start({
  26. script: 'return args.left * args.right',
  27. args: { left: 6, right: 7 },
  28. meta: { name: 'source-runtime-compat', description: 'source workflow and PTC entry' },
  29. parent,
  30. })
  31. try {
  32. const result = await run.result
  33. expect(result, result.error).toMatchObject({ value: 42, stopReason: 'completed', agentsStarted: 0 })
  34. } finally { await run.dispose() }
  35. })
  36. it.each(['read-only', 'workspace-write'] as const)('enforces the Session file policy %s for direct Node writes', async (mode) => {
  37. const { ctx, parent, root, cwd } = await setup(mode)
  38. const outside = join(root, 'outside.txt')
  39. const inside = join(cwd, 'inside.txt')
  40. await writeFile(outside, 'unchanged')
  41. const run = ctx.workflowEngine.start({
  42. script: `const proc = globalThis.constructor.constructor('return process')()
  43. const fs = proc.getBuiltinModule('node:fs')
  44. try { fs.writeFileSync('inside.txt', 'inside') } catch {}
  45. try { fs.writeFileSync(args.outside, 'changed') } catch {}
  46. return 42`,
  47. args: { outside },
  48. meta: { name: 'source-confinement', description: 'direct Node file writes' },
  49. parent,
  50. })
  51. try {
  52. const result = await run.result
  53. expect(result.stopReason, result.error).toBe('completed')
  54. expect(await readFile(outside, 'utf8')).toBe('unchanged')
  55. if (mode === 'workspace-write') expect(await readFile(inside, 'utf8')).toBe('inside')
  56. else await expect(readFile(inside, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' })
  57. } finally { await run.dispose() }
  58. })